changeset 27:803e4156c7a0 laptop

update to 2010 version? Other changes?
author ht
date Wed, 22 Nov 2017 15:26:34 +0000
parents 5d2492e352cc
children 0e2183025318
files xquery-mode.el
diffstat 1 files changed, 478 insertions(+), 210 deletions(-) [+]
line wrap: on
line diff
--- a/xquery-mode.el	Wed Nov 22 15:25:19 2017 +0000
+++ b/xquery-mode.el	Wed Nov 22 15:26:34 2017 +0000
@@ -1,16 +1,19 @@
 ;;; xquery-mode.el --- A simple mode for editing xquery programs
-;; Time-stamp: <2005-03-26 18:05:39 sacharya>
+;; Time-stamp: <2010-08-10 12:15:14 mblakele>
 
 ;;; Copyright (C) 2005 Suraj Acharya
+;;; Copyright (C) 2006-2012 Michael Blakeley
 
-;; Author: Suraj Acharya <sacharya@cs.indiana.edu>
+;; Authors:
+;;   Suraj Acharya <sacharya@cs.indiana.edu>
+;;   Michael Blakeley <mike@blakeley.com>
 
 ;; This file is not part of GNU Emacs.
 
 ;; xquery-mode.el is free software; you can redistribute it
 ;; and/or modify it under the terms of the GNU General Public License
 ;; as published by the Free Software Foundation; either version 2, or
-;; (at your option) any later version.:
+;; (at your option) any later version.
 
 ;; This software is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -23,48 +26,337 @@
 ;; Boston, MA 02111-1307, USA.
 
 ;;; Commentary:
-;;; 
+;;;
+
 
+;;; History:
+;;
+;; 2011-10-08 mostly rewritten, knows about some MarkLogic extensions
+;;
+;; 2005-03-26 release by sacharya
+;;   to http://www.emacswiki.org/cgi-bin/wiki/xquery-mode.el
+;;
+
+(require 'font-lock)
+
+;; TODO 'if()' is highlighted as a function
+
+;; TODO requiring nxml-mode excludes XEmacs - just for colors?
+;; TODO test using featurep 'xemacs
 (require 'nxml-mode)
+
+;; TODO use nxml for element completion?
+
 (require 'generic-mode)
-;;; Code:
 (define-generic-mode 'xquery-mode
-  '(("(:" . ":)") ("!-" . "->"))
-  '("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
-  '(("\\(\\$\\w+\\)" 1 font-lock-variable-name-face) ;; \\(\\s_\\|\\w\\)
-    ("\\(\\w*:?\\w+\\)\\s *(" 1 font-lock-function-name-face)
-    ("\\(<\\)\\(/?\\)\\(\\w*\\)\\(:?\\)\\(\\w+\\).*?\\(/?\\)\\(>\\)" 
-     (1 'nxml-tag-delimiter-face) 
-     (2 'nxml-tag-slash-face)
-     (3 'nxml-element-prefix-face) 
-     (4 'nxml-element-colon-face)
-     (5 'nxml-element-local-name-face)
-     (6 'nxml-tag-slash-face)
-     (7 'nxml-tag-delimiter-face) 
-     )
-    ("\\(\\w*\\)\\(:?\\)\\(\\w+\\)=\\([\"']\\)\\(.*?\\)\\([\"']\\)" 
-     (1 'nxml-attribute-prefix-face) 
-     (2 'nxml-attribute-colon-face)
-     (3 'nxml-attribute-local-name-face) 
-     (4 'nxml-attribute-value-delimiter-face)
-     (5 'nxml-attribute-value-face)
-     (6 'nxml-attribute-value-delimiter-face))
-    ("\\(/\\)\\(\\w*\\)\\(:?\\)\\(\\w+\\)" 
-     (1 font-lock-constant-face)
-     (2 font-lock-constant-face) 
-     (3 font-lock-constant-face)
-     (4 font-lock-constant-face)
-     )
-    ("as\\s +\\(\\w*:?\\w+\\)" 
-     (1 font-lock-type-face)
-     )
-    ) ;font-lock-list
-  '(".xq[ml]?$") ;auto-mode-list
-  '(xquery-set-indent-function xquery-set-up-syntax-table)         ;function list
-  "A Major mode for editing xquery."
+  '()
+  '()
+  '() ;font-lock-list
+  '(".xq\\'") ;auto-mode-list
+  '(xquery-set-indent-function nil) ;function list
+  "A Major mode for editing xquery.")
+
+;; customization hook
+(defcustom xquery-mode-hook nil
+  "Hook run after entering XQuery mode."
+  :type 'hook
+  :options '(turn-on-xquery-indent turn-on-font-lock))
+
+(defvar xquery-toplevel-bovine-table nil "Top level bovinator table")
+
+(defvar xquery-mode-syntax-table () "Syntax table for xquery-mode")
+
+(setq xquery-mode-syntax-table
+  (let ((xquery-mode-syntax-table (make-syntax-table)))
+    ;; single-quotes are equivalent to double-quotes
+    (modify-syntax-entry ?' "\"" xquery-mode-syntax-table)
+    ;; treat underscores as punctuation
+    (modify-syntax-entry ?\_ "." xquery-mode-syntax-table)
+    ;; treat hypens as punctuation
+    (modify-syntax-entry ?\- "." xquery-mode-syntax-table)
+    ;; colons are both punctuation and comments
+    ;; the space after '.' indicates an unused matching character slot
+    (modify-syntax-entry ?\: ". 23" xquery-mode-syntax-table)
+    ;; XPath step separator / is punctuation
+    (modify-syntax-entry ?/ "." xquery-mode-syntax-table)
+    ;; xquery doesn't use backslash-escaping, so \ is punctuation
+    (modify-syntax-entry ?\\ "." xquery-mode-syntax-table)
+    ;; set-up the syntax table correctly for all the different braces
+    (modify-syntax-entry ?\{ "(}" xquery-mode-syntax-table)
+    (modify-syntax-entry ?\} "){" xquery-mode-syntax-table)
+    (modify-syntax-entry ?\[ "(]" xquery-mode-syntax-table)
+    (modify-syntax-entry ?\] ")]" xquery-mode-syntax-table)
+    ;;(modify-syntax-entry ?\< "(" xquery-mode-syntax-table)
+    ;;(modify-syntax-entry ?\> ")" xquery-mode-syntax-table)
+    ;; parens may indicate a comment, or may be a sequence
+    ;; note that (: will balance ), ( will balance ::), etc.
+    ;; note 'n' for comment nesting
+    (modify-syntax-entry ?\( "()1" xquery-mode-syntax-table)
+    (modify-syntax-entry ?\) ")(4" xquery-mode-syntax-table)
+    xquery-mode-syntax-table))
+
+(defvar xquery-mode-keywords () "Keywords for xquery-mode")
+
+(defvar xquery-mode-comment-start "(: "
+  "String used to start an XQuery mode comment.")
+;;(make-local-variable 'comment-start)
+
+
+(defvar xquery-mode-comment-end " :)"
+  "String used to end an XQuery mode comment.")
+
+
+(defvar xquery-mode-comment-fill ":"
+  "String used to fill an XQuery mode comment.")
+
+
+(defvar xquery-mode-comment-start-skip "(:\\s-+"
+  "Regexp to match an XQuery mode comment and any following whitespace.")
+
+
+;; NOTE - derived-mode will automatically copy some vars
+;;   xquery-map as keymap
+;;   xquery-syntax-table as syntax-table
+;;   xquery-abbrev-table as abbrev-table
+;;   xquery-hook as initialization hook
+;;;###autoload
+(define-derived-mode xquery-mode fundamental-mode "XQuery"
+  "A major mode for W3C XQuery 1.0"
+  ;; indentation
+  (set (make-local-variable 'indent-line-function) 'xquery-indent-line)
+  ;; apparently it's important to set at least an empty list up-front
+  (set (make-local-variable 'font-lock-defaults)
+    (list (list ())))
+  (set (make-local-variable 'comment-start) xquery-mode-comment-start)
+  (set (make-local-variable 'comment-end) xquery-mode-comment-end)
+  (set (make-local-variable 'comment-fill)  xquery-mode-comment-fill)
+  (set (make-local-variable 'comment-start-skip) xquery-mode-comment-start-skip)
   )
 
+;; XQuery doesn't have keywords, but these usually work...
+;; TODO remove as many as possible, in favor of parsing
+(setq xquery-mode-keywords
+  (list
+    ;; FLWOR
+    ;;"let" "for"
+    "at" "in"
+    "where"
+    "stable order by" "order by"
+    "ascending" "descending" "empty" "greatest" "least" "collation"
+    "return"
+    ;; XPath axes
+    "self" "child" "descendant" "descendant-or-self"
+    "parent" "ancestor" "ancestor-or-self"
+    "following" "following-sibling"
+    "preceding" "preceding-sibling"
+    ;; conditionals
+    "if" "then" "else"
+    "typeswitch" ;"case" "default"
+    ;; quantified expressions
+    "some" "every" "construction" "satisfies"
+    ;; schema
+    "schema-element" "schema-attribute" "validate"
+    ;; operators
+    "intersect" "union" "except" "to"
+    "is" "eq" "ne" "gt" "ge" "lt" "le"
+    "or" "and"
+    "div" "idiv" "mod"
+    ))
 
+;; to match only word-boundaries, we turn the keywords into a big regex
+(defvar xquery-mode-keywords-regex () "Keywords regex for xquery mode")
+
+;; transform the list of keywords into regex
+;; check for word-boundaries instead of whitespace
+(setq xquery-mode-keywords-regex
+  (concat (concat "\\b\\("
+            (mapconcat
+              (function (lambda (r)
+                          (if (string-match "[ \t]+" r)
+                            (replace-match "[ \t]+" nil t r) r)))
+              xquery-mode-keywords "\\|"))
+    "\\)\\b"))
+
+;;(message xquery-mode-keywords-regex)
+
+;; XQuery syntax - TODO build a real parser
+(defvar xquery-mode-ncname () "NCName regex, in 1 group")
+(setq xquery-mode-ncname "\\(\\sw[-_\\.[:word:]]*\\)")
+
+;; highlighting needs a group, even if it's "" - so use (...?) not (...)?
+;; note that this technique treats the local-name as optional,
+;; when the prefix should be the optional part.
+(defvar xquery-mode-qname () "QName regex, in 3 groups")
+(setq xquery-mode-qname
+  (concat
+    xquery-mode-ncname "\\(:?\\)" "\\(" xquery-mode-ncname "?\\)"))
+
+;; highlighting
+;; these are "matcher . highlighter" forms
+(font-lock-add-keywords
+  'xquery-mode
+  `(
+     ;; prolog version decl
+     ("\\(xquery\\s-+version\\)\\s-+"
+       (1 font-lock-keyword-face))
+     ;; namespace default decl for 0.9 or 1.0
+     (,(concat
+         "\\(\\(declare\\)?"
+         "\\(\\s-+default\\s-+\\(function\\|element\\)\\)"
+         "\\s-+namespace\\)\\s-+")
+       (1 font-lock-keyword-face))
+     ;; namespace decl
+     (,(concat
+         "\\(declare\\s-+namespace\\)\\s-+")
+       (1 font-lock-keyword-face))
+     ;; option decl
+     (,(concat "\\(declare\\s-+option\\s-+" xquery-mode-qname "\\)")
+       (1 font-lock-keyword-face))
+     ;; import module decl - must precede library module decl
+     ("\\(import\\s-+module\\)\\s-+\\(namespace\\)?\\s-+"
+       (1 font-lock-keyword-face)
+       (2 font-lock-keyword-face))
+     ;; library module decl, for 1.0 or 0.9-ml
+     ("\\(module\\)\\s-+\\(namespace\\)?\\s-*"
+       (1 font-lock-keyword-face)
+       (2 font-lock-keyword-face))
+     ;; import schema decl
+     ("\\(import\\s-+schema\\)\\s-+\\(namespace\\)?\\s-+"
+       (1 font-lock-keyword-face)
+       (2 font-lock-keyword-face))
+     ;; variable decl
+     ("\\(for\\|let\\|declare\\s-+variable\\|define\\s-+variable\\)\\s-+\\$"
+       (1 font-lock-keyword-face))
+     ;; variable name
+     (,(concat "\\($" xquery-mode-qname "\\)")
+       (1 font-lock-variable-name-face))
+     ;; function decl
+     (,(concat
+         "\\(declare\\s-+function\\"
+         "|declare\\s-+private\\s-+function\\"
+         "|define\\s-+function\\)\\s-+\\("
+         xquery-mode-qname "\\)(")
+       (1 font-lock-keyword-face)
+       (2 font-lock-function-name-face))
+     ;; schema test or type decl
+     (,(concat
+         "\\("
+         "case"
+         "\\|instance\\s-+of\\|castable\\s-+as\\|treat\\s-+as\\|cast\\s-+as"
+         ;; "as" must be last in the list
+         "\\|as"
+         "\\)"
+         "\\s-+\\(" xquery-mode-qname "\\)"
+         ;; type may be followed by element() or element(x:foo)
+         "(?\\s-*\\(" xquery-mode-qname "\\)?\\s-*)?")
+       (1 font-lock-keyword-face)
+       (2 font-lock-type-face)
+                                        ; TODO the second qname never matches
+       (3 font-lock-type-face))
+     ;; function call
+     (,(concat "\\(" xquery-mode-qname "\\)(")
+       (1 font-lock-function-name-face))
+     ;; named node constructor
+     (,(concat "\\(attribute\\|element\\)\\s-+\\(" xquery-mode-qname "\\)\\s-*{")
+       (1 font-lock-keyword-face)
+       (2 font-lock-constant-face))
+     ;; anonymous node constructor
+     ("\\(binary\\|comment\\|document\\|text\\)\\s-*{"
+       (1 font-lock-keyword-face))
+     ;; typeswitch default
+     ("\\(default\\s-+return\\)\\s-+"
+       (1 font-lock-keyword-face)
+       (2 font-lock-keyword-face))
+     ;;
+     ;; highlighting - use nxml config to font-lock directly-constructed XML
+     ;;
+     ;; xml start element start
+     (,(concat "<" xquery-mode-qname)
+       (1 'nxml-element-prefix-face)
+       (2 'nxml-element-colon-face)
+       (3 'nxml-element-prefix-face))
+     ;; xml start element end
+     ("\\(/?\\)>"
+       (1 'nxml-tag-slash-face))
+     ;; xml end element
+     (,(concat "<\\(/\\)" xquery-mode-qname ">")
+       (1 'nxml-tag-slash-face)
+       (2 'nxml-element-prefix-face)
+       (3 'nxml-element-colon-face)
+       (4 'nxml-element-local-name-face))
+     ;; TODO xml attribute or xmlns decl
+     ;;    (,(concat xquery-mode-qname "=\\([\"']\\)\\(.*?\\)\\([\"']\\)")
+     ;;     (1 'nxml-attribute-prefix-face)
+     ;;     (2 'nxml-attribute-colon-face)
+     ;;     (3 'nxml-attribute-local-name-face)
+     ;;     (4 'nxml-attribute-value-delimiter-face)
+     ;;     (5 'nxml-attribute-value-face)
+     ;;     (6 'nxml-attribute-value-delimiter-face))
+     ;; xml comments
+     ("\\(<!--\\)\\([^-]*\\)\\(-->\\)"
+       (1 'nxml-comment-delimiter-face)
+       (2 'nxml-comment-content-face)
+       (3 'nxml-comment-delimiter-face))
+     ;; highlighting XPath expressions, including *:foo
+     ;; TODO this doesn't match expressions unless they start with slash
+     ;; TODO but matching without a leading slash overrides all the keywords
+     (,(concat "\\(//?\\)\\(*\\|\\sw*\\)\\(:?\\)" xquery-mode-ncname)
+       (1 font-lock-constant-face)
+       (2 font-lock-constant-face)
+       (3 font-lock-constant-face)
+       (4 font-lock-constant-face))
+     ;;
+     ;; highlighting pseudo-keywords - must be late, for problems like 'if ()'
+     ;;
+     (,xquery-mode-keywords-regex (1 font-lock-keyword-face))
+     ))
+
+;; file-extension mappings
+;;;###autoload
+(add-to-list 'auto-mode-alist '(".xq[erxy]\\'" . xquery-mode))
+
+(defun xquery-forward-sexp (&optional arg)
+  "XQuery forward s-expresssion.
+This function is not very smart. It tries to use
+`nxml-forward-balanced-item' if it sees '>' or '<' characters in
+the current line (ARG), and uses the regular `forward-sexp'
+otherwise."
+  (if (> arg 0)
+    (progn
+      (if (looking-at "\\s-*<")
+        (nxml-forward-balanced-item arg)
+        (let ((forward-sexp-function nil)) (forward-sexp arg))))
+    (if (looking-back ">\\s-*")
+      (nxml-forward-balanced-item arg)
+      (let ((forward-sexp-function nil)) (forward-sexp arg)))))
+
+;; indentation
+(defvar xquery-indent-size tab-width "The size of each indent level.")
+
+;; (setq debug-on-error t) ;\ DEBUG ::)
+
+(defvar xquery-indent-debug nil)
+
+;; (setq xquery-indent-debug t) ;\ DEBUG ::)
+
+(defun xquery-toggle-debug-indent ()
+  "Toggle the debug flag used in `xquery-calculate-indentation'."
+  (interactive)
+  (setq xquery-indent-debug (not xquery-indent-debug))
+  (message "xquery-indent-debug is %sabled"
+    (if xquery-indent-debug "en" "dis")))
+
+(defun xquery-indent-debug-toggle ()
+  "Toggle the debug flag used in `xquery-calculate-indentation'."
+  (interactive) (xquery-toggle-debug-indent))
+
+(defun xquery-indent-debug-message (results)
+  "Utility function to display debug messages for indentation.
+RESULTS must be a list of a column number and a string message."
+  (if xquery-indent-debug
+    (let ((cc (car results))
+           (msg (cdr results)))
+      (message "xquery-indent-debug: (%d) %S" cc msg)) ) )
 
 (defun xquery-set-indent-function ()
   "Set the indent function for xquery mode."
@@ -73,191 +365,167 @@
   (set (make-local-variable 'indent-line-function) 'xquery-indent-line)
   (make-local-variable 'forward-sexp-function)
   (setq forward-sexp-function 'xquery-forward-sexp)
-  ;;(local-set-key "/" 'nxml-electric-slash)
-  )
-
-(defun xquery-forward-sexp (&optional arg)
-  "Xquery forward s-expresssion.
-This function is not very smart, it tries to use
-`nxml-forward-balanced-item' if it sees '>' or '<' characters in
-the direction you are going, and uses the regular `forward-sexp'
-otherwise. "
-  (if (> arg 0)
-      (progn                                 
-        (if (looking-at "[ \t]*<")
-            (nxml-forward-balanced-item arg)
-          (let ((forward-sexp-function nil)) (forward-sexp arg))))
-    (if (looking-back ">[ \t]*")
-        (nxml-forward-balanced-item arg)
-                (let ((forward-sexp-function nil)) (forward-sexp arg))))
-  )
-
-
-(defun xquery-set-up-syntax-table ()
-  "Allow the hypen character to be recognized as part of a xquery symbol."
-  (modify-syntax-entry ?- "w" (syntax-table))
-  (modify-syntax-entry ?/ "." (syntax-table))
-  ;; set-up the syntax table correctly for parentheis type characters
-  (modify-syntax-entry ?\{ "(}" (syntax-table))
-  (modify-syntax-entry ?\} "){" (syntax-table))
-  (modify-syntax-entry ?\[ "(]" (syntax-table))
-  (modify-syntax-entry ?\] ")]" (syntax-table))
-  (modify-syntax-entry ?\( "()1" (syntax-table)) 
-  (modify-syntax-entry ?\) ")(4" (syntax-table))
-  ;;(modify-syntax-entry ?\< "(>" (syntax-table))
-  ;;(modify-syntax-entry ?\> ")<" (syntax-table))
-  ;; xquery comments are like (: :) -- handled above at mode decl
-  ;;(modify-syntax-entry ?\: ".23" (syntax-table))
-  )
-
-
+  (local-set-key "/" 'nxml-electric-slash))
 
 (defun xquery-indent-line ()
   "Indent current line as xquery code."
   (interactive)
-   (let ((savep (> (current-column) (current-indentation)))
-	 (indent (condition-case err (max (xquery-calculate-indentation) 0)
-		   (error (message "%S" err)))))
-     (if savep
-	 (save-excursion (indent-line-to indent))
-       (indent-line-to indent))))
-
-(defvar xquery-start-block-regexp "[ \t]*\\((\|{\\|for\\|let\\|where\\|return\\|if\\|else\\|typeswitch\\|declare[ \t]+function\\|.*[({]$\\)"
-  "A regular expression which indicates that a xquery block is starting.")
-
-(defvar xquery-flwr-block-regexp "[ \t]*\\(for\\|let\\|where\\|return\\|order\\|stable\\s *order\\)")
+  (let ((savept (> (current-column) (current-indentation)))
+         (results (xquery-calculate-indentation)))
+    (xquery-indent-debug-message results)
+    (let ( (indent (car results)) )
+      (if (> indent -1)
+        (if savept
+          (save-excursion (indent-line-to indent))
+          (indent-line-to (max 0 indent)) ) ) ) ) )
 
-(defvar xquery-indent-size 2
-  "The size of each indent level.")
-
-(defvar xquery-indent-debug nil)
+(defun xquery-indent-via-nxml ()
+  "This function uses nxml to calculate the indentation."
+  (let ((nxml-prolog-end (point-min))
+         (nxml-scan-end (copy-marker (point-min) nil)) )
+    (nxml-compute-indent) ) )
 
-(defun xquery-toggle-debug-indent ()
-  "Toggle the debug flag used in `xquery-calculate-indentation'. "
-  (interactive)
-  (setq xquery-indent-debug (not xquery-indent-debug))
-  (message (concat "xquery-indent-debug is " (if xquery-indent-debug "en" "dis") "abled"))
-  )
+;; to make debugging easier, use setq to set the actual values
+(defvar xquery-indent-regex ""
+  "A regular expression indicating an indentable xquery sub-expression.")
+
+(setq xquery-indent-regex
+  (concat "^\\s-*\\("
+    "typeswitch\\|for\\|let\\|where\\|order\\s-+by\\|return"
+    "\\|if\\|then\\|else"
+    "\\)\\s-*$") )
 
 (defun xquery-calculate-indentation ()
-   "Return the column to which the current line should be indented."
-  (beginning-of-line)
-  (if (bobp)
-      0 ; First line is always non-indented
-    (skip-chars-forward " \t")
+  "Calculate the indentation for a line of XQuery.
+This function returns the column to which the current line should be indented,
+and a debug expression."
+  (save-excursion
+    (beginning-of-line)
     (cond
-     ;; do nothing if this is a comment
-     ((eq (get-text-property (point) 'face) 'font-lock-comment-face) (current-indentation))
 
-     ((looking-at "\\(</?\\w\\|{\\)")  ;; xml constructor or enclosed expressions
-      (if xquery-indent-debug
-          (message "xquery-indent-debug: xml constructor"))
-      (let ((nxml-prolog-end (point-min))
-            (nxml-scan-end (copy-marker (point-min) nil)))
-        (nxml-compute-indent)
-        ))
+      ;; TODO this sort of works, but needs to set some state
+      ;; TODO once we have state, how and when do we reset it?
+      ;;      ((save-excursion
+      ;;         (previous-line)
+      ;;         (message "current-word = %S" (current-word)) ; DEBUG
+      ;;         (message "looking-at xquery-indent-regex = %S"
+      ;;                  (looking-at xquery-indent-regex)) ; DEBUG
+      ;;          (looking-at xquery-indent-regex))
+      ;;       (save-excursion
+      ;;         (previous-line)
+      ;;         (list
+      ;;          (+ xquery-indent-size (current-indentation))
+      ;;          "previous line starts new block")))
 
-     ;; for close braces or else statements indent to the same level as the opening { 
-     ((looking-at "}")
-      (if xquery-indent-debug
-          (message "xquery-indent-debug: }"))
-      (save-excursion
-        (backward-up-list)
-        (let ((cc (current-column)))
-          (beginning-of-line)
-          (if (looking-at xquery-start-block-regexp)
-              (current-indentation)
-            cc))))
-
-     ((looking-at "else")
-      (if xquery-indent-debug
-          (message "xquery-indent-debug: else"))
-      (save-excursion
-        (xquery-previous-non-empty-line)
-        (- (current-indentation) xquery-indent-size)
-        ))
-
-     ;; for close parens, indent to the start of the func call
-     ((looking-at ")")
-      (if xquery-indent-debug
-          (message "xquery-indent-debug: )"))
-      (save-excursion
-        (backward-up-list) 
-        (if (looking-back "\\w+\\s *")
-            (backward-word))
-        (current-column)
-     ))
+      ;; default, using sexp parser
+      (t
+        ;; calculate indent for beginning of line indent, then end of line
+        (let* ((point-bol (point))
+                (results-bol (parse-partial-sexp (point-min) point-bol))
+                ;; 0. depth in parens.
+                (paren-level-bol (car results-bol))
+                ;; 1. character address of start of innermost containing list.
+                (list-start-bol  (car (cdr results-bol)))
+                ;; 2. character address of start of last complete sexp.
+                (sexp-start-bol  (car (cdr (cdr results-bol))) )
+                ;; 3. non-nil if inside a string.
+                (stringp-bol     (car (cdr (cdr (cdr results-bol)))) )
+                ;; 4. nil if outside comment, t if inside non-nesting comment,
+                ;;    else integer comment nesting.
+                (comment-level-bol
+                  (car (cdr (cdr (cdr (cdr results-bol))))) )
+                ;; 5. t if following a quote character.
+                (quotep-bol
+                  (car (cdr (cdr (cdr (cdr (cdr results-bol)))))) )
+                ;; 6. the minimum paren-depth encountered during this scan.
+                (min-level-bol
+                  (car (cdr (cdr (cdr (cdr (cdr (cdr results-bol))))))) )
+                ;; 7. t if in a comment of style b;
+                ;;    symbol 'syntax-table' if the comment is generic.
+                (bcommentp-bol
+                  (car (cdr (cdr (cdr (cdr (cdr (cdr (cdr results-bol)))))))) )
+                ;; 8. character address of start of comment or string, else nil.
+                (comment-start-bol
+                  (car (cdr (cdr
+                              (cdr (cdr (cdr (cdr (cdr
+                                                    (cdr results-bol))))))))))
+                ;; 9. intermediate data for continuation of parsing. (not used)
 
-     ;; order flwr expressions on the same column
-     ((save-excursion
-        (when
-            (and
-             (looking-at xquery-flwr-block-regexp)
-             (progn 
-               (xquery-previous-non-empty-line)
-               (beginning-of-line)
-               (looking-at xquery-flwr-block-regexp)))
-      (if xquery-indent-debug
-          (message "xquery-indent-debug: nested flwr"))
-          (current-indentation)
-            )
-        ))
-
-     ;; if this is the first non-empty line after a block, indent xquery-indent-size chars relative to the block
-     ((save-excursion
-        (xquery-previous-non-empty-line)
-        (beginning-of-line)
-        (when (looking-at xquery-start-block-regexp)
+                (point-eol (save-excursion (end-of-line) (point)))
+                ;; undocumented, but parse-partial-sexp seems to change point
+                ;; TODO use state-bol? seems to have problems
+                (results-eol (save-excursion
+                               (parse-partial-sexp (point-min) point-eol)))
+                ;; what would nxml do?
+                (results-nxml
+                  (cond
+                    ((looking-at "\\s-*<!--")
+                      (list (xquery-indent-via-nxml) "xml start-comment"))
+                    ((looking-at "\\s-*-->")
+                      (list (xquery-indent-via-nxml) "xml end-comment"))
+                    ((looking-at "\\s-*<\\sw+")
+                      (list (xquery-indent-via-nxml) "xml start-element"))
+                    ((looking-at "\\s-*</?\\sw+")
+                      (list (xquery-indent-via-nxml) "xml end-element"))
+                    (t nil) ) )
+                ;; later we will multiple by xquery-indent-size
+                (nxml-indent
+                  (if results-nxml
+                    (/ (car results-nxml) xquery-indent-size)))
+                )
           (if xquery-indent-debug
-              (message "xquery-indent-debug: first line in block"))
-          (+ xquery-indent-size (current-indentation))))
-      )
-
-     ;; for everything else indent relative to the outer list
-     (t       
-      (if xquery-indent-debug
-          (message "xquery-indent-debug: everyting else"))
-      (save-excursion (xquery-previous-non-empty-line) (current-indentation)))
-     )))
-
-(when (featurep 'xemacs)
-   (unless (functionp 'looking-back)
-     ;; from GNU Emacs subr.el
-     (defun looking-back (regexp &optional limit greedy)
-       "Return non-nil if text before point matches regular expression
-REGEXP.
-     Like `looking-at' except matches before point, and is slower.
-     LIMIT if non-nil speeds up the search by specifying a minimum
-     starting position, to avoid checking matches that would start
-     before LIMIT.
-     If GREEDY is non-nil, extend the match backwards as far as possible,
-     stopping when a single additional previous character cannot be part
-     of a match for REGEXP."
-       (let ((start (point))
-             (pos
-              (save-excursion
-                (and (re-search-backward (concat "\\(?:" regexp
-"\\)\\=") limit t)
-                     (point)))))
-         (if (and greedy pos)
-             (save-restriction
-               (narrow-to-region (point-min) start)
-               (while (and (> pos (point-min))
-                           (save-excursion
-                             (goto-char pos)
-                             (backward-char 1)
-                             (looking-at (concat "\\(?:"  regexp
-"\\)\\'"))))
-                 (setq pos (1- pos)))
-               (save-excursion
-                 (goto-char pos)
-                 (looking-at (concat "\\(?:"  regexp "\\)\\'")))))
-         (not (null pos))))))
-
-(defun xquery-previous-non-empty-line ()
-  "Move to the last non-empty line."
-  (re-search-backward "\\S " (point-min) t)
-  )
+            (progn
+              (message "point-bol = %S" point-bol)
+              (message "point-eol = %S" point-eol)
+              (message "point = %S" (point))
+              (message "results-eol = %S" results-eol)
+              (message "results-nxml = %S" results-nxml)))
+          (let* (
+                  ;; 0. depth in parens
+                  (paren-level-eol (car results-eol))
+                  (indent
+                    (cond
+                      (comment-level-bol
+                                        ; within a multi-line comment
+                                        ; start of comment indentation + 1
+                        (+ 1 (save-excursion
+                               (goto-char comment-start-bol)
+                               (current-indentation) )) )
+                                        ; TODO multi-line prolog variable?
+                      (nil -1)
+                                        ; mult-line module import?
+                      ((and (save-excursion
+                              (beginning-of-line)
+                              (looking-at "^\\s-*at\\s-+"))
+                         (save-excursion
+                           (beginning-of-line)
+                           (previous-line)
+                           (looking-at "^\\s-*import\\s-+module\\s-+")))
+                        xquery-indent-size)
+                                        ; multi-line function decl?
+                                        ; TODO handle more than 1 line previous
+                      ((and (save-excursion
+                              (beginning-of-line)
+                              (looking-at "^\\s-*as\\s-+"))
+                         (save-excursion
+                           (beginning-of-line)
+                           (previous-line)
+                           (looking-at
+                             "^\\s-*\\(define\\|declare\\)\\s-+function\\s-+")))
+                        xquery-indent-size)
+                                        ; default - use paren-level-bol
+                      (t (* xquery-indent-size
+                                        ; special when simply closing 1 level
+                           (cond
+                             ((and (= paren-level-bol (+ 1 paren-level-eol))
+                                (looking-at "^\\s-*\\s)[,;]?\\s-*$") )
+                               paren-level-eol)
+                                        ; factor in the nxml-indent
+                             ((and
+                                nxml-indent (> nxml-indent paren-level-bol))
+                               nxml-indent)
+                             (t paren-level-bol)))))))
+            (list (min 70 indent) results-bol results-eol)))))))
 
 (provide 'xquery-mode)