branch: master commit bef41e1518e54073c1e4a66493036eacc94ba6e3 Merge: d82f357 de8eacd Author: Jackson Ray Hamilton <jack...@jacksonrayhamilton.com> Commit: Jackson Ray Hamilton <jack...@jacksonrayhamilton.com>
Merge branch 'feature/optimize-js2-mode' into develop --- .gitignore | 2 +- Makefile | 2 +- README.md | 10 +++--- benchmark/context-coloring-benchmark.el | 3 +- context-coloring.el | 54 +++++++++++++++++++----------- 5 files changed, 43 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 5282559..d9c77ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ *.elc -*.log +/benchmark/logs/ diff --git a/Makefile b/Makefile index c50712b..2b6569c 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ compile: -f batch-byte-compile *.el lib/*.el clean: - rm -f *.log benchmark/*.log *.elc lib/*.elc + rm -f *.elc lib/*.elc test: ${EMACS} -Q -batch \ diff --git a/README.md b/README.md index 430b229..1216a68 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,11 @@ code*. - Supported languages: JavaScript - Light and dark (customizable) color schemes. -- Mach speed for regular files, reasonably-fast for big ones. - - jQuery (9191 lines): 0.43 seconds (js2-mode), 0.63 seconds (js-mode) - - Lodash (6786 lines): 0.19 seconds (js2-mode), 0.37 seconds (js-mode) - - Async (1124 lines): 0.05 seconds (js2-mode), 0.17 seconds (js-mode) - - mkdirp (98 lines): 0.005 seconds (js2-mode), 0.09 seconds (js-mode) +- Insanely fast for regular files, quick for big ones too. + - jQuery (9191 lines): 0.20 seconds (js2-mode), 0.57 seconds (js-mode) + - Lodash (6786 lines): 0.07 seconds (js2-mode), 0.35 seconds (js-mode) + - Async (1124 lines): 0.03 seconds (js2-mode), 0.17 seconds (js-mode) + - mkdirp (98 lines): 0.002 seconds (js2-mode), 0.09 seconds (js-mode) \* js2-mode parses idly, irrespective of this plugin; its benchmarks represent coloring only. js-mode benchmarks represent parsing and coloring. diff --git a/benchmark/context-coloring-benchmark.el b/benchmark/context-coloring-benchmark.el index 707188f..6d66f18 100644 --- a/benchmark/context-coloring-benchmark.el +++ b/benchmark/context-coloring-benchmark.el @@ -15,6 +15,7 @@ (with-temp-buffer (insert "\n") (append-to-buffer results-buffer (point-min) (point-max)))) + (make-directory (context-coloring-benchmark-resolve-path "./logs") t) (append-to-file nil nil result-file)) (defun context-coloring-benchmark-next-tick (function) @@ -34,7 +35,7 @@ (defun context-coloring-benchmark-async (title setup teardown fixtures callback) (funcall setup) (let ((result-file (context-coloring-benchmark-resolve-path - (concat "./results-" title "-" (format-time-string "%s") ".log")))) + (concat "./logs/results-" title "-" (format-time-string "%s") ".log")))) (context-coloring-benchmark-next fixtures (lambda (path next) diff --git a/context-coloring.el b/context-coloring.el index 283bddd..56e3e28 100644 --- a/context-coloring.el +++ b/context-coloring.el @@ -227,22 +227,28 @@ END (exclusive) with the face corresponding to LEVEL." ;;; js2-mode colorization -;; TODO: Consider `js2-node-top-level-decl-p' as an optimization. +(defvar-local context-coloring-js2-scope-level-hash-table nil + "Associates `js2-scope' structures and with their scope + levels.") + (defsubst context-coloring-js2-scope-level (scope) "Gets the level of SCOPE." - (let ((level 0) - enclosing-scope) - (while (and scope - (js2-node-parent scope) - (setq enclosing-scope (js2-node-get-enclosing-scope scope))) - (when (or context-coloring-js-block-scopes - (let ((type (js2-scope-type scope))) - (or (= type js2-SCRIPT) - (= type js2-FUNCTION) - (= type js2-CATCH)))) - (setq level (+ level 1))) - (setq scope enclosing-scope)) - level)) + (cond ((gethash scope context-coloring-js2-scope-level-hash-table)) + (t + (let ((level 0) + (current-scope scope) + enclosing-scope) + (while (and current-scope + (js2-node-parent current-scope) + (setq enclosing-scope (js2-node-get-enclosing-scope current-scope))) + (when (or context-coloring-js-block-scopes + (let ((type (js2-scope-type current-scope))) + (or (= type js2-SCRIPT) + (= type js2-FUNCTION) + (= type js2-CATCH)))) + (setq level (+ level 1))) + (setq current-scope enclosing-scope)) + (puthash scope level context-coloring-js2-scope-level-hash-table))))) (defsubst context-coloring-js2-local-name-node-p (node) "Determines if NODE is a js2-name-node representing a local @@ -266,6 +272,8 @@ variable." (defun context-coloring-js2-colorize () "Colorizes the current buffer using the abstract syntax tree generated by js2-mode." + ;; Reset the hash table; the old one could be obsolete. + (setq context-coloring-js2-scope-level-hash-table (make-hash-table :test 'eq)) (with-silent-modifications (js2-visit-ast js2-mode-ast @@ -281,12 +289,18 @@ generated by js2-mode." node (context-coloring-js2-scope-level node))) ((context-coloring-js2-local-name-node-p node) - (context-coloring-js2-colorize-node - node - (context-coloring-js2-scope-level - (js2-get-defining-scope - (js2-node-get-enclosing-scope node) - (js2-name-node-name node)))))) + (let* ((enclosing-scope (js2-node-get-enclosing-scope node)) + (defining-scope (js2-get-defining-scope + enclosing-scope + (js2-name-node-name node)))) + ;; The tree seems to be walked lexically, so an entire scope will + ;; be colored, including its name nodes, before they are + ;; reached. Coloring the nodes defined in that scope would be + ;; redundant, so don't do it. + (when (not (eq defining-scope enclosing-scope)) + (context-coloring-js2-colorize-node + node + (context-coloring-js2-scope-level defining-scope)))))) ;; The `t' indicates to search children. t)))))