-For instance, following a 'this' reference requires a parent function node."
- (let ((js2-imenu-fn-type-map (make-hash-table :test 'eq))
- result head fn fn-type parent-chain p elem parent)
- (dolist (chain chains)
- ;; examine the head of each node to get its defining scope
- (setq head (car chain))
- ;; if top-level/external, keep as-is
- (if (js2-node-top-level-decl-p head)
- (push chain result)
- (cond
- ;; starts with this-reference
- ((js2-this-node-p head)
- (setq fn (js2-node-parent-script-or-fn head)
- chain (cdr chain))) ; discard this-node
- ;; nested named function
- ((js2-function-node-p (setq parent (js2-node-parent head)))
- (setq fn (js2-node-parent-script-or-fn parent)))
- ;; variable assigned a function expression
- (t (setq fn (js2-node-parent-script-or-fn head))))
- (when fn
- (setq fn-type (gethash fn js2-imenu-fn-type-map))
- (unless fn-type
- (setq fn-type
- (cond ((js2-nested-function-p fn) 'skip)
- ((setq parent-chain
- (gethash fn js2-imenu-function-map))
- 'named)
- ((js2-wrapper-function-p fn) 'anon)
- (t 'skip)))
- (puthash fn fn-type js2-imenu-fn-type-map))
- (case fn-type
- ('anon (push chain result)) ; anonymous top-level wrapper
- ('named ; top-level named function
- ;; prefix parent fn qname, which is
- ;; parent-chain sans last elem, to this chain.
- (push (append (butlast parent-chain) chain) result))))))
+For instance, processing a nested scope requires a parent function node."
+ (let (result head fn current-fn parent-qname qname p elem)
+ (dolist (entry entries)
+ ;; function node goes first
+ (destructuring-bind (current-fn &rest (&whole chain head &rest)) entry
+ ;; examine its defining scope;
+ ;; if top-level/external, keep as-is
+ (if (js2-node-top-level-decl-p head)
+ (push chain result)
+ (when (js2-this-node-p head)
+ (setq chain (cdr chain))) ; discard this-node
+ (when (setq fn (js2-node-parent-script-or-fn current-fn))
+ (setq parent-qname (gethash fn js2-imenu-function-map 'not-found))
+ (when (eq parent-qname 'not-found)
+ ;; anonymous function expressions are not recorded
+ ;; during the parse, so we need to handle this case here
+ (setq parent-qname
+ (if (js2-wrapper-function-p fn)
+ (let ((grandparent (js2-node-parent-script-or-fn fn)))
+ (if (js2-ast-root-p grandparent)
+ nil
+ (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.
+ (let ((qname (append parent-qname chain)))
+ (puthash current-fn (butlast qname) js2-imenu-function-map)
+ (push qname result)))))))