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:

Reply via email to