branch: externals/sql-indent
commit 3930611c9c0122ffcadd104833cc464a9a8dc313
Author: Alex Harsanyi <[email protected]>
Commit: Alex Harsanyi <[email protected]>
Identify syntax for CASE expressions in assignment (fixes #18)
Also added unit tests that shows/tests the types of CASE expressions that
can
show up in PL/SQL.
---
sql-indent-test.el | 66 +++++++++++++++++++++++++++---------------------
sql-indent.el | 54 +++++++++++++++++++++++++++------------
test-data/pr18-syn.eld | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++
test-data/pr18.sql | 28 +++++++++++++++++++++
4 files changed, 172 insertions(+), 44 deletions(-)
diff --git a/sql-indent-test.el b/sql-indent-test.el
index 1e9d5ad..bec6abe 100644
--- a/sql-indent-test.el
+++ b/sql-indent-test.el
@@ -22,45 +22,51 @@
;;; Commentary:
-;; To run the tests, eval this file and run
+;; This file defines tests for the sql-indent.el package. To run the tests,
+;; eval this file, than type:
;;
-;; M-x ert RET "^sqlind-" RET
+;; M-x ert RET "^sqlind-" RET
;;
;; There are two types of tests,
;;
-;; * syntax checks (see `sqlind-ert-check-file-syntax') check if the syntax in
-;; an SQL file is correctly indentified. These tests are independent of
-;; indentation preferences
+;; * SYNTAX CHECKS check if the syntax in an SQL file is correctly
+;; indentified. These tests are independent of indentation preferences (see
+;; `sqlind-ert-check-file-syntax')
;;
-;; * indentation checks (see `sqlind-ert-check-file-indentation') checks if a
-;; file is indented corectly for a set of rules.
+;; * INDENTATION CHECKS checks if a file is indented corectly for a set of
+;; rules. (see `sqlind-ert-check-file-indentation')
;;
-;; The tests work by having a sample SQL file and syntax and indentation data
-;; saved in an .eld file. These need to be prepared separately, but some
-;; helper functions are available, see the test data preparation section below
-
-(require 'ert)
-(require 'sql-indent)
-(require 'sql-indent-left)
-
-
-
-;;................................................ test data preparation ....
-
-;; The functions below help prepare the required .eld files for the tests. To
-;; create a syntax check file, open an *ielm* buffer (M-x ielm RET) and run:
+;; Both types of tests work by having a sample SQL file with syntax and
+;; indentation data saved in an .eld files. These data files need to be
+;; prepared separately using `sqlind-collect-syntax-from-buffer' and
+;; `sqlind-collect-indentation-offsets-from-buffer'.
;;
-;; (sqlind-collect-syntax-from-buffer (find-file-noselect "pr7.sql")))
+;; To create a syntax check file, open an *ielm* buffer (M-x ielm RET) and
+;; run:
;;
-;; Than put the result in an .eld file
+;; (sqlind-collect-syntax-from-buffer (find-file "./test-data/pr7.sql"))
+;;
+;; The function will output a set of syntax definitions. Put these into an
+;; .eld file.
;;
;; To create an indentation offsets file, run:
;;
-;; (sqlind-collect-indentation-offsets-from-buffer (find-file-noselect
"pr7.sql") sqlind-indentation-left-offsets-alist 2))
+;; (sqlind-collect-indentation-offsets-from-buffer
+;; (find-file "./test-data/pr7.sql")
+;; sqlind-indentation-left-offsets-alist
+;; 2)
;;
-;; and put the result in an .eld file
+;; The function will output a list of indentation offsets. Put these into an
+;; .eld file.
;;
-;; see the end of file on how to put together the actual tests.
+;; See the end of file for examples on how to put together the actual tests.
+
+(require 'ert)
+(require 'sql-indent)
+(require 'sql-indent-left)
+
+
+;;................................................ test data preparation ....
(defun sqlind-collect-syntax-from-buffer (buffer)
(let ((result '()))
@@ -105,7 +111,8 @@ inddent the whole buffer."
(setq sqlind-indentation-offsets-alist rules))
(when basic-offset
(setq sqlind-basic-offset basic-offset))
- (indent-region (point-min) (point-max)))
+ (indent-region (point-min) (point-max))
+ (set-buffer-modified-p nil))
(defun sqlind-ert-check-line-syntax (expected)
"Check that the current line has EXPECTED syntax.
@@ -126,7 +133,7 @@ function should be run a part of an ERT test."
(defun sqlind-ert-read-data (file)
"Read saved ELISP data from FILE."
(with-current-buffer (or (get-buffer file)
- (find-file-literally file))
+ (find-file-noselect file))
(goto-char (point-min))
(read (current-buffer))))
@@ -244,3 +251,6 @@ information read from DATA-FILE (as generated by
(sqlind-ert-check-file-indentation
"test-data/m.sql" "test-data/m-io.eld"
m-indentation-offsets-alist 4))
+
+(ert-deftest sqlind-ert-pr18 ()
+ (sqlind-ert-check-file-syntax "test-data/pr18.sql" "test-data/pr18-syn.eld"))
diff --git a/sql-indent.el b/sql-indent.el
index de492a4..75e3c9c 100644
--- a/sql-indent.el
+++ b/sql-indent.el
@@ -420,7 +420,10 @@ See also `sqlind-beginning-of-block'"
;; an exception statement is a block start only if we have
;; no end statements in the stack
(when (null sqlind-end-stmt-stack)
- (throw 'finished (list 'in-block 'exception "")))))))))
+ (throw 'finished (list 'in-block 'exception ""))))
+ (t ; restore position if we didn't do anything
+ (goto-char start-pos)
+ nil))))))
(defun sqlind-maybe-if-statement ()
"If (point) is on an IF statement, report its syntax."
@@ -439,6 +442,22 @@ See also `sqlind-beginning-of-block'"
(list 'syntax-error
"bad closing for if block" (point) pos))))))))))
+(defun sqlind-maybe-case-statement ()
+ "If (point) is on a case statement"
+ (when (looking-at "case")
+ (save-excursion
+ (sqlind-backward-syntactic-ws)
+ (forward-word -1)
+ (unless (looking-at "end") ; we don't want to match an "end case" here
+ (if (null sqlind-end-stmt-stack)
+ (throw 'finished (list 'in-block 'case ""))
+ (cl-destructuring-bind (pos kind _label)
+ (pop sqlind-end-stmt-stack)
+ (unless (or (not kind) (eq kind 'case))
+ (throw 'finished
+ (list 'syntax-error
+ "bad closing for case block" (point) pos)))))))))
+
(defun sqlind-maybe-else-statement ()
"If (point) is on an ELSE statement, report its syntax.
Only keywords in program code are matched, not the ones inside
@@ -644,7 +663,7 @@ See also `sqlind-beginning-of-block'"
(concat "\\(\\b"
(regexp-opt '("if" "then" "else" "elsif" "loop"
"begin" "declare" "create"
- "procedure" "function" "end") t)
+ "procedure" "function" "end" "case") t)
"\\b\\)\\|)")
"Regexp to match the start of a block.")
@@ -657,17 +676,18 @@ reverse order (a stack) and is used to skip over nested
blocks."
(catch 'finished
(let ((sqlind-end-stmt-stack end-statement-stack))
(while (re-search-backward sqlind-start-block-regexp nil 'noerror)
- (or (sqlind-in-comment-or-string (point))
- (when (looking-at ")") (forward-char 1) (forward-sexp -1) t)
- (sqlind-maybe-end-statement)
+ (or (sqlind-in-comment-or-string (point))
+ (when (looking-at ")") (forward-char 1) (forward-sexp -1) t)
+ (sqlind-maybe-end-statement)
(sqlind-maybe-if-statement)
- (sqlind-maybe-then-statement)
- (sqlind-maybe-else-statement)
- (sqlind-maybe-loop-statement)
- (sqlind-maybe-begin-statement)
- (sqlind-maybe-declare-statement)
- (sqlind-maybe-create-statement)
- (sqlind-maybe-defun-statement))))
+ (sqlind-maybe-case-statement)
+ (sqlind-maybe-then-statement)
+ (sqlind-maybe-else-statement)
+ (sqlind-maybe-loop-statement)
+ (sqlind-maybe-begin-statement)
+ (sqlind-maybe-declare-statement)
+ (sqlind-maybe-create-statement)
+ (sqlind-maybe-defun-statement))))
'toplevel))
;;;;; Determine the syntax inside a case expression
@@ -1208,10 +1228,12 @@ procedure block."
;; at the start of one.
(when (< (point) pos)
(cond
- ;; NOTE: We only catch here "CASE" clauses which start
- ;; inside a nested paranthesis
- ((looking-at "case")
- (push (sqlind-syntax-in-case pos (point)) context))
+ ;; NOTE: We only catch here "CASE" expressions, not CASE
+ ;; statements. We also catch assignments with case (var
+ ;; := CASE ...)
+ ((looking-at "\\(\\w+[ \t\r\n\f]+:=[
\t\r\n\f]+\\)?\\(case\\)")
+ (when (< (match-beginning 2) pos)
+ (push (sqlind-syntax-in-case pos (match-beginning 2))
context)))
((looking-at "with")
(push (sqlind-syntax-in-with pos (point)) context))
((looking-at "select")
diff --git a/test-data/pr18-syn.eld b/test-data/pr18-syn.eld
new file mode 100644
index 0000000..46d4b11
--- /dev/null
+++ b/test-data/pr18-syn.eld
@@ -0,0 +1,68 @@
+(((toplevel . 1))
+ ((toplevel . 1))
+ ((declare-statement . 21))
+ ((declare-statement . 21))
+ (((block-start begin)
+ . 21)
+ (declare-statement . 21))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ ((case-clause . 126)
+ (statement-continuation . 117))
+ ((case-clause . 126)
+ (statement-continuation . 117))
+ ((case-clause . 126)
+ (statement-continuation . 117))
+ (((block-end case "")
+ . 126)
+ (statement-continuation . 117))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((block-start when)
+ . 290)
+ ((in-block case "")
+ . 290))
+ (((block-start when)
+ . 301)
+ ((in-block case "")
+ . 301))
+ (((block-start when)
+ . 301)
+ ((in-block case "")
+ . 301))
+ (((block-start else)
+ . 301)
+ ((in-block case "")
+ . 301))
+ (((block-end case "")
+ . 301)
+ ((in-block else "")
+ . 392))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((block-end toplevel-block "")
+ . 78)
+ ((in-begin-block toplevel-block "")
+ . 78))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
+
+
\ No newline at end of file
diff --git a/test-data/pr18.sql b/test-data/pr18.sql
new file mode 100644
index 0000000..d043f9f
--- /dev/null
+++ b/test-data/pr18.sql
@@ -0,0 +1,28 @@
+set serveroutput on
+declare
+ dummy varchar2(25);
+ ind number := 2;
+begin
+ dbms_output.enable(null);
+
+ dummy := case
+ when ind = 1 then 'Guy'
+ when ind = 2 then 'Abc'
+ else 'World'
+ end;
+
+ dbms_output.put_line('Hello ' || dummy);
+
+ case ind
+ when 1 then dummy := 'Guy';
+ when 2 then dummy := 'Abc';
+ when 3 then dummy := 'Def';
+ else dummy := 'World';
+ end case;
+
+ dbms_output.put_line('Hello ' || dummy);
+end;
+/
+-- Local Variables:
+-- sql-product: oracle
+-- End: