branch: externals/sql-indent
commit ebc09bbbe9cae077428559d4bfa4716d467e0ffa
Author: Alex Harsanyi <[email protected]>
Commit: Alex Harsanyi <[email protected]>
use cl-lib instead of cl and provide some helpers
the cl library is deprecated in Emacs, to be replaced with cl-lib (which has
the same functions, but with the "cl-" prefix)
Also provided some helper functions to access syntactic context, hopefully
this will simplify the code somewhat.
---
sql-indent.el | 228 ++++++++++++++++++++++++++++++++--------------------------
1 file changed, 125 insertions(+), 103 deletions(-)
diff --git a/sql-indent.el b/sql-indent.el
index 35628af..de492a4 100644
--- a/sql-indent.el
+++ b/sql-indent.el
@@ -41,6 +41,7 @@
(require 'sql)
(require 'align)
+(require 'cl-lib)
(eval-when-compile (require 'cc-defs)) ; for c-point
;;;; General setup
@@ -75,9 +76,7 @@ This is slightly different than the syntax table used for
navigation: some punctuation characters are made symbol
constituents so that syntactic navigation works over them.")
-;;;; Syntactic analysis of SQL code
-
-;;;;; Commentary
+;;;; Utilities
;; The following routines perform rudimentary syntactical analysis of SQL
;; code. The indentation engine decides how to indent based on what this code
@@ -87,8 +86,6 @@ constituents so that syntactic navigation works over them.")
;; `sqlind-show-syntax-of-line'. This is only useful if you want to debug this
;; package or are just curious.
-;;;;; Utilities
-
(defconst sqlind-comment-start-skip "\\(--+\\|/\\*+\\)\\s *"
"Regexp to match the start of a SQL comment.")
@@ -180,6 +177,51 @@ statement."
(sqlind-forward-syntactic-ws)
(throw 'found (point)))))))
+(defun sqlind-syntax (context)
+ "Return the most specific syntax of CONTEXT.
+See `sqlind-syntax-of-line' for the definition of CONTEXT."
+ (when context
+ (caar context)))
+
+(defun sqlind-syntax-symbol (context)
+ "Return the syntax symbol for the most specific syntax of CONTEXT.
+See `sqlind-syntax-of-line' for the definition of CONTEXT."
+ (when context
+ (let ((syntax-part (caar context)))
+ (if (symbolp syntax-part)
+ syntax-part
+ (car syntax-part)))))
+
+(defun sqlind-anchor-point (context)
+ "Return the anchor point for the most specifc syntax of CONTEXT.
+See `sqlind-syntax-of-line' for the definition of CONTEXT."
+ (when context
+ (cdar context)))
+
+(defun sqlind-outer-context (context)
+ "Return the outer context from CONTEXT.
+See `sqlind-syntax-of-line' for the definition of CONTEXT."
+ (when context
+ (cdr context)))
+
+(defun sqlind-find-context (syntax-symbol context)
+ "Find SYNTAX-SYMBOL in the CONTEXT chain.
+CONTEXT chain is a list of (SYNTAX-SYMBOL . ANCHOR), as returned
+by `sqlind-syntax-of-line'. The function finds the fist element
+which matches the specified syntax symbol and returns the list
+from that point.
+
+See `sqlind-indentation-syntax-symbols' for the possible syntax
+symbols and their meaning."
+ (cond ((null context)
+ nil)
+ ((eq syntax-symbol (sqlind-syntax-symbol context))
+ context)
+ (t
+ (sqlind-find-context syntax-symbol (sqlind-outer-context context)))))
+
+;;;; Syntactic analysis of SQL code
+
;;;;; Find the beginning of the current statement
(defconst sqlind-sqlplus-directive
@@ -210,7 +252,7 @@ determine the statement start in SQLite scripts.")
"Return the position of an SQL directive, or nil.
We will never move past one of these in our scan. We also assume
they are one-line only directives."
- (let ((rx (case (and (boundp 'sql-product) sql-product)
+ (let ((rx (cl-case (and (boundp 'sql-product) sql-product)
(ms sqlind-ms-directive)
(sqlite sqlind-sqlite-directive)
(oracle sqlind-sqlplus-directive)
@@ -346,7 +388,7 @@ See also `sqlind-beginning-of-block'"
;; "if" blocks (but not "elsif" blocks) need to be
;; ended with "end if"
(when (eq if-kind 'if)
- (destructuring-bind (pos kind label)
+ (cl-destructuring-bind (pos kind label)
(pop sqlind-end-stmt-stack)
(unless (and (eq kind 'if)
(sqlind-labels-match label if-label))
@@ -367,7 +409,7 @@ See also `sqlind-beginning-of-block'"
(throw 'finished
(list 'in-block 'case case-label)))))
;; else
- (destructuring-bind (pos kind label)
+ (cl-destructuring-bind (pos kind label)
(pop sqlind-end-stmt-stack)
(unless (and (eq kind 'case)
(sqlind-labels-match label case-label))
@@ -390,7 +432,7 @@ See also `sqlind-beginning-of-block'"
(cond ((null sqlind-end-stmt-stack)
(throw 'finished (list 'in-block 'if "")))
(t
- (destructuring-bind (pos kind _label)
+ (cl-destructuring-bind (pos kind _label)
(pop sqlind-end-stmt-stack)
(unless (eq kind 'if)
(throw 'finshed
@@ -427,7 +469,7 @@ See also `sqlind-beginning-of-block'"
;; the labels match
(if (null sqlind-end-stmt-stack)
(throw 'finished (list 'in-block 'loop loop-label))
- (destructuring-bind (pos kind label)
+ (cl-destructuring-bind (pos kind label)
(pop sqlind-end-stmt-stack)
(unless (and (eq kind 'loop)
(sqlind-labels-match label loop-label))
@@ -467,7 +509,7 @@ See also `sqlind-beginning-of-block'"
(t
(list 'in-begin-block nil begin-label)))))
- (destructuring-bind (pos kind label) (pop sqlind-end-stmt-stack)
+ (cl-destructuring-bind (pos kind label) (pop sqlind-end-stmt-stack)
(cond
(kind
(throw 'finished
@@ -536,7 +578,7 @@ See also `sqlind-beginning-of-block'"
(throw 'finished
(list (if (memq what '(procedure function)) 'defun-start what)
name))
- (destructuring-bind (pos kind label) (pop sqlind-end-stmt-stack)
+ (cl-destructuring-bind (pos kind label) (pop
sqlind-end-stmt-stack)
(when (not (eq kind nil))
(throw 'finished
(list 'syntax-error
@@ -592,7 +634,7 @@ See also `sqlind-beginning-of-block'"
(when (null sqlind-end-stmt-stack)
(throw 'finished (list 'defun-start proc-name)))
- (destructuring-bind (pos kind label) (pop sqlind-end-stmt-stack)
+ (cl-destructuring-bind (pos kind label) (pop sqlind-end-stmt-stack)
(unless (and (eq kind nil)
(sqlind-labels-match label proc-name))
(throw 'finished
@@ -902,9 +944,9 @@ KIND is the symbol determining the type of the block ('if,
'loop,
end-pos end-pos)
end-pos)))
- (let* ((syntax (car (car context)))
- (anchor (cdr (car context)))
- (syntax-symbol (if (symbolp syntax) syntax (nth 0 syntax))))
+ (let ((syntax (sqlind-syntax context))
+ (anchor (sqlind-anchor-point context))
+ (syntax-symbol (sqlind-syntax-symbol context)))
(cond
((memq syntax-symbol '(package package-body))
;; we are closing a package declaration or body, `end-kind' must be
@@ -1056,9 +1098,10 @@ ANCHOR is a buffer position which is the reference for
the
SYNTAX. `sqlind-indentation-syntax-symbols' lists the syntax
symbols and their meaning.
-Only the first element of this list is used for indentation, the
-rest are 'less specific' syntaxes, mostly left in for debugging
-purposes."
+The first element in the list is the most specific syntax for the
+line, the remaining elemens are more generic ones. For example,
+a line can be inside an SELECT clause which itself is inside a
+procedure block."
(save-excursion
(with-syntax-table sqlind-syntax-table
(let* ((pos (progn (back-to-indentation) (point)))
@@ -1111,10 +1154,9 @@ purposes."
;; now let's refine the syntax by adding info about the current line
;; into the mix.
- (let* ((most-inner-syntax (car context))
- (syntax (car most-inner-syntax))
- (anchor (cdr most-inner-syntax))
- (syntax-symbol (if (symbolp syntax) syntax (nth 0 syntax))))
+ (let ((syntax (sqlind-syntax context))
+ (anchor (sqlind-anchor-point context))
+ (syntax-symbol (sqlind-syntax-symbol context)))
(goto-char pos)
@@ -1181,8 +1223,8 @@ purposes."
((looking-at "update")
(push (sqlind-syntax-in-update pos (point)) context))))
- (when (eq (car (car context)) 'select-column-continuation)
- (let ((cdef (sqlind-column-definition-start pos (cdar
context))))
+ (when (eq (sqlind-syntax-symbol context)
'select-column-continuation)
+ (let ((cdef (sqlind-column-definition-start pos
(sqlind-anchor-point context))))
(when cdef
(save-excursion
(goto-char cdef)
@@ -1254,7 +1296,7 @@ purposes."
;;;; Indentation of SQL code
-
+;;;;; Indentation calculation routines
(defvar sqlind-basic-offset 2
"The basic indentaion amount for SQL code.
Indentation is usually done in multiples of this amount, but
@@ -1532,10 +1574,7 @@ returned."
(goto-char (cdar syntax))
(current-column))))
- (let* ((this-syntax (caar syntax))
- (syntax-symbol (if (symbolp this-syntax)
- this-syntax
- (nth 0 this-syntax)))
+ (let* ((syntax-symbol (sqlind-syntax-symbol syntax))
(indent-info (cdr (assoc syntax-symbol
sqlind-indentation-offsets-alist)))
(new-indentation base-indentation))
@@ -1557,24 +1596,11 @@ returned."
(setq indent-info (cdr indent-info)))
new-indentation)))
-(defun sqlind-find-syntax (syntax-symbol syntax)
- "Find the SYNTAX-SYMBOL in the SYNTAX chain.
-SYNTAX chain is a list of (SYNTAX-SYMBOL . ANCHOR), as returned
-by `sqlind-syntax-of-line'. The function finds the fist element
-which matches the specified syntax symbol.
-
-See `sqlind-indentation-syntax-symbols' for the possible syntax
-symbols and their meaning."
- (if (null syntax)
- nil
- (let ((stx (car (car syntax))))
- (if (if (atom stx) (eq stx syntax-symbol) (eq (car stx) syntax-symbol))
- (car syntax)
- (sqlind-find-syntax syntax-symbol (cdr syntax))))))
+;;;;; Indentation helper functions
(defun sqlind-report-sytax-error (syntax _base-indentation)
"Report a syntax error for a 'syntax-error SYNTAX."
- (destructuring-bind (_sym msg start end) (caar syntax)
+ (cl-destructuring-bind (_sym msg start end) (sqlind-syntax syntax)
(message "%s (%d %d)" msg start end))
nil)
@@ -1590,25 +1616,23 @@ supported in SQL."
By default, the column of the anchor position is uses as a base
indentation. You can use this function to switch to using the
indentation of the anchor as the base indentation."
- (let ((anchor (cdar syntax)))
- (save-excursion
- (goto-char anchor)
- (current-indentation))))
+ (save-excursion
+ (goto-char (sqlind-anchor-point syntax))
+ (current-indentation)))
(defun sqlind-lineup-to-anchor (syntax _base-indentation)
"Return the column of the anchor point of SYNTAX.
This need not be the indentation of the actual line that contains
anchor."
- (let ((anchor (cdar syntax)))
- (save-excursion
- (goto-char anchor)
- (current-column))))
+ (save-excursion
+ (goto-char (sqlind-anchor-point syntax))
+ (current-column)))
(defun sqlind-use-previous-line-indentation (syntax _base-indentation)
"Return the indentation of the previous line.
If the start of the previous line is before the anchor of SYNTAX,
use the column of the anchor + 1."
- (let ((anchor (cdar syntax)))
+ (let ((anchor (sqlind-anchor-point syntax)))
(save-excursion
(forward-line -1)
(back-to-indentation)
@@ -1636,19 +1660,18 @@ comment, like this:
/* Some comment line
Some other comment line
*/"
- (let ((anchor (cdar syntax)))
- (save-excursion
- (back-to-indentation)
- (if (or (looking-at sqlind-comment-prefix)
- (looking-at sqlind-comment-end))
- (progn
- (goto-char anchor)
- (1+ (current-column)))
- ;; else
- (goto-char anchor)
- (when (looking-at sqlind-comment-start-skip)
- (goto-char (match-end 0)))
- (current-column)))))
+ (save-excursion
+ (back-to-indentation)
+ (if (or (looking-at sqlind-comment-prefix)
+ (looking-at sqlind-comment-end))
+ (progn
+ (goto-char (sqlind-anchor-point syntax))
+ (1+ (current-column)))
+ ;; else
+ (goto-char (sqlind-anchor-point syntax))
+ (when (looking-at sqlind-comment-start-skip)
+ (goto-char (match-end 0)))
+ (current-column))))
(defun sqlind-indent-comment-start (syntax base-indentation)
"Return the indentation for a comment start SYNTAX.
@@ -1665,7 +1688,8 @@ BASE-INDENTATION."
(progn
(goto-char (match-beginning 0))
(current-column))
- (sqlind-calculate-indentation (cdr syntax) base-indentation))))
+ (sqlind-calculate-indentation
+ (sqlind-outer-context syntax) base-indentation))))
(defun sqlind-indent-select-column (syntax base-indentation)
"Return the indentation for a column of a SELECT clause.
@@ -1676,16 +1700,15 @@ current indentation, which we need to update.
We try to align to the previous column start, but if we are the
first column after the SELECT clause we simply add
`sqlind-basic-offset'."
- (let ((anchor (cdar syntax)))
- (save-excursion
- (goto-char anchor)
- (when (looking-at "select\\s *\\(top\\s +[0-9]+\\|distinct\\|unique\\)?")
- (goto-char (match-end 0)))
- (skip-syntax-forward " ")
- (if (or (looking-at sqlind-comment-start-skip)
- (looking-at "$"))
- (+ base-indentation sqlind-basic-offset)
- (current-column)))))
+ (save-excursion
+ (goto-char (sqlind-anchor-point syntax))
+ (when (looking-at "select\\s *\\(top\\s +[0-9]+\\|distinct\\|unique\\)?")
+ (goto-char (match-end 0)))
+ (skip-syntax-forward " ")
+ (if (or (looking-at sqlind-comment-start-skip)
+ (looking-at "$"))
+ (+ base-indentation sqlind-basic-offset)
+ (current-column))))
(defun sqlind-indent-select-table (syntax base-indentation)
"Return the indentation for a table in the FROM section.
@@ -1695,16 +1718,15 @@ current indentation, which we need to update.
We try to align to the first table, but if we are the first
table, we simply add `sqlind-basic-offset'."
- (let ((anchor (cdar syntax)))
- (save-excursion
- (goto-char anchor)
- (when (looking-at "from")
- (goto-char (match-end 0)))
- (skip-syntax-forward " ")
- (if (or (looking-at sqlind-comment-start-skip)
- (looking-at "$"))
- (+ base-indentation sqlind-basic-offset)
- (current-column)))))
+ (save-excursion
+ (goto-char (sqlind-anchor-point syntax))
+ (when (looking-at "from")
+ (goto-char (match-end 0)))
+ (skip-syntax-forward " ")
+ (if (or (looking-at sqlind-comment-start-skip)
+ (looking-at "$"))
+ (+ base-indentation sqlind-basic-offset)
+ (current-column))))
(defun sqlind-lineup-to-clause-end (syntax base-indentation)
"Line up the current line with the end of a query clause.
@@ -1718,7 +1740,7 @@ indented by `sqlind-basic-offset', otherwise the current
line is
indented so that it starts in next column from where the clause
keyword ends.
Argument BASE-INDENTATION is updated."
- (destructuring-bind ((_sym clause) . anchor) (car syntax)
+ (cl-destructuring-bind ((_sym clause) . anchor) (car syntax)
(save-excursion
(goto-char anchor)
(forward-char (1+ (length clause)))
@@ -1742,7 +1764,7 @@ If this rule is added to the 'in-select-clause syntax
after the
with AND, OR or NOT to be aligned so they sit under the WHERE clause."
(save-excursion
(back-to-indentation)
- (destructuring-bind ((_sym clause) . anchor) (car syntax)
+ (cl-destructuring-bind ((_sym clause) . anchor) (car syntax)
(if (and (equal clause "where")
(looking-at "and\\|or\\|not"))
(- base-indentation (1+ (- (match-end 0) (match-beginning 0))))
@@ -1759,7 +1781,7 @@ If this rule is added to the 'in-select-clause syntax
after the
with AND, OR or NOT to be aligned so they sit left under the WHERE clause."
(save-excursion
(back-to-indentation)
- (destructuring-bind ((_sym clause) . anchor) (car syntax)
+ (cl-destructuring-bind ((_sym clause) . anchor) (car syntax)
(if (and (equal clause "where")
(looking-at "and\\|or\\|not"))
(progn (goto-char anchor) (current-column))
@@ -1887,11 +1909,11 @@ BASE-INDENTATION, acting as a no-op."
(save-excursion
(back-to-indentation)
(if (looking-at ")")
- (let ((stx (sqlind-find-syntax 'nested-statement-continuation syntax)))
+ (let ((stx (sqlind-find-context 'nested-statement-continuation
syntax)))
(if stx
- (let ((anchor (cdr stx)))
- (goto-char anchor)
- (current-column))
+ (progn
+ (goto-char (sqlind-anchor-point stx))
+ (current-column))
base-indentation))
base-indentation)))
@@ -1919,13 +1941,13 @@ statement. For example:
This function only makes sense in a
'nested-statement-continuation sytnax indentation rule."
(save-excursion
- (let ((anchor (cdr (car syntax))))
- (goto-char anchor)
- (forward-char 1)
- (sqlind-forward-syntactic-ws)
- (current-column))))
+ (goto-char (sqlind-anchor-point syntax))
+ (forward-char 1)
+ (sqlind-forward-syntactic-ws)
+ (current-column)))
+
+;;;;; sqlind-indent-line
-
(defun sqlind-indent-line ()
"Indent the current line according to SQL conventions.
`sqlind-basic-offset' defined the number of spaces in the basic
@@ -1944,7 +1966,7 @@ determine how to indent each type of syntactic element."
(when (> offset 0)
(forward-char offset))))))
-;;; alignment rules
+;;;; Alignment rules
(defvar sqlind-align-rules
'(;; Line up the two side of arrow =>
@@ -1999,7 +2021,7 @@ To use it, select the region to be aligned and run
\\[align].
See also `align' and `align-rules-list'")
-;;;; sqlind-setup
+;;;; sqlind-minor-mode, sqlind-setup
;;;###autoload
(define-minor-mode sqlind-minor-mode