This repository has been archived on 2022-12-11. You can view files and clone it, but cannot push or open issues or pull requests.
dot-files/.emacs.d/site-lisp-extra/scribble.el

1142 lines
39 KiB
EmacsLisp

;; $Id: scribble.el,v 1.48 2013-09-28 04:52:05 user Exp $
(defconst scribble-mode-title "Racket Scribble Emacs Mode")
(defconst scribble-mode-version "0.4")
(defconst scribble-mode-date "2013-09-28")
(defconst scribble-mode-web "http://www.neilvandyke.org/scribble-emacs/")
(defconst scribble-mode-legal-notices
"Copyright (c) 2011, 2013 Neil Van Dyke. This program is Free Software; you can
redistribute it and/or modify it under the terms of the GNU Lesser General
Public License as published by the Free Software Foundation; either version
3 of the License (LGPL 3), or (at your option) any later version. This
program is distributed in the hope that it will be useful, but without any
warranty; without even the implied warranty of merchantability or fitness
for a particular purpose. See http://www.gnu.org/licenses/ for details.
For other licenses and consulting, please contact the author.")
;; Note: Changes to above info must be reflected in "scribble-emacs.scrbl"
;; and "info.rkt".
;;-------------------------------------------------------------------- Requires
(require 'eldoc)
;;---------------------------------------------------------------------- Custom
(defgroup scribble
nil
"Racket Scribble Emacs Mode"
:group 'wp
:prefix "scribble-")
(defface scribble-comment-face
'((t :foreground "cyan"))
"Face for Comments in Scribble mode."
:group 'scribble)
(defface scribble-at-keyword-face
'((t :weight bold :foreground "gray50"))
"Face for `@'-and-name in Scribble mode."
:group 'scribble)
(defface scribble-square-bracket-face
'((t :weight bold :foreground "#c48080"))
"Face for square brackets in Scribble mode."
:group 'scribble)
(defface scribble-curly-brace-face
'((t :weight bold :foreground "#c48080"))
"Face for curly braces in Scribble mode."
:group 'scribble)
(defface scribble-title-face
'((t :family "DejaVu Serif" :height 2.0736 :slant italic))
"Face for titles in Scribble mode."
:group 'scribble)
(defface scribble-section-heading-face
'((t :family "DejaVu Serif" :height 2.0736 :weight bold))
"Face for section headings in Scribble mode."
:group 'scribble)
(defface scribble-subsection-heading-face
'((t :family "DejaVu Serif" :height 1.728 :weight bold))
"Face for subsection headings in Scribble mode."
:group 'scribble)
(defface scribble-subsubsection-heading-face
'((t :family "DejaVu Serif" :height 1.44 :weight bold))
"Face for subsubsection headings in Scribble mode."
:group 'scribble)
(defface scribble-sub*section-heading-face
'((t :family "DejaVu Serif" :height 1.2 :weight bold))
"Face for deep-subsection headings in Scribble mode."
:group 'scribble)
(defface scribble-bold-face
'((t :weight bold))
"Face for @bold in Scribble mode."
:group 'scribble)
(defface scribble-emph-face
'((t :slant italic))
"Face for @emph in Scribble mode."
:group 'scribble)
(defface scribble-tt-face
'((t :family "DejaVu Sans Mono"))
"Face for @tt in Scribble mode."
:group 'scribble)
(defface scribble-link-text-face
'((t :underline t :foreground "blue"))
"Face for link text in Scribble mode."
:group 'scribble)
(defface scribble-planet-face
'((t))
"Face for @PLaneT in Scribble mode."
:group 'scribble)
(defface scribble-racket-result-face
'((t :foreground "blue3"))
"Face for Racket results in Scribble mode."
:group 'scribble)
(defface scribble-racket-value-face
'((t :foreground "green2"))
"Face for Racket values in Scribble mode."
:group 'scribble)
(defface scribble-bnf-nonterm-face
'((t :slant italic))
"Face for @nonterm in Scribble mode."
:group 'scribble)
(defface scribble-racket-litchar-face
'((t :foreground "#aaaa00"
:background "#f4f4f4"
:slant normal
:weight normal))
"Face for @litchar in Scribble mode."
:group 'scribble)
;; Quickref Faces:
;; (defface scribble-quickref-default-face
;; '((t :family "DejaVu Sans"
;; :foreground "#000000"
;; :background "#e8e8ff"
;; :weight normal
;; :slant normal))
;; "Face for Quickref in Scribble mode."
;; :group 'scribble)
;; (defface scribble-quickref-meta-face
;; '((t :inherit scribble-quickref-default-face
;; :foreground "#000000"))
;; "Face for Quickref !!! in Scribble mode."
;; :group 'scribble)
;; (defface scribble-quickref-literal-face
;; '((t :family "DejaVu Sans Mono"
;; :inherit scribble-quickref-default-face
;; :foreground "red4"
;; :weight bold))
;; "Face for Quickref !!! in Scribble mode."
;; :group 'scribble)
;; (defface scribble-quickref-value-face
;; '((t :family "DejaVu Sans Mono"
;; :inherit scribble-quickref-default-face
;; :foreground "green4"))
;; "Face for Quickref !!! in Scribble mode."
;; :group 'scribble)
;; (defface scribble-quickref-at-face
;; '((t :inherit scribble-quickref-literal-face))
;; "Face for Quickref !!! in Scribble mode."
;; :group 'scribble)
;; (defface scribble-quickref-keyword-face
;; '((t :family "DejaVu Sans"
;; :inherit scribble-quickref-literal-face))
;; "Face for Quickref !!! in Scribble mode."
;; :group 'scribble)
;; (defface scribble-quickref-form-name-face
;; '((t :inherit scribble-quickref-default-face
;; :foreground "blue4"
;; :weight bold))
;; "Face for Quickref !!! in Scribble mode."
;; :group 'scribble)
;; (defface scribble-quickref-type-face
;; '((t :inherit scribble-quickref-default-face
;; :foreground "blue")) ;!!!
;; "Face for Quickref !!! in Scribble mode."
;; :group 'scribble)
;; (defface scribble-quickref-arg-face
;; '((t :inherit scribble-quickref-default-face
;; :slant italic
;; :foreground "blue2"))
;; "Face for Quickref !!! in Scribble mode."
;; :group 'scribble)
;; ElDoc Faces:
(defface scribble-eldoc-default-face
'((t :family "DejaVu Sans"
:foreground "#000000"
:background "#fffff0"
:weight normal
:slant normal))
"Face for ElDoc in Scribble mode."
:group 'scribble)
(defface scribble-eldoc-literal-face
'((t :inherit scribble-eldoc-default-face))
"Face for ElDoc literals in Scribble mode."
:group 'scribble)
(defface scribble-eldoc-meta-face
'((t :inherit scribble-eldoc-default-face
:foreground "#4040ff"))
"Face for ElDoc metasyntactic symbols in Scribble mode."
:group 'scribble)
(defface scribble-eldoc-nonterminal-face
'((t :inherit scribble-eldoc-default-face
:slant italic))
"Face for ElDoc nonterminals in Scribble mode."
:group 'scribble)
;;---------------------------------------------------- Customization Procedures
(defun scribble-customize ()
(interactive)
(customize-group 'scribble))
;; TODO: Implement making any non-face changes take effect.
;;-------------------------------------------------------------- Scribble Forms
(defsubst scribble-get-form-name (form) (aref form 0))
(defsubst scribble-get-form-args (form) (aref form 1))
(defsubst scribble-get-form-type (form) (aref form 2))
(defsubst scribble-get-form-face (form) (aref form 3))
;; TODO: Maybe make types be strings.
(defsubst scribble-get-formarg-kind (arg) (aref arg 0))
(defsubst scribble-get-formarg-name (arg) (aref arg 1))
(defsubst scribble-get-formarg-type (arg) (aref arg 2))
(defsubst scribble-get-formarg-default (arg) (aref arg 3))
;; Note: The information is from Racket 5.0.2 Scribble documentation.
(defconst scribble-style-form-type
'(or/c style? f string? symbol? (listof symbol?)))
(defconst scribble-optional-style-form-arg
`[k style ,scribble-style-form-type "#f"])
(defconst scribble-optional-underline?-form-arg
`[k underline? any/c "#t"])
(defconst scribble-optional-tag-form-arg
`[k tag (or/c false/c string? (listof string?)) "#f"])
(defconst scribble-heading-form-args
`(,scribble-optional-tag-form-arg
[k tag-prefix (or/c false/c string? module-path?) "#f"]
,scribble-optional-style-form-arg))
;; TODO: Support more than just "scribble/base".
(defconst scribble-base-forms
`(
;; scribble/base
;; file:///usr/local/racket-5.0.2/share/racket/doc/scribble/base.html
[title (,@scribble-heading-form-args
[k version (or/c string? false/c) "#f"]
[p - pre-content? -]
...)
title-decl?
scribble-title-face]
[section (,@scribble-heading-form-args
[p - pre-content? -]
...)
part-start?
scribble-section-heading-face]
[subsection (,@scribble-heading-form-args
[p - pre-content? -]
...)
part-start?
scribble-subsection-heading-face]
[subsubsection (,@scribble-heading-form-args
[p - pre-content? -]
...)
part-start?
scribble-subsubsection-heading-face]
[sub*section (,@scribble-heading-form-args
[p - pre-content? -]
...)
part-start?
scribble-sub*section-heading-face]
[author ([p - content? -])
block?
nil]
[author+email ([p author elem -]
[p email string? -]
[k obfuscate? any/c "#f"])
element?
nil]
[para (,scribble-optional-style-form-arg
[p - pre-content? -]
...)
paragraph?
nil]
[nested (,scribble-optional-style-form-arg
[p - pre-flow? -]
...)
nested-flow?
nil]
[centered ([p - pre-flow? -]
...)
nested-flow?
nil]
[margin-note ([p - pre-flow? -]
...)
block?
nil]
[margin-note* ([p - pre-content? -]
...)
element?
nil]
[itemlist ([p - item? -]
...
,scribble-optional-style-form-arg)
itemization?
nil]
[item ([p - pre-flow? -]
...)
item?
nil]
[tabular ([p cells (listof (listof (or/c block? content? 'cont))) -]
,scribble-optional-style-form-arg)
table?
nil]
[verbatim ([p indent exact-nonnegative-integer? "0"]
[p - string? -]
...)
block?
nil]
[elem ([p - pre-content? -]
...
,scribble-optional-style-form-arg)
element?
nil]
[italic ([p - pre-content? -]
...)
element?
nil]
[bold ([p - pre-content? -]
...)
element?
scribble-bold-face]
[code ([p - pre-content? -]
...)
element?
nil]
[codeblock ([p - pre-content? -]
...)
element?
nil]
[tt ([p - pre-content? -]
...)
element?
scribble-tt-face]
[subscript ([p - pre-content? -]
...)
element?
nil]
[superscript ([p - pre-content? -]
...)
element?
nil]
[smaller ([p - pre-content? -]
...)
element?
nil]
[larger ([p - pre-content? -]
...)
element?
nil]
[emph ([p - pre-content? -]
...)
element?
scribble-emph-face]
[linebreak ()
element?
nil]
[hspace ([p - exact-nonnegative-integer? -])
element?
nil]
[literal ([p - string? -]
...)
element?
nil]
[image
([p path (or/c path-string? (cons/c 'collects (listof bytes?))) -]
[k scale real? "1.0"]
[k suffixes (listof (rx "^[.]")) "null"]
[p - pre-content? -]
...)
element?
nil]
[hyperlink ([p url string? -]
[p - pre-content? -]
...
,scribble-optional-underline?-form-arg
[k style
(or/c style? string? symbol? false/c)
"(if underline? #f \"plainlink\")"])
element?
scribble-link-text-face]
[url ([p dest string? -])
element?
nil]
[secref ([p tag string -]
[k doc (or/c module-path? false/c) "#f"]
[k tag-prefixes (or/c (listof string?) false/c) "#f"]
,scribble-optional-underline?-form-arg)
element?
scribble-link-text-face]
[seclink ([p tag string? -]
[k doc (or/c module-path? false/c) "#f"]
[k tag-prefixes (or/c (listof string?) false/c) "#f"]
,scribble-optional-underline?-form-arg
[p - pre-content? -]
...)
element?
scribble-link-text-face]
[other-doc ([p module-path module-path? -]
,scribble-optional-underline?-form-arg)
element?
nil]
[elemtag ([p t (or/c tag? string?) -]
[p - pre-content? -]
...)
element?
nil]
[elemref ([p t (or/c tag? string?) -]
[p - pre-content? -]
...
,scribble-optional-underline?-form-arg)
element?
nil]
[index ([p words (or/c string? (listof string?)) -]
[p - pre-content? -]
...)
index-element?
nil]
[index* ([p words (listof string?) -]
[p word-contents (listof list?) -]
[p - pre-content? -]
...)
index-element?
nil]
[as-index ([p - pre-content? -]
...)
index-element?
nil]
[section-index ([p word string? -]
...)
part-index-decl?
nil]
[index-section ([k tag (or/c false/c string?) "\"doc-index\""]
,scribble-optional-tag-form-arg)
part?
nil]
[table-of-contents ()
delayed-block?
nil]
[local-table-of-contents (,scribble-optional-style-form-arg)
delayed-block?
nil]))
(defconst scribble-manual-forms
`(
[racketblock ([p - datum? -] ...) any/c nil]
[RACKETBLOCK ([p - datum? -] ...) any/c nil]
[racketblock0 ([p - datum? -] ...) any/c nil]
[RACKETBLOCK0 ([p - datum? -] ...) any/c nil]
[racketresultblock ([p - datum? -] ...) any/c nil]
[RACKETRESULTBLOCK ([p - datum? -] ...) any/c nil]
[racketresultblock0 ([p - datum? -] ...) any/c nil]
[RACKETRESULTBLOCK0 ([p - datum? -] ...) any/c nil]
[racketinput ([p - datum? -] ...) any/c nil]
[RACKETINPUT ([p - datum? -] ...) any/c nil]
[racketmod ([k file string? "#f"]
[p - datum? -]
...)
any/c
nil]
[racket ([p - datum? -] ...) any/c nil]
[RACKET ([p - datum? -] ...) any/c nil]
;; TODO: For "racketresult" and "racketid", maybe override body faces.
[racketresult ([p - datum? -] ...) any/c scribble-racket-result-face]
[racketid ([p - datum? -] ...) any/c nil]
[racketmodlink ([p - datum? -]
[p - pre-content-expr? -]
...)
any/c
nil]
[litchar ([p - string? -] ...) element? scribble-racket-litchar-face]
[racketfont ([p - pre-content? -] ...) element? nil]
[racketvalfont ([p - pre-content? -] ...) element? scribble-racket-value-face]
[racketresultfont ([p - pre-content? -] ...) element? scribble-racket-result-face]
[racketidfont ([p - pre-content? -] ...) element? nil]
[racketvarfont ([p - pre-content? -] ...) element? nil]
[racketkeywordfont ([p - pre-content? -] ...) element? nil]
[racketparenfont ([p - pre-content? -] ...) element? nil]
[racketmetafont ([p - pre-content? -] ...) element? nil]
[racketerror ([p - pre-content? -] ...) element? nil]
[racketmodfont ([p - pre-content? -] ...) element? nil]
[racketoutputfont ([p - pre-content? -] ...) element? nil]
;; TODO: Define faces for some of the above?
;; TODO: We could use overlays to make "@procedure" content have
;; "#<procedure:" and ">".
[procedure ([p - pre-content? -] ...) element? nil]
[var ([p - datum? -] ...) element? nil]
[svar ([p - datum? -] ...) element? nil]
;; TODO: Possibly add support for all the "scheme" backword-compatibility
;; identifiers.
;; TODO: !!! CONTINUE ADDING AFTER
;; file:///usr/local/racket-5.0.2/share/racket/doc/scribble/scribble_manual_code.html
))
(defconst scribble-bnf-forms
`([BNF ([p - (cons element? (listof element?)) -] ...) table? nil]
[nonterm ([p - pre-content? -] ...) element? scribble-bnf-nonterm-face]
[BNF-seq ([p - element? -] ...) element? nil]
[BNF-group ([p - pre-content? -] ...) element? nil]
[optional ([p - pre-content? -] ...) element? nil]
[kleenestar ([p - pre-content? -] ...) element? nil]
[kleeneplus ([p - pre-content? -] ...) element? nil]
[kleenerange ([p n any/c -]
[p m any/c -]
[p - pre-content? -]
...)
element?
nil]
[BNF-alt ([p - element? -] ...) element? nil]
[BNF-etc ([p - string? -]) void scribble-comment-face]))
(defconst scribble-forms
`(,@scribble-base-forms
,@scribble-manual-forms
,@scribble-bnf-forms))
(defconst scribble-name-string-to-form-hash
(let ((ht (make-hash-table :test 'equal)))
(mapc (lambda (form)
(puthash (symbol-name (scribble-get-form-name form)) form ht))
scribble-forms)
ht))
(defun scribble-minus-to-nil (x)
(if (eq x '-) nil x))
(defun scribble-form-type-to-racket (type)
;; TODO: translate true, false, rx, quote. Or convert types to strings.
(format "%s" type))
;;---------------------------------------------------------------- Syntax Table
(defvar scribble-mode-syntax-table
(let ((st (make-syntax-table)))
(modify-syntax-entry ? " " st)
(modify-syntax-entry ?\t " " st)
(modify-syntax-entry ?\f " " st)
(modify-syntax-entry ?\n ">" st)
(modify-syntax-entry ?\" "." st)
(modify-syntax-entry ?\@ "_ 1" st)
(modify-syntax-entry ?\# "_ 1" st)
(modify-syntax-entry ?\: "_ 1" st)
(modify-syntax-entry ?\; ". 2" st)
(modify-syntax-entry ?\( "()" st)
(modify-syntax-entry ?\) ")(" st)
(modify-syntax-entry ?\[ "(]" st)
(modify-syntax-entry ?\] ")[" st)
(modify-syntax-entry ?\{ "(}" st)
(modify-syntax-entry ?\} "){" st)
st))
;;------------------------------------------------------------- Forms Reference
;; (defun scribble-forms-reference ()
;; (interactive)
;; (scribble-buffer-display
;; "*Scribble Forms Reference*"
;; (function
;; (lambda ()
;; (mapc (lambda (form)
;; (insert (scribble-form-quickref-text form) "\n\n"))
;; scribble-forms)))))
;;------------------------------------------------------------------- Mode Menu
(defvar scribble-mode-menu
(let ((km (make-sparse-keymap "Scribble")))
(define-key km [scribble-about]
'(menu-item "About Scribble Emacs Mode..." scribble-about
:help "See information about Scribble Emacs mode"))
(define-key km [scribble-browse-mode-web]
'(menu-item "Browse Scribble Emacs Mode Web..." scribble-browse-mode-web
:help "Browse Web pages for Scribble Emacs mode"))
;; TODO: Browse/search scribble docs.
(define-key km [scribble-separator-2]
`(menu-item "--"))
;; TODO: Experiment with this. Use local variable "scribble-modules" to
;; specify in files, and update it programmatically from this interface.
;; Can also have operation that scans "#lang" and "@(require" to determine
;; or update this variable. Unfortunately can't use "-*-" line because
;; Emacs currently (GNU 23.2) doesn't support it occuring after the "#lang"
;; line.
(define-key km [scribble-forms-bnf]
'(menu-item "scribble/bnf" nil
:enable nil
:button (:toggle . t)
:help "!!!"))
(define-key km [scribble-forms-manual]
'(menu-item "scribble/manual" nil
:enable nil
:button (:toggle . nil)
:help "!!!"))
(define-key km [scribble-forms-base]
'(menu-item "scribble/base" nil
:enable nil
:button (:toggle . t)
:help "!!!"))
;; (define-key km [scribble-forms-reference]
;; '(menu-item "Forms Reference..." scribble-forms-reference
;; :help "Show a reference of Scribble forms"))
(define-key km [scribble-separator-1]
`(menu-item "--"))
(define-key km [scribble-customize]
'(menu-item "Customize..." scribble-customize
:help "Set Emacs customization options for Scribble"))
;; TODO: Perhaps wrapping "completion-at-point" with another procedure
;; that does "call-interactively" will work.
;;
;; (define-key km [scribble-completion-at-point]
;; `(menu-item "Completion at Point" 'completion-at-point))
km))
;;---------------------------------------------------------------------- Keymap
(defvar scribble-mode-map
(let ((km (make-sparse-keymap)))
(define-key km (kbd "M-TAB") 'completion-at-point)
(define-key km [menu-bar Scribble] (cons "Scribble" scribble-mode-menu))
km))
;;------------------------------------------------------------------- Font-Lock
;; TODO: Should probably switch to using something other than font-lock, like
;; handwritten fuzzy parser.
(defvar scribble-font-lock-keywords
`(
,@(mapcar (lambda (face-to-namerxs-pair)
(let ((face (car face-to-namerxs-pair)))
`(,(concat "\\(" ; < 1 at-name
"@"
(let ((names (cdr face-to-namerxs-pair)))
(if (cdr names)
(regexp-opt names)
(car names)))
"\\)" ; > 1 at-name
"\\b"
"\\(?:" ; < opt-squares
"\\(\\[\\)" ; = 2 open-square
"[^]]*" ;
"\\(\\]\\)" ; = 3 close-square
"\\)?" ; > opt-squares
"\\(?:" ; < opt-curlies
"\\({\\)" ; ; = 4 open-curly
(if face "\\(" "")
"[^}]*" ;
(if face "\\)" "")
"\\(}\\)" ; = 5,6 close-curly
"\\)?" ; > opt-curlies
)
(1 'scribble-at-keyword-face)
(2 'scribble-square-bracket-face nil t)
(3 'scribble-square-bracket-face nil t)
(4 'scribble-curly-brace-face nil t)
,@(if face
`((5 (quote ,face) nil t))
'())
(,(if face 6 5) 'scribble-curly-brace-face nil t))))
(let ((face-to-namerxs-alist '()))
(mapc (lambda (form)
(let* ((namerx (regexp-quote
(symbol-name (scribble-get-form-name
form))))
(face (scribble-get-form-face form))
(pair (assq face face-to-namerxs-alist)))
(if pair
(setcdr pair (cons namerx (cdr pair)))
(setq face-to-namerxs-alist
`((,face . (,namerx))
,@face-to-namerxs-alist)))))
scribble-forms)
face-to-namerxs-alist))
("\\(@\\)\\(PLaneT\\)"
(1 'scribble-at-keyword-face)
(2 'scribble-planet-face))
;; TODO: Is this right?
;;
;; ("\\(@\\)@"
;; (1 'scribble-at-keyword-face))
("\\(@#reader\\)[ ]+\\([^\r\n]+\\)"
(1 'scribble-at-keyword-face)
(2 'scribble-at-keyword-face))
("\\`\\(#lang\\)[ ]+\\([^\r\n]+\\)"
(1 'scribble-at-keyword-face)
(2 'scribble-at-keyword-face))))
(defvar scribble-font-lock-defaults
`(scribble-font-lock-keywords nil))
;;----------------------------------------------------------------------- Imenu
(defvar scribble-imenu-generic-expression
'((nil
"@\\(?:sub\\(?:sub\\)?\\)?section\\(?:\\[[^]]*\\]\\)?{\\([^}]*\\)"
1)))
;; TODO: Use one of the lower-level ways of doing this.
;;------------------------------------------------------------------ Characters
(defconst scribble-right-arrow-char 8594)
;;-------------------------------------------------------------------- Quickref
;; TODO: !!! this needs work since eldoc was forked out of this.
;;
;; (defun scribble-form-type-to-quickref (type)
;; (propertize (scribble-form-type-to-racket type)
;; 'face 'scribble-quickref-type-face))
;;
;; (defconst scribble-quickref-dotdotdot
;; (propertize "..." 'face 'scribble-quickref-meta-face))
;;
;; (defconst scribble-quickref-space
;; (propertize " " 'face 'scribble-quickref-default-face))
;;
;; (defconst scribble-quickref-open-paren
;; (propertize "(" 'face 'scribble-quickref-literal-face))
;;
;; (defconst scribble-quickref-close-paren
;; (propertize ")" 'face 'scribble-quickref-literal-face))
;;
;; (defconst scribble-quickref-open-optional
;; (propertize "[" 'face 'scribble-quickref-meta-face))
;;
;; (defconst scribble-quickref-close-optional
;; (propertize "]" 'face 'scribble-quickref-meta-face))
;;
;; (defconst scribble-quickref-space-colon-space
;; (concat scribble-quickref-space
;; (propertize ":" 'face 'scribble-quickref-meta-face)
;; scribble-quickref-space))
;;
;; (defconst scribble-quickref-space-equal-space
;; (concat scribble-quickref-space
;; (propertize "=" 'face 'scribble-quickref-meta-face)
;; scribble-quickref-space))
;;
;; (defun scribble-form-arg-to-quickref (arg)
;; (if (eq arg '...)
;; scribble-quickref-dotdotdot
;; (let* ((fmt '())
;; (kind (scribble-get-formarg-kind arg))
;; (name (scribble-minus-to-nil
;; (scribble-get-formarg-name arg)))
;; (type (scribble-get-formarg-type arg))
;; (default (scribble-minus-to-nil
;; (scribble-get-formarg-default arg)))
;; (name-str (if name (symbol-name name) nil))
;; (type-str (scribble-form-type-to-racket type))
;; (type-ps (propertize type-str
;; 'face 'scribble-quickref-type-face)))
;; (cond ((eq kind 'p)
;; (setq fmt
;; (if name-str
;; `(,type-ps
;; ,scribble-quickref-space-colon-space
;; ,(propertize name-str
;; 'face
;; 'scribble-quickref-arg-face))
;; `(,type-ps))))
;; ((eq kind 'k)
;; (or name-str
;; (error "formarg kind k must have name in %s" arg))
;; (setq fmt
;; `(,type-ps
;; ,scribble-quickref-space-colon-space
;; ,(propertize (concat "#:" name-str)
;; 'face 'scribble-quickref-keyword-face))))
;; (t (error "invalid formarg kind in %s" arg)))
;; (apply 'concat
;; (if default
;; (reverse
;; `(,(propertize default 'face 'scribble-quickref-value-face)
;; ,scribble-quickref-space-equal-space
;; ,@fmt))
;; (reverse fmt))))))
;;
;; (defun scribble-form-quickref-text (form)
;; ;; TODO: !!! working on this
;; (let* ((at (propertize "@" 'face 'scribble-quickref-at-face))
;; (space-arrow-space
;; (concat scribble-quickref-space
;; (propertize (string scribble-right-arrow-char)
;; 'face
;; 'scribble-quickref-meta-face)
;; scribble-quickref-space)))
;; (apply
;; 'concat
;; `(,at
;; ,(propertize (symbol-name (scribble-get-form-name form))
;; 'face 'scribble-quickref-form-name-face)
;; ,@(let ((args (scribble-get-form-args form)))
;; (if args
;; (let ((fmt '()))
;; (mapc (lambda (arg)
;; (setq fmt `(,(scribble-form-arg-to-quickref arg)
;; ,scribble-quickref-space
;; ,@fmt)))
;; args)
;; (reverse fmt))
;; '()))
;; ,closeparen-space-arrow-space
;; ,(scribble-form-type-to-quickref (scribble-get-form-type form))))))
;;------------------------------------------------------------------------ ElDoc
(defconst scribble-eldoc-dotdotdot
(propertize "..." 'face 'scribble-eldoc-meta-face))
(defconst scribble-eldoc-space
(propertize " " 'face 'scribble-eldoc-default-face))
(defconst scribble-eldoc-open-paren
(propertize "(" 'face 'scribble-eldoc-literal-face))
(defconst scribble-eldoc-close-paren
(propertize ")" 'face 'scribble-eldoc-literal-face))
(defconst scribble-eldoc-open-optional
(propertize "[" 'face 'scribble-eldoc-meta-face))
(defconst scribble-eldoc-close-optional
(propertize "]" 'face 'scribble-eldoc-meta-face))
(defconst scribble-eldoc-closeparen-space-arrow-space
(concat scribble-eldoc-close-paren
scribble-eldoc-space
(propertize (string scribble-right-arrow-char)
'face
'scribble-eldoc-meta-face)
scribble-eldoc-space))
(defun scribble-form-arg-to-eldoc (arg)
(if (eq arg '...)
scribble-eldoc-dotdotdot
(let* ((kind (scribble-get-formarg-kind arg))
(name (scribble-minus-to-nil (scribble-get-formarg-name arg)))
(name-str (if name (symbol-name name) nil))
(id-ps
(cond ((eq kind 'p)
(propertize (or name-str
(scribble-form-type-to-racket
(scribble-get-formarg-type arg)))
'face
'scribble-eldoc-nonterminal-face))
((eq kind 'k)
(propertize
(concat "#:"
(or name-str
(error "formarg kind k must have name in %s"
arg)))
'face 'scribble-eldoc-literal-face))
(t (error "invalid formarg kind in %s" arg)))))
(if (eq '- (scribble-get-formarg-default arg))
id-ps
(concat scribble-eldoc-open-optional
id-ps
scribble-eldoc-close-optional)))))
(defun scribble-make-form-eldoc-message (form)
(apply
'concat
`(,scribble-eldoc-open-paren
,(propertize (symbol-name (scribble-get-form-name form))
'face 'scribble-eldoc-literal-face)
,@(let ((args (scribble-get-form-args form)))
(if args
(let ((fmt '()))
(mapc (lambda (arg)
(setq fmt `(,(scribble-form-arg-to-eldoc arg)
,scribble-eldoc-space
,@fmt)))
args)
(reverse fmt))
'()))
,scribble-eldoc-closeparen-space-arrow-space
,(propertize (scribble-form-type-to-racket (scribble-get-form-type form))
'face
'scribble-eldoc-nonterminal-face))))
(defconst scribble-name-to-eldoc-message-hash
(let ((ht (make-hash-table :test 'equal)))
(mapc (lambda (form)
(puthash (symbol-name (scribble-get-form-name form))
(scribble-make-form-eldoc-message form)
ht))
scribble-forms)
ht))
(defun scribble-get-form-eldoc-message (form)
(scribble-make-form-eldoc-message form))
(defun scribble-eldoc-documentation-function ()
;; TODO: Implement this properly, figuring out which form we're in. Might
;; wait until we implement better fontification, using the same parsing
;; mechanism.
;;
;; TODO: !!! Maybe look for "@"-name under point, and if that fails, then
;; up-sexp, and then look for an @-name to left of point (or left-sexp if
;; there is a square-bracket to left of point.
(let ((word (let ((c (char-after (point))))
(and c
(memq (char-syntax c) '(?w ?_))
(current-word)))))
(and (> (length word) 0)
(equal ?@ (aref word 0))
(gethash (substring word 1)
scribble-name-to-eldoc-message-hash
nil))))
;;------------------------------------------------------------------ Completion
(defun scribble-build-completion-table ()
(sort (let ((kw-seen-hash (make-hash-table :test 'eq))
(result '("Felleisen")))
(mapc (lambda (form)
(setq result (cons (symbol-name (scribble-get-form-name form))
result))
(mapc (lambda (arg)
(and (vectorp arg)
(eq 'k (scribble-get-formarg-kind arg))
(let ((sym (scribble-get-formarg-name arg)))
(or (gethash sym kw-seen-hash)
(progn (puthash sym t kw-seen-hash)
(setq result
(cons (concat
"#:"
(symbol-name sym))
result)))))))
(scribble-get-form-args form)))
scribble-forms)
result)
'string<))
(defconst scribble-completion-table (scribble-build-completion-table))
(defconst scribble-completion-at-point-tail
(list scribble-completion-table
;; :annotation-function 'scribble-completion-annotate-function
))
(defun scribble-completion-at-point-function ()
(let* ((end (point))
(start (let ((c (char-before end)))
(if (and c (memq (char-syntax c) '(?w ?_)))
(save-excursion
(backward-sexp 1)
(and (eq ?\@ (char-after (point)))
(forward-char 1))
(point))
end))))
`(,start ,end ,@scribble-completion-at-point-tail)))
;;-------------------------------------------------------------- Buffer Display
(defun scribble-buffer-display (name proc)
(save-excursion
(set-buffer (get-buffer-create name))
(toggle-read-only 0)
(buffer-disable-undo)
(goto-char (point-min))
(delete-region (point-min) (point-max))
(funcall proc)
(buffer-enable-undo)
(set-buffer-modified-p nil)
(toggle-read-only 1)
(goto-char (point-min))
;; TODO: Maybe make "q" restore window config.
(local-set-key "q" 'scribble-quit-buffer-display)
(pop-to-buffer (current-buffer))
(message "Press \"q\" to quit the \"%s\" buffer." name)))
(defun scribble-quit-buffer-display ()
(interactive)
(let ((buf (current-buffer)))
(condition-case nil
(delete-window)
(error nil))
(kill-buffer buf)))
;;---------------------------------------------------------------- Web Browsing
(defun scribble-browse-mode-web ()
(interactive)
(browse-url scribble-mode-web))
;; TODO: Add browsing to Scribble documentation. First try to find URLs to
;; their existing documentation (based "~/.racket" and on dir of "racket"
;; command). Fall back to public Web version.
;;----------------------------------------------------------------------- About
(defun scribble-about ()
(interactive)
(scribble-buffer-display
"*About Scribble Mode*"
(function
(lambda ()
(insert (propertize scribble-mode-title 'face 'bold)
"\n\n"
"Version "
(propertize scribble-mode-version 'face 'bold)
"\n\n"
(propertize scribble-mode-web 'face 'bold)
"\n\n"
scribble-mode-legal-notices
"\n")))))
;;--------------------------------------------------------------- Mode Function
;;;###autoload
(defun scribble-mode ()
"!!! `\[COMMAND]', `\{KEYMAP}', and `\<KEYMAP>'"
(interactive)
(kill-all-local-variables)
(use-local-map scribble-mode-map)
(set-syntax-table scribble-mode-syntax-table)
(setq fill-column 79)
;; Comments:
(set (make-local-variable 'comment-start) "@;")
(set (make-local-variable 'comment-end) "")
(set (make-local-variable 'comment-padding) 1)
(set (make-local-variable 'comment-multi-line) nil)
(set (make-local-variable 'comment-start-skip)
"\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\);+ *")
;; TODO: comment-indent-function
;; TODO: indent-line-function
;; TODO: abbrev
;; Font-Lock:
(set (make-local-variable 'font-lock-defaults) scribble-font-lock-defaults)
;; Imenu:
(set (make-local-variable 'imenu-generic-expression)
scribble-imenu-generic-expression)
(set (make-local-variable 'imenu-case-fold-search) nil)
(imenu-add-to-menubar "Imenu")
;; ElDoc:
(set (make-local-variable 'eldoc-documentation-function)
'scribble-eldoc-documentation-function)
(turn-on-eldoc-mode)
;; Completion:
(set (make-local-variable 'completion-at-point-functions)
(list 'scribble-completion-at-point-function))
;; Mode Identification:
(setq mode-name "Scribble")
(setq major-mode 'scribble-mode)
;; Hooks:
(run-mode-hooks 'scribble-mode-hook))
;;------------------------------------------------------------------- Auto-Mode
;;;###autoload
(mapc (lambda (pair)
(or (assoc (car pair) auto-mode-alist)
(push pair auto-mode-alist)))
'(("\\.scrbl\\'" . scribble-mode)))
;;--------------------------------------------------------------------- Provide
(provide 'scribble)
;;; scribble.el ends here