Mercurial > hg > xemacs
comparison xquery-mode.el @ 27:803e4156c7a0 laptop
update to 2010 version? Other changes?
author | ht |
---|---|
date | Wed, 22 Nov 2017 15:26:34 +0000 |
parents | 0e4eb9db8a93 |
children |
comparison
equal
deleted
inserted
replaced
26:5d2492e352cc | 27:803e4156c7a0 |
---|---|
1 ;;; xquery-mode.el --- A simple mode for editing xquery programs | 1 ;;; xquery-mode.el --- A simple mode for editing xquery programs |
2 ;; Time-stamp: <2005-03-26 18:05:39 sacharya> | 2 ;; Time-stamp: <2010-08-10 12:15:14 mblakele> |
3 | 3 |
4 ;;; Copyright (C) 2005 Suraj Acharya | 4 ;;; Copyright (C) 2005 Suraj Acharya |
5 | 5 ;;; Copyright (C) 2006-2012 Michael Blakeley |
6 ;; Author: Suraj Acharya <sacharya@cs.indiana.edu> | 6 |
7 ;; Authors: | |
8 ;; Suraj Acharya <sacharya@cs.indiana.edu> | |
9 ;; Michael Blakeley <mike@blakeley.com> | |
7 | 10 |
8 ;; This file is not part of GNU Emacs. | 11 ;; This file is not part of GNU Emacs. |
9 | 12 |
10 ;; xquery-mode.el is free software; you can redistribute it | 13 ;; xquery-mode.el is free software; you can redistribute it |
11 ;; and/or modify it under the terms of the GNU General Public License | 14 ;; and/or modify it under the terms of the GNU General Public License |
12 ;; as published by the Free Software Foundation; either version 2, or | 15 ;; as published by the Free Software Foundation; either version 2, or |
13 ;; (at your option) any later version.: | 16 ;; (at your option) any later version. |
14 | 17 |
15 ;; This software is distributed in the hope that it will be useful, | 18 ;; This software is distributed in the hope that it will be useful, |
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | 19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 ;; GNU General Public License for more details. | 21 ;; GNU General Public License for more details. |
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the | 24 ;; along with GNU Emacs; see the file COPYING. If not, write to the |
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | 25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
23 ;; Boston, MA 02111-1307, USA. | 26 ;; Boston, MA 02111-1307, USA. |
24 | 27 |
25 ;;; Commentary: | 28 ;;; Commentary: |
26 ;;; | 29 ;;; |
27 | 30 |
31 | |
32 ;;; History: | |
33 ;; | |
34 ;; 2011-10-08 mostly rewritten, knows about some MarkLogic extensions | |
35 ;; | |
36 ;; 2005-03-26 release by sacharya | |
37 ;; to http://www.emacswiki.org/cgi-bin/wiki/xquery-mode.el | |
38 ;; | |
39 | |
40 (require 'font-lock) | |
41 | |
42 ;; TODO 'if()' is highlighted as a function | |
43 | |
44 ;; TODO requiring nxml-mode excludes XEmacs - just for colors? | |
45 ;; TODO test using featurep 'xemacs | |
28 (require 'nxml-mode) | 46 (require 'nxml-mode) |
47 | |
48 ;; TODO use nxml for element completion? | |
49 | |
29 (require 'generic-mode) | 50 (require 'generic-mode) |
30 ;;; Code: | |
31 (define-generic-mode 'xquery-mode | 51 (define-generic-mode 'xquery-mode |
32 '(("(:" . ":)") ("!-" . "->")) | 52 '() |
33 '("xquery" "version" "encoding" "at" "module" "namespace" "child" "descendant" "parent" "attribute" "self" "descendant-or-self" "ancestor" "following-sibling" "preceding-sibling" "following" "preceding" "ancestor-or-self" "declare" "function" "option" "ordering" "ordered" "unordered" "default" "order" "external" "or" "and" "div" "idiv" "mod" "in" "construction" "satisfies" "return" "then" "else" "boundary-space" "base-uri" "preserve" "strip" "copy-namespaces" "no-preserve" "inherit" "no-inherit" "to" "where" "collation" "intersect" "union" "except" "as" "case" "instance" "of" "castable" "item" "element" "schema-element" "schema-attribute" "processing-instruction" "comment" "text" "empty" "import" "schema" "is" "eq" "ne" "gt" "ge" "lt" "le" "some" "every" "for" "let" "cast" "treat" "validate" "document-node" "document" "node" "if" "typeswitch" "by" "stable" "ascending" "descending" "greatest" "least" "variable") ;keywords | 53 '() |
34 '(("\\(\\$\\w+\\)" 1 font-lock-variable-name-face) ;; \\(\\s_\\|\\w\\) | 54 '() ;font-lock-list |
35 ("\\(\\w*:?\\w+\\)\\s *(" 1 font-lock-function-name-face) | 55 '(".xq\\'") ;auto-mode-list |
36 ("\\(<\\)\\(/?\\)\\(\\w*\\)\\(:?\\)\\(\\w+\\).*?\\(/?\\)\\(>\\)" | 56 '(xquery-set-indent-function nil) ;function list |
37 (1 'nxml-tag-delimiter-face) | 57 "A Major mode for editing xquery.") |
38 (2 'nxml-tag-slash-face) | 58 |
39 (3 'nxml-element-prefix-face) | 59 ;; customization hook |
40 (4 'nxml-element-colon-face) | 60 (defcustom xquery-mode-hook nil |
41 (5 'nxml-element-local-name-face) | 61 "Hook run after entering XQuery mode." |
42 (6 'nxml-tag-slash-face) | 62 :type 'hook |
43 (7 'nxml-tag-delimiter-face) | 63 :options '(turn-on-xquery-indent turn-on-font-lock)) |
44 ) | 64 |
45 ("\\(\\w*\\)\\(:?\\)\\(\\w+\\)=\\([\"']\\)\\(.*?\\)\\([\"']\\)" | 65 (defvar xquery-toplevel-bovine-table nil "Top level bovinator table") |
46 (1 'nxml-attribute-prefix-face) | 66 |
47 (2 'nxml-attribute-colon-face) | 67 (defvar xquery-mode-syntax-table () "Syntax table for xquery-mode") |
48 (3 'nxml-attribute-local-name-face) | 68 |
49 (4 'nxml-attribute-value-delimiter-face) | 69 (setq xquery-mode-syntax-table |
50 (5 'nxml-attribute-value-face) | 70 (let ((xquery-mode-syntax-table (make-syntax-table))) |
51 (6 'nxml-attribute-value-delimiter-face)) | 71 ;; single-quotes are equivalent to double-quotes |
52 ("\\(/\\)\\(\\w*\\)\\(:?\\)\\(\\w+\\)" | 72 (modify-syntax-entry ?' "\"" xquery-mode-syntax-table) |
53 (1 font-lock-constant-face) | 73 ;; treat underscores as punctuation |
54 (2 font-lock-constant-face) | 74 (modify-syntax-entry ?\_ "." xquery-mode-syntax-table) |
55 (3 font-lock-constant-face) | 75 ;; treat hypens as punctuation |
56 (4 font-lock-constant-face) | 76 (modify-syntax-entry ?\- "." xquery-mode-syntax-table) |
57 ) | 77 ;; colons are both punctuation and comments |
58 ("as\\s +\\(\\w*:?\\w+\\)" | 78 ;; the space after '.' indicates an unused matching character slot |
59 (1 font-lock-type-face) | 79 (modify-syntax-entry ?\: ". 23" xquery-mode-syntax-table) |
60 ) | 80 ;; XPath step separator / is punctuation |
61 ) ;font-lock-list | 81 (modify-syntax-entry ?/ "." xquery-mode-syntax-table) |
62 '(".xq[ml]?$") ;auto-mode-list | 82 ;; xquery doesn't use backslash-escaping, so \ is punctuation |
63 '(xquery-set-indent-function xquery-set-up-syntax-table) ;function list | 83 (modify-syntax-entry ?\\ "." xquery-mode-syntax-table) |
64 "A Major mode for editing xquery." | 84 ;; set-up the syntax table correctly for all the different braces |
85 (modify-syntax-entry ?\{ "(}" xquery-mode-syntax-table) | |
86 (modify-syntax-entry ?\} "){" xquery-mode-syntax-table) | |
87 (modify-syntax-entry ?\[ "(]" xquery-mode-syntax-table) | |
88 (modify-syntax-entry ?\] ")]" xquery-mode-syntax-table) | |
89 ;;(modify-syntax-entry ?\< "(" xquery-mode-syntax-table) | |
90 ;;(modify-syntax-entry ?\> ")" xquery-mode-syntax-table) | |
91 ;; parens may indicate a comment, or may be a sequence | |
92 ;; note that (: will balance ), ( will balance ::), etc. | |
93 ;; note 'n' for comment nesting | |
94 (modify-syntax-entry ?\( "()1" xquery-mode-syntax-table) | |
95 (modify-syntax-entry ?\) ")(4" xquery-mode-syntax-table) | |
96 xquery-mode-syntax-table)) | |
97 | |
98 (defvar xquery-mode-keywords () "Keywords for xquery-mode") | |
99 | |
100 (defvar xquery-mode-comment-start "(: " | |
101 "String used to start an XQuery mode comment.") | |
102 ;;(make-local-variable 'comment-start) | |
103 | |
104 | |
105 (defvar xquery-mode-comment-end " :)" | |
106 "String used to end an XQuery mode comment.") | |
107 | |
108 | |
109 (defvar xquery-mode-comment-fill ":" | |
110 "String used to fill an XQuery mode comment.") | |
111 | |
112 | |
113 (defvar xquery-mode-comment-start-skip "(:\\s-+" | |
114 "Regexp to match an XQuery mode comment and any following whitespace.") | |
115 | |
116 | |
117 ;; NOTE - derived-mode will automatically copy some vars | |
118 ;; xquery-map as keymap | |
119 ;; xquery-syntax-table as syntax-table | |
120 ;; xquery-abbrev-table as abbrev-table | |
121 ;; xquery-hook as initialization hook | |
122 ;;;###autoload | |
123 (define-derived-mode xquery-mode fundamental-mode "XQuery" | |
124 "A major mode for W3C XQuery 1.0" | |
125 ;; indentation | |
126 (set (make-local-variable 'indent-line-function) 'xquery-indent-line) | |
127 ;; apparently it's important to set at least an empty list up-front | |
128 (set (make-local-variable 'font-lock-defaults) | |
129 (list (list ()))) | |
130 (set (make-local-variable 'comment-start) xquery-mode-comment-start) | |
131 (set (make-local-variable 'comment-end) xquery-mode-comment-end) | |
132 (set (make-local-variable 'comment-fill) xquery-mode-comment-fill) | |
133 (set (make-local-variable 'comment-start-skip) xquery-mode-comment-start-skip) | |
65 ) | 134 ) |
66 | 135 |
67 | 136 ;; XQuery doesn't have keywords, but these usually work... |
137 ;; TODO remove as many as possible, in favor of parsing | |
138 (setq xquery-mode-keywords | |
139 (list | |
140 ;; FLWOR | |
141 ;;"let" "for" | |
142 "at" "in" | |
143 "where" | |
144 "stable order by" "order by" | |
145 "ascending" "descending" "empty" "greatest" "least" "collation" | |
146 "return" | |
147 ;; XPath axes | |
148 "self" "child" "descendant" "descendant-or-self" | |
149 "parent" "ancestor" "ancestor-or-self" | |
150 "following" "following-sibling" | |
151 "preceding" "preceding-sibling" | |
152 ;; conditionals | |
153 "if" "then" "else" | |
154 "typeswitch" ;"case" "default" | |
155 ;; quantified expressions | |
156 "some" "every" "construction" "satisfies" | |
157 ;; schema | |
158 "schema-element" "schema-attribute" "validate" | |
159 ;; operators | |
160 "intersect" "union" "except" "to" | |
161 "is" "eq" "ne" "gt" "ge" "lt" "le" | |
162 "or" "and" | |
163 "div" "idiv" "mod" | |
164 )) | |
165 | |
166 ;; to match only word-boundaries, we turn the keywords into a big regex | |
167 (defvar xquery-mode-keywords-regex () "Keywords regex for xquery mode") | |
168 | |
169 ;; transform the list of keywords into regex | |
170 ;; check for word-boundaries instead of whitespace | |
171 (setq xquery-mode-keywords-regex | |
172 (concat (concat "\\b\\(" | |
173 (mapconcat | |
174 (function (lambda (r) | |
175 (if (string-match "[ \t]+" r) | |
176 (replace-match "[ \t]+" nil t r) r))) | |
177 xquery-mode-keywords "\\|")) | |
178 "\\)\\b")) | |
179 | |
180 ;;(message xquery-mode-keywords-regex) | |
181 | |
182 ;; XQuery syntax - TODO build a real parser | |
183 (defvar xquery-mode-ncname () "NCName regex, in 1 group") | |
184 (setq xquery-mode-ncname "\\(\\sw[-_\\.[:word:]]*\\)") | |
185 | |
186 ;; highlighting needs a group, even if it's "" - so use (...?) not (...)? | |
187 ;; note that this technique treats the local-name as optional, | |
188 ;; when the prefix should be the optional part. | |
189 (defvar xquery-mode-qname () "QName regex, in 3 groups") | |
190 (setq xquery-mode-qname | |
191 (concat | |
192 xquery-mode-ncname "\\(:?\\)" "\\(" xquery-mode-ncname "?\\)")) | |
193 | |
194 ;; highlighting | |
195 ;; these are "matcher . highlighter" forms | |
196 (font-lock-add-keywords | |
197 'xquery-mode | |
198 `( | |
199 ;; prolog version decl | |
200 ("\\(xquery\\s-+version\\)\\s-+" | |
201 (1 font-lock-keyword-face)) | |
202 ;; namespace default decl for 0.9 or 1.0 | |
203 (,(concat | |
204 "\\(\\(declare\\)?" | |
205 "\\(\\s-+default\\s-+\\(function\\|element\\)\\)" | |
206 "\\s-+namespace\\)\\s-+") | |
207 (1 font-lock-keyword-face)) | |
208 ;; namespace decl | |
209 (,(concat | |
210 "\\(declare\\s-+namespace\\)\\s-+") | |
211 (1 font-lock-keyword-face)) | |
212 ;; option decl | |
213 (,(concat "\\(declare\\s-+option\\s-+" xquery-mode-qname "\\)") | |
214 (1 font-lock-keyword-face)) | |
215 ;; import module decl - must precede library module decl | |
216 ("\\(import\\s-+module\\)\\s-+\\(namespace\\)?\\s-+" | |
217 (1 font-lock-keyword-face) | |
218 (2 font-lock-keyword-face)) | |
219 ;; library module decl, for 1.0 or 0.9-ml | |
220 ("\\(module\\)\\s-+\\(namespace\\)?\\s-*" | |
221 (1 font-lock-keyword-face) | |
222 (2 font-lock-keyword-face)) | |
223 ;; import schema decl | |
224 ("\\(import\\s-+schema\\)\\s-+\\(namespace\\)?\\s-+" | |
225 (1 font-lock-keyword-face) | |
226 (2 font-lock-keyword-face)) | |
227 ;; variable decl | |
228 ("\\(for\\|let\\|declare\\s-+variable\\|define\\s-+variable\\)\\s-+\\$" | |
229 (1 font-lock-keyword-face)) | |
230 ;; variable name | |
231 (,(concat "\\($" xquery-mode-qname "\\)") | |
232 (1 font-lock-variable-name-face)) | |
233 ;; function decl | |
234 (,(concat | |
235 "\\(declare\\s-+function\\" | |
236 "|declare\\s-+private\\s-+function\\" | |
237 "|define\\s-+function\\)\\s-+\\(" | |
238 xquery-mode-qname "\\)(") | |
239 (1 font-lock-keyword-face) | |
240 (2 font-lock-function-name-face)) | |
241 ;; schema test or type decl | |
242 (,(concat | |
243 "\\(" | |
244 "case" | |
245 "\\|instance\\s-+of\\|castable\\s-+as\\|treat\\s-+as\\|cast\\s-+as" | |
246 ;; "as" must be last in the list | |
247 "\\|as" | |
248 "\\)" | |
249 "\\s-+\\(" xquery-mode-qname "\\)" | |
250 ;; type may be followed by element() or element(x:foo) | |
251 "(?\\s-*\\(" xquery-mode-qname "\\)?\\s-*)?") | |
252 (1 font-lock-keyword-face) | |
253 (2 font-lock-type-face) | |
254 ; TODO the second qname never matches | |
255 (3 font-lock-type-face)) | |
256 ;; function call | |
257 (,(concat "\\(" xquery-mode-qname "\\)(") | |
258 (1 font-lock-function-name-face)) | |
259 ;; named node constructor | |
260 (,(concat "\\(attribute\\|element\\)\\s-+\\(" xquery-mode-qname "\\)\\s-*{") | |
261 (1 font-lock-keyword-face) | |
262 (2 font-lock-constant-face)) | |
263 ;; anonymous node constructor | |
264 ("\\(binary\\|comment\\|document\\|text\\)\\s-*{" | |
265 (1 font-lock-keyword-face)) | |
266 ;; typeswitch default | |
267 ("\\(default\\s-+return\\)\\s-+" | |
268 (1 font-lock-keyword-face) | |
269 (2 font-lock-keyword-face)) | |
270 ;; | |
271 ;; highlighting - use nxml config to font-lock directly-constructed XML | |
272 ;; | |
273 ;; xml start element start | |
274 (,(concat "<" xquery-mode-qname) | |
275 (1 'nxml-element-prefix-face) | |
276 (2 'nxml-element-colon-face) | |
277 (3 'nxml-element-prefix-face)) | |
278 ;; xml start element end | |
279 ("\\(/?\\)>" | |
280 (1 'nxml-tag-slash-face)) | |
281 ;; xml end element | |
282 (,(concat "<\\(/\\)" xquery-mode-qname ">") | |
283 (1 'nxml-tag-slash-face) | |
284 (2 'nxml-element-prefix-face) | |
285 (3 'nxml-element-colon-face) | |
286 (4 'nxml-element-local-name-face)) | |
287 ;; TODO xml attribute or xmlns decl | |
288 ;; (,(concat xquery-mode-qname "=\\([\"']\\)\\(.*?\\)\\([\"']\\)") | |
289 ;; (1 'nxml-attribute-prefix-face) | |
290 ;; (2 'nxml-attribute-colon-face) | |
291 ;; (3 'nxml-attribute-local-name-face) | |
292 ;; (4 'nxml-attribute-value-delimiter-face) | |
293 ;; (5 'nxml-attribute-value-face) | |
294 ;; (6 'nxml-attribute-value-delimiter-face)) | |
295 ;; xml comments | |
296 ("\\(<!--\\)\\([^-]*\\)\\(-->\\)" | |
297 (1 'nxml-comment-delimiter-face) | |
298 (2 'nxml-comment-content-face) | |
299 (3 'nxml-comment-delimiter-face)) | |
300 ;; highlighting XPath expressions, including *:foo | |
301 ;; TODO this doesn't match expressions unless they start with slash | |
302 ;; TODO but matching without a leading slash overrides all the keywords | |
303 (,(concat "\\(//?\\)\\(*\\|\\sw*\\)\\(:?\\)" xquery-mode-ncname) | |
304 (1 font-lock-constant-face) | |
305 (2 font-lock-constant-face) | |
306 (3 font-lock-constant-face) | |
307 (4 font-lock-constant-face)) | |
308 ;; | |
309 ;; highlighting pseudo-keywords - must be late, for problems like 'if ()' | |
310 ;; | |
311 (,xquery-mode-keywords-regex (1 font-lock-keyword-face)) | |
312 )) | |
313 | |
314 ;; file-extension mappings | |
315 ;;;###autoload | |
316 (add-to-list 'auto-mode-alist '(".xq[erxy]\\'" . xquery-mode)) | |
317 | |
318 (defun xquery-forward-sexp (&optional arg) | |
319 "XQuery forward s-expresssion. | |
320 This function is not very smart. It tries to use | |
321 `nxml-forward-balanced-item' if it sees '>' or '<' characters in | |
322 the current line (ARG), and uses the regular `forward-sexp' | |
323 otherwise." | |
324 (if (> arg 0) | |
325 (progn | |
326 (if (looking-at "\\s-*<") | |
327 (nxml-forward-balanced-item arg) | |
328 (let ((forward-sexp-function nil)) (forward-sexp arg)))) | |
329 (if (looking-back ">\\s-*") | |
330 (nxml-forward-balanced-item arg) | |
331 (let ((forward-sexp-function nil)) (forward-sexp arg))))) | |
332 | |
333 ;; indentation | |
334 (defvar xquery-indent-size tab-width "The size of each indent level.") | |
335 | |
336 ;; (setq debug-on-error t) ;\ DEBUG ::) | |
337 | |
338 (defvar xquery-indent-debug nil) | |
339 | |
340 ;; (setq xquery-indent-debug t) ;\ DEBUG ::) | |
341 | |
342 (defun xquery-toggle-debug-indent () | |
343 "Toggle the debug flag used in `xquery-calculate-indentation'." | |
344 (interactive) | |
345 (setq xquery-indent-debug (not xquery-indent-debug)) | |
346 (message "xquery-indent-debug is %sabled" | |
347 (if xquery-indent-debug "en" "dis"))) | |
348 | |
349 (defun xquery-indent-debug-toggle () | |
350 "Toggle the debug flag used in `xquery-calculate-indentation'." | |
351 (interactive) (xquery-toggle-debug-indent)) | |
352 | |
353 (defun xquery-indent-debug-message (results) | |
354 "Utility function to display debug messages for indentation. | |
355 RESULTS must be a list of a column number and a string message." | |
356 (if xquery-indent-debug | |
357 (let ((cc (car results)) | |
358 (msg (cdr results))) | |
359 (message "xquery-indent-debug: (%d) %S" cc msg)) ) ) | |
68 | 360 |
69 (defun xquery-set-indent-function () | 361 (defun xquery-set-indent-function () |
70 "Set the indent function for xquery mode." | 362 "Set the indent function for xquery mode." |
71 (setq nxml-prolog-end (point-min)) | 363 (setq nxml-prolog-end (point-min)) |
72 (setq nxml-scan-end (copy-marker (point-min) nil)) | 364 (setq nxml-scan-end (copy-marker (point-min) nil)) |
73 (set (make-local-variable 'indent-line-function) 'xquery-indent-line) | 365 (set (make-local-variable 'indent-line-function) 'xquery-indent-line) |
74 (make-local-variable 'forward-sexp-function) | 366 (make-local-variable 'forward-sexp-function) |
75 (setq forward-sexp-function 'xquery-forward-sexp) | 367 (setq forward-sexp-function 'xquery-forward-sexp) |
76 ;;(local-set-key "/" 'nxml-electric-slash) | 368 (local-set-key "/" 'nxml-electric-slash)) |
77 ) | |
78 | |
79 (defun xquery-forward-sexp (&optional arg) | |
80 "Xquery forward s-expresssion. | |
81 This function is not very smart, it tries to use | |
82 `nxml-forward-balanced-item' if it sees '>' or '<' characters in | |
83 the direction you are going, and uses the regular `forward-sexp' | |
84 otherwise. " | |
85 (if (> arg 0) | |
86 (progn | |
87 (if (looking-at "[ \t]*<") | |
88 (nxml-forward-balanced-item arg) | |
89 (let ((forward-sexp-function nil)) (forward-sexp arg)))) | |
90 (if (looking-back ">[ \t]*") | |
91 (nxml-forward-balanced-item arg) | |
92 (let ((forward-sexp-function nil)) (forward-sexp arg)))) | |
93 ) | |
94 | |
95 | |
96 (defun xquery-set-up-syntax-table () | |
97 "Allow the hypen character to be recognized as part of a xquery symbol." | |
98 (modify-syntax-entry ?- "w" (syntax-table)) | |
99 (modify-syntax-entry ?/ "." (syntax-table)) | |
100 ;; set-up the syntax table correctly for parentheis type characters | |
101 (modify-syntax-entry ?\{ "(}" (syntax-table)) | |
102 (modify-syntax-entry ?\} "){" (syntax-table)) | |
103 (modify-syntax-entry ?\[ "(]" (syntax-table)) | |
104 (modify-syntax-entry ?\] ")]" (syntax-table)) | |
105 (modify-syntax-entry ?\( "()1" (syntax-table)) | |
106 (modify-syntax-entry ?\) ")(4" (syntax-table)) | |
107 ;;(modify-syntax-entry ?\< "(>" (syntax-table)) | |
108 ;;(modify-syntax-entry ?\> ")<" (syntax-table)) | |
109 ;; xquery comments are like (: :) -- handled above at mode decl | |
110 ;;(modify-syntax-entry ?\: ".23" (syntax-table)) | |
111 ) | |
112 | |
113 | |
114 | 369 |
115 (defun xquery-indent-line () | 370 (defun xquery-indent-line () |
116 "Indent current line as xquery code." | 371 "Indent current line as xquery code." |
117 (interactive) | 372 (interactive) |
118 (let ((savep (> (current-column) (current-indentation))) | 373 (let ((savept (> (current-column) (current-indentation))) |
119 (indent (condition-case err (max (xquery-calculate-indentation) 0) | 374 (results (xquery-calculate-indentation))) |
120 (error (message "%S" err))))) | 375 (xquery-indent-debug-message results) |
121 (if savep | 376 (let ( (indent (car results)) ) |
122 (save-excursion (indent-line-to indent)) | 377 (if (> indent -1) |
123 (indent-line-to indent)))) | 378 (if savept |
124 | 379 (save-excursion (indent-line-to indent)) |
125 (defvar xquery-start-block-regexp "[ \t]*\\((\|{\\|for\\|let\\|where\\|return\\|if\\|else\\|typeswitch\\|declare[ \t]+function\\|.*[({]$\\)" | 380 (indent-line-to (max 0 indent)) ) ) ) ) ) |
126 "A regular expression which indicates that a xquery block is starting.") | 381 |
127 | 382 (defun xquery-indent-via-nxml () |
128 (defvar xquery-flwr-block-regexp "[ \t]*\\(for\\|let\\|where\\|return\\|order\\|stable\\s *order\\)") | 383 "This function uses nxml to calculate the indentation." |
129 | 384 (let ((nxml-prolog-end (point-min)) |
130 (defvar xquery-indent-size 2 | 385 (nxml-scan-end (copy-marker (point-min) nil)) ) |
131 "The size of each indent level.") | 386 (nxml-compute-indent) ) ) |
132 | 387 |
133 (defvar xquery-indent-debug nil) | 388 ;; to make debugging easier, use setq to set the actual values |
134 | 389 (defvar xquery-indent-regex "" |
135 (defun xquery-toggle-debug-indent () | 390 "A regular expression indicating an indentable xquery sub-expression.") |
136 "Toggle the debug flag used in `xquery-calculate-indentation'. " | 391 |
137 (interactive) | 392 (setq xquery-indent-regex |
138 (setq xquery-indent-debug (not xquery-indent-debug)) | 393 (concat "^\\s-*\\(" |
139 (message (concat "xquery-indent-debug is " (if xquery-indent-debug "en" "dis") "abled")) | 394 "typeswitch\\|for\\|let\\|where\\|order\\s-+by\\|return" |
140 ) | 395 "\\|if\\|then\\|else" |
396 "\\)\\s-*$") ) | |
141 | 397 |
142 (defun xquery-calculate-indentation () | 398 (defun xquery-calculate-indentation () |
143 "Return the column to which the current line should be indented." | 399 "Calculate the indentation for a line of XQuery. |
144 (beginning-of-line) | 400 This function returns the column to which the current line should be indented, |
145 (if (bobp) | 401 and a debug expression." |
146 0 ; First line is always non-indented | 402 (save-excursion |
147 (skip-chars-forward " \t") | 403 (beginning-of-line) |
148 (cond | 404 (cond |
149 ;; do nothing if this is a comment | 405 |
150 ((eq (get-text-property (point) 'face) 'font-lock-comment-face) (current-indentation)) | 406 ;; TODO this sort of works, but needs to set some state |
151 | 407 ;; TODO once we have state, how and when do we reset it? |
152 ((looking-at "\\(</?\\w\\|{\\)") ;; xml constructor or enclosed expressions | 408 ;; ((save-excursion |
153 (if xquery-indent-debug | 409 ;; (previous-line) |
154 (message "xquery-indent-debug: xml constructor")) | 410 ;; (message "current-word = %S" (current-word)) ; DEBUG |
155 (let ((nxml-prolog-end (point-min)) | 411 ;; (message "looking-at xquery-indent-regex = %S" |
156 (nxml-scan-end (copy-marker (point-min) nil))) | 412 ;; (looking-at xquery-indent-regex)) ; DEBUG |
157 (nxml-compute-indent) | 413 ;; (looking-at xquery-indent-regex)) |
158 )) | 414 ;; (save-excursion |
159 | 415 ;; (previous-line) |
160 ;; for close braces or else statements indent to the same level as the opening { | 416 ;; (list |
161 ((looking-at "}") | 417 ;; (+ xquery-indent-size (current-indentation)) |
162 (if xquery-indent-debug | 418 ;; "previous line starts new block"))) |
163 (message "xquery-indent-debug: }")) | 419 |
164 (save-excursion | 420 ;; default, using sexp parser |
165 (backward-up-list) | 421 (t |
166 (let ((cc (current-column))) | 422 ;; calculate indent for beginning of line indent, then end of line |
167 (beginning-of-line) | 423 (let* ((point-bol (point)) |
168 (if (looking-at xquery-start-block-regexp) | 424 (results-bol (parse-partial-sexp (point-min) point-bol)) |
169 (current-indentation) | 425 ;; 0. depth in parens. |
170 cc)))) | 426 (paren-level-bol (car results-bol)) |
171 | 427 ;; 1. character address of start of innermost containing list. |
172 ((looking-at "else") | 428 (list-start-bol (car (cdr results-bol))) |
173 (if xquery-indent-debug | 429 ;; 2. character address of start of last complete sexp. |
174 (message "xquery-indent-debug: else")) | 430 (sexp-start-bol (car (cdr (cdr results-bol))) ) |
175 (save-excursion | 431 ;; 3. non-nil if inside a string. |
176 (xquery-previous-non-empty-line) | 432 (stringp-bol (car (cdr (cdr (cdr results-bol)))) ) |
177 (- (current-indentation) xquery-indent-size) | 433 ;; 4. nil if outside comment, t if inside non-nesting comment, |
178 )) | 434 ;; else integer comment nesting. |
179 | 435 (comment-level-bol |
180 ;; for close parens, indent to the start of the func call | 436 (car (cdr (cdr (cdr (cdr results-bol))))) ) |
181 ((looking-at ")") | 437 ;; 5. t if following a quote character. |
182 (if xquery-indent-debug | 438 (quotep-bol |
183 (message "xquery-indent-debug: )")) | 439 (car (cdr (cdr (cdr (cdr (cdr results-bol)))))) ) |
184 (save-excursion | 440 ;; 6. the minimum paren-depth encountered during this scan. |
185 (backward-up-list) | 441 (min-level-bol |
186 (if (looking-back "\\w+\\s *") | 442 (car (cdr (cdr (cdr (cdr (cdr (cdr results-bol))))))) ) |
187 (backward-word)) | 443 ;; 7. t if in a comment of style b; |
188 (current-column) | 444 ;; symbol 'syntax-table' if the comment is generic. |
189 )) | 445 (bcommentp-bol |
190 | 446 (car (cdr (cdr (cdr (cdr (cdr (cdr (cdr results-bol)))))))) ) |
191 ;; order flwr expressions on the same column | 447 ;; 8. character address of start of comment or string, else nil. |
192 ((save-excursion | 448 (comment-start-bol |
193 (when | 449 (car (cdr (cdr |
194 (and | 450 (cdr (cdr (cdr (cdr (cdr |
195 (looking-at xquery-flwr-block-regexp) | 451 (cdr results-bol)))))))))) |
196 (progn | 452 ;; 9. intermediate data for continuation of parsing. (not used) |
197 (xquery-previous-non-empty-line) | 453 |
198 (beginning-of-line) | 454 (point-eol (save-excursion (end-of-line) (point))) |
199 (looking-at xquery-flwr-block-regexp))) | 455 ;; undocumented, but parse-partial-sexp seems to change point |
200 (if xquery-indent-debug | 456 ;; TODO use state-bol? seems to have problems |
201 (message "xquery-indent-debug: nested flwr")) | 457 (results-eol (save-excursion |
202 (current-indentation) | 458 (parse-partial-sexp (point-min) point-eol))) |
203 ) | 459 ;; what would nxml do? |
204 )) | 460 (results-nxml |
205 | 461 (cond |
206 ;; if this is the first non-empty line after a block, indent xquery-indent-size chars relative to the block | 462 ((looking-at "\\s-*<!--") |
207 ((save-excursion | 463 (list (xquery-indent-via-nxml) "xml start-comment")) |
208 (xquery-previous-non-empty-line) | 464 ((looking-at "\\s-*-->") |
209 (beginning-of-line) | 465 (list (xquery-indent-via-nxml) "xml end-comment")) |
210 (when (looking-at xquery-start-block-regexp) | 466 ((looking-at "\\s-*<\\sw+") |
467 (list (xquery-indent-via-nxml) "xml start-element")) | |
468 ((looking-at "\\s-*</?\\sw+") | |
469 (list (xquery-indent-via-nxml) "xml end-element")) | |
470 (t nil) ) ) | |
471 ;; later we will multiple by xquery-indent-size | |
472 (nxml-indent | |
473 (if results-nxml | |
474 (/ (car results-nxml) xquery-indent-size))) | |
475 ) | |
211 (if xquery-indent-debug | 476 (if xquery-indent-debug |
212 (message "xquery-indent-debug: first line in block")) | 477 (progn |
213 (+ xquery-indent-size (current-indentation)))) | 478 (message "point-bol = %S" point-bol) |
214 ) | 479 (message "point-eol = %S" point-eol) |
215 | 480 (message "point = %S" (point)) |
216 ;; for everything else indent relative to the outer list | 481 (message "results-eol = %S" results-eol) |
217 (t | 482 (message "results-nxml = %S" results-nxml))) |
218 (if xquery-indent-debug | 483 (let* ( |
219 (message "xquery-indent-debug: everyting else")) | 484 ;; 0. depth in parens |
220 (save-excursion (xquery-previous-non-empty-line) (current-indentation))) | 485 (paren-level-eol (car results-eol)) |
221 ))) | 486 (indent |
222 | 487 (cond |
223 (when (featurep 'xemacs) | 488 (comment-level-bol |
224 (unless (functionp 'looking-back) | 489 ; within a multi-line comment |
225 ;; from GNU Emacs subr.el | 490 ; start of comment indentation + 1 |
226 (defun looking-back (regexp &optional limit greedy) | 491 (+ 1 (save-excursion |
227 "Return non-nil if text before point matches regular expression | 492 (goto-char comment-start-bol) |
228 REGEXP. | 493 (current-indentation) )) ) |
229 Like `looking-at' except matches before point, and is slower. | 494 ; TODO multi-line prolog variable? |
230 LIMIT if non-nil speeds up the search by specifying a minimum | 495 (nil -1) |
231 starting position, to avoid checking matches that would start | 496 ; mult-line module import? |
232 before LIMIT. | 497 ((and (save-excursion |
233 If GREEDY is non-nil, extend the match backwards as far as possible, | 498 (beginning-of-line) |
234 stopping when a single additional previous character cannot be part | 499 (looking-at "^\\s-*at\\s-+")) |
235 of a match for REGEXP." | 500 (save-excursion |
236 (let ((start (point)) | 501 (beginning-of-line) |
237 (pos | 502 (previous-line) |
238 (save-excursion | 503 (looking-at "^\\s-*import\\s-+module\\s-+"))) |
239 (and (re-search-backward (concat "\\(?:" regexp | 504 xquery-indent-size) |
240 "\\)\\=") limit t) | 505 ; multi-line function decl? |
241 (point))))) | 506 ; TODO handle more than 1 line previous |
242 (if (and greedy pos) | 507 ((and (save-excursion |
243 (save-restriction | 508 (beginning-of-line) |
244 (narrow-to-region (point-min) start) | 509 (looking-at "^\\s-*as\\s-+")) |
245 (while (and (> pos (point-min)) | 510 (save-excursion |
246 (save-excursion | 511 (beginning-of-line) |
247 (goto-char pos) | 512 (previous-line) |
248 (backward-char 1) | 513 (looking-at |
249 (looking-at (concat "\\(?:" regexp | 514 "^\\s-*\\(define\\|declare\\)\\s-+function\\s-+"))) |
250 "\\)\\'")))) | 515 xquery-indent-size) |
251 (setq pos (1- pos))) | 516 ; default - use paren-level-bol |
252 (save-excursion | 517 (t (* xquery-indent-size |
253 (goto-char pos) | 518 ; special when simply closing 1 level |
254 (looking-at (concat "\\(?:" regexp "\\)\\'"))))) | 519 (cond |
255 (not (null pos)))))) | 520 ((and (= paren-level-bol (+ 1 paren-level-eol)) |
256 | 521 (looking-at "^\\s-*\\s)[,;]?\\s-*$") ) |
257 (defun xquery-previous-non-empty-line () | 522 paren-level-eol) |
258 "Move to the last non-empty line." | 523 ; factor in the nxml-indent |
259 (re-search-backward "\\S " (point-min) t) | 524 ((and |
260 ) | 525 nxml-indent (> nxml-indent paren-level-bol)) |
526 nxml-indent) | |
527 (t paren-level-bol))))))) | |
528 (list (min 70 indent) results-bol results-eol))))))) | |
261 | 529 |
262 (provide 'xquery-mode) | 530 (provide 'xquery-mode) |
263 | 531 |
264 ;;; xquery-mode.el ends here | 532 ;;; xquery-mode.el ends here |