branch: externals/sql-indent
commit 1b48bcfa022e8a102a17b416aa858025e9624559
Author: Alex Harsanyi <[email protected]>
Commit: Alex Harsanyi <[email protected]>
Correctly detect syntax for cursor statements (fix #19)
Cursors are correctly detected as beginning of a statement. Also an
optional
type for the cursor is recognized correctly.
Also fixed block navigation for package names (so package end statements are
correctly recognized)
Added tests for the above cases.
---
README.md | 11 ++--
sql-indent-test.el | 4 ++
sql-indent.el | 40 ++++++++++----
test-data/pr19-syn.eld | 138 +++++++++++++++++++++++++++++++++++++++++++++++++
test-data/pr19.sql | 64 +++++++++++++++++++++++
5 files changed, 241 insertions(+), 16 deletions(-)
diff --git a/README.md b/README.md
index 6a3af6b..88c8b1a 100644
--- a/README.md
+++ b/README.md
@@ -288,11 +288,12 @@ defined for SQL code:
* `(in-begin-block KIND LABEL)` -- line is inside a block started by a begin
statement. KIND (a symbol) is "toplevel-block" for a begin at toplevel,
- "defun" for a begin that starts the body of a procedure or function, nil for
- a begin that is none of the previous. For a "defun", LABEL is the name of
- the procedure or function, for the other block types LABEL contains the
- block label, or the empty string if the block has no label. ANCHOR is the
- start of the block.
+ "defun" for a begin that starts the body of a procedure or function,
+ \"package\" for a begin that starts the body of a package, nil for a begin
+ that is none of the previous. For a "defun" or "package", LABEL is the name
+ of the procedure, function or package, for the other block types LABEL
+ contains the block label, or the empty string if the block has no label.
+ ANCHOR is the start of the block.
* `(block-start KIND)` -- line begins with a statement that starts a block.
KIND (a symbol) can be one of "then", "else" or "loop". ANCHOR is the
diff --git a/sql-indent-test.el b/sql-indent-test.el
index bec6abe..8f0028a 100644
--- a/sql-indent-test.el
+++ b/sql-indent-test.el
@@ -254,3 +254,7 @@ information read from DATA-FILE (as generated by
(ert-deftest sqlind-ert-pr18 ()
(sqlind-ert-check-file-syntax "test-data/pr18.sql" "test-data/pr18-syn.eld"))
+
+(ert-deftest sqlind-ert-pr19 ()
+ (sqlind-ert-check-file-syntax "test-data/pr19.sql" "test-data/pr19-syn.eld"))
+
diff --git a/sql-indent.el b/sql-indent.el
index 75e3c9c..4db48fc 100644
--- a/sql-indent.el
+++ b/sql-indent.el
@@ -270,7 +270,7 @@ But don't go before LIMIT."
(catch 'done
(while (not (eq (point) (or limit (point-min))))
(when (re-search-backward
- ";\\|\\b\\(begin\\|loop\\|if\\|then\\|else\\|elsif\\)\\b\\|)"
+
";\\|\\b\\(declare\\|begin\\|cursor\\|loop\\|if\\|then\\|else\\|elsif\\)\\b\\|)"
limit 'noerror)
(unless (sqlind-in-comment-or-string (point))
(let ((candidate-pos (match-end 0)))
@@ -279,6 +279,9 @@ But don't go before LIMIT."
;; of the keywords inside one of them and think this is a
;; statement start.
(progn (forward-char 1) (forward-sexp -1)))
+ ((looking-at "\\bcursor\\b")
+ ;; statement begins at the start of the keyword
+ (throw 'done (point)))
((looking-at "\\b\\(then\\|else\\)\\b")
;; then and else start statements when they are inside
;; blocks, not expressions.
@@ -525,6 +528,9 @@ See also `sqlind-beginning-of-block'"
((and (listp previous-block-kind)
(eq (nth 0 previous-block-kind) 'defun-start))
(list 'in-begin-block 'defun (nth 1 previous-block-kind)))
+ ((and (listp previous-block-kind)
+ (memq (nth 0 previous-block-kind) '(package
package-body)))
+ (list 'in-begin-block 'package (nth 1 previous-block-kind)))
(t
(list 'in-begin-block nil begin-label)))))
@@ -549,7 +555,8 @@ See also `sqlind-beginning-of-block'"
(cond ((memq previous-block-kind '(toplevel declare-statement))
(goto-char (cdr previous-block)))
((and (listp previous-block-kind)
- (eq (nth 0 previous-block-kind) 'defun-start))
+ (memq (nth 0 previous-block-kind)
+ '(defun-start package package-body)))
(unless (sqlind-labels-match
label (nth 1 previous-block-kind))
(throw 'finished
@@ -1214,10 +1221,20 @@ procedure block."
;; else, maybe we have a DML statement (select, insert,
;; update and delete)
- ;; skip a cursor definition if it is before our point
- (when (looking-at "cursor[ \t\r\n\f]+[a-z0-9_]+[
\t\r\n\f]+is[ \t\r\n\f]+")
- (when (<= (match-end 0) pos)
- (goto-char (match-end 0))))
+ ;; skip a cursor definition if it is before our point, in the
+ ;; following format:
+ ;;
+ ;; CURSOR name IS
+ ;; CURSOR name type IS
+ (when (looking-at "cursor\\b")
+ (let ((origin (point)))
+ (forward-sexp 3)
+ (sqlind-forward-syntactic-ws)
+ (when (looking-at "is\\b")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ (unless (<= (point) pos)
+ (goto-char origin))))
;; skip a forall statement if it is before our point
(when (looking-at "forall\\b")
@@ -1363,11 +1380,12 @@ The following syntax symbols are defined for SQL code:
- (in-begin-block KIND LABEL) -- line is inside a block started
by a begin statement. KIND (a symbol) is \"toplevel-block\"
for a begin at toplevel, \"defun\" for a begin that starts the
- body of a procedure or function, nil for a begin that is none
- of the previous. For a \"defun\", LABEL is the name of the
- procedure or function, for the other block types LABEL contains
- the block label, or the empty string if the block has no label.
- ANCHOR is the start of the block.
+ body of a procedure or function, \"package\" for a begin that
+ starts the body of a package, or nil for a begin that is none
+ of the previous. For a \"defun\" or \"package\", LABEL is the
+ name of the procedure, function or package, for the other block
+ types LABEL contains the block label, or the empty string if
+ the block has no label. ANCHOR is the start of the block.
- (block-start KIND) -- line begins with a statement that starts
a block. KIND (a symbol) can be one of \"then\", \"else\" or
diff --git a/test-data/pr19-syn.eld b/test-data/pr19-syn.eld
new file mode 100644
index 0000000..3398c1f
--- /dev/null
+++ b/test-data/pr19-syn.eld
@@ -0,0 +1,138 @@
+(((toplevel . 1))
+ ((declare-statement . 1))
+ ((statement-continuation . 11))
+ ((select-clause . 30)
+ (statement-continuation . 11))
+ ((select-clause . 30)
+ (statement-continuation . 11))
+ (((in-select-clause "where")
+ . 68)
+ (statement-continuation . 11))
+ ((declare-statement . 1))
+ ((declare-statement . 1))
+ ((declare-statement . 1))
+ ((declare-statement . 1))
+ (((defun-start "get_my_value")
+ . 149))
+ (((defun-start "get_my_value")
+ . 149))
+ (((defun-start "get_my_value")
+ . 149))
+ ((statement-continuation . 241))
+ ((select-clause . 297)
+ (statement-continuation . 241))
+ ((select-clause . 297)
+ (statement-continuation . 241))
+ (((defun-start "get_my_value")
+ . 149))
+ (((defun-start "get_my_value")
+ . 149))
+ (((block-start begin)
+ . 149)
+ ((defun-start "get_my_value")
+ . 149))
+ (((in-begin-block defun "get_my_value")
+ . 403))
+ (((in-block loop "")
+ . 413))
+ (((block-end loop "")
+ . 413)
+ ((in-block loop "")
+ . 413))
+ (((block-end defun "get_my_value")
+ . 403)
+ ((in-begin-block defun "get_my_value")
+ . 403))
+ ((declare-statement . 1))
+ (((block-start begin)
+ . 1)
+ (declare-statement . 1))
+ (((in-begin-block toplevel-block "")
+ . 509))
+ (((in-block loop "")
+ . 517))
+ (((block-end loop "")
+ . 517)
+ ((in-block loop "")
+ . 517))
+ (((block-end toplevel-block "")
+ . 509)
+ ((in-begin-block toplevel-block "")
+ . 509))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((package-body "my_pacakge")
+ . 595))
+ (((package-body "my_pacakge")
+ . 595))
+ ((statement-continuation . 661))
+ ((select-clause . 678)
+ (statement-continuation . 661))
+ ((select-clause . 678)
+ (statement-continuation . 661))
+ (((in-select-clause "where")
+ . 714)
+ (statement-continuation . 661))
+ (((package-body "my_pacakge")
+ . 595))
+ (((package-body "my_pacakge")
+ . 595))
+ (((package-body "my_pacakge")
+ . 595))
+ (((package-body "my_pacakge")
+ . 595))
+ (((defun-start "get_my_value")
+ . 785))
+ (((defun-start "get_my_value")
+ . 785))
+ (((defun-start "get_my_value")
+ . 785))
+ ((statement-continuation . 873))
+ ((select-clause . 927)
+ (statement-continuation . 873))
+ ((select-clause . 927)
+ (statement-continuation . 873))
+ (((defun-start "get_my_value")
+ . 785))
+ (((defun-start "get_my_value")
+ . 785))
+ (((block-start begin)
+ . 785)
+ ((defun-start "get_my_value")
+ . 785))
+ (((in-begin-block defun "get_my_value")
+ . 1024))
+ (((in-block loop "")
+ . 1032))
+ (((block-end loop "")
+ . 1032)
+ ((in-block loop "")
+ . 1032))
+ (((block-end defun "get_my_value")
+ . 1024)
+ ((in-begin-block defun "get_my_value")
+ . 1024))
+ (((package-body "my_pacakge")
+ . 595))
+ (((package-body "my_pacakge")
+ . 595))
+ (((in-begin-block package "my_pacakge")
+ . 1120))
+ (((in-block loop "")
+ . 1128))
+ (((block-end loop "")
+ . 1128)
+ ((in-block loop "")
+ . 1128))
+ (((in-begin-block package "my_pacakge")
+ . 1120))
+ (((in-begin-block package "my_pacakge")
+ . 1120))
+ (((block-end package "my_pacakge")
+ . 1120)
+ ((in-begin-block package "my_pacakge")
+ . 1120))
+ ((toplevel . 1))
+ ((toplevel . 1)))
+
diff --git a/test-data/pr19.sql b/test-data/pr19.sql
new file mode 100644
index 0000000..3f21314
--- /dev/null
+++ b/test-data/pr19.sql
@@ -0,0 +1,64 @@
+declare
+ cursor cur1 is
+ select dummy
+ from my_table
+ where 1 = 1
+ and col1 = p_col1;
+
+ p_col1 my_table.col1%type := 42;
+
+ function get_my_value(p_param1 in my_table.col1%type)
+ return my_table.col2%type is
+
+ cursor cur2 (p_val_col1 in my_table.col1%type) is
+ select col2
+ from my_table
+ where col1 = p_val_col1;
+
+ v_result my_table.col2%type;
+ begin
+ for rec in cur2(p_param1) loop
+ v_result := rec.col2;
+ end loop;
+ end get_my_value;
+
+begin
+ for rec in cur1 loop
+ dbms_output.put_line(rec.dummy);
+ end loop;
+end;
+/
+
+create or replace package body my_pacakge authid current user is
+
+cursor cur1 is
+ select 1 dummy
+ from my_table
+ where 1 = 1
+ and col1 = p_col1;
+
+p_col1 my_table.col1%type := 42;
+
+function get_my_value(p_param1 in my_table.col1%type)
+ return my_table.col2%type is
+
+ cursor cur2 (p_val_col1 in my_table.col1%type) is
+ select col2
+ from my_table
+ where col1 = p_val_col1;
+
+ v_result my_table.col2%type;
+begin
+ for rec in cur2(p_param1) loop
+ v_result := rec.col2;
+ end loop;
+end get_my_value;
+
+begin
+ for rec in cur1 loop
+ dbms_output.put_line(rec.dummy);
+ end loop;
+
+ dbms_output.put_line(get_my_value(p_col1));
+end my_pacakge;
+/