]> code.delx.au - gnu-emacs-elpa/blob - js2-imenu-extras.el
Implement imenu support for some frameworks and orphan functions
[gnu-emacs-elpa] / js2-imenu-extras.el
1 (eval-when-compile
2 (require 'cl))
3
4 (defconst js2-imenu-extension-styles
5 `((:framework jquery
6 :call-re "\\_<\\(?:jQuery\\|\\$\\|_\\)\\.extend\\s-*("
7 :recorder js2-imenu-record-jquery-extend)
8
9 (:framework jquery-ui
10 :call-re "^\\s-*\\(?:jQuery\\|\\$\\)\\.widget\\s-*("
11 :recorder js2-imenu-record-string-declare)
12
13 (:framework dojo
14 :call-re "^\\s-*dojo.declare\\s-*("
15 :recorder js2-imenu-record-string-declare)
16
17 (:framework backbone
18 :call-re ,(concat "\\_<" js2-mode-identifier-re "\\.extend\\s-*(")
19 :recorder js2-imenu-record-backbone-extend)))
20
21 (defconst js2-imenu-available-frameworks
22 (mapcar (lambda (style) (plist-get style :framework)) js2-imenu-extension-styles)
23 "List of available JavaScript framework symbols.")
24
25 (defcustom js2-imenu-enabled-frameworks js2-imenu-available-frameworks
26 "Frameworks to be recognized by `js2-mode'."
27 :type (cons 'set (mapcar (lambda (x) (list 'const x))
28 js2-imenu-available-frameworks))
29 :group 'js2-mode)
30
31 (defcustom js2-imenu-show-other-functions t
32 "Non-nil to show functions not recognized by other mechanisms,
33 in a shared namespace."
34 :type 'boolean
35 :group 'js2-mode)
36
37 (defcustom js2-imenu-other-functions-ns "?"
38 "Namespace name to use for other functions."
39 :type 'string
40 :group 'js2-mode)
41
42 (defun js2-imenu-extras-setup ()
43 (when js2-imenu-enabled-frameworks
44 (add-to-list 'js2-post-parse-callbacks 'js2-imenu-record-declarations t))
45 (when js2-imenu-show-other-functions
46 (add-to-list 'js2-post-parse-callbacks 'js2-imenu-record-hashes t)))
47
48 (declare (special root))
49
50 (defun js2-imenu-record-declarations ()
51 (let* ((styles (loop for style in js2-imenu-extension-styles
52 when (memq (plist-get style :framework)
53 js2-imenu-enabled-frameworks)
54 collect style))
55 (re (mapconcat (lambda (style)
56 (concat "\\(" (plist-get style :call-re) "\\)"))
57 styles "\\|"))
58 ;; Dynamic scoping. Ew.
59 (js2-mode-ast root)
60 chains)
61 (goto-char (point-min))
62 (while (js-re-search-forward re nil t)
63 (push (loop for i from 0 to (1- (length styles))
64 when (match-beginning (1+ i))
65 return (funcall (plist-get (nth i styles) :recorder)))
66 chains))
67 chains))
68
69 (defun js2-imenu-record-jquery-extend ()
70 (let ((pred (lambda (subject)
71 (and
72 (js2-prop-get-node-p subject)
73 (string= (js2-name-node-name (js2-prop-get-node-right subject))
74 "prototype")))))
75 (js2-imenu-record-extend-first-arg (1- (point)) pred
76 'js2-compute-nested-prop-get)))
77
78 (defun js2-imenu-record-string-declare ()
79 (js2-imenu-record-extend-first-arg
80 (1- (point)) 'js2-string-node-p
81 (lambda (node) (split-string (js2-string-node-value node) "\\." t))))
82
83 (defun js2-imenu-record-extend-first-arg (point pred qname-fn)
84 (let* ((node (js2-node-at-point point))
85 (args (js2-call-node-args node))
86 (subject (first args)))
87 (when (funcall pred subject)
88 (loop for arg in (cdr args)
89 when (js2-object-node-p arg)
90 do (js2-record-object-literal
91 arg (funcall qname-fn subject) (js2-node-abs-pos arg))))))
92
93 (defun js2-imenu-record-backbone-extend ()
94 (let* ((node (js2-node-at-point (1- (point))))
95 (args (js2-call-node-args node))
96 (methods (first args))
97 (parent (js2-node-parent node)))
98 (when (js2-object-node-p methods)
99 (let ((subject (cond ((js2-var-init-node-p parent)
100 (js2-var-init-node-target parent))
101 ((js2-assign-node-p parent)
102 (js2-assign-node-left parent)))))
103 (when subject
104 (js2-record-object-literal methods
105 (js2-compute-nested-prop-get subject)
106 (js2-node-abs-pos methods)))))))
107
108 (defun js2-imenu-record-hashes ()
109 (js2-visit-ast
110 root
111 (lambda (node end-p)
112 (if (and (js2-object-prop-node-p node)
113 (js2-function-node-p (js2-object-prop-node-right node)))
114 (let ((fn-node (js2-object-prop-node-right node)))
115 (unless (and js2-imenu-function-map
116 (gethash fn-node js2-imenu-function-map))
117 (let ((key-node (js2-object-prop-node-left node)))
118 (js2-record-imenu-entry fn-node
119 (list js2-imenu-other-functions-ns
120 (js2-prop-node-name key-node))
121 (js2-node-abs-pos key-node))))
122 nil)
123 t))))
124
125 (provide 'js2-imenu-extras)