├── .gitignore ├── README.md ├── highlight-lisp.js ├── package.json └── themes ├── dark.css ├── github.css ├── vestigial.css └── wookie.css /.gitignore: -------------------------------------------------------------------------------- 1 | test/* 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | highlight-lisp - Common Lisp syntax highlighter 2 | =============================================== 3 | This is a syntax highlighter for Common Lisp written in Javascript. It is 4 | completely themable via CSS (themes included). 5 | 6 | The purpose of this is to make it really easy to embed beautiful Common Lisp 7 | code into a website with minimal effort. 8 | 9 | [See the demo!](http://orthecreedence.github.com/highlight-lisp/) 10 | 11 | Usage 12 | ----- 13 | Usage is simple. You include `highlight-lisp.js`, link to one of the CSS themes, 14 | and call one of highlight-lisp's highlighting functions: 15 | 16 | ```html 17 | 19 | 20 | 21 | 22 | ... 23 | 24 | 25 |
(defun test-syntax-highlighter ()
26 | "Docstring explaining what this function does."
27 | (let ((hash (make-hash-table :test #'equal)))
28 | ...))
29 | ```
30 |
31 | Once the HTML is set up, there are a few ways to initialize highlighting:
32 |
33 | ```js
34 | // automatically highlight all ...
blocks
35 | HighlightLisp.highlight_auto();
36 |
37 | // specify a custom class name (instead of "lisp"):
38 | HighlightLisp.highlight_auto({className: 'common-lisp'});
39 |
40 | // highlight *every* code block
41 | HighlightLisp.highlight_auto({className: null});
42 |
43 | // manually highlight a code block
44 | var code = document.getElementById('my-code-element');
45 | HighlightLisp.highlight_element(code);
46 | ```
47 |
48 | Paren matching
49 | --------------
50 |
51 | You can now enable paren matching (on mouse hover):
52 |
53 | ```js
54 | HighlightLisp.paren_match();
55 | ```
56 |
57 | This will go through all highlighted blocks of code and add mouseover/mouseout
58 | event listeners to all ('s and )'s that highlight the matching paren on hover.
59 |
60 | What gets highlighted
61 | ---------------------
62 | - **Functions**
63 | CSS class `function`
64 | Anything starting with `(`: `(my-function ...)`
65 | - **Known functions**
66 | CSS class `function known`
67 | Any function known by the highlighter: things like `make-hash-table`, `when`,
68 | `format`, etc
69 | - **Special functions**
70 | CSS class `function known special`
71 | Mainly `let`, `let\*`, `lambda`.
72 | - **Symbol functions**
73 | CSS class `function symbol`
74 | Example: `#'my-function`
75 | - **Known symbol functions**
76 | CSS class `function symbol known`
77 | Examples: `#'equalp`, `#'format`
78 | - **Keywords**
79 | CSS class `keyword`
80 | Anything starting with `:` like `:this-is-a-keyword `
81 | - **Known keywords**
82 | CSS class `keyword known`
83 | Known keywords are things like `:hash-keys`, `:supersede`, etc.
84 | - **Symbols**
85 | CSS class `symbol`
86 | Anything starting with `'`: `'my-symbol`
87 | - **Lambda-list operators**
88 | CSS class `lambda-list`
89 | Things like `&key`, `&body`, etc.
90 | - **Numbers**
91 | CSS class `number`
92 | Any numbers: `69`, `-82.4`, `#xF047`, `#b11010`
93 | - **Integers**
94 | CSS class `number integer`
95 | Simple numbers: `42`, `867`, etc. (no decimals)
96 | - **Ratios**
97 | CSS class `number ratio`
98 | Examples: `80/9`, `23/4`
99 | - **Floats**
100 | CSS class `number float`
101 | Numbers with a decimal: `+47.82112`, `32.9` `3.` `.009`
102 | - **Hex**
103 | CSS class `number hex`
104 | Hex numbers: `#x8090`, `#xc001`
105 | - **Binary**
106 | CSS class `number binary`
107 | Example: `#b01101`
108 | - **Variables**
109 | By themselves, variables remain unhighlighted
110 | - **Known variables**
111 | CSS class `variable known`
112 | Examples: `*package*`, `*standard-output*`, etc
113 | - **Global variables**
114 | CSS class `variable global`
115 | Any symbol surrounded by `\*`: `*main-datastore*`, `*my-thread-local*`, etc
116 | - **Constants**
117 | CSS class `variable constant`
118 | Any symbol surrounded by `+`: `+dt+`, `+contant-time+`, etc
119 | - **nil/t**
120 | CSS class `nil`
121 | Any standalone `nil` or `t` will get this class
122 | - **Comments**
123 | CSS class `comment`
124 | Example: `; this is a comment`
125 | - **Strings**
126 | CSS class `string`
127 | Anthing inside `"`: `"This is a string."`
128 | - **Parens**
129 | CSS class `list`
130 | May be overkill, but any `(` or `)` characters are classified.
131 |
132 | On that note, things that *don't get highlighted/aren't properly highlighted*:
133 |
134 | - Variables...things like `let` bindings or other symols within code that would
135 | be interpreted as variables. Highlighting these would most likely be prohibitive
136 | in terms of time (not the mention the return on investment). Feel free to patch!
137 | - Some number notations. For instance `0.44d0`.
138 | - Multi-line comments `#| ... |#` are unsupported
139 | - Many constants (such as `pi`, `internal-time-units-per-second`) are classified
140 | as functions, not known variables. This is because I pulled the list out of my
141 | vim highlight script, and couldn't find a list of "Common Lisp standard
142 | variables" to cross reference with. I pulled out the ones I know of and put them
143 | into the known variables list, but there are no doubt more. If you see something
144 | that is a known variable but gets treated as a known function, please open a
145 | github issue.
146 |
147 | Why
148 | ---
149 | > Aren't there a bunch of Javascript syntax highlighters out there already?
150 |
151 | Yes, but truth be told, most ignore lisp. You can write custom parsers for some
152 | of them, but the APIs they provide didn't work well enough for me. [highlight.js](http://softwaremaniacs.org/soft/highlight/en/)
153 | has a very nice lisp highlighting mode, along with really nice themes, but I
154 | wanted more control over the process.
155 |
156 | For instance, `highlight-lisp` started as a [SyntaxHighlighter](http://alexgorbatchev.com/SyntaxHighlighter/)
157 | brush, but I quickly realized that because of the limitations of Javascript not
158 | allowing real [lookbehind regular expressions](http://www.regular-expressions.info/lookaround.html),
159 | I needed more direct control over the search/replace process.
160 |
161 | What I discovered was that given the proper tools, parsing lisp is *easy*
162 | (in fact, a cake walk after just releasing [markdown.cl](https://github.com/orthecreedence/markdown.cl))
163 | and there's no need for a big highlighting framework. You plug in some regexes,
164 | slap some tags around certain things, and call it a day.
165 |
166 | License
167 | -------
168 | As always, MIT.
169 |
170 |
171 |
--------------------------------------------------------------------------------
/highlight-lisp.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Common Lisp syntax highlighter
3 | *
4 | * @version 0.1.1
5 | * @author Andrew "Danger" Lyon
6 | * @copyright Lyon Bros. Enterprises, LLC
7 | * @licence MIT
8 | */
9 | var highlight_lisp = function() {
10 | // all of the following functions were pulled straight from my syntax/lisp.vim
11 | // file in my vim directory.
12 | var funcs =
13 | '\\* find-method pprint-indent find-package pprint-linear find-restart ' +
14 | 'pprint-logical-block \\+ find-symbol pprint-newline finish-output ' +
15 | 'pprint-pop first pprint-tab - fixnum pprint-tabular / flet prin1 // float ' +
16 | 'prin1-to-string /// float-digits princ /= float-precision princ-to-string 1\\+ ' +
17 | 'float-radix print 1- float-sign print-not-readable < floating-point-inexact ' +
18 | 'print-not-readable-object <= floating-point-invalid-operation print-object = ' +
19 | 'floating-point-overflow print-unreadable-object > floating-point-underflow ' +
20 | 'probe-file >= floatp proclaim abort floor prog abs fmakunbound prog\\* access ' +
21 | 'force-output prog1 acons format prog2 acos formatter progn acosh fourth ' +
22 | 'program-error add-method fresh-line progv adjoin fround provide adjust-array ' +
23 | 'ftruncate psetf adjustable-array-p ftype psetq allocate-instance funcall push ' +
24 | 'alpha-char-p function pushnew alphanumericp function-keywords putprop and ' +
25 | 'function-lambda-expression quote append functionp random apply gbitp ' +
26 | 'random-state applyhook gcd random-state-p apropos generic-function rassoc ' +
27 | 'apropos-list gensym rassoc-if aref gentemp rassoc-if-not arithmetic-error get ' +
28 | 'ratio arithmetic-error-operands get-decoded-time rational ' +
29 | 'arithmetic-error-operation get-dispatch-macro-character rationalize array ' +
30 | 'get-internal-real-time rationalp array-dimension get-internal-run-time read ' +
31 | 'array-dimension-limit get-macro-character read-byte array-dimensions ' +
32 | 'get-output-stream-string read-char array-displacement get-properties ' +
33 | 'read-char-no-hang array-element-type get-setf-expansion read-delimited-list ' +
34 | 'array-has-fill-pointer-p get-setf-method read-eval-print array-in-bounds-p ' +
35 | 'get-universal-time read-from-string array-rank getf read-line array-rank-limit ' +
36 | 'gethash read-preserving-whitespace array-row-major-index go read-sequence ' +
37 | 'array-total-size graphic-char-p reader-error array-total-size-limit handler-bind ' +
38 | 'readtable arrayp handler-case readtable-case ash hash-table readtablep asin ' +
39 | 'hash-table-count real asinh hash-table-p realp assert hash-table-rehash-size ' +
40 | 'realpart assoc hash-table-rehash-threshold reduce assoc-if hash-table-size ' +
41 | 'reinitialize-instance assoc-if-not hash-table-test rem atan host-namestring ' +
42 | 'remf atanh identity remhash atom if remove base-char if-exists ' +
43 | 'remove-duplicates base-string ignorable remove-if bignum ignore remove-if-not ' +
44 | 'bit ignore-errors remove-method bit-and imagpart remprop bit-andc1 import ' +
45 | 'rename-file bit-andc2 in-package rename-package bit-eqv in-package replace ' +
46 | 'bit-ior incf require bit-nand initialize-instance rest bit-nor inline restart ' +
47 | 'bit-not input-stream-p restart-bind bit-orc1 inspect restart-case bit-orc2 ' +
48 | 'int-char restart-name bit-vector integer return bit-vector-p ' +
49 | 'integer-decode-float return-from bit-xor integer-length revappend block ' +
50 | 'integerp reverse boole interactive-stream-p room boole-1 intern rotatef ' +
51 | 'boole-2 round boole-and intersection ' +
52 | 'row-major-aref boole-andc1 invalid-method-error rplaca boole-andc2 ' +
53 | 'invoke-debugger rplacd boole-c1 invoke-restart safety boole-c2 ' +
54 | 'invoke-restart-interactively satisfies boole-clr isqrt sbit boole-eqv keyword ' +
55 | 'scale-float boole-ior keywordp schar boole-nand labels search boole-nor ' +
56 | 'second boole-orc1 lambda-list-keywords sequence boole-orc2 ' +
57 | 'lambda-parameters-limit serious-condition boole-set last set boole-xor lcm ' +
58 | 'set-char-bit boolean ldb set-difference both-case-p ldb-test ' +
59 | 'set-dispatch-macro-character boundp ldiff set-exclusive-or break ' +
60 | 'least-negative-double-float set-macro-character broadcast-stream ' +
61 | 'least-negative-long-float set-pprint-dispatch broadcast-stream-streams ' +
62 | 'least-negative-normalized-double-float set-syntax-from-char built-in-class ' +
63 | 'least-negative-normalized-long-float setf butlast ' +
64 | 'least-negative-normalized-short-float setq byte ' +
65 | 'least-negative-normalized-single-float seventh byte-position ' +
66 | 'least-negative-short-float shadow byte-size least-negative-single-float ' +
67 | 'shadowing-import call-arguments-limit least-positive-double-float ' +
68 | 'shared-initialize call-method least-positive-long-float shiftf ' +
69 | 'call-next-method least-positive-normalized-double-float short-float capitalize ' +
70 | 'least-positive-normalized-long-float short-float-epsilon car ' +
71 | 'least-positive-normalized-short-float short-float-negative-epsilon case ' +
72 | 'least-positive-normalized-single-float short-site-name catch ' +
73 | 'least-positive-short-float signal ccase least-positive-single-float ' +
74 | 'signed-byte cdr length signum ceiling simple-condition cell-error ' +
75 | 'simple-array cell-error-name lisp simple-base-string cerror ' +
76 | 'lisp-implementation-type simple-bit-vector change-class ' +
77 | 'lisp-implementation-version simple-bit-vector-p char list ' +
78 | 'simple-condition-format-arguments char-bit list\\* ' +
79 | 'simple-condition-format-control char-bits list-all-packages simple-error ' +
80 | 'char-bits-limit list-length simple-string char-code listen simple-string-p ' +
81 | 'char-code-limit listp simple-type-error char-control-bit load simple-vector ' +
82 | 'char-downcase load-logical-pathname-translations simple-vector-p char-equal ' +
83 | 'load-time-value simple-warning char-font locally sin char-font-limit log ' +
84 | 'single-flaot-epsilon char-greaterp logand single-float char-hyper-bit logandc1 ' +
85 | 'single-float-epsilon char-int logandc2 single-float-negative-epsilon ' +
86 | 'char-lessp logbitp sinh char-meta-bit logcount sixth char-name logeqv sleep ' +
87 | 'char-not-equal logical-pathname slot-boundp char-not-greaterp ' +
88 | 'logical-pathname-translations slot-exists-p char-not-lessp logior ' +
89 | 'slot-makunbound char-super-bit lognand slot-missing char-upcase lognor ' +
90 | 'slot-unbound char/= lognot slot-value char< logorc1 software-type char<= ' +
91 | 'logorc2 software-version char= logtest some char> logxor sort char>= ' +
92 | 'long-float space character long-float-epsilon special characterp ' +
93 | 'long-float-negative-epsilon special-form-p check-type long-site-name ' +
94 | 'special-operator-p cis loop speed class loop-finish sqrt class-name ' +
95 | 'lower-case-p stable-sort class-of machine-instance standard clear-input ' +
96 | 'machine-type standard-char clear-output machine-version standard-char-p close ' +
97 | 'macro-function standard-class clrhash macroexpand standard-generic-function ' +
98 | 'code-char macroexpand-1 standard-method coerce macroexpand-l standard-object ' +
99 | 'commonp macrolet step compilation-speed make-array storage-condition compile ' +
100 | 'make-array store-value compile-file make-broadcast-stream stream ' +
101 | 'compile-file-pathname make-char stream-element-type compiled-function ' +
102 | 'make-concatenated-stream stream-error compiled-function-p make-condition ' +
103 | 'stream-error-stream compiler-let make-dispatch-macro-character ' +
104 | 'stream-external-format compiler-macro make-echo-stream streamp ' +
105 | 'compiler-macro-function make-hash-table streamup complement make-instance ' +
106 | 'string complex make-instances-obsolete string-capitalize complexp make-list ' +
107 | 'string-char compute-applicable-methods make-load-form string-char-p ' +
108 | 'compute-restarts make-load-form-saving-slots string-downcase concatenate ' +
109 | 'make-method string-equal concatenated-stream make-package string-greaterp ' +
110 | 'concatenated-stream-streams make-pathname string-left-trim cond ' +
111 | 'make-random-state string-lessp condition make-sequence string-not-equal ' +
112 | 'conjugate make-string string-not-greaterp cons make-string-input-stream ' +
113 | 'string-not-lessp consp make-string-output-stream string-right-strim constantly ' +
114 | 'make-symbol string-right-trim constantp make-synonym-stream string-stream ' +
115 | 'continue make-two-way-stream string-trim control-error makunbound ' +
116 | 'string-upcase copy-alist map string/= copy-list map-into string< ' +
117 | 'copy-pprint-dispatch mapc string<= copy-readtable mapcan string= copy-seq ' +
118 | 'mapcar string> copy-structure mapcon string>= copy-symbol maphash stringp ' +
119 | 'copy-tree mapl structure cos maplist structure-class cosh mask-field ' +
120 | 'structure-object count max style-warning count-if member sublim count-if-not ' +
121 | 'member-if sublis ctypecase member-if-not subseq debug merge subsetp decf ' +
122 | 'merge-pathname subst declaim merge-pathnames subst-if declaration method ' +
123 | 'subst-if-not declare method-combination substitute decode-float ' +
124 | 'method-combination-error substitute-if decode-universal-time method-qualifiers ' +
125 | 'substitute-if-not defclass min subtypep defconstant minusp svref defgeneric ' +
126 | 'mismatch sxhash define-compiler-macro mod symbol define-condition ' +
127 | 'most-negative-double-float symbol-function define-method-combination ' +
128 | 'most-negative-fixnum symbol-macrolet define-modify-macro ' +
129 | 'most-negative-long-float symbol-name define-setf-expander ' +
130 | 'most-negative-short-float symbol-package define-setf-method ' +
131 | 'most-negative-single-float symbol-plist define-symbol-macro ' +
132 | 'most-positive-double-float symbol-value defmacro most-positive-fixnum symbolp ' +
133 | 'defmethod most-positive-long-float synonym-stream defpackage ' +
134 | 'most-positive-short-float synonym-stream-symbol defparameter ' +
135 | 'most-positive-single-float sys defsetf muffle-warning system defstruct ' +
136 | 'multiple-value-bind deftype multiple-value-call tagbody defun ' +
137 | 'multiple-value-list tailp defvar multiple-value-prog1 tan delete ' +
138 | 'multiple-value-seteq tanh delete-duplicates multiple-value-setq tenth ' +
139 | 'delete-file multiple-values-limit terpri delete-if name-char the delete-if-not ' +
140 | 'namestring third delete-package nbutlast throw denominator nconc time ' +
141 | 'deposit-field next-method-p trace describe translate-logical-pathname ' +
142 | 'describe-object nintersection translate-pathname destructuring-bind ninth ' +
143 | 'tree-equal digit-char no-applicable-method truename digit-char-p ' +
144 | 'no-next-method truncase directory not truncate directory-namestring notany ' +
145 | 'two-way-stream disassemble notevery two-way-stream-input-stream ' +
146 | 'division-by-zero notinline two-way-stream-output-stream do nreconc type do\\* ' +
147 | 'nreverse type-error do-all-symbols nset-difference type-error-datum ' +
148 | 'do-exeternal-symbols nset-exclusive-or type-error-expected-type ' +
149 | 'do-external-symbols nstring type-of do-symbols nstring-capitalize typecase ' +
150 | 'documentation nstring-downcase typep dolist nstring-upcase unbound-slot ' +
151 | 'dotimes nsublis unbound-slot-instance double-float nsubst unbound-variable ' +
152 | 'double-float-epsilon nsubst-if undefined-function ' +
153 | 'double-float-negative-epsilon nsubst-if-not unexport dpb nsubstitute unintern ' +
154 | 'dribble nsubstitute-if union dynamic-extent nsubstitute-if-not unless ecase ' +
155 | 'nth unread echo-stream nth-value unread-char echo-stream-input-stream nthcdr ' +
156 | 'unsigned-byte echo-stream-output-stream null untrace ed number unuse-package ' +
157 | 'eighth numberp unwind-protect elt numerator ' +
158 | 'update-instance-for-different-class encode-universal-time nunion ' +
159 | 'update-instance-for-redefined-class end-of-file oddp ' +
160 | 'upgraded-array-element-type endp open upgraded-complex-part-type ' +
161 | 'enough-namestring open-stream-p upper-case-p ensure-directories-exist optimize ' +
162 | 'use-package ensure-generic-function or use-value eq otherwise user eql ' +
163 | 'output-stream-p user-homedir-pathname equal package values equalp ' +
164 | 'package-error values-list error package-error-package vector etypecase ' +
165 | 'package-name vector-pop eval package-nicknames vector-push eval-when ' +
166 | 'package-shadowing-symbols vector-push-extend evalhook package-use-list vectorp ' +
167 | 'evenp package-used-by-list warn every packagep warning exp pairlis when export ' +
168 | 'parse-error wild-pathname-p expt parse-integer with-accessors extended-char ' +
169 | 'parse-namestring with-compilation-unit fboundp pathname ' +
170 | 'with-condition-restarts fceiling pathname-device with-hash-table-iterator ' +
171 | 'fdefinition pathname-directory with-input-from-string ffloor pathname-host ' +
172 | 'with-open-file fifth pathname-match-p with-open-stream file-author ' +
173 | 'pathname-name with-output-to-string file-error pathname-type ' +
174 | 'with-package-iterator file-error-pathname pathname-version with-simple-restart ' +
175 | 'file-length pathnamep with-slots file-namestring peek-char ' +
176 | 'with-standard-io-syntax file-position phase write file-stream write-byte ' +
177 | 'file-string-length plusp write-char file-write-date pop write-line fill ' +
178 | 'position write-sequence fill-pointer position-if write-string find ' +
179 | 'position-if-not write-to-string find-all-symbols pprint y-or-n-p find-class ' +
180 | 'pprint-dispatch yes-or-no-p find-if pprint-exit-if-list-exhausted zerop ' +
181 | 'find-if-not pprint-fill';
182 |
183 | // common lisp global variables. also from lisp.vim
184 | var standard_vars =
185 | '\\*applyhook\\* \\*load-pathname\\* \\*print-pprint-dispatch\\* \\*break-on-signals\\* ' +
186 | '\\*load-print\\* \\*print-pprint-dispatch\\* \\*break-on-signals\\* \\*load-truename\\* ' +
187 | '\\*print-pretty\\* \\*break-on-warnings\\* \\*load-verbose\\* \\*print-radix\\* ' +
188 | '\\*compile-file-pathname\\* \\*macroexpand-hook\\* \\*print-readably\\* ' +
189 | '\\*compile-file-pathname\\* \\*modules\\* \\*print-right-margin\\* \\*compile-file-truename\\* ' +
190 | '\\*package\\* \\*print-right-margin\\* \\*compile-file-truename\\* \\*print-array\\* ' +
191 | '\\*query-io\\* \\*compile-print\\* \\*print-base\\* \\*random-state\\* \\*compile-verbose\\* ' +
192 | '\\*print-case\\* \\*read-base\\* \\*compile-verbose\\* \\*print-circle\\* ' +
193 | '\\*read-default-float-format\\* \\*debug-io\\* \\*print-escape\\* \\*read-eval\\* ' +
194 | '\\*debugger-hook\\* \\*print-gensym\\* \\*read-suppress\\* \\*default-pathname-defaults\\* ' +
195 | '\\*print-length\\* \\*readtable\\* \\*error-output\\* \\*print-level\\* \\*standard-input\\* ' +
196 | '\\*evalhook\\* \\*print-lines\\* \\*standard-output\\* \\*features\\* \\*print-miser-width\\* ' +
197 | '\\*terminal-io\\* \\*gensym-counter\\* \\*print-miser-width\\* \\*trace-output\\* ' +
198 | 'pi internal-time-units-per-second';
199 |
200 | // common lisp known keywords
201 | var keywords =
202 | ':abort :from-end :overwrite :adjustable :gensym :predicate :append :host ' +
203 | ':preserve-whitespace :array :if-does-not-exist :pretty :base :if-exists :print ' +
204 | ':case :include :print-function :circle :index :probe :conc-name :inherited ' +
205 | ':radix :constructor :initial-contents :read-only :copier :initial-element ' +
206 | ':rehash-size :count :initial-offset :rehash-threshold :create :initial-value ' +
207 | ':rename :default :input :rename-and-delete :defaults :internal :size :device ' +
208 | ':io :start :direction :junk-allowed :start1 :directory :key :start2 ' +
209 | ':displaced-index-offset :length :stream :displaced-to :level :supersede ' +
210 | ':element-type :name :test :end :named :test-not :end1 :new-version :type :end2 ' +
211 | ':nicknames :use :error :output :verbose :escape :output-file :version ' +
212 | ':external :documentation :shadowing-import-from :modern :export ' +
213 | ':case-sensitive :case-inverted :shadow :import-from :intern :fill-pointer ' +
214 | ':upcase :downcase :preserve :invert :load-toplevel :compile-toplevel :execute ' +
215 | ':while :until :for :do :if :then :else :when :unless :in :across :finally ' +
216 | ':collect :nconc :maximize :minimize :sum :and :with :initially :append :into ' +
217 | ':count :end :repeat :always :never :thereis :from :to :upto :downto :below ' +
218 | ':above :by :on :being :each :the :hash-key :hash-keys :hash-value :hash-values ' +
219 | ':using :of-type :upfrom :downfrom :arguments :return-type :library :full ' +
220 | ':malloc-free :none :alloca :in :out :in-out :stdc-stdcall :stdc :c :language ' +
221 | ':built-in :typedef :external :fini :init-once :init-always';
222 |
223 | var lambda = '&allow-other-keys &aux &body &environment &key &optional &rest &whole';
224 |
225 | var special = 'let let\\* lambda';
226 |
227 | /**
228 | * Given a list of items in a string: 'item1 item2 item2 ...'
229 | *
230 | * return a regex *string*: '(item1|item2|item2|...)'
231 | */
232 | var list_to_regex = function(list)
233 | {
234 | var items = list.replace(/(^ | $)/gm, '').split(/ /g);
235 | return '('+items.join('|')+')';
236 | };
237 |
238 | var is_in_list = function(item, list)
239 | {
240 | var items = list.replace(/(^ | $)/gm, '').split(/ /g);
241 | for(var i = 0, n = items.length; i < n; i++)
242 | {
243 | if(items[i] == item) return true;
244 | }
245 | return false;
246 | };
247 |
248 | /**
249 | * Collections of search and replaces to make.
250 | */
251 | var replace = [
252 | // ---------------------------------------------------------------------
253 | // strings (should !!ALWAYS!! be first, lest our tags be destroyed...)
254 | // ---------------------------------------------------------------------
255 | {regex: /"([\s\S]*?)"/gm, replace: '"$1"'},
256 |
257 | // ---------------------------------------------------------------------
258 | // comments
259 | // ---------------------------------------------------------------------
260 | {regex: /(;.*)(\n|$)/gm, replace: '$1$2'},
261 |
262 | // ---------------------------------------------------------------------
263 | // "special" (let/lambda)
264 | // ---------------------------------------------------------------------
265 | {
266 | regex: new RegExp('.'+list_to_regex(special)+'(?=[\\s()])', 'g'),
267 | replace: function(fullmatch, fnname) {
268 | if(fullmatch[0] == '(')
269 | {
270 | return '(' + fnname + '';
271 | }
272 | else
273 | {
274 | return fullmatch;
275 | }
276 | }
277 | },
278 |
279 |
280 | // ---------------------------------------------------------------------
281 | // function matches
282 | // ---------------------------------------------------------------------
283 | // known functions
284 | {
285 | regex: new RegExp('.'+list_to_regex(funcs)+'(?=[\\s()])', 'g'),
286 | replace: function(fullmatch, fnname) {
287 | if(fullmatch[0] == '(')
288 | {
289 | return '(' + fnname + '';
290 | }
291 | else
292 | {
293 | return fullmatch;
294 | }
295 | }
296 | },
297 | // symbol functions (#'my-fn)
298 | {
299 | regex: /([\s()])(#'(\w[\w_-]*))(?=[\s()])/g,
300 | replace: function(fullmatch, delim1, symfun, sym)
301 | {
302 | var known = false;
303 | if(is_in_list(sym, funcs))
304 | {
305 | known = true;
306 | }
307 | return delim1 +''+ symfun +'';
308 | }
309 | },
310 |
311 | // ---------------------------------------------------------------------
312 | // lambda keywords
313 | // ---------------------------------------------------------------------
314 | {regex: new RegExp('([\\s()])'+list_to_regex(lambda)+'(?=[\\s()])', 'g'), replace: '$1$2'},
315 |
316 | // ---------------------------------------------------------------------
317 | // symbols/keywords/variables
318 | // ---------------------------------------------------------------------
319 | // generic symbols
320 | {regex: /([\s()])('\w[\w_-]*)(?=[\s()])/g, replace: '$1$2'},
321 | // known keywords
322 | {
323 | regex: new RegExp('([\\s()])'+list_to_regex(keywords)+'([\\s()])', 'g'),
324 | replace: function(fullmatch, whitespace, keyword, whitespace2) {
325 | return whitespace + ''+ keyword +''+ whitespace2;
326 | }
327 | },
328 | // generic keywords
329 | {
330 | regex: /([\s()])(:\w[\w_-]*)/g,
331 | replace: function(fullmatch, delim, keyword) {
332 | if(fullmatch[0].match(/[\s()]/gm))
333 | {
334 | return delim + ''+ keyword +'';
335 | }
336 | return fullmatch;
337 | }
338 | },
339 | // known variables
340 | {
341 | regex: new RegExp('([\\s()])'+list_to_regex(standard_vars)+'([\\s()])', 'g'),
342 | replace: function(fullmatch, whitespace, varname, whitespace2) {
343 | return whitespace + ''+ varname +''+ whitespace2;
344 | }
345 | },
346 | // globals/constants
347 | {regex: /([\s()])(\*\w[\w_-]*\*)(?=[\s()])/g, replace: '$1$2'},
348 | {regex: /([\s()])(\+\w[\w_-]*\+)(?=[\s()])/g, replace: '$1$2'},
349 |
350 | // ---------------------------------------------------------------------
351 | // numbers
352 | // ---------------------------------------------------------------------
353 | // binary
354 | {regex: /([\s()])(#b[01]+)(?=[\s()])/ig, replace: '$1$2'},
355 | // hex
356 | {regex: /([\s()])(#x[\da-f]+)(?=[\s()])/ig, replace: '$1$2'},
357 | // float
358 | {regex: /([\s()])([+-]?(?:\d+\.\d+|\d+\.|\.\d+))(?=[\s()])/g, replace: '$1$2'},
359 | // ratio
360 | {regex: /([\s()])([+-]?\d+(?:\/\d+)?)(?=[\s()])/g, replace: '$1$2'},
361 | // integers
362 | {regex: /([\s()])([+-]?\d+)(?=[\s()])/g, replace: '$1$2'},
363 |
364 | // ---------------------------------------------------------------------
365 | // misc parsers
366 | // ---------------------------------------------------------------------
367 | // t/nil
368 | {regex: /([\s()])(nil|t)(?=[\s()])/g, replace: '$1$2'},
369 |
370 | // generic "maybe a function" forms. best second to last
371 | {regex: /\((\w[\w_:-]*)(?=[\s()])/g, replace: '($1'},
372 |
373 | // ()'s (should most probably be last, unless there's a good reason)
374 | {regex: /([()])/g, replace: '$1'}
375 | ];
376 |
377 | /**
378 | * Main highlight function.
379 | */
380 | this.highlight_element = function(code_el)
381 | {
382 | code_el.className += ' hl-highlighted';
383 | var html = code_el.innerHTML;
384 | // can't have &...;'s running wild like a pack of animals...
385 | html = html.replace(/&/g, '&');
386 | html = html.replace(/</g, '<');
387 | html = html.replace(/>/g, '>');
388 | // pad the HTML string (makes regexs much simpler)
389 | html = "\n" + html + "\n";
390 | for(var i = 0, n = replace.length; i < n; i++)
391 | {
392 | var rep = replace[i];
393 | html = html.replace(rep.regex, rep.replace);
394 | }
395 | // unpad HTML string
396 | html = html.replace(/(^\n|\n$)/g, '');
397 | html = html.replace(/<(?!\/?span)/g, '<');
398 | // Re-encode stray &s to conform with XHTML
399 | //html = html.replace(/&/g, '&');
400 |
401 | code_el.innerHTML = html;
402 | },
403 |
404 | /**
405 | * Automatically highlight all blocks
406 | *
407 | * Takes an options arg, which can be used to specify the classname of the
408 | * tags you wish to highlight.
409 | */
410 | this.highlight_auto = function(options)
411 | {
412 | options || (options = {});
413 | var classname = options.className ? options.className : 'lisp';
414 | var codes = document.getElementsByTagName('code');
415 | for(var i = 0, n = codes.length; i < n; i++)
416 | {
417 | var code = codes[i];
418 | if(code.className.match(classname))
419 | {
420 | this.highlight_element(code);
421 | }
422 | }
423 | },
424 |
425 | /**
426 | * If called, enables paren matching (hovering over a paren will add the
427 | * "active" class to both the highlighted and the matching paren)
428 | */
429 | this.paren_match = function(options)
430 | {
431 | options || (options = {});
432 |
433 | if(!('querySelector' in document))
434 | {
435 | console.error('HighlightLisp.paren_match: browser does not support querySelector/matches');
436 | return;
437 | }
438 |
439 | var matches = function(element, selector)
440 | {
441 | if(!element) return;
442 | var domatch;
443 | var tests = ['matches', 'msMatchesSelector', 'mozMatchesSelector', 'webkitMatchesSelector'];
444 | for(var i = 0; i < tests.length; i++)
445 | {
446 | if(!(tests[i] in element)) continue;
447 | domatch = element[tests[i]];
448 | break;
449 | }
450 |
451 | return domatch.call(element, selector);
452 | };
453 |
454 | var is_paren = function(el)
455 | {
456 | return matches(el, 'code > .list, code span:not(.comment):not(.string) .list');
457 | };
458 |
459 | var find_match = function(paren)
460 | {
461 | // grab all non-commented/stringed parens
462 | var children = paren.parentElement.querySelectorAll('code > span.list, code span:not(.comment):not(.string) .list');
463 | // turn them into a real array
464 | children = Array.prototype.slice.call(children);
465 |
466 | var is_opening = function(el) { return el.innerHTML == '('; };
467 |
468 | // tracks when to start counting parens
469 | var count = false;
470 | // tests if this is an opening or closing paren
471 | var opening = is_opening(paren);
472 | // if this is a closing paren, reverse the children so we can search
473 | // backwards just by going forwards
474 | if(!opening) children.reverse();
475 |
476 | for(var i = 0; i < children.length; i++)
477 | {
478 | var child = children[i];
479 | var open = is_opening(child);
480 | // mark the first occurance of the paren, and start counting
481 | // from there
482 | if(child === paren)
483 | {
484 | count = 1;
485 | continue;
486 | }
487 | if(count === false) continue;
488 | if(opening == open) count++;
489 | else count--;
490 | if(count === 0) return child;
491 | }
492 | };
493 |
494 | var toggle_class = function(element, classname)
495 | {
496 | if (element.classList)
497 | { element.classList.toggle(classname); }
498 | else
499 | {
500 | //for IE9
501 | var classes = element.className.split(" ");
502 | var i = classes.indexOf(classname);
503 | if (i >= 0)
504 | { classes.splice(i, 1); }
505 | else
506 | {
507 | classes.push(classname);
508 | element.className = classes.join(" ");
509 | }
510 | }
511 | }
512 |
513 | var codes = document.getElementsByClassName('hl-highlighted');
514 | for(var i = 0; i < codes.length; i++)
515 | {
516 | var code = codes[i];
517 | var listener = function(add, e)
518 | {
519 | var hovered = e.target;
520 | if(!is_paren(hovered)) return;
521 | var match = find_match(hovered);
522 | toggle_class(hovered, 'active');
523 | toggle_class(match, 'active');
524 | };
525 | code.addEventListener('mouseover', listener.bind(this, true));
526 | code.addEventListener('mouseout', listener.bind(this, false));
527 | }
528 | }
529 | };
530 |
531 | var HighlightLisp = new highlight_lisp();
532 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "highlight-lisp",
3 | "version": "0.1.6",
4 | "description": "An HTML syntax highlighter for Common Lisp",
5 | "author": "Andrew Lyon ",
6 | "license": "MIT",
7 | "homepage": "https://github.com/orthecreedence/highlight-lisp",
8 | "filename": "highlight-lisp.min.js",
9 | "keywords": [
10 | "lisp",
11 | "common lisp",
12 | "highlight",
13 | "syntax"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/orthecreedence/highlight-lisp.git"
18 | },
19 | "autoupdate": {
20 | "source": "git",
21 | "target": "git://github.com/orthecreedence/highlight-lisp.git",
22 | "basePath": "",
23 | "files": [
24 | "highlight-lisp.min.js",
25 | "themes/dark.min.css",
26 | "themes/github.min.css",
27 | "themes/wookie.min.css",
28 | "themes/vestigial.min.css"
29 | ]
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/themes/dark.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Inspired by highlight.js's "Tomorrow night" theme
3 | */
4 | pre { white-space: pre; background-color: #1d1f21; border: 0px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; }
5 | pre code.hl-highlighted {white-space: pre; margin: 0; padding: 0; background: none; border: none; overflow-x: auto; font-size: 13px;}
6 | code.hl-highlighted {margin: 0 2px; padding: 0 5px; white-space: nowrap; font-family: Consolas, "Liberation Mono", Courier, monospace; background: #f8f8f8; border: 1px solid #eaeaea; border-radius: 3px;}
7 |
8 | code.hl-highlighted {color: #c5c8c6;}
9 | code.hl-highlighted .function {color: #abc; color: #cba;}
10 | code.hl-highlighted .function.known {color: #81a2be; color: #91b2ce;}
11 | code.hl-highlighted .function.known.special {color: #ba7; font-weight: bold;}
12 | code.hl-highlighted .keyword {color: #b299ab;}
13 | code.hl-highlighted .keyword.known {color: #b294bb;}
14 | code.hl-highlighted .symbol {color: #c9c;}
15 | code.hl-highlighted .lambda-list {color: #9aa;}
16 | code.hl-highlighted .number {color: #c76;}
17 | code.hl-highlighted .variable.known {color: #9dd;}
18 | code.hl-highlighted .variable.global {color: #aaa;}
19 | code.hl-highlighted .variable.constant {color: #a77;}
20 | code.hl-highlighted .nil {color: #de935f;}
21 | code.hl-highlighted .list {}
22 |
23 | code.hl-highlighted .string, code.hl-highlighted .string * {color: #b5bd88 !important;}
24 | code.hl-highlighted .comment,
25 | code.hl-highlighted .comment *,
26 | code.hl-highlighted .comment .string
27 | code.hl-highlighted .comment .string * {color: #777 !important;}
28 | code.hl-highlighted .string .comment {color: #b5bd88 !important;}
29 |
30 | code.hl-highlighted .list.active {display: inline-block; color: #333; background: #aaa;}
31 |
32 |
--------------------------------------------------------------------------------
/themes/github.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Inspired by github's default code highlighting
3 | */
4 | pre { white-space: pre; background-color: #f8f8f8; border: 1px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; }
5 | pre code.hl-highlighted {white-space: pre; margin: 0; padding: 0; background: none; border: none; overflow-x: auto; font-size: 13px;}
6 | code.hl-highlighted {margin: 0 2px; padding: 0 5px; white-space: nowrap; font-family: Consolas, "Liberation Mono", Courier, monospace; background: #f8f8f8; border: 1px solid #eaeaea; border-radius: 3px;}
7 |
8 | code.hl-highlighted {color: #008080;}
9 | code.hl-highlighted .function {color: #008080;}
10 | code.hl-highlighted .function.known {color: #800603;}
11 | code.hl-highlighted .function.known.special {color: #2d2d2d; font-weight: bold;}
12 | code.hl-highlighted .keyword {color: #990073;}
13 | code.hl-highlighted .keyword.known {color: #990073;}
14 | code.hl-highlighted .symbol {color: #75a;}
15 | code.hl-highlighted .lambda-list {color: #966;}
16 | code.hl-highlighted .number {color: #800;}
17 | code.hl-highlighted .variable.known {color: #c3c;}
18 | code.hl-highlighted .variable.global {color: #939;}
19 | code.hl-highlighted .variable.constant {color: #229;}
20 | code.hl-highlighted .nil {color: #f00;}
21 | code.hl-highlighted .list {color: #222;}
22 |
23 | code.hl-highlighted .string, code.hl-highlighted .string * {color: #d14 !important;}
24 | code.hl-highlighted .comment,
25 | code.hl-highlighted .comment *,
26 | code.hl-highlighted .comment .string
27 | code.hl-highlighted .comment .string * {color: #777777 !important;}
28 | code.hl-highlighted .string .comment {color: #d14 !important;}
29 |
30 | code.hl-highlighted .list.active {display: inline-block; background: #aefff7;}
31 |
32 |
--------------------------------------------------------------------------------
/themes/vestigial.css:
--------------------------------------------------------------------------------
1 | pre { white-space: pre; background-color: #f8f8f8; border: 1px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; }
2 | pre code {white-space: pre; margin: 0; padding: 0; background: none; border: none; overflow-x: auto; font-size: 13px;}
3 | code {margin: 0 2px; padding: 0 5px; white-space: nowrap; font-family: Consolas, "Liberation Mono", Courier, monospace; background: #f8f8f8; border: 1px solid #eaeaea; border-radius: 3px;}
4 |
5 | code.hl-highlighted .function.known {color: #a93;}
6 | code.hl-highlighted .function.known.special {color: #2a3;}
7 | code.hl-highlighted .keyword {color: #7aa;}
8 | code.hl-highlighted .keyword.known {color: #4bb;}
9 | code.hl-highlighted .symbol {color: #75a;}
10 | code.hl-highlighted .lambda-list {color: #966;}
11 | code.hl-highlighted .number {color: #800;}
12 | code.hl-highlighted .variable.known {color: #c3c;}
13 | code.hl-highlighted .variable.global {color: #939;}
14 | code.hl-highlighted .variable.constant {color: #229;}
15 | code.hl-highlighted .nil {color: #f00;}
16 |
17 | code.hl-highlighted .comment, code.hl-highlighted .comment *, code.hl-highlighted .comment .string {color: #aaa !important;}
18 | code.hl-highlighted .string, code.hl-highlighted .string * {color: #088 !important;}
19 |
20 | code.hl-highlighted .list.active {display: inline-block; background: #aefff7;}
21 |
22 |
--------------------------------------------------------------------------------
/themes/wookie.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Inspired by highlight.js's "Tomorrow night" theme
3 | */
4 | pre { white-space: pre; background-color: #1d1f21; border: 0px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; }
5 | pre code.hl-highlighted {white-space: pre; margin: 0; padding: 0; background: none; border: none; overflow-x: auto; font-size: 13px;}
6 | code.hl-highlighted {margin: 0 2px; padding: 0 5px; white-space: nowrap; font-family: Consolas, "Liberation Mono", Courier, monospace; border: 1px solid #eaeaea; border-radius: 3px;}
7 |
8 | code.hl-highlighted {color: #c5c8c6; color: #c9bf9f;}
9 | code.hl-highlighted .function {color: #d9c382;}
10 | code.hl-highlighted .function.known {color: #d9c382;}
11 | code.hl-highlighted .function.known.special {color: #d9c382; font-weight: bold;}
12 | code.hl-highlighted .keyword {color: #74b5ad;}
13 | code.hl-highlighted .keyword.known {color: #91b2ce;}
14 | code.hl-highlighted .symbol {color: #db919e;}
15 | code.hl-highlighted .lambda-list {color: #86adad;}
16 | code.hl-highlighted .number {color: #fa879c;}
17 | code.hl-highlighted .variable.known {color: #db697e;}
18 | code.hl-highlighted .variable.global {color: #96a646;}
19 | code.hl-highlighted .variable.constant {color: #96a646;}
20 | code.hl-highlighted .nil {color: #db919e; font-weight: bold;}
21 | code.hl-highlighted .list {}
22 |
23 | code.hl-highlighted .string, code.hl-highlighted .string * {color: #db7d8e !important;}
24 | code.hl-highlighted .comment,
25 | code.hl-highlighted .comment *,
26 | code.hl-highlighted .comment .string
27 | code.hl-highlighted .comment .string * {color: #777 !important;}
28 | code.hl-highlighted .string .comment {color: #db7d8e !important;}
29 |
30 | code.hl-highlighted .list.active {display: inline-block; color: #333; background: #aaa;}
31 |
32 |
--------------------------------------------------------------------------------