From 93216f5e8a09e41cf4da433294a45756d0dbae93 Mon Sep 17 00:00:00 2001 From: Andrew Danger Lyon Date: Tue, 11 Aug 2015 20:06:14 -0700 Subject: [PATCH] adding paren matching =] --- .gitignore | 2 + README.md | 12 ++++++ highlight-lisp.js | 93 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- themes/dark.css | 3 ++ themes/github.css | 3 ++ themes/vestigial.css | 3 ++ themes/wookie.css | 3 ++ 8 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a777a81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +test/* + diff --git a/README.md b/README.md index 52fe84a..8e423cc 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,18 @@ var code = document.getElementById('my-code-element'); HighlightLisp.highlight_element(code); ``` +Paren matching +-------------- + +You can now enable paren matching (on mouse hover): + +```js +HighlightLisp.paren_match(); +``` + +This will go through all highlighted blocks of code and add mouseover/mouseout +event listeners to all ('s and )'s that highlight the matching paren on hover. + What gets highlighted --------------------- - **Functions** diff --git a/highlight-lisp.js b/highlight-lisp.js index a997288..436dc3d 100644 --- a/highlight-lisp.js +++ b/highlight-lisp.js @@ -420,6 +420,99 @@ var highlight_lisp = function() { this.highlight_element(code); } } + }, + + /** + * If called, enables paren matching (hovering over a paren will add the + * "active" class to both the highlighted and the matching paren) + */ + this.paren_match = function(options) + { + options || (options = {}); + + if(!('querySelector' in document)) + { + console.error('HighlightLisp.paren_match: browser does not support querySelector/matches'); + return; + } + + var matches = function(element, selector) + { + if(!element) return; + var domatch; + var tests = ['matches', 'msMatchesSelector', 'mozMatchesSelector', 'webkitMatchesSelector']; + for(var i = 0; i < tests.length; i++) + { + if(!(tests[i] in element)) continue; + domatch = element[tests[i]]; + break; + } + + return domatch.call(element, selector); + }; + + var is_paren = function(el) + { + return matches(el, 'code > .list, code span:not(.comment):not(.string) .list'); + }; + + var find_match = function(paren) + { + // grab all non-commented/stringed parens + var children = paren.parentElement.querySelectorAll('code > span.list, code span:not(.comment):not(.string) .list'); + // turn them into a real array + children = Array.prototype.slice.call(children); + + var is_opening = function(el) { return el.innerHTML == '('; }; + + // tracks when to start counting parens + var count = false; + // tests if this is an opening or closing paren + var opening = is_opening(paren); + // if this is a closing paren, reverse the children so we can search + // backwards just by going forwards + if(!opening) children.reverse(); + + for(var i = 0; i < children.length; i++) + { + var child = children[i]; + var open = is_opening(child); + // mark the first occurance of the paren, and start counting + // from there + if(child === paren) + { + count = 1; + continue; + } + if(count === false) continue; + if(opening == open) count++; + else count--; + if(count === 0) return child; + } + }; + + var add_class = function(el, classname, add) + { + if(!el) return; + el.className = el.className.replace(new RegExp(classname, 'g'), ''); + if(add) el.className += ' '+classname; + }; + + var codes = document.getElementsByClassName('hl-highlighted'); + for(var i = 0; i < codes.length; i++) + { + var code = codes[i]; + var listener = function(add, e) + { + var hovered = e.target; + if(!is_paren(hovered)) return; + var match = find_match(hovered); + add_class(hovered, 'active', add); + add_class(match, 'active', add); + }; + code.addEventListener('mouseover', listener.bind(this, true)); + code.addEventListener('mouseout', listener.bind(this, false)); + } } }; diff --git a/package.json b/package.json index 83a68c1..07e518e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "highlight-lisp", - "version": "0.1.5", + "version": "0.1.6", "description": "An HTML syntax highlighter for Common Lisp", "author": "Andrew Lyon ", "license": "MIT", diff --git a/themes/dark.css b/themes/dark.css index b011087..272a806 100644 --- a/themes/dark.css +++ b/themes/dark.css @@ -26,3 +26,6 @@ code.hl-highlighted .comment *, code.hl-highlighted .comment .string code.hl-highlighted .comment .string * {color: #777 !important;} code.hl-highlighted .string .comment {color: #b5bd88 !important;} + +code.hl-highlighted .list.active {display: inline-block; color: #333; background: #aaa;} + diff --git a/themes/github.css b/themes/github.css index 65c97f4..e21fcab 100644 --- a/themes/github.css +++ b/themes/github.css @@ -26,3 +26,6 @@ code.hl-highlighted .comment *, code.hl-highlighted .comment .string code.hl-highlighted .comment .string * {color: #aaa !important;} code.hl-highlighted .string .comment {color: #d14 !important;} + +code.hl-highlighted .list.active {display: inline-block; background: #aefff7;} + diff --git a/themes/vestigial.css b/themes/vestigial.css index b50d4ee..90c3835 100644 --- a/themes/vestigial.css +++ b/themes/vestigial.css @@ -16,3 +16,6 @@ code.hl-highlighted .nil {color: #f00;} code.hl-highlighted .comment, code.hl-highlighted .comment *, code.hl-highlighted .comment .string {color: #aaa !important;} code.hl-highlighted .string, code.hl-highlighted .string * {color: #088 !important;} + +code.hl-highlighted .list.active {display: inline-block; background: #aefff7;} + diff --git a/themes/wookie.css b/themes/wookie.css index 7b51712..b2962a0 100644 --- a/themes/wookie.css +++ b/themes/wookie.css @@ -26,3 +26,6 @@ code.hl-highlighted .comment *, code.hl-highlighted .comment .string code.hl-highlighted .comment .string * {color: #777 !important;} code.hl-highlighted .string .comment {color: #db7d8e !important;} + +code.hl-highlighted .list.active {display: inline-block; color: #333; background: #aaa;} +