├── .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 | --------------------------------------------------------------------------------