27
|
1 ;;; js2.el -- an improved JavaScript editing mode |
|
2 ;;; |
|
3 ;;; This file was auto-generated on Mon Jun 16 01:46:45 2008 from files: |
|
4 ;;; js2-vars.el |
|
5 ;;; js2-util.el |
|
6 ;;; js2-scan.el |
|
7 ;;; js2-messages.el |
|
8 ;;; js2-ast.el |
|
9 ;;; js2-highlight.el |
|
10 ;;; js2-browse.el |
|
11 ;;; js2-parse.el |
|
12 ;;; js2-indent.el |
|
13 ;;; js2-mode.el |
|
14 |
|
15 ;;; js2-mode.el --- an improved JavaScript editing mode |
|
16 |
|
17 ;; Author: Steve Yegge (steve.yegge@gmail.com) |
|
18 ;; Version: 20080616 |
|
19 ;; Keywords: javascript languages |
|
20 |
|
21 ;; This program is free software; you can redistribute it and/or |
|
22 ;; modify it under the terms of the GNU General Public License as |
|
23 ;; published by the Free Software Foundation; either version 2 of |
|
24 ;; the License, or (at your option) any later version. |
|
25 |
|
26 ;; This program is distributed in the hope that it will be |
|
27 ;; useful, but WITHOUT ANY WARRANTY; without even the implied |
|
28 ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
|
29 ;; PURPOSE. See the GNU General Public License for more details. |
|
30 |
|
31 ;; You should have received a copy of the GNU General Public |
|
32 ;; License along with this program; if not, write to the Free |
|
33 ;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
|
34 ;; MA 02111-1307 USA |
|
35 |
|
36 ;;; Commentary: |
|
37 |
|
38 ;; This JavaScript editing mode supports: |
|
39 ;; |
|
40 ;; - the full JavaScript language through version 1.7 |
|
41 ;; - support for most Rhino and SpiderMonkey extensions from 1.5 to 1.7 |
|
42 ;; - accurate syntax highlighting using a recursive-descent parser |
|
43 ;; - syntax-error and strict-mode warning reporting |
|
44 ;; - "bouncing" line indentation to choose among alternate indentation points |
|
45 ;; - smart line-wrapping within comments (Emacs 22+) and strings |
|
46 ;; - code folding: |
|
47 ;; - show some or all function bodies as {...} |
|
48 ;; - show some or all block comments as /*...*/ |
|
49 ;; - context-sensitive menu bar and popup menus |
|
50 ;; - code browsing using the `imenu' package |
|
51 ;; - typing helpers (e.g. inserting matching braces/parens) |
|
52 ;; - many customization options |
|
53 ;; |
|
54 ;; It is only compatible with GNU Emacs versions 21 and higher (not XEmacs). |
|
55 ;; |
|
56 ;; Installation: |
|
57 ;; |
|
58 ;; - put `js2.el' somewhere in your emacs load path |
|
59 ;; - M-x byte-compile-file RET <path-to-js2.el> RET |
|
60 ;; Note: it will refuse to run unless byte-compiled |
|
61 ;; - add these lines to your .emacs file: |
|
62 ;; (autoload 'js2-mode "js2" nil t) |
|
63 ;; (add-to-list 'auto-mode-alist '("\\.js$" . js2-mode)) |
|
64 ;; |
|
65 ;; To customize how it works: |
|
66 ;; M-x customize-group RET js2-mode RET |
|
67 ;; |
|
68 ;; The variable `js2-mode-version' is a date stamp. When you upgrade |
|
69 ;; to a newer version, you must byte-compile the file again. |
|
70 ;; |
|
71 ;; Notes: |
|
72 ;; |
|
73 ;; This mode is different in many ways from standard Emacs language editing |
|
74 ;; modes, inasmuch as it attempts to be more like an IDE. If this drives |
|
75 ;; you crazy, it IS possible to customize it to be more like other Emacs |
|
76 ;; editing modes. Please customize the group `js2-mode' to see all of the |
|
77 ;; configuration options. |
|
78 ;; |
|
79 ;; Some of the functionality does not work in Emacs 21 -- upgrading to |
|
80 ;; Emacs 22 or higher will get you better results. If you byte-compiled |
|
81 ;; js2.el with Emacs 21, you should re-compile it for Emacs 22. |
|
82 ;; |
|
83 ;; Unlike cc-engine based language modes, js2-mode's line-indentation is not |
|
84 ;; customizable. It is a surprising amount of work to support customizable |
|
85 ;; indentation. The current compromise is that the tab key lets you cycle among |
|
86 ;; various likely indentation points, similar to the behavior of python-mode. |
|
87 ;; |
|
88 ;; This mode does not yet work with "multi-mode" modes such as mmm-mode |
|
89 ;; and mumamo, although it could possibly be made to do so with some effort. |
|
90 ;; This means that js2-mode is currently only useful for editing JavaScript |
|
91 ;; files, and not for editing JavaScript within <script> tags or templates. |
|
92 ;; |
|
93 ;; This code is part of a larger project, in progress, to enable writing |
|
94 ;; Emacs customizations in JavaScript. |
|
95 ;; |
|
96 ;; Please email bug reports and suggestions to the author, or submit them |
|
97 ;; at http://code.google.com/p/js2-mode/issues |
|
98 |
|
99 ;; TODO: |
|
100 ;; - add unreachable-code warning (error?) using the inconsistent-return analysis |
|
101 ;; - labeled stmt length is now 1 |
|
102 ;; - "anonymous function does not always return a value" - use getter/setter name |
|
103 ;; - extend js2-missing-semi-one-line-override to handle catch (e) {return x} |
|
104 ;; - set a text prop on autoinserted delimiters and don't biff user-entered ones |
|
105 ;; - when inserting magic curlies, look for matching close-curly before inserting |
|
106 ;; - get more use out of the symbol table: |
|
107 ;; - jump to declaration (put hyperlinks on all non-decl var usages?) |
|
108 ;; - rename variable/function |
|
109 ;; - warn on unused var |
|
110 ;; - add some dabbrev-expansions for built-in keywords like finally, function |
|
111 ;; - add at least some completion support, e.g. for built-ins |
|
112 ;; - code formatting |
|
113 |
|
114 ;;; Code: |
|
115 ;;; js2-vars.el -- byte-compiler support for js2-mode |
|
116 |
|
117 ;; Author: Steve Yegge (steve.yegge@gmail.com) |
|
118 ;; Keywords: javascript languages |
|
119 |
|
120 ;;; Code: |
|
121 |
|
122 (eval-when-compile |
|
123 (require 'cl)) |
|
124 |
|
125 (eval-and-compile |
|
126 (require 'cc-mode) ; (only) for `c-populate-syntax-table' |
|
127 (require 'cc-langs) ; it's here in Emacs 21... |
|
128 (require 'cc-engine)) ; for `c-paragraph-start' et. al. |
|
129 |
|
130 (defvar js2-emacs22 (>= emacs-major-version 22)) |
|
131 |
|
132 (defcustom js2-highlight-level 2 |
|
133 "Amount of syntax highlighting to perform. |
|
134 nil, zero or negative means none. |
|
135 1 adds basic syntax highlighting. |
|
136 2 adds highlighting of some Ecma built-in properties. |
|
137 3 adds highlighting of many Ecma built-in functions." |
|
138 :type 'integer |
|
139 :group 'js2-mode) |
|
140 |
|
141 (defvar js2-mode-dev-mode-p nil |
|
142 "Non-nil if running in development mode. Normally nil.") |
|
143 |
|
144 (defgroup js2-mode nil |
|
145 "An improved JavaScript mode." |
|
146 :group 'languages) |
|
147 |
|
148 (defcustom js2-basic-offset (if (and (boundp 'c-basic-offset) |
|
149 (numberp c-basic-offset)) |
|
150 c-basic-offset |
|
151 2) |
|
152 "Number of spaces to indent nested statements. |
|
153 Similar to `c-basic-offset'." |
|
154 :group 'js2-mode |
|
155 :type 'integer) |
|
156 (make-variable-buffer-local 'js2-basic-offset) |
|
157 |
|
158 (defcustom js2-cleanup-whitespace t |
|
159 "Non-nil to invoke `delete-trailing-whitespace' before saves." |
|
160 :type 'boolean |
|
161 :group 'js2-mode) |
|
162 |
|
163 (defcustom js2-move-point-on-right-click t |
|
164 "Non-nil to move insertion point when you right-click. |
|
165 This makes right-click context menu behavior a bit more intuitive, |
|
166 since menu operations generally apply to the point. The exception |
|
167 is if there is a region selection, in which case the point does -not- |
|
168 move, so cut/copy/paste etc. can work properly. |
|
169 |
|
170 Note that IntelliJ moves the point, and Eclipse leaves it alone, |
|
171 so this behavior is customizable." |
|
172 :group 'js2-mode |
|
173 :type 'boolean) |
|
174 |
|
175 (defcustom js2-mirror-mode t |
|
176 "Non-nil to insert closing brackets, parens, etc. automatically." |
|
177 :group 'js2-mode |
|
178 :type 'boolean) |
|
179 |
|
180 (defcustom js2-auto-indent-flag t |
|
181 "Automatic indentation with punctuation characters. If non-nil, the |
|
182 current line is indented when certain punctuations are inserted." |
|
183 :group 'js2-mode |
|
184 :type 'boolean) |
|
185 |
|
186 (defcustom js2-bounce-indent-flag t |
|
187 "Non-nil to have indent-line function choose among alternatives. |
|
188 If nil, the indent-line function will indent to a predetermined column |
|
189 based on heuristic guessing. If non-nil, then if the current line is |
|
190 already indented to that predetermined column, indenting will choose |
|
191 another likely column and indent to that spot. Repeated invocation of |
|
192 the indent-line function will cycle among the computed alternatives. |
|
193 See the function `js2-bounce-indent' for details." |
|
194 :type 'boolean |
|
195 :group 'js2-mode) |
|
196 |
|
197 (defcustom js2-indent-on-enter-key nil |
|
198 "Non-nil to have Enter/Return key indent the line. |
|
199 This is unusual for Emacs modes but common in IDEs like Eclipse." |
|
200 :type 'boolean |
|
201 :group 'js2-mode) |
|
202 |
|
203 (defcustom js2-enter-indents-newline t |
|
204 "Non-nil to have Enter/Return key indent the newly-inserted line. |
|
205 This is unusual for Emacs modes but common in IDEs like Eclipse." |
|
206 :type 'boolean |
|
207 :group 'js2-mode) |
|
208 |
|
209 (defcustom js2-rebind-eol-bol-keys t |
|
210 "Non-nil to rebind beginning-of-line and end-of-line keys. |
|
211 If non-nil, bounce between bol/eol and first/last non-whitespace char." |
|
212 :group 'js2-mode |
|
213 :type 'boolean) |
|
214 |
|
215 (defcustom js2-electric-keys '("{" "}" "(" ")" "[" "]" ":" ";" "," "*") |
|
216 "Keys that auto-indent when `js2-auto-indent-flag' is non-nil. |
|
217 Each value in the list is passed to `define-key'." |
|
218 :type 'list |
|
219 :group 'js2-mode) |
|
220 |
|
221 (defcustom js2-idle-timer-delay 0.2 |
|
222 "Delay in secs before re-parsing after user makes changes. |
|
223 Multiplied by `js2-dynamic-idle-timer-adjust', which see." |
|
224 :type 'number |
|
225 :group 'js2-mode) |
|
226 (make-variable-buffer-local 'js2-idle-timer-delay) |
|
227 |
|
228 (defcustom js2-dynamic-idle-timer-adjust 0 |
|
229 "Positive to adjust `js2-idle-timer-delay' based on file size. |
|
230 The idea is that for short files, parsing is faster so we can be |
|
231 more responsive to user edits without interfering with editing. |
|
232 The buffer length in characters (typically bytes) is divided by |
|
233 this value and used to multiply `js2-idle-timer-delay' for the |
|
234 buffer. For example, a 21k file and 10k adjust yields 21k/10k |
|
235 == 2, so js2-idle-timer-delay is multiplied by 2. |
|
236 If `js2-dynamic-idle-timer-adjust' is 0 or negative, |
|
237 `js2-idle-timer-delay' is not dependent on the file size." |
|
238 :type 'number |
|
239 :group 'js2-mode) |
|
240 |
|
241 (defcustom js2-mode-escape-quotes t |
|
242 "Non-nil to disable automatic quote-escaping inside strings." |
|
243 :type 'boolean |
|
244 :group 'js2-mode) |
|
245 |
|
246 (defcustom js2-mode-squeeze-spaces t |
|
247 "Non-nil to normalize whitespace when filling in comments. |
|
248 Multiple runs of spaces are converted to a single space." |
|
249 :type 'boolean |
|
250 :group 'js2-mode) |
|
251 |
|
252 (defcustom js2-mode-show-parse-errors t |
|
253 "True to highlight parse errors." |
|
254 :type 'boolean |
|
255 :group 'js2-mode) |
|
256 |
|
257 (defcustom js2-mode-show-strict-warnings t |
|
258 "Non-nil to emit Ecma strict-mode warnings. |
|
259 Some of the warnings can be individually disabled by other flags, |
|
260 even if this flag is non-nil." |
|
261 :type 'boolean |
|
262 :group 'js2-mode) |
|
263 |
|
264 (defcustom js2-strict-trailing-comma-warning t |
|
265 "Non-nil to warn about trailing commas in array literals. |
|
266 Ecma-262 forbids them, but many browsers permit them. IE is the |
|
267 big exception, and can produce bugs if you have trailing commas." |
|
268 :type 'boolean |
|
269 :group 'js2-mode) |
|
270 |
|
271 (defcustom js2-strict-missing-semi-warning t |
|
272 "Non-nil to warn about semicolon auto-insertion after statement. |
|
273 Technically this is legal per Ecma-262, but some style guides disallow |
|
274 depending on it." |
|
275 :type 'boolean |
|
276 :group 'js2-mode) |
|
277 |
|
278 (defcustom js2-missing-semi-one-line-override nil |
|
279 "Non-nil to permit missing semicolons in one-line functions. |
|
280 In one-liner functions such as `function identity(x) {return x}' |
|
281 people often omit the semicolon for a cleaner look. If you are |
|
282 such a person, you can suppress the missing-semicolon warning |
|
283 by setting this variable to t." |
|
284 :type 'boolean |
|
285 :group 'js2-mode) |
|
286 |
|
287 (defcustom js2-strict-inconsistent-return-warning t |
|
288 "Non-nil to warn about mixing returns with value-returns. |
|
289 It's perfectly legal to have a `return' and a `return foo' in the |
|
290 same function, but it's often an indicator of a bug, and it also |
|
291 interferes with type inference (in systems that support it.)" |
|
292 :type 'boolean |
|
293 :group 'js2-mode) |
|
294 |
|
295 (defcustom js2-strict-cond-assign-warning t |
|
296 "Non-nil to warn about expressions like if (a = b). |
|
297 This often should have been '==' instead of '='. If the warning |
|
298 is enabled, you can suppress it on a per-expression basis by |
|
299 parenthesizing the expression, e.g. if ((a = b)) ..." |
|
300 :type 'boolean |
|
301 :group 'js2-mode) |
|
302 |
|
303 (defcustom js2-strict-cond-assign-warning t |
|
304 "Non-nil to warn about expressions like if (a = b). |
|
305 This often should have been '==' instead of '='. If the warning |
|
306 is enabled, you can suppress it on a per-expression basis by |
|
307 parenthesizing the expression, e.g. if ((a = b)) ..." |
|
308 :type 'boolean |
|
309 :group 'js2-mode) |
|
310 |
|
311 (defcustom js2-strict-var-redeclaration-warning t |
|
312 "Non-nil to warn about redeclaring variables in a script or function." |
|
313 :type 'boolean |
|
314 :group 'js2-mode) |
|
315 |
|
316 (defcustom js2-strict-var-hides-function-arg-warning t |
|
317 "Non-nil to warn about a var decl hiding a function argument." |
|
318 :type 'boolean |
|
319 :group 'js2-mode) |
|
320 |
|
321 (defcustom js2-skip-preprocessor-directives nil |
|
322 "Non-nil to treat lines beginning with # as comments. |
|
323 Useful for viewing Mozilla JavaScript source code." |
|
324 :type 'boolean |
|
325 :group 'js2-mode) |
|
326 |
|
327 (defcustom js2-basic-offset c-basic-offset |
|
328 "Functions like `c-basic-offset' in js2-mode buffers." |
|
329 :type 'integer |
|
330 :group 'js2-mode) |
|
331 (make-variable-buffer-local 'js2-basic-offset) |
|
332 |
|
333 (defcustom js2-language-version 170 |
|
334 "Configures what JavaScript language version to recognize. |
|
335 Currently only 150, 160 and 170 are supported, corresponding |
|
336 to JavaScript 1.5, 1.6 and 1.7, respectively. In a nutshell, |
|
337 1.6 adds E4X support, and 1.7 adds let, yield, and Array |
|
338 comprehensions." |
|
339 :type 'integer |
|
340 :group 'js2-mode) |
|
341 |
|
342 (defcustom js2-allow-keywords-as-property-names t |
|
343 "If non-nil, you can use JavaScript keywords as object property names. |
|
344 Examples: |
|
345 |
|
346 var foo = {int: 5, while: 6, continue: 7}; |
|
347 foo.return = 8; |
|
348 |
|
349 Ecma-262 forbids this syntax, but many browsers support it." |
|
350 :type 'boolean |
|
351 :group 'js2-mode) |
|
352 |
|
353 (defcustom js2-instanceof-has-side-effects nil |
|
354 "If non-nil, treats the instanceof operator as having side effects. |
|
355 This is useful for xulrunner apps." |
|
356 :type 'boolean |
|
357 :group 'js2-mode) |
|
358 |
|
359 (defcustom js2-allow-rhino-new-expr-initializer nil |
|
360 "Non-nil to support a Rhino's experimental syntactic construct. |
|
361 |
|
362 Rhino supports the ability to follow a `new' expression with an object |
|
363 literal, which is used to set additional properties on the new object |
|
364 after calling its constructor. Syntax: |
|
365 |
|
366 new <expr> [ ( arglist ) ] [initializer] |
|
367 |
|
368 Hence, this expression: |
|
369 |
|
370 new Object {a: 1, b: 2} |
|
371 |
|
372 results in an Object with properties a=1 and b=2. This syntax is |
|
373 apparently not configurable in Rhino - it's currently always enabled, |
|
374 as of Rhino version 1.7R2." |
|
375 :type 'boolean |
|
376 :group 'js2-mode) |
|
377 |
|
378 (defcustom js2-allow-member-expr-as-function-name nil |
|
379 "Non-nil to support experimental Rhino syntax for function names. |
|
380 |
|
381 Rhino supports an experimental syntax configured via the Rhino Context |
|
382 setting `allowMemberExprAsFunctionName'. The experimental syntax is: |
|
383 |
|
384 function <member-expr> ( [ arg-list ] ) { <body> } |
|
385 |
|
386 Where member-expr is a non-parenthesized 'member expression', which |
|
387 is anything at the grammar level of a new-expression or lower, meaning |
|
388 any expression that does not involve infix or unary operators. |
|
389 |
|
390 When <member-expr> is not a simple identifier, then it is syntactic |
|
391 sugar for assigning the anonymous function to the <member-expr>. Hence, |
|
392 this code: |
|
393 |
|
394 function a.b().c[2] (x, y) { ... } |
|
395 |
|
396 is rewritten as: |
|
397 |
|
398 a.b().c[2] = function(x, y) {...} |
|
399 |
|
400 which doesn't seem particularly useful, but Rhino permits it." |
|
401 :type 'boolean |
|
402 :group 'js2-mode) |
|
403 |
|
404 (defvar js2-mode-version 20080616 |
|
405 "Release number for `js2-mode'.") |
|
406 |
|
407 ;; scanner variables |
|
408 |
|
409 ;; We record the start and end position of each token. |
|
410 (defvar js2-token-beg 1) |
|
411 (make-variable-buffer-local 'js2-token-beg) |
|
412 (defvar js2-token-end -1) |
|
413 (make-variable-buffer-local 'js2-token-end) |
|
414 |
|
415 (defvar js2-EOF_CHAR -1 |
|
416 "Represents end of stream. Distinct from js2-EOF token type.") |
|
417 |
|
418 ;; I originally used symbols to represent tokens, but Rhino uses |
|
419 ;; ints and then sets various flag bits in them, so ints it is. |
|
420 ;; The upshot is that we need a `js2-' prefix in front of each name. |
|
421 (defvar js2-ERROR -1) |
|
422 (defvar js2-EOF 0) |
|
423 (defvar js2-EOL 1) |
|
424 (defvar js2-ENTERWITH 2) ; begin interpreter bytecodes |
|
425 (defvar js2-LEAVEWITH 3) |
|
426 (defvar js2-RETURN 4) |
|
427 (defvar js2-GOTO 5) |
|
428 (defvar js2-IFEQ 6) |
|
429 (defvar js2-IFNE 7) |
|
430 (defvar js2-SETNAME 8) |
|
431 (defvar js2-BITOR 9) |
|
432 (defvar js2-BITXOR 10) |
|
433 (defvar js2-BITAND 11) |
|
434 (defvar js2-EQ 12) |
|
435 (defvar js2-NE 13) |
|
436 (defvar js2-LT 14) |
|
437 (defvar js2-LE 15) |
|
438 (defvar js2-GT 16) |
|
439 (defvar js2-GE 17) |
|
440 (defvar js2-LSH 18) |
|
441 (defvar js2-RSH 19) |
|
442 (defvar js2-URSH 20) |
|
443 (defvar js2-ADD 21) ; infix plus |
|
444 (defvar js2-SUB 22) ; infix minus |
|
445 (defvar js2-MUL 23) |
|
446 (defvar js2-DIV 24) |
|
447 (defvar js2-MOD 25) |
|
448 (defvar js2-NOT 26) |
|
449 (defvar js2-BITNOT 27) |
|
450 (defvar js2-POS 28) ; unary plus |
|
451 (defvar js2-NEG 29) ; unary minus |
|
452 (defvar js2-NEW 30) |
|
453 (defvar js2-DELPROP 31) |
|
454 (defvar js2-TYPEOF 32) |
|
455 (defvar js2-GETPROP 33) |
|
456 (defvar js2-GETPROPNOWARN 34) |
|
457 (defvar js2-SETPROP 35) |
|
458 (defvar js2-GETELEM 36) |
|
459 (defvar js2-SETELEM 37) |
|
460 (defvar js2-CALL 38) |
|
461 (defvar js2-NAME 39) ; an identifier |
|
462 (defvar js2-NUMBER 40) |
|
463 (defvar js2-STRING 41) |
|
464 (defvar js2-NULL 42) |
|
465 (defvar js2-THIS 43) |
|
466 (defvar js2-FALSE 44) |
|
467 (defvar js2-TRUE 45) |
|
468 (defvar js2-SHEQ 46) ; shallow equality (===) |
|
469 (defvar js2-SHNE 47) ; shallow inequality (!==) |
|
470 (defvar js2-REGEXP 48) |
|
471 (defvar js2-BINDNAME 49) |
|
472 (defvar js2-THROW 50) |
|
473 (defvar js2-RETHROW 51) ; rethrow caught exception: catch (e if ) uses it |
|
474 (defvar js2-IN 52) |
|
475 (defvar js2-INSTANCEOF 53) |
|
476 (defvar js2-LOCAL_LOAD 54) |
|
477 (defvar js2-GETVAR 55) |
|
478 (defvar js2-SETVAR 56) |
|
479 (defvar js2-CATCH_SCOPE 57) |
|
480 (defvar js2-ENUM_INIT_KEYS 58) |
|
481 (defvar js2-ENUM_INIT_VALUES 59) |
|
482 (defvar js2-ENUM_INIT_ARRAY 60) |
|
483 (defvar js2-ENUM_NEXT 61) |
|
484 (defvar js2-ENUM_ID 62) |
|
485 (defvar js2-THISFN 63) |
|
486 (defvar js2-RETURN_RESULT 64) ; to return previously stored return result |
|
487 (defvar js2-ARRAYLIT 65) ; array literal |
|
488 (defvar js2-OBJECTLIT 66) ; object literal |
|
489 (defvar js2-GET_REF 67) ; *reference |
|
490 (defvar js2-SET_REF 68) ; *reference = something |
|
491 (defvar js2-DEL_REF 69) ; delete reference |
|
492 (defvar js2-REF_CALL 70) ; f(args) = something or f(args)++ |
|
493 (defvar js2-REF_SPECIAL 71) ; reference for special properties like __proto |
|
494 (defvar js2-YIELD 72) ; JS 1.7 yield pseudo keyword |
|
495 |
|
496 ;; XML support |
|
497 (defvar js2-DEFAULTNAMESPACE 73) |
|
498 (defvar js2-ESCXMLATTR 74) |
|
499 (defvar js2-ESCXMLTEXT 75) |
|
500 (defvar js2-REF_MEMBER 76) ; Reference for x.@y, x..y etc. |
|
501 (defvar js2-REF_NS_MEMBER 77) ; Reference for x.ns::y, x..ns::y etc. |
|
502 (defvar js2-REF_NAME 78) ; Reference for @y, @[y] etc. |
|
503 (defvar js2-REF_NS_NAME 79) ; Reference for ns::y, @ns::y@[y] etc. |
|
504 |
|
505 (defvar js2-first-bytecode js2-ENTERWITH) |
|
506 (defvar js2-last-bytecode js2-REF_NS_NAME) |
|
507 |
|
508 (defvar js2-TRY 80) |
|
509 (defvar js2-SEMI 81) ; semicolon |
|
510 (defvar js2-LB 82) ; left and right brackets |
|
511 (defvar js2-RB 83) |
|
512 (defvar js2-LC 84) ; left and right curly-braces |
|
513 (defvar js2-RC 85) |
|
514 (defvar js2-LP 86) ; left and right parens |
|
515 (defvar js2-RP 87) |
|
516 (defvar js2-COMMA 88) ; comma operator |
|
517 |
|
518 (defvar js2-ASSIGN 89) ; simple assignment (=) |
|
519 (defvar js2-ASSIGN_BITOR 90) ; |= |
|
520 (defvar js2-ASSIGN_BITXOR 91) ; ^= |
|
521 (defvar js2-ASSIGN_BITAND 92) ; &= |
|
522 (defvar js2-ASSIGN_LSH 93) ; <<= |
|
523 (defvar js2-ASSIGN_RSH 94) ; >>= |
|
524 (defvar js2-ASSIGN_URSH 95) ; >>>= |
|
525 (defvar js2-ASSIGN_ADD 96) ; += |
|
526 (defvar js2-ASSIGN_SUB 97) ; -= |
|
527 (defvar js2-ASSIGN_MUL 98) ; *= |
|
528 (defvar js2-ASSIGN_DIV 99) ; /= |
|
529 (defvar js2-ASSIGN_MOD 100) ; %= |
|
530 |
|
531 (defvar js2-first-assign js2-ASSIGN) |
|
532 (defvar js2-last-assign js2-ASSIGN_MOD) |
|
533 |
|
534 (defvar js2-HOOK 101) ; conditional (?:) |
|
535 (defvar js2-COLON 102) |
|
536 (defvar js2-OR 103) ; logical or (||) |
|
537 (defvar js2-AND 104) ; logical and (&&) |
|
538 (defvar js2-INC 105) ; increment/decrement (++ --) |
|
539 (defvar js2-DEC 106) |
|
540 (defvar js2-DOT 107) ; member operator (.) |
|
541 (defvar js2-FUNCTION 108) ; function keyword |
|
542 (defvar js2-EXPORT 109) ; export keyword |
|
543 (defvar js2-IMPORT 110) ; import keyword |
|
544 (defvar js2-IF 111) ; if keyword |
|
545 (defvar js2-ELSE 112) ; else keyword |
|
546 (defvar js2-SWITCH 113) ; switch keyword |
|
547 (defvar js2-CASE 114) ; case keyword |
|
548 (defvar js2-DEFAULT 115) ; default keyword |
|
549 (defvar js2-WHILE 116) ; while keyword |
|
550 (defvar js2-DO 117) ; do keyword |
|
551 (defvar js2-FOR 118) ; for keyword |
|
552 (defvar js2-BREAK 119) ; break keyword |
|
553 (defvar js2-CONTINUE 120) ; continue keyword |
|
554 (defvar js2-VAR 121) ; var keyword |
|
555 (defvar js2-WITH 122) ; with keyword |
|
556 (defvar js2-CATCH 123) ; catch keyword |
|
557 (defvar js2-FINALLY 124) ; finally keyword |
|
558 (defvar js2-VOID 125) ; void keyword |
|
559 (defvar js2-RESERVED 126) ; reserved keywords |
|
560 |
|
561 (defvar js2-EMPTY 127) |
|
562 |
|
563 ;; Types used for the parse tree - never returned by scanner. |
|
564 |
|
565 (defvar js2-BLOCK 128) ; statement block |
|
566 (defvar js2-LABEL 129) ; label |
|
567 (defvar js2-TARGET 130) |
|
568 (defvar js2-LOOP 131) |
|
569 (defvar js2-EXPR_VOID 132) ; expression statement in functions |
|
570 (defvar js2-EXPR_RESULT 133) ; expression statement in scripts |
|
571 (defvar js2-JSR 134) |
|
572 (defvar js2-SCRIPT 135) ; top-level node for entire script |
|
573 (defvar js2-TYPEOFNAME 136) ; for typeof(simple-name) |
|
574 (defvar js2-USE_STACK 137) |
|
575 (defvar js2-SETPROP_OP 138) ; x.y op= something |
|
576 (defvar js2-SETELEM_OP 139) ; x[y] op= something |
|
577 (defvar js2-LOCAL_BLOCK 140) |
|
578 (defvar js2-SET_REF_OP 141) ; *reference op= something |
|
579 |
|
580 ;; For XML support: |
|
581 (defvar js2-DOTDOT 142) ; member operator (..) |
|
582 (defvar js2-COLONCOLON 143) ; namespace::name |
|
583 (defvar js2-XML 144) ; XML type |
|
584 (defvar js2-DOTQUERY 145) ; .() -- e.g., x.emps.emp.(name == "terry") |
|
585 (defvar js2-XMLATTR 146) ; @ |
|
586 (defvar js2-XMLEND 147) |
|
587 |
|
588 ;; Optimizer-only tokens |
|
589 (defvar js2-TO_OBJECT 148) |
|
590 (defvar js2-TO_DOUBLE 149) |
|
591 |
|
592 (defvar js2-GET 150) ; JS 1.5 get pseudo keyword |
|
593 (defvar js2-SET 151) ; JS 1.5 set pseudo keyword |
|
594 (defvar js2-LET 152) ; JS 1.7 let pseudo keyword |
|
595 (defvar js2-CONST 153) |
|
596 (defvar js2-SETCONST 154) |
|
597 (defvar js2-SETCONSTVAR 155) |
|
598 (defvar js2-ARRAYCOMP 156) |
|
599 (defvar js2-LETEXPR 157) |
|
600 (defvar js2-WITHEXPR 158) |
|
601 (defvar js2-DEBUGGER 159) |
|
602 |
|
603 (defvar js2-COMMENT 160) ; not yet in Rhino |
|
604 |
|
605 (defvar js2-num-tokens (1+ js2-COMMENT)) |
|
606 |
|
607 (defconst js2-debug-print-trees nil) |
|
608 |
|
609 ;; Rhino accepts any string or stream as input. |
|
610 ;; Emacs character processing works best in buffers, so we'll |
|
611 ;; assume the input is a buffer. JavaScript strings can be |
|
612 ;; copied into temp buffers before scanning them. |
|
613 |
|
614 (defmacro deflocal (name value comment) |
|
615 `(progn |
|
616 (defvar ,name ,value ,comment) |
|
617 (make-variable-buffer-local ',name))) |
|
618 |
|
619 ;; Buffer-local variables yield much cleaner code than using `defstruct'. |
|
620 ;; They're the Emacs equivalent of instance variables, more or less. |
|
621 |
|
622 (deflocal js2-ts-dirty-line nil |
|
623 "Token stream buffer-local variable. |
|
624 Indicates stuff other than whitespace since start of line.") |
|
625 |
|
626 (deflocal js2-ts-regexp-flags nil |
|
627 "Token stream buffer-local variable.") |
|
628 |
|
629 (deflocal js2-ts-string "" |
|
630 "Token stream buffer-local variable. |
|
631 Last string scanned.") |
|
632 |
|
633 (deflocal js2-ts-number nil |
|
634 "Token stream buffer-local variable. |
|
635 Last literal number scanned.") |
|
636 |
|
637 (deflocal js2-ts-hit-eof nil |
|
638 "Token stream buffer-local variable.") |
|
639 |
|
640 (deflocal js2-ts-line-start 0 |
|
641 "Token stream buffer-local variable.") |
|
642 |
|
643 (deflocal js2-ts-lineno 1 |
|
644 "Token stream buffer-local variable.") |
|
645 |
|
646 (deflocal js2-ts-line-end-char -1 |
|
647 "Token stream buffer-local variable.") |
|
648 |
|
649 (deflocal js2-ts-cursor 1 ; emacs buffers are 1-indexed |
|
650 "Token stream buffer-local variable. |
|
651 Current scan position.") |
|
652 |
|
653 (deflocal js2-ts-is-xml-attribute nil |
|
654 "Token stream buffer-local variable.") |
|
655 |
|
656 (deflocal js2-ts-xml-is-tag-content nil |
|
657 "Token stream buffer-local variable.") |
|
658 |
|
659 (deflocal js2-ts-xml-open-tags-count 0 |
|
660 "Token stream buffer-local variable.") |
|
661 |
|
662 (deflocal js2-ts-string-buffer nil |
|
663 "Token stream buffer-local variable. |
|
664 List of chars built up while scanning various tokens.") |
|
665 |
|
666 (deflocal js2-ts-comment-type nil |
|
667 "Token stream buffer-local variable.") |
|
668 |
|
669 ;;; Parser variables |
|
670 |
|
671 (defvar js2-parsed-errors nil |
|
672 "List of errors produced during scanning/parsing.") |
|
673 (make-variable-buffer-local 'js2-parsed-errors) |
|
674 |
|
675 (defvar js2-parsed-warnings nil |
|
676 "List of warnings produced during scanning/parsing.") |
|
677 (make-variable-buffer-local 'js2-parsed-warnings) |
|
678 |
|
679 (defvar js2-recover-from-parse-errors t |
|
680 "Non-nil to continue parsing after a syntax error. |
|
681 |
|
682 In recovery mode, the AST will be built in full, and any error |
|
683 nodes will be flagged with appropriate error information. If |
|
684 this flag is nil, a syntax error will result in an error being |
|
685 signaled. |
|
686 |
|
687 The variable is automatically buffer-local, because different |
|
688 modes that use the parser will need different settings.") |
|
689 (make-variable-buffer-local 'js2-recover-from-parse-errors) |
|
690 |
|
691 (defvar js2-parse-hook nil |
|
692 "List of callbacks for receiving parsing progress.") |
|
693 (make-variable-buffer-local 'js2-parse-hook) |
|
694 |
|
695 (defvar js2-parse-finished-hook nil |
|
696 "List of callbacks to notify when parsing finishes. |
|
697 Not called if parsing was interrupted.") |
|
698 |
|
699 (defvar js2-is-eval-code nil |
|
700 "True if we're evaluating code in a string. |
|
701 If non-nil, the tokenizer will record the token text, and the AST nodes |
|
702 will record their source text. Off by default for IDE modes, since the |
|
703 text is available in the buffer.") |
|
704 (make-variable-buffer-local 'js2-is-eval-code) |
|
705 |
|
706 (defvar js2-parse-ide-mode t |
|
707 "Non-nil if the parser is being used for `js2-mode'. |
|
708 If non-nil, the parser will set text properties for fontification |
|
709 and the syntax-table. The value should be nil when using the |
|
710 parser as a frontend to an interpreter or byte compiler.") |
|
711 |
|
712 ;;; Parser instance variables (buffer-local vars for js2-parse) |
|
713 |
|
714 (defconst js2-clear-ti-mask #xFFFF |
|
715 "Mask to clear token information bits.") |
|
716 |
|
717 (defconst js2-ti-after-eol (lsh 1 16) |
|
718 "Flag: first token of the source line.") |
|
719 |
|
720 (defconst js2-ti-check-label (lsh 1 17) |
|
721 "Flag: indicates to check for label.") |
|
722 |
|
723 ;; Inline Rhino's CompilerEnvirons vars as buffer-locals. |
|
724 |
|
725 (defvar js2-compiler-generate-debug-info t) |
|
726 (make-variable-buffer-local 'js2-compiler-generate-debug-info) |
|
727 |
|
728 (defvar js2-compiler-use-dynamic-scope nil) |
|
729 (make-variable-buffer-local 'js2-compiler-use-dynamic-scope) |
|
730 |
|
731 (defvar js2-compiler-reserved-keywords-as-identifier nil) |
|
732 (make-variable-buffer-local 'js2-compiler-reserved-keywords-as-identifier) |
|
733 |
|
734 (defvar js2-compiler-xml-available t) |
|
735 (make-variable-buffer-local 'js2-compiler-xml-available) |
|
736 |
|
737 (defvar js2-compiler-optimization-level 0) |
|
738 (make-variable-buffer-local 'js2-compiler-optimization-level) |
|
739 |
|
740 (defvar js2-compiler-generating-source t) |
|
741 (make-variable-buffer-local 'js2-compiler-generating-source) |
|
742 |
|
743 (defvar js2-compiler-strict-mode nil) |
|
744 (make-variable-buffer-local 'js2-compiler-strict-mode) |
|
745 |
|
746 (defvar js2-compiler-report-warning-as-error nil) |
|
747 (make-variable-buffer-local 'js2-compiler-report-warning-as-error) |
|
748 |
|
749 (defvar js2-compiler-generate-observer-count nil) |
|
750 (make-variable-buffer-local 'js2-compiler-generate-observer-count) |
|
751 |
|
752 (defvar js2-compiler-activation-names nil) |
|
753 (make-variable-buffer-local 'js2-compiler-activation-names) |
|
754 |
|
755 ;; SKIP: sourceURI |
|
756 |
|
757 ;; There's a compileFunction method in Context.java - may need it. |
|
758 (defvar js2-called-by-compile-function nil |
|
759 "True if `js2-parse' was called by `js2-compile-function'. |
|
760 Will only be used when we finish implementing the interpreter.") |
|
761 (make-variable-buffer-local 'js2-called-by-compile-function) |
|
762 |
|
763 ;; SKIP: ts (we just call `js2-init-scanner' and use its vars) |
|
764 |
|
765 (defvar js2-current-flagged-token js2-EOF) |
|
766 (make-variable-buffer-local 'js2-current-flagged-token) |
|
767 |
|
768 (defvar js2-current-token js2-EOF) |
|
769 (make-variable-buffer-local 'js2-current-token) |
|
770 |
|
771 ;; SKIP: node factory - we're going to just call functions directly, |
|
772 ;; and eventually go to a unified AST format. |
|
773 |
|
774 (defvar js2-nesting-of-function 0) |
|
775 (make-variable-buffer-local 'js2-nesting-of-function) |
|
776 |
|
777 (defvar js2-recorded-assignments nil) |
|
778 (make-variable-buffer-local 'js2-assignments-from-parse) |
|
779 |
|
780 ;; SKIP: decompiler |
|
781 ;; SKIP: encoded-source |
|
782 |
|
783 ;;; These variables are per-function and should be saved/restored |
|
784 ;;; during function parsing. |
|
785 |
|
786 (defvar js2-current-script-or-fn nil) |
|
787 (make-variable-buffer-local 'js2-current-script-or-fn) |
|
788 |
|
789 (defvar js2-current-scope nil) |
|
790 (make-variable-buffer-local 'js2-current-scope) |
|
791 |
|
792 (defvar js2-nesting-of-with 0) |
|
793 (make-variable-buffer-local 'js2-nesting-of-with) |
|
794 |
|
795 (defvar js2-label-set nil |
|
796 "An alist mapping label names to nodes.") |
|
797 (make-variable-buffer-local 'js2-label-set) |
|
798 |
|
799 (defvar js2-loop-set nil) |
|
800 (make-variable-buffer-local 'js2-loop-set) |
|
801 |
|
802 (defvar js2-loop-and-switch-set nil) |
|
803 (make-variable-buffer-local 'js2-loop-and-switch-set) |
|
804 |
|
805 (defvar js2-has-return-value nil) |
|
806 (make-variable-buffer-local 'js2-has-return-value) |
|
807 |
|
808 (defvar js2-end-flags 0) |
|
809 (make-variable-buffer-local 'js2-end-flags) |
|
810 |
|
811 ;;; end of per function variables |
|
812 |
|
813 ;; Without 2-token lookahead, labels are a problem. |
|
814 ;; These vars store the token info of the last matched name, |
|
815 ;; iff it wasn't the last matched token. Only valid in some contexts. |
|
816 (defvar js2-prev-name-token-start nil) |
|
817 (defvar js2-prev-name-token-string nil) |
|
818 |
|
819 (defsubst js2-save-name-token-data (pos name) |
|
820 (setq js2-prev-name-token-start pos |
|
821 js2-prev-name-token-string name)) |
|
822 |
|
823 ;; These flags enumerate the possible ways a statement/function can |
|
824 ;; terminate. These flags are used by endCheck() and by the Parser to |
|
825 ;; detect inconsistent return usage. |
|
826 ;; |
|
827 ;; END_UNREACHED is reserved for code paths that are assumed to always be |
|
828 ;; able to execute (example: throw, continue) |
|
829 ;; |
|
830 ;; END_DROPS_OFF indicates if the statement can transfer control to the |
|
831 ;; next one. Statement such as return dont. A compound statement may have |
|
832 ;; some branch that drops off control to the next statement. |
|
833 ;; |
|
834 ;; END_RETURNS indicates that the statement can return (without arguments) |
|
835 ;; END_RETURNS_VALUE indicates that the statement can return a value. |
|
836 ;; |
|
837 ;; A compound statement such as |
|
838 ;; if (condition) { |
|
839 ;; return value; |
|
840 ;; } |
|
841 ;; Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck() |
|
842 |
|
843 (defconst js2-end-unreached #x0) |
|
844 (defconst js2-end-drops-off #x1) |
|
845 (defconst js2-end-returns #x2) |
|
846 (defconst js2-end-returns-value #x4) |
|
847 (defconst js2-end-yields #x8) |
|
848 |
|
849 ;; Rhino awkwardly passes a statementLabel parameter to the |
|
850 ;; statementHelper() function, the main statement parser, which |
|
851 ;; is then used by quite a few of the sub-parsers. We just make |
|
852 ;; it a buffer-local variable and make sure it's cleaned up properly. |
|
853 (defvar js2-labeled-stmt nil) ; type `js2-labeled-stmt-node' |
|
854 (make-variable-buffer-local 'js2-labeled-stmt) |
|
855 |
|
856 ;; Similarly, Rhino passes an inForInit boolean through about half |
|
857 ;; the expression parsers. We use a dynamically-scoped variable, |
|
858 ;; which makes it easier to funcall the parsers individually without |
|
859 ;; worrying about whether they take the parameter or not. |
|
860 (defvar js2-in-for-init nil) |
|
861 (make-variable-buffer-local 'js2-in-for-init) |
|
862 |
|
863 (defvar js2-temp-name-counter 0) |
|
864 (make-variable-buffer-local 'js2-temp-name-counter) |
|
865 |
|
866 (defvar js2-parse-stmt-count 0) |
|
867 (make-variable-buffer-local 'js2-parse-stmt-count) |
|
868 |
|
869 (defsubst js2-get-next-temp-name () |
|
870 (format "$%d" (incf js2-temp-name-counter))) |
|
871 |
|
872 (defvar js2-parse-interruptable-p t |
|
873 "Set this to nil to force parse to continue until finished. |
|
874 This will mostly be useful for interpreters.") |
|
875 |
|
876 (defvar js2-statements-per-pause 50 |
|
877 "Pause after this many statements to check for user input. |
|
878 If user input is pending, stop the parse and discard the tree. |
|
879 This makes for a smoother user experience for large files. |
|
880 You may have to wait a second or two before the highlighting |
|
881 and error-reporting appear, but you can always type ahead if |
|
882 you wish. This appears to be more or less how Eclipse, IntelliJ |
|
883 and other editors work.") |
|
884 |
|
885 (defvar js2-record-comments t |
|
886 "Instructs the scanner to record comments in `js2-scanned-comments'.") |
|
887 (make-variable-buffer-local 'js2-record-comments) |
|
888 |
|
889 (defvar js2-scanned-comments nil |
|
890 "List of all comments from the current parse.") |
|
891 (make-variable-buffer-local 'js2-scanned-comments) |
|
892 |
|
893 (defun js2-underline-color (color) |
|
894 "Return a legal value for the :underline face attribute based on COLOR." |
|
895 ;; In XEmacs the :underline attribute can only be a boolean. |
|
896 ;; In GNU it can be the name of a colour. |
|
897 (if (featurep 'xemacs) |
|
898 (if color t nil) |
|
899 color)) |
|
900 |
|
901 (defcustom js2-mode-indent-inhibit-undo nil |
|
902 "Non-nil to disable collection of Undo information when indenting lines. |
|
903 Some users have requested this behavior. It's nil by default because |
|
904 other Emacs modes don't work this way." |
|
905 :type 'boolean |
|
906 :group 'js2-mode) |
|
907 |
|
908 (defcustom js2-mode-indent-ignore-first-tab nil |
|
909 "If non-nil, ignore first TAB keypress if we look indented properly. |
|
910 It's fairly common for users to navigate to an already-indented line |
|
911 and press TAB for reassurance that it's been indented. For this class |
|
912 of users, we want the first TAB press on a line to be ignored if the |
|
913 line is already indented to one of the precomputed alternatives. |
|
914 |
|
915 This behavior is only partly implemented. If you TAB-indent a line, |
|
916 navigate to another line, and then navigate back, it fails to clear |
|
917 the last-indented variable, so it thinks you've already hit TAB once, |
|
918 and performs the indent. A full solution would involve getting on the |
|
919 point-motion hooks for the entire buffer. If we come across another |
|
920 use cases that requires watching point motion, I'll consider doing it. |
|
921 |
|
922 If you set this variable to nil, then the TAB key will always change |
|
923 the indentation of the current line, if more than one alternative |
|
924 indentation spot exists." |
|
925 :type 'boolean |
|
926 :group 'js2-mode) |
|
927 |
|
928 (defvar js2-indent-hook nil |
|
929 "A hook for user-defined indentation rules. |
|
930 |
|
931 Functions on this hook should expect two arguments: (LIST INDEX) |
|
932 The LIST argument is the list of computed indentation points for |
|
933 the current line. INDEX is the list index of the indentation point |
|
934 that `js2-bounce-indent' plans to use. If INDEX is nil, then the |
|
935 indent function is not going to change the current line indentation. |
|
936 |
|
937 If a hook function on this list returns a non-nil value, then |
|
938 `js2-bounce-indent' assumes the hook function has performed its own |
|
939 indentation, and will do nothing. If all hook functions on the list |
|
940 return nil, then `js2-bounce-indent' will use its computed indentation |
|
941 and reindent the line. |
|
942 |
|
943 When hook functions on this hook list are called, the variable |
|
944 `js2-mode-ast' may or may not be set, depending on whether the |
|
945 parse tree is available. If the variable is nil, you can pass a |
|
946 callback to `js2-mode-wait-for-parse', and your callback will be |
|
947 called after the new parse tree is built. This can take some time |
|
948 in large files.") |
|
949 |
|
950 (defface js2-warning-face |
|
951 `((((class color) (background light)) |
|
952 (:underline ,(js2-underline-color "orange"))) |
|
953 (((class color) (background dark)) |
|
954 (:underline ,(js2-underline-color "orange"))) |
|
955 (t (:underline t))) |
|
956 "Face for JavaScript warnings." |
|
957 :group 'js2-mode) |
|
958 |
|
959 (defface js2-error-face |
|
960 `((((class color) (background light)) |
|
961 (:foreground "red")) |
|
962 (((class color) (background dark)) |
|
963 (:foreground "red")) |
|
964 (t (:foreground "red"))) |
|
965 "Face for JavaScript errors." |
|
966 :group 'js2-mode) |
|
967 |
|
968 (defface js2-jsdoc-tag-face |
|
969 '((t :foreground "SlateGray")) |
|
970 "Face used to highlight @whatever tags in jsdoc comments." |
|
971 :group 'js2-mode) |
|
972 |
|
973 (defface js2-jsdoc-type-face |
|
974 '((t :foreground "SteelBlue")) |
|
975 "Face used to highlight {FooBar} types in jsdoc comments." |
|
976 :group 'js2-mode) |
|
977 |
|
978 (defface js2-jsdoc-value-face |
|
979 '((t :foreground "PeachPuff3")) |
|
980 "Face used to highlight tag values in jsdoc comments." |
|
981 :group 'js2-mode) |
|
982 |
|
983 (defface js2-function-param-face |
|
984 '((t :foreground "SeaGreen")) |
|
985 "Face used to highlight function parameters in javascript." |
|
986 :group 'js2-mode) |
|
987 |
|
988 (defface js2-instance-member-face |
|
989 '((t :foreground "DarkOrchid")) |
|
990 "Face used to highlight instance variables in javascript. |
|
991 Not currently used." |
|
992 :group 'js2-mode) |
|
993 |
|
994 (defface js2-private-member-face |
|
995 '((t :foreground "PeachPuff3")) |
|
996 "Face used to highlight calls to private methods in javascript. |
|
997 Not currently used." |
|
998 :group 'js2-mode) |
|
999 |
|
1000 (defface js2-private-function-call-face |
|
1001 '((t :foreground "goldenrod")) |
|
1002 "Face used to highlight calls to private functions in javascript. |
|
1003 Not currently used." |
|
1004 :group 'js2-mode) |
|
1005 |
|
1006 (defface js2-jsdoc-html-tag-name-face |
|
1007 (if js2-emacs22 |
|
1008 '((((class color) (min-colors 88) (background light)) |
|
1009 (:foreground "rosybrown")) |
|
1010 (((class color) (min-colors 8) (background dark)) |
|
1011 (:foreground "yellow")) |
|
1012 (((class color) (min-colors 8) (background light)) |
|
1013 (:foreground "magenta"))) |
|
1014 '((((type tty pc) (class color) (background light)) |
|
1015 (:foreground "magenta")) |
|
1016 (((type tty pc) (class color) (background dark)) |
|
1017 (:foreground "yellow")) |
|
1018 (t (:foreground "RosyBrown")))) |
|
1019 "Face used to highlight jsdoc html tag names" |
|
1020 :group 'js2-mode) |
|
1021 |
|
1022 (defface js2-jsdoc-html-tag-delimiter-face |
|
1023 (if js2-emacs22 |
|
1024 '((((class color) (min-colors 88) (background light)) |
|
1025 (:foreground "dark khaki")) |
|
1026 (((class color) (min-colors 8) (background dark)) |
|
1027 (:foreground "green")) |
|
1028 (((class color) (min-colors 8) (background light)) |
|
1029 (:foreground "green"))) |
|
1030 '((((type tty pc) (class color) (background light)) |
|
1031 (:foreground "green")) |
|
1032 (((type tty pc) (class color) (background dark)) |
|
1033 (:foreground "green")) |
|
1034 (t (:foreground "dark khaki")))) |
|
1035 "Face used to highlight brackets in jsdoc html tags." |
|
1036 :group 'js2-mode) |
|
1037 |
|
1038 (defface js2-external-variable-face |
|
1039 '((t :foreground "orange")) |
|
1040 "Face used to highlight assignments to undeclared variables. |
|
1041 An undeclared variable is any variable not declared with var or let |
|
1042 in the current scope or any lexically enclosing scope. If you assign |
|
1043 to such a variable, then you are either expecting it to originate from |
|
1044 another file, or you've got a potential bug." |
|
1045 :group 'js2-mode) |
|
1046 |
|
1047 (defcustom js2-highlight-external-variables t |
|
1048 "Non-nil to higlight assignments to undeclared variables." |
|
1049 :type 'boolean |
|
1050 :group 'js2-mode) |
|
1051 |
|
1052 (defvar js2-mode-map |
|
1053 (let ((map (make-sparse-keymap)) |
|
1054 keys) |
|
1055 (define-key map [mouse-1] #'js2-mode-show-node) |
|
1056 (define-key map "\C-m" #'js2-enter-key) |
|
1057 (when js2-rebind-eol-bol-keys |
|
1058 (define-key map "\C-a" #'js2-beginning-of-line) |
|
1059 (define-key map "\C-e" #'js2-end-of-line)) |
|
1060 (define-key map "\C-c\C-e" #'js2-mode-hide-element) |
|
1061 (define-key map "\C-c\C-s" #'js2-mode-show-element) |
|
1062 (define-key map "\C-c\C-a" #'js2-mode-show-all) |
|
1063 (define-key map "\C-c\C-f" #'js2-mode-toggle-hide-functions) |
|
1064 (define-key map "\C-c\C-t" #'js2-mode-toggle-hide-comments) |
|
1065 (define-key map "\C-c\C-o" #'js2-mode-toggle-element) |
|
1066 (define-key map "\C-c\C-w" #'js2-mode-toggle-warnings-and-errors) |
|
1067 (define-key map (kbd "C-c C-'") #'js2-next-error) |
|
1068 ;; also define user's preference for next-error, if available |
|
1069 (if (setq keys (where-is-internal #'next-error)) |
|
1070 (define-key map (car keys) #'js2-next-error)) |
|
1071 (define-key map (or (car (where-is-internal #'mark-defun)) |
|
1072 (kbd "M-C-h")) |
|
1073 #'js2-mark-defun) |
|
1074 (define-key map (or (car (where-is-internal #'narrow-to-defun)) |
|
1075 (kbd "C-x nd")) |
|
1076 #'js2-narrow-to-defun) |
|
1077 (define-key map [down-mouse-3] #'js2-mouse-3) |
|
1078 (when js2-auto-indent-flag |
|
1079 (mapc (lambda (key) |
|
1080 (define-key map key #'js2-insert-and-indent)) |
|
1081 js2-electric-keys)) |
|
1082 |
|
1083 (define-key map [menu-bar javascript] |
|
1084 (cons "JavaScript" (make-sparse-keymap "JavaScript"))) |
|
1085 |
|
1086 (define-key map [menu-bar javascript customize-js2-mode] |
|
1087 '(menu-item "Customize js2-mode" js2-mode-customize |
|
1088 :help "Customize the behavior of this mode")) |
|
1089 |
|
1090 (define-key map [menu-bar javascript js2-force-refresh] |
|
1091 '(menu-item "Force buffer refresh" js2-mode-reset |
|
1092 :help "Re-parse the buffer from scratch")) |
|
1093 |
|
1094 (define-key map [menu-bar javascript separator-2] |
|
1095 '("--")) |
|
1096 |
|
1097 (define-key map [menu-bar javascript next-error] |
|
1098 '(menu-item "Next warning or error" js2-next-error |
|
1099 :enabled (and js2-mode-ast |
|
1100 (or (js2-ast-root-errors js2-mode-ast) |
|
1101 (js2-ast-root-warnings js2-mode-ast))) |
|
1102 :help "Move to next warning or error")) |
|
1103 |
|
1104 (define-key map [menu-bar javascript display-errors] |
|
1105 '(menu-item "Show errors and warnings" js2-mode-display-warnings-and-errors |
|
1106 :visible (not js2-mode-show-parse-errors) |
|
1107 :help "Turn on display of warnings and errors")) |
|
1108 |
|
1109 (define-key map [menu-bar javascript hide-errors] |
|
1110 '(menu-item "Hide errors and warnings" js2-mode-hide-warnings-and-errors |
|
1111 :visible js2-mode-show-parse-errors |
|
1112 :help "Turn off display of warnings and errors")) |
|
1113 |
|
1114 (define-key map [menu-bar javascript separator-1] |
|
1115 '("--")) |
|
1116 |
|
1117 (define-key map [menu-bar javascript js2-toggle-function] |
|
1118 '(menu-item "Show/collapse element" js2-mode-toggle-element |
|
1119 :help "Hide or show function body or comment")) |
|
1120 |
|
1121 (define-key map [menu-bar javascript show-comments] |
|
1122 '(menu-item "Show block comments" js2-mode-toggle-hide-comments |
|
1123 :visible js2-mode-comments-hidden |
|
1124 :help "Expand all hidden block comments")) |
|
1125 |
|
1126 (define-key map [menu-bar javascript hide-comments] |
|
1127 '(menu-item "Hide block comments" js2-mode-toggle-hide-comments |
|
1128 :visible (not js2-mode-comments-hidden) |
|
1129 :help "Show block comments as /*...*/")) |
|
1130 |
|
1131 (define-key map [menu-bar javascript show-all-functions] |
|
1132 '(menu-item "Show function bodies" js2-mode-toggle-hide-functions |
|
1133 :visible js2-mode-functions-hidden |
|
1134 :help "Expand all hidden function bodies")) |
|
1135 |
|
1136 (define-key map [menu-bar javascript hide-all-functions] |
|
1137 '(menu-item "Hide function bodies" js2-mode-toggle-hide-functions |
|
1138 :visible (not js2-mode-functions-hidden) |
|
1139 :help "Show {...} for all top-level function bodies")) |
|
1140 |
|
1141 map) |
|
1142 "Keymap used in `js2-mode' buffers.") |
|
1143 |
|
1144 (defconst js2-mode-identifier-re "[a-zA-Z_$][a-zA-Z0-9_$]*") |
|
1145 |
|
1146 (defvar js2-mode-//-comment-re "^\\(\\s-*\\)//.+" |
|
1147 "Matches a //-comment line. Must be first non-whitespace on line. |
|
1148 First match-group is the leading whitespace.") |
|
1149 |
|
1150 (defvar js2-mode-ast nil "Private variable.") |
|
1151 (make-variable-buffer-local 'js2-mode-ast) |
|
1152 |
|
1153 (defvar js2-mode-hook nil) |
|
1154 |
|
1155 (defvar js2-mode-parse-timer nil "Private variable.") |
|
1156 (make-variable-buffer-local 'js2-mode-parse-timer) |
|
1157 |
|
1158 (defvar js2-mode-buffer-dirty-p nil "Private variable.") |
|
1159 (make-variable-buffer-local 'js2-mode-buffer-dirty-p) |
|
1160 |
|
1161 (defvar js2-mode-parsing nil "Private variable.") |
|
1162 (make-variable-buffer-local 'js2-mode-parsing) |
|
1163 |
|
1164 (defvar js2-mode-node-overlay nil) |
|
1165 (make-variable-buffer-local 'js2-mode-node-overlay) |
|
1166 |
|
1167 (defvar js2-mode-show-overlay js2-mode-dev-mode-p |
|
1168 "Debug: Non-nil to highlight AST nodes on mouse-down.") |
|
1169 |
|
1170 (defvar js2-mode-fontifications nil "Private variable") |
|
1171 (make-variable-buffer-local 'js2-mode-fontifications) |
|
1172 |
|
1173 (defvar js2-mode-deferred-properties nil "Private variable") |
|
1174 (make-variable-buffer-local 'js2-mode-deferred-properties) |
|
1175 |
|
1176 (defvar js2-imenu-recorder nil "Private variable") |
|
1177 (make-variable-buffer-local 'js2-imenu-recorder) |
|
1178 |
|
1179 (defvar js2-imenu-function-map nil "Private variable") |
|
1180 (make-variable-buffer-local 'js2-imenu-function-map) |
|
1181 |
|
1182 (defvar js2-paragraph-start |
|
1183 "\\(@[a-zA-Z]+\\>\\|$\\)") |
|
1184 |
|
1185 ;; Note that we also set a 'c-in-sws text property in html comments, |
|
1186 ;; so that `c-forward-sws' and `c-backward-sws' work properly. |
|
1187 (defvar js2-syntactic-ws-start |
|
1188 "\\s \\|/[*/]\\|[\n\r]\\|\\\\[\n\r]\\|\\s!\\|<!--\\|^\\s-*-->") |
|
1189 |
|
1190 (defvar js2-syntactic-ws-end |
|
1191 "\\s \\|[\n\r/]\\|\\s!") |
|
1192 |
|
1193 (defvar js2-syntactic-eol |
|
1194 (concat "\\s *\\(/\\*[^*\n\r]*" |
|
1195 "\\(\\*+[^*\n\r/][^*\n\r]*\\)*" |
|
1196 "\\*+/\\s *\\)*" |
|
1197 "\\(//\\|/\\*[^*\n\r]*" |
|
1198 "\\(\\*+[^*\n\r/][^*\n\r]*\\)*$" |
|
1199 "\\|\\\\$\\|$\\)") |
|
1200 "Copied from java-mode. Needed for some cc-engine functions.") |
|
1201 |
|
1202 (defvar js2-comment-prefix-regexp |
|
1203 "//+\\|\\**") |
|
1204 |
|
1205 (defvar js2-comment-start-skip |
|
1206 "\\(//+\\|/\\*+\\)\\s *") |
|
1207 |
|
1208 (defvar js2-mode-verbose-parse-p js2-mode-dev-mode-p |
|
1209 "Non-nil to emit status messages during parsing.") |
|
1210 |
|
1211 (defvar js2-mode-functions-hidden nil "private variable") |
|
1212 (defvar js2-mode-comments-hidden nil "private variable") |
|
1213 |
|
1214 (defvar js2-mode-syntax-table |
|
1215 (let ((table (make-syntax-table))) |
|
1216 (c-populate-syntax-table table) |
|
1217 table) |
|
1218 "Syntax table used in js2-mode buffers.") |
|
1219 |
|
1220 (defvar js2-mode-abbrev-table nil |
|
1221 "Abbrev table in use in `js2-mode' buffers.") |
|
1222 (define-abbrev-table 'js2-mode-abbrev-table ()) |
|
1223 |
|
1224 (defvar js2-mode-must-byte-compile (not js2-mode-dev-mode-p) |
|
1225 "Non-nil to have `js2-mode' signal an error if not byte-compiled.") |
|
1226 |
|
1227 (defvar js2-mode-pending-parse-callbacks nil |
|
1228 "List of functions waiting to be notified that parse is finished.") |
|
1229 |
|
1230 (defvar js2-mode-last-indented-line -1) |
|
1231 |
|
1232 (eval-when-compile |
|
1233 (defvar c-paragraph-start nil) |
|
1234 (defvar c-paragraph-separate nil) |
|
1235 (defvar c-syntactic-ws-start nil) |
|
1236 (defvar c-syntactic-ws-end nil) |
|
1237 (defvar c-syntactic-eol nil) |
|
1238 (defvar running-xemacs nil) |
|
1239 (defvar font-lock-mode nil) |
|
1240 (defvar font-lock-keywords nil)) |
|
1241 |
|
1242 (eval-when-compile |
|
1243 (if (< emacs-major-version 22) |
|
1244 (defun c-setup-paragraph-variables () nil))) |
|
1245 |
|
1246 (provide 'js2-vars) |
|
1247 |
|
1248 ;;; js2-vars.el ends here |
|
1249 ;;; js2-util.el -- JavaScript utilities |
|
1250 |
|
1251 ;; Author: Steve Yegge (steve.yegge@gmail.com) |
|
1252 ;; Keywords: javascript languages |
|
1253 |
|
1254 ;;; Code |
|
1255 |
|
1256 (eval-when-compile |
|
1257 (require 'cl)) |
|
1258 |
|
1259 |
|
1260 ;; Emacs21 compatibility, plus some stuff to avoid runtime dependency on CL |
|
1261 |
|
1262 (unless (fboundp #'looking-back) |
|
1263 (defun looking-back (regexp &optional limit greedy) |
|
1264 "Return non-nil if text before point matches regular expression REGEXP. |
|
1265 Like `looking-at' except matches before point, and is slower. |
|
1266 LIMIT if non-nil speeds up the search by specifying a minimum |
|
1267 starting position, to avoid checking matches that would start |
|
1268 before LIMIT. |
|
1269 |
|
1270 If GREEDY is non-nil, extend the match backwards as far as possible, |
|
1271 stopping when a single additional previous character cannot be part |
|
1272 of a match for REGEXP." |
|
1273 (let ((start (point)) |
|
1274 (pos |
|
1275 (save-excursion |
|
1276 (and (re-search-backward (concat "\\(?:" regexp "\\)\\=") limit t) |
|
1277 (point))))) |
|
1278 (if (and greedy pos) |
|
1279 (save-restriction |
|
1280 (narrow-to-region (point-min) start) |
|
1281 (while (and (> pos (point-min)) |
|
1282 (save-excursion |
|
1283 (goto-char pos) |
|
1284 (backward-char 1) |
|
1285 (looking-at (concat "\\(?:" regexp "\\)\\'")))) |
|
1286 (setq pos (1- pos))) |
|
1287 (save-excursion |
|
1288 (goto-char pos) |
|
1289 (looking-at (concat "\\(?:" regexp "\\)\\'"))))) |
|
1290 (not (null pos))))) |
|
1291 |
|
1292 (unless (fboundp #'copy-overlay) |
|
1293 (defun copy-overlay (o) |
|
1294 "Return a copy of overlay O." |
|
1295 (let ((o1 (make-overlay (overlay-start o) (overlay-end o) |
|
1296 ;; FIXME: there's no easy way to find the |
|
1297 ;; insertion-type of the two markers. |
|
1298 (overlay-buffer o))) |
|
1299 (props (overlay-properties o))) |
|
1300 (while props |
|
1301 (overlay-put o1 (pop props) (pop props))) |
|
1302 o1))) |
|
1303 |
|
1304 (unless (fboundp #'remove-overlays) |
|
1305 (defun remove-overlays (&optional beg end name val) |
|
1306 "Clear BEG and END of overlays whose property NAME has value VAL. |
|
1307 Overlays might be moved and/or split. |
|
1308 BEG and END default respectively to the beginning and end of buffer." |
|
1309 (unless beg (setq beg (point-min))) |
|
1310 (unless end (setq end (point-max))) |
|
1311 (if (< end beg) |
|
1312 (setq beg (prog1 end (setq end beg)))) |
|
1313 (save-excursion |
|
1314 (dolist (o (overlays-in beg end)) |
|
1315 (when (eq (overlay-get o name) val) |
|
1316 ;; Either push this overlay outside beg...end |
|
1317 ;; or split it to exclude beg...end |
|
1318 ;; or delete it entirely (if it is contained in beg...end). |
|
1319 (if (< (overlay-start o) beg) |
|
1320 (if (> (overlay-end o) end) |
|
1321 (progn |
|
1322 (move-overlay (copy-overlay o) |
|
1323 (overlay-start o) beg) |
|
1324 (move-overlay o end (overlay-end o))) |
|
1325 (move-overlay o (overlay-start o) beg)) |
|
1326 (if (> (overlay-end o) end) |
|
1327 (move-overlay o end (overlay-end o)) |
|
1328 (delete-overlay o)))))))) |
|
1329 |
|
1330 ;; we don't want a runtime dependency on the CL package, so define |
|
1331 ;; our own versions of these functions. |
|
1332 |
|
1333 (defun js2-delete-if (predicate list) |
|
1334 "Remove all items satisfying PREDICATE in LIST." |
|
1335 (loop for item in list |
|
1336 if (not (funcall predicate item)) |
|
1337 collect item)) |
|
1338 |
|
1339 (defun js2-position (element list) |
|
1340 "Find 0-indexed position of ELEMENT in LIST comparing with `eq'. |
|
1341 Returns nil if element is not found in the list." |
|
1342 (let ((count 0) |
|
1343 found) |
|
1344 (while (and list (not found)) |
|
1345 (if (eq element (car list)) |
|
1346 (setq found t) |
|
1347 (setq count (1+ count) |
|
1348 list (cdr list)))) |
|
1349 (if found count))) |
|
1350 |
|
1351 (defun js2-find-if (predicate list) |
|
1352 "Find first item satisfying PREDICATE in LIST." |
|
1353 (let (result) |
|
1354 (while (and list (not result)) |
|
1355 (if (funcall predicate (car list)) |
|
1356 (setq result (car list))) |
|
1357 (setq list (cdr list))) |
|
1358 result)) |
|
1359 |
|
1360 ;;; end Emacs 21 compat |
|
1361 |
|
1362 (defmacro js2-time (form) |
|
1363 "Evaluate FORM, discard result, and return elapsed time in sec" |
|
1364 (let ((beg (make-symbol "--js2-time-beg--")) |
|
1365 (delta (make-symbol "--js2-time-end--"))) |
|
1366 `(let ((,beg (current-time)) |
|
1367 ,delta) |
|
1368 ,form |
|
1369 (/ (truncate (* (- (float-time (current-time)) |
|
1370 (float-time ,beg))) |
|
1371 10000) |
|
1372 10000.0)))) |
|
1373 |
|
1374 (def-edebug-spec js2-time t) |
|
1375 |
|
1376 (defsubst neq (expr1 expr2) |
|
1377 "Return (not (eq expr1 expr2))." |
|
1378 (not (eq expr1 expr2))) |
|
1379 |
|
1380 (defsubst js2-same-line (pos) |
|
1381 "Return t if POS is on the same line as current point." |
|
1382 (and (>= pos (point-at-bol)) |
|
1383 (<= pos (point-at-eol)))) |
|
1384 |
|
1385 (defsubst js2-same-line-2 (p1 p2) |
|
1386 "Return t if p1 is on the same line as p2." |
|
1387 (save-excursion |
|
1388 (goto-char p1) |
|
1389 (js2-same-line p2))) |
|
1390 |
|
1391 (defun js2-code-bug () |
|
1392 "Signal an error when we encounter an unexpected code path." |
|
1393 (error "failed assertion")) |
|
1394 |
|
1395 ;; I'd like to associate errors with nodes, but for now the |
|
1396 ;; easiest thing to do is get the context info from the last token. |
|
1397 (defsubst js2-record-parse-error (msg &optional arg pos len) |
|
1398 (push (list (list msg arg) |
|
1399 (or pos js2-token-beg) |
|
1400 (or len (- js2-token-end js2-token-beg))) |
|
1401 js2-parsed-errors)) |
|
1402 |
|
1403 (defsubst js2-report-error (msg &optional msg-arg pos len) |
|
1404 "Signal a syntax error or record a parse error." |
|
1405 (if js2-recover-from-parse-errors |
|
1406 (js2-record-parse-error msg msg-arg pos len) |
|
1407 (signal 'js2-syntax-error |
|
1408 (list msg |
|
1409 js2-ts-lineno |
|
1410 (save-excursion |
|
1411 (goto-char js2-ts-cursor) |
|
1412 (current-column)) |
|
1413 js2-ts-hit-eof)))) |
|
1414 |
|
1415 (defsubst js2-report-warning (msg &optional msg-arg pos len) |
|
1416 (if js2-compiler-report-warning-as-error |
|
1417 (js2-report-error msg msg-arg pos len) |
|
1418 (push (list (list msg msg-arg) |
|
1419 (or pos js2-token-beg) |
|
1420 (or len (- js2-token-end js2-token-beg))) |
|
1421 js2-parsed-warnings))) |
|
1422 |
|
1423 (defsubst js2-add-strict-warning (msg-id &optional msg-arg beg end) |
|
1424 (if js2-compiler-strict-mode |
|
1425 (js2-report-warning msg-id msg-arg beg |
|
1426 (and beg end (- end beg))))) |
|
1427 |
|
1428 (put 'js2-syntax-error 'error-conditions |
|
1429 '(error syntax-error js2-syntax-error)) |
|
1430 (put 'js2-syntax-error 'error-message "Syntax error") |
|
1431 |
|
1432 (put 'js2-parse-error 'error-conditions |
|
1433 '(error parse-error js2-parse-error)) |
|
1434 (put 'js2-parse-error 'error-message "Parse error") |
|
1435 |
|
1436 (defmacro js2-clear-flag (flags flag) |
|
1437 `(setq ,flags (logand ,flags (lognot ,flag)))) |
|
1438 |
|
1439 (defmacro js2-set-flag (flags flag) |
|
1440 "Logical-or FLAG into FLAGS." |
|
1441 `(setq ,flags (logior ,flags ,flag))) |
|
1442 |
|
1443 (defsubst js2-flag-set-p (flags flag) |
|
1444 (/= 0 (logand flags flag))) |
|
1445 |
|
1446 (defsubst js2-flag-not-set-p (flags flag) |
|
1447 (zerop (logand flags flag))) |
|
1448 |
|
1449 ;; Stolen shamelessly from James Clark's nxml-mode. |
|
1450 (defmacro js2-with-unmodifying-text-property-changes (&rest body) |
|
1451 "Evaluate BODY without any text property changes modifying the buffer. |
|
1452 Any text properties changes happen as usual but the changes are not treated as |
|
1453 modifications to the buffer." |
|
1454 (let ((modified (make-symbol "modified"))) |
|
1455 `(let ((,modified (buffer-modified-p)) |
|
1456 (inhibit-read-only t) |
|
1457 (inhibit-modification-hooks t) |
|
1458 (buffer-undo-list t) |
|
1459 (deactivate-mark nil) |
|
1460 ;; Apparently these avoid file locking problems. |
|
1461 (buffer-file-name nil) |
|
1462 (buffer-file-truename nil)) |
|
1463 (unwind-protect |
|
1464 (progn ,@body) |
|
1465 (unless ,modified |
|
1466 (restore-buffer-modified-p nil)))))) |
|
1467 |
|
1468 (put 'js2-with-unmodifying-text-property-changes 'lisp-indent-function 0) |
|
1469 (def-edebug-spec js2-with-unmodifying-text-property-changes t) |
|
1470 |
|
1471 (defmacro js2-with-underscore-as-word-syntax (&rest body) |
|
1472 "Evaluate BODY with the _ character set to be word-syntax." |
|
1473 (let ((old-syntax (make-symbol "old-syntax"))) |
|
1474 `(let ((,old-syntax (string (char-syntax ?_)))) |
|
1475 (unwind-protect |
|
1476 (progn |
|
1477 (modify-syntax-entry ?_ "w" js2-mode-syntax-table) |
|
1478 ,@body) |
|
1479 (modify-syntax-entry ?_ ,old-syntax js2-mode-syntax-table))))) |
|
1480 |
|
1481 (put 'js2-with-underscore-as-word-syntax 'lisp-indent-function 0) |
|
1482 (def-edebug-spec js2-with-underscore-as-word-syntax t) |
|
1483 |
|
1484 (defmacro with-buffer (buf form) |
|
1485 "Executes FORM in buffer BUF. |
|
1486 BUF can be a buffer name or a buffer object. |
|
1487 If the buffer doesn't exist, it's created." |
|
1488 `(let ((buffer (gentemp))) |
|
1489 (setq buffer |
|
1490 (if (stringp ,buf) |
|
1491 (get-buffer-create ,buf) |
|
1492 ,buf)) |
|
1493 (save-excursion |
|
1494 (set-buffer buffer) |
|
1495 ,form))) |
|
1496 |
|
1497 (defsubst char-is-uppercase (c) |
|
1498 "Return t if C is an uppercase character. |
|
1499 Handles unicode and latin chars properly." |
|
1500 (/= c (downcase c))) |
|
1501 |
|
1502 (defsubst char-is-lowercase (c) |
|
1503 "Return t if C is an uppercase character. |
|
1504 Handles unicode and latin chars properly." |
|
1505 (/= c (upcase c))) |
|
1506 |
|
1507 (put 'with-buffer 'lisp-indent-function 1) |
|
1508 (def-edebug-spec with-buffer t) |
|
1509 |
|
1510 (provide 'js2-util) |
|
1511 |
|
1512 ;;; js2-util.el ends here |
|
1513 ;;; js2-scan.el --- JavaScript scanner |
|
1514 |
|
1515 ;; Author: Steve Yegge (steve.yegge@gmail.com) |
|
1516 ;; Keywords: javascript languages |
|
1517 |
|
1518 ;;; Commentary: |
|
1519 |
|
1520 ;; A port of Mozilla Rhino's scanner. |
|
1521 ;; Corresponds to Rhino files Token.java and TokenStream.java. |
|
1522 |
|
1523 ;;; Code: |
|
1524 |
|
1525 |
|
1526 (eval-when-compile |
|
1527 (require 'cl)) |
|
1528 |
|
1529 (defvar js2-tokens nil |
|
1530 "List of all defined token names.") ; intialized below |
|
1531 |
|
1532 (defvar js2-token-names |
|
1533 (let* ((names (make-vector js2-num-tokens -1)) |
|
1534 (case-fold-search nil) ; only match js2-UPPER_CASE |
|
1535 (syms (apropos-internal "^js2-\\(?:[A-Z_]+\\)"))) |
|
1536 (loop for sym in syms |
|
1537 for i from 0 |
|
1538 do |
|
1539 (unless (or (memq sym '(js2-EOF_CHAR js2-ERROR)) |
|
1540 (not (boundp sym))) |
|
1541 (aset names (symbol-value sym) ; code, e.g. 152 |
|
1542 (substring (symbol-name sym) 4)) ; name, e.g. "LET" |
|
1543 (push sym js2-tokens))) |
|
1544 names) |
|
1545 "Vector mapping int values to token string names, sans `js2-' prefix.") |
|
1546 |
|
1547 (defun js2-token-name (tok) |
|
1548 "Return a string name for TOK, a token symbol or code. |
|
1549 Signals an error if it's not a recognized token." |
|
1550 (let ((code tok)) |
|
1551 (if (symbolp tok) |
|
1552 (setq code (symbol-value tok))) |
|
1553 (if (eq code -1) |
|
1554 "ERROR" |
|
1555 (if (and (numberp code) |
|
1556 (not (minusp code)) |
|
1557 (< code js2-num-tokens)) |
|
1558 (aref js2-token-names code) |
|
1559 (error "Invalid token: %s" code))))) |
|
1560 |
|
1561 (defsubst js2-token-sym (tok) |
|
1562 "Return symbol for TOK given its code, e.g. 'js2-LP for code 86." |
|
1563 (intern (js2-token-name tok))) |
|
1564 |
|
1565 (defvar js2-token-codes |
|
1566 (let ((table (make-hash-table :test 'eq :size 256))) |
|
1567 (loop for name across js2-token-names |
|
1568 for sym = (intern (concat "js2-" name)) |
|
1569 do |
|
1570 (puthash sym (symbol-value sym) table)) |
|
1571 ;; clean up a few that are "wrong" in Rhino's token codes |
|
1572 (puthash 'js2-DELETE js2-DELPROP table) |
|
1573 table) |
|
1574 "Hashtable mapping token symbols to their bytecodes.") |
|
1575 |
|
1576 (defsubst js2-token-code (sym) |
|
1577 "Return code for token symbol SYM, e.g. 86 for 'js2-LP." |
|
1578 (or (gethash sym js2-token-codes) |
|
1579 (error "Invalid token symbol: %s " sym))) ; signal code bug |
|
1580 |
|
1581 (defsubst js2-report-scan-error (msg &optional no-throw beg len) |
|
1582 (setq js2-token-end js2-ts-cursor) |
|
1583 (js2-report-error msg nil |
|
1584 (or beg js2-token-beg) |
|
1585 (or len (- js2-token-end js2-token-beg))) |
|
1586 (unless no-throw |
|
1587 (throw 'return js2-ERROR))) |
|
1588 |
|
1589 (defsubst js2-get-string-from-buffer () |
|
1590 "Reverse the char accumulator and return it as a string." |
|
1591 (setq js2-token-end js2-ts-cursor) |
|
1592 (if js2-ts-string-buffer |
|
1593 (apply #'string (nreverse js2-ts-string-buffer)) |
|
1594 "")) |
|
1595 |
|
1596 ;; TODO: could potentially avoid a lot of consing by allocating a |
|
1597 ;; char buffer the way Rhino does. |
|
1598 (defsubst js2-add-to-string (c) |
|
1599 (push c js2-ts-string-buffer)) |
|
1600 |
|
1601 ;; Note that when we "read" the end-of-file, we advance js2-ts-cursor |
|
1602 ;; to (1+ (point-max)), which lets the scanner treat end-of-file like |
|
1603 ;; any other character: when it's not part of the current token, we |
|
1604 ;; unget it, allowing it to be read again by the following call. |
|
1605 (defsubst js2-unget-char () |
|
1606 (decf js2-ts-cursor)) |
|
1607 |
|
1608 ;; Rhino distinguishes \r and \n line endings. We don't need to |
|
1609 ;; because we only scan from Emacs buffers, which always use \n. |
|
1610 (defsubst js2-get-char () |
|
1611 "Read and return the next character from the input buffer. |
|
1612 Increments `js2-ts-lineno' if the return value is a newline char. |
|
1613 Updates `js2-ts-cursor' to the point after the returned char. |
|
1614 Returns `js2-EOF_CHAR' if we hit the end of the buffer. |
|
1615 Also updates `js2-ts-hit-eof' and `js2-ts-line-start' as needed." |
|
1616 (let (c) |
|
1617 ;; check for end of buffer |
|
1618 (if (>= js2-ts-cursor (point-max)) |
|
1619 (setq js2-ts-hit-eof t |
|
1620 js2-ts-cursor (1+ js2-ts-cursor) |
|
1621 c js2-EOF_CHAR) ; return value |
|
1622 |
|
1623 ;; otherwise read next char |
|
1624 (setq c (char-before (incf js2-ts-cursor))) |
|
1625 |
|
1626 ;; if we read a newline, update counters |
|
1627 (if (= c ?\n) |
|
1628 (setq js2-ts-line-start js2-ts-cursor |
|
1629 js2-ts-lineno (1+ js2-ts-lineno))) |
|
1630 |
|
1631 ;; TODO: skip over format characters |
|
1632 c))) |
|
1633 |
|
1634 (defsubst js2-read-unicode-escape () |
|
1635 "Read a \\uNNNN sequence from the input. |
|
1636 Assumes the ?\ and ?u have already been read. |
|
1637 Returns the unicode character, or nil if it wasn't a valid character. |
|
1638 Doesn't change the values of any scanner variables." |
|
1639 ;; I really wish I knew a better way to do this, but I can't |
|
1640 ;; find the Emacs function that takes a 16-bit int and converts |
|
1641 ;; it to a Unicode/utf-8 character. So I basically eval it with (read). |
|
1642 ;; Have to first check that it's 4 hex characters or it may stop |
|
1643 ;; the read early. |
|
1644 (ignore-errors |
|
1645 (let ((s (buffer-substring-no-properties js2-ts-cursor |
|
1646 (+ 4 js2-ts-cursor)))) |
|
1647 (if (string-match "[a-zA-Z0-9]\\{4\\}" s) |
|
1648 (read (concat "?\\u" s)))))) |
|
1649 |
|
1650 (defsubst js2-match-char (test) |
|
1651 "Consume and return next character if it matches TEST, a character. |
|
1652 Returns nil and consumes nothing if TEST is not the next character." |
|
1653 (let ((c (js2-get-char))) |
|
1654 (if (eq c test) |
|
1655 t |
|
1656 (js2-unget-char) |
|
1657 nil))) |
|
1658 |
|
1659 (defsubst js2-peek-char () |
|
1660 (prog1 |
|
1661 (js2-get-char) |
|
1662 (js2-unget-char))) |
|
1663 |
|
1664 (defsubst js2-java-identifier-start-p (c) |
|
1665 (or |
|
1666 (memq c '(?$ ?_)) |
|
1667 (char-is-uppercase c) |
|
1668 (char-is-lowercase c))) |
|
1669 |
|
1670 (defsubst js2-java-identifier-part-p (c) |
|
1671 "Implementation of java.lang.Character.isJavaIdentifierPart()" |
|
1672 ;; TODO: make me Unicode-friendly. See comments above. |
|
1673 (or |
|
1674 (memq c '(?$ ?_)) |
|
1675 (char-is-uppercase c) |
|
1676 (char-is-lowercase c) |
|
1677 (and (>= c ?0) (<= c ?9)))) |
|
1678 |
|
1679 (defsubst js2-alpha-p (c) |
|
1680 ;; Use 'Z' < 'a' |
|
1681 (if (<= c ?Z) |
|
1682 (<= ?A c) |
|
1683 (and (<= ?a c) |
|
1684 (<= c ?z)))) |
|
1685 |
|
1686 (defsubst js2-digit-p (c) |
|
1687 (and (<= ?0 c) (<= c ?9))) |
|
1688 |
|
1689 (defsubst js2-js-space-p (c) |
|
1690 (if (<= c 127) |
|
1691 (memq c '(#x20 #x9 #xC #xB)) |
|
1692 (or |
|
1693 (eq c #xA0) |
|
1694 ;; TODO: change this nil to check for Unicode space character |
|
1695 nil))) |
|
1696 |
|
1697 (defsubst js2-skip-line () |
|
1698 "Skip to end of line" |
|
1699 (let (c) |
|
1700 (while (and (/= js2-EOF_CHAR (setq c (js2-get-char))) |
|
1701 (/= c ?\n))) |
|
1702 (js2-unget-char) |
|
1703 (setq js2-token-end js2-ts-cursor))) |
|
1704 |
|
1705 (defun js2-init-scanner (&optional buf line) |
|
1706 "Create token stream for BUF starting on LINE. |
|
1707 BUF defaults to current-buffer and line defaults to 1. |
|
1708 |
|
1709 A buffer can only have one scanner active at a time, which yields |
|
1710 dramatically simpler code than using a defstruct. If you need to |
|
1711 have simultaneous scanners in a buffer, copy the regions to scan |
|
1712 into temp buffers." |
|
1713 (save-excursion |
|
1714 (when buf |
|
1715 (set-buffer buf)) |
|
1716 (setq js2-ts-dirty-line nil |
|
1717 js2-ts-regexp-flags nil |
|
1718 js2-ts-string "" |
|
1719 js2-ts-number nil |
|
1720 js2-ts-hit-eof nil |
|
1721 js2-ts-line-start 0 |
|
1722 js2-ts-lineno (or line 1) |
|
1723 js2-ts-line-end-char -1 |
|
1724 js2-ts-cursor (point-min) |
|
1725 js2-ts-is-xml-attribute nil |
|
1726 js2-ts-xml-is-tag-content nil |
|
1727 js2-ts-xml-open-tags-count 0 |
|
1728 js2-ts-string-buffer nil))) |
|
1729 |
|
1730 ;; This function uses the cached op, string and number fields in |
|
1731 ;; TokenStream; if getToken has been called since the passed token |
|
1732 ;; was scanned, the op or string printed may be incorrect. |
|
1733 (defun js2-token-to-string (token) |
|
1734 ;; Not sure where this function is used in Rhino. Not tested. |
|
1735 (if (not js2-debug-print-trees) |
|
1736 "" |
|
1737 (let ((name (js2-token-name token))) |
|
1738 (cond |
|
1739 ((memq token (list js2-STRING js2-REGEXP js2-NAME)) |
|
1740 (concat name " `" js2-ts-string "'")) |
|
1741 ((eq token js2-NUMBER) |
|
1742 (format "NUMBER %g" js2-ts-number)) |
|
1743 (t |
|
1744 name))))) |
|
1745 |
|
1746 (defconst js2-keywords |
|
1747 '(break |
|
1748 case catch const continue |
|
1749 debugger default delete do |
|
1750 else enum |
|
1751 false finally for function |
|
1752 if in instanceof import |
|
1753 let |
|
1754 new null |
|
1755 return |
|
1756 switch |
|
1757 this throw true try typeof |
|
1758 var void |
|
1759 while with |
|
1760 yield)) |
|
1761 |
|
1762 ;; Token names aren't exactly the same as the keywords, unfortunately. |
|
1763 ;; E.g. enum isn't in the tokens, and delete is js2-DELPROP. |
|
1764 (defconst js2-kwd-tokens |
|
1765 (let ((table (make-vector js2-num-tokens nil)) |
|
1766 (tokens |
|
1767 (list js2-BREAK |
|
1768 js2-CASE js2-CATCH js2-CONST js2-CONTINUE |
|
1769 js2-DEBUGGER js2-DEFAULT js2-DELPROP js2-DO |
|
1770 js2-ELSE |
|
1771 js2-FALSE js2-FINALLY js2-FOR js2-FUNCTION |
|
1772 js2-IF js2-IN js2-INSTANCEOF js2-IMPORT |
|
1773 js2-LET |
|
1774 js2-NEW js2-NULL |
|
1775 js2-RETURN |
|
1776 js2-SWITCH |
|
1777 js2-THIS js2-THROW js2-TRUE js2-TRY js2-TYPEOF |
|
1778 js2-VAR |
|
1779 js2-WHILE js2-WITH |
|
1780 js2-YIELD))) |
|
1781 (dolist (i tokens) |
|
1782 (aset table i 'font-lock-keyword-face)) |
|
1783 (aset table js2-STRING 'font-lock-string-face) |
|
1784 (aset table js2-REGEXP 'font-lock-string-face) |
|
1785 (aset table js2-COMMENT 'font-lock-comment-face) |
|
1786 (aset table js2-THIS 'font-lock-builtin-face) |
|
1787 (aset table js2-VOID 'font-lock-constant-face) |
|
1788 (aset table js2-NULL 'font-lock-constant-face) |
|
1789 (aset table js2-TRUE 'font-lock-constant-face) |
|
1790 (aset table js2-FALSE 'font-lock-constant-face) |
|
1791 table) |
|
1792 "Vector whose values are non-nil for tokens that are keywords. |
|
1793 The values are default faces to use for highlighting the keywords.") |
|
1794 |
|
1795 (defconst js2-reserved-words |
|
1796 '(abstract |
|
1797 boolean byte |
|
1798 char class |
|
1799 double |
|
1800 enum extends |
|
1801 final float |
|
1802 goto |
|
1803 implements int interface |
|
1804 long |
|
1805 native |
|
1806 package private protected public |
|
1807 short static super synchronized |
|
1808 throws transient |
|
1809 volatile)) |
|
1810 |
|
1811 (defconst js2-keyword-names |
|
1812 (let ((table (make-hash-table :test 'equal))) |
|
1813 (loop for k in js2-keywords |
|
1814 do (puthash |
|
1815 (symbol-name k) ; instanceof |
|
1816 (intern (concat "js2-" |
|
1817 (upcase (symbol-name k)))) ; js2-INSTANCEOF |
|
1818 table)) |
|
1819 table) |
|
1820 "JavaScript keywords by name, mapped to their symbols.") |
|
1821 |
|
1822 (defconst js2-reserved-word-names |
|
1823 (let ((table (make-hash-table :test 'equal))) |
|
1824 (loop for k in js2-reserved-words |
|
1825 do |
|
1826 (puthash (symbol-name k) 'js2-RESERVED table)) |
|
1827 table) |
|
1828 "JavaScript reserved words by name, mapped to 'js2-RESERVED.") |
|
1829 |
|
1830 (defsubst js2-collect-string (buf) |
|
1831 "Convert BUF, a list of chars, to a string. |
|
1832 Reverses BUF before converting." |
|
1833 (cond |
|
1834 ((stringp buf) |
|
1835 buf) |
|
1836 ((null buf) ; for emacs21 compat |
|
1837 "") |
|
1838 (t |
|
1839 (if buf |
|
1840 (apply #'string (nreverse buf)) |
|
1841 "")))) |
|
1842 |
|
1843 (defun js2-string-to-keyword (s) |
|
1844 "Return token for S, a string, if S is a keyword or reserved word. |
|
1845 Returns a symbol such as 'js2-BREAK, or nil if not keyword/reserved." |
|
1846 (or (gethash s js2-keyword-names) |
|
1847 (gethash s js2-reserved-word-names))) |
|
1848 |
|
1849 (defsubst js2-ts-set-char-token-bounds () |
|
1850 "Used when next token is one character." |
|
1851 (setq js2-token-beg (1- js2-ts-cursor) |
|
1852 js2-token-end js2-ts-cursor)) |
|
1853 |
|
1854 (defsubst js2-ts-return (token) |
|
1855 "Return an N-character TOKEN from `js2-get-token'. |
|
1856 Updates `js2-token-end' accordingly." |
|
1857 (setq js2-token-end js2-ts-cursor) |
|
1858 (throw 'return token)) |
|
1859 |
|
1860 (defsubst js2-x-digit-to-int (c accumulator) |
|
1861 "Build up a hex number. |
|
1862 If C is a hexadecimal digit, return ACCUMULATOR * 16 plus |
|
1863 corresponding number. Otherwise return -1." |
|
1864 (catch 'return |
|
1865 (catch 'check |
|
1866 ;; Use 0..9 < A..Z < a..z |
|
1867 (cond |
|
1868 ((<= c ?9) |
|
1869 (decf c ?0) |
|
1870 (if (<= 0 c) |
|
1871 (throw 'check nil))) |
|
1872 ((<= c ?F) |
|
1873 (when (<= ?A c) |
|
1874 (decf c (- ?A 10)) |
|
1875 (throw 'check nil))) |
|
1876 ((<= c ?f) |
|
1877 (when (<= ?a c) |
|
1878 (decf c (- ?a 10)) |
|
1879 (throw 'check nil)))) |
|
1880 (throw 'return -1)) |
|
1881 (logior c (lsh accumulator 4)))) |
|
1882 |
|
1883 (defun js2-get-token () |
|
1884 "Return next JavaScript token, an int such as js2-RETURN." |
|
1885 (let (c |
|
1886 c1 |
|
1887 identifier-start |
|
1888 is-unicode-escape-start |
|
1889 contains-escape |
|
1890 escape-val |
|
1891 escape-start |
|
1892 str |
|
1893 result |
|
1894 base |
|
1895 is-integer |
|
1896 quote-char |
|
1897 val |
|
1898 look-for-slash |
|
1899 continue) |
|
1900 (catch 'return |
|
1901 (while t |
|
1902 ;; Eat whitespace, possibly sensitive to newlines. |
|
1903 (setq continue t) |
|
1904 (while continue |
|
1905 (setq c (js2-get-char)) |
|
1906 (cond |
|
1907 ((eq c js2-EOF_CHAR) |
|
1908 (js2-ts-set-char-token-bounds) |
|
1909 (throw 'return js2-EOF)) |
|
1910 ((eq c ?\n) |
|
1911 (js2-ts-set-char-token-bounds) |
|
1912 (setq js2-ts-dirty-line nil) |
|
1913 (throw 'return js2-EOL)) |
|
1914 ((not (js2-js-space-p c)) |
|
1915 (if (/= c ?-) ; in case end of HTML comment |
|
1916 (setq js2-ts-dirty-line t)) |
|
1917 (setq continue nil)))) |
|
1918 |
|
1919 ;; Assume the token will be 1 char - fixed up below. |
|
1920 (js2-ts-set-char-token-bounds) |
|
1921 |
|
1922 (when (eq c ?@) |
|
1923 (throw 'return js2-XMLATTR)) |
|
1924 |
|
1925 ;; identifier/keyword/instanceof? |
|
1926 ;; watch out for starting with a <backslash> |
|
1927 (cond |
|
1928 ((eq c ?\\) |
|
1929 (setq c (js2-get-char)) |
|
1930 (if (eq c ?u) |
|
1931 (setq identifier-start t |
|
1932 is-unicode-escape-start t |
|
1933 js2-ts-string-buffer nil) |
|
1934 (setq identifier-start nil) |
|
1935 (js2-unget-char) |
|
1936 (setq c ?\\))) |
|
1937 (t |
|
1938 (when (setq identifier-start (js2-java-identifier-start-p c)) |
|
1939 (setq js2-ts-string-buffer nil) |
|
1940 (js2-add-to-string c)))) |
|
1941 |
|
1942 (when identifier-start |
|
1943 (setq contains-escape is-unicode-escape-start) |
|
1944 (catch 'break |
|
1945 (while t |
|
1946 (if is-unicode-escape-start |
|
1947 ;; strictly speaking we should probably push-back |
|
1948 ;; all the bad characters if the <backslash>uXXXX |
|
1949 ;; sequence is malformed. But since there isn't a |
|
1950 ;; correct context(is there?) for a bad Unicode |
|
1951 ;; escape sequence in an identifier, we can report |
|
1952 ;; an error here. |
|
1953 (progn |
|
1954 (setq escape-val 0) |
|
1955 (dotimes (i 4) |
|
1956 (setq c (js2-get-char) |
|
1957 escape-val (js2-x-digit-to-int c escape-val)) |
|
1958 ;; Next check takes care of c < 0 and bad escape |
|
1959 (if (minusp escape-val) |
|
1960 (throw 'break nil))) |
|
1961 (if (minusp escape-val) |
|
1962 (js2-report-scan-error "msg.invalid.escape" t)) |
|
1963 (js2-add-to-string escape-val) |
|
1964 (setq is-unicode-escape-start nil)) |
|
1965 (setq c (js2-get-char)) |
|
1966 (cond |
|
1967 ((eq c ?\\) |
|
1968 (setq c (js2-get-char)) |
|
1969 (if (eq c ?u) |
|
1970 (setq is-unicode-escape-start t |
|
1971 contains-escape t) |
|
1972 (js2-report-scan-error "msg.illegal.character" t))) |
|
1973 (t |
|
1974 (if (or (eq c js2-EOF_CHAR) |
|
1975 (not (js2-java-identifier-part-p c))) |
|
1976 (throw 'break nil)) |
|
1977 (js2-add-to-string c)))))) |
|
1978 (js2-unget-char) |
|
1979 |
|
1980 (setq str (js2-get-string-from-buffer)) |
|
1981 (unless contains-escape |
|
1982 ;; OPT we shouldn't have to make a string (object!) to |
|
1983 ;; check if it's a keyword. |
|
1984 |
|
1985 ;; Return the corresponding token if it's a keyword |
|
1986 (when (setq result (js2-string-to-keyword str)) |
|
1987 (if (and (< js2-language-version 170) |
|
1988 (memq result '(js2-LET js2-YIELD))) |
|
1989 ;; LET and YIELD are tokens only in 1.7 and later |
|
1990 (setq result 'js2-NAME)) |
|
1991 (if (neq result js2-RESERVED) |
|
1992 (throw 'return (js2-token-code result))) |
|
1993 (js2-report-warning "msg.reserved.keyword" str))) |
|
1994 |
|
1995 ;; If we want to intern these as Rhino does, just use (intern str) |
|
1996 (setq js2-ts-string str) |
|
1997 (throw 'return js2-NAME)) ; end identifier/kwd check |
|
1998 |
|
1999 ;; is it a number? |
|
2000 (when (or (js2-digit-p c) |
|
2001 (and (eq c ?.) (js2-digit-p (js2-peek-char)))) |
|
2002 (setq js2-ts-string-buffer nil |
|
2003 base 10) |
|
2004 (when (eq c ?0) |
|
2005 (setq c (js2-get-char)) |
|
2006 (cond |
|
2007 ((or (eq c ?x) (eq c ?X)) |
|
2008 (setq base 16) |
|
2009 (setq c (js2-get-char))) |
|
2010 ((js2-digit-p c) |
|
2011 (setq base 8)) |
|
2012 (t |
|
2013 (js2-add-to-string ?0)))) |
|
2014 |
|
2015 (if (eq base 16) |
|
2016 (while (<= 0 (js2-x-digit-to-int c 0)) |
|
2017 (js2-add-to-string c) |
|
2018 (setq c (js2-get-char))) |
|
2019 (while (and (<= ?0 c) (<= c ?9)) |
|
2020 ;; We permit 08 and 09 as decimal numbers, which |
|
2021 ;; makes our behavior a superset of the ECMA |
|
2022 ;; numeric grammar. We might not always be so |
|
2023 ;; permissive, so we warn about it. |
|
2024 (when (and (eq base 8) (>= c ?8)) |
|
2025 (js2-report-warning "msg.bad.octal.literal" |
|
2026 (if (eq c ?8) "8" "9")) |
|
2027 (setq base 10)) |
|
2028 (js2-add-to-string c) |
|
2029 (setq c (js2-get-char)))) |
|
2030 |
|
2031 (setq is-integer t) |
|
2032 |
|
2033 (when (and (eq base 10) (memq c '(?. ?e ?E))) |
|
2034 (setq is-integer nil) |
|
2035 (when (eq c ?.) |
|
2036 (loop do |
|
2037 (js2-add-to-string c) |
|
2038 (setq c (js2-get-char)) |
|
2039 while (js2-digit-p c))) |
|
2040 (when (memq c '(?e ?E)) |
|
2041 (js2-add-to-string c) |
|
2042 (setq c (js2-get-char)) |
|
2043 (when (memq c '(?+ ?-)) |
|
2044 (js2-add-to-string c) |
|
2045 (setq c (js2-get-char))) |
|
2046 (unless (js2-digit-p c) |
|
2047 (js2-report-scan-error "msg.missing.exponent" t)) |
|
2048 (loop do |
|
2049 (js2-add-to-string c) |
|
2050 (setq c (js2-get-char)) |
|
2051 while (js2-digit-p c)))) |
|
2052 |
|
2053 (js2-unget-char) |
|
2054 (setq js2-ts-string (js2-get-string-from-buffer) |
|
2055 js2-ts-number |
|
2056 (if (and (eq base 10) (not is-integer)) |
|
2057 (string-to-number js2-ts-string) |
|
2058 ;; TODO: call runtime number-parser. Some of it is in |
|
2059 ;; js2-util.el, but I need to port ScriptRuntime.stringToNumber. |
|
2060 (string-to-number js2-ts-string))) |
|
2061 (throw 'return js2-NUMBER)) |
|
2062 |
|
2063 ;; is it a string? |
|
2064 (when (memq c '(?\" ?\')) |
|
2065 ;; We attempt to accumulate a string the fast way, by |
|
2066 ;; building it directly out of the reader. But if there |
|
2067 ;; are any escaped characters in the string, we revert to |
|
2068 ;; building it out of a string buffer. |
|
2069 (setq quote-char c |
|
2070 js2-ts-string-buffer nil |
|
2071 c (js2-get-char)) |
|
2072 (catch 'break |
|
2073 (while (/= c quote-char) |
|
2074 (catch 'continue |
|
2075 (when (or (eq c ?\n) (eq c js2-EOF_CHAR)) |
|
2076 (js2-unget-char) |
|
2077 (setq js2-token-end js2-ts-cursor) |
|
2078 (js2-report-error "msg.unterminated.string.lit") |
|
2079 (throw 'return js2-STRING)) |
|
2080 |
|
2081 (when (eq c ?\\) |
|
2082 ;; We've hit an escaped character |
|
2083 (setq c (js2-get-char)) |
|
2084 (case c |
|
2085 (?b (setq c ?\b)) |
|
2086 (?f (setq c ?\f)) |
|
2087 (?n (setq c ?\n)) |
|
2088 (?r (setq c ?\r)) |
|
2089 (?t (setq c ?\t)) |
|
2090 (?v (setq c ?\v)) |
|
2091 (?u |
|
2092 (setq c1 (js2-read-unicode-escape)) |
|
2093 (if js2-parse-ide-mode |
|
2094 (if c1 |
|
2095 (progn |
|
2096 ;; just copy the string in IDE-mode |
|
2097 (js2-add-to-string ?\\) |
|
2098 (js2-add-to-string ?u) |
|
2099 (dotimes (i 3) |
|
2100 (js2-add-to-string (js2-get-char))) |
|
2101 (setq c (js2-get-char))) ; added at end of loop |
|
2102 ;; flag it as an invalid escape |
|
2103 (js2-report-warning "msg.invalid.escape" |
|
2104 nil (- js2-ts-cursor 2) 6)) |
|
2105 ;; Get 4 hex digits; if the u escape is not |
|
2106 ;; followed by 4 hex digits, use 'u' + the |
|
2107 ;; literal character sequence that follows. |
|
2108 (js2-add-to-string ?u) |
|
2109 (setq escape-val 0) |
|
2110 (dotimes (i 4) |
|
2111 (setq c (js2-get-char) |
|
2112 escape-val (js2-x-digit-to-int c escape-val)) |
|
2113 (if (minusp escape-val) |
|
2114 (throw 'continue nil)) |
|
2115 (js2-add-to-string c)) |
|
2116 ;; prepare for replace of stored 'u' sequence by escape value |
|
2117 (setq js2-ts-string-buffer (nthcdr 5 js2-ts-string-buffer) |
|
2118 c escape-val))) |
|
2119 (?x |
|
2120 ;; Get 2 hex digits, defaulting to 'x'+literal |
|
2121 ;; sequence, as above. |
|
2122 (setq c (js2-get-char) |
|
2123 escape-val (js2-x-digit-to-int c 0)) |
|
2124 (if (minusp escape-val) |
|
2125 (progn |
|
2126 (js2-add-to-string ?x) |
|
2127 (throw 'continue nil)) |
|
2128 (setq c1 c |
|
2129 c (js2-get-char) |
|
2130 escape-val (js2-x-digit-to-int c escape-val)) |
|
2131 (if (minusp escape-val) |
|
2132 (progn |
|
2133 (js2-add-to-string ?x) |
|
2134 (js2-add-to-string c1) |
|
2135 (throw 'continue nil)) |
|
2136 ;; got 2 hex digits |
|
2137 (setq c escape-val)))) |
|
2138 (?\n |
|
2139 ;; Remove line terminator after escape to follow |
|
2140 ;; SpiderMonkey and C/C++ |
|
2141 (setq c (js2-get-char)) |
|
2142 (throw 'continue nil)) |
|
2143 (t |
|
2144 (when (and (<= ?0 c) (< c ?8)) |
|
2145 (setq val (- c ?0) |
|
2146 c (js2-get-char)) |
|
2147 (when (and (<= ?0 c) (< c ?8)) |
|
2148 (setq val (- (+ (* 8 val) c) ?0) |
|
2149 c (js2-get-char)) |
|
2150 (when (and (<= ?0 c) |
|
2151 (< c ?8) |
|
2152 (< val #o37)) |
|
2153 ;; c is 3rd char of octal sequence only |
|
2154 ;; if the resulting val <= 0377 |
|
2155 (setq val (- (+ (* 8 val) c) ?0) |
|
2156 c (js2-get-char)))) |
|
2157 (js2-unget-char) |
|
2158 (setq c val))))) |
|
2159 (js2-add-to-string c) |
|
2160 (setq c (js2-get-char))))) |
|
2161 (setq js2-ts-string (js2-get-string-from-buffer)) |
|
2162 (throw 'return js2-STRING)) |
|
2163 |
|
2164 (case c |
|
2165 (?\; |
|
2166 (throw 'return js2-SEMI)) |
|
2167 (?\[ |
|
2168 (throw 'return js2-LB)) |
|
2169 (?\] |
|
2170 (throw 'return js2-RB)) |
|
2171 (?{ |
|
2172 (throw 'return js2-LC)) |
|
2173 (?} |
|
2174 (throw 'return js2-RC)) |
|
2175 (?\( |
|
2176 (throw 'return js2-LP)) |
|
2177 (?\) |
|
2178 (throw 'return js2-RP)) |
|
2179 (?, |
|
2180 (throw 'return js2-COMMA)) |
|
2181 (?? |
|
2182 (throw 'return js2-HOOK)) |
|
2183 (?: |
|
2184 (if (js2-match-char ?:) |
|
2185 (js2-ts-return js2-COLONCOLON) |
|
2186 (throw 'return js2-COLON))) |
|
2187 (?. |
|
2188 (if (js2-match-char ?.) |
|
2189 (js2-ts-return js2-DOTDOT) |
|
2190 (if (js2-match-char ?\() |
|
2191 (js2-ts-return js2-DOTQUERY) |
|
2192 (throw 'return js2-DOT)))) |
|
2193 (?| |
|
2194 (if (js2-match-char ?|) |
|
2195 (throw 'return js2-OR) |
|
2196 (if (js2-match-char ?=) |
|
2197 (js2-ts-return js2-ASSIGN_BITOR) |
|
2198 (throw 'return js2-BITOR)))) |
|
2199 (?^ |
|
2200 (if (js2-match-char ?=) |
|
2201 (js2-ts-return js2-ASSIGN_BITOR) |
|
2202 (throw 'return js2-BITXOR))) |
|
2203 (?& |
|
2204 (if (js2-match-char ?&) |
|
2205 (throw 'return js2-AND) |
|
2206 (if (js2-match-char ?=) |
|
2207 (js2-ts-return js2-ASSIGN_BITAND) |
|
2208 (throw 'return js2-BITAND)))) |
|
2209 (?= |
|
2210 (if (js2-match-char ?=) |
|
2211 (if (js2-match-char ?=) |
|
2212 (js2-ts-return js2-SHEQ) |
|
2213 (throw 'return js2-EQ)) |
|
2214 (throw 'return js2-ASSIGN))) |
|
2215 (?! |
|
2216 (if (js2-match-char ?=) |
|
2217 (if (js2-match-char ?=) |
|
2218 (js2-ts-return js2-SHNE) |
|
2219 (js2-ts-return js2-NE)) |
|
2220 (throw 'return js2-NOT))) |
|
2221 (?< |
|
2222 ;; NB:treat HTML begin-comment as comment-till-eol |
|
2223 (when (js2-match-char ?!) |
|
2224 (when (js2-match-char ?-) |
|
2225 (when (js2-match-char ?-) |
|
2226 (js2-skip-line) |
|
2227 (setq js2-ts-comment-type 'html) |
|
2228 (throw 'return js2-COMMENT))) |
|
2229 (js2-unget-char)) |
|
2230 |
|
2231 (if (js2-match-char ?<) |
|
2232 (if (js2-match-char ?=) |
|
2233 (js2-ts-return js2-ASSIGN_LSH) |
|
2234 (js2-ts-return js2-LSH)) |
|
2235 (if (js2-match-char ?=) |
|
2236 (js2-ts-return js2-LE) |
|
2237 (throw 'return js2-LT)))) |
|
2238 (?> |
|
2239 (if (js2-match-char ?>) |
|
2240 (if (js2-match-char ?>) |
|
2241 (if (js2-match-char ?=) |
|
2242 (js2-ts-return js2-ASSIGN_URSH) |
|
2243 (js2-ts-return js2-URSH)) |
|
2244 (if (js2-match-char ?=) |
|
2245 (js2-ts-return js2-ASSIGN_RSH) |
|
2246 (js2-ts-return js2-RSH))) |
|
2247 (if (js2-match-char ?=) |
|
2248 (js2-ts-return js2-GE) |
|
2249 (throw 'return js2-GT)))) |
|
2250 (?* |
|
2251 (if (js2-match-char ?=) |
|
2252 (js2-ts-return js2-ASSIGN_MUL) |
|
2253 (throw 'return js2-MUL))) |
|
2254 |
|
2255 (?/ |
|
2256 ;; is it a // comment? |
|
2257 (when (js2-match-char ?/) |
|
2258 (setq js2-token-beg (- js2-ts-cursor 2)) |
|
2259 (js2-skip-line) |
|
2260 (setq js2-ts-comment-type 'line) |
|
2261 (throw 'return js2-COMMENT)) |
|
2262 |
|
2263 ;; is it a /* comment? |
|
2264 (when (js2-match-char ?*) |
|
2265 (setq look-for-slash nil |
|
2266 js2-token-beg (- js2-ts-cursor 2) |
|
2267 js2-ts-comment-type |
|
2268 (if (js2-match-char ?*) |
|
2269 (progn |
|
2270 (setq look-for-slash t) |
|
2271 'jsdoc) |
|
2272 'block)) |
|
2273 (while t |
|
2274 (setq c (js2-get-char)) |
|
2275 (cond |
|
2276 ((eq c js2-EOF_CHAR) |
|
2277 (setq js2-token-end (1- js2-ts-cursor)) |
|
2278 (js2-report-error "msg.unterminated.comment") |
|
2279 (throw 'return js2-COMMENT)) |
|
2280 ((eq c ?*) |
|
2281 (setq look-for-slash t)) |
|
2282 ((eq c ?/) |
|
2283 (if look-for-slash |
|
2284 (js2-ts-return js2-COMMENT))) |
|
2285 (t |
|
2286 (setq look-for-slash nil |
|
2287 js2-token-end js2-ts-cursor))))) |
|
2288 |
|
2289 (if (js2-match-char ?=) |
|
2290 (js2-ts-return js2-ASSIGN_DIV) |
|
2291 (throw 'return js2-DIV))) |
|
2292 |
|
2293 (?# |
|
2294 (when js2-skip-preprocessor-directives |
|
2295 (js2-skip-line) |
|
2296 (setq js2-ts-comment-type 'preprocessor |
|
2297 js2-token-end js2-ts-cursor) |
|
2298 (throw 'return js2-COMMENT)) |
|
2299 (throw 'return js2-ERROR)) |
|
2300 |
|
2301 (?% |
|
2302 (if (js2-match-char ?=) |
|
2303 (js2-ts-return js2-ASSIGN_MOD) |
|
2304 (throw 'return js2-MOD))) |
|
2305 (?~ |
|
2306 (throw 'return js2-BITNOT)) |
|
2307 (?+ |
|
2308 (if (js2-match-char ?=) |
|
2309 (js2-ts-return js2-ASSIGN_ADD) |
|
2310 (if (js2-match-char ?+) |
|
2311 (js2-ts-return js2-INC) |
|
2312 (throw 'return js2-ADD)))) |
|
2313 (?- |
|
2314 (cond |
|
2315 ((js2-match-char ?=) |
|
2316 (setq c js2-ASSIGN_SUB)) |
|
2317 ((js2-match-char ?-) |
|
2318 (unless js2-ts-dirty-line |
|
2319 ;; treat HTML end-comment after possible whitespace |
|
2320 ;; after line start as comment-until-eol |
|
2321 (when (js2-match-char ?>) |
|
2322 (js2-skip-line) |
|
2323 (setq js2-ts-comment-type 'html) |
|
2324 (throw 'return js2-COMMENT))) |
|
2325 (setq c js2-DEC)) |
|
2326 (t |
|
2327 (setq c js2-SUB))) |
|
2328 (setq js2-ts-dirty-line t) |
|
2329 (js2-ts-return c)) |
|
2330 |
|
2331 (otherwise |
|
2332 (js2-report-scan-error "msg.illegal.character"))))))) |
|
2333 |
|
2334 (defun js2-read-regexp (start-token) |
|
2335 "Called by parser when it gets / or /= in literal context." |
|
2336 (let (c |
|
2337 err |
|
2338 in-class ; inside a '[' .. ']' character-class |
|
2339 flags |
|
2340 (continue t)) |
|
2341 (setq js2-token-beg js2-ts-cursor |
|
2342 js2-ts-string-buffer nil |
|
2343 js2-ts-regexp-flags nil) |
|
2344 |
|
2345 (if (eq start-token js2-ASSIGN_DIV) |
|
2346 ;; mis-scanned /= |
|
2347 (js2-add-to-string ?=) |
|
2348 (if (neq start-token js2-DIV) |
|
2349 (error "failed assertion"))) |
|
2350 |
|
2351 (while (and (not err) |
|
2352 (or (/= (setq c (js2-get-char)) ?/) |
|
2353 in-class)) |
|
2354 (cond |
|
2355 ((or (= c ?\n) |
|
2356 (= c js2-EOF_CHAR)) |
|
2357 (setq js2-token-end (1- js2-ts-cursor) |
|
2358 err t |
|
2359 js2-ts-string (js2-collect-string js2-ts-string-buffer)) |
|
2360 (js2-report-error "msg.unterminated.re.lit")) |
|
2361 (t (cond |
|
2362 ((= c ?\\) |
|
2363 (js2-add-to-string c) |
|
2364 (setq c (js2-get-char))) |
|
2365 |
|
2366 ((= c ?\[) |
|
2367 (setq in-class t)) |
|
2368 |
|
2369 ((= c ?\]) |
|
2370 (setq in-class nil))) |
|
2371 (js2-add-to-string c)))) |
|
2372 |
|
2373 (unless err |
|
2374 (while continue |
|
2375 (cond |
|
2376 ((js2-match-char ?g) |
|
2377 (push ?g flags)) |
|
2378 ((js2-match-char ?i) |
|
2379 (push ?i flags)) |
|
2380 ((js2-match-char ?m) |
|
2381 (push ?m flags)) |
|
2382 (t |
|
2383 (setq continue nil)))) |
|
2384 (if (js2-alpha-p (js2-peek-char)) |
|
2385 (js2-report-scan-error "msg.invalid.re.flag" t |
|
2386 js2-ts-cursor 1)) |
|
2387 (setq js2-ts-string (js2-collect-string js2-ts-string-buffer) |
|
2388 js2-ts-regexp-flags (js2-collect-string flags) |
|
2389 js2-token-end js2-ts-cursor) |
|
2390 ;; tell `parse-partial-sexp' to ignore this range of chars |
|
2391 (put-text-property js2-token-beg js2-token-end 'syntax-class '(2))))) |
|
2392 |
|
2393 (defun js2-get-first-xml-token () |
|
2394 (setq js2-ts-xml-open-tags-count 0 |
|
2395 js2-ts-is-xml-attribute nil |
|
2396 js2-ts-xml-is-tag-content nil) |
|
2397 (js2-unget-char) |
|
2398 (js2-get-next-xml-token)) |
|
2399 |
|
2400 (defsubst js2-xml-discard-string () |
|
2401 "Throw away the string in progress and flag an XML parse error." |
|
2402 (setq js2-ts-string-buffer nil |
|
2403 js2-ts-string nil) |
|
2404 (js2-report-scan-error "msg.XML.bad.form" t)) |
|
2405 |
|
2406 (defun js2-get-next-xml-token () |
|
2407 (setq js2-ts-string-buffer nil ; for recording the XML |
|
2408 js2-token-beg js2-ts-cursor) |
|
2409 (let (c result) |
|
2410 (setq result |
|
2411 (catch 'return |
|
2412 (while t |
|
2413 (setq c (js2-get-char)) |
|
2414 (cond |
|
2415 ((= c js2-EOF_CHAR) |
|
2416 (throw 'return js2-ERROR)) |
|
2417 |
|
2418 (js2-ts-xml-is-tag-content |
|
2419 (case c |
|
2420 (?> |
|
2421 (js2-add-to-string c) |
|
2422 (setq js2-ts-xml-is-tag-content nil |
|
2423 js2-ts-is-xml-attribute nil)) |
|
2424 (?/ |
|
2425 (js2-add-to-string c) |
|
2426 (when (eq ?> (js2-peek-char)) |
|
2427 (setq c (js2-get-char)) |
|
2428 (js2-add-to-string c) |
|
2429 (setq js2-ts-xml-is-tag-content nil) |
|
2430 (decf js2-ts-xml-open-tags-count))) |
|
2431 (?{ |
|
2432 (js2-unget-char) |
|
2433 (setq js2-ts-string (js2-get-string-from-buffer)) |
|
2434 (throw 'return js2-XML)) |
|
2435 ((?\' ?\") |
|
2436 (js2-add-to-string c) |
|
2437 (unless (js2-read-quoted-string c) |
|
2438 (throw 'return js2-ERROR))) |
|
2439 (?= |
|
2440 (js2-add-to-string c) |
|
2441 (setq js2-ts-is-xml-attribute t)) |
|
2442 ((? ?\t ?\r ?\n) |
|
2443 (js2-add-to-string c)) |
|
2444 (t |
|
2445 (js2-add-to-string c) |
|
2446 (setq js2-ts-is-xml-attribute nil))) |
|
2447 (when (and (not js2-ts-xml-is-tag-content) |
|
2448 (zerop js2-ts-xml-open-tags-count)) |
|
2449 (setq js2-ts-string (js2-get-string-from-buffer)) |
|
2450 (throw 'return js2-XMLEND))) |
|
2451 |
|
2452 (t |
|
2453 ;; else not tag content |
|
2454 (case c |
|
2455 (?< |
|
2456 (js2-add-to-string c) |
|
2457 (setq c (js2-peek-char)) |
|
2458 (case c |
|
2459 (?! |
|
2460 (setq c (js2-get-char)) ;; skip ! |
|
2461 (js2-add-to-string c) |
|
2462 (setq c (js2-peek-char)) |
|
2463 (case c |
|
2464 (?- |
|
2465 (setq c (js2-get-char)) ;; skip - |
|
2466 (js2-add-to-string c) |
|
2467 (if (eq c ?-) |
|
2468 (progn |
|
2469 (js2-add-to-string c) |
|
2470 (unless (js2-read-xml-comment) |
|
2471 (throw 'return js2-ERROR))) |
|
2472 (js2-xml-discard-string) |
|
2473 (throw 'return js2-ERROR))) |
|
2474 (?\[ |
|
2475 (setq c (js2-get-char)) ;; skip [ |
|
2476 (js2-add-to-string c) |
|
2477 (if (and (= (js2-get-char) ?C) |
|
2478 (= (js2-get-char) ?D) |
|
2479 (= (js2-get-char) ?A) |
|
2480 (= (js2-get-char) ?T) |
|
2481 (= (js2-get-char) ?A) |
|
2482 (= (js2-get-char) ?\[)) |
|
2483 (progn |
|
2484 (js2-add-to-string ?C) |
|
2485 (js2-add-to-string ?D) |
|
2486 (js2-add-to-string ?A) |
|
2487 (js2-add-to-string ?T) |
|
2488 (js2-add-to-string ?A) |
|
2489 (js2-add-to-string ?\[) |
|
2490 (unless (js2-read-cdata) |
|
2491 (throw 'return js2-ERROR))) |
|
2492 (js2-xml-discard-string) |
|
2493 (throw 'return js2-ERROR))) |
|
2494 (t |
|
2495 (unless (js2-read-entity) |
|
2496 (throw 'return js2-ERROR))))) |
|
2497 (?? |
|
2498 (setq c (js2-get-char)) ;; skip ? |
|
2499 (js2-add-to-string c) |
|
2500 (unless (js2-read-PI) |
|
2501 (throw 'return js2-ERROR))) |
|
2502 (?/ |
|
2503 ;; end tag |
|
2504 (setq c (js2-get-char)) ;; skip / |
|
2505 (js2-add-to-string c) |
|
2506 (when (zerop js2-ts-xml-open-tags-count) |
|
2507 (js2-xml-discard-string) |
|
2508 (throw 'return js2-ERROR)) |
|
2509 (setq js2-ts-xml-is-tag-content t) |
|
2510 (decf js2-ts-xml-open-tags-count)) |
|
2511 (t |
|
2512 ;; start tag |
|
2513 (setq js2-ts-xml-is-tag-content t) |
|
2514 (incf js2-ts-xml-open-tags-count)))) |
|
2515 (?{ |
|
2516 (js2-unget-char) |
|
2517 (setq js2-ts-string (js2-get-string-from-buffer)) |
|
2518 (throw 'return js2-XML)) |
|
2519 (t |
|
2520 (js2-add-to-string c)))))))) |
|
2521 (setq js2-token-end js2-ts-cursor) |
|
2522 result)) |
|
2523 |
|
2524 (defun js2-read-quoted-string (quote) |
|
2525 (let (c) |
|
2526 (catch 'return |
|
2527 (while (/= (setq c (js2-get-char)) js2-EOF_CHAR) |
|
2528 (js2-add-to-string c) |
|
2529 (if (eq c quote) |
|
2530 (throw 'return t))) |
|
2531 (js2-xml-discard-string) ;; throw away string in progress |
|
2532 nil))) |
|
2533 |
|
2534 (defun js2-read-xml-comment () |
|
2535 (let ((c (js2-get-char))) |
|
2536 (catch 'return |
|
2537 (while (/= c js2-EOF_CHAR) |
|
2538 (catch 'continue |
|
2539 (js2-add-to-string c) |
|
2540 (when (and (eq c ?-) (eq ?- (js2-peek-char))) |
|
2541 (setq c (js2-get-char)) |
|
2542 (js2-add-to-string c) |
|
2543 (if (eq (js2-peek-char) ?>) |
|
2544 (progn |
|
2545 (setq c (js2-get-char)) ;; skip > |
|
2546 (js2-add-to-string c) |
|
2547 (throw 'return t)) |
|
2548 (throw 'continue nil))) |
|
2549 (setq c (js2-get-char)))) |
|
2550 (js2-xml-discard-string) |
|
2551 nil))) |
|
2552 |
|
2553 (defun js2-read-cdata () |
|
2554 (let ((c (js2-get-char))) |
|
2555 (catch 'return |
|
2556 (while (/= c js2-EOF_CHAR) |
|
2557 (catch 'continue |
|
2558 (js2-add-to-string c) |
|
2559 (when (and (eq c ?\]) (eq (js2-peek-char) ?\])) |
|
2560 (setq c (js2-get-char)) |
|
2561 (js2-add-to-string c) |
|
2562 (if (eq (js2-peek-char) ?>) |
|
2563 (progn |
|
2564 (setq c (js2-get-char)) ;; Skip > |
|
2565 (js2-add-to-string c) |
|
2566 (throw 'return t)) |
|
2567 (throw 'continue nil))) |
|
2568 (setq c (js2-get-char)))) |
|
2569 (js2-xml-discard-string) |
|
2570 nil))) |
|
2571 |
|
2572 (defun js2-read-entity () |
|
2573 (let ((decl-tags 1) |
|
2574 c) |
|
2575 (catch 'return |
|
2576 (while (/= js2-EOF_CHAR (setq c (js2-get-char))) |
|
2577 (js2-add-to-string c) |
|
2578 (case c |
|
2579 (?< |
|
2580 (incf decl-tags)) |
|
2581 (?> |
|
2582 (decf decl-tags) |
|
2583 (if (zerop decl-tags) |
|
2584 (throw 'return t))))) |
|
2585 (js2-xml-discard-string) |
|
2586 nil))) |
|
2587 |
|
2588 (defun js2-read-PI () |
|
2589 "Scan an XML processing instruction." |
|
2590 (let (c) |
|
2591 (catch 'return |
|
2592 (while (/= js2-EOF_CHAR (setq c (js2-get-char))) |
|
2593 (js2-add-to-string c) |
|
2594 (when (and (eq c ??) (eq (js2-peek-char) ?>)) |
|
2595 (setq c (js2-get-char)) ;; Skip > |
|
2596 (js2-add-to-string c) |
|
2597 (throw 'return t))) |
|
2598 (js2-xml-discard-string) |
|
2599 nil))) |
|
2600 |
|
2601 (defun js2-scanner-get-line () |
|
2602 "Return the text of the current scan line." |
|
2603 (buffer-substring (point-at-bol) (point-at-eol))) |
|
2604 |
|
2605 (provide 'js2-scan) |
|
2606 |
|
2607 ;;; js2-scan.el ends here |
|
2608 ;;; js2-messages: localizable messages for js2-mode |
|
2609 |
|
2610 ;; Author: Steve Yegge (steve.yegge@gmail.com) |
|
2611 ;; Keywords: javascript languages |
|
2612 |
|
2613 ;;; Commentary: |
|
2614 |
|
2615 ;; Messages are copied from Rhino's Messages.properties. |
|
2616 ;; Many of the Java-specific messages have been elided. |
|
2617 ;; Add any js2-specific ones at the end, so we can keep |
|
2618 ;; this file synced with changes to Rhino's. |
|
2619 ;; |
|
2620 ;; TODO: |
|
2621 ;; - move interpreter messages into separate file |
|
2622 |
|
2623 ;;; Code: |
|
2624 |
|
2625 (defvar js2-message-table |
|
2626 (make-hash-table :test 'equal :size 250) |
|
2627 "Contains localized messages for js2-mode.") |
|
2628 |
|
2629 ;; TODO: construct this hashtable at compile-time. |
|
2630 (defmacro js2-msg (key &rest strings) |
|
2631 `(puthash ,key (funcall #'concat ,@strings) |
|
2632 js2-message-table)) |
|
2633 |
|
2634 (defun js2-get-msg (msg-key) |
|
2635 "Look up a localized message. |
|
2636 MSG-KEY is a list of (MSG ARGS). If the message takes parameters, |
|
2637 the correct number of ARGS must be provided." |
|
2638 (let* ((key (if (listp msg-key) (car msg-key) msg-key)) |
|
2639 (args (if (listp msg-key) (cdr msg-key))) |
|
2640 (msg (gethash key js2-message-table))) |
|
2641 (if msg |
|
2642 (apply #'format msg args) |
|
2643 key))) ; default to showing the key |
|
2644 |
|
2645 (js2-msg "msg.dup.parms" |
|
2646 "Duplicate parameter name '%s'.") |
|
2647 |
|
2648 (js2-msg "msg.too.big.jump" |
|
2649 "Program too complex: jump offset too big.") |
|
2650 |
|
2651 (js2-msg "msg.too.big.index" |
|
2652 "Program too complex: internal index exceeds 64K limit.") |
|
2653 |
|
2654 (js2-msg "msg.while.compiling.fn" |
|
2655 "Encountered code generation error while compiling function '%s': %s") |
|
2656 |
|
2657 (js2-msg "msg.while.compiling.script" |
|
2658 "Encountered code generation error while compiling script: %s") |
|
2659 |
|
2660 ;; Context |
|
2661 (js2-msg "msg.ctor.not.found" |
|
2662 "Constructor for '%s' not found.") |
|
2663 |
|
2664 (js2-msg "msg.not.ctor" |
|
2665 "'%s' is not a constructor.") |
|
2666 |
|
2667 ;; FunctionObject |
|
2668 (js2-msg "msg.varargs.ctor" |
|
2669 "Method or constructor '%s' must be static " |
|
2670 "with the signature (Context cx, Object[] args, " |
|
2671 "Function ctorObj, boolean inNewExpr) " |
|
2672 "to define a variable arguments constructor.") |
|
2673 |
|
2674 (js2-msg "msg.varargs.fun" |
|
2675 "Method '%s' must be static with the signature " |
|
2676 "(Context cx, Scriptable thisObj, Object[] args, Function funObj) " |
|
2677 "to define a variable arguments function.") |
|
2678 |
|
2679 (js2-msg "msg.incompat.call" |
|
2680 "Method '%s' called on incompatible object.") |
|
2681 |
|
2682 (js2-msg "msg.bad.parms" |
|
2683 "Unsupported parameter type '%s' in method '%s'.") |
|
2684 |
|
2685 (js2-msg "msg.bad.method.return" |
|
2686 "Unsupported return type '%s' in method '%s'.") |
|
2687 |
|
2688 (js2-msg "msg.bad.ctor.return" |
|
2689 "Construction of objects of type '%s' is not supported.") |
|
2690 |
|
2691 (js2-msg "msg.no.overload" |
|
2692 "Method '%s' occurs multiple times in class '%s'.") |
|
2693 |
|
2694 (js2-msg "msg.method.not.found" |
|
2695 "Method '%s' not found in '%s'.") |
|
2696 |
|
2697 ;; IRFactory |
|
2698 |
|
2699 (js2-msg "msg.bad.for.in.lhs" |
|
2700 "Invalid left-hand side of for..in loop.") |
|
2701 |
|
2702 (js2-msg "msg.mult.index" |
|
2703 "Only one variable allowed in for..in loop.") |
|
2704 |
|
2705 (js2-msg "msg.bad.for.in.destruct" |
|
2706 "Left hand side of for..in loop must be an array of " |
|
2707 "length 2 to accept key/value pair.") |
|
2708 |
|
2709 (js2-msg "msg.cant.convert" |
|
2710 "Can't convert to type '%s'.") |
|
2711 |
|
2712 (js2-msg "msg.bad.assign.left" |
|
2713 "Invalid assignment left-hand side.") |
|
2714 |
|
2715 (js2-msg "msg.bad.decr" |
|
2716 "Invalid decerement operand.") |
|
2717 |
|
2718 (js2-msg "msg.bad.incr" |
|
2719 "Invalid increment operand.") |
|
2720 |
|
2721 (js2-msg "msg.bad.yield" |
|
2722 "yield must be in a function.") |
|
2723 |
|
2724 (js2-msg "msg.yield.parenthesized" |
|
2725 "yield expression must be parenthesized.") |
|
2726 |
|
2727 ;; NativeGlobal |
|
2728 (js2-msg "msg.cant.call.indirect" |
|
2729 "Function '%s' must be called directly, and not by way of a " |
|
2730 "function of another name.") |
|
2731 |
|
2732 (js2-msg "msg.eval.nonstring" |
|
2733 "Calling eval() with anything other than a primitive " |
|
2734 "string value will simply return the value. " |
|
2735 "Is this what you intended?") |
|
2736 |
|
2737 (js2-msg "msg.eval.nonstring.strict" |
|
2738 "Calling eval() with anything other than a primitive " |
|
2739 "string value is not allowed in strict mode.") |
|
2740 |
|
2741 (js2-msg "msg.bad.destruct.op" |
|
2742 "Invalid destructuring assignment operator") |
|
2743 |
|
2744 ;; NativeCall |
|
2745 (js2-msg "msg.only.from.new" |
|
2746 "'%s' may only be invoked from a `new' expression.") |
|
2747 |
|
2748 (js2-msg "msg.deprec.ctor" |
|
2749 "The '%s' constructor is deprecated.") |
|
2750 |
|
2751 ;; NativeFunction |
|
2752 (js2-msg "msg.no.function.ref.found" |
|
2753 "no source found to decompile function reference %s") |
|
2754 |
|
2755 (js2-msg "msg.arg.isnt.array" |
|
2756 "second argument to Function.prototype.apply must be an array") |
|
2757 |
|
2758 ;; NativeGlobal |
|
2759 (js2-msg "msg.bad.esc.mask" |
|
2760 "invalid string escape mask") |
|
2761 |
|
2762 ;; NativeRegExp |
|
2763 (js2-msg "msg.bad.quant" |
|
2764 "Invalid quantifier %s") |
|
2765 |
|
2766 (js2-msg "msg.overlarge.backref" |
|
2767 "Overly large back reference %s") |
|
2768 |
|
2769 (js2-msg "msg.overlarge.min" |
|
2770 "Overly large minimum %s") |
|
2771 |
|
2772 (js2-msg "msg.overlarge.max" |
|
2773 "Overly large maximum %s") |
|
2774 |
|
2775 (js2-msg "msg.zero.quant" |
|
2776 "Zero quantifier %s") |
|
2777 |
|
2778 (js2-msg "msg.max.lt.min" |
|
2779 "Maximum %s less than minimum") |
|
2780 |
|
2781 (js2-msg "msg.unterm.quant" |
|
2782 "Unterminated quantifier %s") |
|
2783 |
|
2784 (js2-msg "msg.unterm.paren" |
|
2785 "Unterminated parenthetical %s") |
|
2786 |
|
2787 (js2-msg "msg.unterm.class" |
|
2788 "Unterminated character class %s") |
|
2789 |
|
2790 (js2-msg "msg.bad.range" |
|
2791 "Invalid range in character class.") |
|
2792 |
|
2793 (js2-msg "msg.trail.backslash" |
|
2794 "Trailing \\ in regular expression.") |
|
2795 |
|
2796 (js2-msg "msg.re.unmatched.right.paren" |
|
2797 "unmatched ) in regular expression.") |
|
2798 |
|
2799 (js2-msg "msg.no.regexp" |
|
2800 "Regular expressions are not available.") |
|
2801 |
|
2802 (js2-msg "msg.bad.backref" |
|
2803 "back-reference exceeds number of capturing parentheses.") |
|
2804 |
|
2805 (js2-msg "msg.bad.regexp.compile" |
|
2806 "Only one argument may be specified if the first " |
|
2807 "argument to RegExp.prototype.compile is a RegExp object.") |
|
2808 |
|
2809 ;; Parser |
|
2810 (js2-msg "msg.got.syntax.errors" |
|
2811 "Compilation produced %s syntax errors.") |
|
2812 |
|
2813 (js2-msg "msg.var.redecl" |
|
2814 "TypeError: redeclaration of var %s.") |
|
2815 |
|
2816 (js2-msg "msg.const.redecl" |
|
2817 "TypeError: redeclaration of const %s.") |
|
2818 |
|
2819 (js2-msg "msg.let.redecl" |
|
2820 "TypeError: redeclaration of variable %s.") |
|
2821 |
|
2822 (js2-msg "msg.parm.redecl" |
|
2823 "TypeError: redeclaration of formal parameter %s.") |
|
2824 |
|
2825 (js2-msg "msg.fn.redecl" |
|
2826 "TypeError: redeclaration of function %s.") |
|
2827 |
|
2828 (js2-msg "msg.let.decl.not.in.block" |
|
2829 "SyntaxError: let declaration not directly within block") |
|
2830 |
|
2831 ;; NodeTransformer |
|
2832 (js2-msg "msg.dup.label" |
|
2833 "duplicated label") |
|
2834 |
|
2835 (js2-msg "msg.undef.label" |
|
2836 "undefined label") |
|
2837 |
|
2838 (js2-msg "msg.bad.break" |
|
2839 "unlabelled break must be inside loop or switch") |
|
2840 |
|
2841 (js2-msg "msg.continue.outside" |
|
2842 "continue must be inside loop") |
|
2843 |
|
2844 (js2-msg "msg.continue.nonloop" |
|
2845 "continue can only use labels of iteration statements") |
|
2846 |
|
2847 (js2-msg "msg.bad.throw.eol" |
|
2848 "Line terminator is not allowed between the throw " |
|
2849 "keyword and throw expression.") |
|
2850 |
|
2851 (js2-msg "msg.no.paren.parms" |
|
2852 "missing ( before function parameters.") |
|
2853 |
|
2854 (js2-msg "msg.no.parm" |
|
2855 "missing formal parameter") |
|
2856 |
|
2857 (js2-msg "msg.no.paren.after.parms" |
|
2858 "missing ) after formal parameters") |
|
2859 |
|
2860 (js2-msg "msg.no.brace.body" |
|
2861 "missing '{' before function body") |
|
2862 |
|
2863 (js2-msg "msg.no.brace.after.body" |
|
2864 "missing } after function body") |
|
2865 |
|
2866 (js2-msg "msg.no.paren.cond" |
|
2867 "missing ( before condition") |
|
2868 |
|
2869 (js2-msg "msg.no.paren.after.cond" |
|
2870 "missing ) after condition") |
|
2871 |
|
2872 (js2-msg "msg.no.semi.stmt" |
|
2873 "missing ; before statement") |
|
2874 |
|
2875 (js2-msg "msg.missing.semi" |
|
2876 "missing ; after statement") |
|
2877 |
|
2878 (js2-msg "msg.no.name.after.dot" |
|
2879 "missing name after . operator") |
|
2880 |
|
2881 (js2-msg "msg.no.name.after.coloncolon" |
|
2882 "missing name after :: operator") |
|
2883 |
|
2884 (js2-msg "msg.no.name.after.dotdot" |
|
2885 "missing name after .. operator") |
|
2886 |
|
2887 (js2-msg "msg.no.name.after.xmlAttr" |
|
2888 "missing name after .@") |
|
2889 |
|
2890 (js2-msg "msg.no.bracket.index" |
|
2891 "missing ] in index expression") |
|
2892 |
|
2893 (js2-msg "msg.no.paren.switch" |
|
2894 "missing ( before switch expression") |
|
2895 |
|
2896 (js2-msg "msg.no.paren.after.switch" |
|
2897 "missing ) after switch expression") |
|
2898 |
|
2899 (js2-msg "msg.no.brace.switch" |
|
2900 "missing '{' before switch body") |
|
2901 |
|
2902 (js2-msg "msg.bad.switch" |
|
2903 "invalid switch statement") |
|
2904 |
|
2905 (js2-msg "msg.no.colon.case" |
|
2906 "missing : after case expression") |
|
2907 |
|
2908 (js2-msg "msg.double.switch.default" |
|
2909 "double default label in the switch statement") |
|
2910 |
|
2911 (js2-msg "msg.no.while.do" |
|
2912 "missing while after do-loop body") |
|
2913 |
|
2914 (js2-msg "msg.no.paren.for" |
|
2915 "missing ( after for") |
|
2916 |
|
2917 (js2-msg "msg.no.semi.for" |
|
2918 "missing ; after for-loop initializer") |
|
2919 |
|
2920 (js2-msg "msg.no.semi.for.cond" |
|
2921 "missing ; after for-loop condition") |
|
2922 |
|
2923 (js2-msg "msg.in.after.for.name" |
|
2924 "missing in after for") |
|
2925 |
|
2926 (js2-msg "msg.no.paren.for.ctrl" |
|
2927 "missing ) after for-loop control") |
|
2928 |
|
2929 (js2-msg "msg.no.paren.with" |
|
2930 "missing ( before with-statement object") |
|
2931 |
|
2932 (js2-msg "msg.no.paren.after.with" |
|
2933 "missing ) after with-statement object") |
|
2934 |
|
2935 (js2-msg "msg.no.paren.after.let" |
|
2936 "missing ( after let") |
|
2937 |
|
2938 (js2-msg "msg.no.paren.let" |
|
2939 "missing ) after variable list") |
|
2940 |
|
2941 (js2-msg "msg.no.curly.let" |
|
2942 "missing } after let statement") |
|
2943 |
|
2944 (js2-msg "msg.bad.return" |
|
2945 "invalid return") |
|
2946 |
|
2947 (js2-msg "msg.no.brace.block" |
|
2948 "missing } in compound statement") |
|
2949 |
|
2950 (js2-msg "msg.bad.label" |
|
2951 "invalid label") |
|
2952 |
|
2953 (js2-msg "msg.bad.var" |
|
2954 "missing variable name") |
|
2955 |
|
2956 (js2-msg "msg.bad.var.init" |
|
2957 "invalid variable initialization") |
|
2958 |
|
2959 (js2-msg "msg.no.colon.cond" |
|
2960 "missing : in conditional expression") |
|
2961 |
|
2962 (js2-msg "msg.no.paren.arg" |
|
2963 "missing ) after argument list") |
|
2964 |
|
2965 (js2-msg "msg.no.bracket.arg" |
|
2966 "missing ] after element list") |
|
2967 |
|
2968 (js2-msg "msg.bad.prop" |
|
2969 "invalid property id") |
|
2970 |
|
2971 (js2-msg "msg.no.colon.prop" |
|
2972 "missing : after property id") |
|
2973 |
|
2974 (js2-msg "msg.no.brace.prop" |
|
2975 "missing } after property list") |
|
2976 |
|
2977 (js2-msg "msg.no.paren" |
|
2978 "missing ) in parenthetical") |
|
2979 |
|
2980 (js2-msg "msg.reserved.id" |
|
2981 "identifier is a reserved word") |
|
2982 |
|
2983 (js2-msg "msg.no.paren.catch" |
|
2984 "missing ( before catch-block condition") |
|
2985 |
|
2986 (js2-msg "msg.bad.catchcond" |
|
2987 "invalid catch block condition") |
|
2988 |
|
2989 (js2-msg "msg.catch.unreachable" |
|
2990 "any catch clauses following an unqualified catch are unreachable") |
|
2991 |
|
2992 (js2-msg "msg.no.brace.try" |
|
2993 "missing '{' before try block") |
|
2994 |
|
2995 (js2-msg "msg.no.brace.catchblock" |
|
2996 "missing '{' before catch-block body") |
|
2997 |
|
2998 (js2-msg "msg.try.no.catchfinally" |
|
2999 "'try' without 'catch' or 'finally'") |
|
3000 |
|
3001 (js2-msg "msg.no.return.value" |
|
3002 "function %s does not always return a value") |
|
3003 |
|
3004 (js2-msg "msg.anon.no.return.value" |
|
3005 "anonymous function does not always return a value") |
|
3006 |
|
3007 (js2-msg "msg.return.inconsistent" |
|
3008 "return statement is inconsistent with previous usage") |
|
3009 |
|
3010 (js2-msg "msg.generator.returns" |
|
3011 "TypeError: generator function '%s' returns a value") |
|
3012 |
|
3013 (js2-msg "msg.anon.generator.returns" |
|
3014 "TypeError: anonymous generator function returns a value") |
|
3015 |
|
3016 (js2-msg "msg.syntax" |
|
3017 "syntax error") |
|
3018 |
|
3019 (js2-msg "msg.unexpected.eof" |
|
3020 "Unexpected end of file") |
|
3021 |
|
3022 (js2-msg "msg.XML.bad.form" |
|
3023 "illegally formed XML syntax") |
|
3024 |
|
3025 (js2-msg "msg.XML.not.available" |
|
3026 "XML runtime not available") |
|
3027 |
|
3028 (js2-msg "msg.too.deep.parser.recursion" |
|
3029 "Too deep recursion while parsing") |
|
3030 |
|
3031 (js2-msg "msg.no.side.effects" |
|
3032 "Code has no side effects") |
|
3033 |
|
3034 (js2-msg "msg.extra.trailing.comma" |
|
3035 "Trailing comma is not legal in an ECMA-262 object initializer") |
|
3036 |
|
3037 (js2-msg "msg.array.trailing.comma" |
|
3038 "Trailing comma yields different behavior across browsers") |
|
3039 |
|
3040 (js2-msg "msg.equal.as.assign" |
|
3041 (concat "Test for equality (==) mistyped as assignment (=)?" |
|
3042 " (parenthesize to suppress warning)")) |
|
3043 |
|
3044 (js2-msg "msg.var.hides.arg" |
|
3045 "Variable %s hides argument") |
|
3046 |
|
3047 (js2-msg "msg.destruct.assign.no.init" |
|
3048 "Missing = in destructuring declaration") |
|
3049 |
|
3050 ;; ScriptRuntime |
|
3051 (js2-msg "msg.no.properties" |
|
3052 "%s has no properties.") |
|
3053 |
|
3054 (js2-msg "msg.invalid.iterator" |
|
3055 "Invalid iterator value") |
|
3056 |
|
3057 (js2-msg "msg.iterator.primitive" |
|
3058 "__iterator__ returned a primitive value") |
|
3059 |
|
3060 (js2-msg "msg.assn.create.strict" |
|
3061 "Assignment to undeclared variable %s") |
|
3062 |
|
3063 (js2-msg "msg.ref.undefined.prop" |
|
3064 "Reference to undefined property '%s'") |
|
3065 |
|
3066 (js2-msg "msg.prop.not.found" |
|
3067 "Property %s not found.") |
|
3068 |
|
3069 (js2-msg "msg.invalid.type" |
|
3070 "Invalid JavaScript value of type %s") |
|
3071 |
|
3072 (js2-msg "msg.primitive.expected" |
|
3073 "Primitive type expected (had %s instead)") |
|
3074 |
|
3075 (js2-msg "msg.namespace.expected" |
|
3076 "Namespace object expected to left of :: (found %s instead)") |
|
3077 |
|
3078 (js2-msg "msg.null.to.object" |
|
3079 "Cannot convert null to an object.") |
|
3080 |
|
3081 (js2-msg "msg.undef.to.object" |
|
3082 "Cannot convert undefined to an object.") |
|
3083 |
|
3084 (js2-msg "msg.cyclic.value" |
|
3085 "Cyclic %s value not allowed.") |
|
3086 |
|
3087 (js2-msg "msg.is.not.defined" |
|
3088 "'%s' is not defined.") |
|
3089 |
|
3090 (js2-msg "msg.undef.prop.read" |
|
3091 "Cannot read property '%s' from %s") |
|
3092 |
|
3093 (js2-msg "msg.undef.prop.write" |
|
3094 "Cannot set property '%s' of %s to '%s'") |
|
3095 |
|
3096 (js2-msg "msg.undef.prop.delete" |
|
3097 "Cannot delete property '%s' of %s") |
|
3098 |
|
3099 (js2-msg "msg.undef.method.call" |
|
3100 "Cannot call method '%s' of %s") |
|
3101 |
|
3102 (js2-msg "msg.undef.with" |
|
3103 "Cannot apply 'with' to %s") |
|
3104 |
|
3105 (js2-msg "msg.isnt.function" |
|
3106 "%s is not a function, it is %s.") |
|
3107 |
|
3108 (js2-msg "msg.isnt.function.in" |
|
3109 "Cannot call property %s in object %s. " |
|
3110 "It is not a function, it is '%s'.") |
|
3111 |
|
3112 (js2-msg "msg.function.not.found" |
|
3113 "Cannot find function %s.") |
|
3114 |
|
3115 (js2-msg "msg.function.not.found.in" |
|
3116 "Cannot find function %s in object %s.") |
|
3117 |
|
3118 (js2-msg "msg.isnt.xml.object" |
|
3119 "%s is not an xml object.") |
|
3120 |
|
3121 (js2-msg "msg.no.ref.to.get" |
|
3122 "%s is not a reference to read reference value.") |
|
3123 |
|
3124 (js2-msg "msg.no.ref.to.set" |
|
3125 "%s is not a reference to set reference value to %s.") |
|
3126 |
|
3127 (js2-msg "msg.no.ref.from.function" |
|
3128 "Function %s can not be used as the left-hand " |
|
3129 "side of assignment or as an operand of ++ or -- operator.") |
|
3130 |
|
3131 (js2-msg "msg.bad.default.value" |
|
3132 "Object's getDefaultValue() method returned an object.") |
|
3133 |
|
3134 (js2-msg "msg.instanceof.not.object" |
|
3135 "Can't use instanceof on a non-object.") |
|
3136 |
|
3137 (js2-msg "msg.instanceof.bad.prototype" |
|
3138 "'prototype' property of %s is not an object.") |
|
3139 |
|
3140 (js2-msg "msg.bad.radix" |
|
3141 "illegal radix %s.") |
|
3142 |
|
3143 ;; ScriptableObject |
|
3144 (js2-msg "msg.default.value" |
|
3145 "Cannot find default value for object.") |
|
3146 |
|
3147 (js2-msg "msg.zero.arg.ctor" |
|
3148 "Cannot load class '%s' which has no zero-parameter constructor.") |
|
3149 |
|
3150 (js2-msg "msg.ctor.multiple.parms" |
|
3151 "Can't define constructor or class %s since more than " |
|
3152 "one constructor has multiple parameters.") |
|
3153 |
|
3154 (js2-msg "msg.extend.scriptable" |
|
3155 "%s must extend ScriptableObject in order to define property %s.") |
|
3156 |
|
3157 (js2-msg "msg.bad.getter.parms" |
|
3158 "In order to define a property, getter %s must have zero " |
|
3159 "parameters or a single ScriptableObject parameter.") |
|
3160 |
|
3161 (js2-msg "msg.obj.getter.parms" |
|
3162 "Expected static or delegated getter %s to take " |
|
3163 "a ScriptableObject parameter.") |
|
3164 |
|
3165 (js2-msg "msg.getter.static" |
|
3166 "Getter and setter must both be static or neither be static.") |
|
3167 |
|
3168 (js2-msg "msg.setter.return" |
|
3169 "Setter must have void return type: %s") |
|
3170 |
|
3171 (js2-msg "msg.setter2.parms" |
|
3172 "Two-parameter setter must take a ScriptableObject as " |
|
3173 "its first parameter.") |
|
3174 |
|
3175 (js2-msg "msg.setter1.parms" |
|
3176 "Expected single parameter setter for %s") |
|
3177 |
|
3178 (js2-msg "msg.setter2.expected" |
|
3179 "Expected static or delegated setter %s to take two parameters.") |
|
3180 |
|
3181 (js2-msg "msg.setter.parms" |
|
3182 "Expected either one or two parameters for setter.") |
|
3183 |
|
3184 (js2-msg "msg.setter.bad.type" |
|
3185 "Unsupported parameter type '%s' in setter '%s'.") |
|
3186 |
|
3187 (js2-msg "msg.add.sealed" |
|
3188 "Cannot add a property to a sealed object: %s.") |
|
3189 |
|
3190 (js2-msg "msg.remove.sealed" |
|
3191 "Cannot remove a property from a sealed object: %s.") |
|
3192 |
|
3193 (js2-msg "msg.modify.sealed" |
|
3194 "Cannot modify a property of a sealed object: %s.") |
|
3195 |
|
3196 (js2-msg "msg.modify.readonly" |
|
3197 "Cannot modify readonly property: %s.") |
|
3198 |
|
3199 ;; TokenStream |
|
3200 (js2-msg "msg.missing.exponent" |
|
3201 "missing exponent") |
|
3202 |
|
3203 (js2-msg "msg.caught.nfe" |
|
3204 "number format error") |
|
3205 |
|
3206 (js2-msg "msg.unterminated.string.lit" |
|
3207 "unterminated string literal") |
|
3208 |
|
3209 (js2-msg "msg.unterminated.comment" |
|
3210 "unterminated comment") |
|
3211 |
|
3212 (js2-msg "msg.unterminated.re.lit" |
|
3213 "unterminated regular expression literal") |
|
3214 |
|
3215 (js2-msg "msg.invalid.re.flag" |
|
3216 "invalid flag after regular expression") |
|
3217 |
|
3218 (js2-msg "msg.no.re.input.for" |
|
3219 "no input for %s") |
|
3220 |
|
3221 (js2-msg "msg.illegal.character" |
|
3222 "illegal character") |
|
3223 |
|
3224 (js2-msg "msg.invalid.escape" |
|
3225 "invalid Unicode escape sequence") |
|
3226 |
|
3227 (js2-msg "msg.bad.namespace" |
|
3228 "not a valid default namespace statement. " |
|
3229 "Syntax is: default xml namespace = EXPRESSION;") |
|
3230 |
|
3231 ;; TokensStream warnings |
|
3232 (js2-msg "msg.bad.octal.literal" |
|
3233 "illegal octal literal digit %s; " |
|
3234 "interpreting it as a decimal digit") |
|
3235 |
|
3236 (js2-msg "msg.reserved.keyword" |
|
3237 "illegal usage of future reserved keyword %s; " |
|
3238 "interpreting it as ordinary identifier") |
|
3239 |
|
3240 (js2-msg "msg.script.is.not.constructor" |
|
3241 "Script objects are not constructors.") |
|
3242 |
|
3243 ;; Arrays |
|
3244 (js2-msg "msg.arraylength.bad" |
|
3245 "Inappropriate array length.") |
|
3246 |
|
3247 ;; Arrays |
|
3248 (js2-msg "msg.arraylength.too.big" |
|
3249 "Array length %s exceeds supported capacity limit.") |
|
3250 |
|
3251 ;; URI |
|
3252 (js2-msg "msg.bad.uri" |
|
3253 "Malformed URI sequence.") |
|
3254 |
|
3255 ;; Number |
|
3256 (js2-msg "msg.bad.precision" |
|
3257 "Precision %s out of range.") |
|
3258 |
|
3259 ;; NativeGenerator |
|
3260 (js2-msg "msg.send.newborn" |
|
3261 "Attempt to send value to newborn generator") |
|
3262 |
|
3263 (js2-msg "msg.already.exec.gen" |
|
3264 "Already executing generator") |
|
3265 |
|
3266 (js2-msg "msg.StopIteration.invalid" |
|
3267 "StopIteration may not be changed to an arbitrary object.") |
|
3268 |
|
3269 ;; Interpreter |
|
3270 (js2-msg "msg.yield.closing" |
|
3271 "Yield from closing generator") |
|
3272 |
|
3273 (provide 'js2-messages) |
|
3274 ;;; js2-ast.el --- JavaScript syntax tree node definitions |
|
3275 |
|
3276 ;; Author: Steve Yegge (steve.yegge@gmail.com) |
|
3277 ;; Keywords: javascript languages |
|
3278 |
|
3279 ;;; Code: |
|
3280 |
|
3281 (eval-and-compile |
|
3282 (require 'cl)) |
|
3283 |
|
3284 |
|
3285 ;; flags for ast node property 'member-type (used for e4x operators) |
|
3286 (defvar js2-property-flag #x1 "property access: element is valid name") |
|
3287 (defvar js2-attribute-flag #x2 "x.@y or x..@y") |
|
3288 (defvar js2-descendants-flag #x4 "x..y or x..@i") |
|
3289 |
|
3290 (defsubst js2-relpos (pos anchor) |
|
3291 "Convert POS to be relative to ANCHOR. |
|
3292 If POS is nil, returns nil." |
|
3293 (and pos (- pos anchor))) |
|
3294 |
|
3295 (defsubst js2-make-pad (indent) |
|
3296 (if (zerop indent) |
|
3297 "" |
|
3298 (make-string (* indent js2-basic-offset) ? ))) |
|
3299 |
|
3300 (defsubst js2-visit-ast (node callback) |
|
3301 "Visit every node in ast NODE with visitor CALLBACK. |
|
3302 |
|
3303 CALLBACK is a function that takes two arguments: (NODE END-P). It is |
|
3304 called twice: once to visit the node, and again after all the node's |
|
3305 children have been processed. The END-P argument is nil on the first |
|
3306 call and non-nil on the second call. The return value of the callback |
|
3307 affects the traversal: if non-nil, the children of NODE are processed. |
|
3308 If the callback returns nil, or if the node has no children, then the |
|
3309 callback is called immediately with a non-nil END-P argument. |
|
3310 |
|
3311 The node traversal is approximately lexical-order, although there |
|
3312 are currently no guarantees around this." |
|
3313 (let ((vfunc (get (aref node 0) 'js2-visitor))) |
|
3314 ;; visit the node |
|
3315 (when (funcall callback node nil) |
|
3316 ;; visit the kids |
|
3317 (cond |
|
3318 ((eq vfunc 'js2-visit-none) |
|
3319 nil) ; don't even bother calling it |
|
3320 ;; Each AST node type has to define a `js2-visitor' function |
|
3321 ;; that takes a node and a callback, and calls `js2-visit-ast' |
|
3322 ;; on each child of the node. |
|
3323 (vfunc |
|
3324 (funcall vfunc node callback)) |
|
3325 (t |
|
3326 (error "%s does not define a visitor-traversal function" |
|
3327 (aref node 0))))) |
|
3328 ;; call the end-visit |
|
3329 (funcall callback node t))) |
|
3330 |
|
3331 (defstruct (js2-node |
|
3332 (:constructor nil)) ; abstract |
|
3333 "Base AST node type." |
|
3334 (type -1) ; token type |
|
3335 (pos -1) ; start position of this AST node in parsed input |
|
3336 (len 1) ; num characters spanned by the node |
|
3337 props ; optional node property list (an alist) |
|
3338 parent) ; link to parent node; null for root |
|
3339 |
|
3340 (defsubst js2-node-get-prop (node prop &optional default) |
|
3341 (or (cadr (assoc prop (js2-node-props node))) default)) |
|
3342 |
|
3343 (defsubst js2-node-set-prop (node prop value) |
|
3344 (setf (js2-node-props node) |
|
3345 (cons (list prop value) (js2-node-props node)))) |
|
3346 |
|
3347 (defsubst js2-fixup-starts (n nodes) |
|
3348 "Adjust the start positions of NODES to be relative to N. |
|
3349 Any node in the list may be nil, for convenience." |
|
3350 (dolist (node nodes) |
|
3351 (when node |
|
3352 (setf (js2-node-pos node) (- (js2-node-pos node) |
|
3353 (js2-node-pos n)))))) |
|
3354 |
|
3355 (defsubst js2-node-add-children (parent &rest nodes) |
|
3356 "Set parent node of NODES to PARENT, and return PARENT. |
|
3357 Does nothing if we're not recording parent links. |
|
3358 If any given node in NODES is nil, doesn't record that link." |
|
3359 (js2-fixup-starts parent nodes) |
|
3360 (dolist (node nodes) |
|
3361 (and node |
|
3362 (setf (js2-node-parent node) parent)))) |
|
3363 |
|
3364 ;; Non-recursive since it's called a frightening number of times. |
|
3365 (defsubst js2-node-abs-pos (n) |
|
3366 (let ((pos (js2-node-pos n))) |
|
3367 (while (setq n (js2-node-parent n)) |
|
3368 (setq pos (+ pos (js2-node-pos n)))) |
|
3369 pos)) |
|
3370 |
|
3371 (defsubst js2-node-abs-end (n) |
|
3372 "Return absolute buffer position of end of N." |
|
3373 (+ (js2-node-abs-pos n) (js2-node-len n))) |
|
3374 |
|
3375 ;; It's important to make sure block nodes have a lisp list for the |
|
3376 ;; child nodes, to limit printing recursion depth in an AST that |
|
3377 ;; otherwise consists of defstruct vectors. Emacs will crash printing |
|
3378 ;; a sufficiently large vector tree. |
|
3379 |
|
3380 (defstruct (js2-block-node |
|
3381 (:include js2-node) |
|
3382 (:constructor nil) |
|
3383 (:constructor make-js2-block-node (&key (type js2-BLOCK) |
|
3384 (pos js2-token-beg) |
|
3385 len |
|
3386 props |
|
3387 kids))) |
|
3388 "A block of statements." |
|
3389 kids) ; a lisp list of the child statement nodes |
|
3390 |
|
3391 (put 'cl-struct-js2-block-node 'js2-visitor 'js2-visit-block) |
|
3392 (put 'cl-struct-js2-block-node 'js2-printer 'js2-print-block) |
|
3393 |
|
3394 (defsubst js2-visit-block (ast callback) |
|
3395 "Visit the `js2-block-node' children of AST." |
|
3396 (dolist (kid (js2-block-node-kids ast)) |
|
3397 (js2-visit-ast kid callback))) |
|
3398 |
|
3399 (defun js2-print-block (n i) |
|
3400 (let ((pad (js2-make-pad i))) |
|
3401 (insert pad "{\n") |
|
3402 (dolist (kid (js2-block-node-kids n)) |
|
3403 (js2-print-ast kid (1+ i))) |
|
3404 (insert pad "}"))) |
|
3405 |
|
3406 (defstruct (js2-scope |
|
3407 (:include js2-block-node) |
|
3408 (:constructor nil) |
|
3409 (:constructor make-js2-scope (&key (type js2-BLOCK) |
|
3410 (pos js2-token-beg) |
|
3411 len |
|
3412 kids))) |
|
3413 ;; The symbol-table is a LinkedHashMap<String,Symbol> in Rhino. |
|
3414 ;; I don't have one of those handy, so I'll use an alist for now. |
|
3415 ;; It's as fast as an emacs hashtable for up to about 50 elements, |
|
3416 ;; and is much lighter-weight to construct (both CPU and mem). |
|
3417 ;; The keys are interned strings (symbols) for faster lookup. |
|
3418 ;; Should switch to hybrid alist/hashtable eventually. |
|
3419 symbol-table ; an alist of (symbol . js2-symbol) |
|
3420 parent-scope ; a `js2-scope' |
|
3421 top) ; top-level `js2-scope' (script/function) |
|
3422 |
|
3423 (put 'cl-struct-js2-scope 'js2-visitor 'js2-visit-none) |
|
3424 (put 'cl-struct-js2-scope 'js2-printer 'js2-print-none) |
|
3425 |
|
3426 (defun js2-scope-set-parent-scope (scope parent) |
|
3427 (setf (js2-scope-parent-scope scope) parent |
|
3428 (js2-scope-top scope) (if (null parent) |
|
3429 scope |
|
3430 (js2-scope-top parent)))) |
|
3431 |
|
3432 (defun js2-node-get-enclosing-scope (node) |
|
3433 "Return the innermost `js2-scope' node surrounding NODE. |
|
3434 Returns nil if there is no enclosing scope node." |
|
3435 (let ((parent (js2-node-parent node))) |
|
3436 (while (not (js2-scope-p parent)) |
|
3437 (setq parent (js2-node-parent parent))) |
|
3438 parent)) |
|
3439 |
|
3440 (defun js2-get-defining-scope (scope name) |
|
3441 "Search up scope chain from SCOPE looking for NAME, a string or symbol. |
|
3442 Returns `js2-scope' in which NAME is defined, or nil if not found." |
|
3443 (let ((sym (if (symbolp name) |
|
3444 name |
|
3445 (intern name))) |
|
3446 table |
|
3447 result |
|
3448 (continue t)) |
|
3449 (while (and scope continue) |
|
3450 (if (and (setq table (js2-scope-symbol-table scope)) |
|
3451 (assq sym table)) |
|
3452 (setq continue nil |
|
3453 result scope) |
|
3454 (setq scope (js2-scope-parent-scope scope)))) |
|
3455 result)) |
|
3456 |
|
3457 (defsubst js2-scope-get-symbol (scope name) |
|
3458 "Return symbol table entry for NAME in SCOPE. |
|
3459 NAME can be a string or symbol. Returns a `js2-symbol' or nil if not found." |
|
3460 (and (js2-scope-symbol-table scope) |
|
3461 (cdr (assq (if (symbolp name) |
|
3462 name |
|
3463 (intern name)) |
|
3464 (js2-scope-symbol-table scope))))) |
|
3465 |
|
3466 (defsubst js2-scope-put-symbol (scope name symbol) |
|
3467 "Enter SYMBOL into symbol-table for SCOPE under NAME. |
|
3468 NAME can be a lisp symbol or string. SYMBOL is a `js2-symbol'." |
|
3469 (let* ((table (js2-scope-symbol-table scope)) |
|
3470 (sym (if (symbolp name) name (intern name))) |
|
3471 (entry (assq sym table))) |
|
3472 (if entry |
|
3473 (setcdr entry symbol) |
|
3474 (push (cons sym symbol) |
|
3475 (js2-scope-symbol-table scope))))) |
|
3476 |
|
3477 (defstruct (js2-symbol |
|
3478 (:constructor nil) |
|
3479 (:constructor make-js2-symbol (decl-type name &optional ast-node))) |
|
3480 "A symbol table entry." |
|
3481 ;; One of js2-FUNCTION, js2-LP (for parameters), js2-VAR, |
|
3482 ;; js2-LET, or js2-CONST |
|
3483 decl-type |
|
3484 name ; string |
|
3485 ast-node) ; a `js2-node' |
|
3486 |
|
3487 (defstruct (js2-error-node |
|
3488 (:include js2-node) |
|
3489 (:constructor nil) ; silence emacs21 byte-compiler |
|
3490 (:constructor make-js2-error-node (&key (type js2-ERROR) |
|
3491 (pos js2-token-beg) |
|
3492 len))) |
|
3493 "AST node representing a parse error.") |
|
3494 |
|
3495 (put 'cl-struct-js2-error-node 'js2-visitor 'js2-visit-none) |
|
3496 (put 'cl-struct-js2-error-node 'js2-printer 'js2-print-none) |
|
3497 |
|
3498 (defstruct (js2-script-node |
|
3499 (:include js2-scope) |
|
3500 (:constructor nil) |
|
3501 (:constructor make-js2-script-node (&key (type js2-SCRIPT) |
|
3502 (pos js2-token-beg) |
|
3503 len |
|
3504 var-decls |
|
3505 fun-decls))) |
|
3506 functions ; lisp list of nested functions |
|
3507 regexps ; lisp list of (string . flags) |
|
3508 symbols ; alist (every symbol gets unique index) |
|
3509 (param-count 0) |
|
3510 var-names ; vector of string names |
|
3511 consts ; bool-vector matching var-decls |
|
3512 (temp-number 0)) ; for generating temp variables |
|
3513 |
|
3514 (put 'cl-struct-js2-script-node 'js2-visitor 'js2-visit-block) |
|
3515 (put 'cl-struct-js2-script-node 'js2-printer 'js2-print-script) |
|
3516 |
|
3517 (defun js2-print-script (node indent) |
|
3518 (dolist (kid (js2-block-node-kids node)) |
|
3519 (js2-print-ast kid indent))) |
|
3520 |
|
3521 (defstruct (js2-ast-root |
|
3522 (:include js2-script-node) |
|
3523 (:constructor nil) |
|
3524 (:constructor make-js2-ast-root (&key (type js2-SCRIPT) |
|
3525 (pos js2-token-beg) |
|
3526 len |
|
3527 buffer))) |
|
3528 "The root node of a js2 AST." |
|
3529 buffer ; the source buffer from which the code was parsed |
|
3530 comments ; a lisp list of comments, ordered by start position |
|
3531 errors ; a lisp list of errors found during parsing |
|
3532 warnings ; a lisp list of warnings found during parsing |
|
3533 node-count) ; number of nodes in the tree, including the root |
|
3534 |
|
3535 (put 'cl-struct-js2-ast-root 'js2-visitor 'js2-visit-ast-root) |
|
3536 (put 'cl-struct-js2-ast-root 'js2-printer 'js2-print-script) |
|
3537 |
|
3538 (defun js2-visit-ast-root (ast callback) |
|
3539 (dolist (kid (js2-ast-root-kids ast)) |
|
3540 (js2-visit-ast kid callback)) |
|
3541 (dolist (comment (js2-ast-root-comments ast)) |
|
3542 (js2-visit-ast comment callback))) |
|
3543 |
|
3544 (defstruct (js2-comment-node |
|
3545 (:include js2-node) |
|
3546 (:constructor nil) |
|
3547 (:constructor make-js2-comment-node (&key (type js2-COMMENT) |
|
3548 (pos js2-token-beg) |
|
3549 len |
|
3550 (format js2-ts-comment-type)))) |
|
3551 format) ; 'line, 'block, 'jsdoc or 'html |
|
3552 |
|
3553 (put 'cl-struct-js2-comment-node 'js2-visitor 'js2-visit-none) |
|
3554 (put 'cl-struct-js2-comment-node 'js2-printer 'js2-print-comment) |
|
3555 |
|
3556 (defun js2-print-comment (n i) |
|
3557 ;; We really ought to link end-of-line comments to their nodes. |
|
3558 ;; Or maybe we could add a new comment type, 'endline. |
|
3559 (insert (js2-make-pad i) |
|
3560 (js2-node-string n))) |
|
3561 |
|
3562 (defstruct (js2-expr-stmt-node |
|
3563 (:include js2-node) |
|
3564 (:constructor nil) |
|
3565 (:constructor make-js2-expr-stmt-node (&key (type js2-EXPR_VOID) |
|
3566 (pos js2-ts-cursor) |
|
3567 len |
|
3568 expr))) |
|
3569 "An expression statement." |
|
3570 expr) |
|
3571 |
|
3572 (defsubst js2-expr-stmt-node-set-has-result (node) |
|
3573 "Change the node type to `js2-EXPR_RESULT'. Used for code generation." |
|
3574 (setf (js2-node-type node) js2-EXPR_RESULT)) |
|
3575 |
|
3576 (put 'cl-struct-js2-expr-stmt-node 'js2-visitor 'js2-visit-expr-stmt-node) |
|
3577 (put 'cl-struct-js2-expr-stmt-node 'js2-printer 'js2-print-expr-stmt-node) |
|
3578 |
|
3579 (defun js2-visit-expr-stmt-node (n v) |
|
3580 (js2-visit-ast (js2-expr-stmt-node-expr n) v)) |
|
3581 |
|
3582 (defun js2-print-expr-stmt-node (n indent) |
|
3583 (js2-print-ast (js2-expr-stmt-node-expr n) indent) |
|
3584 (insert ";\n")) |
|
3585 |
|
3586 (defstruct (js2-loop-node |
|
3587 (:include js2-scope) |
|
3588 (:constructor nil)) |
|
3589 "Abstract supertype of loop nodes." |
|
3590 body ; a `js2-block-node' |
|
3591 lp ; position of left-paren, nil if omitted |
|
3592 rp) ; position of right-paren, nil if omitted |
|
3593 |
|
3594 (defstruct (js2-do-node |
|
3595 (:include js2-loop-node) |
|
3596 (:constructor nil) |
|
3597 (:constructor make-js2-do-node (&key (type js2-DO) |
|
3598 (pos js2-token-beg) |
|
3599 len |
|
3600 body |
|
3601 condition |
|
3602 while-pos |
|
3603 lp |
|
3604 rp))) |
|
3605 "AST node for do-loop." |
|
3606 condition ; while (expression) |
|
3607 while-pos) ; buffer position of 'while' keyword |
|
3608 |
|
3609 (put 'cl-struct-js2-do-node 'js2-visitor 'js2-visit-do-node) |
|
3610 (put 'cl-struct-js2-do-node 'js2-printer 'js2-print-do-node) |
|
3611 |
|
3612 (defun js2-visit-do-node (n v) |
|
3613 (js2-visit-ast (js2-do-node-body n) v) |
|
3614 (js2-visit-ast (js2-do-node-condition n) v)) |
|
3615 |
|
3616 (defun js2-print-do-node (n i) |
|
3617 (let ((pad (js2-make-pad i))) |
|
3618 (insert pad "do {\n") |
|
3619 (dolist (kid (js2-block-node-kids (js2-do-node-body n))) |
|
3620 (js2-print-ast kid (1+ i))) |
|
3621 (insert pad "} while (") |
|
3622 (js2-print-ast (js2-do-node-condition n) 0) |
|
3623 (insert ");\n"))) |
|
3624 |
|
3625 (defstruct (js2-while-node |
|
3626 (:include js2-loop-node) |
|
3627 (:constructor nil) |
|
3628 (:constructor make-js2-while-node (&key (type js2-WHILE) |
|
3629 (pos js2-token-beg) |
|
3630 len |
|
3631 body |
|
3632 condition |
|
3633 lp |
|
3634 rp))) |
|
3635 "AST node for while-loop." |
|
3636 condition) ; while-condition |
|
3637 |
|
3638 (put 'cl-struct-js2-while-node 'js2-visitor 'js2-visit-while-node) |
|
3639 (put 'cl-struct-js2-while-node 'js2-printer 'js2-print-while-node) |
|
3640 |
|
3641 (defun js2-visit-while-node (n v) |
|
3642 (js2-visit-ast (js2-while-node-condition n) v) |
|
3643 (js2-visit-ast (js2-while-node-body n) v)) |
|
3644 |
|
3645 (defun js2-print-while-node (n i) |
|
3646 (let ((pad (js2-make-pad i))) |
|
3647 (insert pad "while (") |
|
3648 (js2-print-ast (js2-while-node-condition n) 0) |
|
3649 (insert ") {\n") |
|
3650 (js2-print-body (js2-while-node-body n) (1+ i)) |
|
3651 (insert pad "}\n"))) |
|
3652 |
|
3653 (defstruct (js2-for-node |
|
3654 (:include js2-loop-node) |
|
3655 (:constructor nil) |
|
3656 (:constructor make-js2-for-node (&key (type js2-FOR) |
|
3657 (pos js2-ts-cursor) |
|
3658 len |
|
3659 body |
|
3660 init |
|
3661 condition |
|
3662 update |
|
3663 lp |
|
3664 rp))) |
|
3665 "AST node for a C-style for-loop." |
|
3666 init ; initialization expression |
|
3667 condition ; loop condition |
|
3668 update) ; update clause |
|
3669 |
|
3670 (put 'cl-struct-js2-for-node 'js2-visitor 'js2-visit-for-node) |
|
3671 (put 'cl-struct-js2-for-node 'js2-printer 'js2-print-for-node) |
|
3672 |
|
3673 (defun js2-visit-for-node (n v) |
|
3674 (js2-visit-ast (js2-for-node-init n) v) |
|
3675 (js2-visit-ast (js2-for-node-condition n) v) |
|
3676 (js2-visit-ast (js2-for-node-update n) v) |
|
3677 (js2-visit-ast (js2-for-node-body n) v)) |
|
3678 |
|
3679 (defun js2-print-for-node (n i) |
|
3680 (let ((pad (js2-make-pad i))) |
|
3681 (insert pad "for (") |
|
3682 (js2-print-ast (js2-for-node-init n) 0) |
|
3683 (insert "; ") |
|
3684 (js2-print-ast (js2-for-node-condition n) 0) |
|
3685 (insert "; ") |
|
3686 (js2-print-ast (js2-for-node-update n) 0) |
|
3687 (insert ") {\n") |
|
3688 (js2-print-body (js2-for-node-body n) (1+ i)) |
|
3689 (insert pad "}\n"))) |
|
3690 |
|
3691 (defstruct (js2-for-in-node |
|
3692 (:include js2-loop-node) |
|
3693 (:constructor nil) |
|
3694 (:constructor make-js2-for-in-node (&key (type js2-FOR) |
|
3695 (pos js2-ts-cursor) |
|
3696 len |
|
3697 body |
|
3698 iterator |
|
3699 object |
|
3700 in-pos |
|
3701 each-pos |
|
3702 foreach-p |
|
3703 lp |
|
3704 rp))) |
|
3705 "AST node for a for..in loop." |
|
3706 iterator ; [var] foo in ... |
|
3707 object ; object over which we're iterating |
|
3708 in-pos ; buffer position of 'in' keyword |
|
3709 each-pos ; buffer position of 'each' keyword, if foreach-p |
|
3710 foreach-p) ; t if it's a for-each loop |
|
3711 |
|
3712 (put 'cl-struct-js2-for-in-node 'js2-visitor 'js2-visit-for-in-node) |
|
3713 (put 'cl-struct-js2-for-in-node 'js2-printer 'js2-print-for-in-node) |
|
3714 |
|
3715 (defun js2-visit-for-in-node (n v) |
|
3716 (js2-visit-ast (js2-for-in-node-iterator n) v) |
|
3717 (js2-visit-ast (js2-for-in-node-object n) v) |
|
3718 (js2-visit-ast (js2-for-in-node-body n) v)) |
|
3719 |
|
3720 (defun js2-print-for-in-node (n i) |
|
3721 (let ((pad (js2-make-pad i)) |
|
3722 (foreach (js2-for-in-node-foreach-p n))) |
|
3723 (insert pad "for ") |
|
3724 (if foreach |
|
3725 (insert "each ")) |
|
3726 (insert "(") |
|
3727 (js2-print-ast (js2-for-in-node-iterator n) 0) |
|
3728 (insert " in ") |
|
3729 (js2-print-ast (js2-for-in-node-object n) 0) |
|
3730 (insert ") {\n") |
|
3731 (js2-print-body (js2-for-in-node-body n) (1+ i)) |
|
3732 (insert pad "}\n"))) |
|
3733 |
|
3734 (defstruct (js2-return-node |
|
3735 (:include js2-node) |
|
3736 (:constructor nil) |
|
3737 (:constructor make-js2-return-node (&key (type js2-RETURN) |
|
3738 (pos js2-ts-cursor) |
|
3739 len |
|
3740 retval))) |
|
3741 "AST node for a return statement." |
|
3742 retval) ; expression to return, or 'undefined |
|
3743 |
|
3744 (put 'cl-struct-js2-return-node 'js2-visitor 'js2-visit-return-node) |
|
3745 (put 'cl-struct-js2-return-node 'js2-printer 'js2-print-return-node) |
|
3746 |
|
3747 (defun js2-visit-return-node (n v) |
|
3748 (if (js2-return-node-retval n) |
|
3749 (js2-visit-ast (js2-return-node-retval n) v))) |
|
3750 |
|
3751 (defun js2-print-return-node (n i) |
|
3752 (insert (js2-make-pad i) "return") |
|
3753 (when (js2-return-node-retval n) |
|
3754 (insert " ") |
|
3755 (js2-print-ast (js2-return-node-retval n) 0)) |
|
3756 (insert ";\n")) |
|
3757 |
|
3758 (defstruct (js2-if-node |
|
3759 (:include js2-node) |
|
3760 (:constructor nil) |
|
3761 (:constructor make-js2-if-node (&key (type js2-IF) |
|
3762 (pos js2-ts-cursor) |
|
3763 len |
|
3764 condition |
|
3765 then-part |
|
3766 else-pos |
|
3767 else-part |
|
3768 lp |
|
3769 rp))) |
|
3770 "AST node for an if-statement." |
|
3771 condition ; expression |
|
3772 then-part ; statement or block |
|
3773 else-pos ; optional buffer position of 'else' keyword |
|
3774 else-part ; optional statement or block |
|
3775 lp ; position of left-paren, nil if omitted |
|
3776 rp) ; position of right-paren, nil if omitted |
|
3777 |
|
3778 (put 'cl-struct-js2-if-node 'js2-visitor 'js2-visit-if-node) |
|
3779 (put 'cl-struct-js2-if-node 'js2-printer 'js2-print-if-node) |
|
3780 |
|
3781 (defun js2-visit-if-node (n v) |
|
3782 (js2-visit-ast (js2-if-node-condition n) v) |
|
3783 (js2-visit-ast (js2-if-node-then-part n) v) |
|
3784 (if (js2-if-node-else-part n) |
|
3785 (js2-visit-ast (js2-if-node-else-part n) v))) |
|
3786 |
|
3787 (defun js2-print-if-node (n i) |
|
3788 (let ((pad (js2-make-pad i)) |
|
3789 (then-part (js2-if-node-then-part n)) |
|
3790 (else-part (js2-if-node-else-part n))) |
|
3791 (insert pad "if (") |
|
3792 (js2-print-ast (js2-if-node-condition n) 0) |
|
3793 (insert ") {\n") |
|
3794 (js2-print-body then-part (1+ i)) |
|
3795 (insert pad "}") |
|
3796 (cond |
|
3797 ((not else-part) |
|
3798 (insert "\n")) |
|
3799 ((js2-if-node-p else-part) |
|
3800 (insert " else ") |
|
3801 (js2-print-body else-part i)) |
|
3802 (t |
|
3803 (insert " else {\n") |
|
3804 (js2-print-body else-part (1+ i)) |
|
3805 (insert pad "}\n"))))) |
|
3806 |
|
3807 (defstruct (js2-try-node |
|
3808 (:include js2-node) |
|
3809 (:constructor nil) |
|
3810 (:constructor make-js2-try-node (&key (type js2-TRY) |
|
3811 (pos js2-ts-cursor) |
|
3812 len |
|
3813 try-block |
|
3814 catch-clauses |
|
3815 finally-block))) |
|
3816 "AST node for a try-statement." |
|
3817 try-block |
|
3818 catch-clauses ; a lisp list of `js2-catch-node' |
|
3819 finally-block) ; a `js2-finally-node' |
|
3820 |
|
3821 (put 'cl-struct-js2-try-node 'js2-visitor 'js2-visit-try-node) |
|
3822 (put 'cl-struct-js2-try-node 'js2-printer 'js2-print-try-node) |
|
3823 |
|
3824 (defun js2-visit-try-node (n v) |
|
3825 (js2-visit-ast (js2-try-node-try-block n) v) |
|
3826 (dolist (clause (js2-try-node-catch-clauses n)) |
|
3827 (js2-visit-ast clause v)) |
|
3828 (if (js2-try-node-finally-block n) |
|
3829 (js2-visit-ast (js2-try-node-finally-block n) v))) |
|
3830 |
|
3831 (defun js2-print-try-node (n i) |
|
3832 (let ((pad (js2-make-pad i)) |
|
3833 (catches (js2-try-node-catch-clauses n)) |
|
3834 (finally (js2-try-node-finally-block n))) |
|
3835 (insert pad "try {\n") |
|
3836 (js2-print-body (js2-try-node-try-block n) (1+ i)) |
|
3837 (insert pad "}") |
|
3838 (when catches |
|
3839 (dolist (catch catches) |
|
3840 (js2-print-ast catch i))) |
|
3841 (if finally |
|
3842 (js2-print-ast finally i) |
|
3843 (insert "\n")))) |
|
3844 |
|
3845 (defstruct (js2-catch-node |
|
3846 (:include js2-node) |
|
3847 (:constructor nil) |
|
3848 (:constructor make-js2-catch-node (&key (type js2-CATCH) |
|
3849 (pos js2-ts-cursor) |
|
3850 len |
|
3851 var-name |
|
3852 guard-kwd |
|
3853 guard-expr |
|
3854 block |
|
3855 lp |
|
3856 rp))) |
|
3857 "AST node for a catch clause." |
|
3858 var-name ; a `js2-name-node' |
|
3859 guard-kwd ; relative buffer position of "if" in "catch (x if ...)" |
|
3860 guard-expr ; catch condition, a `js2-node' |
|
3861 block ; statements, a `js2-block-node' |
|
3862 lp ; buffer position of left-paren, nil if omitted |
|
3863 rp) ; buffer position of right-paren, nil if omitted |
|
3864 |
|
3865 (put 'cl-struct-js2-catch-node 'js2-visitor 'js2-visit-catch-node) |
|
3866 (put 'cl-struct-js2-catch-node 'js2-printer 'js2-print-catch-node) |
|
3867 |
|
3868 (defun js2-visit-catch-node (n v) |
|
3869 (js2-visit-ast (js2-catch-node-var-name n) v) |
|
3870 (when (js2-catch-node-guard-kwd n) |
|
3871 (js2-visit-ast (js2-catch-node-guard-expr n) v)) |
|
3872 (js2-visit-ast (js2-catch-node-block n) v)) |
|
3873 |
|
3874 (defun js2-print-catch-node (n i) |
|
3875 (let ((pad (js2-make-pad i)) |
|
3876 (guard-kwd (js2-catch-node-guard-kwd n)) |
|
3877 (guard-expr (js2-catch-node-guard-expr n))) |
|
3878 (insert " catch (") |
|
3879 (js2-print-ast (js2-catch-node-var-name n) 0) |
|
3880 (when guard-kwd |
|
3881 (insert " if ") |
|
3882 (js2-print-ast guard-expr 0)) |
|
3883 (insert ") {\n") |
|
3884 (js2-print-body (js2-catch-node-block n) (1+ i)) |
|
3885 (insert pad "}"))) |
|
3886 |
|
3887 (defstruct (js2-finally-node |
|
3888 (:include js2-node) |
|
3889 (:constructor nil) |
|
3890 (:constructor make-js2-finally-node (&key (type js2-FINALLY) |
|
3891 (pos js2-ts-cursor) |
|
3892 len |
|
3893 body))) |
|
3894 "AST node for a finally clause." |
|
3895 body) ; a `js2-node', often but not always a block node |
|
3896 |
|
3897 (put 'cl-struct-js2-finally-node 'js2-visitor 'js2-visit-finally-node) |
|
3898 (put 'cl-struct-js2-finally-node 'js2-printer 'js2-print-finally-node) |
|
3899 |
|
3900 (defun js2-visit-finally-node (n v) |
|
3901 (js2-visit-ast (js2-finally-node-body n) v)) |
|
3902 |
|
3903 (defun js2-print-finally-node (n i) |
|
3904 (let ((pad (js2-make-pad i))) |
|
3905 (insert " finally {\n") |
|
3906 (js2-print-body (js2-finally-node-body n) (1+ i)) |
|
3907 (insert pad "}\n"))) |
|
3908 |
|
3909 (defstruct (js2-switch-node |
|
3910 (:include js2-node) |
|
3911 (:constructor nil) |
|
3912 (:constructor make-js2-switch-node (&key (type js2-SWITCH) |
|
3913 (pos js2-ts-cursor) |
|
3914 len |
|
3915 discriminant |
|
3916 cases |
|
3917 lp |
|
3918 rp))) |
|
3919 "AST node for a switch statement." |
|
3920 discriminant ; a `js2-node' (switch expression) |
|
3921 cases ; a lisp list of `js2-case-node' |
|
3922 lp ; position of open-paren for discriminant, nil if omitted |
|
3923 rp) ; position of close-paren for discriminant, nil if omitted |
|
3924 |
|
3925 (put 'cl-struct-js2-switch-node 'js2-visitor 'js2-visit-switch-node) |
|
3926 (put 'cl-struct-js2-switch-node 'js2-printer 'js2-print-switch-node) |
|
3927 |
|
3928 (defun js2-visit-switch-node (n v) |
|
3929 (js2-visit-ast (js2-switch-node-discriminant n) v) |
|
3930 (dolist (c (js2-switch-node-cases n)) |
|
3931 (js2-visit-ast c v))) |
|
3932 |
|
3933 (defun js2-print-switch-node (n i) |
|
3934 (let ((pad (js2-make-pad i)) |
|
3935 (cases (js2-switch-node-cases n))) |
|
3936 (insert pad "switch (") |
|
3937 (js2-print-ast (js2-switch-node-discriminant n) 0) |
|
3938 (insert ") {\n") |
|
3939 (dolist (case cases) |
|
3940 (js2-print-ast case i)) |
|
3941 (insert pad "}\n"))) |
|
3942 |
|
3943 (defstruct (js2-case-node |
|
3944 (:include js2-block-node) |
|
3945 (:constructor nil) |
|
3946 (:constructor make-js2-case-node (&key (type js2-CASE) |
|
3947 (pos js2-ts-cursor) |
|
3948 len |
|
3949 kids |
|
3950 expr))) |
|
3951 "AST node for a case clause of a switch statement." |
|
3952 expr) ; the case expression (nil for default) |
|
3953 |
|
3954 (put 'cl-struct-js2-case-node 'js2-visitor 'js2-visit-case-node) |
|
3955 (put 'cl-struct-js2-case-node 'js2-printer 'js2-print-case-node) |
|
3956 |
|
3957 (defun js2-visit-case-node (n v) |
|
3958 (if (js2-case-node-expr n) ; nil for default: case |
|
3959 (js2-visit-ast (js2-case-node-expr n) v)) |
|
3960 (js2-visit-block n v)) |
|
3961 |
|
3962 (defun js2-print-case-node (n i) |
|
3963 (let ((pad (js2-make-pad i)) |
|
3964 (expr (js2-case-node-expr n))) |
|
3965 (insert pad) |
|
3966 (if (null expr) |
|
3967 (insert "default:\n") |
|
3968 (insert "case ") |
|
3969 (js2-print-ast expr 0) |
|
3970 (insert ":\n")) |
|
3971 (dolist (kid (js2-case-node-kids n)) |
|
3972 (js2-print-ast kid (1+ i))))) |
|
3973 |
|
3974 (defstruct (js2-throw-node |
|
3975 (:include js2-node) |
|
3976 (:constructor nil) |
|
3977 (:constructor make-js2-throw-node (&key (type js2-THROW) |
|
3978 (pos js2-ts-cursor) |
|
3979 len |
|
3980 expr))) |
|
3981 "AST node for a throw statement." |
|
3982 expr) ; the expression to throw |
|
3983 |
|
3984 (put 'cl-struct-js2-throw-node 'js2-visitor 'js2-visit-throw-node) |
|
3985 (put 'cl-struct-js2-throw-node 'js2-printer 'js2-print-throw-node) |
|
3986 |
|
3987 (defun js2-visit-throw-node (n v) |
|
3988 (js2-visit-ast (js2-throw-node-expr n) v)) |
|
3989 |
|
3990 (defun js2-print-throw-node (n i) |
|
3991 (insert (js2-make-pad i) "throw ") |
|
3992 (js2-print-ast (js2-throw-node-expr n) 0) |
|
3993 (insert ";\n")) |
|
3994 |
|
3995 (defstruct (js2-with-node |
|
3996 (:include js2-node) |
|
3997 (:constructor nil) |
|
3998 (:constructor make-js2-with-node (&key (type js2-WITH) |
|
3999 (pos js2-ts-cursor) |
|
4000 len |
|
4001 object |
|
4002 body |
|
4003 lp |
|
4004 rp))) |
|
4005 "AST node for a with-statement." |
|
4006 object |
|
4007 body |
|
4008 lp ; buffer position of left-paren around object, nil if omitted |
|
4009 rp) ; buffer position of right-paren around object, nil if omitted |
|
4010 |
|
4011 (put 'cl-struct-js2-with-node 'js2-visitor 'js2-visit-with-node) |
|
4012 (put 'cl-struct-js2-with-node 'js2-printer 'js2-print-with-node) |
|
4013 |
|
4014 (defun js2-visit-with-node (n v) |
|
4015 (js2-visit-ast (js2-with-node-object n) v) |
|
4016 (js2-visit-ast (js2-with-node-body n) v)) |
|
4017 |
|
4018 (defun js2-print-with-node (n i) |
|
4019 (let ((pad (js2-make-pad i))) |
|
4020 (insert pad "with (") |
|
4021 (js2-print-ast (js2-with-node-object n) 0) |
|
4022 (insert ") {\n") |
|
4023 (js2-print-body (js2-with-node-body n) (1+ i)) |
|
4024 (insert pad "}\n"))) |
|
4025 |
|
4026 (defstruct (js2-label-node |
|
4027 (:include js2-node) |
|
4028 (:constructor nil) |
|
4029 (:constructor make-js2-label-node (&key (type js2-LABEL) |
|
4030 (pos js2-ts-cursor) |
|
4031 len |
|
4032 name))) |
|
4033 "AST node for a statement label or case label." |
|
4034 name ; a string |
|
4035 loop) ; for validating and code-generating continue-to-label |
|
4036 |
|
4037 (put 'cl-struct-js2-label-node 'js2-visitor 'js2-visit-none) |
|
4038 (put 'cl-struct-js2-label-node 'js2-printer 'js2-print-label) |
|
4039 |
|
4040 (defun js2-print-label (n i) |
|
4041 (insert (js2-make-pad i) |
|
4042 (js2-label-node-name n) |
|
4043 ":\n")) |
|
4044 |
|
4045 (defstruct (js2-labeled-stmt-node |
|
4046 (:include js2-node) |
|
4047 (:constructor nil) |
|
4048 ;; type needs to be in `js2-side-effecting-tokens' to avoid spurious |
|
4049 ;; no-side-effects warnings, hence js2-EXPR_RESULT. |
|
4050 (:constructor make-js2-labeled-stmt-node (&key (type js2-EXPR_RESULT) |
|
4051 (pos js2-ts-cursor) |
|
4052 len |
|
4053 labels |
|
4054 stmt))) |
|
4055 "AST node for a statement with one or more labels. |
|
4056 Multiple labels for a statement are collapsed into the labels field." |
|
4057 labels ; lisp list of `js2-label-node' |
|
4058 stmt) ; the statement these labels are for |
|
4059 |
|
4060 (put 'cl-struct-js2-labeled-stmt-node 'js2-visitor 'js2-visit-labeled-stmt) |
|
4061 (put 'cl-struct-js2-labeled-stmt-node 'js2-printer 'js2-print-labeled-stmt) |
|
4062 |
|
4063 (defun js2-get-label-by-name (lbl-stmt name) |
|
4064 "Return a `js2-label-node' by NAME from LBL-STMT's labels list. |
|
4065 Returns nil if no such label is in the list." |
|
4066 (let ((label-list (js2-labeled-stmt-node-labels lbl-stmt)) |
|
4067 result) |
|
4068 (while (and label-list (not result)) |
|
4069 (if (string= (js2-label-node-name (car label-list)) name) |
|
4070 (setq result (car label-list)) |
|
4071 (setq label-list (cdr label-list)))) |
|
4072 result)) |
|
4073 |
|
4074 (defun js2-visit-labeled-stmt (n v) |
|
4075 (dolist (label (js2-labeled-stmt-node-labels n)) |
|
4076 (js2-visit-ast label v)) |
|
4077 (js2-visit-ast (js2-labeled-stmt-node-stmt n) v)) |
|
4078 |
|
4079 (defun js2-print-labeled-stmt (n i) |
|
4080 (dolist (label (js2-labeled-stmt-node-labels n)) |
|
4081 (js2-print-ast label i)) |
|
4082 (js2-print-ast (js2-labeled-stmt-node-stmt n) (1+ i))) |
|
4083 |
|
4084 (defun js2-labeled-stmt-node-contains (node label) |
|
4085 "Return t if NODE contains LABEL in its label set. |
|
4086 NODE is a `js2-labels-node'. LABEL is an identifier." |
|
4087 (loop for nl in (js2-labeled-stmt-node-labels node) |
|
4088 if (string= label (js2-label-node-name nl)) |
|
4089 return t |
|
4090 finally return nil)) |
|
4091 |
|
4092 (defsubst js2-labeled-stmt-node-add-label (node label) |
|
4093 "Add a `js2-label-node' to the label set for this statement." |
|
4094 (setf (js2-labeled-stmt-node-labels node) |
|
4095 (nconc (js2-labeled-stmt-node-labels node) (list label)))) |
|
4096 |
|
4097 (defstruct (js2-jump-node |
|
4098 (:include js2-node) |
|
4099 (:constructor nil)) |
|
4100 "Abstract supertype of break and continue nodes." |
|
4101 label ; `js2-name-node' for location of label identifier, if present |
|
4102 target) ; target js2-labels-node or loop/switch statement |
|
4103 |
|
4104 (defun js2-visit-jump-node (n v) |
|
4105 ;; we don't visit the target, since it's a back-link |
|
4106 (if (js2-jump-node-label n) |
|
4107 (js2-visit-ast (js2-jump-node-label n) v))) |
|
4108 |
|
4109 (defstruct (js2-break-node |
|
4110 (:include js2-jump-node) |
|
4111 (:constructor nil) |
|
4112 (:constructor make-js2-break-node (&key (type js2-BREAK) |
|
4113 (pos js2-ts-cursor) |
|
4114 len |
|
4115 label |
|
4116 target))) |
|
4117 "AST node for a break statement. |
|
4118 The label field is a `js2-name-node', possibly nil, for the named label |
|
4119 if provided. E.g. in 'break foo', it represents 'foo'. The target field |
|
4120 is the target of the break - a label node or enclosing loop/switch statement.") |
|
4121 |
|
4122 (put 'cl-struct-js2-break-node 'js2-visitor 'js2-visit-jump-node) |
|
4123 (put 'cl-struct-js2-break-node 'js2-printer 'js2-print-break-node) |
|
4124 |
|
4125 (defun js2-print-break-node (n i) |
|
4126 (insert (js2-make-pad i) "break") |
|
4127 (when (js2-break-node-label n) |
|
4128 (insert " ") |
|
4129 (js2-print-ast (js2-break-node-label n) 0)) |
|
4130 (insert ";\n")) |
|
4131 |
|
4132 (defstruct (js2-continue-node |
|
4133 (:include js2-jump-node) |
|
4134 (:constructor nil) |
|
4135 (:constructor make-js2-continue-node (&key (type js2-CONTINUE) |
|
4136 (pos js2-ts-cursor) |
|
4137 len |
|
4138 label |
|
4139 target))) |
|
4140 "AST node for a continue statement. |
|
4141 The label field is the user-supplied enclosing label name, a `js2-name-node'. |
|
4142 It is nil if continue specifies no label. The target field is the jump target: |
|
4143 a `js2-label-node' or the innermost enclosing loop.") |
|
4144 |
|
4145 (put 'cl-struct-js2-continue-node 'js2-visitor 'js2-visit-jump-node) |
|
4146 (put 'cl-struct-js2-continue-node 'js2-printer 'js2-print-continue-node) |
|
4147 |
|
4148 (defun js2-print-continue-node (n i) |
|
4149 (insert (js2-make-pad i) "continue") |
|
4150 (when (js2-continue-node-label n) |
|
4151 (insert " ") |
|
4152 (js2-print-ast (js2-continue-node-label n) 0)) |
|
4153 (insert ";\n")) |
|
4154 |
|
4155 (defstruct (js2-function-node |
|
4156 (:include js2-script-node) |
|
4157 (:constructor nil) |
|
4158 (:constructor make-js2-function-node (&key (type js2-FUNCTION) |
|
4159 (pos js2-ts-cursor) |
|
4160 len |
|
4161 (ftype 'FUNCTION) |
|
4162 (form 'FUNCTION_STATEMENT) |
|
4163 (name "") |
|
4164 params |
|
4165 body |
|
4166 lp |
|
4167 rp))) |
|
4168 "AST node for a function declaration. |
|
4169 The `params' field is a lisp list of nodes. Each node is either a simple |
|
4170 `js2-name-node', or if it's a destructuring-assignment parameter, a |
|
4171 `js2-array-node' or `js2-object-node'." |
|
4172 ftype ; FUNCTION, GETTER or SETTER |
|
4173 form ; FUNCTION_{STATEMENT|EXPRESSION|EXPRESSION_STATEMENT} |
|
4174 name ; function name (a `js2-name-node', or nil if anonymous) |
|
4175 params ; a lisp list of destructuring forms or simple name nodes |
|
4176 body ; a `js2-block-node' |
|
4177 lp ; position of arg-list open-paren, or nil if omitted |
|
4178 rp ; position of arg-list close-paren, or nil if omitted |
|
4179 ignore-dynamic ; ignore value of the dynamic-scope flag (interpreter only) |
|
4180 needs-activation ; t if we need an activation object for this frame |
|
4181 is-generator ; t if this function contains a yield |
|
4182 member-expr) ; nonstandard Ecma extension from Rhino |
|
4183 |
|
4184 (put 'cl-struct-js2-function-node 'js2-visitor 'js2-visit-function-node) |
|
4185 (put 'cl-struct-js2-function-node 'js2-printer 'js2-print-function-node) |
|
4186 |
|
4187 (defun js2-visit-function-node (n v) |
|
4188 (if (js2-function-node-name n) |
|
4189 (js2-visit-ast (js2-function-node-name n) v)) |
|
4190 (dolist (p (js2-function-node-params n)) |
|
4191 (js2-visit-ast p v)) |
|
4192 (js2-visit-ast (js2-function-node-body n) v)) |
|
4193 |
|
4194 (defun js2-print-function-node (n i) |
|
4195 (let ((pad (js2-make-pad i)) |
|
4196 (getter (js2-node-get-prop n 'GETTER_SETTER)) |
|
4197 (name (js2-function-node-name n)) |
|
4198 (params (js2-function-node-params n)) |
|
4199 (body (js2-function-node-body n)) |
|
4200 (expr (eq (js2-function-node-form n) 'FUNCTION_EXPRESSION))) |
|
4201 (unless getter |
|
4202 (insert pad "function")) |
|
4203 (when name |
|
4204 (insert " ") |
|
4205 (js2-print-ast name 0)) |
|
4206 (insert "(") |
|
4207 (loop with len = (length params) |
|
4208 for param in params |
|
4209 for count from 1 |
|
4210 do |
|
4211 (js2-print-ast param 0) |
|
4212 (if (< count len) |
|
4213 (insert ", "))) |
|
4214 (insert ") {") |
|
4215 (unless expr |
|
4216 (insert "\n")) |
|
4217 ;; TODO: fix this to be smarter about indenting, etc. |
|
4218 (js2-print-body body (1+ i)) |
|
4219 (insert pad "}") |
|
4220 (unless expr |
|
4221 (insert "\n")))) |
|
4222 |
|
4223 (defsubst js2-function-name (node) |
|
4224 "Return function name for NODE, a `js2-function-node', or nil if anonymous." |
|
4225 (and (js2-function-node-name node) |
|
4226 (js2-name-node-name (js2-function-node-name node)))) |
|
4227 |
|
4228 ;; Having this be an expression node makes it more flexible. |
|
4229 ;; There are IDE contexts, such as indentation in a for-loop initializer, |
|
4230 ;; that work better if you assume it's an expression. Whenever we have |
|
4231 ;; a standalone var/const declaration, we just wrap with an expr stmt. |
|
4232 ;; Eclipse apparently screwed this up and now has two versions, expr and stmt. |
|
4233 (defstruct (js2-var-decl-node |
|
4234 (:include js2-node) |
|
4235 (:constructor nil) |
|
4236 (:constructor make-js2-var-decl-node (&key (type js2-VAR) |
|
4237 (pos js2-token-beg) |
|
4238 len |
|
4239 kids |
|
4240 decl-type))) |
|
4241 "AST node for a variable declaration list (VAR, CONST or LET). |
|
4242 The node bounds differ depending on the declaration type. For VAR or |
|
4243 CONST declarations, the bounds include the var/const keyword. For LET |
|
4244 declarations, the node begins at the position of the first child." |
|
4245 kids ; a lisp list of `js2-var-init-node' structs. |
|
4246 decl-type) ; js2-VAR, js2-CONST or js2-LET |
|
4247 |
|
4248 (put 'cl-struct-js2-var-decl-node 'js2-visitor 'js2-visit-var-decl) |
|
4249 (put 'cl-struct-js2-var-decl-node 'js2-printer 'js2-print-var-decl) |
|
4250 |
|
4251 (defun js2-visit-var-decl (n v) |
|
4252 (dolist (kid (js2-var-decl-node-kids n)) |
|
4253 (js2-visit-ast kid v))) |
|
4254 |
|
4255 (defun js2-print-var-decl (n i) |
|
4256 (let ((pad (js2-make-pad i)) |
|
4257 (tt (js2-var-decl-node-decl-type n))) |
|
4258 (insert pad) |
|
4259 (insert (cond |
|
4260 ((= tt js2-VAR) "var ") |
|
4261 ((= tt js2-LET) "") ; handled by parent let-{expr/stmt} |
|
4262 ((= tt js2-CONST) "const ") |
|
4263 (t |
|
4264 (error "malformed var-decl node")))) |
|
4265 (loop with kids = (js2-var-decl-node-kids n) |
|
4266 with len = (length kids) |
|
4267 for kid in kids |
|
4268 for count from 1 |
|
4269 do |
|
4270 (js2-print-ast kid 0) |
|
4271 (if (< count len) |
|
4272 (insert ", "))))) |
|
4273 |
|
4274 (defstruct (js2-var-init-node |
|
4275 (:include js2-node) |
|
4276 (:constructor nil) |
|
4277 (:constructor make-js2-var-init-node (&key (type js2-VAR) |
|
4278 (pos js2-ts-cursor) |
|
4279 len |
|
4280 target |
|
4281 initializer))) |
|
4282 "AST node for a variable declaration. |
|
4283 The type field will be js2-CONST for a const decl." |
|
4284 target ; `js2-name-node', `js2-object-node', or `js2-array-node' |
|
4285 initializer) ; initializer expression, a `js2-node' |
|
4286 |
|
4287 (put 'cl-struct-js2-var-init-node 'js2-visitor 'js2-visit-var-init-node) |
|
4288 (put 'cl-struct-js2-var-init-node 'js2-printer 'js2-print-var-init-node) |
|
4289 |
|
4290 (defun js2-visit-var-init-node (n v) |
|
4291 (js2-visit-ast (js2-var-init-node-target n) v) |
|
4292 (if (js2-var-init-node-initializer n) |
|
4293 (js2-visit-ast (js2-var-init-node-initializer n) v))) |
|
4294 |
|
4295 (defun js2-print-var-init-node (n i) |
|
4296 (let ((pad (js2-make-pad i)) |
|
4297 (name (js2-var-init-node-target n)) |
|
4298 (init (js2-var-init-node-initializer n))) |
|
4299 (insert pad) |
|
4300 (js2-print-ast name 0) |
|
4301 (when init |
|
4302 (insert " = ") |
|
4303 (js2-print-ast init 0)))) |
|
4304 |
|
4305 (defstruct (js2-cond-node |
|
4306 (:include js2-node) |
|
4307 (:constructor nil) |
|
4308 (:constructor make-js2-cond-node (&key (type js2-HOOK) |
|
4309 (pos js2-ts-cursor) |
|
4310 len |
|
4311 test-expr |
|
4312 true-expr |
|
4313 false-expr |
|
4314 q-pos |
|
4315 c-pos))) |
|
4316 "AST node for the ternary operator" |
|
4317 test-expr |
|
4318 true-expr |
|
4319 false-expr |
|
4320 q-pos ; buffer position of ? |
|
4321 c-pos) ; buffer position of : |
|
4322 |
|
4323 (put 'cl-struct-js2-cond-node 'js2-visitor 'js2-visit-cond-node) |
|
4324 (put 'cl-struct-js2-cond-node 'js2-printer 'js2-print-cond-node) |
|
4325 |
|
4326 (defun js2-visit-cond-node (n v) |
|
4327 (js2-visit-ast (js2-cond-node-test-expr n) v) |
|
4328 (js2-visit-ast (js2-cond-node-true-expr n) v) |
|
4329 (js2-visit-ast (js2-cond-node-false-expr n) v)) |
|
4330 |
|
4331 (defun js2-print-cond-node (n i) |
|
4332 (let ((pad (js2-make-pad i))) |
|
4333 (insert pad) |
|
4334 (js2-print-ast (js2-cond-node-test-expr n) 0) |
|
4335 (insert " ? ") |
|
4336 (js2-print-ast (js2-cond-node-true-expr n) 0) |
|
4337 (insert " : ") |
|
4338 (js2-print-ast (js2-cond-node-false-expr n) 0))) |
|
4339 |
|
4340 (defstruct (js2-infix-node |
|
4341 (:include js2-node) |
|
4342 (:constructor nil) |
|
4343 (:constructor make-js2-infix-node (&key type |
|
4344 (pos js2-ts-cursor) |
|
4345 len |
|
4346 op-pos |
|
4347 left |
|
4348 right))) |
|
4349 "Represents infix expressions. |
|
4350 Includes assignment ops like `|=', and the comma operator. |
|
4351 The type field inherited from `js2-node' holds the operator." |
|
4352 op-pos ; buffer position where operator begins |
|
4353 left ; any `js2-node' |
|
4354 right) ; any `js2-node' |
|
4355 |
|
4356 (put 'cl-struct-js2-infix-node 'js2-visitor 'js2-visit-infix-node) |
|
4357 (put 'cl-struct-js2-infix-node 'js2-printer 'js2-print-infix-node) |
|
4358 |
|
4359 (defun js2-visit-infix-node (n v) |
|
4360 (when (js2-infix-node-left n) |
|
4361 (js2-visit-ast (js2-infix-node-left n) v)) |
|
4362 (when (js2-infix-node-right n) |
|
4363 (js2-visit-ast (js2-infix-node-right n) v))) |
|
4364 |
|
4365 (defconst js2-operator-tokens |
|
4366 (let ((table (make-hash-table :test 'eq)) |
|
4367 (tokens |
|
4368 (list (cons js2-IN "in") |
|
4369 (cons js2-TYPEOF "typeof") |
|
4370 (cons js2-INSTANCEOF "instanceof") |
|
4371 (cons js2-DELPROP "delete") |
|
4372 (cons js2-COMMA ",") |
|
4373 (cons js2-COLON ":") |
|
4374 (cons js2-OR "||") |
|
4375 (cons js2-AND "&&") |
|
4376 (cons js2-INC "++") |
|
4377 (cons js2-DEC "--") |
|
4378 (cons js2-BITOR "|") |
|
4379 (cons js2-BITXOR "^") |
|
4380 (cons js2-BITAND "&") |
|
4381 (cons js2-EQ "==") |
|
4382 (cons js2-NE "!=") |
|
4383 (cons js2-LT "<") |
|
4384 (cons js2-LE "<=") |
|
4385 (cons js2-GT ">") |
|
4386 (cons js2-GE ">=") |
|
4387 (cons js2-LSH "<<") |
|
4388 (cons js2-RSH ">>") |
|
4389 (cons js2-URSH ">>>") |
|
4390 (cons js2-ADD "+") ; infix plus |
|
4391 (cons js2-SUB "-") ; infix minus |
|
4392 (cons js2-MUL "*") |
|
4393 (cons js2-DIV "/") |
|
4394 (cons js2-MOD "%") |
|
4395 (cons js2-NOT "!") |
|
4396 (cons js2-BITNOT "~") |
|
4397 (cons js2-POS "+") ; unary plus |
|
4398 (cons js2-NEG "-") ; unary minus |
|
4399 (cons js2-SHEQ "===") ; shallow equality |
|
4400 (cons js2-SHNE "!==") ; shallow inequality |
|
4401 (cons js2-ASSIGN "=") |
|
4402 (cons js2-ASSIGN_BITOR "|=") |
|
4403 (cons js2-ASSIGN_BITXOR "^=") |
|
4404 (cons js2-ASSIGN_BITAND "&=") |
|
4405 (cons js2-ASSIGN_LSH "<<=") |
|
4406 (cons js2-ASSIGN_RSH ">>=") |
|
4407 (cons js2-ASSIGN_URSH ">>>=") |
|
4408 (cons js2-ASSIGN_ADD "+=") |
|
4409 (cons js2-ASSIGN_SUB "-=") |
|
4410 (cons js2-ASSIGN_MUL "*=") |
|
4411 (cons js2-ASSIGN_DIV "/=") |
|
4412 (cons js2-ASSIGN_MOD "%=")))) |
|
4413 (loop for (k . v) in tokens do |
|
4414 (puthash k v table)) |
|
4415 table)) |
|
4416 |
|
4417 (defun js2-print-infix-node (n i) |
|
4418 (let* ((tt (js2-node-type n)) |
|
4419 (op (gethash tt js2-operator-tokens))) |
|
4420 (unless op |
|
4421 (error "unrecognized infix operator %s" (js2-node-type n))) |
|
4422 (insert (js2-make-pad i)) |
|
4423 (js2-print-ast (js2-infix-node-left n) 0) |
|
4424 (unless (= tt js2-COMMA) |
|
4425 (insert " ")) |
|
4426 (insert op) |
|
4427 (insert " ") |
|
4428 (js2-print-ast (js2-infix-node-right n) 0))) |
|
4429 |
|
4430 (defstruct (js2-assign-node |
|
4431 (:include js2-infix-node) |
|
4432 (:constructor nil) |
|
4433 (:constructor make-js2-assign-node (&key type |
|
4434 (pos js2-ts-cursor) |
|
4435 len |
|
4436 op-pos |
|
4437 left |
|
4438 right))) |
|
4439 "Represents any assignment. |
|
4440 The type field holds the actual assignment operator.") |
|
4441 |
|
4442 (put 'cl-struct-js2-assign-node 'js2-visitor 'js2-visit-infix-node) |
|
4443 (put 'cl-struct-js2-assign-node 'js2-printer 'js2-print-infix-node) |
|
4444 |
|
4445 (defstruct (js2-unary-node |
|
4446 (:include js2-node) |
|
4447 (:constructor nil) |
|
4448 (:constructor make-js2-unary-node (&key type ; required |
|
4449 (pos js2-ts-cursor) |
|
4450 len |
|
4451 operand))) |
|
4452 "AST node type for unary operator nodes. |
|
4453 The type field can be NOT, BITNOT, POS, NEG, INC, DEC, |
|
4454 TYPEOF, or DELPROP. For INC or DEC, a 'postfix node |
|
4455 property is added if the operator follows the operand." |
|
4456 operand) ; a `js2-node' expression |
|
4457 |
|
4458 (put 'cl-struct-js2-unary-node 'js2-visitor 'js2-visit-unary-node) |
|
4459 (put 'cl-struct-js2-unary-node 'js2-printer 'js2-print-unary-node) |
|
4460 |
|
4461 (defun js2-visit-unary-node (n v) |
|
4462 (js2-visit-ast (js2-unary-node-operand n) v)) |
|
4463 |
|
4464 (defun js2-print-unary-node (n i) |
|
4465 (let* ((tt (js2-node-type n)) |
|
4466 (op (gethash tt js2-operator-tokens)) |
|
4467 (postfix (js2-node-get-prop n 'postfix))) |
|
4468 (unless op |
|
4469 (error "unrecognized unary operator %s" tt)) |
|
4470 (insert (js2-make-pad i)) |
|
4471 (unless postfix |
|
4472 (insert op)) |
|
4473 (if (or (= tt js2-TYPEOF) |
|
4474 (= tt js2-DELPROP)) |
|
4475 (insert " ")) |
|
4476 (js2-print-ast (js2-unary-node-operand n) 0) |
|
4477 (when postfix |
|
4478 (insert op)))) |
|
4479 |
|
4480 (defstruct (js2-let-node |
|
4481 (:include js2-scope) |
|
4482 (:constructor nil) |
|
4483 (:constructor make-js2-let-node (&key (type js2-LETEXPR) |
|
4484 (pos js2-token-beg) |
|
4485 len |
|
4486 vars |
|
4487 body |
|
4488 lp |
|
4489 rp))) |
|
4490 "AST node for a let expression or a let statement. |
|
4491 Note that a let declaration such as let x=6, y=7 is a `js2-var-decl-node'." |
|
4492 vars ; a `js2-var-decl-node' |
|
4493 body ; a `js2-node' representing the expression or body block |
|
4494 lp |
|
4495 rp) |
|
4496 |
|
4497 (put 'cl-struct-js2-let-node 'js2-visitor 'js2-visit-let-node) |
|
4498 (put 'cl-struct-js2-let-node 'js2-printer 'js2-print-let-node) |
|
4499 |
|
4500 (defun js2-visit-let-node (n v) |
|
4501 (when (js2-let-node-vars n) |
|
4502 (js2-visit-ast (js2-let-node-vars n) v)) |
|
4503 (when (js2-let-node-body n) |
|
4504 (js2-visit-ast (js2-let-node-body n) v))) |
|
4505 |
|
4506 (defun js2-print-let-node (n i) |
|
4507 (insert (js2-make-pad i) "let (") |
|
4508 (js2-print-ast (js2-let-node-vars n) 0) |
|
4509 (insert ") ") |
|
4510 (js2-print-ast (js2-let-node-body n) i)) |
|
4511 |
|
4512 (defstruct (js2-keyword-node |
|
4513 (:include js2-node) |
|
4514 (:constructor nil) |
|
4515 (:constructor make-js2-keyword-node (&key type |
|
4516 (pos js2-token-beg) |
|
4517 (len (- js2-ts-cursor pos))))) |
|
4518 "AST node representing a literal keyword such as `null'. |
|
4519 Used for `null', `this', `true', `false' and `debugger'. |
|
4520 The node type is set to js2-NULL, js2-THIS, etc.") |
|
4521 |
|
4522 (put 'cl-struct-js2-keyword-node 'js2-visitor 'js2-visit-none) |
|
4523 (put 'cl-struct-js2-keyword-node 'js2-printer 'js2-print-keyword-node) |
|
4524 |
|
4525 (defun js2-print-keyword-node (n i) |
|
4526 (insert (js2-make-pad i) |
|
4527 (let ((tt (js2-node-type n))) |
|
4528 (cond |
|
4529 ((= tt 'js2-THIS) "this") |
|
4530 ((= tt 'js2-NULL) "null") |
|
4531 ((= tt 'js2-TRUE) "true") |
|
4532 ((= tt 'js2-FALSE) "false") |
|
4533 ((= tt 'js2-DEBUGGER) "debugger") |
|
4534 (t (error "Invalid keyword literal type: %d" tt)))))) |
|
4535 |
|
4536 (defsubst js2-this-node-p (node) |
|
4537 "Return t if this node is a `js2-literal-node' of type js2-THIS." |
|
4538 (eq (js2-node-type node) js2-THIS)) |
|
4539 |
|
4540 (defstruct (js2-new-node |
|
4541 (:include js2-node) |
|
4542 (:constructor nil) |
|
4543 (:constructor make-js2-new-node (&key (type js2-NEW) |
|
4544 (pos js2-token-beg) |
|
4545 len |
|
4546 target |
|
4547 args |
|
4548 initializer |
|
4549 lp |
|
4550 rp))) |
|
4551 "AST node for new-expression such as new Foo()." |
|
4552 target ; an identifier or reference |
|
4553 args ; a lisp list of argument nodes |
|
4554 lp ; position of left-paren, nil if omitted |
|
4555 rp ; position of right-paren, nil if omitted |
|
4556 initializer) ; experimental Rhino syntax: optional `js2-object-node' |
|
4557 |
|
4558 (put 'cl-struct-js2-new-node 'js2-visitor 'js2-visit-new-node) |
|
4559 (put 'cl-struct-js2-new-node 'js2-printer 'js2-print-new-node) |
|
4560 |
|
4561 (defun js2-visit-new-node (n v) |
|
4562 (js2-visit-ast (js2-new-node-target n) v) |
|
4563 (dolist (arg (js2-new-node-args n)) |
|
4564 (js2-visit-ast arg v)) |
|
4565 (when (js2-new-node-initializer n) |
|
4566 (js2-visit-ast (js2-new-node-initializer n) v))) |
|
4567 |
|
4568 (defun js2-print-new-node (n i) |
|
4569 (insert (js2-make-pad i) "new ") |
|
4570 (js2-print-ast (js2-new-node-target n)) |
|
4571 (insert "(") |
|
4572 (js2-print-list (js2-new-node-args n)) |
|
4573 (insert ")") |
|
4574 (when (js2-new-node-initializer n) |
|
4575 (insert " ") |
|
4576 (js2-print-ast (js2-new-node-initializer n)))) |
|
4577 |
|
4578 (defstruct (js2-name-node |
|
4579 (:include js2-node) |
|
4580 (:constructor nil) |
|
4581 (:constructor make-js2-name-node (&key (type js2-NAME) |
|
4582 (pos js2-token-beg) |
|
4583 (len (- js2-ts-cursor |
|
4584 js2-token-beg)) |
|
4585 (name js2-ts-string)))) |
|
4586 "AST node for a JavaScript identifier" |
|
4587 name ; a string |
|
4588 scope) ; a `js2-scope' (optional, used for codegen) |
|
4589 |
|
4590 (put 'cl-struct-js2-name-node 'js2-visitor 'js2-visit-none) |
|
4591 (put 'cl-struct-js2-name-node 'js2-printer 'js2-print-name-node) |
|
4592 |
|
4593 (defun js2-print-name-node (n i) |
|
4594 (insert (js2-make-pad i) |
|
4595 (js2-name-node-name n))) |
|
4596 |
|
4597 (defsubst js2-name-node-length (node) |
|
4598 "Return identifier length of NODE, a `js2-name-node'. |
|
4599 Returns 0 if NODE is nil or its identifier field is nil." |
|
4600 (if node |
|
4601 (length (js2-name-node-name node)) |
|
4602 0)) |
|
4603 |
|
4604 (defstruct (js2-number-node |
|
4605 (:include js2-node) |
|
4606 (:constructor nil) |
|
4607 (:constructor make-js2-number-node (&key (type js2-NUMBER) |
|
4608 (pos js2-token-beg) |
|
4609 (len (- js2-ts-cursor |
|
4610 js2-token-beg)) |
|
4611 (value js2-ts-string) |
|
4612 (num-value js2-ts-number)))) |
|
4613 "AST node for a number literal." |
|
4614 value ; the original string, e.g. "6.02e23" |
|
4615 num-value) ; the parsed number value |
|
4616 |
|
4617 (put 'cl-struct-js2-number-node 'js2-visitor 'js2-visit-none) |
|
4618 (put 'cl-struct-js2-number-node 'js2-printer 'js2-print-number-node) |
|
4619 |
|
4620 (defun js2-print-number-node (n i) |
|
4621 (insert (js2-make-pad i) |
|
4622 (number-to-string (js2-number-node-value n)))) |
|
4623 |
|
4624 (defstruct (js2-regexp-node |
|
4625 (:include js2-node) |
|
4626 (:constructor nil) |
|
4627 (:constructor make-js2-regexp-node (&key (type js2-REGEXP) |
|
4628 (pos js2-token-beg) |
|
4629 (len (- js2-ts-cursor |
|
4630 js2-token-beg)) |
|
4631 value |
|
4632 flags))) |
|
4633 "AST node for a regular expression literal." |
|
4634 value ; the regexp string, without // delimiters |
|
4635 flags) ; a string of flags, e.g. `mi'. |
|
4636 |
|
4637 (put 'cl-struct-js2-regexp-node 'js2-visitor 'js2-visit-none) |
|
4638 (put 'cl-struct-js2-regexp-node 'js2-printer 'js2-print-regexp) |
|
4639 |
|
4640 (defun js2-print-regexp (n i) |
|
4641 (insert (js2-make-pad i) |
|
4642 "/" |
|
4643 (js2-regexp-node-value n) |
|
4644 "/") |
|
4645 (if (js2-regexp-node-flags n) |
|
4646 (insert (js2-regexp-node-flags n)))) |
|
4647 |
|
4648 (defstruct (js2-string-node |
|
4649 (:include js2-node) |
|
4650 (:constructor nil) |
|
4651 (:constructor make-js2-string-node (&key (type js2-STRING) |
|
4652 (pos js2-token-beg) |
|
4653 (len (- js2-ts-cursor |
|
4654 js2-token-beg)) |
|
4655 (value js2-ts-string)))) |
|
4656 "String literal. |
|
4657 Escape characters are not evaluated; e.g. \n is 2 chars in value field. |
|
4658 You can tell the quote type by looking at the first character." |
|
4659 value) ; the characters of the string, including the quotes |
|
4660 |
|
4661 (put 'cl-struct-js2-string-node 'js2-visitor 'js2-visit-none) |
|
4662 (put 'cl-struct-js2-string-node 'js2-printer 'js2-print-string-node) |
|
4663 |
|
4664 (defun js2-print-string-node (n i) |
|
4665 (insert (js2-make-pad i) |
|
4666 (js2-node-string n))) |
|
4667 |
|
4668 (defstruct (js2-array-node |
|
4669 (:include js2-node) |
|
4670 (:constructor nil) |
|
4671 (:constructor make-js2-array-node (&key (type js2-ARRAYLIT) |
|
4672 (pos js2-ts-cursor) |
|
4673 len |
|
4674 elems))) |
|
4675 "AST node for an array literal." |
|
4676 elems) ; list of expressions. [foo,,bar] yields a nil middle element. |
|
4677 |
|
4678 (put 'cl-struct-js2-array-node 'js2-visitor 'js2-visit-array-node) |
|
4679 (put 'cl-struct-js2-array-node 'js2-printer 'js2-print-array-node) |
|
4680 |
|
4681 (defun js2-visit-array-node (n v) |
|
4682 (dolist (e (js2-array-node-elems n)) |
|
4683 (when e ; can be nil, e.g. [a, ,b] |
|
4684 (js2-visit-ast e v)))) |
|
4685 |
|
4686 (defun js2-print-array-node (n i) |
|
4687 (insert (js2-make-pad i) "[") |
|
4688 (js2-print-list (js2-array-node-elems n)) |
|
4689 (insert "]")) |
|
4690 |
|
4691 (defstruct (js2-object-node |
|
4692 (:include js2-node) |
|
4693 (:constructor nil) |
|
4694 (:constructor make-js2-object-node (&key (type js2-OBJECTLIT) |
|
4695 (pos js2-ts-cursor) |
|
4696 len |
|
4697 elems))) |
|
4698 "AST node for an object literal expression." |
|
4699 elems) ; a lisp list of `js2-object-prop-node' |
|
4700 |
|
4701 (put 'cl-struct-js2-object-node 'js2-visitor 'js2-visit-object-node) |
|
4702 (put 'cl-struct-js2-object-node 'js2-printer 'js2-print-object-node) |
|
4703 |
|
4704 (defun js2-visit-object-node (n v) |
|
4705 (dolist (e (js2-object-node-elems n)) |
|
4706 (js2-visit-ast e v))) |
|
4707 |
|
4708 (defun js2-print-object-node (n i) |
|
4709 (insert (js2-make-pad i) "{") |
|
4710 (js2-print-list (js2-object-node-elems n)) |
|
4711 (insert "}")) |
|
4712 |
|
4713 (defstruct (js2-object-prop-node |
|
4714 (:include js2-infix-node) |
|
4715 (:constructor nil) |
|
4716 (:constructor make-js2-object-prop-node (&key (type js2-COLON) |
|
4717 (pos js2-ts-cursor) |
|
4718 len |
|
4719 left |
|
4720 right |
|
4721 op-pos))) |
|
4722 "AST node for an object literal prop:value entry. |
|
4723 The `left' field is the property: a name node, string node or number node. |
|
4724 The `right' field is a `js2-node' representing the initializer value.") |
|
4725 |
|
4726 (put 'cl-struct-js2-object-prop-node 'js2-visitor 'js2-visit-infix-node) |
|
4727 (put 'cl-struct-js2-object-prop-node 'js2-printer 'js2-print-object-prop-node) |
|
4728 |
|
4729 (defun js2-print-object-prop-node (n i) |
|
4730 (insert (js2-make-pad i)) |
|
4731 (js2-print-ast (js2-object-prop-node-left n) 0) |
|
4732 (insert ":") |
|
4733 (js2-print-ast (js2-object-prop-node-right n) 0)) |
|
4734 |
|
4735 (defstruct (js2-getter-setter-node |
|
4736 (:include js2-infix-node) |
|
4737 (:constructor nil) |
|
4738 (:constructor make-js2-getter-setter-node (&key type ; GET or SET |
|
4739 (pos js2-ts-cursor) |
|
4740 len |
|
4741 left |
|
4742 right))) |
|
4743 "AST node for a getter/setter property in an object literal. |
|
4744 The `left' field is the `js2-name-node' naming the getter/setter prop. |
|
4745 The `right' field is always an anonymous `js2-function-node' with a node |
|
4746 property `GETTER_SETTER' set to js2-GET or js2-SET. ") |
|
4747 |
|
4748 (put 'cl-struct-js2-getter-setter-node 'js2-visitor 'js2-visit-infix-node) |
|
4749 (put 'cl-struct-js2-getter-setter-node 'js2-printer 'js2-print-getter-setter) |
|
4750 |
|
4751 (defun js2-print-getter-setter (n i) |
|
4752 (let ((pad (js2-make-pad i)) |
|
4753 (left (js2-getter-setter-node-left n)) |
|
4754 (right (js2-getter-setter-node-right n))) |
|
4755 (insert pad) |
|
4756 (insert (if (= (js2-node-type n) js2-GET) "get " "set ")) |
|
4757 (js2-print-ast left 0) |
|
4758 (js2-print-ast right 0))) |
|
4759 |
|
4760 (defstruct (js2-prop-get-node |
|
4761 (:include js2-infix-node) |
|
4762 (:constructor nil) |
|
4763 (:constructor make-js2-prop-get-node (&key (type js2-GETPROP) |
|
4764 (pos js2-ts-cursor) |
|
4765 len |
|
4766 left |
|
4767 right))) |
|
4768 "AST node for a dotted property reference, e.g. foo.bar or foo().bar") |
|
4769 |
|
4770 (put 'cl-struct-js2-prop-get-node 'js2-visitor 'js2-visit-prop-get-node) |
|
4771 (put 'cl-struct-js2-prop-get-node 'js2-printer 'js2-print-prop-get-node) |
|
4772 |
|
4773 (defun js2-visit-prop-get-node (n v) |
|
4774 (when (js2-prop-get-node-left n) |
|
4775 (js2-visit-ast (js2-prop-get-node-left n) v)) |
|
4776 (when (js2-prop-get-node-right n) |
|
4777 (js2-visit-ast (js2-prop-get-node-right n) v))) |
|
4778 |
|
4779 (defun js2-print-prop-get-node (n i) |
|
4780 (insert (js2-make-pad i)) |
|
4781 (js2-print-ast (js2-prop-get-node-left n) 0) |
|
4782 (insert ".") |
|
4783 (js2-print-ast (js2-prop-get-node-right n) 0)) |
|
4784 |
|
4785 (defstruct (js2-elem-get-node |
|
4786 (:include js2-node) |
|
4787 (:constructor nil) |
|
4788 (:constructor make-js2-elem-get-node (&key (type js2-GETELEM) |
|
4789 (pos js2-ts-cursor) |
|
4790 len |
|
4791 target |
|
4792 element |
|
4793 lb |
|
4794 rb))) |
|
4795 "AST node for an array index expression such as foo[bar]." |
|
4796 target ; a `js2-node' - the expression preceding the "." |
|
4797 element ; a `js2-node' - the expression in brackets |
|
4798 lb ; position of left-bracket, nil if omitted |
|
4799 rb) ; position of right-bracket, nil if omitted |
|
4800 |
|
4801 (put 'cl-struct-js2-elem-get-node 'js2-visitor 'js2-visit-elem-get-node) |
|
4802 (put 'cl-struct-js2-elem-get-node 'js2-printer 'js2-print-elem-get-node) |
|
4803 |
|
4804 (defun js2-visit-elem-get-node (n v) |
|
4805 (when (js2-elem-get-node-target n) |
|
4806 (js2-visit-ast (js2-elem-get-node-target n) v)) |
|
4807 (when (js2-elem-get-node-element n) |
|
4808 (js2-visit-ast (js2-elem-get-node-element n) v))) |
|
4809 |
|
4810 (defun js2-print-elem-get-node (n i) |
|
4811 (insert (js2-make-pad i)) |
|
4812 (js2-print-ast (js2-elem-get-node-target n) 0) |
|
4813 (insert "[") |
|
4814 (js2-print-ast (js2-elem-get-node-element n) 0) |
|
4815 (insert "]")) |
|
4816 |
|
4817 (defstruct (js2-call-node |
|
4818 (:include js2-node) |
|
4819 (:constructor nil) |
|
4820 (:constructor make-js2-call-node (&key (type js2-CALL) |
|
4821 (pos js2-ts-cursor) |
|
4822 len |
|
4823 target |
|
4824 args |
|
4825 lp |
|
4826 rp))) |
|
4827 "AST node for a JavaScript function call." |
|
4828 target ; a `js2-node' evaluating to the function to call |
|
4829 args ; a lisp list of `js2-node' arguments |
|
4830 lp ; position of open-paren, or nil if missing |
|
4831 rp) ; position of close-paren, or nil if missing |
|
4832 |
|
4833 (put 'cl-struct-js2-call-node 'js2-visitor 'js2-visit-call-node) |
|
4834 (put 'cl-struct-js2-call-node 'js2-printer 'js2-print-call-node) |
|
4835 |
|
4836 (defun js2-visit-call-node (n v) |
|
4837 (js2-visit-ast (js2-call-node-target n) v) |
|
4838 (dolist (arg (js2-call-node-args n)) |
|
4839 (js2-visit-ast arg v))) |
|
4840 |
|
4841 (defun js2-print-call-node (n i) |
|
4842 (insert (js2-make-pad i)) |
|
4843 (js2-print-ast (js2-call-node-target n) 0) |
|
4844 (insert "(") |
|
4845 (js2-print-list (js2-call-node-args n)) |
|
4846 (insert ")")) |
|
4847 |
|
4848 (defstruct (js2-yield-node |
|
4849 (:include js2-node) |
|
4850 (:constructor nil) |
|
4851 (:constructor make-js2-yield-node (&key (type js2-YIELD) |
|
4852 (pos js2-ts-cursor) |
|
4853 len |
|
4854 value))) |
|
4855 "AST node for yield statement or expression." |
|
4856 value) ; optional: value to be yielded |
|
4857 |
|
4858 (put 'cl-struct-js2-yield-node 'js2-visitor 'js2-visit-yield-node) |
|
4859 (put 'cl-struct-js2-yield-node 'js2-printer 'js2-print-yield-node) |
|
4860 |
|
4861 (defun js2-visit-yield-node (n v) |
|
4862 (js2-visit-ast (js2-yield-node-value n) v)) |
|
4863 |
|
4864 (defun js2-print-yield-node (n i) |
|
4865 (insert (js2-make-pad i)) |
|
4866 (insert "yield") |
|
4867 (when (js2-yield-node-value n) |
|
4868 (insert " ") |
|
4869 (js2-print-ast (js2-yield-node-value n) 0))) |
|
4870 |
|
4871 (defstruct (js2-paren-node |
|
4872 (:include js2-node) |
|
4873 (:constructor nil) |
|
4874 (:constructor make-js2-paren-node (&key (type js2-LP) |
|
4875 (pos js2-ts-cursor) |
|
4876 len |
|
4877 expr))) |
|
4878 "AST node for a parenthesized expression. |
|
4879 In particular, used when the parens are syntactically optional, |
|
4880 as opposed to required parens such as those enclosing an if-conditional." |
|
4881 expr) ; `js2-node' |
|
4882 |
|
4883 (put 'cl-struct-js2-paren-node 'js2-visitor 'js2-visit-paren-node) |
|
4884 (put 'cl-struct-js2-paren-node 'js2-printer 'js2-print-paren-node) |
|
4885 |
|
4886 (defun js2-visit-paren-node (n v) |
|
4887 (js2-visit-ast (js2-paren-node-expr n) v)) |
|
4888 |
|
4889 (defun js2-print-paren-node (n i) |
|
4890 (insert (js2-make-pad i)) |
|
4891 (insert "(") |
|
4892 (js2-print-ast (js2-paren-node-expr n) 0) |
|
4893 (insert ")")) |
|
4894 |
|
4895 (defstruct (js2-array-comp-node |
|
4896 (:include js2-scope) |
|
4897 (:constructor nil) |
|
4898 (:constructor make-js2-array-comp-node (&key (type js2-ARRAYCOMP) |
|
4899 (pos js2-ts-cursor) |
|
4900 len |
|
4901 result |
|
4902 loops |
|
4903 filter |
|
4904 if-pos |
|
4905 lp |
|
4906 rp))) |
|
4907 "AST node for an Array comprehension such as [[x,y] for (x in foo) for (y in bar)]." |
|
4908 result ; result expression (just after left-bracket) |
|
4909 loops ; a lisp list of `js2-array-comp-loop-node' |
|
4910 filter ; guard/filter expression |
|
4911 if-pos ; buffer pos of 'if' keyword, if present, else nil |
|
4912 lp ; buffer position of if-guard left-paren, or nil if not present |
|
4913 rp) ; buffer position of if-guard right-paren, or nil if not present |
|
4914 |
|
4915 (put 'cl-struct-js2-array-comp-node 'js2-visitor 'js2-visit-array-comp-node) |
|
4916 (put 'cl-struct-js2-array-comp-node 'js2-printer 'js2-print-array-comp-node) |
|
4917 |
|
4918 (defun js2-visit-array-comp-node (n v) |
|
4919 (js2-visit-ast (js2-array-comp-node-result n) v) |
|
4920 (dolist (l (js2-array-comp-node-loops n)) |
|
4921 (js2-visit-ast l v)) |
|
4922 (if (js2-array-comp-node-filter n) |
|
4923 (js2-visit-ast (js2-array-comp-node-filter n) v))) |
|
4924 |
|
4925 (defun js2-print-array-comp-node (n i) |
|
4926 (let ((pad (js2-make-pad i)) |
|
4927 (result (js2-array-comp-node-result n)) |
|
4928 (loops (js2-array-comp-node-loops n)) |
|
4929 (filter (js2-array-comp-node-filter n))) |
|
4930 (insert pad "[") |
|
4931 (js2-print-ast result 0) |
|
4932 (dolist (l loops) |
|
4933 (insert " ") |
|
4934 (js2-print-ast l 0)) |
|
4935 (when filter |
|
4936 (insert " if (") |
|
4937 (js2-print-ast filter 0)) |
|
4938 (insert ")]"))) |
|
4939 |
|
4940 (defstruct (js2-array-comp-loop-node |
|
4941 (:include js2-for-in-node) |
|
4942 (:constructor nil) |
|
4943 (:constructor make-js2-array-comp-loop-node (&key (type js2-FOR) |
|
4944 (pos js2-ts-cursor) |
|
4945 len |
|
4946 iterator |
|
4947 object |
|
4948 in-pos |
|
4949 foreach-p |
|
4950 each-pos |
|
4951 lp |
|
4952 rp))) |
|
4953 "AST subtree for each 'for (foo in bar)' loop in an array comprehension.") |
|
4954 |
|
4955 (put 'cl-struct-js2-array-comp-loop-node 'js2-visitor 'js2-visit-array-comp-loop) |
|
4956 (put 'cl-struct-js2-array-comp-loop-node 'js2-printer 'js2-print-array-comp-loop) |
|
4957 |
|
4958 (defun js2-visit-array-comp-loop (n v) |
|
4959 (js2-visit-ast (js2-array-comp-loop-node-iterator n) v) |
|
4960 (js2-visit-ast (js2-array-comp-loop-node-object n) v)) |
|
4961 |
|
4962 (defun js2-print-array-comp-loop (n i) |
|
4963 (insert "for (") |
|
4964 (js2-print-ast (js2-array-comp-loop-node-iterator n) 0) |
|
4965 (insert " in ") |
|
4966 (js2-print-ast (js2-array-comp-loop-node-object n) 0) |
|
4967 (insert ")")) |
|
4968 |
|
4969 (defstruct (js2-empty-expr-node |
|
4970 (:include js2-node) |
|
4971 (:constructor nil) |
|
4972 (:constructor make-js2-empty-expr-node (&key (type js2-EMPTY) |
|
4973 (pos js2-token-beg) |
|
4974 len))) |
|
4975 "AST node for an empty expression.") |
|
4976 |
|
4977 (put 'cl-struct-js2-empty-expr-node 'js2-visitor 'js2-visit-none) |
|
4978 (put 'cl-struct-js2-empty-expr-node 'js2-printer 'js2-print-none) |
|
4979 |
|
4980 (defstruct (js2-xml-node |
|
4981 (:include js2-block-node) |
|
4982 (:constructor nil) |
|
4983 (:constructor make-js2-xml-node (&key (type js2-XML) |
|
4984 (pos js2-token-beg) |
|
4985 len |
|
4986 kids))) |
|
4987 "AST node for initial parse of E4X literals. |
|
4988 The kids field is a list of XML fragments, each a `js2-string-node' or |
|
4989 a `js2-xml-js-expr-node'. Equivalent to Rhino's XmlLiteral node.") |
|
4990 |
|
4991 (put 'cl-struct-js2-xml-node 'js2-visitor 'js2-visit-block) |
|
4992 (put 'cl-struct-js2-xml-node 'js2-printer 'js2-print-xml-node) |
|
4993 |
|
4994 (defun js2-print-xml-node (n i) |
|
4995 (dolist (kid (js2-xml-node-kids n)) |
|
4996 (js2-print-ast kid i))) |
|
4997 |
|
4998 (defstruct (js2-xml-js-expr-node |
|
4999 (:include js2-xml-node) |
|
5000 (:constructor nil) |
|
5001 (:constructor make-js2-xml-js-expr-node (&key (type js2-XML) |
|
5002 (pos js2-ts-cursor) |
|
5003 len |
|
5004 expr))) |
|
5005 "AST node for an embedded JavaScript {expression} in an E4X literal. |
|
5006 The start and end fields correspond to the curly-braces." |
|
5007 expr) ; a `js2-expr-node' of some sort |
|
5008 |
|
5009 (put 'cl-struct-js2-xml-js-expr-node 'js2-visitor 'js2-visit-xml-js-expr) |
|
5010 (put 'cl-struct-js2-xml-js-expr-node 'js2-printer 'js2-print-xml-js-expr) |
|
5011 |
|
5012 (defun js2-visit-xml-js-expr (n v) |
|
5013 (js2-visit-ast (js2-xml-js-expr-node-expr n) v)) |
|
5014 |
|
5015 (defun js2-print-xml-js-expr (n i) |
|
5016 (insert (js2-make-pad i)) |
|
5017 (insert "{") |
|
5018 (js2-print-ast (js2-xml-js-expr-node-expr n) 0) |
|
5019 (insert "}")) |
|
5020 |
|
5021 (defstruct (js2-xml-dot-query-node |
|
5022 (:include js2-infix-node) |
|
5023 (:constructor nil) |
|
5024 (:constructor make-js2-xml-dot-query-node (&key (type js2-DOTQUERY) |
|
5025 (pos js2-ts-cursor) |
|
5026 op-pos |
|
5027 len |
|
5028 left |
|
5029 right |
|
5030 rp))) |
|
5031 "AST node for an E4X foo.(bar) filter expression. |
|
5032 Note that the left-paren is automatically the character immediately |
|
5033 following the dot (.) in the operator. No whitespace is permitted |
|
5034 between the dot and the lp by the scanner." |
|
5035 rp) |
|
5036 |
|
5037 (put 'cl-struct-js2-xml-dot-query-node 'js2-visitor 'js2-visit-infix-node) |
|
5038 (put 'cl-struct-js2-xml-dot-query-node 'js2-printer 'js2-print-xml-dot-query) |
|
5039 |
|
5040 (defun js2-print-xml-dot-query (n i) |
|
5041 (insert (js2-make-pad i)) |
|
5042 (js2-print-ast (js2-xml-dot-query-node-left n) 0) |
|
5043 (insert ".(") |
|
5044 (js2-print-ast (js2-xml-dot-query-node-right n) 0) |
|
5045 (insert ")")) |
|
5046 |
|
5047 (defstruct (js2-xml-ref-node |
|
5048 (:include js2-node) |
|
5049 (:constructor nil)) ; abstract |
|
5050 "Base type for E4X XML attribute-access or property-get expressions. |
|
5051 Such expressions can take a variety of forms. The general syntax has |
|
5052 three parts: |
|
5053 |
|
5054 - (optional) an @ (specifying an attribute access) |
|
5055 - (optional) a namespace (a `js2-name-node') and double-colon |
|
5056 - (required) either a `js2-name-node' or a bracketed [expression] |
|
5057 |
|
5058 The property-name expressions (examples: ns::name, @name) are |
|
5059 represented as `js2-xml-prop-ref' nodes. The bracketed-expression |
|
5060 versions (examples: ns::[name], @[name]) become `js2-xml-elem-ref' nodes. |
|
5061 |
|
5062 This node type (or more specifically, its subclasses) will sometimes |
|
5063 be the right-hand child of a `js2-prop-get-node' or a |
|
5064 `js2-infix-node' of type `js2-DOTDOT', the .. xml-descendants operator. |
|
5065 The `js2-xml-ref-node' may also be a standalone primary expression with |
|
5066 no explicit target, which is valid in certain expression contexts such as |
|
5067 |
|
5068 company..employee.(@id < 100) |
|
5069 |
|
5070 in this case, the @id is a `js2-xml-ref' that is part of an infix '<' |
|
5071 expression whose parent is a `js2-xml-dot-query-node'." |
|
5072 namespace |
|
5073 at-pos |
|
5074 colon-pos) |
|
5075 |
|
5076 (defsubst js2-xml-ref-node-attr-access-p (node) |
|
5077 "Return non-nil if this expression began with an @-token." |
|
5078 (and (numberp (js2-xml-ref-node-at-pos node)) |
|
5079 (plusp (js2-xml-ref-node-at-pos node)))) |
|
5080 |
|
5081 (defstruct (js2-xml-prop-ref-node |
|
5082 (:include js2-xml-ref-node) |
|
5083 (:constructor nil) |
|
5084 (:constructor make-js2-xml-prop-ref-node (&key (type js2-REF_NAME) |
|
5085 (pos js2-token-beg) |
|
5086 len |
|
5087 propname |
|
5088 namespace |
|
5089 at-pos |
|
5090 colon-pos))) |
|
5091 "AST node for an E4X XML [expr] property-ref expression. |
|
5092 The JavaScript syntax is an optional @, an optional ns::, and a name. |
|
5093 |
|
5094 [ '@' ] [ name '::' ] name |
|
5095 |
|
5096 Examples include name, ns::name, ns::*, *::name, *::*, @attr, @ns::attr, |
|
5097 @ns::*, @*::attr, @*::*, and @*. |
|
5098 |
|
5099 The node starts at the @ token, if present. Otherwise it starts at the |
|
5100 namespace name. The node bounds extend through the closing right-bracket, |
|
5101 or if it is missing due to a syntax error, through the end of the index |
|
5102 expression." |
|
5103 propname) |
|
5104 |
|
5105 (put 'cl-struct-js2-xml-prop-ref-node 'js2-visitor 'js2-visit-xml-prop-ref-node) |
|
5106 (put 'cl-struct-js2-xml-prop-ref-node 'js2-printer 'js2-print-xml-prop-ref-node) |
|
5107 |
|
5108 (defun js2-visit-xml-prop-ref-node (n v) |
|
5109 (if (js2-xml-prop-ref-node-namespace n) |
|
5110 (js2-visit-ast (js2-xml-prop-ref-node-namespace n) v)) |
|
5111 (if (js2-xml-prop-ref-node-propname n) |
|
5112 (js2-visit-ast (js2-xml-prop-ref-node-propname n) v))) |
|
5113 |
|
5114 (defun js2-print-xml-prop-ref-node (n i) |
|
5115 (insert (js2-make-pad i)) |
|
5116 (if (js2-xml-ref-node-attr-access-p n) |
|
5117 (insert "@")) |
|
5118 (when (js2-xml-prop-ref-node-namespace n) |
|
5119 (js2-print-ast (js2-xml-prop-ref-node-namespace n) 0) |
|
5120 (insert "::")) |
|
5121 (if (js2-xml-prop-ref-node-propname n) |
|
5122 (js2-print-ast (js2-xml-prop-ref-node-propname n) 0))) |
|
5123 |
|
5124 (defstruct (js2-xml-elem-ref-node |
|
5125 (:include js2-xml-ref-node) |
|
5126 (:constructor nil) |
|
5127 (:constructor make-js2-xml-elem-ref-node (&key (type js2-REF_MEMBER) |
|
5128 (pos js2-token-beg) |
|
5129 len |
|
5130 expr |
|
5131 lb |
|
5132 rb |
|
5133 namespace |
|
5134 at-pos |
|
5135 colon-pos))) |
|
5136 "AST node for an E4X XML [expr] member-ref expression. |
|
5137 Syntax: |
|
5138 |
|
5139 [ '@' ] [ name '::' ] '[' expr ']' |
|
5140 |
|
5141 Examples include ns::[expr], @ns::[expr], @[expr], *::[expr] and @*::[expr]. |
|
5142 |
|
5143 Note that the form [expr] (i.e. no namespace or attribute-qualifier) |
|
5144 is not a legal E4X XML element-ref expression, since it's already used |
|
5145 for standard JavaScript element-get array indexing. Hence, a |
|
5146 `js2-xml-elem-ref-node' always has either the attribute-qualifier, a |
|
5147 non-nil namespace node, or both. |
|
5148 |
|
5149 The node starts at the @ token, if present. Otherwise it starts |
|
5150 at the namespace name. The node bounds extend through the closing |
|
5151 right-bracket, or if it is missing due to a syntax error, through the |
|
5152 end of the index expression." |
|
5153 expr ; the bracketed index expression |
|
5154 lb |
|
5155 rb) |
|
5156 |
|
5157 (put 'cl-struct-js2-xml-elem-ref-node 'js2-visitor 'js2-visit-xml-elem-ref-node) |
|
5158 (put 'cl-struct-js2-xml-elem-ref-node 'js2-printer 'js2-print-xml-elem-ref-node) |
|
5159 |
|
5160 (defun js2-visit-xml-elem-ref-node (n v) |
|
5161 (if (js2-xml-elem-ref-node-namespace n) |
|
5162 (js2-visit-ast (js2-xml-elem-ref-node-namespace n) v)) |
|
5163 (if (js2-xml-elem-ref-node-expr n) |
|
5164 (js2-visit-ast (js2-xml-elem-ref-node-expr n) v))) |
|
5165 |
|
5166 (defun js2-print-xml-elem-ref-node (n i) |
|
5167 (insert (js2-make-pad i)) |
|
5168 (if (js2-xml-ref-node-attr-access-p n) |
|
5169 (insert "@")) |
|
5170 (when (js2-xml-elem-ref-node-namespace n) |
|
5171 (js2-print-ast (js2-xml-elem-ref-node-namespace n) 0) |
|
5172 (insert "::")) |
|
5173 (insert "[") |
|
5174 (if (js2-xml-elem-ref-node-expr n) |
|
5175 (js2-print-ast (js2-xml-elem-ref-node-expr n) 0)) |
|
5176 (insert "]")) |
|
5177 |
|
5178 ;;; Placeholder nodes for when we try parsing the XML literals structurally. |
|
5179 |
|
5180 (defstruct (js2-xml-start-tag-node |
|
5181 (:include js2-xml-node) |
|
5182 (:constructor nil) |
|
5183 (:constructor make-js2-xml-start-tag-node (&key (type js2-XML) |
|
5184 (pos js2-ts-cursor) |
|
5185 len |
|
5186 name |
|
5187 attrs |
|
5188 kids |
|
5189 empty-p))) |
|
5190 "AST node for an XML start-tag. Not currently used. |
|
5191 The `kids' field is a lisp list of child content nodes." |
|
5192 name ; a `js2-xml-name-node' |
|
5193 attrs ; a lisp list of `js2-xml-attr-node' |
|
5194 empty-p) ; t if this is an empty element such as <foo bar="baz"/> |
|
5195 |
|
5196 (put 'cl-struct-js2-xml-start-tag-node 'js2-visitor 'js2-visit-xml-start-tag) |
|
5197 (put 'cl-struct-js2-xml-start-tag-node 'js2-printer 'js2-print-xml-start-tag) |
|
5198 |
|
5199 (defun js2-visit-xml-start-tag (n v) |
|
5200 (js2-visit-ast (js2-xml-start-tag-node-name n) v) |
|
5201 (dolist (attr (js2-xml-start-tag-node-attrs n)) |
|
5202 (js2-visit-ast attr v)) |
|
5203 (js2-visit-block n v)) |
|
5204 |
|
5205 (defun js2-print-xml-start-tag (n i) |
|
5206 (insert (js2-make-pad i) "<") |
|
5207 (js2-print-ast (js2-xml-start-tag-node-name n) 0) |
|
5208 (when (js2-xml-start-tag-node-attrs n) |
|
5209 (insert " ") |
|
5210 (js2-print-list (js2-xml-start-tag-node-attrs n) " ")) |
|
5211 (insert ">")) |
|
5212 |
|
5213 ;; I -think- I'm going to make the parent node the corresponding start-tag, |
|
5214 ;; and add the end-tag to the kids list of the parent as well. |
|
5215 (defstruct (js2-xml-end-tag-node |
|
5216 (:include js2-xml-node) |
|
5217 (:constructor nil) |
|
5218 (:constructor make-js2-xml-end-tag-node (&key (type js2-XML) |
|
5219 (pos js2-ts-cursor) |
|
5220 len |
|
5221 name))) |
|
5222 "AST node for an XML end-tag. Not currently used." |
|
5223 name) ; a `js2-xml-name-node' |
|
5224 |
|
5225 (put 'cl-struct-js2-xml-end-tag-node 'js2-visitor 'js2-visit-xml-end-tag) |
|
5226 (put 'cl-struct-js2-xml-end-tag-node 'js2-printer 'js2-print-xml-end-tag) |
|
5227 |
|
5228 (defun js2-visit-xml-end-tag (n v) |
|
5229 (js2-visit-ast (js2-xml-end-tag-node-name n) v)) |
|
5230 |
|
5231 (defun js2-print-xml-end-tag (n i) |
|
5232 (insert (js2-make-pad i)) |
|
5233 (insert "</") |
|
5234 (js2-print-ast (js2-xml-end-tag-node-name n) 0) |
|
5235 (insert ">")) |
|
5236 |
|
5237 (defstruct (js2-xml-name-node |
|
5238 (:include js2-xml-node) |
|
5239 (:constructor nil) |
|
5240 (:constructor make-js2-xml-name-node (&key (type js2-XML) |
|
5241 (pos js2-ts-cursor) |
|
5242 len |
|
5243 namespace |
|
5244 kids))) |
|
5245 "AST node for an E4X XML name. Not currently used. |
|
5246 Any XML name can be qualified with a namespace, hence the namespace field. |
|
5247 Further, any E4X name can be comprised of arbitrary JavaScript {} expressions. |
|
5248 The kids field is a list of `js2-name-node' and `js2-xml-js-expr-node'. |
|
5249 For a simple name, the kids list has exactly one node, a `js2-name-node'." |
|
5250 namespace) ; a `js2-string-node' |
|
5251 |
|
5252 (put 'cl-struct-js2-xml-name-node 'js2-visitor 'js2-visit-xml-name-node) |
|
5253 (put 'cl-struct-js2-xml-name-node 'js2-printer 'js2-print-xml-name-node) |
|
5254 |
|
5255 (defun js2-visit-xml-name-node (n v) |
|
5256 (js2-visit-ast (js2-xml-name-node-namespace n) v)) |
|
5257 |
|
5258 (defun js2-print-xml-name-node (n i) |
|
5259 (insert (js2-make-pad i)) |
|
5260 (when (js2-xml-name-node-namespace n) |
|
5261 (js2-print-ast (js2-xml-name-node-namespace n) 0) |
|
5262 (insert "::")) |
|
5263 (dolist (kid (js2-xml-name-node-kids n)) |
|
5264 (js2-print-ast kid 0))) |
|
5265 |
|
5266 (defstruct (js2-xml-pi-node |
|
5267 (:include js2-xml-node) |
|
5268 (:constructor nil) |
|
5269 (:constructor make-js2-xml-pi-node (&key (type js2-XML) |
|
5270 (pos js2-ts-cursor) |
|
5271 len |
|
5272 name |
|
5273 attrs))) |
|
5274 "AST node for an E4X XML processing instruction. Not currently used." |
|
5275 name ; a `js2-xml-name-node' |
|
5276 attrs) ; a list of `js2-xml-attr-node' |
|
5277 |
|
5278 (put 'cl-struct-js2-xml-pi-node 'js2-visitor 'js2-visit-xml-pi-node) |
|
5279 (put 'cl-struct-js2-xml-pi-node 'js2-printer 'js2-print-xml-pi-node) |
|
5280 |
|
5281 (defun js2-visit-xml-pi-node (n v) |
|
5282 (js2-visit-ast (js2-xml-pi-node-name n) v) |
|
5283 (dolist (attr (js2-xml-pi-node-attrs n)) |
|
5284 (js2-visit-ast attr v))) |
|
5285 |
|
5286 (defun js2-print-xml-pi-node (n i) |
|
5287 (insert (js2-make-pad i) "<?") |
|
5288 (js2-print-ast (js2-xml-pi-node-name n)) |
|
5289 (when (js2-xml-pi-node-attrs n) |
|
5290 (insert " ") |
|
5291 (js2-print-list (js2-xml-pi-node-attrs n))) |
|
5292 (insert "?>")) |
|
5293 |
|
5294 (defstruct (js2-xml-cdata-node |
|
5295 (:include js2-xml-node) |
|
5296 (:constructor nil) |
|
5297 (:constructor make-js2-xml-cdata-node (&key (type js2-XML) |
|
5298 (pos js2-ts-cursor) |
|
5299 len |
|
5300 content))) |
|
5301 "AST node for a CDATA escape section. Not currently used." |
|
5302 content) ; a `js2-string-node' with node-property 'quote-type 'cdata |
|
5303 |
|
5304 (put 'cl-struct-js2-xml-cdata-node 'js2-visitor 'js2-visit-xml-cdata-node) |
|
5305 (put 'cl-struct-js2-xml-cdata-node 'js2-printer 'js2-print-xml-cdata-node) |
|
5306 |
|
5307 (defun js2-visit-xml-cdata-node (n v) |
|
5308 (js2-visit-ast (js2-xml-cdata-node-content n) v)) |
|
5309 |
|
5310 (defun js2-print-xml-cdata-node (n i) |
|
5311 (insert (js2-make-pad i)) |
|
5312 (js2-print-ast (js2-xml-cdata-node-content n))) |
|
5313 |
|
5314 (defstruct (js2-xml-attr-node |
|
5315 (:include js2-xml-node) |
|
5316 (:constructor nil) |
|
5317 (:constructor make-js2-attr-node (&key (type js2-XML) |
|
5318 (pos js2-ts-cursor) |
|
5319 len |
|
5320 name |
|
5321 value |
|
5322 eq-pos |
|
5323 quote-type))) |
|
5324 "AST node representing a foo='bar' XML attribute value. Not yet used." |
|
5325 name ; a `js2-xml-name-node' |
|
5326 value ; a `js2-xml-name-node' |
|
5327 eq-pos ; buffer position of "=" sign |
|
5328 quote-type) ; 'single or 'double |
|
5329 |
|
5330 (put 'cl-struct-js2-xml-attr-node 'js2-visitor 'js2-visit-xml-attr-node) |
|
5331 (put 'cl-struct-js2-xml-attr-node 'js2-printer 'js2-print-xml-attr-node) |
|
5332 |
|
5333 (defun js2-visit-xml-attr-node (n v) |
|
5334 (js2-visit-ast (js2-xml-attr-node-name n) v) |
|
5335 (js2-visit-ast (js2-xml-attr-node-value n) v)) |
|
5336 |
|
5337 (defun js2-print-xml-attr-node (n i) |
|
5338 (let ((quote (if (eq (js2-xml-attr-node-quote-type n) 'single) |
|
5339 "'" |
|
5340 "\""))) |
|
5341 (insert (js2-make-pad i)) |
|
5342 (js2-print-ast (js2-xml-attr-node-name n) 0) |
|
5343 (insert "=" quote) |
|
5344 (js2-print-ast (js2-xml-attr-node-value n) 0) |
|
5345 (insert quote))) |
|
5346 |
|
5347 (defstruct (js2-xml-text-node |
|
5348 (:include js2-xml-node) |
|
5349 (:constructor nil) |
|
5350 (:constructor make-js2-text-node (&key (type js2-XML) |
|
5351 (pos js2-ts-cursor) |
|
5352 len |
|
5353 content))) |
|
5354 "AST node for an E4X XML text node. Not currently used." |
|
5355 content) ; a lisp list of `js2-string-node' and `js2-xml-js-expr-node' |
|
5356 |
|
5357 (put 'cl-struct-js2-xml-text-node 'js2-visitor 'js2-visit-xml-text-node) |
|
5358 (put 'cl-struct-js2-xml-text-node 'js2-printer 'js2-print-xml-text-node) |
|
5359 |
|
5360 (defun js2-visit-xml-text-node (n v) |
|
5361 (js2-visit-ast (js2-xml-text-node-content n) v)) |
|
5362 |
|
5363 (defun js2-print-xml-text-node (n i) |
|
5364 (insert (js2-make-pad i)) |
|
5365 (dolist (kid (js2-xml-text-node-content n)) |
|
5366 (js2-print-ast kid))) |
|
5367 |
|
5368 (defstruct (js2-xml-comment-node |
|
5369 (:include js2-xml-node) |
|
5370 (:constructor nil) |
|
5371 (:constructor make-js2-xml-comment-node (&key (type js2-XML) |
|
5372 (pos js2-ts-cursor) |
|
5373 len))) |
|
5374 "AST node for E4X XML comment. Not currently used.") |
|
5375 |
|
5376 (put 'cl-struct-js2-xml-comment-node 'js2-visitor 'js2-visit-none) |
|
5377 (put 'cl-struct-js2-xml-comment-node 'js2-printer 'js2-print-xml-comment) |
|
5378 |
|
5379 (defun js2-print-xml-comment (n i) |
|
5380 (insert (js2-make-pad i) |
|
5381 (js2-node-string n))) |
|
5382 |
|
5383 ;;; Node utilities |
|
5384 |
|
5385 (defsubst js2-node-line (n) |
|
5386 "Fetch the source line number at the start of node N. |
|
5387 This is O(n) in the length of the source buffer; use prudently." |
|
5388 (1+ (count-lines (point-min) (js2-node-abs-pos n)))) |
|
5389 |
|
5390 (defsubst js2-block-node-kid (n i) |
|
5391 "Return child I of node N, or nil if there aren't that many." |
|
5392 (nth i (js2-block-node-kids n))) |
|
5393 |
|
5394 (defsubst js2-block-node-first (n) |
|
5395 "Return first child of block node N, or nil if there is none." |
|
5396 (first (js2-block-node-kids n))) |
|
5397 |
|
5398 (defun js2-node-root (n) |
|
5399 "Return the root of the AST containing N. |
|
5400 If N has no parent pointer, returns N." |
|
5401 (let ((parent (js2-node-parent n))) |
|
5402 (if parent |
|
5403 (js2-node-root parent) |
|
5404 n))) |
|
5405 |
|
5406 (defun js2-node-position-in-parent (node &optional parent) |
|
5407 "Return the position of NODE in parent's block-kids list. |
|
5408 PARENT can be supplied if known. Positioned returned is zero-indexed. |
|
5409 Returns 0 if NODE is not a child of a block statement, or if NODE |
|
5410 is not a statement node." |
|
5411 (let ((p (or parent (js2-node-parent node))) |
|
5412 (i 0)) |
|
5413 (if (not (js2-block-node-p p)) |
|
5414 i |
|
5415 (or (js2-position node (js2-block-node-kids p)) |
|
5416 0)))) |
|
5417 |
|
5418 (defsubst js2-node-short-name (n) |
|
5419 "Return the short name of node N as a string, e.g. `js2-if-node'." |
|
5420 (substring (symbol-name (aref n 0)) |
|
5421 (length "cl-struct-"))) |
|
5422 |
|
5423 (defsubst js2-node-child-list (node) |
|
5424 "Return the child list for NODE, a lisp list of nodes. |
|
5425 Works for block nodes, array nodes, obj literals, funarg lists, |
|
5426 var decls and try nodes (for catch clauses). Note that you should call |
|
5427 `js2-block-node-kids' on the function body for the body statements. |
|
5428 Returns nil for zero-length child lists or unsupported nodes." |
|
5429 (cond |
|
5430 ((js2-function-node-p node) |
|
5431 (js2-function-node-params node)) |
|
5432 ((js2-block-node-p node) |
|
5433 (js2-block-node-kids node)) |
|
5434 ((js2-try-node-p node) |
|
5435 (js2-try-node-catch-clauses node)) |
|
5436 ((js2-array-node-p node) |
|
5437 (js2-array-node-elems node)) |
|
5438 ((js2-object-node-p node) |
|
5439 (js2-object-node-elems node)) |
|
5440 ((js2-call-node-p node) |
|
5441 (js2-call-node-args node)) |
|
5442 ((js2-new-node-p node) |
|
5443 (js2-new-node-args node)) |
|
5444 ((js2-var-decl-node-p node) |
|
5445 (js2-var-decl-node-kids node)) |
|
5446 (t |
|
5447 nil))) |
|
5448 |
|
5449 (defsubst js2-node-set-child-list (node kids) |
|
5450 "Set the child list for NODE to KIDS." |
|
5451 (cond |
|
5452 ((js2-function-node-p node) |
|
5453 (setf (js2-function-node-params node) kids)) |
|
5454 ((js2-block-node-p node) |
|
5455 (setf (js2-block-node-kids node) kids)) |
|
5456 ((js2-try-node-p node) |
|
5457 (setf (js2-try-node-catch-clauses node) kids)) |
|
5458 ((js2-array-node-p node) |
|
5459 (setf (js2-array-node-elems node) kids)) |
|
5460 ((js2-object-node-p node) |
|
5461 (setf (js2-object-node-elems node) kids)) |
|
5462 ((js2-call-node-p node) |
|
5463 (setf (js2-call-node-args node) kids)) |
|
5464 ((js2-new-node-p node) |
|
5465 (setf (js2-new-node-args node) kids)) |
|
5466 ((js2-var-decl-node-p node) |
|
5467 (setf (js2-var-decl-node-kids node) kids)) |
|
5468 (t |
|
5469 (error "Unsupported node type: %s" (js2-node-short-name node)))) |
|
5470 kids) |
|
5471 |
|
5472 ;; All because Common Lisp doesn't support multiple inheritance for defstructs. |
|
5473 (defconst js2-paren-expr-nodes |
|
5474 '(cl-struct-js2-array-comp-loop-node |
|
5475 cl-struct-js2-array-comp-node |
|
5476 cl-struct-js2-call-node |
|
5477 cl-struct-js2-catch-node |
|
5478 cl-struct-js2-do-node |
|
5479 cl-struct-js2-elem-get-node |
|
5480 cl-struct-js2-for-in-node |
|
5481 cl-struct-js2-for-node |
|
5482 cl-struct-js2-function-node |
|
5483 cl-struct-js2-if-node |
|
5484 cl-struct-js2-let-node |
|
5485 cl-struct-js2-new-node |
|
5486 cl-struct-js2-paren-node |
|
5487 cl-struct-js2-switch-node |
|
5488 cl-struct-js2-while-node |
|
5489 cl-struct-js2-with-node |
|
5490 cl-struct-js2-xml-dot-query-node) |
|
5491 "Node types that can have a parenthesized child expression. |
|
5492 In particular, nodes that respond to `js2-node-lp' and `js2-node-rp'.") |
|
5493 |
|
5494 (defsubst js2-paren-expr-node-p (node) |
|
5495 "Return t for nodes that typically have a parenthesized child expression. |
|
5496 Useful for computing the indentation anchors for arg-lists and conditions. |
|
5497 Note that it may return a false positive, for instance when NODE is |
|
5498 a `js2-new-node' and there are no arguments or parentheses." |
|
5499 (memq (aref node 0) js2-paren-expr-nodes)) |
|
5500 |
|
5501 ;; Fake polymorphism... yech. |
|
5502 (defsubst js2-node-lp (node) |
|
5503 "Return relative left-paren position for NODE, if applicable. |
|
5504 For `js2-elem-get-node' structs, returns left-bracket position. |
|
5505 Note that the position may be nil in the case of a parse error." |
|
5506 (cond |
|
5507 ((js2-elem-get-node-p node) |
|
5508 (js2-elem-get-node-lb node)) |
|
5509 ((js2-loop-node-p node) |
|
5510 (js2-loop-node-lp node)) |
|
5511 ((js2-function-node-p node) |
|
5512 (js2-function-node-lp node)) |
|
5513 ((js2-if-node-p node) |
|
5514 (js2-if-node-lp node)) |
|
5515 ((js2-new-node-p node) |
|
5516 (js2-new-node-lp node)) |
|
5517 ((js2-call-node-p node) |
|
5518 (js2-call-node-lp node)) |
|
5519 ((js2-paren-node-p node) |
|
5520 (js2-node-pos node)) |
|
5521 ((js2-switch-node-p node) |
|
5522 (js2-switch-node-lp node)) |
|
5523 ((js2-catch-node-p node) |
|
5524 (js2-catch-node-lp node)) |
|
5525 ((js2-let-node-p node) |
|
5526 (js2-let-node-lp node)) |
|
5527 ((js2-array-comp-node-p node) |
|
5528 (js2-array-comp-node-lp node)) |
|
5529 ((js2-with-node-p node) |
|
5530 (js2-with-node-lp node)) |
|
5531 ((js2-xml-dot-query-node-p node) |
|
5532 (1+ (js2-infix-node-op-pos node))) |
|
5533 (t |
|
5534 (error "Unsupported node type: %s" (js2-node-short-name node))))) |
|
5535 |
|
5536 ;; Fake polymorphism... blech. |
|
5537 (defsubst js2-node-rp (node) |
|
5538 "Return relative right-paren position for NODE, if applicable. |
|
5539 For `js2-elem-get-node' structs, returns right-bracket position. |
|
5540 Note that the position may be nil in the case of a parse error." |
|
5541 (cond |
|
5542 ((js2-elem-get-node-p node) |
|
5543 (js2-elem-get-node-lb node)) |
|
5544 ((js2-loop-node-p node) |
|
5545 (js2-loop-node-rp node)) |
|
5546 ((js2-function-node-p node) |
|
5547 (js2-function-node-rp node)) |
|
5548 ((js2-if-node-p node) |
|
5549 (js2-if-node-rp node)) |
|
5550 ((js2-new-node-p node) |
|
5551 (js2-new-node-rp node)) |
|
5552 ((js2-call-node-p node) |
|
5553 (js2-call-node-rp node)) |
|
5554 ((js2-paren-node-p node) |
|
5555 (+ (js2-node-pos node) (js2-node-len node))) |
|
5556 ((js2-switch-node-p node) |
|
5557 (js2-switch-node-rp node)) |
|
5558 ((js2-catch-node-p node) |
|
5559 (js2-catch-node-rp node)) |
|
5560 ((js2-let-node-p node) |
|
5561 (js2-let-node-rp node)) |
|
5562 ((js2-array-comp-node-p node) |
|
5563 (js2-array-comp-node-rp node)) |
|
5564 ((js2-with-node-p node) |
|
5565 (js2-with-node-rp node)) |
|
5566 ((js2-xml-dot-query-node-p node) |
|
5567 (1+ (js2-xml-dot-query-node-rp node))) |
|
5568 (t |
|
5569 (error "Unsupported node type: %s" (js2-node-short-name node))))) |
|
5570 |
|
5571 (defsubst js2-node-first-child (node) |
|
5572 "Returns the first element of `js2-node-child-list' for NODE." |
|
5573 (car (js2-node-child-list node))) |
|
5574 |
|
5575 (defsubst js2-node-last-child (node) |
|
5576 "Returns the last element of `js2-node-last-child' for NODE." |
|
5577 (car (last (js2-node-child-list node)))) |
|
5578 |
|
5579 (defun js2-node-prev-sibling (node) |
|
5580 "Return the previous statement in parent. |
|
5581 Works for parents supported by `js2-node-child-list'. |
|
5582 Returns nil if NODE is not in the parent, or PARENT is |
|
5583 not a supported node, or if NODE is the first child." |
|
5584 (let* ((p (js2-node-parent node)) |
|
5585 (kids (js2-node-child-list p)) |
|
5586 (sib (car kids))) |
|
5587 (while (and kids |
|
5588 (neq node (cadr kids))) |
|
5589 (setq kids (cdr kids) |
|
5590 sib (car kids))) |
|
5591 sib)) |
|
5592 |
|
5593 (defun js2-node-next-sibling (node) |
|
5594 "Return the next statement in parent block. |
|
5595 Returns nil if NODE is not in the block, or PARENT is not |
|
5596 a block node, or if NODE is the last statement." |
|
5597 (let* ((p (js2-node-parent node)) |
|
5598 (kids (js2-node-child-list p))) |
|
5599 (while (and kids |
|
5600 (neq node (car kids))) |
|
5601 (setq kids (cdr kids))) |
|
5602 (cadr kids))) |
|
5603 |
|
5604 (defun js2-node-find-child-before (pos parent &optional after) |
|
5605 "Find the last child that starts before POS in parent. |
|
5606 If AFTER is non-nil, returns first child starting after POS. |
|
5607 POS is an absolute buffer position. PARENT is any node |
|
5608 supported by `js2-node-child-list'. |
|
5609 Returns nil if no applicable child is found." |
|
5610 (let ((kids (if (js2-function-node-p parent) |
|
5611 (js2-block-node-kids (js2-function-node-body parent)) |
|
5612 (js2-node-child-list parent))) |
|
5613 (beg (if (js2-function-node-p parent) |
|
5614 (js2-node-abs-pos (js2-function-node-body parent)) |
|
5615 (js2-node-abs-pos parent))) |
|
5616 kid |
|
5617 result |
|
5618 fn |
|
5619 (continue t)) |
|
5620 (setq fn (if after '> '<)) |
|
5621 (while (and kids continue) |
|
5622 (setq kid (car kids)) |
|
5623 (if (funcall fn (+ beg (js2-node-pos kid)) pos) |
|
5624 (setq result kid |
|
5625 continue (if after nil t)) |
|
5626 (setq continue (if after t nil))) |
|
5627 (setq kids (cdr kids))) |
|
5628 result)) |
|
5629 |
|
5630 (defun js2-node-find-child-after (pos parent) |
|
5631 "Find first child that starts after POS in parent. |
|
5632 POS is an absolute buffer position. PARENT is any node |
|
5633 supported by `js2-node-child-list'. |
|
5634 Returns nil if no applicable child is found." |
|
5635 (js2-node-find-child-before pos parent 'after)) |
|
5636 |
|
5637 (defun js2-node-replace-child (pos parent new-node) |
|
5638 "Replace node at index POS in PARENT with NEW-NODE. |
|
5639 Only works for parents supported by `js2-node-child-list'." |
|
5640 (let ((kids (js2-node-child-list parent)) |
|
5641 (i 0)) |
|
5642 (while (< i pos) |
|
5643 (setq kids (cdr kids) |
|
5644 i (1+ i))) |
|
5645 (setcar kids new-node) |
|
5646 (js2-node-add-children parent new-node))) |
|
5647 |
|
5648 (defun js2-node-buffer (n) |
|
5649 "Return the buffer associated with AST N. |
|
5650 Returns nil if the buffer is not set as a property on the root |
|
5651 node, or if parent links were not recorded during parsing." |
|
5652 (let ((root (js2-node-root n))) |
|
5653 (and root |
|
5654 (js2-ast-root-p root) |
|
5655 (js2-ast-root-buffer root)))) |
|
5656 |
|
5657 (defsubst js2-block-node-push (n kid) |
|
5658 "Push js2-node KID onto the end of js2-block-node N's child list. |
|
5659 KID is always added to the -end- of the kids list. |
|
5660 Function also calls `js2-node-add-children' to add the parent link." |
|
5661 (let ((kids (js2-node-child-list n))) |
|
5662 (if kids |
|
5663 (setcdr kids (nconc (cdr kids) (list kid))) |
|
5664 (js2-node-set-child-list n (list kid))) |
|
5665 (js2-node-add-children n kid))) |
|
5666 |
|
5667 (defun js2-node-string (node) |
|
5668 (let ((buf (js2-node-buffer node)) |
|
5669 pos) |
|
5670 (unless buf |
|
5671 (error "No buffer available for node %s" node)) |
|
5672 (save-excursion |
|
5673 (set-buffer buf) |
|
5674 (buffer-substring-no-properties (setq pos (js2-node-abs-pos node)) |
|
5675 (+ pos (js2-node-len node)))))) |
|
5676 |
|
5677 ;; Container for storing the node we're looking for in a traversal. |
|
5678 (defvar js2-discovered-node nil) |
|
5679 (make-variable-buffer-local 'js2-discovered-node) |
|
5680 |
|
5681 ;; Keep track of absolute node position during traversals. |
|
5682 (defvar js2-visitor-offset nil) |
|
5683 (make-variable-buffer-local 'js2-visitor-offset) |
|
5684 |
|
5685 (defvar js2-node-search-point nil) |
|
5686 (make-variable-buffer-local 'js2-node-search-point) |
|
5687 |
|
5688 (when js2-mode-dev-mode-p |
|
5689 (defun js2-find-node-at-point () |
|
5690 (interactive) |
|
5691 (let ((node (js2-node-at-point))) |
|
5692 (message "%s" (or node "No node found at point")))) |
|
5693 (defun js2-node-name-at-point () |
|
5694 (interactive) |
|
5695 (let ((node (js2-node-at-point))) |
|
5696 (message "%s" (if node |
|
5697 (js2-node-short-name node) |
|
5698 "No node found at point."))))) |
|
5699 |
|
5700 (defun js2-node-at-point (&optional pos skip-comments) |
|
5701 "Return AST node at POS, a buffer position, defaulting to current point. |
|
5702 The `js2-mode-ast' variable must be set to the current parse tree. |
|
5703 Signals an error if the AST (`js2-mode-ast') is nil. |
|
5704 Always returns a node - if it can't find one, it returns the root. |
|
5705 If SKIP-COMMENTS is non-nil, comment nodes are ignored." |
|
5706 (let ((ast js2-mode-ast) |
|
5707 result) |
|
5708 (unless ast |
|
5709 (error "No JavaScript AST available")) |
|
5710 ;; Look through comments first, since they may be inside nodes that |
|
5711 ;; would otherwise report a match. |
|
5712 (setq pos (or pos (point)) |
|
5713 result (if (> pos (js2-node-abs-end ast)) |
|
5714 ast |
|
5715 (if (not skip-comments) |
|
5716 (js2-comment-at-point pos)))) |
|
5717 (unless result |
|
5718 (setq js2-discovered-node nil |
|
5719 js2-visitor-offset 0 |
|
5720 js2-node-search-point pos) |
|
5721 (unwind-protect |
|
5722 (catch 'js2-visit-done |
|
5723 (js2-visit-ast ast #'js2-node-at-point-visitor)) |
|
5724 (setq js2-visitor-offset nil |
|
5725 js2-node-search-point nil)) |
|
5726 (setq result js2-discovered-node)) |
|
5727 ;; may have found a comment beyond end of last child node, |
|
5728 ;; since visiting the ast-root looks at the comment-list last. |
|
5729 (if (and skip-comments |
|
5730 (js2-comment-node-p result)) |
|
5731 (setq result nil)) |
|
5732 (or result js2-mode-ast))) |
|
5733 |
|
5734 (defun js2-node-at-point-visitor (node end-p) |
|
5735 (let ((rel-pos (js2-node-pos node)) |
|
5736 abs-pos |
|
5737 abs-end |
|
5738 (point js2-node-search-point)) |
|
5739 (cond |
|
5740 (end-p |
|
5741 ;; this evaluates to a non-nil return value, even if it's zero |
|
5742 (decf js2-visitor-offset rel-pos)) |
|
5743 ;; we already looked for comments before visiting, and don't want them now |
|
5744 ((js2-comment-node-p node) |
|
5745 nil) |
|
5746 (t |
|
5747 (setq abs-pos (incf js2-visitor-offset rel-pos) |
|
5748 ;; we only want to use the node if the point is before |
|
5749 ;; the last character position in the node, so we decrement |
|
5750 ;; the absolute end by 1. |
|
5751 abs-end (+ abs-pos (js2-node-len node) -1)) |
|
5752 (cond |
|
5753 ;; If this node starts after search-point, stop the search. |
|
5754 ((> abs-pos point) |
|
5755 (throw 'js2-visit-done nil)) |
|
5756 ;; If this node ends before the search-point, don't check kids. |
|
5757 ((> point abs-end) |
|
5758 nil) |
|
5759 (t |
|
5760 ;; Otherwise point is within this node, possibly in a child. |
|
5761 (setq js2-discovered-node node) |
|
5762 t)))))) ; keep processing kids to look for more specific match |
|
5763 |
|
5764 (defsubst js2-block-comment-p (node) |
|
5765 "Return non-nil if NODE is a comment node of format `jsdoc' or `block'." |
|
5766 (and (js2-comment-node-p node) |
|
5767 (memq (js2-comment-node-format node) '(jsdoc block)))) |
|
5768 |
|
5769 ;; TODO: put the comments in a vector and binary-search them instead |
|
5770 (defun js2-comment-at-point (&optional pos) |
|
5771 "Look through scanned comment nodes for one containing POS. |
|
5772 POS is a buffer position that defaults to current point. |
|
5773 Function returns nil if POS was not in any comment node." |
|
5774 (let ((ast js2-mode-ast) |
|
5775 (x (or pos (point))) |
|
5776 beg |
|
5777 end) |
|
5778 (unless ast |
|
5779 (error "No JavaScript AST available")) |
|
5780 (catch 'done |
|
5781 ;; Comments are stored in lexical order. |
|
5782 (dolist (comment (js2-ast-root-comments ast) nil) |
|
5783 (setq beg (js2-node-abs-pos comment) |
|
5784 end (+ beg (js2-node-len comment))) |
|
5785 (if (and (>= x beg) |
|
5786 (<= x end)) |
|
5787 (throw 'done comment)))))) |
|
5788 |
|
5789 (defun js2-mode-find-parent-fn (node) |
|
5790 "Find function enclosing NODE. |
|
5791 Returns nil if NODE is not inside a function." |
|
5792 (setq node (js2-node-parent node)) |
|
5793 (while (and node (not (js2-function-node-p node))) |
|
5794 (setq node (js2-node-parent node))) |
|
5795 (and (js2-function-node-p node) node)) |
|
5796 |
|
5797 (defun js2-mode-find-enclosing-fn (node) |
|
5798 "Find function or root enclosing NODE." |
|
5799 (if (js2-ast-root-p node) |
|
5800 node |
|
5801 (setq node (js2-node-parent node)) |
|
5802 (while (not (or (js2-ast-root-p node) |
|
5803 (js2-function-node-p node))) |
|
5804 (setq node (js2-node-parent node))) |
|
5805 node)) |
|
5806 |
|
5807 (defun js2-mode-find-enclosing-node (beg end) |
|
5808 "Find script or function fully enclosing BEG and END." |
|
5809 (let ((node (js2-node-at-point beg)) |
|
5810 pos |
|
5811 (continue t)) |
|
5812 (while continue |
|
5813 (if (or (js2-ast-root-p node) |
|
5814 (and (js2-function-node-p node) |
|
5815 (<= (setq pos (js2-node-abs-pos node)) beg) |
|
5816 (>= (+ pos (js2-node-len node)) end))) |
|
5817 (setq continue nil) |
|
5818 (setq node (js2-node-parent node)))) |
|
5819 node)) |
|
5820 |
|
5821 (defun js2-node-parent-script-or-fn (node) |
|
5822 "Find script or function immediately enclosing NODE. |
|
5823 If NODE is the ast-root, returns nil." |
|
5824 (if (js2-ast-root-p node) |
|
5825 nil |
|
5826 (setq node (js2-node-parent node)) |
|
5827 (while (and node (not (or (js2-function-node-p node) |
|
5828 (js2-script-node-p node)))) |
|
5829 (setq node (js2-node-parent node))) |
|
5830 node)) |
|
5831 |
|
5832 (defsubst js2-nested-function-p (node) |
|
5833 "Return t if NODE is a nested function, or is inside a nested function." |
|
5834 (js2-function-node-p (if (js2-function-node-p node) |
|
5835 (js2-node-parent-script-or-fn node) |
|
5836 (js2-node-parent-script-or-fn |
|
5837 (js2-node-parent-script-or-fn node))))) |
|
5838 |
|
5839 (defsubst js2-mode-shift-kids (kids start offset) |
|
5840 (dolist (kid kids) |
|
5841 (if (> (js2-node-pos kid) start) |
|
5842 (incf (js2-node-pos kid) offset)))) |
|
5843 |
|
5844 (defsubst js2-mode-shift-children (parent start offset) |
|
5845 "Update start-positions of all children of PARENT beyond START." |
|
5846 (let ((root (js2-node-root parent))) |
|
5847 (js2-mode-shift-kids (js2-node-child-list parent) start offset) |
|
5848 (js2-mode-shift-kids (js2-ast-root-comments root) start offset))) |
|
5849 |
|
5850 (defsubst js2-node-is-descendant (node ancestor) |
|
5851 "Return t if NODE is a descendant of ANCESTOR." |
|
5852 (while (and node |
|
5853 (neq node ancestor)) |
|
5854 (setq node (js2-node-parent node))) |
|
5855 node) |
|
5856 |
|
5857 ;;; visitor infrastructure |
|
5858 |
|
5859 (defun js2-visit-none (node callback) |
|
5860 "Visitor for AST node that have no node children." |
|
5861 nil) |
|
5862 |
|
5863 (defun js2-print-none (node indent) |
|
5864 "Visitor for AST node with no printed representation.") |
|
5865 |
|
5866 (defun js2-print-body (node indent) |
|
5867 "Print a statement, or a block without braces." |
|
5868 (if (js2-block-node-p node) |
|
5869 (dolist (kid (js2-block-node-kids node)) |
|
5870 (js2-print-ast kid indent)) |
|
5871 (js2-print-ast node indent))) |
|
5872 |
|
5873 (defun js2-print-list (args &optional delimiter) |
|
5874 (loop with len = (length args) |
|
5875 for arg in args |
|
5876 for count from 1 |
|
5877 do |
|
5878 (js2-print-ast arg 0) |
|
5879 (if (< count len) |
|
5880 (insert (or delimiter ", "))))) |
|
5881 |
|
5882 (defun js2-print-tree (ast) |
|
5883 "Prints an AST to the current buffer. |
|
5884 Makes `js2-ast-parent-nodes' available to the printer functions." |
|
5885 (let ((max-lisp-eval-depth (max max-lisp-eval-depth 1500))) |
|
5886 (js2-print-ast ast))) |
|
5887 |
|
5888 (defun js2-print-ast (node &optional indent) |
|
5889 "Helper function for printing AST nodes. |
|
5890 Requires `js2-ast-parent-nodes' to be non-nil. |
|
5891 You should use `js2-print-tree' instead of this function." |
|
5892 (let ((printer (get (aref node 0) 'js2-printer)) |
|
5893 (i (or indent 0)) |
|
5894 (pos (js2-node-abs-pos node))) |
|
5895 ;; TODO: wedge comments in here somewhere |
|
5896 (if printer |
|
5897 (funcall printer node i)))) |
|
5898 |
|
5899 (defconst js2-side-effecting-tokens |
|
5900 (let ((tokens (make-bool-vector js2-num-tokens nil))) |
|
5901 (dolist (tt (list js2-ASSIGN |
|
5902 js2-ASSIGN_ADD |
|
5903 js2-ASSIGN_BITAND |
|
5904 js2-ASSIGN_BITOR |
|
5905 js2-ASSIGN_BITXOR |
|
5906 js2-ASSIGN_DIV |
|
5907 js2-ASSIGN_LSH |
|
5908 js2-ASSIGN_MOD |
|
5909 js2-ASSIGN_MUL |
|
5910 js2-ASSIGN_RSH |
|
5911 js2-ASSIGN_SUB |
|
5912 js2-ASSIGN_URSH |
|
5913 js2-BLOCK |
|
5914 js2-BREAK |
|
5915 js2-CALL |
|
5916 js2-CATCH |
|
5917 js2-CATCH_SCOPE |
|
5918 js2-CONST |
|
5919 js2-CONTINUE |
|
5920 js2-DEBUGGER |
|
5921 js2-DEC |
|
5922 js2-DELPROP |
|
5923 js2-DEL_REF |
|
5924 js2-DO |
|
5925 js2-ELSE |
|
5926 js2-EMPTY |
|
5927 js2-ENTERWITH |
|
5928 js2-EXPORT |
|
5929 js2-EXPR_RESULT |
|
5930 js2-FINALLY |
|
5931 js2-FOR |
|
5932 js2-FUNCTION |
|
5933 js2-GOTO |
|
5934 js2-IF |
|
5935 js2-IFEQ |
|
5936 js2-IFNE |
|
5937 js2-IMPORT |
|
5938 js2-INC |
|
5939 js2-JSR |
|
5940 js2-LABEL |
|
5941 js2-LEAVEWITH |
|
5942 js2-LET |
|
5943 js2-LETEXPR |
|
5944 js2-LOCAL_BLOCK |
|
5945 js2-LOOP |
|
5946 js2-NEW |
|
5947 js2-REF_CALL |
|
5948 js2-RETHROW |
|
5949 js2-RETURN |
|
5950 js2-RETURN_RESULT |
|
5951 js2-SEMI |
|
5952 js2-SETELEM |
|
5953 js2-SETELEM_OP |
|
5954 js2-SETNAME |
|
5955 js2-SETPROP |
|
5956 js2-SETPROP_OP |
|
5957 js2-SETVAR |
|
5958 js2-SET_REF |
|
5959 js2-SET_REF_OP |
|
5960 js2-SWITCH |
|
5961 js2-TARGET |
|
5962 js2-THROW |
|
5963 js2-TRY |
|
5964 js2-VAR |
|
5965 js2-WHILE |
|
5966 js2-WITH |
|
5967 js2-WITHEXPR |
|
5968 js2-YIELD)) |
|
5969 (aset tokens tt t)) |
|
5970 (if js2-instanceof-has-side-effects |
|
5971 (aset tokens js2-INSTANCEOF t)) |
|
5972 tokens)) |
|
5973 |
|
5974 (defun js2-node-has-side-effects (node) |
|
5975 "Return t if NODE has side effects." |
|
5976 (when node ; makes it easier to handle malformed expressions |
|
5977 (let ((tt (js2-node-type node))) |
|
5978 (cond |
|
5979 ;; This doubtless needs some work, since EXPR_VOID is used |
|
5980 ;; in several ways in Rhino, and I may not have caught them all. |
|
5981 ;; I'll wait for people to notice incorrect warnings. |
|
5982 ((and (= tt js2-EXPR_VOID) |
|
5983 (js2-expr-stmt-node-p node)) ; but not if EXPR_RESULT |
|
5984 (js2-node-has-side-effects (js2-expr-stmt-node-expr node))) |
|
5985 |
|
5986 ((= tt js2-COMMA) |
|
5987 (js2-node-has-side-effects (js2-infix-node-right node))) |
|
5988 |
|
5989 ((or (= tt js2-AND) |
|
5990 (= tt js2-OR)) |
|
5991 (or (js2-node-has-side-effects (js2-infix-node-right node)) |
|
5992 (js2-node-has-side-effects (js2-infix-node-left node)))) |
|
5993 |
|
5994 ((= tt js2-HOOK) |
|
5995 (and (js2-node-has-side-effects (js2-cond-node-true-expr node)) |
|
5996 (js2-node-has-side-effects (js2-cond-node-false-expr node)))) |
|
5997 |
|
5998 ((js2-paren-node-p node) |
|
5999 (js2-node-has-side-effects (js2-paren-node-expr node))) |
|
6000 |
|
6001 ((= tt js2-ERROR) ; avoid cascaded error messages |
|
6002 nil) |
|
6003 (t |
|
6004 (aref js2-side-effecting-tokens tt)))))) |
|
6005 |
|
6006 (defun js2-member-expr-leftmost-name (node) |
|
6007 "For an expr such as foo.bar.baz, return leftmost node foo. |
|
6008 NODE is any `js2-node' object. If it represents a member expression, |
|
6009 which is any sequence of property gets, element-gets, function calls, |
|
6010 or xml descendants/filter operators, then we look at the lexically |
|
6011 leftmost (first) node in the chain. If it is a name-node we return it. |
|
6012 Note that NODE can be a raw name-node and it will be returned as well. |
|
6013 If NODE is not a name-node or member expression, or if it is a member |
|
6014 expression whose leftmost target is not a name node, returns nil." |
|
6015 (let ((continue t) |
|
6016 result) |
|
6017 (while (and continue (not result)) |
|
6018 (cond |
|
6019 ((js2-name-node-p node) |
|
6020 (setq result node)) |
|
6021 ((js2-prop-get-node-p node) |
|
6022 (setq node (js2-prop-get-node-left node))) |
|
6023 ;; TODO: handle call-nodes, xml-nodes, others? |
|
6024 (t |
|
6025 (setq continue nil)))) |
|
6026 result)) |
|
6027 |
|
6028 (defconst js2-stmt-node-types |
|
6029 (list js2-BLOCK |
|
6030 js2-BREAK |
|
6031 js2-CONTINUE |
|
6032 js2-DEFAULT ; e4x "default xml namespace" statement |
|
6033 js2-DO |
|
6034 js2-EXPR_RESULT |
|
6035 js2-EXPR_VOID |
|
6036 js2-FOR |
|
6037 js2-IF |
|
6038 js2-RETURN |
|
6039 js2-SWITCH |
|
6040 js2-THROW |
|
6041 js2-TRY |
|
6042 js2-WHILE |
|
6043 js2-WITH) |
|
6044 "Node types that only appear in statement contexts. |
|
6045 The list does not include nodes that always appear as the child |
|
6046 of another specific statement type, such as switch-cases, |
|
6047 catch and finally blocks, and else-clauses. The list also excludes |
|
6048 nodes like yield, let and var, which may appear in either expression |
|
6049 or statement context, and in the latter context always have a |
|
6050 `js2-expr-stmt-node' parent. Finally, the list does not include |
|
6051 functions or scripts, which are treated separately from statements |
|
6052 by the JavaScript parser and runtime.") |
|
6053 |
|
6054 (defun js2-stmt-node-p (node) |
|
6055 "Heuristic for figuring out if NODE is a statement. |
|
6056 Some node types can appear in either an expression context or a |
|
6057 statement context, e.g. let-nodes, yield-nodes, and var-decl nodes. |
|
6058 For these node types in a statement context, the parent will be a |
|
6059 `js2-expr-stmt-node'. |
|
6060 Functions aren't included in the check." |
|
6061 (memq (js2-node-type node) js2-stmt-node-types)) |
|
6062 |
|
6063 (defsubst js2-mode-find-first-stmt (node) |
|
6064 "Search upward starting from NODE looking for a statement. |
|
6065 For purposes of this function, a `js2-function-node' counts." |
|
6066 (while (not (or (js2-stmt-node-p node) |
|
6067 (js2-function-node-p node))) |
|
6068 (setq node (js2-node-parent node))) |
|
6069 node) |
|
6070 |
|
6071 (defun js2-node-parent-stmt (node) |
|
6072 "Return the node's first ancestor that is a statement. |
|
6073 Returns nil if NODE is a `js2-ast-root'. Note that any expression |
|
6074 appearing in a statement context will have a parent that is a |
|
6075 `js2-expr-stmt-node' that will be returned by this function." |
|
6076 (let ((parent (js2-node-parent node))) |
|
6077 (if (or (null parent) |
|
6078 (js2-stmt-node-p parent) |
|
6079 (and (js2-function-node-p parent) |
|
6080 (neq (js2-function-node-form parent) 'FUNCTION_EXPRESSION))) |
|
6081 parent |
|
6082 (js2-node-parent-stmt parent)))) |
|
6083 |
|
6084 ;; Roshan James writes: |
|
6085 ;; Does consistent-return analysis on the function body when strict mode is |
|
6086 ;; enabled. |
|
6087 ;; |
|
6088 ;; function (x) { return (x+1) } |
|
6089 ;; |
|
6090 ;; is ok, but |
|
6091 ;; |
|
6092 ;; function (x) { if (x < 0) return (x+1); } |
|
6093 ;; |
|
6094 ;; is not because the function can potentially return a value when the |
|
6095 ;; condition is satisfied and if not, the function does not explicitly |
|
6096 ;; return a value. |
|
6097 ;; |
|
6098 ;; This extends to checking mismatches such as "return" and "return <value>" |
|
6099 ;; used in the same function. Warnings are not emitted if inconsistent |
|
6100 ;; returns exist in code that can be statically shown to be unreachable. |
|
6101 ;; Ex. |
|
6102 ;; function (x) { while (true) { ... if (..) { return value } ... } } |
|
6103 ;; |
|
6104 ;; emits no warning. However if the loop had a break statement, then a |
|
6105 ;; warning would be emitted. |
|
6106 ;; |
|
6107 ;; The consistency analysis looks at control structures such as loops, ifs, |
|
6108 ;; switch, try-catch-finally blocks, examines the reachable code paths and |
|
6109 ;; warns the user about an inconsistent set of termination possibilities. |
|
6110 ;; |
|
6111 ;; These flags enumerate the possible ways a statement/function can |
|
6112 ;; terminate. These flags are used by endCheck() and by the Parser to |
|
6113 ;; detect inconsistent return usage. |
|
6114 ;; |
|
6115 ;; END_UNREACHED is reserved for code paths that are assumed to always be |
|
6116 ;; able to execute (example: throw, continue) |
|
6117 ;; |
|
6118 ;; END_DROPS_OFF indicates if the statement can transfer control to the |
|
6119 ;; next one. Statement such as return dont. A compound statement may have |
|
6120 ;; some branch that drops off control to the next statement. |
|
6121 ;; |
|
6122 ;; END_RETURNS indicates that the statement can return with no value. |
|
6123 ;; END_RETURNS_VALUE indicates that the statement can return a value. |
|
6124 ;; |
|
6125 ;; A compound statement such as |
|
6126 ;; if (condition) { |
|
6127 ;; return value; |
|
6128 ;; } |
|
6129 ;; Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck() |
|
6130 |
|
6131 (defconst js2-END_UNREACHED 0) |
|
6132 (defconst js2-END_DROPS_OFF 1) |
|
6133 (defconst js2-END_RETURNS 2) |
|
6134 (defconst js2-END_RETURNS_VALUE 4) |
|
6135 (defconst js2-END_YIELDS 8) |
|
6136 |
|
6137 (defun js2-has-consistent-return-usage (node) |
|
6138 "Check that every return usage in a function body is consistent. |
|
6139 Returns t if the function satisfies strict mode requirement." |
|
6140 (let ((n (js2-end-check node))) |
|
6141 ;; either it doesn't return a value in any branch... |
|
6142 (or (js2-flag-not-set-p n js2-END_RETURNS_VALUE) |
|
6143 ;; or it returns a value (or is unreached) at every branch |
|
6144 (js2-flag-not-set-p n (logior js2-END_DROPS_OFF |
|
6145 js2-END_RETURNS |
|
6146 js2-END_YIELDS))))) |
|
6147 |
|
6148 (defun js2-end-check-if (node) |
|
6149 "Returns in the then and else blocks must be consistent with each other. |
|
6150 If there is no else block, then the return statement can fall through. |
|
6151 Returns logical OR of END_* flags" |
|
6152 (let ((th (js2-if-node-then-part node)) |
|
6153 (el (js2-if-node-else-part node))) |
|
6154 (if (null th) |
|
6155 js2-END_UNREACHED |
|
6156 (logior (js2-end-check th) (if el |
|
6157 (js2-end-check el) |
|
6158 js2-END_DROPS_OFF))))) |
|
6159 |
|
6160 (defun js2-end-check-switch (node) |
|
6161 "Consistency of return statements is checked between the case statements. |
|
6162 If there is no default, then the switch can fall through. If there is a |
|
6163 default, we check to see if all code paths in the default return or if |
|
6164 there is a code path that can fall through. |
|
6165 Returns logical OR of END_* flags." |
|
6166 (let ((rv js2-END_UNREACHED) |
|
6167 default-case) |
|
6168 ;; examine the cases |
|
6169 (catch 'break |
|
6170 (dolist (c (js2-switch-node-cases node)) |
|
6171 (if (js2-case-node-expr c) |
|
6172 (js2-set-flag rv (js2-end-check-block c)) |
|
6173 (setq default-case c) |
|
6174 (throw 'break nil)))) |
|
6175 |
|
6176 ;; we don't care how the cases drop into each other |
|
6177 (js2-clear-flag rv js2-END_DROPS_OFF) |
|
6178 |
|
6179 ;; examine the default |
|
6180 (js2-set-flag rv (if default-case |
|
6181 (js2-end-check default-case) |
|
6182 js2-END_DROPS_OFF)) |
|
6183 rv)) |
|
6184 |
|
6185 (defun js2-end-check-try (node) |
|
6186 "If the block has a finally, return consistency is checked in the |
|
6187 finally block. If all code paths in the finally return, then the |
|
6188 returns in the try-catch blocks don't matter. If there is a code path |
|
6189 that does not return or if there is no finally block, the returns |
|
6190 of the try and catch blocks are checked for mismatch. |
|
6191 Returns logical OR of END_* flags." |
|
6192 (let ((finally (js2-try-node-finally-block node)) |
|
6193 rv) |
|
6194 ;; check the finally if it exists |
|
6195 (setq rv (if finally |
|
6196 (js2-end-check (js2-finally-node-body finally)) |
|
6197 js2-END_DROPS_OFF)) |
|
6198 |
|
6199 ;; If the finally block always returns, then none of the returns |
|
6200 ;; in the try or catch blocks matter. |
|
6201 (when (js2-flag-set-p rv js2-END_DROPS_OFF) |
|
6202 (js2-clear-flag rv js2-END_DROPS_OFF) |
|
6203 |
|
6204 ;; examine the try block |
|
6205 (js2-set-flag rv (js2-end-check (js2-try-node-try-block node))) |
|
6206 |
|
6207 ;; check each catch block |
|
6208 (dolist (cb (js2-try-node-catch-clauses node)) |
|
6209 (js2-set-flag rv (js2-end-check (js2-catch-node-block cb))))) |
|
6210 rv)) |
|
6211 |
|
6212 (defun js2-end-check-loop (node) |
|
6213 "Return statement in the loop body must be consistent. The default |
|
6214 assumption for any kind of a loop is that it will eventually terminate. |
|
6215 The only exception is a loop with a constant true condition. Code that |
|
6216 follows such a loop is examined only if one can statically determine |
|
6217 that there is a break out of the loop. |
|
6218 |
|
6219 for(... ; ... ; ...) {} |
|
6220 for(... in ... ) {} |
|
6221 while(...) { } |
|
6222 do { } while(...) |
|
6223 |
|
6224 Returns logical OR of END_* flags." |
|
6225 (let ((rv (js2-end-check (js2-loop-node-body node))) |
|
6226 (condition (cond |
|
6227 ((js2-while-node-p node) |
|
6228 (js2-while-node-condition node)) |
|
6229 ((js2-do-node-p node) |
|
6230 (js2-do-node-condition node)) |
|
6231 ((js2-for-node-p node) |
|
6232 (js2-for-node-condition node))))) |
|
6233 |
|
6234 ;; check to see if the loop condition is always true |
|
6235 (if (and condition |
|
6236 (eq (js2-always-defined-boolean-p condition) 'ALWAYS_TRUE)) |
|
6237 (js2-clear-flag rv js2-END_DROPS_OFF)) |
|
6238 |
|
6239 ;; look for effect of breaks |
|
6240 (js2-set-flag rv (js2-node-get-prop node |
|
6241 'CONTROL_BLOCK_PROP |
|
6242 js2-END_UNREACHED)) |
|
6243 rv)) |
|
6244 |
|
6245 (defun js2-end-check-block (node) |
|
6246 "A general block of code is examined statement by statement. |
|
6247 If any statement (even a compound one) returns in all branches, then |
|
6248 subsequent statements are not examined. |
|
6249 Returns logical OR of END_* flags." |
|
6250 (let* ((rv js2-END_DROPS_OFF) |
|
6251 (kids (js2-block-node-kids node)) |
|
6252 (n (car kids))) |
|
6253 ;; Check each statment. If the statement can continue onto the next |
|
6254 ;; one (i.e. END_DROPS_OFF is set), then check the next statement. |
|
6255 (while (and n (js2-flag-set-p rv js2-END_DROPS_OFF)) |
|
6256 (js2-clear-flag rv js2-END_DROPS_OFF) |
|
6257 (js2-set-flag rv (js2-end-check n)) |
|
6258 (setq kids (cdr kids) |
|
6259 n (car kids))) |
|
6260 rv)) |
|
6261 |
|
6262 (defun js2-end-check-label (node) |
|
6263 "A labeled statement implies that there may be a break to the label. |
|
6264 The function processes the labeled statement and then checks the |
|
6265 CONTROL_BLOCK_PROP property to see if there is ever a break to the |
|
6266 particular label. |
|
6267 Returns logical OR of END_* flags." |
|
6268 (let ((rv (js2-end-check (js2-labeled-stmt-node-stmt node)))) |
|
6269 (logior rv (js2-node-get-prop node |
|
6270 'CONTROL_BLOCK_PROP |
|
6271 js2-END_UNREACHED)))) |
|
6272 |
|
6273 (defun js2-end-check-break (node) |
|
6274 "When a break is encountered annotate the statement being broken |
|
6275 out of by setting its CONTROL_BLOCK_PROP property. |
|
6276 Returns logical OR of END_* flags." |
|
6277 (and (js2-break-node-target node) |
|
6278 (js2-node-set-prop (js2-break-node-target node) |
|
6279 'CONTROL_BLOCK_PROP |
|
6280 js2-END_DROPS_OFF)) |
|
6281 js2-END_UNREACHED) |
|
6282 |
|
6283 (defun js2-end-check (node) |
|
6284 "Examine the body of a function, doing a basic reachability analysis. |
|
6285 Returns a combination of flags END_* flags that indicate |
|
6286 how the function execution can terminate. These constitute only the |
|
6287 pessimistic set of termination conditions. It is possible that at |
|
6288 runtime certain code paths will never be actually taken. Hence this |
|
6289 analysis will flag errors in cases where there may not be errors. |
|
6290 Returns logical OR of END_* flags" |
|
6291 (let (kid) |
|
6292 (cond |
|
6293 ((js2-break-node-p node) |
|
6294 (js2-end-check-break node)) |
|
6295 |
|
6296 ((js2-expr-stmt-node-p node) |
|
6297 (if (setq kid (js2-expr-stmt-node-expr node)) |
|
6298 (js2-end-check kid) |
|
6299 js2-END_DROPS_OFF)) |
|
6300 |
|
6301 ((or (js2-continue-node-p node) |
|
6302 (js2-throw-node-p node)) |
|
6303 js2-END_UNREACHED) |
|
6304 |
|
6305 ((js2-return-node-p node) |
|
6306 (if (setq kid (js2-return-node-retval node)) |
|
6307 js2-END_RETURNS_VALUE |
|
6308 js2-END_RETURNS)) |
|
6309 |
|
6310 ((js2-loop-node-p node) |
|
6311 (js2-end-check-loop node)) |
|
6312 |
|
6313 ((js2-switch-node-p node) |
|
6314 (js2-end-check-switch node)) |
|
6315 |
|
6316 ((js2-labeled-stmt-node-p node) |
|
6317 (js2-end-check-label node)) |
|
6318 |
|
6319 ((js2-if-node-p node) |
|
6320 (js2-end-check-if node)) |
|
6321 |
|
6322 ((js2-try-node-p node) |
|
6323 (js2-end-check-try node)) |
|
6324 |
|
6325 ((js2-block-node-p node) |
|
6326 (if (null (js2-block-node-kids node)) |
|
6327 js2-END_DROPS_OFF |
|
6328 (js2-end-check-block node))) |
|
6329 |
|
6330 ((js2-yield-node-p node) |
|
6331 js2-END_YIELDS) |
|
6332 |
|
6333 (t |
|
6334 js2-END_DROPS_OFF)))) |
|
6335 |
|
6336 (defun js2-always-defined-boolean-p (node) |
|
6337 "Check if NODE always evaluates to true or false in boolean context. |
|
6338 Returns 'ALWAYS_TRUE, 'ALWAYS_FALSE, or nil if it's neither always true |
|
6339 nor always false." |
|
6340 (let ((tt (js2-node-type node)) |
|
6341 num) |
|
6342 (cond |
|
6343 ((or (= tt js2-FALSE) (= tt js2-NULL)) |
|
6344 'ALWAYS_FALSE) |
|
6345 ((= tt js2-TRUE) |
|
6346 'ALWAYS_TRUE) |
|
6347 ((= tt js2-NUMBER) |
|
6348 (setq num (js2-number-node-num-value node)) |
|
6349 (if (and (not (eq num 0.0e+NaN)) |
|
6350 (not (zerop num))) |
|
6351 'ALWAYS_TRUE |
|
6352 'ALWAYS_FALSE)) |
|
6353 (t |
|
6354 nil)))) |
|
6355 |
|
6356 (provide 'js2-ast) |
|
6357 |
|
6358 ;;; js2-ast.el ends here |
|
6359 ;;; js2-highlight.el --- JavaScript syntax coloring support |
|
6360 |
|
6361 ;; Author: Steve Yegge (steve.yegge@gmail.com) |
|
6362 ;; Keywords: javascript languages |
|
6363 |
|
6364 ;;; Code: |
|
6365 |
|
6366 |
|
6367 (defsubst js2-set-face (beg end face &optional record) |
|
6368 "Fontify a region. If RECORD is non-nil, record for later." |
|
6369 (when (plusp js2-highlight-level) |
|
6370 (setq beg (min (point-max) beg) |
|
6371 beg (max (point-min) beg) |
|
6372 end (min (point-max) end) |
|
6373 end (max (point-min) end)) |
|
6374 (if record |
|
6375 (push (list beg end face) js2-mode-fontifications) |
|
6376 (put-text-property beg end 'face face)))) |
|
6377 |
|
6378 (defsubst js2-set-kid-face (pos kid len face) |
|
6379 "Set-face on a child node. |
|
6380 POS is absolute buffer position of parent. |
|
6381 KID is the child node. |
|
6382 LEN is the length to fontify. |
|
6383 FACE is the face to fontify with." |
|
6384 (js2-set-face (+ pos (js2-node-pos kid)) |
|
6385 (+ pos (js2-node-pos kid) (js2-node-len kid)) |
|
6386 face)) |
|
6387 |
|
6388 (defsubst js2-fontify-kwd (start length) |
|
6389 (js2-set-face start (+ start length) 'font-lock-keyword-face)) |
|
6390 |
|
6391 (defsubst js2-clear-face (beg end) |
|
6392 (remove-text-properties beg end '(face nil |
|
6393 help-echo nil |
|
6394 point-entered nil |
|
6395 c-in-sws nil))) |
|
6396 |
|
6397 (defsubst js2-record-text-property (beg end prop value) |
|
6398 "Record a text property to set when parsing finishes." |
|
6399 (push (list beg end prop value) js2-mode-deferred-properties)) |
|
6400 |
|
6401 (defconst js2-ecma-global-props |
|
6402 (concat "^" |
|
6403 (regexp-opt |
|
6404 '("Infinity" "NaN" "undefined" "arguments") t) |
|
6405 "$") |
|
6406 "Value properties of the Ecma-262 Global Object. |
|
6407 Shown at or above `js2-highlight-level' 2.") |
|
6408 |
|
6409 ;; might want to add the name "arguments" to this list? |
|
6410 (defconst js2-ecma-object-props |
|
6411 (concat "^" |
|
6412 (regexp-opt |
|
6413 '("prototype" "__proto__" "__parent__") t) |
|
6414 "$") |
|
6415 "Value properties of the Ecma-262 Object constructor. |
|
6416 Shown at or above `js2-highlight-level' 2.") |
|
6417 |
|
6418 (defconst js2-ecma-global-funcs |
|
6419 (concat |
|
6420 "^" |
|
6421 (regexp-opt |
|
6422 '("decodeURI" "decodeURIComponent" "encodeURI" "encodeURIComponent" |
|
6423 "eval" "isFinite" "isNaN" "parseFloat" "parseInt") t) |
|
6424 "$") |
|
6425 "Function properties of the Ecma-262 Global object. |
|
6426 Shown at or above `js2-highlight-level' 2.") |
|
6427 |
|
6428 (defconst js2-ecma-number-props |
|
6429 (concat "^" |
|
6430 (regexp-opt '("MAX_VALUE" "MIN_VALUE" "NaN" |
|
6431 "NEGATIVE_INFINITY" |
|
6432 "POSITIVE_INFINITY") t) |
|
6433 "$") |
|
6434 "Properties of the Ecma-262 Number constructor. |
|
6435 Shown at or above `js2-highlight-level' 2.") |
|
6436 |
|
6437 (defconst js2-ecma-date-props "^\\(parse\\|UTC\\)$" |
|
6438 "Properties of the Ecma-262 Date constructor. |
|
6439 Shown at or above `js2-highlight-level' 2.") |
|
6440 |
|
6441 |
|
6442 (defconst js2-ecma-math-props |
|
6443 (concat "^" |
|
6444 (regexp-opt |
|
6445 '("E" "LN10" "LN2" "LOG2E" "LOG10E" "PI" "SQRT1_2" "SQRT2") |
|
6446 t) |
|
6447 "$") |
|
6448 "Properties of the Ecma-262 Math object. |
|
6449 Shown at or above `js2-highlight-level' 2.") |
|
6450 |
|
6451 |
|
6452 (defconst js2-ecma-math-funcs |
|
6453 (concat "^" |
|
6454 (regexp-opt |
|
6455 '("abs" "acos" "asin" "atan" "atan2" "ceil" "cos" "exp" "floor" |
|
6456 "log" "max" "min" "pow" "random" "round" "sin" "sqrt" "tan") t) |
|
6457 "$") |
|
6458 "Function properties of the Ecma-262 Math object. |
|
6459 Shown at or above `js2-highlight-level' 2.") |
|
6460 |
|
6461 (defconst js2-ecma-function-props |
|
6462 (concat |
|
6463 "^" |
|
6464 (regexp-opt |
|
6465 '(;; properties of the Object prototype object |
|
6466 "hasOwnProperty" "isPrototypeOf" "propertyIsEnumerable" |
|
6467 "toLocaleString" "toString" "valueOf" |
|
6468 ;; properties of the Function prototype object |
|
6469 "apply" "call" |
|
6470 ;; properties of the Array prototype object |
|
6471 "concat" "join" "pop" "push" "reverse" "shift" "slice" "sort" |
|
6472 "splice" "unshift" |
|
6473 ;; properties of the String prototype object |
|
6474 "charAt" "charCodeAt" "fromCharCode" "indexOf" "lastIndexOf" |
|
6475 "localeCompare" "match" "replace" "search" "split" "substring" |
|
6476 "toLocaleLowerCase" "toLocaleUpperCase" "toLowerCase" |
|
6477 "toUpperCase" |
|
6478 ;; properties of the Number prototype object |
|
6479 "toExponential" "toFixed" "toPrecision" |
|
6480 ;; properties of the Date prototype object |
|
6481 "getDate" "getDay" "getFullYear" "getHours" "getMilliseconds" |
|
6482 "getMinutes" "getMonth" "getSeconds" "getTime" |
|
6483 "getTimezoneOffset" "getUTCDate" "getUTCDay" "getUTCFullYear" |
|
6484 "getUTCHours" "getUTCMilliseconds" "getUTCMinutes" "getUTCMonth" |
|
6485 "getUTCSeconds" "setDate" "setFullYear" "setHours" |
|
6486 "setMilliseconds" "setMinutes" "setMonth" "setSeconds" "setTime" |
|
6487 "setUTCDate" "setUTCFullYear" "setUTCHours" "setUTCMilliseconds" |
|
6488 "setUTCMinutes" "setUTCMonth" "setUTCSeconds" "toDateString" |
|
6489 "toLocaleDateString" "toLocaleString" "toLocaleTimeString" |
|
6490 "toTimeString" "toUTCString" |
|
6491 ;; properties of the RegExp prototype object |
|
6492 "exec" "test" |
|
6493 ;; SpiderMonkey/Rhino extensions, versions 1.5+ |
|
6494 "toSource" "__defineGetter__" "__defineSetter__" |
|
6495 "__lookupGetter__" "__lookupSetter__" "__noSuchMethod__" |
|
6496 "every" "filter" "forEach" "lastIndexOf" "map" "some") |
|
6497 t) |
|
6498 "$") |
|
6499 "Built-in functions defined by Ecma-262 and SpiderMonkey extensions. |
|
6500 Shown at or above `js2-highlight-level' 3.") |
|
6501 |
|
6502 (defsubst js2-parse-highlight-prop-get (parent target prop call-p) |
|
6503 (let ((target-name (and target |
|
6504 (js2-name-node-p target) |
|
6505 (js2-name-node-name target))) |
|
6506 (prop-name (if prop (js2-name-node-name prop))) |
|
6507 (level1 (>= js2-highlight-level 1)) |
|
6508 (level2 (>= js2-highlight-level 2)) |
|
6509 (level3 (>= js2-highlight-level 3)) |
|
6510 pos |
|
6511 face) |
|
6512 (when level2 |
|
6513 (if call-p |
|
6514 (cond |
|
6515 ((and target prop) |
|
6516 (cond |
|
6517 ((and level3 (string-match js2-ecma-function-props prop-name)) |
|
6518 (setq face 'font-lock-builtin-face)) |
|
6519 ((and target-name prop) |
|
6520 (cond |
|
6521 ((string= target-name "Date") |
|
6522 (if (string-match js2-ecma-date-props prop-name) |
|
6523 (setq face 'font-lock-builtin-face))) |
|
6524 ((string= target-name "Math") |
|
6525 (if (string-match js2-ecma-math-funcs prop-name) |
|
6526 (setq face 'font-lock-builtin-face))))))) |
|
6527 (prop |
|
6528 (if (string-match js2-ecma-global-funcs prop-name) |
|
6529 (setq face 'font-lock-builtin-face)))) |
|
6530 (cond |
|
6531 ((and target prop) |
|
6532 (cond |
|
6533 ((string= target-name "Number") |
|
6534 (if (string-match js2-ecma-number-props prop-name) |
|
6535 (setq face 'font-lock-constant-face))) |
|
6536 ((string= target-name "Math") |
|
6537 (if (string-match js2-ecma-math-props prop-name) |
|
6538 (setq face 'font-lock-constant-face))))) |
|
6539 (prop |
|
6540 (if (string-match js2-ecma-object-props prop-name) |
|
6541 (setq face 'font-lock-constant-face))))) |
|
6542 (when face |
|
6543 (js2-set-face (setq pos (+ (js2-node-pos parent) ; absolute |
|
6544 (js2-node-pos prop))) ; relative |
|
6545 (+ pos (js2-node-len prop)) |
|
6546 face))))) |
|
6547 |
|
6548 (defun js2-parse-highlight-member-expr-node (node) |
|
6549 "Perform syntax highlighting of EcmaScript built-in properties. |
|
6550 The variable `js2-highlight-level' governs this highighting." |
|
6551 (let (face target prop name pos end parent call-p callee) |
|
6552 (cond |
|
6553 ;; case 1: simple name, e.g. foo |
|
6554 ((js2-name-node-p node) |
|
6555 (setq name (js2-name-node-name node)) |
|
6556 ;; possible for name to be nil in rare cases - saw it when |
|
6557 ;; running js2-mode on an elisp buffer. Might as well try to |
|
6558 ;; make it so js2-mode never barfs. |
|
6559 (when name |
|
6560 (setq face (if (string-match js2-ecma-global-props name) |
|
6561 'font-lock-constant-face)) |
|
6562 (when face |
|
6563 (setq pos (js2-node-pos node) |
|
6564 end (+ pos (js2-node-len node))) |
|
6565 (js2-set-face pos end face)))) |
|
6566 |
|
6567 ;; case 2: property access or function call |
|
6568 ((or (js2-prop-get-node-p node) |
|
6569 ;; highlight function call if expr is a prop-get node |
|
6570 ;; or a plain name (i.e. unqualified function call) |
|
6571 (and (setq call-p (js2-call-node-p node)) |
|
6572 (setq callee (js2-call-node-target node)) ; separate setq! |
|
6573 (or (js2-prop-get-node-p callee) |
|
6574 (js2-name-node-p callee)))) |
|
6575 (setq parent node |
|
6576 node (if call-p callee node)) |
|
6577 (if (and call-p (js2-name-node-p callee)) |
|
6578 (setq prop callee) |
|
6579 (setq target (js2-prop-get-node-left node) |
|
6580 prop (js2-prop-get-node-right node))) |
|
6581 (cond |
|
6582 ((js2-name-node-p target) |
|
6583 (if (js2-name-node-p prop) |
|
6584 ;; case 2a: simple target, simple prop name, e.g. foo.bar |
|
6585 (js2-parse-highlight-prop-get parent target prop call-p) |
|
6586 ;; case 2b: simple target, complex name, e.g. foo.x[y] |
|
6587 (js2-parse-highlight-prop-get parent target nil call-p))) |
|
6588 ((js2-name-node-p prop) |
|
6589 ;; case 2c: complex target, simple name, e.g. x[y].bar |
|
6590 (js2-parse-highlight-prop-get parent target prop call-p))))))) |
|
6591 |
|
6592 (defun js2-parse-highlight-member-expr-fn-name (expr) |
|
6593 "Highlight the `baz' in function foo.bar.baz(args) {...}. |
|
6594 This is experimental Rhino syntax. EXPR is the foo.bar.baz member expr. |
|
6595 We currently only handle the case where the last component is a prop-get |
|
6596 of a simple name. Called before EXPR has a parent node." |
|
6597 (let (pos |
|
6598 (name (and (js2-prop-get-node-p expr) |
|
6599 (js2-prop-get-node-right expr)))) |
|
6600 (when (js2-name-node-p name) |
|
6601 (js2-set-face (setq pos (+ (js2-node-pos expr) ; parent is absolute |
|
6602 (js2-node-pos name))) |
|
6603 (+ pos (js2-node-len name)) |
|
6604 'font-lock-function-name-face |
|
6605 'record)))) |
|
6606 |
|
6607 ;; source: http://jsdoc.sourceforge.net/ |
|
6608 ;; Note - this syntax is for Google's enhanced jsdoc parser that |
|
6609 ;; allows type specifications, and needs work before entering the wild. |
|
6610 |
|
6611 (defconst js2-jsdoc-param-tag-regexp |
|
6612 (concat "^\\s-*\\*+\\s-*\\(@" |
|
6613 "\\(?:param\\|argument\\)" |
|
6614 "\\)" |
|
6615 "\\s-*\\({[^}]+}\\)?" ; optional type |
|
6616 "\\s-*\\([a-zA-Z0-9_$]+\\)?" ; name |
|
6617 "\\>") |
|
6618 "Matches jsdoc tags with optional type and optional param name.") |
|
6619 |
|
6620 (defconst js2-jsdoc-typed-tag-regexp |
|
6621 (concat "^\\s-*\\*+\\s-*\\(@\\(?:" |
|
6622 (regexp-opt |
|
6623 '("requires" "return" "returns" "throw" "throws")) |
|
6624 "\\)\\)\\s-*\\({[^}]+}\\)?") |
|
6625 "Matches jsdoc tags with optional type.") |
|
6626 |
|
6627 (defconst js2-jsdoc-arg-tag-regexp |
|
6628 (concat "^\\s-*\\*+\\s-*\\(@\\(?:" |
|
6629 (regexp-opt |
|
6630 '("base" "extends" "member" "type" "version")) |
|
6631 "\\)\\)\\s-+\\([^ \t]+\\)") |
|
6632 "Matches jsdoc tags with a single argument.") |
|
6633 |
|
6634 (defconst js2-jsdoc-empty-tag-regexp |
|
6635 (concat "^\\s-*\\*+\\s-*\\(@\\(?:" |
|
6636 (regexp-opt |
|
6637 '("addon" "author" "class" "constructor" "deprecated" "exec" |
|
6638 "exception" "fileoverview" "final" "ignore" "private")) |
|
6639 "\\)\\)\\s-*") |
|
6640 "Matches empty jsdoc tags.") |
|
6641 |
|
6642 (defconst js2-jsdoc-link-tag-regexp |
|
6643 "{\\(@link\\)\\s-+\\([^#}\n]+\\)\\(#.+\\)?}" |
|
6644 "Matches a jsdoc link tag.") |
|
6645 |
|
6646 (defconst js2-jsdoc-see-tag-regexp |
|
6647 "^\\s-*\\*+\\s-*\\(@see\\)\\s-+\\([^#}\n]+\\)\\(#.+\\)?" |
|
6648 "Matches a jsdoc @see tag.") |
|
6649 |
|
6650 (defconst js2-jsdoc-html-tag-regexp |
|
6651 "\\(</?\\)\\([a-zA-Z]+\\)\\s-*\\(/?>\\)" |
|
6652 "Matches a simple (no attributes) html start- or end-tag.") |
|
6653 |
|
6654 (defsubst js2-jsdoc-highlight-helper () |
|
6655 (js2-set-face (match-beginning 1) |
|
6656 (match-end 1) |
|
6657 'js2-jsdoc-tag-face) |
|
6658 (if (match-beginning 2) |
|
6659 (if (save-excursion |
|
6660 (goto-char (match-beginning 2)) |
|
6661 (= (char-after) ?{)) |
|
6662 (js2-set-face (1+ (match-beginning 2)) |
|
6663 (1- (match-end 2)) |
|
6664 'js2-jsdoc-type-face) |
|
6665 (js2-set-face (match-beginning 2) |
|
6666 (match-end 2) |
|
6667 'js2-jsdoc-value-face))) |
|
6668 (if (match-beginning 3) |
|
6669 (js2-set-face (match-beginning 3) |
|
6670 (match-end 3) |
|
6671 'js2-jsdoc-value-face))) |
|
6672 |
|
6673 (defun js2-highlight-jsdoc (ast) |
|
6674 "Highlight doc comment tags." |
|
6675 (let ((comments (js2-ast-root-comments ast)) |
|
6676 beg end) |
|
6677 (save-excursion |
|
6678 (dolist (node comments) |
|
6679 (when (eq (js2-comment-node-format node) 'jsdoc) |
|
6680 (setq beg (js2-node-abs-pos node) |
|
6681 end (+ beg (js2-node-len node))) |
|
6682 (save-restriction |
|
6683 (narrow-to-region beg end) |
|
6684 (dolist (re (list js2-jsdoc-param-tag-regexp |
|
6685 js2-jsdoc-typed-tag-regexp |
|
6686 js2-jsdoc-arg-tag-regexp |
|
6687 js2-jsdoc-link-tag-regexp |
|
6688 js2-jsdoc-see-tag-regexp |
|
6689 js2-jsdoc-empty-tag-regexp)) |
|
6690 (goto-char beg) |
|
6691 (while (re-search-forward re nil t) |
|
6692 (js2-jsdoc-highlight-helper))) |
|
6693 ;; simple highlighting for html tags |
|
6694 (goto-char beg) |
|
6695 (while (re-search-forward js2-jsdoc-html-tag-regexp nil t) |
|
6696 (js2-set-face (match-beginning 1) |
|
6697 (match-end 1) |
|
6698 'js2-jsdoc-html-tag-delimiter-face) |
|
6699 (js2-set-face (match-beginning 2) |
|
6700 (match-end 2) |
|
6701 'js2-jsdoc-html-tag-name-face) |
|
6702 (js2-set-face (match-beginning 3) |
|
6703 (match-end 3) |
|
6704 'js2-jsdoc-html-tag-delimiter-face)))))))) |
|
6705 |
|
6706 (defun js2-highlight-assign-targets (node left right) |
|
6707 "Highlight function properties and external variables." |
|
6708 (let (leftpos end name) |
|
6709 ;; highlight vars and props assigned function values |
|
6710 (when (js2-function-node-p right) |
|
6711 (cond |
|
6712 ;; var foo = function() {...} |
|
6713 ((js2-name-node-p left) |
|
6714 (setq name left)) |
|
6715 |
|
6716 ;; foo.bar.baz = function() {...} |
|
6717 ((and (js2-prop-get-node-p left) |
|
6718 (js2-name-node-p (js2-prop-get-node-right left))) |
|
6719 (setq name (js2-prop-get-node-right left)))) |
|
6720 |
|
6721 (when name |
|
6722 (js2-set-face (setq leftpos (js2-node-abs-pos name)) |
|
6723 (+ leftpos (js2-node-len name)) |
|
6724 'font-lock-function-name-face |
|
6725 'record))) |
|
6726 |
|
6727 ;; save variable assignments so we can check for undeclared later |
|
6728 ;; (can't do it here since var decls can come at end of script) |
|
6729 (when (and js2-highlight-external-variables |
|
6730 (setq name (js2-member-expr-leftmost-name left))) |
|
6731 (push (list name js2-current-scope |
|
6732 (setq leftpos (js2-node-abs-pos name)) |
|
6733 (setq end (+ leftpos (js2-node-len name)))) |
|
6734 js2-recorded-assignments)))) |
|
6735 |
|
6736 (defun js2-highlight-undeclared-vars () |
|
6737 "After entire parse is finished, look for undeclared variable assignments. |
|
6738 Have to wait until entire buffer is parsed, since JavaScript permits var |
|
6739 decls to occur after they're used. |
|
6740 |
|
6741 We currently use a simple heuristic to rule out complaining about built-ins: |
|
6742 if the name is capitalized we don't highlight it. This could be improved a |
|
6743 bit by declaring all the Ecma global object, constructor and function names |
|
6744 in a hashtable, but we'd still wind up complaining about all the DHTML |
|
6745 builtins, the Mozilla builtins, etc." |
|
6746 (let (name first-char) |
|
6747 (dolist (entry js2-recorded-assignments) |
|
6748 (destructuring-bind (name-node scope pos end) entry |
|
6749 (setq name (js2-name-node-name name-node) |
|
6750 first-char (aref name 0)) |
|
6751 (unless (or (and (>= first-char ?A) (<= first-char ?Z)) |
|
6752 (js2-get-defining-scope scope name)) |
|
6753 (js2-set-face pos end 'js2-external-variable-face 'record) |
|
6754 (js2-record-text-property pos end 'help-echo "Undeclared variable") |
|
6755 (js2-record-text-property pos end 'point-entered #'js2-echo-help)))) |
|
6756 (setq js2-recorded-assignments nil))) |
|
6757 |
|
6758 (provide 'js2-highlight) |
|
6759 |
|
6760 ;;; js2-highlight.el ends here |
|
6761 ;;; js2-browse.el --- browsing/hierarchy support for js2-mode |
|
6762 |
|
6763 ;; Author: Steve Yegge (steve.yegge@gmail.com) |
|
6764 ;; Keywords: javascript languages |
|
6765 |
|
6766 ;; Commentary: |
|
6767 ;; |
|
6768 ;; We currently only support imenu, but eventually should support speedbar and |
|
6769 ;; possibly other browsing mechanisms. |
|
6770 ;; |
|
6771 ;; The basic strategy is to identify function assignment targets of the form |
|
6772 ;; `foo.bar.baz', convert them to (list foo bar baz <position>), and push the |
|
6773 ;; list into `js2-imenu-recorder'. The lists are merged into a trie-like tree |
|
6774 ;; for imenu after parsing is finished. |
|
6775 ;; |
|
6776 ;; A `foo.bar.baz' assignment target may be expressed in many ways in |
|
6777 ;; JavaScript, and the general problem is undecidable. However, several forms |
|
6778 ;; are readily recognizable at parse-time; the forms we attempt to recognize |
|
6779 ;; include: |
|
6780 ;; |
|
6781 ;; function foo() -- function declaration |
|
6782 ;; foo = function() -- function expression assigned to variable |
|
6783 ;; foo.bar.baz = function() -- function expr assigned to nested property-get |
|
6784 ;; foo = {bar: function()} -- fun prop in object literal assigned to var |
|
6785 ;; foo = {bar: {baz: function()}} -- inside nested object literal |
|
6786 ;; foo.bar = {baz: function()}} -- obj lit assigned to nested prop get |
|
6787 ;; a.b = {c: {d: function()}} -- nested obj lit assigned to nested prop get |
|
6788 ;; foo = {get bar() {...}} -- getter/setter in obj literal |
|
6789 ;; function foo() {function bar() {...}} -- nested function |
|
6790 ;; foo['a'] = function() -- fun expr assigned to deterministic element-get |
|
6791 ;; |
|
6792 ;; This list boils down to a few forms that can be combined recursively. |
|
6793 ;; Top-level named function declarations include both the left-hand (name) |
|
6794 ;; and the right-hand (function value) expressions needed to produce an imenu |
|
6795 ;; entry. The other "right-hand" forms we need to look for are: |
|
6796 ;; - functions declared as props/getters/setters in object literals |
|
6797 ;; - nested named function declarations |
|
6798 ;; The "left-hand" expressions that functions can be assigned to include: |
|
6799 ;; - local/global variables |
|
6800 ;; - nested property-get expressions like a.b.c.d |
|
6801 ;; - element gets like foo[10] or foo['bar'] where the index |
|
6802 ;; expression can be trivially converted to a property name. They |
|
6803 ;; effectively then become property gets. |
|
6804 ;; |
|
6805 ;; All the different definition types are canonicalized into the form |
|
6806 ;; foo.bar.baz = position-of-function-keyword |
|
6807 ;; |
|
6808 ;; We need to build a trie-like structure for imenu. As an example, |
|
6809 ;; consider the following JavaScript code: |
|
6810 ;; |
|
6811 ;; a = function() {...} // function at position 5 |
|
6812 ;; b = function() {...} // function at position 25 |
|
6813 ;; foo = function() {...} // function at position 100 |
|
6814 ;; foo.bar = function() {...} // function at position 200 |
|
6815 ;; foo.bar.baz = function() {...} // function at position 300 |
|
6816 ;; foo.bar.zab = function() {...} // function at position 400 |
|
6817 ;; |
|
6818 ;; During parsing we accumulate an entry for each definition in |
|
6819 ;; the variable `js2-imenu-recorder', like so: |
|
6820 ;; |
|
6821 ;; '((a 5) |
|
6822 ;; (b 25) |
|
6823 ;; (foo 100) |
|
6824 ;; (foo bar 200) |
|
6825 ;; (foo bar baz 300) |
|
6826 ;; (foo bar zab 400)) |
|
6827 ;; |
|
6828 ;; After parsing these entries are merged into this alist-trie: |
|
6829 ;; |
|
6830 ;; '((a . 1) |
|
6831 ;; (b . 2) |
|
6832 ;; (foo (<definition> . 3) |
|
6833 ;; (bar (<definition> . 6) |
|
6834 ;; (baz . 100) |
|
6835 ;; (zab . 200)))) |
|
6836 ;; |
|
6837 ;; Note the wacky need for a <definition> name. The token can be anything |
|
6838 ;; that isn't a valid JavaScript identifier, because you might make foo |
|
6839 ;; a function and then start setting properties on it that are also functions. |
|
6840 |
|
6841 ;;; Code: |
|
6842 |
|
6843 |
|
6844 (defsubst js2-prop-node-name (node) |
|
6845 "Return the name of a node that may be a property-get/property-name. |
|
6846 If NODE is not a valid name-node, string-node or integral number-node, |
|
6847 returns nil. Otherwise returns the string name/value of the node." |
|
6848 (cond |
|
6849 ((js2-name-node-p node) |
|
6850 (js2-name-node-name node)) |
|
6851 ((js2-string-node-p node) |
|
6852 (js2-string-node-value node)) |
|
6853 ((and (js2-number-node-p node) |
|
6854 (string-match "^[0-9]+$" (js2-number-node-value node))) |
|
6855 (js2-number-node-value node)) |
|
6856 ((js2-this-node-p node) |
|
6857 "this"))) |
|
6858 |
|
6859 (defsubst js2-node-qname-component (node) |
|
6860 "Test function: return the name of this node, if it contributes to a qname. |
|
6861 Returns nil if the node doesn't contribute." |
|
6862 (copy-sequence |
|
6863 (or (js2-prop-node-name node) |
|
6864 (if (and (js2-function-node-p node) |
|
6865 (js2-function-node-name node)) |
|
6866 (js2-name-node-name (js2-function-node-name node)))))) |
|
6867 |
|
6868 (defsubst js2-record-function-qname (fn-node qname) |
|
6869 "Associate FN-NODE with its QNAME for later lookup. |
|
6870 This is used in postprocessing the chain list. When we find a chain |
|
6871 whose first element is a js2-THIS keyword node, we look up the parent |
|
6872 function and see (using this map) whether it is the tail of a chain. |
|
6873 If so, we replace the this-node with a copy of the parent's qname." |
|
6874 (unless js2-imenu-function-map |
|
6875 (setq js2-imenu-function-map (make-hash-table :test 'eq))) |
|
6876 (puthash fn-node qname js2-imenu-function-map)) |
|
6877 |
|
6878 (defun js2-record-imenu-functions (node &optional var) |
|
6879 "Record function definitions for imenu. |
|
6880 NODE is a function node or an object literal. |
|
6881 VAR, if non-nil, is the expression that NODE is being assigned to." |
|
6882 (when js2-parse-ide-mode |
|
6883 (let ((fun-p (js2-function-node-p node)) |
|
6884 qname left fname-node pos) |
|
6885 (cond |
|
6886 ;; non-anonymous function declaration? |
|
6887 ((and fun-p |
|
6888 (not var) |
|
6889 (setq fname-node (js2-function-node-name node))) |
|
6890 (push (setq qname (list fname-node (js2-node-pos node))) |
|
6891 js2-imenu-recorder) |
|
6892 (js2-record-function-qname node qname)) |
|
6893 |
|
6894 ;; for remaining forms, compute left-side tree branch first |
|
6895 ((and var (setq qname (js2-compute-nested-prop-get var))) |
|
6896 (cond |
|
6897 ;; foo.bar.baz = function |
|
6898 (fun-p |
|
6899 (push (nconc qname (list (js2-node-pos node))) |
|
6900 js2-imenu-recorder) |
|
6901 (js2-record-function-qname node qname)) |
|
6902 ;; foo.bar.baz = object-literal |
|
6903 ;; look for nested functions: {a: {b: function() {...} }} |
|
6904 ((js2-object-node-p node) |
|
6905 (js2-record-object-literal node qname)))))))) |
|
6906 |
|
6907 (defun js2-compute-nested-prop-get (node) |
|
6908 "If NODE is of form foo.bar.baz, return component nodes as a list. |
|
6909 Otherwise returns nil. Element-gets can be treated as property-gets |
|
6910 if the index expression is a name, a string, or a positive integer." |
|
6911 (let (left right head) |
|
6912 (cond |
|
6913 ((or (js2-name-node-p node) |
|
6914 (js2-this-node-p node)) |
|
6915 (list node)) |
|
6916 ;; foo.bar.baz is parenthesized as (foo.bar).baz => right operand is a leaf |
|
6917 ((js2-prop-get-node-p node) ; includes elem-get nodes |
|
6918 (setq left (js2-prop-get-node-left node) |
|
6919 right (js2-prop-get-node-right node)) |
|
6920 (if (and (or (js2-prop-get-node-p left) ; left == foo.bar |
|
6921 (js2-name-node-p left) |
|
6922 (js2-this-node-p left)) ; or left == foo |
|
6923 (or (js2-name-node-p right) ; .bar |
|
6924 (js2-string-node-p right) ; ['bar'] |
|
6925 (and (js2-number-node-p right) ; [10] |
|
6926 (string-match "^[0-9]+$" |
|
6927 (js2-number-node-value right))))) |
|
6928 (if (setq head (js2-compute-nested-prop-get left)) |
|
6929 (nconc head (list right)))))))) |
|
6930 |
|
6931 (defun js2-record-object-literal (node qname) |
|
6932 "Recursively process an object literal looking for functions. |
|
6933 NODE is an object literal that is the right-hand child of an assignment |
|
6934 expression. QNAME is a list of nodes representing the assignment target, |
|
6935 e.g. for foo.bar.baz = {...}, QNAME is (foo-node bar-node baz-node). |
|
6936 We do a depth-first traversal of NODE. Any functions we find are prefixed |
|
6937 with QNAME plus the property name of the function and appended to the |
|
6938 variable `js2-imenu-recorder'." |
|
6939 ;; Elements are relative to parent position, which is still absolute, |
|
6940 ;; since the parser passes the assignment target and value expressions |
|
6941 ;; to us before they are added as children of the assignment node. |
|
6942 (let ((pos (js2-node-pos node)) |
|
6943 left right) |
|
6944 (dolist (e (js2-object-node-elems node)) ; e is a `js2-object-prop-node' |
|
6945 (setq left (js2-infix-node-left e)) |
|
6946 (cond |
|
6947 ;; foo: function() {...} |
|
6948 ((js2-function-node-p (setq right (js2-infix-node-right e))) |
|
6949 (when (js2-prop-node-name left) |
|
6950 ;; As a policy decision, we record the position of the property, |
|
6951 ;; not the position of the `function' keyword, since the property |
|
6952 ;; is effectively the name of the function. |
|
6953 (push (append qname (list left) (list (+ pos (js2-node-pos e)))) |
|
6954 js2-imenu-recorder) |
|
6955 (js2-record-function-qname right qname))) |
|
6956 ;; foo: {object-literal} -- add foo to qname and recurse |
|
6957 ((js2-object-node-p right) |
|
6958 (js2-record-object-literal right |
|
6959 (append qname (list (js2-infix-node-left e))))))))) |
|
6960 |
|
6961 (defsubst js2-node-top-level-decl-p (node) |
|
6962 "Return t if NODE's name is defined in the top-level scope. |
|
6963 Also returns t if NODE's name is not defined in any scope, since it implies |
|
6964 that it's an external variable, which must also be in the top-level scope." |
|
6965 (let* ((name (js2-prop-node-name node)) |
|
6966 (this-scope (js2-node-get-enclosing-scope node)) |
|
6967 defining-scope) |
|
6968 (cond |
|
6969 ((js2-this-node-p node) |
|
6970 nil) |
|
6971 ((null this-scope) |
|
6972 t) |
|
6973 ((setq defining-scope (js2-get-defining-scope this-scope name)) |
|
6974 (js2-ast-root-p defining-scope)) |
|
6975 (t t)))) |
|
6976 |
|
6977 (defun js2-browse-postprocess-chains (chains) |
|
6978 "Modify function-declaration name chains after parsing finishes. |
|
6979 Some of the information is only available after the parse tree is complete. |
|
6980 For instance, following a 'this' reference requires a parent function node." |
|
6981 (let (result head fn parent-chain p elem) |
|
6982 (dolist (chain chains) |
|
6983 ;; examine the head of each node to get its defining scope |
|
6984 (setq head (car chain)) |
|
6985 (cond |
|
6986 ;; if top-level/external, keep as-is |
|
6987 ((js2-node-top-level-decl-p head) |
|
6988 (push chain result)) |
|
6989 ;; check for a this-reference |
|
6990 ((eq (js2-node-type head) js2-THIS) |
|
6991 (setq fn (js2-node-parent-script-or-fn head)) |
|
6992 ;; if there is no parent function, or if the parent function |
|
6993 ;; is nested, discard the head node and keep the rest of the chain. |
|
6994 (if (or (null fn) (js2-nested-function-p fn)) |
|
6995 (push (cdr chain) result) |
|
6996 ;; else look up parent in function-map. If not found, discard chain. |
|
6997 (when (setq parent-chain (and js2-imenu-function-map |
|
6998 (gethash fn js2-imenu-function-map))) |
|
6999 ;; else discard head node and prefix parent fn qname, which is |
|
7000 ;; the parent-chain sans tail, to this chain. |
|
7001 (push (append (butlast parent-chain) (cdr chain)) result)))))) |
|
7002 ;; finally replace each node in each chain with its name. |
|
7003 (dolist (chain result) |
|
7004 (setq p chain) |
|
7005 (while p |
|
7006 (if (js2-node-p (setq elem (car p))) |
|
7007 (setcar p (js2-node-qname-component elem))) |
|
7008 (setq p (cdr p)))) |
|
7009 result)) |
|
7010 |
|
7011 ;; Merge name chains into a trie-like tree structure of nested lists. |
|
7012 ;; To simplify construction of the trie, we first build it out using the rule |
|
7013 ;; that the trie consists of lists of pairs. Each pair is a 2-element array: |
|
7014 ;; [key, num-or-list]. The second element can be a number; if so, this key |
|
7015 ;; is a leaf-node with only one value. (I.e. there is only one declaration |
|
7016 ;; associated with the key at this level.) Otherwise the second element is |
|
7017 ;; a list of pairs, with the rule applied recursively. This symmetry permits |
|
7018 ;; a simple recursive formulation. |
|
7019 ;; |
|
7020 ;; js2-mode is building the data structure for imenu. The imenu documentation |
|
7021 ;; claims that it's the structure above, but in practice it wants the children |
|
7022 ;; at the same list level as the key for that level, which is how I've drawn |
|
7023 ;; the "Expected final result" above. We'll postprocess the trie to remove the |
|
7024 ;; list wrapper around the children at each level. |
|
7025 ;; |
|
7026 ;; A completed nested imenu-alist entry looks like this: |
|
7027 ;; '(("foo" |
|
7028 ;; ("<definition>" . 7) |
|
7029 ;; ("bar" |
|
7030 ;; ("a" . 40) |
|
7031 ;; ("b" . 60)))) |
|
7032 ;; |
|
7033 ;; In particular, the documentation for `imenu--index-alist' says that |
|
7034 ;; a nested sub-alist element looks like (INDEX-NAME SUB-ALIST). |
|
7035 ;; The sub-alist entries immediately follow INDEX-NAME, the head of the list. |
|
7036 |
|
7037 (defsubst js2-treeify (lst) |
|
7038 "Convert (a b c d) to (a ((b ((c d)))))" |
|
7039 (if (null (cddr lst)) ; list length <= 2 |
|
7040 lst |
|
7041 (list (car lst) (list (js2-treeify (cdr lst)))))) |
|
7042 |
|
7043 (defun js2-build-alist-trie (chains trie) |
|
7044 "Merge declaration name chains into a trie-like alist structure for imenu. |
|
7045 CHAINS is the qname chain list produced during parsing. TRIE is a |
|
7046 list of elements built up so far." |
|
7047 (let (head tail pos branch kids) |
|
7048 (dolist (chain chains) |
|
7049 (setq head (car chain) |
|
7050 tail (cdr chain) |
|
7051 pos (if (numberp (car tail)) (car tail)) |
|
7052 branch (js2-find-if (lambda (n) |
|
7053 (string= (car n) head)) |
|
7054 trie) |
|
7055 kids (second branch)) |
|
7056 (cond |
|
7057 ;; case 1: this key isn't in the trie yet |
|
7058 ((null branch) |
|
7059 (if trie |
|
7060 (setcdr (last trie) (list (js2-treeify chain))) |
|
7061 (setq trie (list (js2-treeify chain))))) |
|
7062 |
|
7063 ;; case 2: key is present with a single number entry: replace w/ list |
|
7064 ;; ("a1" 10) + ("a1" 20) => ("a1" (("<definition>" 10) |
|
7065 ;; ("<definition>" 20))) |
|
7066 ((numberp kids) |
|
7067 (setcar (cdr branch) |
|
7068 (list (list "<definition-1>" kids) |
|
7069 (if pos |
|
7070 (list "<definition-2>" pos) |
|
7071 (js2-treeify tail))))) |
|
7072 |
|
7073 ;; case 3: key is there (with kids), and we're a number entry |
|
7074 (pos |
|
7075 (setcdr (last kids) |
|
7076 (list |
|
7077 (list (format "<definition-%d>" |
|
7078 (1+ (loop for kid in kids |
|
7079 count (eq ?< (aref (car kid) 0))))) |
|
7080 pos)))) |
|
7081 |
|
7082 ;; case 4: key is there with kids, need to merge in our chain |
|
7083 (t |
|
7084 (js2-build-alist-trie (list tail) kids)))) |
|
7085 trie)) |
|
7086 |
|
7087 (defun js2-flatten-trie (trie) |
|
7088 "Convert TRIE to imenu-format. |
|
7089 Recurses through nodes, and for each one whose second element is a list, |
|
7090 appends the list's flattened elements to the current element. Also |
|
7091 changes the tails into conses. For instance, this pre-flattened trie |
|
7092 |
|
7093 '(a ((b 20) |
|
7094 (c ((d 30) |
|
7095 (e 40))))) |
|
7096 |
|
7097 becomes |
|
7098 |
|
7099 '(a (b . 20) |
|
7100 (c (d . 30) |
|
7101 (e . 40))) |
|
7102 |
|
7103 Note that the root of the trie has no key, just a list of chains. |
|
7104 This is also true for the value of any key with multiple children, |
|
7105 e.g. key 'c' in the example above." |
|
7106 (cond |
|
7107 ((listp (car trie)) |
|
7108 (mapcar #'js2-flatten-trie trie)) |
|
7109 (t |
|
7110 (if (numberp (second trie)) |
|
7111 (cons (car trie) (second trie)) |
|
7112 ;; else pop list and append its kids |
|
7113 (apply #'append (list (car trie)) (js2-flatten-trie (cdr trie))))))) |
|
7114 |
|
7115 (defun js2-build-imenu-index () |
|
7116 "Turn `js2-imenu-recorder' into an imenu data structure." |
|
7117 (unless (eq js2-imenu-recorder 'empty) |
|
7118 (let* ((chains (js2-browse-postprocess-chains js2-imenu-recorder)) |
|
7119 (result (js2-build-alist-trie chains nil))) |
|
7120 (js2-flatten-trie result)))) |
|
7121 |
|
7122 (defun js2-test-print-chains (chains) |
|
7123 "Print a list of qname chains. |
|
7124 Each element of CHAINS is a list of the form (NODE [NODE *] pos); |
|
7125 i.e. one or more nodes, and an integer position as the list tail." |
|
7126 (mapconcat (lambda (chain) |
|
7127 (concat "(" |
|
7128 (mapconcat (lambda (elem) |
|
7129 (if (js2-node-p elem) |
|
7130 (or (js2-node-qname-component elem) |
|
7131 "nil") |
|
7132 (number-to-string elem))) |
|
7133 chain |
|
7134 " ") |
|
7135 ")")) |
|
7136 chains |
|
7137 "\n")) |
|
7138 |
|
7139 |
|
7140 (provide 'js2-browse) |
|
7141 |
|
7142 ;;; js2-browse.el ends here |
|
7143 ;;; js2-parse.el --- JavaScript parser |
|
7144 |
|
7145 ;; Author: Steve Yegge (steve.yegge@gmail.com) |
|
7146 ;; Keywords: javascript languages |
|
7147 |
|
7148 ;; Commentary: |
|
7149 |
|
7150 ;; This is based on Rhino's parser and tries to follow its code |
|
7151 ;; structure as closely as practical, so that changes to the Rhino |
|
7152 ;; parser can easily be propagated into this code. However, Rhino |
|
7153 ;; does not currently generate a usable AST representation, at least |
|
7154 ;; from an IDE perspective, so we build our own more suitable AST. |
|
7155 |
|
7156 ;; The AST node structures are defined in `js2-ast.el'. |
|
7157 ;; Every parser function that creates and returns an AST node has |
|
7158 ;; the following responsibilities: |
|
7159 |
|
7160 ;; 1) set the node start to the absolute buffer start position |
|
7161 ;; 2) set the node length to include any closing chars (RC, SEMI) |
|
7162 ;; 3) fix up any child-node starts to be relative to this node |
|
7163 ;; 4) set any field positions (e.g. keywords) relative to this node |
|
7164 ;; 5) report any child nodes with `js2-node-add-children' |
|
7165 ;; (note that this call fixes up start positions by default) |
|
7166 |
|
7167 ;; The resulting AST has all node start positions relative to the |
|
7168 ;; parent nodes; only the root has an absolute start position. |
|
7169 |
|
7170 ;; Note: fontification is done inline while parsing. It used to be |
|
7171 ;; done in a second pass over the AST, but doing it inline is about |
|
7172 ;; twice as fast. Most of the fontification happens when tokens are |
|
7173 ;; scanned, and the parser has a few spots that perform extra |
|
7174 ;; fontification. In addition to speed, a second benefit of inline |
|
7175 ;; parsing is that if a long parse is interrupted, everything parsed |
|
7176 ;; so far is still fontified. |
|
7177 |
|
7178 ;; The editing mode that uses this parser, `js2-mode', directs the |
|
7179 ;; parser to check periodically for user input. If user input |
|
7180 ;; arrives, the parse is abandoned, except for the highlighting that |
|
7181 ;; has occurred so far, and a re-parse is rescheduled for when Emacs |
|
7182 ;; becomes idle again. This works pretty well, but could be better. |
|
7183 ;; In particular, when the user input has not resulted in changes to |
|
7184 ;; the buffer (for instance, navigation input), the parse tree built |
|
7185 ;; so far should not be discarded, and the parse should continue where |
|
7186 ;; it left off. It will be some work to create what amounts to a |
|
7187 ;; continuation, but it should not be unreasonably difficult. |
|
7188 |
|
7189 ;; TODO: |
|
7190 ;; - make non-editing input restart parse at previous continuation |
|
7191 ;; - in Eclipse, sibling nodes never overlap start/end ranges |
|
7192 ;; - for getters, prop name and function nodes overlap |
|
7193 ;; - should write a debug tree visitor to look for overlaps |
|
7194 ;; - mark array and object literals as "destructuring" (node prop?) |
|
7195 ;; so we can syntax-highlight them properly. |
|
7196 ;; - figure out a way not to store value in string/name nodes |
|
7197 ;; - needs a solution for synthetic nodes |
|
7198 |
|
7199 ;;; Code |
|
7200 |
|
7201 (eval-and-compile |
|
7202 (require 'cl)) ; for delete-if |
|
7203 |
|
7204 |
|
7205 (defconst js2-version "1.7.0" |
|
7206 "Version of JavaScript supported, plus minor js2 version.") |
|
7207 |
|
7208 (defmacro js2-record-face (face) |
|
7209 "Record a style run of FACE for the current token." |
|
7210 `(js2-set-face js2-token-beg js2-token-end ,face 'record)) |
|
7211 |
|
7212 (defsubst js2-node-end (n) |
|
7213 "Computes the absolute end of node N. |
|
7214 Use with caution! Assumes `js2-node-pos' is -absolute-, which |
|
7215 is only true until the node is added to its parent; i.e., while parsing." |
|
7216 (+ (js2-node-pos n) |
|
7217 (js2-node-len n))) |
|
7218 |
|
7219 (defsubst js2-record-comment () |
|
7220 (push (make-js2-comment-node :len (- js2-token-end js2-token-beg) |
|
7221 :format js2-ts-comment-type) |
|
7222 js2-scanned-comments) |
|
7223 (when js2-parse-ide-mode |
|
7224 (js2-record-face 'font-lock-comment-face) |
|
7225 (when (memq js2-ts-comment-type '(html preprocessor)) |
|
7226 ;; Tell cc-engine the bounds of the comment. |
|
7227 (put-text-property js2-token-beg (1- js2-token-end) 'c-in-sws t)))) |
|
7228 |
|
7229 ;; This function is called depressingly often, so it should be fast. |
|
7230 ;; Most of the time it's looking at the same token it peeked before. |
|
7231 (defsubst js2-peek-token () |
|
7232 "Returns the next token without consuming it. |
|
7233 If previous token was consumed, calls scanner to get new token. |
|
7234 If previous token was -not- consumed, returns it (idempotent). |
|
7235 |
|
7236 This function will not return a newline (js2-EOL) - instead, it |
|
7237 gobbles newlines until it finds a non-newline token, and flags |
|
7238 that token as appearing just after a newline. |
|
7239 |
|
7240 This function will also not return a js2-COMMENT. Instead, it |
|
7241 records comments found in `js2-scanned-comments'. If the token |
|
7242 returned by this function immediately follows a jsdoc comment, |
|
7243 the token is flagged as such. |
|
7244 |
|
7245 Note that this function always returned the un-flagged token! |
|
7246 The flags, if any, are saved in `js2-current-flagged-token'." |
|
7247 (if (/= js2-current-flagged-token js2-EOF) ; last token not consumed |
|
7248 js2-current-token ; most common case - return already-peeked token |
|
7249 (let ((tt (js2-get-token)) ; call scanner |
|
7250 saw-eol |
|
7251 face) |
|
7252 ;; process comments and whitespace |
|
7253 (while (or (= tt js2-EOL) |
|
7254 (= tt js2-COMMENT)) |
|
7255 (if (= tt js2-EOL) |
|
7256 (setq saw-eol t) |
|
7257 (setq saw-eol nil) |
|
7258 (if js2-record-comments |
|
7259 (js2-record-comment))) |
|
7260 (setq tt (js2-get-token))) ; call scanner |
|
7261 |
|
7262 (setq js2-current-token tt |
|
7263 js2-current-flagged-token (if saw-eol |
|
7264 (logior tt js2-ti-after-eol) |
|
7265 tt)) |
|
7266 ;; perform lexical fontification as soon as token is scanned |
|
7267 (when js2-parse-ide-mode |
|
7268 (cond |
|
7269 ((minusp tt) |
|
7270 (js2-record-face 'js2-error-face)) |
|
7271 ((setq face (aref js2-kwd-tokens tt)) |
|
7272 (js2-record-face face)) |
|
7273 ((and (= tt js2-NAME) |
|
7274 (equal js2-ts-string "undefined")) |
|
7275 (js2-record-face 'font-lock-constant-face)))) |
|
7276 tt))) ; return unflagged token |
|
7277 |
|
7278 (defsubst js2-peek-flagged-token () |
|
7279 "Returns the current token along with any flags set for it." |
|
7280 (js2-peek-token) |
|
7281 js2-current-flagged-token) |
|
7282 |
|
7283 (defsubst js2-consume-token () |
|
7284 (setq js2-current-flagged-token js2-EOF)) |
|
7285 |
|
7286 (defsubst js2-next-token () |
|
7287 (prog1 |
|
7288 (js2-peek-token) |
|
7289 (js2-consume-token))) |
|
7290 |
|
7291 (defsubst js2-next-flagged-token () |
|
7292 (js2-peek-token) |
|
7293 (prog1 js2-current-flagged-token |
|
7294 (js2-consume-token))) |
|
7295 |
|
7296 (defsubst js2-match-token (match) |
|
7297 "Consume and return t if next token matches MATCH, a bytecode. |
|
7298 Returns nil and consumes nothing if MATCH is not the next token." |
|
7299 (if (/= (js2-peek-token) match) |
|
7300 nil |
|
7301 (js2-consume-token) |
|
7302 t)) |
|
7303 |
|
7304 (defsubst js2-valid-prop-name-token (tt) |
|
7305 (or (= tt js2-NAME) |
|
7306 (and js2-allow-keywords-as-property-names |
|
7307 (plusp tt) |
|
7308 (aref js2-kwd-tokens tt)))) |
|
7309 |
|
7310 (defsubst js2-match-prop-name () |
|
7311 "Consume token and return t if next token is a valid property name. |
|
7312 It's valid if it's a js2-NAME, or `js2-allow-keywords-as-property-names' |
|
7313 is non-nil and it's a keyword token." |
|
7314 (if (js2-valid-prop-name-token (js2-peek-token)) |
|
7315 (progn |
|
7316 (js2-consume-token) |
|
7317 t) |
|
7318 nil)) |
|
7319 |
|
7320 (defsubst js2-must-match-prop-name (msg-id &optional pos len) |
|
7321 (if (js2-match-prop-name) |
|
7322 t |
|
7323 (js2-report-error msg-id nil pos len) |
|
7324 nil)) |
|
7325 |
|
7326 (defsubst js2-peek-token-or-eol () |
|
7327 "Return js2-EOL if the current token immediately follows a newline. |
|
7328 Else returns the current token. Used in situations where we don't |
|
7329 consider certain token types valid if they are preceded by a newline. |
|
7330 One example is the postfix ++ or -- operator, which has to be on the |
|
7331 same line as its operand." |
|
7332 (let ((tt (js2-peek-token))) |
|
7333 ;; Check for last peeked token flags |
|
7334 (if (js2-flag-set-p js2-current-flagged-token js2-ti-after-eol) |
|
7335 js2-EOL |
|
7336 tt))) |
|
7337 |
|
7338 (defsubst js2-set-check-for-label () |
|
7339 (assert (= (logand js2-current-flagged-token js2-clear-ti-mask) js2-NAME)) |
|
7340 (js2-set-flag js2-current-flagged-token js2-ti-check-label)) |
|
7341 |
|
7342 (defsubst js2-must-match (token msg-id &optional pos len) |
|
7343 "Match next token to token code TOKEN, or record a syntax error. |
|
7344 MSG-ID is the error message to report if the match fails. |
|
7345 Returns t on match, nil if no match." |
|
7346 (if (js2-match-token token) |
|
7347 t |
|
7348 (js2-report-error msg-id nil pos len) |
|
7349 nil)) |
|
7350 |
|
7351 (defsubst js2-inside-function () |
|
7352 (plusp js2-nesting-of-function)) |
|
7353 |
|
7354 (defsubst js2-set-requires-activation () |
|
7355 (if (js2-function-node-p js2-current-script-or-fn) |
|
7356 (setf (js2-function-node-needs-activation js2-current-script-or-fn) t))) |
|
7357 |
|
7358 (defsubst js2-check-activation-name (name token) |
|
7359 (when (js2-inside-function) |
|
7360 ;; skip language-version 1.2 check from Rhino |
|
7361 (if (or (string= "arguments" name) |
|
7362 (and js2-compiler-activation-names ; only used in codegen |
|
7363 (gethash name js2-compiler-activation-names))) |
|
7364 (js2-set-requires-activation)))) |
|
7365 |
|
7366 (defsubst js2-set-is-generator () |
|
7367 (if (js2-function-node-p js2-current-script-or-fn) |
|
7368 (setf (js2-function-node-is-generator js2-current-script-or-fn) t))) |
|
7369 |
|
7370 (defsubst js2-must-have-xml () |
|
7371 (unless js2-compiler-xml-available |
|
7372 (js2-report-error "msg.XML.not.available"))) |
|
7373 |
|
7374 (defsubst js2-push-scope (scope) |
|
7375 "Push SCOPE, a `js2-scope', onto the lexical scope chain." |
|
7376 (assert (js2-scope-p scope)) |
|
7377 (assert (null (js2-scope-parent-scope scope))) |
|
7378 (assert (neq js2-current-scope scope)) |
|
7379 (setf (js2-scope-parent-scope scope) js2-current-scope |
|
7380 js2-current-scope scope)) |
|
7381 |
|
7382 (defsubst js2-pop-scope () |
|
7383 (setq js2-current-scope |
|
7384 (js2-scope-parent-scope js2-current-scope))) |
|
7385 |
|
7386 (defsubst js2-enter-loop (loop-node) |
|
7387 (push loop-node js2-loop-set) |
|
7388 (push loop-node js2-loop-and-switch-set) |
|
7389 (js2-push-scope loop-node) |
|
7390 ;; Tell the current labeled statement (if any) its statement, |
|
7391 ;; and set the jump target of the first label to the loop. |
|
7392 ;; These are used in `js2-parse-continue' to verify that the |
|
7393 ;; continue target is an actual labeled loop. (And for codegen.) |
|
7394 (when js2-labeled-stmt |
|
7395 (setf (js2-labeled-stmt-node-stmt js2-labeled-stmt) loop-node |
|
7396 (js2-label-node-loop (car (js2-labeled-stmt-node-labels |
|
7397 js2-labeled-stmt))) loop-node))) |
|
7398 |
|
7399 (defsubst js2-exit-loop () |
|
7400 (pop js2-loop-set) |
|
7401 (pop js2-loop-and-switch-set) |
|
7402 (js2-pop-scope)) |
|
7403 |
|
7404 (defsubst js2-enter-switch (switch-node) |
|
7405 (push switch-node js2-loop-and-switch-set)) |
|
7406 |
|
7407 (defsubst js2-exit-switch () |
|
7408 (pop js2-loop-and-switch-set)) |
|
7409 |
|
7410 (defun js2-parse (&optional buf cb) |
|
7411 "Tells the js2 parser to parse a region of JavaScript. |
|
7412 |
|
7413 BUF is a buffer or buffer name containing the code to parse. |
|
7414 Call `narrow-to-region' first to parse only part of the buffer. |
|
7415 |
|
7416 The returned AST root node is given some additional properties: |
|
7417 `node-count' - total number of nodes in the AST |
|
7418 `buffer' - BUF. The buffer it refers to may change or be killed, |
|
7419 so the value is not necessarily reliable. |
|
7420 |
|
7421 An optional callback CB can be specified to report parsing |
|
7422 progress. If `(functionp CB)' returns t, it will be called with |
|
7423 the current line number once before parsing begins, then again |
|
7424 each time the lexer reaches a new line number. |
|
7425 |
|
7426 CB can also be a list of the form `(symbol cb ...)' to specify |
|
7427 multiple callbacks with different criteria. Each symbol is a |
|
7428 criterion keyword, and the following element is the callback to |
|
7429 call |
|
7430 |
|
7431 :line - called whenever the line number changes |
|
7432 :token - called for each new token consumed |
|
7433 |
|
7434 The list of criteria could be extended to include entering or |
|
7435 leaving a statement, an expression, or a function definition." |
|
7436 (if (and cb (not (functionp cb))) |
|
7437 (error "criteria callbacks not yet implemented")) |
|
7438 (let ((inhibit-point-motion-hooks t) |
|
7439 (js2-compiler-xml-available (>= js2-language-version 160)) |
|
7440 ;; This is a recursive-descent parser, so give it a big stack. |
|
7441 (max-lisp-eval-depth (max max-lisp-eval-depth 3000)) |
|
7442 (max-specpdl-size (max max-specpdl-size 3000)) |
|
7443 (case-fold-search nil) |
|
7444 ast) |
|
7445 (or buf (setq buf (current-buffer))) |
|
7446 (save-excursion |
|
7447 (set-buffer buf) |
|
7448 (setq js2-scanned-comments nil |
|
7449 js2-parsed-errors nil |
|
7450 js2-parsed-warnings nil |
|
7451 js2-imenu-recorder nil |
|
7452 js2-imenu-function-map nil |
|
7453 js2-label-set nil) |
|
7454 (js2-init-scanner) |
|
7455 (setq ast (js2-with-unmodifying-text-property-changes |
|
7456 (js2-do-parse))) |
|
7457 (unless js2-ts-hit-eof |
|
7458 (js2-report-error "msg.got.syntax.errors" (length js2-parsed-errors))) |
|
7459 (setf (js2-ast-root-errors ast) js2-parsed-errors |
|
7460 (js2-ast-root-warnings ast) js2-parsed-warnings) |
|
7461 ;; if we didn't find any declarations, put a dummy in this list so we |
|
7462 ;; don't end up re-parsing the buffer in `js2-mode-create-imenu-index' |
|
7463 (unless js2-imenu-recorder |
|
7464 (setq js2-imenu-recorder 'empty)) |
|
7465 (run-hooks 'js2-parse-finished-hook) |
|
7466 ast))) |
|
7467 |
|
7468 ;; Corresponds to Rhino's Parser.parse() method. |
|
7469 (defun js2-do-parse () |
|
7470 "Parse current buffer starting from current point. |
|
7471 Scanner should be initialized." |
|
7472 (let ((pos js2-ts-cursor) |
|
7473 (end js2-ts-cursor) ; in case file is empty |
|
7474 root n tt) |
|
7475 ;; initialize buffer-local parsing vars |
|
7476 (setf root (make-js2-ast-root :buffer (buffer-name) :pos pos) |
|
7477 js2-current-script-or-fn root |
|
7478 js2-current-scope root |
|
7479 js2-current-flagged-token js2-EOF |
|
7480 js2-nesting-of-function 0 |
|
7481 js2-labeled-stmt nil |
|
7482 js2-recorded-assignments nil) ; for js2-highlight |
|
7483 |
|
7484 (while (/= (setq tt (js2-peek-token)) js2-EOF) |
|
7485 (if (= tt js2-FUNCTION) |
|
7486 (progn |
|
7487 (js2-consume-token) |
|
7488 (setq n (js2-parse-function (if js2-called-by-compile-function |
|
7489 'FUNCTION_EXPRESSION |
|
7490 'FUNCTION_STATEMENT))) |
|
7491 (js2-record-imenu-functions n)) |
|
7492 ;; not a function - parse a statement |
|
7493 (setq n (js2-parse-statement))) |
|
7494 ;; add function or statement to script |
|
7495 (setq end (js2-node-end n)) |
|
7496 (js2-block-node-push root n)) |
|
7497 |
|
7498 ;; add comments to root in lexical order |
|
7499 (when js2-scanned-comments |
|
7500 ;; if we find a comment beyond end of normal kids, use its end |
|
7501 (setq end (max end (js2-node-end (first js2-scanned-comments)))) |
|
7502 (dolist (comment js2-scanned-comments) |
|
7503 (push comment (js2-ast-root-comments root)) |
|
7504 (js2-node-add-children root comment))) |
|
7505 |
|
7506 (setf (js2-node-len root) (- end pos)) |
|
7507 (js2-highlight-undeclared-vars) |
|
7508 root)) |
|
7509 |
|
7510 (defun js2-function-parser () |
|
7511 (js2-consume-token) |
|
7512 (js2-parse-function 'FUNCTION_EXPRESSION_STATEMENT)) |
|
7513 |
|
7514 (defun js2-parse-function-body (fn-node) |
|
7515 (js2-must-match js2-LC "msg.no.brace.body") |
|
7516 (let ((pos js2-token-beg) ; LC position |
|
7517 (pn (make-js2-block-node)) ; starts at LC position |
|
7518 tt |
|
7519 end) |
|
7520 (incf js2-nesting-of-function) |
|
7521 (unwind-protect |
|
7522 (while (not (or (= (setq tt (js2-peek-token)) js2-ERROR) |
|
7523 (= tt js2-EOF) |
|
7524 (= tt js2-RC))) |
|
7525 (js2-block-node-push pn (if (/= tt js2-FUNCTION) |
|
7526 (js2-parse-statement) |
|
7527 (js2-consume-token) |
|
7528 (js2-parse-function 'FUNCTION_STATEMENT)))) |
|
7529 (decf js2-nesting-of-function)) |
|
7530 (setq end js2-token-end) ; assume no curly and leave at current token |
|
7531 (if (js2-must-match js2-RC "msg.no.brace.after.body" pos) |
|
7532 (setq end js2-token-end)) |
|
7533 (setf (js2-node-pos pn) pos |
|
7534 (js2-node-len pn) (- end pos)) |
|
7535 (setf (js2-function-node-body fn-node) pn) |
|
7536 (js2-node-add-children fn-node pn) |
|
7537 pn)) |
|
7538 |
|
7539 (defun js2-parse-function-params (fn-node pos) |
|
7540 (if (js2-match-token js2-RP) |
|
7541 (setf (js2-function-node-rp fn-node) (- js2-token-beg pos)) |
|
7542 (let (params len param) |
|
7543 (loop for tt = (js2-peek-token) |
|
7544 do |
|
7545 (cond |
|
7546 ;; destructuring param |
|
7547 ((or (= tt js2-LB) (= tt js2-LC)) |
|
7548 (push (js2-parse-primary-expr) params)) |
|
7549 ;; simple name |
|
7550 (t |
|
7551 (js2-must-match js2-NAME "msg.no.parm") |
|
7552 (js2-record-face 'js2-function-param-face) |
|
7553 (setq param (js2-create-name-node)) |
|
7554 (js2-define-symbol js2-LP js2-ts-string param) |
|
7555 (push param params))) |
|
7556 while |
|
7557 (js2-match-token js2-COMMA)) |
|
7558 (if (js2-must-match js2-RP "msg.no.paren.after.parms") |
|
7559 (setf (js2-function-node-rp fn-node) (- js2-token-beg pos))) |
|
7560 (dolist (p params) |
|
7561 (js2-node-add-children fn-node p) |
|
7562 (push p (js2-function-node-params fn-node)))))) |
|
7563 |
|
7564 (defsubst js2-check-inconsistent-return-warning (fn-node name) |
|
7565 "Possibly show inconsistent-return warning. |
|
7566 Last token scanned is the close-curly for the function body." |
|
7567 (when (and js2-mode-show-strict-warnings |
|
7568 js2-strict-inconsistent-return-warning |
|
7569 (not (js2-has-consistent-return-usage |
|
7570 (js2-function-node-body fn-node)))) |
|
7571 ;; Have it extend from close-curly to bol or beginning of block. |
|
7572 (let ((pos (save-excursion |
|
7573 (goto-char js2-token-end) |
|
7574 (max (js2-node-abs-pos (js2-function-node-body fn-node)) |
|
7575 (point-at-bol)))) |
|
7576 (end js2-token-end)) |
|
7577 (if (plusp (js2-name-node-length name)) |
|
7578 (js2-add-strict-warning "msg.no.return.value" |
|
7579 (js2-name-node-name name) pos end) |
|
7580 (js2-add-strict-warning "msg.anon.no.return.value" nil pos end))))) |
|
7581 |
|
7582 (defun js2-parse-function (function-type) |
|
7583 "Function parser. FUNCTION-TYPE is a symbol." |
|
7584 (let ((pos js2-token-beg) ; start of 'function' keyword |
|
7585 name |
|
7586 name-beg |
|
7587 name-end |
|
7588 fn-node |
|
7589 lp |
|
7590 (synthetic-type function-type) |
|
7591 member-expr-node) |
|
7592 |
|
7593 ;; parse function name, expression, or non-name (anonymous) |
|
7594 (cond |
|
7595 ;; function foo(...) |
|
7596 ((js2-match-token js2-NAME) |
|
7597 (setq name (js2-create-name-node t) |
|
7598 name-beg js2-token-beg |
|
7599 name-end js2-token-end) |
|
7600 (unless (js2-match-token js2-LP) |
|
7601 (when js2-allow-member-expr-as-function-name |
|
7602 ;; function foo.bar(...) |
|
7603 (setq member-expr-node name |
|
7604 name nil |
|
7605 member-expr-node (js2-parse-member-expr-tail |
|
7606 nil member-expr-node))) |
|
7607 (js2-must-match js2-LP "msg.no.paren.parms"))) |
|
7608 |
|
7609 ((js2-match-token js2-LP) |
|
7610 nil) ; anonymous function: leave name as null |
|
7611 |
|
7612 (t |
|
7613 ;; function random-member-expr(...) |
|
7614 (when js2-allow-member-expr-as-function-name |
|
7615 ;; Note that memberExpr can not start with '(' like |
|
7616 ;; in function (1+2).toString(), because 'function (' already |
|
7617 ;; processed as anonymous function |
|
7618 (setq member-expr-node (js2-parse-member-expr))) |
|
7619 (js2-must-match js2-LP "msg.no.paren.parms"))) |
|
7620 |
|
7621 (if (= js2-current-token js2-LP) ; eventually matched LP? |
|
7622 (setq lp js2-token-beg)) |
|
7623 |
|
7624 (if member-expr-node |
|
7625 (progn |
|
7626 (setq synthetic-type 'FUNCTION_EXPRESSION) |
|
7627 (js2-parse-highlight-member-expr-fn-name member-expr-node)) |
|
7628 (if name |
|
7629 (js2-set-face name-beg name-end |
|
7630 'font-lock-function-name-face 'record))) |
|
7631 |
|
7632 (if (and (neq synthetic-type 'FUNCTION_EXPRESSION) |
|
7633 (plusp (js2-name-node-length name))) |
|
7634 ;; Function statements define a symbol in the enclosing scope |
|
7635 (js2-define-symbol js2-FUNCTION (js2-name-node-name name) fn-node)) |
|
7636 |
|
7637 (setf fn-node (make-js2-function-node :pos pos |
|
7638 :name name |
|
7639 :form function-type |
|
7640 :lp (if lp (- lp pos)))) |
|
7641 |
|
7642 (if (or (js2-inside-function) (plusp js2-nesting-of-with)) |
|
7643 ;; 1. Nested functions are not affected by the dynamic scope flag |
|
7644 ;; as dynamic scope is already a parent of their scope. |
|
7645 ;; 2. Functions defined under the with statement also immune to |
|
7646 ;; this setup, in which case dynamic scope is ignored in favor |
|
7647 ;; of the with object. |
|
7648 (setf (js2-function-node-ignore-dynamic fn-node) t)) |
|
7649 |
|
7650 ;; dynamically bind all the per-function variables |
|
7651 (let ((js2-current-script-or-fn fn-node) |
|
7652 (js2-current-scope fn-node) |
|
7653 (js2-nesting-of-with 0) |
|
7654 (js2-end-flags 0) |
|
7655 js2-label-set |
|
7656 js2-loop-set |
|
7657 js2-loop-and-switch-set) |
|
7658 |
|
7659 ;; parse params and function body |
|
7660 (js2-parse-function-params fn-node pos) |
|
7661 (js2-parse-function-body fn-node) |
|
7662 (if name |
|
7663 (js2-node-add-children fn-node name)) |
|
7664 |
|
7665 (js2-check-inconsistent-return-warning fn-node name) |
|
7666 |
|
7667 ;; Function expressions define a name only in the body of the |
|
7668 ;; function, and only if not hidden by a parameter name |
|
7669 (if (and name |
|
7670 (eq synthetic-type 'FUNCTION_EXPRESSION) |
|
7671 (null (js2-scope-get-symbol js2-current-scope |
|
7672 (js2-name-node-name name)))) |
|
7673 (js2-define-symbol js2-FUNCTION |
|
7674 (js2-name-node-name name) |
|
7675 fn-node)) |
|
7676 (if (and name |
|
7677 (eq function-type 'FUNCTION_EXPRESSION_STATEMENT)) |
|
7678 (js2-record-imenu-functions fn-node))) |
|
7679 |
|
7680 (setf (js2-node-len fn-node) (- js2-ts-cursor pos) |
|
7681 (js2-function-node-member-expr fn-node) member-expr-node) ; may be nil |
|
7682 |
|
7683 ;; Rhino doesn't do this, but we need it for finding undeclared vars. |
|
7684 ;; We wait until after parsing the function to set its parent scope, |
|
7685 ;; since `js2-define-symbol' needs the defining-scope check to stop |
|
7686 ;; at the function boundary when checking for redeclarations. |
|
7687 (setf (js2-scope-parent-scope fn-node) js2-current-scope) |
|
7688 |
|
7689 fn-node)) |
|
7690 |
|
7691 (defun js2-parse-statements (&optional parent) |
|
7692 "Parse a statement list. Last token consumed must be js2-LC. |
|
7693 |
|
7694 PARENT can be a `js2-block-node', in which case the statements are |
|
7695 appended to PARENT. Otherwise a new `js2-block-node' is created |
|
7696 and returned. |
|
7697 |
|
7698 This function does not match the closing js2-RC: the caller |
|
7699 matches the RC so it can provide a suitable error message if not |
|
7700 matched. This means it's up to the caller to set the length of |
|
7701 the node to include the closing RC. The node start pos is set to |
|
7702 the absolute buffer start position, and the caller should fix it |
|
7703 up to be relative to the parent node. All children of this block |
|
7704 node are given relative start positions and correct lengths." |
|
7705 (let ((pn (or parent (make-js2-block-node))) |
|
7706 tt) |
|
7707 (setf (js2-node-pos pn) js2-token-beg) |
|
7708 (while (and (> (setq tt (js2-peek-token)) js2-EOF) |
|
7709 (/= tt js2-RC)) |
|
7710 (js2-block-node-push pn (js2-parse-statement))) |
|
7711 pn)) |
|
7712 |
|
7713 (defun js2-parse-statement () |
|
7714 (let (tt pn beg end) |
|
7715 |
|
7716 ;; coarse-grained user-interrupt check - needs work |
|
7717 (and js2-parse-interruptable-p |
|
7718 (zerop (% (incf js2-parse-stmt-count) |
|
7719 js2-statements-per-pause)) |
|
7720 (input-pending-p) |
|
7721 (throw 'interrupted t)) |
|
7722 |
|
7723 (setq pn (js2-statement-helper)) |
|
7724 |
|
7725 ;; no-side-effects warning check |
|
7726 (unless (js2-node-has-side-effects pn) |
|
7727 (setq end (js2-node-end pn)) |
|
7728 (save-excursion |
|
7729 (goto-char end) |
|
7730 (setq beg (max (js2-node-pos pn) (point-at-bol)))) |
|
7731 (js2-add-strict-warning "msg.no.side.effects" nil beg end)) |
|
7732 |
|
7733 pn)) |
|
7734 |
|
7735 ;; These correspond to the switch cases in Parser.statementHelper |
|
7736 (defconst js2-parsers |
|
7737 (let ((parsers (make-vector js2-num-tokens |
|
7738 #'js2-parse-expr-stmt))) |
|
7739 (aset parsers js2-BREAK #'js2-parse-break) |
|
7740 (aset parsers js2-CONST #'js2-parse-const-var) |
|
7741 (aset parsers js2-CONTINUE #'js2-parse-continue) |
|
7742 (aset parsers js2-DEBUGGER #'js2-parse-debugger) |
|
7743 (aset parsers js2-DEFAULT #'js2-parse-default-xml-namespace) |
|
7744 (aset parsers js2-DO #'js2-parse-do) |
|
7745 (aset parsers js2-FOR #'js2-parse-for) |
|
7746 (aset parsers js2-FUNCTION #'js2-function-parser) |
|
7747 (aset parsers js2-IF #'js2-parse-if) |
|
7748 (aset parsers js2-LC #'js2-parse-block) |
|
7749 (aset parsers js2-LET #'js2-parse-let-stmt) |
|
7750 (aset parsers js2-NAME #'js2-parse-name-or-label) |
|
7751 (aset parsers js2-RETURN #'js2-parse-ret-yield) |
|
7752 (aset parsers js2-SEMI #'js2-parse-semi) |
|
7753 (aset parsers js2-SWITCH #'js2-parse-switch) |
|
7754 (aset parsers js2-THROW #'js2-parse-throw) |
|
7755 (aset parsers js2-TRY #'js2-parse-try) |
|
7756 (aset parsers js2-VAR #'js2-parse-const-var) |
|
7757 (aset parsers js2-WHILE #'js2-parse-while) |
|
7758 (aset parsers js2-WITH #'js2-parse-with) |
|
7759 (aset parsers js2-YIELD #'js2-parse-ret-yield) |
|
7760 parsers) |
|
7761 "A vector mapping token types to parser functions.") |
|
7762 |
|
7763 (defsubst js2-parse-warn-missing-semi (beg end) |
|
7764 (and js2-mode-show-strict-warnings |
|
7765 js2-strict-missing-semi-warning |
|
7766 (js2-add-strict-warning |
|
7767 "msg.missing.semi" nil |
|
7768 ;; back up to beginning of statement or line |
|
7769 (max beg (save-excursion |
|
7770 (goto-char end) |
|
7771 (point-at-bol))) |
|
7772 end))) |
|
7773 |
|
7774 (defconst js2-no-semi-insertion |
|
7775 (list js2-IF |
|
7776 js2-SWITCH |
|
7777 js2-WHILE |
|
7778 js2-DO |
|
7779 js2-FOR |
|
7780 js2-TRY |
|
7781 js2-WITH |
|
7782 js2-LC |
|
7783 js2-ERROR |
|
7784 js2-SEMI |
|
7785 js2-FUNCTION) |
|
7786 "List of tokens that don't do automatic semicolon insertion.") |
|
7787 |
|
7788 (defconst js2-autoinsert-semi-and-warn |
|
7789 (list js2-ERROR js2-EOF js2-RC)) |
|
7790 |
|
7791 (defun js2-statement-helper () |
|
7792 (let* ((tt (js2-peek-token)) |
|
7793 (first-tt tt) |
|
7794 (beg js2-token-beg) |
|
7795 (parser (if (= tt js2-ERROR) |
|
7796 #'js2-parse-semi |
|
7797 (aref js2-parsers tt))) |
|
7798 pn |
|
7799 tt-flagged) |
|
7800 ;; If the statement is set, then it's been told its label by now. |
|
7801 (and js2-labeled-stmt |
|
7802 (js2-labeled-stmt-node-stmt js2-labeled-stmt) |
|
7803 (setq js2-labeled-stmt nil)) |
|
7804 |
|
7805 (setq pn (funcall parser) |
|
7806 tt-flagged (js2-peek-flagged-token) |
|
7807 tt (logand tt-flagged js2-clear-ti-mask)) |
|
7808 |
|
7809 ;; Don't do auto semi insertion for certain statement types. |
|
7810 (unless (or (memq first-tt js2-no-semi-insertion) |
|
7811 (js2-labeled-stmt-node-p pn)) |
|
7812 (cond |
|
7813 ((= tt js2-SEMI) |
|
7814 ;; Consume ';' as a part of expression |
|
7815 (js2-consume-token) |
|
7816 ;; extend the node bounds to include the semicolon. |
|
7817 (setf (js2-node-len pn) (- js2-token-end beg))) |
|
7818 ((memq tt js2-autoinsert-semi-and-warn) |
|
7819 ;; Autoinsert ; |
|
7820 (js2-parse-warn-missing-semi beg (js2-node-end pn))) |
|
7821 (t |
|
7822 (if (js2-flag-not-set-p tt-flagged js2-ti-after-eol) |
|
7823 ;; Report error if no EOL or autoinsert ';' otherwise |
|
7824 (js2-report-error "msg.no.semi.stmt") |
|
7825 (js2-parse-warn-missing-semi beg (js2-node-end pn)))))) |
|
7826 pn)) |
|
7827 |
|
7828 (defun js2-parse-condition () |
|
7829 "Parse a parenthesized boolean expression, e.g. in an if- or while-stmt. |
|
7830 The parens are discarded and the expression node is returned. |
|
7831 The `pos' field of the return value is set to an absolute position |
|
7832 that must be fixed up by the caller. |
|
7833 Return value is a list (EXPR LP RP), with absolute paren positions." |
|
7834 (let (pn lp rp) |
|
7835 (if (js2-must-match js2-LP "msg.no.paren.cond") |
|
7836 (setq lp js2-token-beg)) |
|
7837 (setq pn (js2-parse-expr)) |
|
7838 (if (js2-must-match js2-RP "msg.no.paren.after.cond") |
|
7839 (setq rp js2-token-beg)) |
|
7840 ;; Report strict warning on code like "if (a = 7) ..." |
|
7841 (if (and js2-strict-cond-assign-warning |
|
7842 (js2-assign-node-p pn)) |
|
7843 (js2-add-strict-warning "msg.equal.as.assign" nil |
|
7844 (js2-node-pos pn) |
|
7845 (+ (js2-node-pos pn) |
|
7846 (js2-node-len pn)))) |
|
7847 (list pn lp rp))) |
|
7848 |
|
7849 (defun js2-parse-if () |
|
7850 "Parser for if-statement. Last matched token must be js2-IF." |
|
7851 (let ((pos js2-token-beg) |
|
7852 cond |
|
7853 if-true |
|
7854 if-false |
|
7855 else-pos |
|
7856 end |
|
7857 pn) |
|
7858 (js2-consume-token) |
|
7859 (setq cond (js2-parse-condition) |
|
7860 if-true (js2-parse-statement) |
|
7861 if-false (if (js2-match-token js2-ELSE) |
|
7862 (progn |
|
7863 (setq else-pos (- js2-token-beg pos)) |
|
7864 (js2-parse-statement))) |
|
7865 end (js2-node-end (or if-false if-true)) |
|
7866 pn (make-js2-if-node :pos pos |
|
7867 :len (- end pos) |
|
7868 :condition (car cond) |
|
7869 :then-part if-true |
|
7870 :else-part if-false |
|
7871 :else-pos else-pos |
|
7872 :lp (js2-relpos (second cond) pos) |
|
7873 :rp (js2-relpos (third cond) pos))) |
|
7874 (js2-node-add-children pn (car cond) if-true if-false) |
|
7875 pn)) |
|
7876 |
|
7877 (defun js2-parse-switch () |
|
7878 "Parser for if-statement. Last matched token must be js2-SWITCH." |
|
7879 (let ((pos js2-token-beg) |
|
7880 tt |
|
7881 pn |
|
7882 discriminant |
|
7883 has-default |
|
7884 case-expr |
|
7885 case-node |
|
7886 case-pos |
|
7887 cases |
|
7888 stmt |
|
7889 lp |
|
7890 rp) |
|
7891 (js2-consume-token) |
|
7892 (if (js2-must-match js2-LP "msg.no.paren.switch") |
|
7893 (setq lp js2-token-beg)) |
|
7894 (setq discriminant (js2-parse-expr) |
|
7895 pn (make-js2-switch-node :discriminant discriminant |
|
7896 :pos pos |
|
7897 :lp (js2-relpos lp pos))) |
|
7898 (js2-node-add-children pn discriminant) |
|
7899 (js2-enter-switch pn) |
|
7900 (unwind-protect |
|
7901 (progn |
|
7902 (if (js2-must-match js2-RP "msg.no.paren.after.switch") |
|
7903 (setf (js2-switch-node-rp pn) (- js2-token-beg pos))) |
|
7904 (js2-must-match js2-LC "msg.no.brace.switch") |
|
7905 (catch 'break |
|
7906 (while t |
|
7907 (setq tt (js2-next-token) |
|
7908 case-pos js2-token-beg) |
|
7909 (cond |
|
7910 ((= tt js2-RC) |
|
7911 (setf (js2-node-len pn) (- js2-token-end pos)) |
|
7912 (throw 'break nil)) ; done |
|
7913 |
|
7914 ((= tt js2-CASE) |
|
7915 (setq case-expr (js2-parse-expr)) |
|
7916 (js2-must-match js2-COLON "msg.no.colon.case")) |
|
7917 |
|
7918 ((= tt js2-DEFAULT) |
|
7919 (if has-default |
|
7920 (js2-report-error "msg.double.switch.default")) |
|
7921 (setq has-default t |
|
7922 case-expr nil) |
|
7923 (js2-must-match js2-COLON "msg.no.colon.case")) |
|
7924 |
|
7925 (t |
|
7926 (js2-report-error "msg.bad.switch") |
|
7927 (throw 'break nil))) |
|
7928 |
|
7929 (setq case-node (make-js2-case-node :pos case-pos |
|
7930 :len (- js2-token-end case-pos) |
|
7931 :expr case-expr)) |
|
7932 (js2-node-add-children case-node case-expr) |
|
7933 (while (and (/= (setq tt (js2-peek-token)) js2-RC) |
|
7934 (/= tt js2-CASE) |
|
7935 (/= tt js2-DEFAULT) |
|
7936 (/= tt js2-EOF)) |
|
7937 (setf stmt (js2-parse-statement) |
|
7938 (js2-node-len case-node) (- (js2-node-end stmt) case-pos)) |
|
7939 (js2-block-node-push case-node stmt)) |
|
7940 (push case-node cases))) |
|
7941 ;; add cases last, as pushing reverses the order to be correct |
|
7942 (dolist (kid cases) |
|
7943 (js2-node-add-children pn kid) |
|
7944 (push kid (js2-switch-node-cases pn))) |
|
7945 pn) ; return value |
|
7946 (js2-exit-switch)))) |
|
7947 |
|
7948 (defun js2-parse-while () |
|
7949 "Parser for while-statement. Last matched token must be js2-WHILE." |
|
7950 (let ((pos js2-token-beg) |
|
7951 (pn (make-js2-while-node)) |
|
7952 cond |
|
7953 body) |
|
7954 (js2-consume-token) |
|
7955 (js2-enter-loop pn) |
|
7956 (unwind-protect |
|
7957 (progn |
|
7958 (setf cond (js2-parse-condition) |
|
7959 (js2-while-node-condition pn) (car cond) |
|
7960 body (js2-parse-statement) |
|
7961 (js2-while-node-body pn) body |
|
7962 (js2-node-len pn) (- (js2-node-end body) pos) |
|
7963 (js2-while-node-lp pn) (js2-relpos (second cond) pos) |
|
7964 (js2-while-node-rp pn) (js2-relpos (third cond) pos)) |
|
7965 (js2-node-add-children pn body (car cond))) |
|
7966 (js2-exit-loop)) |
|
7967 pn)) |
|
7968 |
|
7969 (defun js2-parse-do () |
|
7970 "Parser for do-statement. Last matched token must be js2-DO." |
|
7971 (let ((pos js2-token-beg) |
|
7972 (pn (make-js2-do-node)) |
|
7973 cond |
|
7974 body |
|
7975 end) |
|
7976 (js2-consume-token) |
|
7977 (js2-enter-loop pn) |
|
7978 (unwind-protect |
|
7979 (progn |
|
7980 (setq body (js2-parse-statement)) |
|
7981 (js2-must-match js2-WHILE "msg.no.while.do") |
|
7982 (setf (js2-do-node-while-pos pn) (- js2-token-beg pos) |
|
7983 cond (js2-parse-condition) |
|
7984 (js2-do-node-condition pn) (car cond) |
|
7985 (js2-do-node-body pn) body |
|
7986 end js2-ts-cursor |
|
7987 (js2-do-node-lp pn) (js2-relpos (second cond) pos) |
|
7988 (js2-do-node-rp pn) (js2-relpos (third cond) pos)) |
|
7989 (js2-node-add-children pn (car cond) body)) |
|
7990 (js2-exit-loop)) |
|
7991 ;; Always auto-insert semicolon to follow SpiderMonkey: |
|
7992 ;; It is required by ECMAScript but is ignored by the rest of |
|
7993 ;; world; see bug 238945 |
|
7994 (if (js2-match-token js2-SEMI) |
|
7995 (setq end js2-ts-cursor)) |
|
7996 (setf (js2-node-len pn) (- end pos)) |
|
7997 pn)) |
|
7998 |
|
7999 (defun js2-parse-for () |
|
8000 "Parser for for-statement. Last matched token must be js2-FOR. |
|
8001 Parses for, for-in, and for each-in statements." |
|
8002 (let ((for-pos js2-token-beg) |
|
8003 pn |
|
8004 is-for-each |
|
8005 is-for-in |
|
8006 in-pos |
|
8007 each-pos |
|
8008 tmp-pos |
|
8009 init ; Node init is also foo in 'foo in object' |
|
8010 cond ; Node cond is also object in 'foo in object' |
|
8011 incr ; 3rd section of for-loop initializer |
|
8012 body |
|
8013 tt |
|
8014 lp |
|
8015 rp) |
|
8016 (js2-consume-token) |
|
8017 ;; See if this is a for each () instead of just a for () |
|
8018 (when (js2-match-token js2-NAME) |
|
8019 (if (string= "each" js2-ts-string) |
|
8020 (progn |
|
8021 (setq is-for-each t |
|
8022 each-pos (- js2-token-beg for-pos)) ; relative |
|
8023 (js2-record-face 'font-lock-keyword-face)) |
|
8024 (js2-report-error "msg.no.paren.for"))) |
|
8025 |
|
8026 (if (js2-must-match js2-LP "msg.no.paren.for") |
|
8027 (setq lp (- js2-token-beg for-pos))) |
|
8028 (setq tt (js2-peek-token)) |
|
8029 |
|
8030 ;; parse init clause |
|
8031 (let ((js2-in-for-init t)) ; set as dynamic variable |
|
8032 (cond |
|
8033 ((= tt js2-SEMI) |
|
8034 (setq init (make-js2-empty-expr-node))) |
|
8035 ((or (= tt js2-VAR) (= tt js2-LET)) |
|
8036 (js2-consume-token) |
|
8037 (setq init (js2-parse-variables tt js2-token-beg))) |
|
8038 (t |
|
8039 (setq init (js2-parse-expr))))) |
|
8040 |
|
8041 (if (js2-match-token js2-IN) |
|
8042 (setq is-for-in t |
|
8043 in-pos (- js2-token-beg for-pos) |
|
8044 cond (js2-parse-expr)) ; object over which we're iterating |
|
8045 ;; else ordinary for loop - parse cond and incr |
|
8046 (js2-must-match js2-SEMI "msg.no.semi.for") |
|
8047 (setq cond (if (= (js2-peek-token) js2-SEMI) |
|
8048 (make-js2-empty-expr-node) ; no loop condition |
|
8049 (js2-parse-expr))) |
|
8050 (js2-must-match js2-SEMI "msg.no.semi.for.cond") |
|
8051 (setq tmp-pos js2-token-end |
|
8052 incr (if (= (js2-peek-token) js2-RP) |
|
8053 (make-js2-empty-expr-node :pos tmp-pos) |
|
8054 (js2-parse-expr)))) |
|
8055 |
|
8056 (if (js2-must-match js2-RP "msg.no.paren.for.ctrl") |
|
8057 (setq rp (- js2-token-beg for-pos))) |
|
8058 (if (not is-for-in) |
|
8059 (setq pn (make-js2-for-node :init init |
|
8060 :condition cond |
|
8061 :update incr |
|
8062 :lp lp |
|
8063 :rp rp)) |
|
8064 ;; cond could be null if 'in obj' got eaten by the init node. |
|
8065 (if (js2-infix-node-p init) |
|
8066 ;; it was (foo in bar) instead of (var foo in bar) |
|
8067 (setq cond (js2-infix-node-right init) |
|
8068 init (js2-infix-node-left init)) |
|
8069 (if (and (js2-var-decl-node-p init) |
|
8070 (> (length (js2-var-decl-node-kids init)) 1)) |
|
8071 (js2-report-error "msg.mult.index"))) |
|
8072 |
|
8073 (setq pn (make-js2-for-in-node :iterator init |
|
8074 :object cond |
|
8075 :in-pos in-pos |
|
8076 :foreach-p is-for-each |
|
8077 :each-pos each-pos |
|
8078 :lp lp |
|
8079 :rp rp))) |
|
8080 (unwind-protect |
|
8081 (progn |
|
8082 (js2-enter-loop pn) |
|
8083 ;; We have to parse the body -after- creating the loop node, |
|
8084 ;; so that the loop node appears in the js2-loop-set, allowing |
|
8085 ;; break/continue statements to find the enclosing loop. |
|
8086 (setf body (js2-parse-statement) |
|
8087 (js2-loop-node-body pn) body |
|
8088 (js2-node-pos pn) for-pos |
|
8089 (js2-node-len pn) (- (js2-node-end body) for-pos)) |
|
8090 (js2-node-add-children pn init cond incr body)) |
|
8091 ;; finally |
|
8092 (js2-exit-loop)) |
|
8093 pn)) |
|
8094 |
|
8095 (defun js2-parse-try () |
|
8096 "Parser for try-statement. Last matched token must be js2-TRY." |
|
8097 (let ((try-pos js2-token-beg) |
|
8098 try-end |
|
8099 try-block |
|
8100 catch-blocks |
|
8101 finally-block |
|
8102 saw-default-catch |
|
8103 peek |
|
8104 var-name |
|
8105 catch-cond |
|
8106 catch-node |
|
8107 guard-kwd |
|
8108 catch-pos |
|
8109 finally-pos |
|
8110 pn |
|
8111 block |
|
8112 lp |
|
8113 rp) |
|
8114 (js2-consume-token) |
|
8115 (if (/= (js2-peek-token) js2-LC) |
|
8116 (js2-report-error "msg.no.brace.try")) |
|
8117 (setq try-block (js2-parse-statement) |
|
8118 try-end (js2-node-end try-block) |
|
8119 peek (js2-peek-token)) |
|
8120 (cond |
|
8121 ((= peek js2-CATCH) |
|
8122 (while (js2-match-token js2-CATCH) |
|
8123 (setq catch-pos js2-token-beg |
|
8124 guard-kwd nil |
|
8125 catch-cond nil |
|
8126 lp nil |
|
8127 rp nil) |
|
8128 (if saw-default-catch |
|
8129 (js2-report-error "msg.catch.unreachable")) |
|
8130 (if (js2-must-match js2-LP "msg.no.paren.catch") |
|
8131 (setq lp (- js2-token-beg catch-pos))) |
|
8132 |
|
8133 (js2-must-match js2-NAME "msg.bad.catchcond") |
|
8134 (setq var-name (js2-create-name-node)) |
|
8135 |
|
8136 (if (js2-match-token js2-IF) |
|
8137 (setq guard-kwd (- js2-token-beg catch-pos) |
|
8138 catch-cond (js2-parse-expr)) |
|
8139 (setq saw-default-catch t)) |
|
8140 |
|
8141 (if (js2-must-match js2-RP "msg.bad.catchcond") |
|
8142 (setq rp (- js2-token-beg catch-pos))) |
|
8143 (js2-must-match js2-LC "msg.no.brace.catchblock") |
|
8144 |
|
8145 (setq block (js2-parse-statements) |
|
8146 try-end (js2-node-end block) |
|
8147 catch-node (make-js2-catch-node :pos catch-pos |
|
8148 :var-name var-name |
|
8149 :guard-expr catch-cond |
|
8150 :guard-kwd guard-kwd |
|
8151 :block block |
|
8152 :lp lp |
|
8153 :rp rp)) |
|
8154 (if (js2-must-match js2-RC "msg.no.brace.after.body") |
|
8155 (setq try-end js2-token-beg)) |
|
8156 (setf (js2-node-len block) (- try-end (js2-node-pos block)) |
|
8157 (js2-node-len catch-node) (- try-end catch-pos)) |
|
8158 (js2-node-add-children catch-node var-name catch-cond block) |
|
8159 (push catch-node catch-blocks))) |
|
8160 |
|
8161 ((/= peek js2-FINALLY) |
|
8162 (js2-must-match js2-FINALLY "msg.try.no.catchfinally" |
|
8163 (js2-node-pos try-block) |
|
8164 (- (setq try-end (js2-node-end try-block)) |
|
8165 (js2-node-pos try-block))))) |
|
8166 |
|
8167 (when (js2-match-token js2-FINALLY) |
|
8168 (setq finally-pos js2-token-beg |
|
8169 block (js2-parse-statement) |
|
8170 try-end (js2-node-end block) |
|
8171 finally-block (make-js2-finally-node :pos finally-pos |
|
8172 :len (- try-end finally-pos) |
|
8173 :body block)) |
|
8174 (js2-node-add-children finally-block block)) |
|
8175 |
|
8176 (setq pn (make-js2-try-node :pos try-pos |
|
8177 :len (- try-end try-pos) |
|
8178 :try-block try-block |
|
8179 :finally-block finally-block)) |
|
8180 (js2-node-add-children pn try-block finally-block) |
|
8181 |
|
8182 ;; push them onto the try-node, which reverses and corrects their order |
|
8183 (dolist (cb catch-blocks) |
|
8184 (js2-node-add-children pn cb) |
|
8185 (push cb (js2-try-node-catch-clauses pn))) |
|
8186 pn)) |
|
8187 |
|
8188 (defun js2-parse-throw () |
|
8189 "Parser for throw-statement. Last matched token must be js2-THROW." |
|
8190 (let ((pos js2-token-beg) |
|
8191 expr |
|
8192 pn) |
|
8193 (js2-consume-token) |
|
8194 (if (= (js2-peek-token-or-eol) js2-EOL) |
|
8195 ;; ECMAScript does not allow new lines before throw expression, |
|
8196 ;; see bug 256617 |
|
8197 (js2-report-error "msg.bad.throw.eol")) |
|
8198 (setq expr (js2-parse-expr) |
|
8199 pn (make-js2-throw-node :pos pos |
|
8200 :len (- (js2-node-end expr) pos) |
|
8201 :expr expr)) |
|
8202 (js2-node-add-children pn expr) |
|
8203 pn)) |
|
8204 |
|
8205 (defsubst js2-match-jump-label-name (label-name) |
|
8206 "If break/continue specified a label, return that label's labeled stmt. |
|
8207 Returns the corresponding `js2-labeled-stmt-node', or if LABEL-NAME |
|
8208 does not match an existing label, reports an error and returns nil." |
|
8209 (let ((bundle (cdr (assoc label-name js2-label-set)))) |
|
8210 (if (null bundle) |
|
8211 (js2-report-error "msg.undef.label")) |
|
8212 bundle)) |
|
8213 |
|
8214 (defun js2-parse-break () |
|
8215 "Parser for break-statement. Last matched token must be js2-BREAK." |
|
8216 (let ((pos js2-token-beg) |
|
8217 (end js2-token-end) |
|
8218 break-target ; statement to break from |
|
8219 break-label ; in "break foo", name-node representing the foo |
|
8220 labels ; matching labeled statement to break to |
|
8221 pn) |
|
8222 (js2-consume-token) ; `break' |
|
8223 (when (eq (js2-peek-token-or-eol) js2-NAME) |
|
8224 (js2-consume-token) |
|
8225 (setq break-label (js2-create-name-node) |
|
8226 end (js2-node-end break-label) |
|
8227 ;; matchJumpLabelName only matches if there is one |
|
8228 labels (js2-match-jump-label-name js2-ts-string) |
|
8229 break-target (if labels (car (js2-labeled-stmt-node-labels labels))))) |
|
8230 |
|
8231 (unless (or break-target break-label) |
|
8232 ;; no break target specified - try for innermost enclosing loop/switch |
|
8233 (if (null js2-loop-and-switch-set) |
|
8234 (unless break-label |
|
8235 (js2-report-error "msg.bad.break" nil pos (length "break"))) |
|
8236 (setq break-target (car js2-loop-and-switch-set)))) |
|
8237 |
|
8238 (setq pn (make-js2-break-node :pos pos |
|
8239 :len (- end pos) |
|
8240 :label break-label |
|
8241 :target break-target)) |
|
8242 (js2-node-add-children pn break-label) ; but not break-target |
|
8243 pn)) |
|
8244 |
|
8245 (defun js2-parse-continue () |
|
8246 "Parser for continue-statement. Last matched token must be js2-CONTINUE." |
|
8247 (let ((pos js2-token-beg) |
|
8248 (end js2-token-end) |
|
8249 label ; optional user-specified label, a `js2-name-node' |
|
8250 labels ; current matching labeled stmt, if any |
|
8251 target ; the `js2-loop-node' target of this continue stmt |
|
8252 pn) |
|
8253 (js2-consume-token) ; `continue' |
|
8254 (when (= (js2-peek-token-or-eol) js2-NAME) |
|
8255 (js2-consume-token) |
|
8256 (setq label (js2-create-name-node) |
|
8257 end (js2-node-end label) |
|
8258 ;; matchJumpLabelName only matches if there is one |
|
8259 labels (js2-match-jump-label-name js2-ts-string))) |
|
8260 (cond |
|
8261 ((null labels) ; no current label to go to |
|
8262 (if (null js2-loop-set) ; no loop to continue to |
|
8263 (js2-report-error "msg.continue.outside" nil pos |
|
8264 (length "continue")) |
|
8265 (setq target (car js2-loop-set)))) ; innermost enclosing loop |
|
8266 (t |
|
8267 (if (js2-loop-node-p (js2-labeled-stmt-node-stmt labels)) |
|
8268 (setq target (js2-labeled-stmt-node-stmt labels)) |
|
8269 (js2-report-error "msg.continue.nonloop" nil pos (- end pos))))) |
|
8270 |
|
8271 (setq pn (make-js2-continue-node :pos pos |
|
8272 :len (- end pos) |
|
8273 :label label |
|
8274 :target target)) |
|
8275 (js2-node-add-children pn label) ; but not target - it's not our child |
|
8276 pn)) |
|
8277 |
|
8278 (defun js2-parse-with () |
|
8279 "Parser for with-statement. Last matched token must be js2-WITH." |
|
8280 (js2-consume-token) |
|
8281 (let ((pos js2-token-beg) |
|
8282 obj body pn lp rp) |
|
8283 |
|
8284 (if (js2-must-match js2-LP "msg.no.paren.with") |
|
8285 (setq lp js2-token-beg)) |
|
8286 |
|
8287 (setq obj (js2-parse-expr)) |
|
8288 |
|
8289 (if (js2-must-match js2-RP "msg.no.paren.after.with") |
|
8290 (setq rp js2-token-beg)) |
|
8291 |
|
8292 (let ((js2-nesting-of-with (1+ js2-nesting-of-with))) |
|
8293 (setq body (js2-parse-statement))) |
|
8294 |
|
8295 (setq pn (make-js2-with-node :pos pos |
|
8296 :len (- (js2-node-end body) pos) |
|
8297 :object obj |
|
8298 :body body |
|
8299 :lp (js2-relpos lp pos) |
|
8300 :rp (js2-relpos rp pos))) |
|
8301 (js2-node-add-children pn obj body) |
|
8302 pn)) |
|
8303 |
|
8304 (defun js2-parse-const-var () |
|
8305 "Parser for var- or const-statement. |
|
8306 Last matched token must be js2-CONST or js2-VAR." |
|
8307 (let ((tt (js2-peek-token)) |
|
8308 (pos js2-token-beg) |
|
8309 expr |
|
8310 pn) |
|
8311 (js2-consume-token) |
|
8312 (setq expr (js2-parse-variables tt js2-token-beg) |
|
8313 pn (make-js2-expr-stmt-node :pos pos |
|
8314 :len (- (js2-node-end expr) pos) |
|
8315 :expr expr)) |
|
8316 (js2-node-add-children pn expr) |
|
8317 pn)) |
|
8318 |
|
8319 (defsubst js2-wrap-with-expr-stmt (pos expr &optional add-child) |
|
8320 (let ((pn (make-js2-expr-stmt-node :pos pos |
|
8321 :len (js2-node-len expr) |
|
8322 :type (if (js2-inside-function) |
|
8323 js2-EXPR_VOID |
|
8324 js2-EXPR_RESULT) |
|
8325 :expr expr))) |
|
8326 (if add-child |
|
8327 (js2-node-add-children pn expr)) |
|
8328 pn)) |
|
8329 |
|
8330 (defun js2-parse-let-stmt () |
|
8331 "Parser for let-statement. Last matched token must be js2-LET." |
|
8332 (js2-consume-token) |
|
8333 (let ((pos js2-token-beg) |
|
8334 expr |
|
8335 pn) |
|
8336 (if (= (js2-peek-token) js2-LP) |
|
8337 ;; let expression in statement context |
|
8338 (setq expr (js2-parse-let pos 'statement) |
|
8339 pn (js2-wrap-with-expr-stmt pos expr t)) |
|
8340 ;; else we're looking at a statement like let x=6, y=7; |
|
8341 (setf expr (js2-parse-variables js2-LET pos) |
|
8342 pn (js2-wrap-with-expr-stmt pos expr t) |
|
8343 (js2-node-type pn) js2-EXPR_RESULT)) |
|
8344 pn)) |
|
8345 |
|
8346 (defun js2-parse-ret-yield () |
|
8347 (js2-parse-return-or-yield (js2-peek-token) nil)) |
|
8348 |
|
8349 (defconst js2-parse-return-stmt-enders |
|
8350 (list js2-SEMI js2-RC js2-EOF js2-EOL js2-ERROR js2-RB js2-RP js2-YIELD)) |
|
8351 |
|
8352 (defsubst js2-now-all-set (before after mask) |
|
8353 "Return whether or not the bits in the mask have changed to all set. |
|
8354 BEFORE is bits before change, AFTER is bits after change, and MASK is |
|
8355 the mask for bits. Returns t if all the bits in the mask are set in AFTER |
|
8356 but not BEFORE." |
|
8357 (and (/= (logand before mask) mask) |
|
8358 (= (logand after mask) mask))) |
|
8359 |
|
8360 (defun js2-parse-return-or-yield (tt expr-context) |
|
8361 (let ((pos js2-token-beg) |
|
8362 (end js2-token-end) |
|
8363 (before js2-end-flags) |
|
8364 (inside-function (js2-inside-function)) |
|
8365 e |
|
8366 ret |
|
8367 name) |
|
8368 (unless inside-function |
|
8369 (js2-report-error (if (eq tt js2-RETURN) |
|
8370 "msg.bad.return" |
|
8371 "msg.bad.yield"))) |
|
8372 (js2-consume-token) |
|
8373 ;; This is ugly, but we don't want to require a semicolon. |
|
8374 (unless (memq (js2-peek-token-or-eol) js2-parse-return-stmt-enders) |
|
8375 (setq e (js2-parse-expr) |
|
8376 end (js2-node-end e))) |
|
8377 (cond |
|
8378 ((eq tt js2-RETURN) |
|
8379 (js2-set-flag js2-end-flags (if (null e) |
|
8380 js2-end-returns |
|
8381 js2-end-returns-value)) |
|
8382 (setq ret (make-js2-return-node :pos pos |
|
8383 :len (- end pos) |
|
8384 :retval e)) |
|
8385 (js2-node-add-children ret e) |
|
8386 ;; See if we need a strict mode warning. |
|
8387 ;; TODO: The analysis done by `js2-has-consistent-return-usage' is |
|
8388 ;; more thorough and accurate than this before/after flag check. |
|
8389 ;; E.g. if there's a finally-block that always returns, we shouldn't |
|
8390 ;; show a warning generated by inconsistent returns in the catch blocks. |
|
8391 ;; Basically `js2-has-consistent-return-usage' needs to keep more state, |
|
8392 ;; so we know which returns/yields to highlight, and we should get rid of |
|
8393 ;; all the checking in `js2-parse-return-or-yield'. |
|
8394 (if (and js2-strict-inconsistent-return-warning |
|
8395 (js2-now-all-set before js2-end-flags |
|
8396 (logior js2-end-returns js2-end-returns-value))) |
|
8397 (js2-add-strict-warning "msg.return.inconsistent" nil pos end))) |
|
8398 (t |
|
8399 (unless (js2-inside-function) |
|
8400 (js2-report-error "msg.bad.yield")) |
|
8401 (js2-set-flag js2-end-flags js2-end-yields) |
|
8402 (setq ret (make-js2-yield-node :pos pos |
|
8403 :len (- end pos) |
|
8404 :value e)) |
|
8405 (js2-node-add-children ret e) |
|
8406 (unless expr-context |
|
8407 (setq e ret |
|
8408 ret (js2-wrap-with-expr-stmt pos e t)) |
|
8409 (js2-set-requires-activation) |
|
8410 (js2-set-is-generator)))) |
|
8411 |
|
8412 ;; see if we are mixing yields and value returns. |
|
8413 (when (and inside-function |
|
8414 (js2-now-all-set before js2-end-flags |
|
8415 (logior js2-end-yields js2-end-returns-value))) |
|
8416 (setq name (js2-function-name js2-current-script-or-fn)) |
|
8417 (if (zerop (length name)) |
|
8418 (js2-report-error "msg.anon.generator.returns" nil pos (- end pos)) |
|
8419 (js2-report-error "msg.generator.returns" name pos (- end pos)))) |
|
8420 |
|
8421 ret)) |
|
8422 |
|
8423 (defun js2-parse-debugger () |
|
8424 (js2-consume-token) |
|
8425 (make-js2-keyword-node :type js2-DEBUGGER)) |
|
8426 |
|
8427 (defun js2-parse-block () |
|
8428 "Parser for a curly-delimited statement block. |
|
8429 Last token matched must be js2-LC." |
|
8430 (let ((pos js2-token-beg) |
|
8431 (pn (make-js2-scope))) |
|
8432 (js2-consume-token) |
|
8433 (js2-push-scope pn) |
|
8434 (unwind-protect |
|
8435 (progn |
|
8436 (js2-parse-statements pn) |
|
8437 (js2-must-match js2-RC "msg.no.brace.block") |
|
8438 (setf (js2-node-len pn) (- js2-token-end pos))) |
|
8439 (js2-pop-scope)) |
|
8440 pn)) |
|
8441 |
|
8442 ;; for js2-ERROR too, to have a node for error recovery to work on |
|
8443 (defun js2-parse-semi () |
|
8444 "Parse a statement or handle an error. |
|
8445 Last matched token is js-SEMI or js-ERROR." |
|
8446 (let ((tt (js2-peek-token)) pos len) |
|
8447 (js2-consume-token) |
|
8448 (if (eq tt js2-SEMI) |
|
8449 (make-js2-empty-expr-node :len 1) |
|
8450 (setq pos js2-token-beg |
|
8451 len (- js2-token-beg pos)) |
|
8452 (js2-report-error "msg.syntax" nil pos len) |
|
8453 (make-js2-error-node :pos pos :len len)))) |
|
8454 |
|
8455 (defun js2-parse-default-xml-namespace () |
|
8456 "Parse a `default xml namespace = <expr>' e4x statement." |
|
8457 (let ((pos js2-token-beg) |
|
8458 end len expr unary es) |
|
8459 (js2-consume-token) |
|
8460 (js2-must-have-xml) |
|
8461 (js2-set-requires-activation) |
|
8462 (setq len (- js2-ts-cursor pos)) |
|
8463 (unless (and (js2-match-token js2-NAME) |
|
8464 (string= js2-ts-string "xml")) |
|
8465 (js2-report-error "msg.bad.namespace" nil pos len)) |
|
8466 (unless (and (js2-match-token js2-NAME) |
|
8467 (string= js2-ts-string "namespace")) |
|
8468 (js2-report-error "msg.bad.namespace" nil pos len)) |
|
8469 (unless (js2-match-token js2-ASSIGN) |
|
8470 (js2-report-error "msg.bad.namespace" nil pos len)) |
|
8471 (setq expr (js2-parse-expr) |
|
8472 end (js2-node-end expr) |
|
8473 unary (make-js2-unary-node :type js2-DEFAULTNAMESPACE |
|
8474 :pos pos |
|
8475 :len (- end pos) |
|
8476 :operand expr)) |
|
8477 (js2-node-add-children unary expr) |
|
8478 (make-js2-expr-stmt-node :pos pos |
|
8479 :len (- end pos) |
|
8480 :expr unary))) |
|
8481 |
|
8482 (defun js2-record-label (label bundle) |
|
8483 ;; current token should be colon that `js2-parse-primary-expr' left untouched |
|
8484 (js2-consume-token) |
|
8485 (let ((name (js2-label-node-name label)) |
|
8486 labeled-stmt |
|
8487 dup) |
|
8488 (when (setq labeled-stmt (cdr (assoc name js2-label-set))) |
|
8489 ;; flag both labels if possible when used in editing mode |
|
8490 (if (and js2-parse-ide-mode |
|
8491 (setq dup (js2-get-label-by-name labeled-stmt name))) |
|
8492 (js2-report-error "msg.dup.label" nil |
|
8493 (js2-node-abs-pos dup) (js2-node-len dup))) |
|
8494 (js2-report-error "msg.dup.label" nil |
|
8495 (js2-node-pos label) (js2-node-len label))) |
|
8496 (js2-labeled-stmt-node-add-label bundle label) |
|
8497 (js2-node-add-children bundle label) |
|
8498 ;; Add one reference to the bundle per label in `js2-label-set' |
|
8499 (push (cons name bundle) js2-label-set))) |
|
8500 |
|
8501 (defun js2-parse-name-or-label () |
|
8502 "Parser for identifier or label. Last token matched must be js2-NAME. |
|
8503 Called when we found a name in a statement context. If it's a label, we gather |
|
8504 up any following labels and the next non-label statement into a |
|
8505 `js2-labeled-stmt-node' bundle and return that. Otherwise we parse an |
|
8506 expression and return it wrapped in a `js2-expr-stmt-node'." |
|
8507 (let ((pos js2-token-beg) |
|
8508 (end js2-token-end) |
|
8509 expr |
|
8510 stmt |
|
8511 pn |
|
8512 bundle |
|
8513 (continue t)) |
|
8514 ;; set check for label and call down to `js2-parse-primary-expr' |
|
8515 (js2-set-check-for-label) |
|
8516 (setq expr (js2-parse-expr)) |
|
8517 |
|
8518 (if (/= (js2-node-type expr) js2-LABEL) |
|
8519 ;; Parsed non-label expression - wrap with expression stmt. |
|
8520 (setq pn (js2-wrap-with-expr-stmt pos expr t)) |
|
8521 |
|
8522 ;; else parsed a label |
|
8523 (setq bundle (make-js2-labeled-stmt-node :pos pos)) |
|
8524 (js2-record-label expr bundle) |
|
8525 |
|
8526 ;; look for more labels |
|
8527 (while (and continue (= (js2-peek-token) js2-NAME)) |
|
8528 (js2-set-check-for-label) |
|
8529 (setq expr (js2-parse-expr)) |
|
8530 (if (/= (js2-node-type expr) js2-LABEL) |
|
8531 (setq stmt (js2-wrap-with-expr-stmt pos expr t) |
|
8532 continue nil) |
|
8533 (js2-record-label expr bundle))) |
|
8534 |
|
8535 ;; no more labels; now parse the labeled statement |
|
8536 (unwind-protect |
|
8537 (unless stmt |
|
8538 (let ((js2-labeled-stmt bundle)) ; bind dynamically |
|
8539 (setq stmt (js2-statement-helper)))) |
|
8540 ;; remove the labels for this statement from the global set |
|
8541 (dolist (label (js2-labeled-stmt-node-labels bundle)) |
|
8542 (setq js2-label-set (remove label js2-label-set)))) |
|
8543 |
|
8544 (setf (js2-labeled-stmt-node-stmt bundle) stmt) |
|
8545 (js2-node-add-children bundle stmt) |
|
8546 bundle))) |
|
8547 |
|
8548 (defun js2-parse-expr-stmt () |
|
8549 "Default parser in statement context, if no recognized statement found." |
|
8550 (js2-wrap-with-expr-stmt js2-token-beg (js2-parse-expr) t)) |
|
8551 |
|
8552 (defun js2-parse-variables (decl-type pos) |
|
8553 "Parse a comma-separated list of variable declarations. |
|
8554 Could be a 'var', 'const' or 'let' expression, possibly in a for-loop initializer. |
|
8555 |
|
8556 DECL-TYPE is a token value: either VAR, CONST, or LET depending on context. |
|
8557 For 'var' or 'const', the keyword should be the token last scanned. |
|
8558 |
|
8559 POS is the position where the node should start. It's sometimes the |
|
8560 var/const/let keyword, and other times the beginning of the first token |
|
8561 in the first variable declaration. |
|
8562 |
|
8563 Returns the parsed `js2-var-decl-node' expression node." |
|
8564 (let* ((result (make-js2-var-decl-node :decl-type decl-type |
|
8565 :pos pos)) |
|
8566 destructuring |
|
8567 kid-pos |
|
8568 tt |
|
8569 init |
|
8570 name |
|
8571 end |
|
8572 nbeg nend |
|
8573 vi |
|
8574 (continue t)) |
|
8575 ;; Example: |
|
8576 ;; var foo = {a: 1, b: 2}, bar = [3, 4]; |
|
8577 ;; var {b: s2, a: s1} = foo, x = 6, y, [s3, s4] = bar; |
|
8578 (while continue |
|
8579 (setq destructuring nil |
|
8580 name nil |
|
8581 tt (js2-peek-token) |
|
8582 kid-pos js2-token-beg |
|
8583 end js2-token-end |
|
8584 init nil) |
|
8585 (if (or (= tt js2-LB) (= tt js2-LC)) |
|
8586 ;; Destructuring assignment, e.g., var [a, b] = ... |
|
8587 (setq destructuring (js2-parse-primary-expr) |
|
8588 end (js2-node-end destructuring)) |
|
8589 ;; Simple variable name |
|
8590 (when (js2-must-match js2-NAME "msg.bad.var") |
|
8591 (setq name (js2-create-name-node) |
|
8592 nbeg js2-token-beg |
|
8593 nend js2-token-end |
|
8594 end nend) |
|
8595 (js2-define-symbol decl-type js2-ts-string name js2-in-for-init))) |
|
8596 |
|
8597 (when (js2-match-token js2-ASSIGN) |
|
8598 (setq init (js2-parse-assign-expr) |
|
8599 end (js2-node-end init)) |
|
8600 (if (and js2-parse-ide-mode |
|
8601 (or (js2-object-node-p init) |
|
8602 (js2-function-node-p init))) |
|
8603 (js2-record-imenu-functions init name))) |
|
8604 |
|
8605 (when name |
|
8606 (js2-set-face nbeg nend (if (js2-function-node-p init) |
|
8607 'font-lock-function-name-face |
|
8608 'font-lock-variable-name-face) |
|
8609 'record)) |
|
8610 |
|
8611 (setq vi (make-js2-var-init-node :pos kid-pos |
|
8612 :len (- end kid-pos) |
|
8613 :type decl-type)) |
|
8614 (if destructuring |
|
8615 (progn |
|
8616 (if (and (null init) (not js2-in-for-init)) |
|
8617 (js2-report-error "msg.destruct.assign.no.init")) |
|
8618 (setf (js2-var-init-node-target vi) destructuring)) |
|
8619 (setf (js2-var-init-node-target vi) name)) |
|
8620 (setf (js2-var-init-node-initializer vi) init) |
|
8621 (js2-node-add-children vi name destructuring init) |
|
8622 |
|
8623 (js2-block-node-push result vi) |
|
8624 (unless (js2-match-token js2-COMMA) |
|
8625 (setq continue nil))) |
|
8626 |
|
8627 (setf (js2-node-len result) (- end pos)) |
|
8628 result)) |
|
8629 |
|
8630 (defun js2-parse-let (pos &optional stmt-p) |
|
8631 "Parse a let expression or statement. |
|
8632 A let-expression is of the form `let (vars) expr'. |
|
8633 A let-statment is of the form `let (vars) {statements}'. |
|
8634 The third form of let is a variable declaration list, handled |
|
8635 by `js2-parse-variables'." |
|
8636 (let ((pn (make-js2-let-node :pos pos)) |
|
8637 beg vars body) |
|
8638 (if (js2-must-match js2-LP "msg.no.paren.after.let") |
|
8639 (setf (js2-let-node-lp pn) (- js2-token-beg pos))) |
|
8640 (js2-push-scope pn) |
|
8641 (unwind-protect |
|
8642 (progn |
|
8643 (setq vars (js2-parse-variables js2-LET js2-token-beg)) |
|
8644 (if (js2-must-match js2-RP "msg.no.paren.let") |
|
8645 (setf (js2-let-node-rp pn) (- js2-token-beg pos))) |
|
8646 (if (and stmt-p (eq (js2-peek-token) js2-LC)) |
|
8647 ;; let statement |
|
8648 (progn |
|
8649 (js2-consume-token) |
|
8650 (setf beg js2-token-beg ; position stmt at LC |
|
8651 body (js2-parse-statements)) |
|
8652 (js2-must-match js2-RC "msg.no.curly.let") |
|
8653 (setf (js2-node-len body) (- js2-token-end beg) |
|
8654 (js2-node-len pn) (- js2-token-end pos) |
|
8655 (js2-let-node-body pn) body |
|
8656 (js2-node-type pn) js2-LET)) |
|
8657 ;; let expression |
|
8658 (setf body (js2-parse-expr) |
|
8659 (js2-node-len pn) (- (js2-node-end body) pos) |
|
8660 (js2-let-node-body pn) body)) |
|
8661 (js2-node-add-children pn vars body)) |
|
8662 (js2-pop-scope)) |
|
8663 pn)) |
|
8664 |
|
8665 (defsubst js2-define-new-symbol (decl-type name node) |
|
8666 (js2-scope-put-symbol js2-current-scope |
|
8667 name |
|
8668 (make-js2-symbol decl-type name node))) |
|
8669 |
|
8670 (defun js2-define-symbol (decl-type name &optional node ignore-not-in-block) |
|
8671 "Define a symbol in the current scope. |
|
8672 If NODE is non-nil, it is the AST node associated with the symbol." |
|
8673 (let* ((defining-scope (js2-get-defining-scope js2-current-scope name)) |
|
8674 (symbol (if defining-scope |
|
8675 (js2-scope-get-symbol defining-scope name))) |
|
8676 (sdt (if symbol (js2-symbol-decl-type symbol) -1))) |
|
8677 (cond |
|
8678 ((and symbol ; already defined |
|
8679 (or (= sdt js2-CONST) ; old version is const |
|
8680 (= decl-type js2-CONST) ; new version is const |
|
8681 ;; two let-bound vars in this block have same name |
|
8682 (and (= sdt js2-LET) |
|
8683 (eq defining-scope js2-current-scope)))) |
|
8684 (js2-report-error |
|
8685 (cond |
|
8686 ((= sdt js2-CONST) "msg.const.redecl") |
|
8687 ((= sdt js2-LET) "msg.let.redecl") |
|
8688 ((= sdt js2-VAR) "msg.var.redecl") |
|
8689 ((= sdt js2-FUNCTION) "msg.function.redecl") |
|
8690 (t "msg.parm.redecl")) |
|
8691 name)) |
|
8692 |
|
8693 ((= decl-type js2-LET) |
|
8694 (if (and (not ignore-not-in-block) |
|
8695 (or (= (js2-node-type js2-current-scope) js2-IF) |
|
8696 (js2-loop-node-p js2-current-scope))) |
|
8697 (js2-report-error "msg.let.decl.not.in.block") |
|
8698 (js2-define-new-symbol decl-type name node))) |
|
8699 |
|
8700 ((or (= decl-type js2-VAR) |
|
8701 (= decl-type js2-CONST) |
|
8702 (= decl-type js2-FUNCTION)) |
|
8703 (if symbol |
|
8704 (if (and js2-strict-var-redeclaration-warning (= sdt js2-VAR)) |
|
8705 (js2-add-strict-warning "msg.var.redecl" name) |
|
8706 (if (and js2-strict-var-hides-function-arg-warning (= sdt js2-LP)) |
|
8707 (js2-add-strict-warning "msg.var.hides.arg" name))) |
|
8708 (js2-define-new-symbol decl-type name node))) |
|
8709 |
|
8710 ((= decl-type js2-LP) |
|
8711 (if symbol |
|
8712 ;; must be duplicate parameter. Second parameter hides the |
|
8713 ;; first, so go ahead and add the second pararameter |
|
8714 (js2-report-warning "msg.dup.parms" name)) |
|
8715 (js2-define-new-symbol decl-type name node)) |
|
8716 |
|
8717 (t (js2-code-bug))))) |
|
8718 |
|
8719 (defun js2-parse-expr () |
|
8720 (let* ((pn (js2-parse-assign-expr)) |
|
8721 (pos (js2-node-pos pn)) |
|
8722 left |
|
8723 right |
|
8724 op-pos) |
|
8725 (while (js2-match-token js2-COMMA) |
|
8726 (setq op-pos (- js2-token-beg pos)) ; relative |
|
8727 (if (= (js2-peek-token) js2-YIELD) |
|
8728 (js2-report-error "msg.yield.parenthesized")) |
|
8729 (setq right (js2-parse-assign-expr) |
|
8730 left pn |
|
8731 pn (make-js2-infix-node :type js2-COMMA |
|
8732 :pos pos |
|
8733 :len (- js2-ts-cursor pos) |
|
8734 :op-pos op-pos |
|
8735 :left left |
|
8736 :right right)) |
|
8737 (js2-node-add-children pn left right)) |
|
8738 pn)) |
|
8739 |
|
8740 (defun js2-parse-assign-expr () |
|
8741 (let ((tt (js2-peek-token)) |
|
8742 (pos js2-token-beg) |
|
8743 pn |
|
8744 left |
|
8745 right |
|
8746 op-pos) |
|
8747 (if (= tt js2-YIELD) |
|
8748 (js2-parse-return-or-yield tt t) |
|
8749 ;; not yield - parse assignment expression |
|
8750 (setq pn (js2-parse-cond-expr) |
|
8751 tt (js2-peek-token)) |
|
8752 (when (and (<= js2-first-assign tt) |
|
8753 (<= tt js2-last-assign)) |
|
8754 (js2-consume-token) |
|
8755 (setq op-pos (- js2-token-beg pos) ; relative |
|
8756 left pn |
|
8757 right (js2-parse-assign-expr) |
|
8758 pn (make-js2-assign-node :type tt |
|
8759 :pos pos |
|
8760 :len (- (js2-node-end right) pos) |
|
8761 :op-pos op-pos |
|
8762 :left left |
|
8763 :right right)) |
|
8764 (when js2-parse-ide-mode |
|
8765 (js2-highlight-assign-targets pn left right) |
|
8766 (if (or (js2-function-node-p right) |
|
8767 (js2-object-node-p right)) |
|
8768 (js2-record-imenu-functions right left))) |
|
8769 ;; do this last so ide checks above can use absolute positions |
|
8770 (js2-node-add-children pn left right)) |
|
8771 pn))) |
|
8772 |
|
8773 (defun js2-parse-cond-expr () |
|
8774 (let ((pos js2-token-beg) |
|
8775 (pn (js2-parse-or-expr)) |
|
8776 test-expr |
|
8777 if-true |
|
8778 if-false |
|
8779 q-pos |
|
8780 c-pos) |
|
8781 (when (js2-match-token js2-HOOK) |
|
8782 (setq q-pos (- js2-token-beg pos) |
|
8783 if-true (js2-parse-assign-expr)) |
|
8784 (js2-must-match js2-COLON "msg.no.colon.cond") |
|
8785 (setq c-pos (- js2-token-beg pos) |
|
8786 if-false (js2-parse-assign-expr) |
|
8787 test-expr pn |
|
8788 pn (make-js2-cond-node :pos pos |
|
8789 :len (- (js2-node-end if-false) pos) |
|
8790 :test-expr test-expr |
|
8791 :true-expr if-true |
|
8792 :false-expr if-false |
|
8793 :q-pos q-pos |
|
8794 :c-pos c-pos)) |
|
8795 (js2-node-add-children pn test-expr if-true if-false)) |
|
8796 pn)) |
|
8797 |
|
8798 (defun js2-make-binary (type left parser) |
|
8799 "Helper for constructing a binary-operator AST node. |
|
8800 LEFT is the left-side-expression, already parsed, and the |
|
8801 binary operator should have just been matched. |
|
8802 PARSER is a function to call to parse the right operand, |
|
8803 or a `js2-node' struct if it has already been parsed." |
|
8804 (let* ((pos (js2-node-pos left)) |
|
8805 (op-pos (- js2-token-beg pos)) |
|
8806 (right (if (js2-node-p parser) |
|
8807 parser |
|
8808 (funcall parser))) |
|
8809 (pn (make-js2-infix-node :type type |
|
8810 :pos pos |
|
8811 :len (- (js2-node-end right) pos) |
|
8812 :op-pos op-pos |
|
8813 :left left |
|
8814 :right right))) |
|
8815 (js2-node-add-children pn left right) |
|
8816 pn)) |
|
8817 |
|
8818 (defun js2-parse-or-expr () |
|
8819 (let ((pn (js2-parse-and-expr))) |
|
8820 (when (js2-match-token js2-OR) |
|
8821 (setq pn (js2-make-binary js2-OR |
|
8822 pn |
|
8823 'js2-parse-or-expr))) |
|
8824 pn)) |
|
8825 |
|
8826 (defun js2-parse-and-expr () |
|
8827 (let ((pn (js2-parse-bit-or-expr))) |
|
8828 (when (js2-match-token js2-AND) |
|
8829 (setq pn (js2-make-binary js2-AND |
|
8830 pn |
|
8831 'js2-parse-and-expr))) |
|
8832 pn)) |
|
8833 |
|
8834 (defun js2-parse-bit-or-expr () |
|
8835 (let ((pn (js2-parse-bit-xor-expr))) |
|
8836 (while (js2-match-token js2-BITOR) |
|
8837 (setq pn (js2-make-binary js2-BITOR |
|
8838 pn |
|
8839 'js2-parse-bit-xor-expr))) |
|
8840 pn)) |
|
8841 |
|
8842 (defun js2-parse-bit-xor-expr () |
|
8843 (let ((pn (js2-parse-bit-and-expr))) |
|
8844 (while (js2-match-token js2-BITXOR) |
|
8845 (setq pn (js2-make-binary js2-BITXOR |
|
8846 pn |
|
8847 'js2-parse-bit-and-expr))) |
|
8848 pn)) |
|
8849 |
|
8850 (defun js2-parse-bit-and-expr () |
|
8851 (let ((pn (js2-parse-eq-expr))) |
|
8852 (while (js2-match-token js2-BITAND) |
|
8853 (setq pn (js2-make-binary js2-BITAND |
|
8854 pn |
|
8855 'js2-parse-eq-expr))) |
|
8856 pn)) |
|
8857 |
|
8858 (defconst js2-parse-eq-ops |
|
8859 (list js2-EQ js2-NE js2-SHEQ js2-SHNE)) |
|
8860 |
|
8861 (defun js2-parse-eq-expr () |
|
8862 (let ((pn (js2-parse-rel-expr)) |
|
8863 tt) |
|
8864 (while (memq (setq tt (js2-peek-token)) js2-parse-eq-ops) |
|
8865 (js2-consume-token) |
|
8866 (setq pn (js2-make-binary tt |
|
8867 pn |
|
8868 'js2-parse-rel-expr))) |
|
8869 pn)) |
|
8870 |
|
8871 (defconst js2-parse-rel-ops |
|
8872 (list js2-IN js2-INSTANCEOF js2-LE js2-LT js2-GE js2-GT)) |
|
8873 |
|
8874 (defun js2-parse-rel-expr () |
|
8875 (let ((pn (js2-parse-shift-expr)) |
|
8876 (continue t) |
|
8877 tt) |
|
8878 (while continue |
|
8879 (setq tt (js2-peek-token)) |
|
8880 (cond |
|
8881 ((and js2-in-for-init (= tt js2-IN)) |
|
8882 (setq continue nil)) |
|
8883 ((memq tt js2-parse-rel-ops) |
|
8884 (js2-consume-token) |
|
8885 (setq pn (js2-make-binary tt pn 'js2-parse-shift-expr))) |
|
8886 (t |
|
8887 (setq continue nil)))) |
|
8888 pn)) |
|
8889 |
|
8890 (defconst js2-parse-shift-ops |
|
8891 (list js2-LSH js2-URSH js2-RSH)) |
|
8892 |
|
8893 (defun js2-parse-shift-expr () |
|
8894 (let ((pn (js2-parse-add-expr)) |
|
8895 tt |
|
8896 (continue t)) |
|
8897 (while continue |
|
8898 (setq tt (js2-peek-token)) |
|
8899 (if (memq tt js2-parse-shift-ops) |
|
8900 (progn |
|
8901 (js2-consume-token) |
|
8902 (setq pn (js2-make-binary tt pn 'js2-parse-add-expr))) |
|
8903 (setq continue nil))) |
|
8904 pn)) |
|
8905 |
|
8906 (defun js2-parse-add-expr () |
|
8907 (let ((pn (js2-parse-mul-expr)) |
|
8908 tt |
|
8909 (continue t)) |
|
8910 (while continue |
|
8911 (setq tt (js2-peek-token)) |
|
8912 (if (or (= tt js2-ADD) (= tt js2-SUB)) |
|
8913 (progn |
|
8914 (js2-consume-token) |
|
8915 (setq pn (js2-make-binary tt pn 'js2-parse-mul-expr))) |
|
8916 (setq continue nil))) |
|
8917 pn)) |
|
8918 |
|
8919 (defconst js2-parse-mul-ops |
|
8920 (list js2-MUL js2-DIV js2-MOD)) |
|
8921 |
|
8922 (defun js2-parse-mul-expr () |
|
8923 (let ((pn (js2-parse-unary-expr)) |
|
8924 tt |
|
8925 (continue t)) |
|
8926 (while continue |
|
8927 (setq tt (js2-peek-token)) |
|
8928 (if (memq tt js2-parse-mul-ops) |
|
8929 (progn |
|
8930 (js2-consume-token) |
|
8931 (setq pn (js2-make-binary tt pn 'js2-parse-unary-expr))) |
|
8932 (setq continue nil))) |
|
8933 pn)) |
|
8934 |
|
8935 (defsubst js2-make-unary (type parser &rest args) |
|
8936 "Make a unary node of type TYPE. |
|
8937 PARSER is either a node (for postfix operators) or a function to call |
|
8938 to parse the operand (for prefix operators)." |
|
8939 (let* ((pos js2-token-beg) |
|
8940 (postfix (js2-node-p parser)) |
|
8941 (expr (if postfix |
|
8942 parser |
|
8943 (apply parser args))) |
|
8944 end |
|
8945 pn) |
|
8946 (if postfix ; e.g. i++ |
|
8947 (setq pos (js2-node-pos expr) |
|
8948 end js2-token-end) |
|
8949 (setq end (js2-node-end expr))) |
|
8950 (setq pn (make-js2-unary-node :type type |
|
8951 :pos pos |
|
8952 :len (- end pos) |
|
8953 :operand expr)) |
|
8954 (js2-node-add-children pn expr) |
|
8955 pn)) |
|
8956 |
|
8957 (defconst js2-incrementable-node-types |
|
8958 (list js2-NAME js2-GETPROP js2-GETELEM js2-GET_REF js2-CALL) |
|
8959 "Node types that can be the operand of a ++ or -- operator.") |
|
8960 |
|
8961 (defsubst js2-check-bad-inc-dec (tt beg end unary) |
|
8962 (unless (memq (js2-node-type (js2-unary-node-operand unary)) |
|
8963 js2-incrementable-node-types) |
|
8964 (js2-report-error (if (= tt js2-INC) |
|
8965 "msg.bad.incr" |
|
8966 "msg.bad.decr") |
|
8967 nil beg (- end beg)))) |
|
8968 |
|
8969 (defun js2-parse-unary-expr () |
|
8970 (let ((tt (js2-peek-token)) |
|
8971 pn expr beg end) |
|
8972 (cond |
|
8973 ((or (= tt js2-VOID) |
|
8974 (= tt js2-NOT) |
|
8975 (= tt js2-BITNOT) |
|
8976 (= tt js2-TYPEOF)) |
|
8977 (js2-consume-token) |
|
8978 (js2-make-unary tt 'js2-parse-unary-expr)) |
|
8979 |
|
8980 ((= tt js2-ADD) |
|
8981 (js2-consume-token) |
|
8982 ;; Convert to special POS token in decompiler and parse tree |
|
8983 (js2-make-unary js2-POS 'js2-parse-unary-expr)) |
|
8984 |
|
8985 ((= tt js2-SUB) |
|
8986 (js2-consume-token) |
|
8987 ;; Convert to special NEG token in decompiler and parse tree |
|
8988 (js2-make-unary js2-NEG 'js2-parse-unary-expr)) |
|
8989 |
|
8990 ((or (= tt js2-INC) |
|
8991 (= tt js2-DEC)) |
|
8992 (js2-consume-token) |
|
8993 (prog1 |
|
8994 (setq beg js2-token-beg |
|
8995 end js2-token-end |
|
8996 expr (js2-make-unary tt 'js2-parse-member-expr t)) |
|
8997 (js2-check-bad-inc-dec tt beg end expr))) |
|
8998 |
|
8999 ((= tt js2-DELPROP) |
|
9000 (js2-consume-token) |
|
9001 (js2-make-unary js2-DELPROP 'js2-parse-unary-expr)) |
|
9002 |
|
9003 ((= tt js2-ERROR) |
|
9004 (js2-consume-token) |
|
9005 (make-js2-error-node)) ; try to continue |
|
9006 |
|
9007 ((and (= tt js2-LT) |
|
9008 js2-compiler-xml-available) |
|
9009 ;; XML stream encountered in expression. |
|
9010 (js2-consume-token) |
|
9011 (js2-parse-member-expr-tail t (js2-parse-xml-initializer))) |
|
9012 (t |
|
9013 (setq pn (js2-parse-member-expr t) |
|
9014 ;; Don't look across a newline boundary for a postfix incop. |
|
9015 tt (js2-peek-token-or-eol)) |
|
9016 (when (or (= tt js2-INC) (= tt js2-DEC)) |
|
9017 (js2-consume-token) |
|
9018 (setf expr pn |
|
9019 pn (js2-make-unary tt expr)) |
|
9020 (js2-node-set-prop pn 'postfix t) |
|
9021 (js2-check-bad-inc-dec tt js2-token-beg js2-token-end pn)) |
|
9022 pn)))) |
|
9023 |
|
9024 (defun js2-parse-xml-initializer () |
|
9025 "Parse an E4X XML initializer. |
|
9026 I'm parsing it the way Rhino parses it, but without the tree-rewriting. |
|
9027 Then I'll postprocess the result, depending on whether we're in IDE |
|
9028 mode or codegen mode, and generate the appropriate rewritten AST. |
|
9029 IDE mode uses a rich AST that models the XML structure. Codegen mode |
|
9030 just concatenates everything and makes a new XML or XMLList out of it." |
|
9031 (let ((tt (js2-get-first-xml-token)) |
|
9032 pn-xml |
|
9033 pn |
|
9034 expr |
|
9035 kids |
|
9036 expr-pos |
|
9037 (continue t) |
|
9038 (first-token t)) |
|
9039 (when (not (or (= tt js2-XML) (= tt js2-XMLEND))) |
|
9040 (js2-report-error "msg.syntax")) |
|
9041 (setq pn-xml (make-js2-xml-node)) |
|
9042 (while continue |
|
9043 (if first-token |
|
9044 (setq first-token nil) |
|
9045 (setq tt (js2-get-next-xml-token))) |
|
9046 (cond |
|
9047 ;; js2-XML means we found a {expr} in the XML stream. |
|
9048 ;; The js2-ts-string is the XML up to the left-curly. |
|
9049 ((= tt js2-XML) |
|
9050 (push (make-js2-string-node :pos js2-token-beg |
|
9051 :len (- js2-ts-cursor js2-token-beg)) |
|
9052 kids) |
|
9053 (js2-must-match js2-LC "msg.syntax") |
|
9054 (setq expr-pos js2-ts-cursor |
|
9055 expr (if (eq (js2-peek-token) js2-RC) |
|
9056 (make-js2-empty-expr-node :pos expr-pos) |
|
9057 (js2-parse-expr))) |
|
9058 (js2-must-match js2-RC "msg.syntax") |
|
9059 (setq pn (make-js2-xml-js-expr-node :pos (js2-node-pos expr) |
|
9060 :len (js2-node-len expr) |
|
9061 :expr expr)) |
|
9062 (js2-node-add-children pn expr) |
|
9063 (push pn kids)) |
|
9064 |
|
9065 ;; a js2-XMLEND token means we hit the final close-tag. |
|
9066 ((= tt js2-XMLEND) |
|
9067 (push (make-js2-string-node :pos js2-token-beg |
|
9068 :len (- js2-ts-cursor js2-token-beg)) |
|
9069 kids) |
|
9070 (dolist (kid (nreverse kids)) |
|
9071 (js2-block-node-push pn-xml kid)) |
|
9072 (setf (js2-node-len pn-xml) (- js2-ts-cursor |
|
9073 (js2-node-pos pn-xml)) |
|
9074 continue nil)) |
|
9075 (t |
|
9076 (js2-report-error "msg.syntax") |
|
9077 (setq continue nil)))) |
|
9078 pn-xml)) |
|
9079 |
|
9080 |
|
9081 (defun js2-parse-argument-list () |
|
9082 "Parse an argument list and return it as a lisp list of nodes. |
|
9083 Returns the list in reverse order. Consumes the right-paren token." |
|
9084 (let (result) |
|
9085 (unless (js2-match-token js2-RP) |
|
9086 (loop do |
|
9087 (if (= (js2-peek-token) js2-YIELD) |
|
9088 (js2-report-error "msg.yield.parenthesized")) |
|
9089 (push (js2-parse-assign-expr) result) |
|
9090 while |
|
9091 (js2-match-token js2-COMMA)) |
|
9092 (js2-must-match js2-RP "msg.no.paren.arg") |
|
9093 result))) |
|
9094 |
|
9095 (defun js2-parse-member-expr (&optional allow-call-syntax) |
|
9096 (let ((tt (js2-peek-token)) |
|
9097 pn |
|
9098 pos |
|
9099 target |
|
9100 args |
|
9101 beg |
|
9102 end |
|
9103 init |
|
9104 tail) |
|
9105 (if (/= tt js2-NEW) |
|
9106 (setq pn (js2-parse-primary-expr)) |
|
9107 ;; parse a 'new' expression |
|
9108 (js2-consume-token) |
|
9109 (setq pos js2-token-beg |
|
9110 beg pos |
|
9111 target (js2-parse-member-expr) |
|
9112 end (js2-node-end target) |
|
9113 pn (make-js2-new-node :pos pos |
|
9114 :target target |
|
9115 :len (- end pos))) |
|
9116 (js2-node-add-children pn target) |
|
9117 (when (js2-match-token js2-LP) |
|
9118 ;; Add the arguments to pn, if any are supplied. |
|
9119 (setf beg pos ; start of "new" keyword |
|
9120 pos js2-token-beg |
|
9121 args (nreverse (js2-parse-argument-list)) |
|
9122 (js2-new-node-args pn) args |
|
9123 end js2-token-end |
|
9124 (js2-new-node-lp pn) (- pos beg) |
|
9125 (js2-new-node-rp pn) (- end 1 beg)) |
|
9126 (apply #'js2-node-add-children pn args)) |
|
9127 |
|
9128 (when (and js2-allow-rhino-new-expr-initializer |
|
9129 (js2-match-token js2-LC)) |
|
9130 (setf init (js2-parse-object-literal) |
|
9131 end (js2-node-end init) |
|
9132 (js2-new-node-initializer pn) init) |
|
9133 (js2-node-add-children pn init)) |
|
9134 |
|
9135 (setf (js2-node-len pn) (- beg pos))) ; end outer if |
|
9136 |
|
9137 (js2-parse-member-expr-tail allow-call-syntax pn))) |
|
9138 |
|
9139 (defun js2-parse-member-expr-tail (allow-call-syntax pn) |
|
9140 "Parse a chain of property/array accesses or function calls. |
|
9141 Includes parsing for E4X operators like `..' and `.@'. |
|
9142 If ALLOW-CALL-SYNTAX is nil, stops when we encounter a left-paren. |
|
9143 Returns an expression tree that includes PN, the parent node." |
|
9144 (let ((beg (js2-node-pos pn)) |
|
9145 tt |
|
9146 (continue t)) |
|
9147 (while continue |
|
9148 (setq tt (js2-peek-token)) |
|
9149 (cond |
|
9150 ((or (= tt js2-DOT) (= tt js2-DOTDOT)) |
|
9151 (setq pn (js2-parse-property-access tt pn))) |
|
9152 |
|
9153 ((= tt js2-DOTQUERY) |
|
9154 (setq pn (js2-parse-dot-query pn))) |
|
9155 |
|
9156 ((= tt js2-LB) |
|
9157 (setq pn (js2-parse-element-get pn))) |
|
9158 |
|
9159 ((= tt js2-LP) |
|
9160 (if allow-call-syntax |
|
9161 (setq pn (js2-parse-function-call pn)) |
|
9162 (setq continue nil))) |
|
9163 (t |
|
9164 (setq continue nil)))) |
|
9165 (if (>= js2-highlight-level 2) |
|
9166 (js2-parse-highlight-member-expr-node pn)) |
|
9167 pn)) |
|
9168 |
|
9169 (defun js2-parse-dot-query (pn) |
|
9170 "Parse a dot-query expression, e.g. foo.bar.(@name == 2) |
|
9171 Last token parsed must be `js2-DOTQUERY'." |
|
9172 (let ((pos (js2-node-pos pn)) |
|
9173 op-pos |
|
9174 expr |
|
9175 end) |
|
9176 (js2-consume-token) |
|
9177 (js2-must-have-xml) |
|
9178 (js2-set-requires-activation) |
|
9179 (setq op-pos js2-token-beg |
|
9180 expr (js2-parse-expr) |
|
9181 end (js2-node-end expr) |
|
9182 pn (make-js2-xml-dot-query-node :left pn |
|
9183 :pos pos |
|
9184 :op-pos op-pos |
|
9185 :right expr)) |
|
9186 (js2-node-add-children pn |
|
9187 (js2-xml-dot-query-node-left pn) |
|
9188 (js2-xml-dot-query-node-right pn)) |
|
9189 (if (js2-must-match js2-RP "msg.no.paren") |
|
9190 (setf (js2-xml-dot-query-node-rp pn) js2-token-beg |
|
9191 end js2-token-end)) |
|
9192 (setf (js2-node-len pn) (- end pos)) |
|
9193 pn)) |
|
9194 |
|
9195 (defun js2-parse-element-get (pn) |
|
9196 "Parse an element-get expression, e.g. foo[bar]. |
|
9197 Last token parsed must be `js2-RB'." |
|
9198 (let ((lb js2-token-beg) |
|
9199 (pos (js2-node-pos pn)) |
|
9200 rb |
|
9201 expr) |
|
9202 (js2-consume-token) |
|
9203 (setq expr (js2-parse-expr)) |
|
9204 (if (js2-must-match js2-RB "msg.no.bracket.index") |
|
9205 (setq rb js2-token-beg)) |
|
9206 (setq pn (make-js2-elem-get-node :target pn |
|
9207 :pos pos |
|
9208 :element expr |
|
9209 :lb (js2-relpos lb pos) |
|
9210 :rb (js2-relpos rb pos) |
|
9211 :len (- js2-token-end pos))) |
|
9212 (js2-node-add-children pn |
|
9213 (js2-elem-get-node-target pn) |
|
9214 (js2-elem-get-node-element pn)) |
|
9215 pn)) |
|
9216 |
|
9217 (defun js2-parse-function-call (pn) |
|
9218 (let (args |
|
9219 (pos (js2-node-pos pn))) |
|
9220 (js2-consume-token) |
|
9221 (setq pn (make-js2-call-node :pos pos |
|
9222 :target pn |
|
9223 :lp (- js2-token-beg pos))) |
|
9224 (js2-node-add-children pn (js2-call-node-target pn)) |
|
9225 |
|
9226 ;; Add the arguments to pn, if any are supplied. |
|
9227 (setf args (nreverse (js2-parse-argument-list)) |
|
9228 (js2-call-node-rp pn) (- js2-token-beg pos) |
|
9229 (js2-call-node-args pn) args) |
|
9230 (apply #'js2-node-add-children pn args) |
|
9231 |
|
9232 (setf (js2-node-len pn) (- js2-ts-cursor pos)) |
|
9233 pn)) |
|
9234 |
|
9235 (defun js2-parse-property-access (tt pn) |
|
9236 "Parse a property access, XML descendants access, or XML attr access." |
|
9237 (let ((member-type-flags 0) |
|
9238 (dot-pos js2-token-beg) |
|
9239 (dot-len (if (= tt js2-DOTDOT) 2 1)) |
|
9240 name |
|
9241 ref ; right side of . or .. operator |
|
9242 result) |
|
9243 (js2-consume-token) |
|
9244 (when (= tt js2-DOTDOT) |
|
9245 (js2-must-have-xml) |
|
9246 (setq member-type-flags js2-descendants-flag)) |
|
9247 (if (not js2-compiler-xml-available) |
|
9248 (progn |
|
9249 (js2-must-match-prop-name "msg.no.name.after.dot") |
|
9250 (setq name (js2-create-name-node t js2-GETPROP) |
|
9251 result (make-js2-prop-get-node :left pn |
|
9252 :pos js2-token-beg |
|
9253 :right name |
|
9254 :len (- js2-token-end |
|
9255 js2-token-beg))) |
|
9256 (js2-node-add-children result pn name) |
|
9257 result) |
|
9258 ;; otherwise look for XML operators |
|
9259 (setf result (if (= tt js2-DOT) |
|
9260 (make-js2-prop-get-node) |
|
9261 (make-js2-infix-node :type js2-DOTDOT)) |
|
9262 (js2-node-pos result) (js2-node-pos pn) |
|
9263 (js2-infix-node-op-pos result) dot-pos |
|
9264 (js2-infix-node-left result) pn ; do this after setting position |
|
9265 tt (js2-next-token)) |
|
9266 (cond |
|
9267 ;; needed for generator.throw() |
|
9268 ((= tt js2-THROW) |
|
9269 (js2-save-name-token-data js2-token-beg "throw") |
|
9270 (setq ref (js2-parse-property-name nil js2-ts-string member-type-flags))) |
|
9271 |
|
9272 ;; handles: name, ns::name, ns::*, ns::[expr] |
|
9273 ((js2-valid-prop-name-token tt) |
|
9274 (setq ref (js2-parse-property-name -1 js2-ts-string member-type-flags))) |
|
9275 |
|
9276 ;; handles: *, *::name, *::*, *::[expr] |
|
9277 ((= tt js2-MUL) |
|
9278 (js2-save-name-token-data js2-token-beg "*") |
|
9279 (setq ref (js2-parse-property-name nil "*" member-type-flags))) |
|
9280 |
|
9281 ;; handles: '@attr', '@ns::attr', '@ns::*', '@ns::[expr]', etc. |
|
9282 ((= tt js2-XMLATTR) |
|
9283 (setq result (js2-parse-attribute-access))) |
|
9284 |
|
9285 (t |
|
9286 (js2-report-error "msg.no.name.after.dot" nil dot-pos dot-len))) |
|
9287 |
|
9288 (if ref |
|
9289 (setf (js2-node-len result) (- (js2-node-end ref) |
|
9290 (js2-node-pos result)) |
|
9291 (js2-infix-node-right result) ref)) |
|
9292 (if (js2-infix-node-p result) |
|
9293 (js2-node-add-children result |
|
9294 (js2-infix-node-left result) |
|
9295 (js2-infix-node-right result))) |
|
9296 result))) |
|
9297 |
|
9298 (defun js2-parse-attribute-access () |
|
9299 "Parse an E4X XML attribute expression. |
|
9300 This includes expressions of the forms: |
|
9301 |
|
9302 @attr @ns::attr @ns::* |
|
9303 @* @*::attr @*::* |
|
9304 @[expr] @*::[expr] @ns::[expr] |
|
9305 |
|
9306 Called if we peeked an '@' token." |
|
9307 (let ((tt (js2-next-token)) |
|
9308 (at-pos js2-token-beg)) |
|
9309 (cond |
|
9310 ;; handles: @name, @ns::name, @ns::*, @ns::[expr] |
|
9311 ((js2-valid-prop-name-token tt) |
|
9312 (js2-parse-property-name at-pos js2-ts-string 0)) |
|
9313 |
|
9314 ;; handles: @*, @*::name, @*::*, @*::[expr] |
|
9315 ((= tt js2-MUL) |
|
9316 (js2-save-name-token-data js2-token-beg "*") |
|
9317 (js2-parse-property-name js2-token-beg "*" 0)) |
|
9318 |
|
9319 ;; handles @[expr] |
|
9320 ((= tt js2-LB) |
|
9321 (js2-parse-xml-elem-ref at-pos)) |
|
9322 |
|
9323 (t |
|
9324 (js2-report-error "msg.no.name.after.xmlAttr") |
|
9325 ;; Avoid cascaded errors that happen if we make an error node here. |
|
9326 (js2-save-name-token-data js2-token-beg "") |
|
9327 (js2-parse-property-name js2-token-beg "" 0))))) |
|
9328 |
|
9329 (defun js2-parse-property-name (at-pos s member-type-flags) |
|
9330 "Check if :: follows name in which case it becomes qualified name. |
|
9331 |
|
9332 AT-POS is a natural number if we just read an '@' token, else nil. |
|
9333 S is the name or string that was matched: an identifier, 'throw' or '*'. |
|
9334 MEMBER-TYPE-FLAGS is a bit set tracking whether we're a '.' or '..' child. |
|
9335 |
|
9336 Returns a `js2-xml-ref-node' if it's an attribute access, a child of a '..' |
|
9337 operator, or the name is followed by ::. For a plain name, returns a |
|
9338 `js2-name-node'. Returns a `js2-error-node' for malformed XML expressions." |
|
9339 (let ((pos (or at-pos js2-token-beg)) |
|
9340 colon-pos |
|
9341 (name (js2-create-name-node t js2-current-token)) |
|
9342 ns |
|
9343 tt |
|
9344 ref |
|
9345 pn) |
|
9346 (catch 'return |
|
9347 (when (js2-match-token js2-COLONCOLON) |
|
9348 (setq ns name |
|
9349 colon-pos js2-token-beg |
|
9350 tt (js2-next-token)) |
|
9351 (cond |
|
9352 ;; handles name::name |
|
9353 ((js2-valid-prop-name-token tt) |
|
9354 (setq name (js2-create-name-node))) |
|
9355 |
|
9356 ;; handles name::* |
|
9357 ((= tt js2-MUL) |
|
9358 (js2-save-name-token-data js2-token-beg "*") |
|
9359 (setq name (js2-create-name-node))) |
|
9360 |
|
9361 ;; handles name::[expr] |
|
9362 ((= tt js2-LB) |
|
9363 (throw 'return (js2-parse-xml-elem-ref at-pos ns colon-pos))) |
|
9364 |
|
9365 (t |
|
9366 (js2-report-error "msg.no.name.after.coloncolon")))) |
|
9367 |
|
9368 (if (and (null ns) (zerop member-type-flags)) |
|
9369 name |
|
9370 (prog1 |
|
9371 (setq pn |
|
9372 (make-js2-xml-prop-ref-node :pos pos |
|
9373 :len (- (js2-node-end name) pos) |
|
9374 :at-pos at-pos |
|
9375 :colon-pos colon-pos |
|
9376 :propname name)) |
|
9377 (js2-node-add-children pn name)))))) |
|
9378 |
|
9379 (defun js2-parse-xml-elem-ref (at-pos &optional namespace colon-pos) |
|
9380 "Parse the [expr] portion of an xml element reference. |
|
9381 For instance, @[expr], @*::[expr], or ns::[expr]." |
|
9382 (let* ((lb js2-token-beg) |
|
9383 (pos (or at-pos lb)) |
|
9384 rb |
|
9385 (expr (js2-parse-expr)) |
|
9386 (end (js2-node-end expr)) |
|
9387 pn) |
|
9388 (if (js2-must-match js2-RB "msg.no.bracket.index") |
|
9389 (setq rb js2-token-beg |
|
9390 end js2-token-end)) |
|
9391 (prog1 |
|
9392 (setq pn |
|
9393 (make-js2-xml-elem-ref-node :pos pos |
|
9394 :len (- end pos) |
|
9395 :namespace namespace |
|
9396 :colon-pos colon-pos |
|
9397 :at-pos at-pos |
|
9398 :expr expr |
|
9399 :lb (js2-relpos lb pos) |
|
9400 :rb (js2-relpos rb pos))) |
|
9401 (js2-node-add-children pn namespace expr)))) |
|
9402 |
|
9403 (defun js2-parse-primary-expr () |
|
9404 "Parses a literal (leaf) expression of some sort. |
|
9405 Includes complex literals such as functions, object-literals, |
|
9406 array-literals, array comprehensions and regular expressions." |
|
9407 (let ((tt-flagged (js2-next-flagged-token)) |
|
9408 pn ; parent node (usually return value) |
|
9409 tt |
|
9410 px-pos ; paren-expr pos |
|
9411 len |
|
9412 flags ; regexp flags |
|
9413 expr) |
|
9414 (setq tt js2-current-token) |
|
9415 (cond |
|
9416 ((= tt js2-FUNCTION) |
|
9417 (js2-parse-function 'FUNCTION_EXPRESSION)) |
|
9418 |
|
9419 ((= tt js2-LB) |
|
9420 (js2-parse-array-literal)) |
|
9421 |
|
9422 ((= tt js2-LC) |
|
9423 (js2-parse-object-literal)) |
|
9424 |
|
9425 ((= tt js2-LET) |
|
9426 (js2-parse-let js2-token-beg)) |
|
9427 |
|
9428 ((= tt js2-LP) |
|
9429 (setq px-pos js2-token-beg |
|
9430 expr (js2-parse-expr)) |
|
9431 (js2-must-match js2-RP "msg.no.paren") |
|
9432 (setq pn (make-js2-paren-node :pos px-pos |
|
9433 :expr expr |
|
9434 :len (- js2-token-end px-pos))) |
|
9435 (js2-node-add-children pn (js2-paren-node-expr pn)) |
|
9436 pn) |
|
9437 |
|
9438 ((= tt js2-XMLATTR) |
|
9439 (js2-must-have-xml) |
|
9440 (js2-parse-attribute-access)) |
|
9441 |
|
9442 ((= tt js2-NAME) |
|
9443 (js2-parse-name tt-flagged tt)) |
|
9444 |
|
9445 ((= tt js2-NUMBER) |
|
9446 (make-js2-number-node)) |
|
9447 |
|
9448 ((= tt js2-STRING) |
|
9449 (prog1 |
|
9450 (make-js2-string-node) |
|
9451 (js2-record-face 'font-lock-string-face))) |
|
9452 |
|
9453 ((or (= tt js2-DIV) (= tt js2-ASSIGN_DIV)) |
|
9454 ;; Got / or /= which in this context means a regexp literal |
|
9455 (setq px-pos js2-token-beg) |
|
9456 (js2-read-regexp tt) |
|
9457 (setq flags js2-ts-regexp-flags |
|
9458 js2-ts-regexp-flags nil) |
|
9459 (prog1 |
|
9460 (make-js2-regexp-node :pos px-pos |
|
9461 :len (- js2-ts-cursor px-pos) |
|
9462 :value js2-ts-string |
|
9463 :flags flags) |
|
9464 (js2-set-face px-pos js2-ts-cursor 'font-lock-string-face 'record))) |
|
9465 |
|
9466 ((or (= tt js2-NULL) |
|
9467 (= tt js2-THIS) |
|
9468 (= tt js2-FALSE) |
|
9469 (= tt js2-TRUE)) |
|
9470 (make-js2-keyword-node :type tt)) |
|
9471 |
|
9472 ((= tt js2-RESERVED) |
|
9473 (js2-report-error "msg.reserved.id") |
|
9474 (make-js2-name-node)) |
|
9475 |
|
9476 ((= tt js2-ERROR) |
|
9477 ;; the scanner or one of its subroutines reported the error. |
|
9478 (make-js2-error-node)) |
|
9479 |
|
9480 ((= tt js2-EOF) |
|
9481 (setq px-pos (point-at-bol) |
|
9482 len (- js2-ts-cursor px-pos)) |
|
9483 (js2-report-error "msg.unexpected.eof" nil px-pos len) |
|
9484 (make-js2-error-node :pos px-pos :len len)) |
|
9485 |
|
9486 (t |
|
9487 (js2-report-error "msg.syntax") |
|
9488 (make-js2-error-node))))) |
|
9489 |
|
9490 (defun js2-parse-name (tt-flagged tt) |
|
9491 (let ((name js2-ts-string) |
|
9492 (name-pos js2-token-beg)) |
|
9493 (if (and (js2-flag-set-p tt-flagged js2-ti-check-label) |
|
9494 (= (js2-peek-token) js2-COLON)) |
|
9495 (prog1 |
|
9496 ;; Do not consume colon, it is used as unwind indicator |
|
9497 ;; to return to statementHelper. |
|
9498 (make-js2-label-node :pos name-pos |
|
9499 :len (- js2-token-end name-pos) |
|
9500 :name name) |
|
9501 (js2-set-face name-pos |
|
9502 js2-token-end |
|
9503 'font-lock-variable-name-face 'record)) |
|
9504 ;; Otherwise not a label, just a name. Unfortunately peeking |
|
9505 ;; the next token to check for a colon has biffed js2-token-beg |
|
9506 ;; and js2-token-end. We store the name's bounds in buffer vars |
|
9507 ;; and `js2-create-name-node' uses them. |
|
9508 (js2-save-name-token-data name-pos name) |
|
9509 (if js2-compiler-xml-available |
|
9510 (js2-parse-property-name nil name 0) |
|
9511 (js2-create-name-node 'check-activation))))) |
|
9512 |
|
9513 (defsubst js2-parse-warn-trailing-comma (msg pos elems comma-pos) |
|
9514 (js2-add-strict-warning |
|
9515 msg nil |
|
9516 ;; back up from comma to beginning of line or array/objlit |
|
9517 (max (if elems |
|
9518 (js2-node-pos (car elems)) |
|
9519 pos) |
|
9520 (save-excursion |
|
9521 (goto-char comma-pos) |
|
9522 (back-to-indentation) |
|
9523 (point))) |
|
9524 comma-pos)) |
|
9525 |
|
9526 (defun js2-parse-array-literal () |
|
9527 (let ((pos js2-token-beg) |
|
9528 (end js2-token-end) |
|
9529 (after-lb-or-comma t) |
|
9530 after-comma |
|
9531 tt |
|
9532 elems |
|
9533 pn |
|
9534 (continue t)) |
|
9535 (while continue |
|
9536 (setq tt (js2-peek-token)) |
|
9537 (cond |
|
9538 ;; comma |
|
9539 ((= tt js2-COMMA) |
|
9540 (js2-consume-token) |
|
9541 (setq after-comma js2-token-end) |
|
9542 (if (not after-lb-or-comma) |
|
9543 (setq after-lb-or-comma t) |
|
9544 (push nil elems))) |
|
9545 |
|
9546 ;; end of array |
|
9547 ((or (= tt js2-RB) |
|
9548 (= tt js2-EOF)) ; prevent infinite loop |
|
9549 (if (= tt js2-EOF) |
|
9550 (js2-report-error "msg.no.bracket.arg" nil pos) |
|
9551 (js2-consume-token)) |
|
9552 (setq continue nil |
|
9553 end js2-token-end |
|
9554 pn (make-js2-array-node :pos pos |
|
9555 :len (- js2-ts-cursor pos) |
|
9556 :elems (nreverse elems))) |
|
9557 (apply #'js2-node-add-children pn (js2-array-node-elems pn)) |
|
9558 (when after-comma |
|
9559 (js2-parse-warn-trailing-comma "msg.array.trailing.comma" |
|
9560 pos elems after-comma))) |
|
9561 |
|
9562 ;; array comp |
|
9563 ((and (>= js2-language-version 170) |
|
9564 (= tt js2-FOR) ; check for array comprehension |
|
9565 (not after-lb-or-comma) ; "for" can't follow a comma |
|
9566 elems ; must have at least 1 element |
|
9567 (not (cdr elems))) ; but no 2nd element |
|
9568 (setf continue nil |
|
9569 pn (js2-parse-array-comprehension (car elems) pos))) |
|
9570 |
|
9571 ;; another element |
|
9572 (t |
|
9573 (unless after-lb-or-comma |
|
9574 (js2-report-error "msg.no.bracket.arg")) |
|
9575 (push (js2-parse-assign-expr) elems) |
|
9576 (setq after-lb-or-comma nil |
|
9577 after-comma nil)))) |
|
9578 pn)) |
|
9579 |
|
9580 (defun js2-parse-array-comprehension (expr pos) |
|
9581 "Parse a JavaScript 1.7 Array Comprehension. |
|
9582 EXPR is the first expression after the opening left-bracket. |
|
9583 POS is the beginning of the LB token preceding EXPR. |
|
9584 We should have just parsed the 'for' keyword before calling this function." |
|
9585 (let (loops |
|
9586 filter |
|
9587 if-pos |
|
9588 result) |
|
9589 (while (= (js2-peek-token) js2-FOR) |
|
9590 (push (js2-parse-array-comp-loop) loops)) |
|
9591 (when (= (js2-peek-token) js2-IF) |
|
9592 (js2-consume-token) |
|
9593 (setq if-pos (- js2-token-beg pos) ; relative |
|
9594 filter (js2-parse-condition))) |
|
9595 (js2-must-match js2-RB "msg.no.bracket.arg" pos) |
|
9596 (setq result (make-js2-array-comp-node :pos pos |
|
9597 :len (- js2-ts-cursor pos) |
|
9598 :result expr |
|
9599 :loops (nreverse loops) |
|
9600 :filter (car filter) |
|
9601 :lp (js2-relpos (second filter) pos) |
|
9602 :rp (js2-relpos (third filter) pos) |
|
9603 :if-pos if-pos)) |
|
9604 (apply #'js2-node-add-children result expr (car filter) |
|
9605 (js2-array-comp-node-loops result)) |
|
9606 result)) |
|
9607 |
|
9608 (defun js2-parse-array-comp-loop () |
|
9609 "Parse a 'for [each] (foo in bar)' expression in an Array comprehension. |
|
9610 Last token peeked should be the initial FOR." |
|
9611 (let ((pos js2-token-beg) |
|
9612 (pn (make-js2-array-comp-loop-node)) |
|
9613 tt |
|
9614 iter |
|
9615 obj |
|
9616 foreach-p |
|
9617 in-pos |
|
9618 each-pos |
|
9619 lp |
|
9620 rp) |
|
9621 (assert (= (js2-next-token) js2-FOR)) ; consumes token |
|
9622 (js2-push-scope pn) |
|
9623 (unwind-protect |
|
9624 (progn |
|
9625 (when (js2-match-token js2-NAME) |
|
9626 (if (string= js2-ts-string "each") |
|
9627 (progn |
|
9628 (setq foreach-p t |
|
9629 each-pos (- js2-token-beg pos)) ; relative |
|
9630 (js2-record-face 'font-lock-keyword-face)) |
|
9631 (js2-report-error "msg.no.paren.for"))) |
|
9632 |
|
9633 (if (js2-must-match js2-LP "msg.no.paren.for") |
|
9634 (setq lp (- js2-token-beg pos))) |
|
9635 |
|
9636 (setq tt (js2-peek-token)) |
|
9637 (cond |
|
9638 ((or (= tt js2-LB) |
|
9639 (= tt js2-LC)) |
|
9640 ;; handle destructuring assignment |
|
9641 (setq iter (js2-parse-primary-expr))) |
|
9642 |
|
9643 ((js2-valid-prop-name-token tt) |
|
9644 (js2-consume-token) |
|
9645 (setq iter (js2-create-name-node))) |
|
9646 |
|
9647 (t |
|
9648 (js2-report-error "msg.bad.var"))) |
|
9649 |
|
9650 ;; Define as a let since we want the scope of the variable to |
|
9651 ;; be restricted to the array comprehension |
|
9652 (if (js2-name-node-p iter) |
|
9653 (js2-define-symbol js2-LET (js2-name-node-name iter) pn t)) |
|
9654 |
|
9655 (if (js2-must-match js2-IN "msg.in.after.for.name") |
|
9656 (setq in-pos (- js2-token-beg pos))) |
|
9657 |
|
9658 (setq obj (js2-parse-expr)) |
|
9659 (if (js2-must-match js2-RP "msg.no.paren.for.ctrl") |
|
9660 (setq rp (- js2-token-beg pos))) |
|
9661 |
|
9662 (setf (js2-node-pos pn) pos |
|
9663 (js2-node-len pn) (- js2-ts-cursor pos) |
|
9664 (js2-array-comp-loop-node-iterator pn) iter |
|
9665 (js2-array-comp-loop-node-object pn) obj |
|
9666 (js2-array-comp-loop-node-in-pos pn) in-pos |
|
9667 (js2-array-comp-loop-node-each-pos pn) each-pos |
|
9668 (js2-array-comp-loop-node-foreach-p pn) foreach-p |
|
9669 (js2-array-comp-loop-node-lp pn) lp |
|
9670 (js2-array-comp-loop-node-rp pn) rp) |
|
9671 (js2-node-add-children pn iter obj)) |
|
9672 (js2-pop-scope)) |
|
9673 pn)) |
|
9674 |
|
9675 (defun js2-parse-object-literal () |
|
9676 (let ((pos js2-token-beg) |
|
9677 tt |
|
9678 elems |
|
9679 result |
|
9680 after-comma |
|
9681 (continue t)) |
|
9682 (while continue |
|
9683 (setq tt (js2-peek-token)) |
|
9684 (cond |
|
9685 ;; {foo: ...}, {'foo': ...}, {get foo() {...}}, or {set foo(x) {...}} |
|
9686 ((or (js2-valid-prop-name-token tt) |
|
9687 (= tt js2-STRING)) |
|
9688 (setq after-comma nil |
|
9689 result (js2-parse-named-prop tt)) |
|
9690 (if (and (null result) |
|
9691 (not js2-recover-from-parse-errors)) |
|
9692 (setq continue nil) |
|
9693 (push result elems))) |
|
9694 |
|
9695 ;; {12: x} or {10.7: x} |
|
9696 ((= tt js2-NUMBER) |
|
9697 (js2-consume-token) |
|
9698 (setq after-comma nil) |
|
9699 (push (js2-parse-plain-property (make-js2-number-node)) elems)) |
|
9700 |
|
9701 ;; trailing comma |
|
9702 ((= tt js2-RC) |
|
9703 (setq continue nil) |
|
9704 (if after-comma |
|
9705 (js2-parse-warn-trailing-comma "msg.extra.trailing.comma" |
|
9706 pos elems after-comma))) |
|
9707 (t |
|
9708 (js2-report-error "msg.bad.prop") |
|
9709 (unless js2-recover-from-parse-errors |
|
9710 (setq continue nil)))) ; end switch |
|
9711 |
|
9712 (if (js2-match-token js2-COMMA) |
|
9713 (setq after-comma js2-token-end) |
|
9714 (setq continue nil))) ; end loop |
|
9715 |
|
9716 (js2-must-match js2-RC "msg.no.brace.prop") |
|
9717 (setq result (make-js2-object-node :pos pos |
|
9718 :len (- js2-ts-cursor pos) |
|
9719 :elems (nreverse elems))) |
|
9720 (apply #'js2-node-add-children result (js2-object-node-elems result)) |
|
9721 result)) |
|
9722 |
|
9723 (defun js2-parse-named-prop (tt) |
|
9724 "Parse a name, string, or getter/setter object property." |
|
9725 (js2-consume-token) |
|
9726 (let ((string-prop (and (= tt js2-STRING) |
|
9727 (make-js2-string-node))) |
|
9728 expr |
|
9729 (ppos js2-token-beg) |
|
9730 (pend js2-token-end) |
|
9731 (name (js2-create-name-node)) |
|
9732 (prop js2-ts-string)) |
|
9733 |
|
9734 (if (and (= tt js2-NAME) |
|
9735 (= (js2-peek-token) js2-NAME) |
|
9736 (or (string= prop "get") |
|
9737 (string= prop "set"))) |
|
9738 (progn |
|
9739 ;; getter/setter prop |
|
9740 (js2-consume-token) |
|
9741 (js2-set-face ppos pend 'font-lock-keyword-face 'record) ; get/set |
|
9742 (js2-record-face 'font-lock-function-name-face) ; for peeked name |
|
9743 (setq name (js2-create-name-node)) ; discard get/set & use peeked name |
|
9744 (js2-parse-getter-setter-prop ppos name (string= prop "get"))) |
|
9745 |
|
9746 ;; regular prop |
|
9747 (prog1 |
|
9748 (setq expr (js2-parse-plain-property (or string-prop name))) |
|
9749 (js2-set-face ppos pend |
|
9750 (if (js2-function-node-p |
|
9751 (js2-object-prop-node-right expr)) |
|
9752 'font-lock-function-name-face |
|
9753 'font-lock-variable-name-face) |
|
9754 'record))))) |
|
9755 |
|
9756 (defun js2-parse-plain-property (prop) |
|
9757 "Parse a non-getter/setter property in an object literal. |
|
9758 PROP is the node representing the property: a number, name or string." |
|
9759 (js2-must-match js2-COLON "msg.no.colon.prop") |
|
9760 (let* ((pos (js2-node-pos prop)) |
|
9761 (colon (- js2-token-beg pos)) |
|
9762 (expr (js2-parse-assign-expr)) |
|
9763 (result (make-js2-object-prop-node |
|
9764 :pos pos |
|
9765 ;; don't include last consumed token in length |
|
9766 :len (- (+ (js2-node-pos expr) |
|
9767 (js2-node-len expr)) |
|
9768 pos) |
|
9769 :left prop |
|
9770 :right expr |
|
9771 :op-pos colon))) |
|
9772 (js2-node-add-children result prop expr) |
|
9773 result)) |
|
9774 |
|
9775 (defun js2-parse-getter-setter-prop (pos prop get-p) |
|
9776 "Parse getter or setter property in an object literal. |
|
9777 JavaScript syntax is: |
|
9778 |
|
9779 { get foo() {...}, set foo(x) {...} } |
|
9780 |
|
9781 POS is the start position of the `get' or `set' keyword. |
|
9782 PROP is the `js2-name-node' representing the property name. |
|
9783 GET-P is non-nil if the keyword was `get'." |
|
9784 (let ((type (if get-p js2-GET js2-SET)) |
|
9785 result |
|
9786 end |
|
9787 (fn (js2-parse-function 'FUNCTION_EXPRESSION))) |
|
9788 |
|
9789 ;; it has to be an anonymous function, as we already parsed the name |
|
9790 (if (/= (js2-node-type fn) js2-FUNCTION) |
|
9791 (js2-report-error "msg.bad.prop") |
|
9792 (if (plusp (length (js2-function-name fn))) |
|
9793 (js2-report-error "msg.bad.prop"))) |
|
9794 |
|
9795 (js2-node-set-prop fn 'GETTER_SETTER type) ; for codegen |
|
9796 (setq end (js2-node-end fn) |
|
9797 result (make-js2-getter-setter-node :type type |
|
9798 :pos pos |
|
9799 :len (- end pos) |
|
9800 :left prop |
|
9801 :right fn)) |
|
9802 (js2-node-add-children result prop fn) |
|
9803 result)) |
|
9804 |
|
9805 (defun js2-create-name-node (&optional check-activation-p token) |
|
9806 "Create a name node using the token info from last scanned name. |
|
9807 In some cases we need to either synthesize a name node, or we lost |
|
9808 the name token information by peeking. If the TOKEN parameter is |
|
9809 not `js2-NAME', then we use the token info saved in instance vars." |
|
9810 (let ((beg js2-token-beg) |
|
9811 (s js2-ts-string) |
|
9812 name) |
|
9813 (when (/= js2-current-token js2-NAME) |
|
9814 (setq beg (or js2-prev-name-token-start js2-ts-cursor) |
|
9815 s js2-prev-name-token-string |
|
9816 js2-prev-name-token-start nil |
|
9817 js2-prev-name-token-string nil)) |
|
9818 (setq name (make-js2-name-node :pos beg |
|
9819 :name s |
|
9820 :len (length s))) |
|
9821 (if check-activation-p |
|
9822 (js2-check-activation-name s (or token js2-NAME))) |
|
9823 name)) |
|
9824 |
|
9825 (provide 'js2-parse) |
|
9826 |
|
9827 ;;; js2-parse.el ends here |
|
9828 ;;; js2-indent.el --- indentation for js2-mode |
|
9829 ;; |
|
9830 ;; Copyright (C) 2008 Steve Yegge |
|
9831 ;; Author: Steve Yegge (steve.yegge@gmail.com) |
|
9832 ;; Maintainer: Steve Yegge (steve.yegge@gmail.com) |
|
9833 |
|
9834 ;; Commentary: |
|
9835 ;; |
|
9836 ;; This indenter is based on Karl Landström's "javascript.el" indenter. |
|
9837 ;; Karl cleverly deduces that the desired indentation level is often a |
|
9838 ;; function of paren/bracket/brace nesting depth, which can be determined |
|
9839 ;; quickly via the built-in `parse-partial-sexp' function. His indenter |
|
9840 ;; then does some equally clever checks to see if we're in the context of a |
|
9841 ;; substatement of a possibly braceless statement keyword such as if, while, |
|
9842 ;; or finally. This approach yields pretty good results. |
|
9843 ;; |
|
9844 ;; The indenter is often "wrong", however, and needs to be overridden. |
|
9845 ;; The right long-term solution is probably to emulate (or modify) |
|
9846 ;; cc-engine, but it's thousands upon thousands of lines of code. Even |
|
9847 ;; if you were to assume the accurate parse tree from `js2-parse' is |
|
9848 ;; present, indentation is still thousands of lines of code (I've been |
|
9849 ;; down that path) to handle every possible syntactic edge case, and in |
|
9850 ;; any case, relying on the parse tree is undesirable because parsing is |
|
9851 ;; slow. So you might as well go the cc-engine approach, but it's a |
|
9852 ;; huge pile of work that I'm just not up for any time soon. |
|
9853 ;; |
|
9854 ;; In the meantime, the compromise solution is that we offer a |
|
9855 ;; "bounce indenter", configured with `js2-bounce-indent-flag', which |
|
9856 ;; cycles the current line indent among various likely guess points. |
|
9857 ;; This approach is far from perfect, but should at least make it |
|
9858 ;; slightly easier to move the line towards its desired indentation |
|
9859 ;; when manually overriding Karl's heuristic nesting guesser. |
|
9860 ;; |
|
9861 ;; I've made miscellaneous tweaks to Karl's code to handle some Ecma |
|
9862 ;; extensions such as `let' and Array comprehensions, and will likely |
|
9863 ;; make further tweaks to it, but major kudos to Karl for coming up with |
|
9864 ;; the initial approach, which packs a lot of punch for so little code. |
|
9865 |
|
9866 ;;; Code: |
|
9867 |
|
9868 (defconst js-possibly-braceless-keyword-re |
|
9869 (regexp-opt |
|
9870 '("catch" "do" "else" "finally" "for" "if" "try" "while" "with" "let") |
|
9871 'words) |
|
9872 "Regular expression matching keywords that are optionally |
|
9873 followed by an opening brace.") |
|
9874 |
|
9875 (defconst js-indent-operator-re |
|
9876 (concat "[-+*/%<>=&^|?:.]\\([^-+*/]\\|$\\)\\|" |
|
9877 (regexp-opt '("in" "instanceof") 'words)) |
|
9878 "Regular expression matching operators that affect indentation |
|
9879 of continued expressions.") |
|
9880 |
|
9881 ;; This function has horrible results if you're typing an array |
|
9882 ;; such as [[1, 2], [3, 4], [5, 6]]. Bounce indenting -really- sucks |
|
9883 ;; in conjunction with electric-indent, so just disabling it. |
|
9884 (defsubst js2-code-at-bol-p () |
|
9885 "Return t if the first character on line is non-whitespace." |
|
9886 nil) |
|
9887 ;; (not (memq (char-after (point-at-bol)) |
|
9888 ;; '(? ?\t))))) |
|
9889 |
|
9890 (defun js2-insert-and-indent (key) |
|
9891 "Run command bound to key and indent current line. Runs the command |
|
9892 bound to KEY in the global keymap and indents the current line." |
|
9893 (interactive (list (this-command-keys))) |
|
9894 (let ((cmd (lookup-key (current-global-map) key))) |
|
9895 (if (commandp cmd) |
|
9896 (call-interactively cmd))) |
|
9897 ;; don't do the electric keys inside comments or strings, |
|
9898 ;; and don't do bounce-indent with them. |
|
9899 (let ((parse-state (parse-partial-sexp (point-min) (point))) |
|
9900 (js2-bounce-indent-flag (js2-code-at-bol-p))) |
|
9901 (unless (or (nth 3 parse-state) |
|
9902 (nth 4 parse-state)) |
|
9903 (indent-according-to-mode)))) |
|
9904 |
|
9905 (defun js-re-search-forward-inner (regexp &optional bound count) |
|
9906 "Auxiliary function for `js-re-search-forward'." |
|
9907 (let ((parse) |
|
9908 (saved-point (point-min))) |
|
9909 (while (> count 0) |
|
9910 (re-search-forward regexp bound) |
|
9911 (setq parse (parse-partial-sexp saved-point (point))) |
|
9912 (cond ((nth 3 parse) |
|
9913 (re-search-forward |
|
9914 (concat "\\([^\\]\\|^\\)" (string (nth 3 parse))) |
|
9915 (save-excursion (end-of-line) (point)) t)) |
|
9916 ((nth 7 parse) |
|
9917 (forward-line)) |
|
9918 ((or (nth 4 parse) |
|
9919 (and (eq (char-before) ?\/) (eq (char-after) ?\*))) |
|
9920 (re-search-forward "\\*/")) |
|
9921 (t |
|
9922 (setq count (1- count)))) |
|
9923 (setq saved-point (point)))) |
|
9924 (point)) |
|
9925 |
|
9926 (defun js-re-search-forward (regexp &optional bound noerror count) |
|
9927 "Search forward but ignore strings and comments. Invokes |
|
9928 `re-search-forward' but treats the buffer as if strings and |
|
9929 comments have been removed." |
|
9930 (let ((saved-point (point)) |
|
9931 (search-expr |
|
9932 (cond ((null count) |
|
9933 '(js-re-search-forward-inner regexp bound 1)) |
|
9934 ((< count 0) |
|
9935 '(js-re-search-backward-inner regexp bound (- count))) |
|
9936 ((> count 0) |
|
9937 '(js-re-search-forward-inner regexp bound count))))) |
|
9938 (condition-case err |
|
9939 (eval search-expr) |
|
9940 (search-failed |
|
9941 (goto-char saved-point) |
|
9942 (unless noerror |
|
9943 (error (error-message-string err))))))) |
|
9944 |
|
9945 (defun js-re-search-backward-inner (regexp &optional bound count) |
|
9946 "Auxiliary function for `js-re-search-backward'." |
|
9947 (let ((parse) |
|
9948 (saved-point (point-min))) |
|
9949 (while (> count 0) |
|
9950 (re-search-backward regexp bound) |
|
9951 (setq parse (parse-partial-sexp saved-point (point))) |
|
9952 (cond ((nth 3 parse) |
|
9953 (re-search-backward |
|
9954 (concat "\\([^\\]\\|^\\)" (string (nth 3 parse))) |
|
9955 (save-excursion (beginning-of-line) (point)) t)) |
|
9956 ((nth 7 parse) |
|
9957 (goto-char (nth 8 parse))) |
|
9958 ((or (nth 4 parse) |
|
9959 (and (eq (char-before) ?/) (eq (char-after) ?*))) |
|
9960 (re-search-backward "/\\*")) |
|
9961 (t |
|
9962 (setq count (1- count)))))) |
|
9963 (point)) |
|
9964 |
|
9965 (defun js-re-search-backward (regexp &optional bound noerror count) |
|
9966 "Search backward but ignore strings and comments. Invokes |
|
9967 `re-search-backward' but treats the buffer as if strings and |
|
9968 comments have been removed." |
|
9969 (let ((saved-point (point)) |
|
9970 (search-expr |
|
9971 (cond ((null count) |
|
9972 '(js-re-search-backward-inner regexp bound 1)) |
|
9973 ((< count 0) |
|
9974 '(js-re-search-forward-inner regexp bound (- count))) |
|
9975 ((> count 0) |
|
9976 '(js-re-search-backward-inner regexp bound count))))) |
|
9977 (condition-case err |
|
9978 (eval search-expr) |
|
9979 (search-failed |
|
9980 (goto-char saved-point) |
|
9981 (unless noerror |
|
9982 (error (error-message-string err))))))) |
|
9983 |
|
9984 (defun js-looking-at-operator-p () |
|
9985 "Return non-nil if text after point is an operator (that is not |
|
9986 a comma)." |
|
9987 (save-match-data |
|
9988 (and (looking-at js-indent-operator-re) |
|
9989 (or (not (looking-at ":")) |
|
9990 (save-excursion |
|
9991 (and (js-re-search-backward "[?:{]\\|\\<case\\>" nil t) |
|
9992 (looking-at "?"))))))) |
|
9993 |
|
9994 (defun js-continued-expression-p () |
|
9995 "Returns non-nil if the current line continues an expression." |
|
9996 (save-excursion |
|
9997 (back-to-indentation) |
|
9998 (or (js-looking-at-operator-p) |
|
9999 (and (js-re-search-backward "\n" nil t) |
|
10000 (progn |
|
10001 (skip-chars-backward " \t") |
|
10002 (backward-char) |
|
10003 (and (js-looking-at-operator-p) |
|
10004 (and (progn (backward-char) |
|
10005 (not (looking-at "\\*\\|++\\|--\\|/[/*]")))))))))) |
|
10006 |
|
10007 (defun js-end-of-do-while-loop-p () |
|
10008 "Returns non-nil if word after point is `while' of a do-while |
|
10009 statement, else returns nil. A braceless do-while statement |
|
10010 spanning several lines requires that the start of the loop is |
|
10011 indented to the same column as the current line." |
|
10012 (interactive) |
|
10013 (save-excursion |
|
10014 (save-match-data |
|
10015 (when (looking-at "\\s-*\\<while\\>") |
|
10016 (if (save-excursion |
|
10017 (skip-chars-backward "[ \t\n]*}") |
|
10018 (looking-at "[ \t\n]*}")) |
|
10019 (save-excursion |
|
10020 (backward-list) (backward-word 1) (looking-at "\\<do\\>")) |
|
10021 (js-re-search-backward "\\<do\\>" (point-at-bol) t) |
|
10022 (or (looking-at "\\<do\\>") |
|
10023 (let ((saved-indent (current-indentation))) |
|
10024 (while (and (js-re-search-backward "^[ \t]*\\<" nil t) |
|
10025 (/= (current-indentation) saved-indent))) |
|
10026 (and (looking-at "[ \t]*\\<do\\>") |
|
10027 (not (js-re-search-forward |
|
10028 "\\<while\\>" (point-at-eol) t)) |
|
10029 (= (current-indentation) saved-indent))))))))) |
|
10030 |
|
10031 (defun js-ctrl-statement-indentation () |
|
10032 "Returns the proper indentation of the current line if it |
|
10033 starts the body of a control statement without braces, else |
|
10034 returns nil." |
|
10035 (let (forward-sexp-function) ; temporarily unbind it |
|
10036 (save-excursion |
|
10037 (back-to-indentation) |
|
10038 (when (save-excursion |
|
10039 (and (not (js2-same-line (point-min))) |
|
10040 (not (looking-at "{")) |
|
10041 (js-re-search-backward "[[:graph:]]" nil t) |
|
10042 (not (looking-at "[{([]")) |
|
10043 (progn |
|
10044 (forward-char) |
|
10045 ;; scan-sexps sometimes throws an error |
|
10046 (ignore-errors (backward-sexp)) |
|
10047 (when (looking-at "(") (backward-word 1)) |
|
10048 (and (save-excursion |
|
10049 (skip-chars-backward " \t}" (point-at-bol)) |
|
10050 (bolp)) |
|
10051 (looking-at js-possibly-braceless-keyword-re) |
|
10052 (not (js-end-of-do-while-loop-p)))))) |
|
10053 (save-excursion |
|
10054 (goto-char (match-beginning 0)) |
|
10055 (+ (current-indentation) js2-basic-offset)))))) |
|
10056 |
|
10057 (defun js2-indent-in-array-comp (parse-status) |
|
10058 "Return non-nil if we think we're in an array comprehension. |
|
10059 In particular, return the buffer position of the first `for' kwd." |
|
10060 (let ((end (point))) |
|
10061 (when (nth 1 parse-status) |
|
10062 (save-excursion |
|
10063 (goto-char (nth 1 parse-status)) |
|
10064 (when (looking-at "\\[") |
|
10065 (forward-char 1) |
|
10066 (js2-forward-sws) |
|
10067 (if (looking-at "[[{]") |
|
10068 (let (forward-sexp-function) ; use lisp version |
|
10069 (forward-sexp) ; skip destructuring form |
|
10070 (js2-forward-sws) |
|
10071 (if (and (/= (char-after) ?,) ; regular array |
|
10072 (looking-at "for")) |
|
10073 (match-beginning 0))) |
|
10074 ;; to skip arbitrary expressions we need the parser, |
|
10075 ;; so we'll just guess at it. |
|
10076 (if (re-search-forward "[^,]* \\(for\\) " end t) |
|
10077 (match-beginning 1)))))))) |
|
10078 |
|
10079 (defun js2-array-comp-indentation (parse-status for-kwd) |
|
10080 (if (js2-same-line for-kwd) |
|
10081 ;; first continuation line |
|
10082 (save-excursion |
|
10083 (goto-char (nth 1 parse-status)) |
|
10084 (forward-char 1) |
|
10085 (skip-chars-forward " \t") |
|
10086 (current-column)) |
|
10087 (save-excursion |
|
10088 (goto-char for-kwd) |
|
10089 (current-column)))) |
|
10090 |
|
10091 (defun js-proper-indentation (parse-status) |
|
10092 "Return the proper indentation for the current line." |
|
10093 (save-excursion |
|
10094 (back-to-indentation) |
|
10095 (let ((ctrl-stmt-indent (js-ctrl-statement-indentation)) |
|
10096 (same-indent-p (looking-at "[]})]\\|\\<case\\>\\|\\<default\\>")) |
|
10097 (continued-expr-p (js-continued-expression-p)) |
|
10098 (bracket (nth 1 parse-status)) |
|
10099 beg) |
|
10100 (cond |
|
10101 ;; indent array comprehension continuation lines specially |
|
10102 ((and bracket |
|
10103 (not (js2-same-line bracket)) |
|
10104 (setq beg (js2-indent-in-array-comp parse-status)) |
|
10105 (>= (point) (save-excursion |
|
10106 (goto-char beg) |
|
10107 (point-at-bol)))) ; at or after first loop? |
|
10108 (js2-array-comp-indentation parse-status beg)) |
|
10109 |
|
10110 (ctrl-stmt-indent) |
|
10111 |
|
10112 (bracket |
|
10113 (goto-char bracket) |
|
10114 (cond |
|
10115 ((looking-at "[({[][ \t]*\\(/[/*]\\|$\\)") |
|
10116 (let ((p (parse-partial-sexp (point-at-bol) (point)))) |
|
10117 (when (save-excursion (skip-chars-backward " \t)") |
|
10118 (looking-at ")")) |
|
10119 (backward-list)) |
|
10120 (if (nth 1 p) |
|
10121 (progn (goto-char (1+ (nth 1 p))) |
|
10122 (skip-chars-forward " \t")) |
|
10123 (back-to-indentation)) |
|
10124 (cond (same-indent-p |
|
10125 (current-column)) |
|
10126 (continued-expr-p |
|
10127 (+ (current-column) (* 2 js2-basic-offset))) |
|
10128 (t |
|
10129 (+ (current-column) js2-basic-offset))))) |
|
10130 (t |
|
10131 (unless same-indent-p |
|
10132 (forward-char) |
|
10133 (skip-chars-forward " \t")) |
|
10134 (current-column)))) |
|
10135 |
|
10136 (continued-expr-p js2-basic-offset) |
|
10137 (t 0))))) |
|
10138 |
|
10139 (defun js2-lineup-comment (parse-status) |
|
10140 "Indent a multi-line block comment continuation line." |
|
10141 (let* ((beg (nth 8 parse-status)) |
|
10142 (first-line (js2-same-line beg)) |
|
10143 (offset (save-excursion |
|
10144 (goto-char beg) |
|
10145 (if (looking-at "/\\*") |
|
10146 (+ 1 (current-column)) |
|
10147 0)))) |
|
10148 (unless first-line |
|
10149 (indent-line-to offset)))) |
|
10150 |
|
10151 (defun js2-backward-sws () |
|
10152 "Move backward through whitespace and comments." |
|
10153 (interactive) |
|
10154 (while (forward-comment -1))) |
|
10155 |
|
10156 (defun js2-forward-sws () |
|
10157 "Move forward through whitespace and comments." |
|
10158 (interactive) |
|
10159 (while (forward-comment 1))) |
|
10160 |
|
10161 (defsubst js2-current-indent (&optional pos) |
|
10162 "Return column of indentation on current line. |
|
10163 If POS is non-nil, go to that point and return indentation for that line." |
|
10164 (save-excursion |
|
10165 (if pos |
|
10166 (goto-char pos)) |
|
10167 (back-to-indentation) |
|
10168 (current-column))) |
|
10169 |
|
10170 (defsubst js2-arglist-close () |
|
10171 "Return non-nil if we're on a line beginning with a close-paren/brace." |
|
10172 (save-match-data |
|
10173 (save-excursion |
|
10174 (goto-char (point-at-bol)) |
|
10175 (js2-forward-sws) |
|
10176 (looking-at "[])}]")))) |
|
10177 |
|
10178 (defsubst js2-indent-looks-like-label-p () |
|
10179 (goto-char (point-at-bol)) |
|
10180 (js2-forward-sws) |
|
10181 (looking-at (concat js2-mode-identifier-re ":"))) |
|
10182 |
|
10183 (defun js2-indent-in-objlit-p (parse-status) |
|
10184 "Return non-nil if this looks like an object-literal entry." |
|
10185 (let ((start (nth 1 parse-status))) |
|
10186 (and |
|
10187 start |
|
10188 (save-excursion |
|
10189 (and (zerop (forward-line -1)) |
|
10190 (not (< (point) start)) ; crossed a {} boundary |
|
10191 (js2-indent-looks-like-label-p))) |
|
10192 (save-excursion |
|
10193 (js2-indent-looks-like-label-p))))) |
|
10194 |
|
10195 ;; if prev line looks like foobar({ then we're passing an object |
|
10196 ;; literal to a function call, and people pretty much always want to |
|
10197 ;; de-dent back to the previous line, so move the 'basic-offset' |
|
10198 ;; position to the front. |
|
10199 (defsubst js2-indent-objlit-arg-p (parse-status) |
|
10200 (save-excursion |
|
10201 (back-to-indentation) |
|
10202 (js2-backward-sws) |
|
10203 (and (eq (1- (point)) (nth 1 parse-status)) |
|
10204 (eq (char-before) ?{) |
|
10205 (progn |
|
10206 (forward-char -1) |
|
10207 (skip-chars-backward " \t") |
|
10208 (eq (char-before) ?\())))) |
|
10209 |
|
10210 (defsubst js2-indent-case-block-p () |
|
10211 (save-excursion |
|
10212 (back-to-indentation) |
|
10213 (js2-backward-sws) |
|
10214 (goto-char (point-at-bol)) |
|
10215 (skip-chars-forward " \t") |
|
10216 (save-match-data |
|
10217 (looking-at "case\\s-.+:")))) |
|
10218 |
|
10219 (defsubst js2-syntax-bol () |
|
10220 "Return the point at the first non-whitespace char on the line. |
|
10221 Returns `point-at-bol' if the line is empty." |
|
10222 (save-excursion |
|
10223 (beginning-of-line) |
|
10224 (skip-chars-forward " \t") |
|
10225 (point))) |
|
10226 |
|
10227 (defun js2-bounce-indent (normal-col parse-status) |
|
10228 "Cycle among alternate computed indentation positions. |
|
10229 PARSE-STATUS is the result of `parse-partial-sexp' from the beginning |
|
10230 of the buffer to the current point. NORMAL-COL is the indentation |
|
10231 column computed by the heuristic guesser based on current paren, |
|
10232 bracket, brace and statement nesting." |
|
10233 (let ((cur-indent (js2-current-indent)) |
|
10234 (old-buffer-undo-list buffer-undo-list) |
|
10235 ;; Emacs 21 only has `count-lines', not `line-number-at-pos' |
|
10236 (current-line (save-excursion |
|
10237 (forward-line 0) ; move to bol |
|
10238 (1+ (count-lines (point-min) (point))))) |
|
10239 positions |
|
10240 pos |
|
10241 anchor |
|
10242 arglist-cont |
|
10243 same-indent |
|
10244 prev-line-col |
|
10245 basic-offset |
|
10246 computed-pos) |
|
10247 ;; temporarily don't record undo info, if user requested this |
|
10248 (if js2-mode-indent-inhibit-undo |
|
10249 (setq buffer-undo-list t)) |
|
10250 (unwind-protect |
|
10251 (progn |
|
10252 ;; first likely point: indent from beginning of previous code line |
|
10253 (push (setq basic-offset |
|
10254 (+ (save-excursion |
|
10255 (back-to-indentation) |
|
10256 (js2-backward-sws) |
|
10257 (back-to-indentation) |
|
10258 (setq prev-line-col (current-column))) |
|
10259 js2-basic-offset)) |
|
10260 positions) |
|
10261 |
|
10262 ;; second likely point: indent from assign-expr RHS. This |
|
10263 ;; is just a crude guess based on finding " = " on the previous |
|
10264 ;; line containing actual code. |
|
10265 (setq pos (save-excursion |
|
10266 (save-match-data |
|
10267 (forward-line -1) |
|
10268 (goto-char (point-at-bol)) |
|
10269 (when (re-search-forward "\\s-+\\(=\\)\\s-+" |
|
10270 (point-at-eol) t) |
|
10271 (goto-char (match-end 1)) |
|
10272 (skip-chars-forward " \t\r\n") |
|
10273 (current-column))))) |
|
10274 (when pos |
|
10275 (incf pos js2-basic-offset) |
|
10276 (unless (member pos positions) |
|
10277 (push pos positions))) |
|
10278 |
|
10279 ;; third likely point: same indent as previous line of code. |
|
10280 ;; Make it the first likely point if we're not on an |
|
10281 ;; arglist-close line and previous line ends in a comma, or |
|
10282 ;; both this line and prev line look like object-literal |
|
10283 ;; elements. |
|
10284 (setq pos (save-excursion |
|
10285 (goto-char (point-at-bol)) |
|
10286 (js2-backward-sws) |
|
10287 (back-to-indentation) |
|
10288 (prog1 |
|
10289 (current-column) |
|
10290 ;; while we're here, look for trailing comma |
|
10291 (if (save-excursion |
|
10292 (goto-char (point-at-eol)) |
|
10293 (js2-backward-sws) |
|
10294 (eq (char-before) ?,)) |
|
10295 (setq arglist-cont (1- (point))))))) |
|
10296 (when pos |
|
10297 (if (and (or arglist-cont |
|
10298 (js2-indent-in-objlit-p parse-status)) |
|
10299 (not (js2-arglist-close))) |
|
10300 (setq same-indent pos)) |
|
10301 (unless (member pos positions) |
|
10302 (push pos positions))) |
|
10303 |
|
10304 ;; fourth likely position: first preceding code with less indentation |
|
10305 ;; than the immediately preceding code line. |
|
10306 (setq pos (save-excursion |
|
10307 (js2-backward-sws) |
|
10308 (back-to-indentation) |
|
10309 (setq anchor (current-column)) |
|
10310 (while (and (zerop (forward-line -1)) |
|
10311 (>= (progn |
|
10312 (back-to-indentation) |
|
10313 (current-column)) |
|
10314 anchor))) |
|
10315 (setq pos (current-column)))) |
|
10316 (unless (member pos positions) |
|
10317 (push pos positions)) |
|
10318 |
|
10319 ;; put nesting-heuristic position first in list, sort rest |
|
10320 (setq positions (nreverse (sort positions '<))) |
|
10321 (setq positions (cons normal-col (delete normal-col positions))) |
|
10322 |
|
10323 ;; comma-list continuation lines: prev line indent takes precedence |
|
10324 (if same-indent |
|
10325 (setq positions |
|
10326 (cons same-indent |
|
10327 (sort (delete same-indent positions) '<)))) |
|
10328 |
|
10329 ;; common special cases where we want to indent in from previous line |
|
10330 (if (or (js2-indent-case-block-p) |
|
10331 (js2-indent-objlit-arg-p parse-status)) |
|
10332 (setq positions |
|
10333 (cons basic-offset |
|
10334 (delete basic-offset positions)))) |
|
10335 |
|
10336 ;; record whether we're already sitting on one of the alternatives |
|
10337 (setq pos (member cur-indent positions)) |
|
10338 (cond |
|
10339 ;; case 0: we're one one of the alternatives and this is the |
|
10340 ;; first time they've pressed TAB on this line (best-guess). |
|
10341 ((and js2-mode-indent-ignore-first-tab |
|
10342 pos |
|
10343 ;; first time pressing TAB on this line? |
|
10344 (not (eq js2-mode-last-indented-line current-line))) |
|
10345 ;; do nothing |
|
10346 (setq computed-pos nil)) |
|
10347 ;; case 1: only one computed position => use it |
|
10348 ((null (cdr positions)) |
|
10349 (setq computed-pos 0)) |
|
10350 ;; case 2: not on any of the computed spots => use main spot |
|
10351 ((not pos) |
|
10352 (setq computed-pos 0)) |
|
10353 ;; case 3: on last position: cycle to first position |
|
10354 ((null (cdr pos)) |
|
10355 (setq computed-pos 0)) |
|
10356 ;; case 4: on intermediate position: cycle to next position |
|
10357 (t |
|
10358 (setq computed-pos (js2-position (second pos) positions)))) |
|
10359 |
|
10360 ;; see if any hooks want to indent; otherwise we do it |
|
10361 (loop with result = nil |
|
10362 for hook in js2-indent-hook |
|
10363 while (null result) |
|
10364 do |
|
10365 (setq result (funcall hook positions computed-pos)) |
|
10366 finally do |
|
10367 (unless (or result (null computed-pos)) |
|
10368 (indent-line-to (nth computed-pos positions))))) |
|
10369 |
|
10370 ;; finally |
|
10371 (if js2-mode-indent-inhibit-undo |
|
10372 (setq buffer-undo-list old-buffer-undo-list)) |
|
10373 ;; see commentary for `js2-mode-last-indented-line' |
|
10374 (setq js2-mode-last-indented-line current-line)))) |
|
10375 |
|
10376 (defsubst js2-1-line-comment-continuation-p () |
|
10377 "Return t if we're in a 1-line comment continuation. |
|
10378 If so, we don't ever want to use bounce-indent." |
|
10379 (save-excursion |
|
10380 (save-match-data |
|
10381 (and (progn |
|
10382 (forward-line 0) |
|
10383 (looking-at "\\s-*//")) |
|
10384 (progn |
|
10385 (forward-line -1) |
|
10386 (forward-line 0) |
|
10387 (when (looking-at "\\s-*$") |
|
10388 (js2-backward-sws) |
|
10389 (forward-line 0)) |
|
10390 (looking-at "\\s-*//")))))) |
|
10391 |
|
10392 (defun js2-indent-line () |
|
10393 "Indent the current line as JavaScript source text." |
|
10394 (interactive) |
|
10395 (let (parse-status |
|
10396 current-indent |
|
10397 offset |
|
10398 indent-col |
|
10399 moved |
|
10400 ;; don't whine about errors/warnings when we're indenting. |
|
10401 ;; This has to be set before calling parse-partial-sexp below. |
|
10402 (inhibit-point-motion-hooks t)) |
|
10403 (setq parse-status (save-excursion |
|
10404 (parse-partial-sexp (point-min) |
|
10405 (point-at-bol))) |
|
10406 offset (- (point) (save-excursion |
|
10407 (back-to-indentation) |
|
10408 (setq current-indent (current-column)) |
|
10409 (point)))) |
|
10410 (js2-with-underscore-as-word-syntax |
|
10411 (if (nth 4 parse-status) |
|
10412 (js2-lineup-comment parse-status) |
|
10413 (setq indent-col (js-proper-indentation parse-status)) |
|
10414 ;; see comments below about js2-mode-last-indented-line |
|
10415 (when |
|
10416 (cond |
|
10417 ;; bounce-indenting is disabled during electric-key indent. |
|
10418 ;; It doesn't work well on first line of buffer. |
|
10419 ((and js2-bounce-indent-flag |
|
10420 (not (js2-same-line (point-min))) |
|
10421 (not (js2-1-line-comment-continuation-p))) |
|
10422 (js2-bounce-indent indent-col parse-status) |
|
10423 (setq moved t)) |
|
10424 ;; just indent to the guesser's likely spot |
|
10425 ((/= current-indent indent-col) |
|
10426 (indent-line-to indent-col) |
|
10427 (setq moved t))) |
|
10428 (when (and moved (plusp offset)) |
|
10429 (forward-char offset))))))) |
|
10430 |
|
10431 (defun js2-indent-region (start end) |
|
10432 "Indent the region, but don't use bounce indenting." |
|
10433 (let ((js2-bounce-indent-flag nil) |
|
10434 (indent-region-function nil)) |
|
10435 (indent-region start end nil))) ; nil for byte-compiler |
|
10436 |
|
10437 (provide 'js2-indent) |
|
10438 |
|
10439 ;;; js2-indent.el ends here |
|
10440 |
|
10441 (eval-when-compile |
|
10442 (require 'cl)) |
|
10443 |
|
10444 (require 'imenu) |
|
10445 (require 'cc-cmds) ; for `c-fill-paragraph' |
|
10446 |
|
10447 |
|
10448 ;;;###autoload (add-to-list 'auto-mode-alist '("\\.js$" . js2-mode)) |
|
10449 |
|
10450 ;;;###autoload |
|
10451 (defun js2-mode () |
|
10452 "Major mode for editing JavaScript code." |
|
10453 (interactive) |
|
10454 (js2-mode-check-compat) |
|
10455 (kill-all-local-variables) |
|
10456 (set-syntax-table js2-mode-syntax-table) |
|
10457 (use-local-map js2-mode-map) |
|
10458 (setq major-mode 'js2-mode |
|
10459 mode-name "JavaScript-IDE" |
|
10460 comment-start "//" ; used by comment-region; don't change it |
|
10461 comment-end "") |
|
10462 (setq local-abbrev-table js2-mode-abbrev-table) |
|
10463 (set (make-local-variable 'max-lisp-eval-depth) |
|
10464 (max max-lisp-eval-depth 3000)) |
|
10465 (set (make-local-variable 'indent-line-function) #'js2-indent-line) |
|
10466 (set (make-local-variable 'indent-region-function) #'js2-indent-region) |
|
10467 (set (make-local-variable 'fill-paragraph-function) #'js2-fill-paragraph) |
|
10468 (set (make-local-variable 'before-save-hook) #'js2-before-save) |
|
10469 (set (make-local-variable 'next-error-function) #'js2-next-error) |
|
10470 (set (make-local-variable 'beginning-of-defun-function) #'js2-beginning-of-defun) |
|
10471 (set (make-local-variable 'end-of-defun-function) #'js2-end-of-defun) |
|
10472 ;; We un-confuse `parse-partial-sexp' by setting syntax-table properties |
|
10473 ;; for characters inside regexp literals. |
|
10474 (set (make-local-variable 'parse-sexp-lookup-properties) t) |
|
10475 ;; this is necessary to make `show-paren-function' work properly |
|
10476 (set (make-local-variable 'parse-sexp-ignore-comments) t) |
|
10477 ;; needed for M-x rgrep, among other things |
|
10478 (put 'js2-mode 'find-tag-default-function #'js2-mode-find-tag) |
|
10479 |
|
10480 ;; some variables needed by cc-engine for paragraph-fill, etc. |
|
10481 (setq c-buffer-is-cc-mode t |
|
10482 c-comment-prefix-regexp js2-comment-prefix-regexp |
|
10483 c-paragraph-start js2-paragraph-start |
|
10484 c-paragraph-separate "$" |
|
10485 comment-start-skip js2-comment-start-skip |
|
10486 c-syntactic-ws-start js2-syntactic-ws-start |
|
10487 c-syntactic-ws-end js2-syntactic-ws-end |
|
10488 c-syntactic-eol js2-syntactic-eol) |
|
10489 (if js2-emacs22 |
|
10490 (c-setup-paragraph-variables)) |
|
10491 |
|
10492 ;; We do our own syntax highlighting based on the parse tree. |
|
10493 ;; However, we want minor modes that add keywords to highlight properly |
|
10494 ;; (examples: doxymacs, column-marker). We do this by not letting |
|
10495 ;; font-lock unfontify anything, and telling it to fontify after we |
|
10496 ;; re-parse and re-highlight the buffer. (We currently don't do any |
|
10497 ;; work with regions other than the whole buffer.) |
|
10498 (dolist (var '(font-lock-unfontify-buffer-function |
|
10499 font-lock-unfontify-region-function)) |
|
10500 (set (make-local-variable var) (lambda (&rest args) t))) |
|
10501 |
|
10502 ;; Don't let font-lock do syntactic (string/comment) fontification. |
|
10503 (set (make-local-variable #'font-lock-syntactic-face-function) |
|
10504 (lambda (state) nil)) |
|
10505 |
|
10506 ;; Experiment: make reparse-delay longer for longer files. |
|
10507 (if (plusp js2-dynamic-idle-timer-adjust) |
|
10508 (setq js2-idle-timer-delay |
|
10509 (* js2-idle-timer-delay |
|
10510 (/ (point-max) js2-dynamic-idle-timer-adjust)))) |
|
10511 |
|
10512 (add-hook 'change-major-mode-hook #'js2-mode-exit nil t) |
|
10513 (add-hook 'after-change-functions #'js2-mode-edit nil t) |
|
10514 (setq imenu-create-index-function #'js2-mode-create-imenu-index) |
|
10515 (imenu-add-to-menubar (concat "IM-" mode-name)) |
|
10516 (when js2-mirror-mode |
|
10517 (js2-enter-mirror-mode)) |
|
10518 (add-to-invisibility-spec '(js2-outline . t)) |
|
10519 (set (make-local-variable 'line-move-ignore-invisible) t) |
|
10520 (set (make-local-variable 'forward-sexp-function) #'js2-mode-forward-sexp) |
|
10521 (setq js2-mode-functions-hidden nil |
|
10522 js2-mode-comments-hidden nil |
|
10523 js2-mode-buffer-dirty-p t |
|
10524 js2-mode-parsing nil) |
|
10525 (js2-reparse) |
|
10526 (run-hooks 'js2-mode-hook)) |
|
10527 |
|
10528 (defun js2-mode-check-compat () |
|
10529 "Signal an error if we can't run with this version of Emacs." |
|
10530 (if (and js2-mode-must-byte-compile |
|
10531 (not (byte-code-function-p (symbol-function 'js2-mode)))) |
|
10532 (error "You must byte-compile js2-mode before using it.")) |
|
10533 (if (and (boundp 'running-xemacs) |
|
10534 running-xemacs) |
|
10535 (error "js2-mode is not compatible with XEmacs")) |
|
10536 (unless (>= emacs-major-version 21) |
|
10537 (error "js2-mode requires GNU Emacs version 21 or higher"))) |
|
10538 |
|
10539 (defun js2-mode-exit () |
|
10540 (interactive) |
|
10541 (when js2-mode-node-overlay |
|
10542 (delete-overlay js2-mode-node-overlay) |
|
10543 (setq js2-mode-node-overlay nil)) |
|
10544 (js2-remove-overlays) |
|
10545 (setq js2-mode-ast nil) |
|
10546 (remove-hook 'change-major-mode-hook #'js2-mode-exit t) |
|
10547 (remove-from-invisibility-spec '(js2-outline . t)) |
|
10548 (js2-mode-show-all) |
|
10549 (js2-with-unmodifying-text-property-changes |
|
10550 (js2-clear-face (point-min) (point-max)))) |
|
10551 |
|
10552 (defun js2-before-save () |
|
10553 "Clean up whitespace before saving file. |
|
10554 You can disable this by customizing `js2-cleanup-whitespace'." |
|
10555 (when js2-cleanup-whitespace |
|
10556 (let ((col (current-column))) |
|
10557 (delete-trailing-whitespace) |
|
10558 ;; don't change trailing whitespace on current line |
|
10559 (unless (eq (current-column) col) |
|
10560 (indent-to col))))) |
|
10561 |
|
10562 (defsubst js2-mode-reset-timer () |
|
10563 (if js2-mode-parse-timer |
|
10564 (cancel-timer js2-mode-parse-timer)) |
|
10565 (setq js2-mode-parsing nil) |
|
10566 (setq js2-mode-parse-timer |
|
10567 (run-with-idle-timer js2-idle-timer-delay nil #'js2-reparse))) |
|
10568 |
|
10569 (defun js2-mode-edit (beg end len) |
|
10570 "Schedule a new parse after buffer is edited." |
|
10571 (setq js2-mode-buffer-dirty-p t) |
|
10572 (js2-mode-hide-overlay) |
|
10573 (js2-mode-reset-timer)) |
|
10574 |
|
10575 (defun js2-mode-run-font-lock () |
|
10576 "Run `font-lock-fontify-buffer' after parsing/highlighting. |
|
10577 This is intended to allow modes that install their own font-lock keywords |
|
10578 to work with js2-mode. In practice it never seems to work for long. |
|
10579 Hopefully the Emacs maintainers can help figure out a way to make it work." |
|
10580 (when (and (boundp 'font-lock-keywords) |
|
10581 font-lock-keywords |
|
10582 (boundp 'font-lock-mode) |
|
10583 font-lock-mode) |
|
10584 ;; TODO: font-lock and jit-lock really really REALLY don't want to |
|
10585 ;; play nicely with js2-mode. They go out of their way to fail to |
|
10586 ;; provide any option for saying "look, fontify the goddamn buffer |
|
10587 ;; with just the keywords already". Argh. |
|
10588 (setq font-lock-defaults (list font-lock-keywords 'keywords-only)) |
|
10589 (let (font-lock-verbose) |
|
10590 (font-lock-default-fontify-buffer)))) |
|
10591 |
|
10592 (defun js2-reparse (&optional force) |
|
10593 "Re-parse current buffer after user finishes some data entry. |
|
10594 If we get any user input while parsing, including cursor motion, |
|
10595 we discard the parse and reschedule it. If FORCE is nil, then the |
|
10596 buffer will only rebuild its `js2-mode-ast' if the buffer is dirty." |
|
10597 (let (time |
|
10598 interrupted-p |
|
10599 (js2-compiler-strict-mode js2-mode-show-strict-warnings)) |
|
10600 (unless js2-mode-parsing |
|
10601 (setq js2-mode-parsing t) |
|
10602 (unwind-protect |
|
10603 (when (or js2-mode-buffer-dirty-p force) |
|
10604 (js2-remove-overlays) |
|
10605 (js2-with-unmodifying-text-property-changes |
|
10606 (setq js2-mode-buffer-dirty-p nil |
|
10607 js2-mode-fontifications nil |
|
10608 js2-mode-deferred-properties nil) |
|
10609 (if js2-mode-verbose-parse-p |
|
10610 (message "parsing...")) |
|
10611 (setq time |
|
10612 (js2-time |
|
10613 (setq interrupted-p |
|
10614 (catch 'interrupted |
|
10615 (setq js2-mode-ast (js2-parse)) |
|
10616 (js2-mode-fontify-regions) |
|
10617 (js2-mode-remove-suppressed-warnings) |
|
10618 (js2-mode-show-warnings) |
|
10619 (js2-mode-show-errors) |
|
10620 (js2-mode-run-font-lock) ; note: doesn't work |
|
10621 (if (>= js2-highlight-level 1) |
|
10622 (js2-highlight-jsdoc js2-mode-ast)) |
|
10623 nil)))) |
|
10624 (if interrupted-p |
|
10625 (progn |
|
10626 ;; unfinished parse => try again |
|
10627 (setq js2-mode-buffer-dirty-p t) |
|
10628 (js2-mode-reset-timer)) |
|
10629 (if js2-mode-verbose-parse-p |
|
10630 (message "Parse time: %s" time))))) |
|
10631 ;; finally |
|
10632 (setq js2-mode-parsing nil) |
|
10633 (unless interrupted-p |
|
10634 (setq js2-mode-parse-timer nil)))))) |
|
10635 |
|
10636 (defun js2-mode-show-node () |
|
10637 "Debugging aid: highlight selected AST node on mouse click." |
|
10638 (interactive) |
|
10639 (let ((node (js2-node-at-point)) |
|
10640 beg |
|
10641 end) |
|
10642 (when js2-mode-show-overlay |
|
10643 (if (null node) |
|
10644 (message "No node found at location %s" (point)) |
|
10645 (setq beg (js2-node-abs-pos node) |
|
10646 end (+ beg (js2-node-len node))) |
|
10647 (if js2-mode-node-overlay |
|
10648 (move-overlay js2-mode-node-overlay beg end) |
|
10649 (setq js2-mode-node-overlay (make-overlay beg end)) |
|
10650 (overlay-put js2-mode-node-overlay 'face 'highlight)) |
|
10651 (js2-with-unmodifying-text-property-changes |
|
10652 (put-text-property beg end 'point-left #'js2-mode-hide-overlay)) |
|
10653 (message "%s, parent: %s" |
|
10654 (js2-node-short-name node) |
|
10655 (if (js2-node-parent node) |
|
10656 (js2-node-short-name (js2-node-parent node)) |
|
10657 "nil")))))) |
|
10658 |
|
10659 (defun js2-mode-hide-overlay (&optional p1 p2) |
|
10660 "Remove the debugging overlay when the point moves." |
|
10661 (when js2-mode-node-overlay |
|
10662 (let ((beg (overlay-start js2-mode-node-overlay)) |
|
10663 (end (overlay-end js2-mode-node-overlay))) |
|
10664 ;; Sometimes we're called spuriously. |
|
10665 (unless (and p2 |
|
10666 (>= p2 beg) |
|
10667 (<= p2 end)) |
|
10668 (js2-with-unmodifying-text-property-changes |
|
10669 (remove-text-properties beg end '(point-left nil))) |
|
10670 (delete-overlay js2-mode-node-overlay) |
|
10671 (setq js2-mode-node-overlay nil))))) |
|
10672 |
|
10673 (defun js2-mode-reset () |
|
10674 "Debugging helper; resets everything." |
|
10675 (interactive) |
|
10676 (js2-mode-exit) |
|
10677 (js2-mode)) |
|
10678 |
|
10679 (defsubst js2-mode-show-warn-or-err (e face) |
|
10680 "Highlight a warning or error E with FACE. |
|
10681 E is a list of ((MSG-KEY MSG-ARG) BEG END)." |
|
10682 (let* ((key (first e)) |
|
10683 (beg (second e)) |
|
10684 (end (+ beg (third e))) |
|
10685 ;; Don't inadvertently go out of bounds. |
|
10686 (beg (max (point-min) (min beg (point-max)))) |
|
10687 (end (max (point-min) (min end (point-max)))) |
|
10688 (js2-highlight-level 3) ; so js2-set-face is sure to fire |
|
10689 (ovl (make-overlay beg end))) |
|
10690 (overlay-put ovl 'face face) |
|
10691 (overlay-put ovl 'js2 t) |
|
10692 (put-text-property beg end 'help-echo (js2-get-msg key)) |
|
10693 (put-text-property beg end 'point-entered #'js2-echo-error))) |
|
10694 |
|
10695 (defun js2-remove-overlays () |
|
10696 "Remove overlays from buffer that have a `js2' property." |
|
10697 (let ((beg (point-min)) |
|
10698 (end (point-max))) |
|
10699 (save-excursion |
|
10700 (dolist (o (overlays-in beg end)) |
|
10701 (when (overlay-get o 'js2) |
|
10702 (delete-overlay o)))))) |
|
10703 |
|
10704 (defun js2-mode-fontify-regions () |
|
10705 "Apply fontifications recorded during parsing." |
|
10706 ;; We defer clearing faces as long as possible to eliminate flashing. |
|
10707 (js2-clear-face (point-min) (point-max)) |
|
10708 ;; have to reverse the recorded fontifications so that errors and |
|
10709 ;; warnings overwrite the normal fontifications |
|
10710 (dolist (f (nreverse js2-mode-fontifications)) |
|
10711 (put-text-property (first f) (second f) 'face (third f))) |
|
10712 (setq js2-mode-fontifications nil) |
|
10713 (dolist (p js2-mode-deferred-properties) |
|
10714 (apply #'put-text-property p)) |
|
10715 (setq js2-mode-deferred-properties nil)) |
|
10716 |
|
10717 (defun js2-mode-show-errors () |
|
10718 "Highlight syntax errors." |
|
10719 (when js2-mode-show-parse-errors |
|
10720 (dolist (e (js2-ast-root-errors js2-mode-ast)) |
|
10721 (js2-mode-show-warn-or-err e 'js2-error-face)))) |
|
10722 |
|
10723 (defun js2-mode-remove-suppressed-warnings () |
|
10724 "Take suppressed warnings out of the AST warnings list. |
|
10725 This ensures that the counts and `next-error' are correct." |
|
10726 (setf (js2-ast-root-warnings js2-mode-ast) |
|
10727 (js2-delete-if |
|
10728 (lambda (e) |
|
10729 (let ((key (caar e))) |
|
10730 (or |
|
10731 (and (not js2-strict-trailing-comma-warning) |
|
10732 (string-match "trailing\\.comma" key)) |
|
10733 (and (not js2-strict-cond-assign-warning) |
|
10734 (string= key "msg.equal.as.assign")) |
|
10735 (and js2-missing-semi-one-line-override |
|
10736 (string= key "msg.missing.semi") |
|
10737 (let* ((beg (second e)) |
|
10738 (node (js2-node-at-point beg)) |
|
10739 (fn (js2-mode-find-parent-fn node)) |
|
10740 (body (and fn (js2-function-node-body fn))) |
|
10741 (lc (and body (js2-node-abs-pos body))) |
|
10742 (rc (and lc (+ lc (js2-node-len body))))) |
|
10743 (and fn |
|
10744 (or (null body) |
|
10745 (save-excursion |
|
10746 (goto-char beg) |
|
10747 (and (js2-same-line lc) |
|
10748 (js2-same-line rc)))))))))) |
|
10749 (js2-ast-root-warnings js2-mode-ast)))) |
|
10750 |
|
10751 (defun js2-mode-show-warnings () |
|
10752 "Highlight strict-mode warnings." |
|
10753 (when js2-mode-show-strict-warnings |
|
10754 (dolist (e (js2-ast-root-warnings js2-mode-ast)) |
|
10755 (js2-mode-show-warn-or-err e 'js2-warning-face)))) |
|
10756 |
|
10757 (defun js2-echo-error (old-point new-point) |
|
10758 "Called by point-motion hooks." |
|
10759 (let ((msg (get-text-property new-point 'help-echo))) |
|
10760 (if msg |
|
10761 (message msg)))) |
|
10762 |
|
10763 (defalias #'js2-echo-help #'js2-echo-error) |
|
10764 |
|
10765 (defun js2-enter-key () |
|
10766 "Handle user pressing the Enter key." |
|
10767 (interactive) |
|
10768 (let ((parse-status (save-excursion |
|
10769 (parse-partial-sexp (point-min) (point))))) |
|
10770 (cond |
|
10771 ;; check if we're inside a string |
|
10772 ((nth 3 parse-status) |
|
10773 (js2-mode-split-string parse-status)) |
|
10774 ;; check if inside a block comment |
|
10775 ((nth 4 parse-status) |
|
10776 (js2-mode-extend-comment)) |
|
10777 (t |
|
10778 ;; should probably figure out what the mode-map says we should do |
|
10779 (if js2-indent-on-enter-key |
|
10780 (let ((js2-bounce-indent-flag nil)) |
|
10781 (js2-indent-line))) |
|
10782 (insert "\n") |
|
10783 (if js2-enter-indents-newline |
|
10784 (let ((js2-bounce-indent-flag nil)) |
|
10785 (js2-indent-line))))))) |
|
10786 |
|
10787 (defun js2-mode-split-string (parse-status) |
|
10788 "Turn a newline in mid-string into a string concatenation." |
|
10789 (let* ((col (current-column)) |
|
10790 (quote-char (nth 3 parse-status)) |
|
10791 (quote-string (string quote-char)) |
|
10792 (string-beg (nth 8 parse-status)) |
|
10793 (indent (save-match-data |
|
10794 (or |
|
10795 (save-excursion |
|
10796 (back-to-indentation) |
|
10797 (if (looking-at "\\+") |
|
10798 (current-column))) |
|
10799 (save-excursion |
|
10800 (goto-char string-beg) |
|
10801 (if (looking-back "\\+\\s-+") |
|
10802 (goto-char (match-beginning 0))) |
|
10803 (current-column)))))) |
|
10804 (insert quote-char "\n") |
|
10805 (indent-to indent) |
|
10806 (insert "+ " quote-string) |
|
10807 (when (eolp) |
|
10808 (insert quote-string) |
|
10809 (backward-char 1)))) |
|
10810 |
|
10811 (defun js2-mode-extend-comment () |
|
10812 "When inside a comment block, add comment prefix." |
|
10813 (let (star single col first-line needs-close) |
|
10814 (save-excursion |
|
10815 (back-to-indentation) |
|
10816 (cond |
|
10817 ((looking-at "\\*[^/]") |
|
10818 (setq star t |
|
10819 col (current-column))) |
|
10820 ((looking-at "/\\*") |
|
10821 (setq star t |
|
10822 first-line t |
|
10823 col (1+ (current-column)))) |
|
10824 ((looking-at "//") |
|
10825 (setq single t |
|
10826 col (current-column))))) |
|
10827 ;; Heuristic for whether we need to close the comment: |
|
10828 ;; if we've got a parse error here, assume it's an unterminated |
|
10829 ;; comment. |
|
10830 (setq needs-close |
|
10831 (or |
|
10832 (eq (get-text-property (1- (point)) 'point-entered) |
|
10833 'js2-echo-error) |
|
10834 ;; The heuristic above doesn't work well when we're |
|
10835 ;; creating a comment and there's another one downstream, |
|
10836 ;; as our parser thinks this one ends at the end of the |
|
10837 ;; next one. (You can have a /* inside a js block comment.) |
|
10838 ;; So just close it if the next non-ws char isn't a *. |
|
10839 (and first-line |
|
10840 (eolp) |
|
10841 (save-excursion |
|
10842 (skip-syntax-forward " ") |
|
10843 (not (eq (char-after) ?*)))))) |
|
10844 (insert "\n") |
|
10845 (cond |
|
10846 (star |
|
10847 (indent-to col) |
|
10848 (insert "* ") |
|
10849 (if (and first-line needs-close) |
|
10850 (save-excursion |
|
10851 (insert "\n") |
|
10852 (indent-to col) |
|
10853 (insert "*/")))) |
|
10854 (single |
|
10855 (when (save-excursion |
|
10856 (and (zerop (forward-line 1)) |
|
10857 (looking-at "\\s-*//"))) |
|
10858 (indent-to col) |
|
10859 (insert "// ")))))) |
|
10860 |
|
10861 (defun js2-fill-string (beg quote) |
|
10862 "Line-wrap a single-line string into a multi-line string. |
|
10863 BEG is the string beginning, QUOTE is the quote char." |
|
10864 (let* ((squote (string quote)) |
|
10865 (end (if (re-search-forward (concat "[^\\]" squote) |
|
10866 (point-at-eol) t) |
|
10867 (1+ (match-beginning 0)) |
|
10868 (point-at-eol))) |
|
10869 (tag (make-marker)) |
|
10870 (fill-column (- fill-column 4))) ; make room |
|
10871 (unwind-protect |
|
10872 (progn |
|
10873 (move-marker tag end) |
|
10874 (fill-paragraph nil) |
|
10875 (goto-char beg) |
|
10876 (while (not (js2-same-line tag)) |
|
10877 (goto-char (point-at-eol)) |
|
10878 (insert squote) |
|
10879 (when (zerop (forward-line 1)) |
|
10880 (back-to-indentation) |
|
10881 (if (looking-at (concat squote "\\s-*$")) |
|
10882 (progn |
|
10883 (setq end (point-at-eol)) |
|
10884 (forward-line -1) |
|
10885 (delete-region (point-at-eol) end)) |
|
10886 (insert "+ " squote))))) |
|
10887 (move-marker tag nil)))) |
|
10888 |
|
10889 (defun js2-fill-paragraph (arg) |
|
10890 "Fill paragraph after point. Prefix ARG means justify as well. |
|
10891 Has special handling for filling in comments and strings." |
|
10892 (let* ((parse-status (save-excursion |
|
10893 (parse-partial-sexp (point-min) (point)))) |
|
10894 (quote-char (or (nth 3 parse-status) |
|
10895 (save-match-data |
|
10896 (if (looking-at "[\"\']") |
|
10897 (char-after)))))) |
|
10898 (cond |
|
10899 (quote-char |
|
10900 (js2-fill-string (or (nth 8 parse-status) |
|
10901 (point)) |
|
10902 quote-char) |
|
10903 t) ; or fill-paragraph does evil things afterwards |
|
10904 ((nth 4 parse-status) ; in block comment? |
|
10905 (js2-fill-comment parse-status arg)) |
|
10906 (t |
|
10907 (fill-paragraph arg))))) |
|
10908 |
|
10909 (defun js2-fill-comment (parse-status arg) |
|
10910 "Fill-paragraph in a block comment." |
|
10911 (let* ((beg (nth 8 parse-status)) |
|
10912 (end (save-excursion |
|
10913 (goto-char beg) |
|
10914 (re-search-forward "[^\\]\\*/" nil t))) |
|
10915 indent |
|
10916 end-marker) |
|
10917 (when end |
|
10918 (setq end-marker (make-marker)) |
|
10919 (move-marker end-marker end)) |
|
10920 (when (and end js2-mode-squeeze-spaces) |
|
10921 (save-excursion |
|
10922 (save-restriction |
|
10923 (narrow-to-region beg end) |
|
10924 (goto-char (point-min)) |
|
10925 (while (re-search-forward "[ \t][ \t]+" nil t) |
|
10926 (replace-match " "))))) |
|
10927 ;; `c-fill-paragraph' doesn't indent the continuation stars properly |
|
10928 ;; if the comment isn't left-justified. They align to the first star |
|
10929 ;; on the first continuation line after the comment-open, so we make |
|
10930 ;; sure the first continuation line has the proper indentation. |
|
10931 (save-excursion |
|
10932 (goto-char beg) |
|
10933 (setq indent (1+ (current-column))) |
|
10934 (goto-char (point-at-eol)) |
|
10935 (skip-chars-forward " \t\r\n") |
|
10936 (indent-line-to indent) |
|
10937 |
|
10938 ;; Invoke `c-fill-paragraph' from the first continuation line, |
|
10939 ;; since it provides better results. Otherwise if you're on the |
|
10940 ;; last line, it doesn't prefix with stars the way you'd expect. |
|
10941 ;; TODO: write our own fill function that works in Emacs 21 |
|
10942 (c-fill-paragraph arg)) |
|
10943 |
|
10944 ;; last line is typically indented wrong, so fix it |
|
10945 (when end-marker |
|
10946 (save-excursion |
|
10947 (goto-char end-marker) |
|
10948 (js2-indent-line))))) |
|
10949 |
|
10950 (defun js2-beginning-of-line () |
|
10951 "Toggles point between bol and first non-whitespace char in line. |
|
10952 Also moves past comment delimiters when inside comments." |
|
10953 (interactive) |
|
10954 (let (node beg) |
|
10955 (cond |
|
10956 ((bolp) |
|
10957 (back-to-indentation)) |
|
10958 ((looking-at "//") |
|
10959 (skip-chars-forward "/ \t")) |
|
10960 ((and (eq (char-after) ?*) |
|
10961 (setq node (js2-comment-at-point)) |
|
10962 (memq (js2-comment-node-format node) '(jsdoc block)) |
|
10963 (save-excursion |
|
10964 (skip-chars-backward " \t") |
|
10965 (bolp))) |
|
10966 (skip-chars-forward "\* \t")) |
|
10967 (t |
|
10968 (goto-char (point-at-bol)))))) |
|
10969 |
|
10970 (defun js2-end-of-line () |
|
10971 "Toggles point between eol and last non-whitespace char in line." |
|
10972 (interactive) |
|
10973 (if (eolp) |
|
10974 (skip-chars-backward " \t") |
|
10975 (goto-char (point-at-eol)))) |
|
10976 |
|
10977 (defun js2-enter-mirror-mode() |
|
10978 "Turns on mirror mode, where quotes, brackets etc are mirrored automatically |
|
10979 on insertion." |
|
10980 (interactive) |
|
10981 (define-key js2-mode-map (read-kbd-macro "{") 'js2-mode-match-curly) |
|
10982 (define-key js2-mode-map (read-kbd-macro "}") 'js2-mode-magic-close-paren) |
|
10983 (define-key js2-mode-map (read-kbd-macro "\"") 'js2-mode-match-double-quote) |
|
10984 (define-key js2-mode-map (read-kbd-macro "'") 'js2-mode-match-single-quote) |
|
10985 (define-key js2-mode-map (read-kbd-macro "(") 'js2-mode-match-paren) |
|
10986 (define-key js2-mode-map (read-kbd-macro ")") 'js2-mode-magic-close-paren) |
|
10987 (define-key js2-mode-map (read-kbd-macro "[") 'js2-mode-match-bracket) |
|
10988 (define-key js2-mode-map (read-kbd-macro "]") 'js2-mode-magic-close-paren)) |
|
10989 |
|
10990 (defun js2-leave-mirror-mode() |
|
10991 "Turns off mirror mode." |
|
10992 (interactive) |
|
10993 (dolist (key '("{" "\"" "'" "(" ")" "[" "]")) |
|
10994 (define-key js2-mode-map (read-kbd-macro key) 'self-insert-command))) |
|
10995 |
|
10996 (defsubst js2-mode-inside-string () |
|
10997 "Return non-nil if inside a string. |
|
10998 Actually returns the quote character that begins the string." |
|
10999 (let ((parse-state (save-excursion |
|
11000 (parse-partial-sexp (point-min) (point))))) |
|
11001 (nth 3 parse-state))) |
|
11002 |
|
11003 (defsubst js2-mode-inside-comment-or-string () |
|
11004 "Return non-nil if inside a comment or string." |
|
11005 (or |
|
11006 (let ((comment-start |
|
11007 (save-excursion |
|
11008 (goto-char (point-at-bol)) |
|
11009 (if (re-search-forward "//" (point-at-eol) t) |
|
11010 (match-beginning 0))))) |
|
11011 (and comment-start |
|
11012 (<= comment-start (point)))) |
|
11013 (let ((parse-state (save-excursion |
|
11014 (parse-partial-sexp (point-min) (point))))) |
|
11015 (or (nth 3 parse-state) |
|
11016 (nth 4 parse-state))))) |
|
11017 |
|
11018 (defun js2-mode-match-curly (arg) |
|
11019 "Insert matching curly-brace." |
|
11020 (interactive "p") |
|
11021 (insert "{") |
|
11022 (if current-prefix-arg |
|
11023 (save-excursion |
|
11024 (insert "}")) |
|
11025 (unless (or (not (looking-at "\\s-*$")) |
|
11026 (js2-mode-inside-comment-or-string)) |
|
11027 (undo-boundary) |
|
11028 |
|
11029 ;; absolutely mystifying bug: when inserting the next "\n", |
|
11030 ;; the buffer-undo-list is given two new entries: the inserted range, |
|
11031 ;; and the incorrect position of the point. It's recorded incorrectly |
|
11032 ;; as being before the opening "{", not after it. But it's recorded |
|
11033 ;; as the correct value if you're debugging `js2-mode-match-curly' |
|
11034 ;; in edebug. I have no idea why it's doing this, but incrementing |
|
11035 ;; the inserted position fixes the problem, so that the undo takes us |
|
11036 ;; back to just after the user-inserted "{". |
|
11037 (insert "\n") |
|
11038 (ignore-errors |
|
11039 (incf (cadr buffer-undo-list))) |
|
11040 |
|
11041 (js2-indent-line) |
|
11042 (save-excursion |
|
11043 (insert "\n}") |
|
11044 (let ((js2-bounce-indent-flag (js2-code-at-bol-p))) |
|
11045 (js2-indent-line)))))) |
|
11046 |
|
11047 (defun js2-mode-match-bracket () |
|
11048 "Insert matching bracket." |
|
11049 (interactive) |
|
11050 (insert "[") |
|
11051 (unless (or (not (looking-at "\\s-*$")) |
|
11052 (js2-mode-inside-comment-or-string)) |
|
11053 (save-excursion |
|
11054 (insert "]")) |
|
11055 (when js2-auto-indent-flag |
|
11056 (let ((js2-bounce-indent-flag (js2-code-at-bol-p))) |
|
11057 (js2-indent-line))))) |
|
11058 |
|
11059 (defun js2-mode-match-paren () |
|
11060 "Insert matching paren unless already inserted." |
|
11061 (interactive) |
|
11062 (insert "(") |
|
11063 (unless (or (not (looking-at "\\s-*$")) |
|
11064 (js2-mode-inside-comment-or-string)) |
|
11065 (save-excursion |
|
11066 (insert ")")) |
|
11067 (when js2-auto-indent-flag |
|
11068 (let ((js2-bounce-indent-flag (js2-code-at-bol-p))) |
|
11069 (js2-indent-line))))) |
|
11070 |
|
11071 (defsubst js2-match-quote (quote-string) |
|
11072 (let ((start-quote (js2-mode-inside-string))) |
|
11073 (cond |
|
11074 ;; inside a comment - don't do quote-matching, since we can't |
|
11075 ;; reliably figure out if we're in a string inside the comment |
|
11076 ((js2-comment-at-point) |
|
11077 (insert quote-string)) |
|
11078 ((not start-quote) |
|
11079 ;; not in string => insert matched quotes |
|
11080 (insert quote-string) |
|
11081 ;; exception: if we're just before a word, don't double it. |
|
11082 (unless (looking-at "[^ \t\r\n]") |
|
11083 (save-excursion |
|
11084 (insert quote-string)))) |
|
11085 ((looking-at quote-string) |
|
11086 (if (looking-back "[^\\]\\\\") |
|
11087 (insert quote-string) |
|
11088 (forward-char 1))) |
|
11089 ((and js2-mode-escape-quotes |
|
11090 (save-excursion |
|
11091 (save-match-data |
|
11092 (re-search-forward quote-string (point-at-eol) t)))) |
|
11093 ;; inside terminated string, escape quote (unless already escaped) |
|
11094 (insert (if (looking-back "[^\\]\\\\") |
|
11095 quote-string |
|
11096 (concat "\\" quote-string)))) |
|
11097 (t |
|
11098 (insert quote-string))))) ; else terminate the string |
|
11099 |
|
11100 (defun js2-mode-match-single-quote () |
|
11101 "Insert matching single-quote." |
|
11102 (interactive) |
|
11103 (let ((parse-status (parse-partial-sexp (point-min) (point)))) |
|
11104 ;; don't match inside comments, since apostrophe is more common |
|
11105 (if (nth 4 parse-status) |
|
11106 (insert "'") |
|
11107 (js2-match-quote "'")))) |
|
11108 |
|
11109 (defun js2-mode-match-double-quote () |
|
11110 "Insert matching double-quote." |
|
11111 (interactive) |
|
11112 (js2-match-quote "\"")) |
|
11113 |
|
11114 (defun js2-mode-magic-close-paren () |
|
11115 "Skip over close-paren rather than inserting, where appropriate. |
|
11116 Uses some heuristics to try to figure out the right thing to do." |
|
11117 (interactive) |
|
11118 (let* ((parse-status (parse-partial-sexp (point-min) (point))) |
|
11119 (open-pos (nth 1 parse-status)) |
|
11120 (close last-input-char) |
|
11121 (open (cond |
|
11122 ((eq close 41) ; close-paren |
|
11123 40) ; open-paren |
|
11124 ((eq close 93) ; close-bracket |
|
11125 91) ; open-bracket |
|
11126 ((eq close ?}) |
|
11127 ?{) |
|
11128 (t nil)))) |
|
11129 (if (and (looking-at (string close)) |
|
11130 (eq open (char-after open-pos)) |
|
11131 (js2-same-line open-pos)) |
|
11132 (forward-char 1) |
|
11133 (insert (string close))) |
|
11134 (blink-matching-open))) |
|
11135 |
|
11136 (defun js2-mode-wait-for-parse (callback) |
|
11137 "Invoke CALLBACK when parsing is finished. |
|
11138 If parsing is already finished, calls CALLBACK immediately." |
|
11139 (if (not js2-mode-buffer-dirty-p) |
|
11140 (funcall callback) |
|
11141 (push callback js2-mode-pending-parse-callbacks) |
|
11142 (add-hook 'js2-parse-finished-hook #'js2-mode-parse-finished))) |
|
11143 |
|
11144 (defun js2-mode-parse-finished () |
|
11145 "Invoke callbacks in `js2-mode-pending-parse-callbacks'." |
|
11146 ;; We can't let errors propagate up, since it prevents the |
|
11147 ;; `js2-parse' method from completing normally and returning |
|
11148 ;; the ast, which makes things mysteriously not work right. |
|
11149 (unwind-protect |
|
11150 (dolist (cb js2-mode-pending-parse-callbacks) |
|
11151 (condition-case err |
|
11152 (funcall cb) |
|
11153 (error (message "%s" err)))) |
|
11154 (setq js2-mode-pending-parse-callbacks nil))) |
|
11155 |
|
11156 (defun js2-mode-flag-region (from to flag) |
|
11157 "Hide or show text from FROM to TO, according to FLAG. |
|
11158 If FLAG is nil then text is shown, while if FLAG is t the text is hidden. |
|
11159 Returns the created overlay if FLAG is non-nil." |
|
11160 (remove-overlays from to 'invisible 'js2-outline) |
|
11161 (when flag |
|
11162 (let ((o (make-overlay from to))) |
|
11163 (overlay-put o 'invisible 'js2-outline) |
|
11164 (overlay-put o 'isearch-open-invisible |
|
11165 'js2-isearch-open-invisible) |
|
11166 o))) |
|
11167 |
|
11168 ;; Function to be set as an outline-isearch-open-invisible' property |
|
11169 ;; to the overlay that makes the outline invisible (see |
|
11170 ;; `js2-mode-flag-region'). |
|
11171 (defun js2-isearch-open-invisible (overlay) |
|
11172 ;; We rely on the fact that isearch places point on the matched text. |
|
11173 (js2-mode-show-element)) |
|
11174 |
|
11175 (defun js2-mode-invisible-overlay-bounds (&optional pos) |
|
11176 "Return cons cell of bounds of folding overlay at POS. |
|
11177 Returns nil if not found." |
|
11178 (let ((overlays (overlays-at (or pos (point)))) |
|
11179 o) |
|
11180 (while (and overlays |
|
11181 (not o)) |
|
11182 (if (overlay-get (car overlays) 'invisible) |
|
11183 (setq o (car overlays)) |
|
11184 (setq overlays (cdr overlays)))) |
|
11185 (if o |
|
11186 (cons (overlay-start o) (overlay-end o))))) |
|
11187 |
|
11188 (defun js2-mode-function-at-point (&optional pos) |
|
11189 "Return the innermost function node enclosing current point. |
|
11190 Returns nil if point is not in a function." |
|
11191 (let ((node (js2-node-at-point pos))) |
|
11192 (while (and node (not (js2-function-node-p node))) |
|
11193 (setq node (js2-node-parent node))) |
|
11194 (if (js2-function-node-p node) |
|
11195 node))) |
|
11196 |
|
11197 (defun js2-mode-toggle-element () |
|
11198 "Hide or show the foldable element at the point." |
|
11199 (interactive) |
|
11200 (let (comment fn pos) |
|
11201 (save-excursion |
|
11202 (save-match-data |
|
11203 (cond |
|
11204 ;; /* ... */ comment? |
|
11205 ((js2-block-comment-p (setq comment (js2-comment-at-point))) |
|
11206 (if (js2-mode-invisible-overlay-bounds |
|
11207 (setq pos (+ 3 (js2-node-abs-pos comment)))) |
|
11208 (progn |
|
11209 (goto-char pos) |
|
11210 (js2-mode-show-element)) |
|
11211 (js2-mode-hide-element))) |
|
11212 |
|
11213 ;; //-comment? |
|
11214 ((save-excursion |
|
11215 (back-to-indentation) |
|
11216 (looking-at js2-mode-//-comment-re)) |
|
11217 (js2-mode-toggle-//-comment)) |
|
11218 |
|
11219 ;; function? |
|
11220 ((setq fn (js2-mode-function-at-point)) |
|
11221 (setq pos (and (js2-function-node-body fn) |
|
11222 (js2-node-abs-pos (js2-function-node-body fn)))) |
|
11223 (goto-char (1+ pos)) |
|
11224 (if (js2-mode-invisible-overlay-bounds) |
|
11225 (js2-mode-show-element) |
|
11226 (js2-mode-hide-element))) |
|
11227 (t |
|
11228 (message "Nothing at point to hide or show"))))))) |
|
11229 |
|
11230 (defun js2-mode-hide-element () |
|
11231 "Fold/hide contents of a block, showing ellipses. |
|
11232 Show the hidden text with \\[js2-mode-show-element]." |
|
11233 (interactive) |
|
11234 (if js2-mode-buffer-dirty-p |
|
11235 (js2-mode-wait-for-parse #'js2-mode-hide-element)) |
|
11236 (let (node body beg end) |
|
11237 (cond |
|
11238 ((js2-mode-invisible-overlay-bounds) |
|
11239 (message "already hidden")) |
|
11240 (t |
|
11241 (setq node (js2-node-at-point)) |
|
11242 (cond |
|
11243 ((js2-block-comment-p node) |
|
11244 (js2-mode-hide-comment node)) |
|
11245 (t |
|
11246 (while (and node (not (js2-function-node-p node))) |
|
11247 (setq node (js2-node-parent node))) |
|
11248 (if (and node |
|
11249 (setq body (js2-function-node-body node))) |
|
11250 (progn |
|
11251 (setq beg (js2-node-abs-pos body) |
|
11252 end (+ beg (js2-node-len body))) |
|
11253 (js2-mode-flag-region (1+ beg) (1- end) 'hide)) |
|
11254 (message "No collapsable element found at point")))))))) |
|
11255 |
|
11256 (defun js2-mode-show-element () |
|
11257 "Show the hidden element at current point." |
|
11258 (interactive) |
|
11259 (let ((bounds (js2-mode-invisible-overlay-bounds))) |
|
11260 (if bounds |
|
11261 (js2-mode-flag-region (car bounds) (cdr bounds) nil) |
|
11262 (message "Nothing to un-hide")))) |
|
11263 |
|
11264 (defun js2-mode-show-all () |
|
11265 "Show all of the text in the buffer." |
|
11266 (interactive) |
|
11267 (js2-mode-flag-region (point-min) (point-max) nil)) |
|
11268 |
|
11269 (defun js2-mode-toggle-hide-functions () |
|
11270 (interactive) |
|
11271 (if js2-mode-functions-hidden |
|
11272 (js2-mode-show-functions) |
|
11273 (js2-mode-hide-functions))) |
|
11274 |
|
11275 (defun js2-mode-hide-functions () |
|
11276 "Hides all non-nested function bodies in the buffer. |
|
11277 Use \\[js2-mode-show-all] to reveal them, or \\[js2-mode-show-element] |
|
11278 to open an individual entry." |
|
11279 (interactive) |
|
11280 (if js2-mode-buffer-dirty-p |
|
11281 (js2-mode-wait-for-parse #'js2-mode-hide-functions)) |
|
11282 (if (null js2-mode-ast) |
|
11283 (message "Oops - parsing failed") |
|
11284 (setq js2-mode-functions-hidden t) |
|
11285 (js2-visit-ast js2-mode-ast #'js2-mode-function-hider))) |
|
11286 |
|
11287 (defun js2-mode-function-hider (n endp) |
|
11288 (when (not endp) |
|
11289 (let ((tt (js2-node-type n)) |
|
11290 body beg end) |
|
11291 (cond |
|
11292 ((and (= tt js2-FUNCTION) |
|
11293 (setq body (js2-function-node-body n))) |
|
11294 (setq beg (js2-node-abs-pos body) |
|
11295 end (+ beg (js2-node-len body))) |
|
11296 (js2-mode-flag-region (1+ beg) (1- end) 'hide) |
|
11297 nil) ; don't process children of function |
|
11298 (t |
|
11299 t))))) ; keep processing other AST nodes |
|
11300 |
|
11301 (defun js2-mode-show-functions () |
|
11302 "Un-hide any folded function bodies in the buffer." |
|
11303 (interactive) |
|
11304 (setq js2-mode-functions-hidden nil) |
|
11305 (save-excursion |
|
11306 (goto-char (point-min)) |
|
11307 (while (/= (goto-char (next-overlay-change (point))) |
|
11308 (point-max)) |
|
11309 (dolist (o (overlays-at (point))) |
|
11310 (when (and (overlay-get o 'invisible) |
|
11311 (not (overlay-get o 'comment))) |
|
11312 (js2-mode-flag-region (overlay-start o) (overlay-end o) nil)))))) |
|
11313 |
|
11314 (defun js2-mode-hide-comment (n) |
|
11315 (let* ((head (if (eq (js2-comment-node-format n) 'jsdoc) |
|
11316 3 ; /** |
|
11317 2)) ; /* |
|
11318 (beg (+ (js2-node-abs-pos n) head)) |
|
11319 (end (- (+ beg (js2-node-len n)) head 2)) |
|
11320 (o (js2-mode-flag-region beg end 'hide))) |
|
11321 (overlay-put o 'comment t))) |
|
11322 |
|
11323 (defun js2-mode-toggle-hide-comments () |
|
11324 "Folds all block comments in the buffer. |
|
11325 Use \\[js2-mode-show-all] to reveal them, or \\[js2-mode-show-element] |
|
11326 to open an individual entry." |
|
11327 (interactive) |
|
11328 (if js2-mode-comments-hidden |
|
11329 (js2-mode-show-comments) |
|
11330 (js2-mode-hide-comments))) |
|
11331 |
|
11332 (defun js2-mode-hide-comments () |
|
11333 (interactive) |
|
11334 (if js2-mode-buffer-dirty-p |
|
11335 (js2-mode-wait-for-parse #'js2-mode-hide-comments)) |
|
11336 (if (null js2-mode-ast) |
|
11337 (message "Oops - parsing failed") |
|
11338 (setq js2-mode-comments-hidden t) |
|
11339 (dolist (n (js2-ast-root-comments js2-mode-ast)) |
|
11340 (let ((format (js2-comment-node-format n))) |
|
11341 (when (js2-block-comment-p n) |
|
11342 (js2-mode-hide-comment n)))) |
|
11343 (js2-mode-hide-//-comments))) |
|
11344 |
|
11345 (defsubst js2-mode-extend-//-comment (direction) |
|
11346 "Find start or end of a block of similar //-comment lines. |
|
11347 DIRECTION is -1 to look back, 1 to look forward. |
|
11348 INDENT is the indentation level to match. |
|
11349 Returns the end-of-line position of the furthest adjacent |
|
11350 //-comment line with the same indentation as the current line. |
|
11351 If there is no such matching line, returns current end of line." |
|
11352 (let ((pos (point-at-eol)) |
|
11353 (indent (current-indentation))) |
|
11354 (save-excursion |
|
11355 (save-match-data |
|
11356 (while (and (zerop (forward-line direction)) |
|
11357 (looking-at js2-mode-//-comment-re) |
|
11358 (eq indent (length (match-string 1)))) |
|
11359 (setq pos (point-at-eol))) |
|
11360 pos)))) |
|
11361 |
|
11362 (defun js2-mode-hide-//-comments () |
|
11363 "Fold adjacent 1-line comments, showing only snippet of first one." |
|
11364 (let (beg end) |
|
11365 (save-excursion |
|
11366 (save-match-data |
|
11367 (goto-char (point-min)) |
|
11368 (while (re-search-forward js2-mode-//-comment-re nil t) |
|
11369 (setq beg (point) |
|
11370 end (js2-mode-extend-//-comment 1)) |
|
11371 (unless (eq beg end) |
|
11372 (overlay-put (js2-mode-flag-region beg end 'hide) |
|
11373 'comment t)) |
|
11374 (goto-char end) |
|
11375 (forward-char 1)))))) |
|
11376 |
|
11377 (defun js2-mode-toggle-//-comment () |
|
11378 "Fold or un-fold any multi-line //-comment at point. |
|
11379 Caller should have determined that this line starts with a //-comment." |
|
11380 (let* ((beg (point-at-eol)) |
|
11381 (end beg)) |
|
11382 (save-excursion |
|
11383 (goto-char end) |
|
11384 (if (js2-mode-invisible-overlay-bounds) |
|
11385 (js2-mode-show-element) |
|
11386 ;; else hide the comment |
|
11387 (setq beg (js2-mode-extend-//-comment -1) |
|
11388 end (js2-mode-extend-//-comment 1)) |
|
11389 (unless (eq beg end) |
|
11390 (overlay-put (js2-mode-flag-region beg end 'hide) |
|
11391 'comment t)))))) |
|
11392 |
|
11393 (defun js2-mode-show-comments () |
|
11394 "Un-hide any hidden comments, leaving other hidden elements alone." |
|
11395 (interactive) |
|
11396 (setq js2-mode-comments-hidden nil) |
|
11397 (save-excursion |
|
11398 (goto-char (point-min)) |
|
11399 (while (/= (goto-char (next-overlay-change (point))) |
|
11400 (point-max)) |
|
11401 (dolist (o (overlays-at (point))) |
|
11402 (when (overlay-get o 'comment) |
|
11403 (js2-mode-flag-region (overlay-start o) (overlay-end o) nil)))))) |
|
11404 |
|
11405 (defun js2-mode-display-warnings-and-errors () |
|
11406 "Turn on display of warnings and errors." |
|
11407 (interactive) |
|
11408 (setq js2-mode-show-parse-errors t |
|
11409 js2-mode-show-strict-warnings t) |
|
11410 (js2-reparse 'force)) |
|
11411 |
|
11412 (defun js2-mode-hide-warnings-and-errors () |
|
11413 "Turn off display of warnings and errors." |
|
11414 (interactive) |
|
11415 (setq js2-mode-show-parse-errors nil |
|
11416 js2-mode-show-strict-warnings nil) |
|
11417 (js2-reparse 'force)) |
|
11418 |
|
11419 (defun js2-mode-toggle-warnings-and-errors () |
|
11420 "Toggle the display of warnings and errors. |
|
11421 Some users don't like having warnings/errors reported while they type." |
|
11422 (interactive) |
|
11423 (setq js2-mode-show-parse-errors (not js2-mode-show-parse-errors) |
|
11424 js2-mode-show-strict-warnings (not js2-mode-show-strict-warnings)) |
|
11425 (if (interactive-p) |
|
11426 (message "warnings and errors %s" |
|
11427 (if js2-mode-show-parse-errors |
|
11428 "enabled" |
|
11429 "disabled"))) |
|
11430 (js2-reparse 'force)) |
|
11431 |
|
11432 (defun js2-mode-customize () |
|
11433 (interactive) |
|
11434 (customize-group 'js2-mode)) |
|
11435 |
|
11436 (defun js2-mode-forward-sexp (&optional arg) |
|
11437 "Move forward across one statement or balanced expression. |
|
11438 With ARG, do it that many times. Negative arg -N means |
|
11439 move backward across N balanced expressions." |
|
11440 (interactive "p") |
|
11441 (setq arg (or arg 1)) |
|
11442 (if js2-mode-buffer-dirty-p |
|
11443 (js2-mode-wait-for-parse #'js2-mode-forward-sexp)) |
|
11444 (let (node end (start (point))) |
|
11445 (cond |
|
11446 ;; backward-sexp |
|
11447 ;; could probably make this "better" for some cases: |
|
11448 ;; - if in statement block (e.g. function body), go to parent |
|
11449 ;; - infix exprs like (foo in bar) - maybe go to beginning |
|
11450 ;; of infix expr if in the right-side expression? |
|
11451 ((and arg (minusp arg)) |
|
11452 (dotimes (i (- arg)) |
|
11453 (js2-backward-sws) |
|
11454 (forward-char -1) ; enter the node we backed up to |
|
11455 (setq node (js2-node-at-point (point) t)) |
|
11456 (goto-char (if node |
|
11457 (js2-node-abs-pos node) |
|
11458 (point-min))))) |
|
11459 (t |
|
11460 ;; forward-sexp |
|
11461 (js2-forward-sws) |
|
11462 (dotimes (i arg) |
|
11463 (js2-forward-sws) |
|
11464 (setq node (js2-node-at-point (point) t) |
|
11465 end (if node (+ (js2-node-abs-pos node) |
|
11466 (js2-node-len node)))) |
|
11467 (goto-char (or end (point-max)))))))) |
|
11468 |
|
11469 (defun js2-next-error (&optional arg reset) |
|
11470 "Move to next parse error. |
|
11471 Typically invoked via \\[next-error]. |
|
11472 ARG is the number of errors, forward or backward, to move. |
|
11473 RESET means start over from the beginning." |
|
11474 (interactive "p") |
|
11475 (if (or (null js2-mode-ast) |
|
11476 (and (null (js2-ast-root-errors js2-mode-ast)) |
|
11477 (null (js2-ast-root-warnings js2-mode-ast)))) |
|
11478 (message "No errors") |
|
11479 (when reset |
|
11480 (goto-char (point-min))) |
|
11481 (let* ((errs (copy-sequence |
|
11482 (append (js2-ast-root-errors js2-mode-ast) |
|
11483 (js2-ast-root-warnings js2-mode-ast)))) |
|
11484 (continue t) |
|
11485 (start (point)) |
|
11486 (count (or arg 1)) |
|
11487 (backward (minusp count)) |
|
11488 (sorter (if backward '> '<)) |
|
11489 (stopper (if backward '< '>)) |
|
11490 (count (abs count)) |
|
11491 all-errs |
|
11492 err) |
|
11493 ;; sort by start position |
|
11494 (setq errs (sort errs (lambda (e1 e2) |
|
11495 (funcall sorter (second e1) (second e2)))) |
|
11496 all-errs errs) |
|
11497 ;; find nth error with pos > start |
|
11498 (while (and errs continue) |
|
11499 (when (funcall stopper (cadar errs) start) |
|
11500 (setq err (car errs)) |
|
11501 (if (zerop (decf count)) |
|
11502 (setq continue nil))) |
|
11503 (setq errs (cdr errs))) |
|
11504 (if err |
|
11505 (goto-char (second err)) |
|
11506 ;; wrap around to first error |
|
11507 (goto-char (second (car all-errs))) |
|
11508 ;; if we were already on it, echo msg again |
|
11509 (if (= (point) start) |
|
11510 (js2-echo-error (point) (point))))))) |
|
11511 |
|
11512 (defun js2-mouse-3 () |
|
11513 "Make right-click move the point to the click location. |
|
11514 This makes right-click context menu operations a bit more intuitive. |
|
11515 The point will not move if the region is active, however, to avoid |
|
11516 destroying the region selection." |
|
11517 (interactive) |
|
11518 (when (and js2-move-point-on-right-click |
|
11519 (not mark-active)) |
|
11520 (let ((e last-input-event)) |
|
11521 (ignore-errors |
|
11522 (goto-char (cadadr e)))))) |
|
11523 |
|
11524 (defun js2-mode-create-imenu-index () |
|
11525 "Return an alist for `imenu--index-alist'." |
|
11526 ;; This is built up in `js2-parse-record-imenu' during parsing. |
|
11527 (when js2-mode-ast |
|
11528 ;; if we have an ast but no recorder, they're requesting a rescan |
|
11529 (unless js2-imenu-recorder |
|
11530 (js2-reparse 'force)) |
|
11531 (prog1 |
|
11532 (js2-build-imenu-index) |
|
11533 (setq js2-imenu-recorder nil |
|
11534 js2-imenu-function-map nil)))) |
|
11535 |
|
11536 (defun js2-mode-find-tag () |
|
11537 "Replacement for `find-tag-default'. |
|
11538 `find-tag-default' returns a ridiculous answer inside comments." |
|
11539 (let (beg end) |
|
11540 (js2-with-underscore-as-word-syntax |
|
11541 (save-excursion |
|
11542 (if (and (not (looking-at "[A-Za-z0-9_$]")) |
|
11543 (looking-back "[A-Za-z0-9_$]")) |
|
11544 (setq beg (progn (forward-word -1) (point)) |
|
11545 end (progn (forward-word 1) (point))) |
|
11546 (setq beg (progn (forward-word 1) (point)) |
|
11547 end (progn (forward-word -1) (point)))) |
|
11548 (replace-regexp-in-string |
|
11549 "[\"']" "" |
|
11550 (buffer-substring-no-properties beg end)))))) |
|
11551 |
|
11552 (defun js2-mode-forward-sibling () |
|
11553 "Move to the end of the sibling following point in parent. |
|
11554 Returns non-nil if successful, or nil if there was no following sibling." |
|
11555 (let* ((node (js2-node-at-point)) |
|
11556 (parent (js2-mode-find-enclosing-fn node)) |
|
11557 sib) |
|
11558 (when (setq sib (js2-node-find-child-after (point) parent)) |
|
11559 (goto-char (+ (js2-node-abs-pos sib) |
|
11560 (js2-node-len sib)))))) |
|
11561 |
|
11562 (defun js2-mode-backward-sibling () |
|
11563 "Move to the beginning of the sibling node preceding point in parent. |
|
11564 Parent is defined as the enclosing script or function." |
|
11565 (let* ((node (js2-node-at-point)) |
|
11566 (parent (js2-mode-find-enclosing-fn node)) |
|
11567 sib) |
|
11568 (when (setq sib (js2-node-find-child-before (point) parent)) |
|
11569 (goto-char (js2-node-abs-pos sib))))) |
|
11570 |
|
11571 (defun js2-beginning-of-defun () |
|
11572 "Go to line on which current function starts, and return non-nil. |
|
11573 If we're not in a function, go to beginning of previous script-level element." |
|
11574 (interactive) |
|
11575 (let ((parent (js2-node-parent-script-or-fn (js2-node-at-point))) |
|
11576 pos sib) |
|
11577 (cond |
|
11578 ((and (js2-function-node-p parent) |
|
11579 (not (eq (point) (setq pos (js2-node-abs-pos parent))))) |
|
11580 (goto-char pos)) |
|
11581 (t |
|
11582 (js2-mode-backward-sibling))))) |
|
11583 |
|
11584 (defun js2-end-of-defun () |
|
11585 "Go to the char after the last position of the current function. |
|
11586 If we're not in a function, skips over the next script-level element." |
|
11587 (interactive) |
|
11588 (let ((parent (js2-node-parent-script-or-fn (js2-node-at-point)))) |
|
11589 (if (not (js2-function-node-p parent)) |
|
11590 ;; punt: skip over next script-level element beyond point |
|
11591 (js2-mode-forward-sibling) |
|
11592 (goto-char (+ 1 (+ (js2-node-abs-pos parent) |
|
11593 (js2-node-len parent))))))) |
|
11594 |
|
11595 (defun js2-mark-defun (&optional allow-extend) |
|
11596 "Put mark at end of this function, point at beginning. |
|
11597 The function marked is the one that contains point. |
|
11598 |
|
11599 Interactively, if this command is repeated, |
|
11600 or (in Transient Mark mode) if the mark is active, |
|
11601 it marks the next defun after the ones already marked." |
|
11602 (interactive "p") |
|
11603 (let (extended) |
|
11604 (when (and allow-extend |
|
11605 (or (and (eq last-command this-command) (mark t)) |
|
11606 (and transient-mark-mode mark-active))) |
|
11607 (let ((sib (save-excursion |
|
11608 (goto-char (mark)) |
|
11609 (if (js2-mode-forward-sibling) |
|
11610 (point)))) |
|
11611 node) |
|
11612 (if sib |
|
11613 (progn |
|
11614 (set-mark sib) |
|
11615 (setq extended t)) |
|
11616 ;; no more siblings - try extending to enclosing node |
|
11617 (goto-char (mark t))))) |
|
11618 (when (not extended) |
|
11619 (let ((node (js2-node-at-point (point) t)) ; skip comments |
|
11620 ast fn stmt parent beg end) |
|
11621 (when (js2-ast-root-p node) |
|
11622 (setq ast node |
|
11623 node (or (js2-node-find-child-after (point) node) |
|
11624 (js2-node-find-child-before (point) node)))) |
|
11625 ;; only mark whole buffer if we can't find any children |
|
11626 (if (null node) |
|
11627 (setq node ast)) |
|
11628 (if (js2-function-node-p node) |
|
11629 (setq parent node) |
|
11630 (setq fn (js2-mode-find-enclosing-fn node) |
|
11631 stmt (if (or (null fn) |
|
11632 (js2-ast-root-p fn)) |
|
11633 (js2-mode-find-first-stmt node)) |
|
11634 parent (or stmt fn))) |
|
11635 (setq beg (js2-node-abs-pos parent) |
|
11636 end (+ beg (js2-node-len parent))) |
|
11637 (push-mark beg) |
|
11638 (goto-char end) |
|
11639 (exchange-point-and-mark))))) |
|
11640 |
|
11641 (defun js2-narrow-to-defun () |
|
11642 "Narrow to the function enclosing point." |
|
11643 (interactive) |
|
11644 (let* ((node (js2-node-at-point (point) t)) ; skip comments |
|
11645 (fn (if (js2-script-node-p node) |
|
11646 node |
|
11647 (js2-mode-find-enclosing-fn node))) |
|
11648 (beg (js2-node-abs-pos fn))) |
|
11649 (unless (js2-ast-root-p fn) |
|
11650 (narrow-to-region beg (+ beg (js2-node-len fn)))))) |
|
11651 |
|
11652 (defalias 'js2r 'js2-mode-reset) |
|
11653 |
|
11654 (provide 'js2-mode) |
|
11655 |
|
11656 ;;; js2-mode.el ends here |
|
11657 |
|
11658 |
|
11659 ;;; js2.el ends here |