;;;###autoload
(defun js2-imenu-extras-setup ()
(when js2-imenu-enabled-frameworks
- (add-hook 'js2-post-parse-callbacks 'js2-imenu-record-declarations t t))
+ (add-hook 'js2-build-imenu-callbacks 'js2-imenu-record-declarations t t))
(when (or js2-imenu-show-other-functions js2-imenu-show-module-pattern)
- (add-hook 'js2-post-parse-callbacks 'js2-imenu-walk-ast t t)))
+ (add-hook 'js2-build-imenu-callbacks 'js2-imenu-walk-ast t t)))
(defun js2-imenu-extras-remove ()
- (remove-hook 'js2-post-parse-callbacks 'js2-imenu-record-declarations t)
- (remove-hook 'js2-post-parse-callbacks 'js2-imenu-walk-ast t))
+ (remove-hook 'js2-build-imenu-callbacks 'js2-imenu-record-declarations t)
+ (remove-hook 'js2-build-imenu-callbacks 'js2-imenu-walk-ast t))
(defun js2-imenu-record-declarations ()
(let* ((styles (loop for style in js2-imenu-extension-styles
(cond
((and js2-imenu-show-other-functions
(js2-object-prop-node-p node))
- (js2-imenu-record-orphan-function node))
- ((and js2-imenu-show-module-pattern
- (js2-assign-node-p node))
- (js2-imenu-record-module-pattern node)))
+ (js2-imenu-record-orphan-prop-node-function node))
+ ((js2-assign-node-p node)
+ (cond
+ ((and js2-imenu-show-other-functions
+ (js2-function-node-p
+ (js2-assign-node-right node)))
+ (js2-imenu-record-orphan-assign-node-function node))
+ (js2-imenu-show-module-pattern
+ (js2-imenu-record-module-pattern node)))))
t))))
(defun js2-imenu-parent-key-names (node)
(setq p3 (js2-node-parent p2))
(if (and p3 (js2-object-prop-node-p p3)) p3))))
-(defun js2-imenu-record-orphan-function (node)
+(defun js2-imenu-record-orphan-prop-node-function (node)
"Record orphan function when it's the value of NODE.
NODE must be `js2-object-prop-node'."
(when (js2-function-node-p (js2-object-prop-node-right node))
(js2-record-imenu-entry fn-node chain
(js2-node-abs-pos key-node)))))))
+(defun js2-imenu-record-orphan-assign-node-function (node)
+ "Return orphan function entry when it's the right hand of NODE.
+NODE must be `js2-assign-node'."
+ (let ((fn-node (js2-assign-node-right node)))
+ (when (or (not js2-imenu-function-map)
+ (eq 'skip
+ (gethash fn-node js2-imenu-function-map 'skip)))
+ (let* ((target-node (js2-assign-node-left node))
+ (chain (js2-compute-nested-prop-get target-node)))
+ (when chain
+ (push js2-imenu-other-functions-ns chain)
+ (js2-record-imenu-entry fn-node chain (js2-node-abs-pos fn-node)))))))
+
(defun js2-imenu-record-module-pattern (node)
"Recognize and record module pattern use instance.
NODE must be `js2-assign-node'."
:type 'hook
:group 'js2-mode)
+(defcustom js2-build-imenu-callbacks nil
+ "List of functions called during Imenu index generation.
+It's a good place to add additional entries to it, using
+`js2-record-imenu-entry'."
+ :type 'hook
+ :group 'js2-mode)
+
(defcustom js2-highlight-external-variables t
"Non-nil to highlight undeclared variable identifiers.
An undeclared variable is any variable not declared with var or let
(let ((parent (js2-node-parent node)))
(or
;; function(){...}();
- (js2-call-node-p parent)
+ (and (js2-call-node-p parent)
+ (eq node (js2-call-node-target parent)))
(and (js2-paren-node-p parent)
;; (function(){...})();
(or (js2-call-node-p (setq parent (js2-node-parent parent)))
'("call" "apply"))
(js2-call-node-p (js2-node-parent parent))))))))
-(defun js2-browse-postprocess-chains (entries)
+(defun js2-browse-postprocess-chains ()
"Modify function-declaration name chains after parsing finishes.
Some of the information is only available after the parse tree is complete.
For instance, processing a nested scope requires a parent function node."
(let (result fn parent-qname p elem)
- (dolist (entry entries)
+ (dolist (entry js2-imenu-recorder)
;; function node goes first
(destructuring-bind (current-fn &rest (&whole chain head &rest)) entry
;; Examine head's defining scope:
(gethash grandparent js2-imenu-function-map 'skip)))
'skip))
(puthash fn parent-qname js2-imenu-function-map))
- (unless (eq parent-qname 'skip)
- ;; prefix parent fn qname to this chain.
+ (if (eq parent-qname 'skip)
+ ;; We don't show it, let's record that fact.
+ (remhash current-fn js2-imenu-function-map)
+ ;; Prepend parent fn qname to this chain.
(let ((qname (append parent-qname chain)))
(puthash current-fn (butlast qname) js2-imenu-function-map)
(push qname result)))))))
- ;; finally replace each node in each chain with its name.
+ ;; Collect chains obtained by third-party code.
+ (let (js2-imenu-recorder)
+ (run-hooks 'js2-build-imenu-callbacks)
+ (dolist (entry js2-imenu-recorder)
+ (push (cdr entry) result)))
+ ;; Finally replace each node in each chain with its name.
(dolist (chain result)
(setq p chain)
(while p
(defun js2-build-imenu-index ()
"Turn `js2-imenu-recorder' into an imenu data structure."
- (unless (eq js2-imenu-recorder 'empty)
- (let* ((chains (js2-browse-postprocess-chains js2-imenu-recorder))
- (result (js2-build-alist-trie chains nil)))
- (js2-flatten-trie result))))
+ (when (eq js2-imenu-recorder 'empty)
+ (setq js2-imenu-recorder nil))
+ (let* ((chains (js2-browse-postprocess-chains))
+ (result (js2-build-alist-trie chains nil)))
+ (js2-flatten-trie result)))
(defun js2-test-print-chains (chains)
"Print a list of qname chains.