└── plsql.el /plsql.el: -------------------------------------------------------------------------------- 1 | ;;; plsql.el --- Programming support for PL/SQL code 2 | 3 | ;; Copyright (C) 2001, 2002 by Free Software Foundation, Inc. 4 | 5 | ;; Author: Kahlil (Kal) HODGSON 6 | ;; X-URL: http://www.emacswiki.org/elisp/plsql.el 7 | ;; Keywords: languages 8 | ;; Time-stamp: "2002-11-07 01:26:20 kahlil" 9 | ;; Version: 0.8.0 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;; This file is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation; either version 2, or (at your option) 16 | ;; any later version. 17 | 18 | ;; This file is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with GNU Emacs; see the file COPYING. If not, write to 25 | ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 26 | ;; Boston, MA 02111-1307, USA. 27 | 28 | ;;;_ * Commentary 29 | 30 | ;; Provides a major mode (PL/SQL) that supports writing PL/SQL code. 31 | ;; 32 | ;; This `plsql-mode' is an extension of `sql-mode' and uses that mode 33 | ;; to provide keyword fontification, symbol-tables, and interaction 34 | ;; with the database. In addition to foxing some bugs this mode 35 | ;; provides the following: 36 | ;; 37 | ;; 1. Fast and sophisticated indentation function `plsql-indent' 38 | ;; (normally bound to TAB) which can help to present (and maintain) 39 | ;; complex code in a readable form. 40 | ;; 41 | ;; 2. Imenu support which allows you to jump to specific modules using 42 | ;; either the "Contents" menu or speedbar. 43 | ;; 44 | ;; 3. Align support which allows you to automatically "beautify" code 45 | ;; as you write by aligning certain constructs e.g. `align-current' 46 | ;; will align all =, :=, and => tokens in the current paragraph. 47 | ;; XEmacs users may have to get a copy of the align package to use 48 | ;; this feature. 49 | 50 | ;; This mode was written for FSF Emacs but has been tested in XEmacs. 51 | 52 | ;;;_ * Installation: 53 | 54 | ;; Place this file somewhere in your load path and byte-compile (!) it. 55 | ;; Also check the customizable variables for this package 56 | ;; `plsql-indent' and `plsql-uses-font-lock'. You can then set the 57 | ;; plsql major mode interactively via the command `plsql-mode' or 58 | ;; automatically via filename extensions by adding the following to 59 | ;; your dot emacs file: 60 | ;; 61 | ;; (setq auto-mode-alist 62 | ;; (append 63 | ;; '(("\\.\\(p\\(?:k[bg]\\|ls\\)\\|sql\\)\\'" . plsql-mode)) 64 | ;; auto-mode-alist)) 65 | ;; 66 | ;; If you use the speedbar you may want to set the following 67 | ;; 68 | ;; (speedbar-add-supported-extension "pls") 69 | ;; (speedbar-add-supported-extension "pkg") 70 | ;; (speedbar-add-supported-extension "pkb") 71 | ;; (speedbar-add-supported-extension "sql") 72 | ;; 73 | ;; as per your favorite filename extensions. 74 | 75 | ;; Enjoy! 76 | 77 | ;;;_ * Known Bugs 78 | 79 | ;; This has only been tested on a handful of programs I had to write 80 | ;; for a specific job. That code was reasonably complex but certainly 81 | ;; did not cover every aspect of PL/SQL so there are bound to be some 82 | ;; bugs. I am not currently working with this language so further 83 | ;; development has been suspended; however, if you find a bug please 84 | ;; email me with the offending code and I'll have a go at fixing it:-) 85 | 86 | ;; (1) A comment flush against a keyword/statement end produces bad 87 | ;; indentation. Probably too hard to fix. Does anyone actually like 88 | ;; to do this? 89 | 90 | ;; (2) Don't know if TAB gets bound properly in XEmacs. If anyone can 91 | ;; confirm this please email me (saves me installing XEmacs:-) 92 | 93 | ;; (3) The some inconsistencies will screw up the indention that follows e.g. 94 | 95 | ;; if CONDITION then 96 | ;; STATEMENT 97 | ;; else STATEMENT; 98 | ;; STATEMENT; 99 | ;; end if; 100 | 101 | ;; is screwed up but 102 | 103 | ;; if CONDITION then 104 | ;; STATEMENT 105 | ;; else 106 | ;; STATEMENT; 107 | ;; STATEMENT; 108 | ;; end if; 109 | 110 | ;; is O.K. Let me know if you really want to do the former and I'll 111 | ;; fix it :-) 112 | 113 | ;;;_ * Credits 114 | 115 | ;; Inspired by an pls-mode.el by Dmitry Nizhegorodov (Oracle) and 116 | ;; plsql-mode.el by Karel Sprenger. 117 | 118 | ;; Thanks to Arch Murphy for noting a problem with XEmacs, to Brett 119 | ;; for noting a problem with push, and Andreas Zielke for noting a 120 | ;; indentation problem with "blocks within blocks". Thanks to Jérome 121 | ;; Haguet for spotting another indentation bug and for suggesting 122 | ;; `plsql-indent-region'. Thanks to Alex Schroeder for compilation 123 | ;; suggestions and suggestions regarding SQL level indentation. Thanks 124 | ;; to Mark Buckle for convincing me to support block-less PL/SQL and 125 | ;; inspiring a huge code cleanup. 126 | 127 | ;;;_ * To do 128 | 129 | ;; (*) Check logic on calling `plsql-sql-statement-indent' do we still 130 | ;; need to do this. 131 | 132 | ;; (1) BNF for PL/SQL to add to semantic bovinator (Eeek!) 133 | ;; (2) Imenu for local modules, global variables? (really need 134 | ;; bovinator for that) 135 | ;; (3) Object oriented extensions? What are they? Haven't used this 136 | ;; stuff yet. 137 | ;; (4) Movement functions. Does anyone actually use these? Speedbar is 138 | ;; faster? 139 | ;; (5) Filling comments? Defer to rebox. 140 | ;; (6) Set the current indentation context as a text-property. This 141 | ;; could vastly simplify the code. Perhaps a new package entirely. 142 | ;; (7) Develop a test file containing all the PL/SQL contortions I can 143 | ;; think of. 144 | ;; (8) Pass on comment-indent and string-indent to user definable 145 | ;; functions. 146 | ;; (9) Can we have anonymous blocks inside exception sections? 147 | ;; (10) Make "align" line up anything that is highlighted? 148 | 149 | ;;;_ * Code 150 | 151 | ;; Note to hackers: explanatory notes scatter throughout. 152 | ;; Suggested starting point: search for ";;;;; Top Level" 153 | 154 | (defgroup plsql nil "") 155 | 156 | ;;;; Indentation: 157 | ;;;;; User Variables: 158 | 159 | (defcustom plsql-indent 3 160 | "*Number of columns of indentation for each level." 161 | :group 'plsql 162 | :type 'number) 163 | 164 | (defcustom plsql-uses-font-lock t 165 | "*Indicate whether font-lock properties can be use to speed up indentation. 166 | This is up to 3 times faster, and hence, highly recommended." 167 | :group 'plsql 168 | :type 'boolean) 169 | 170 | (defcustom plsql-mode-hook '() 171 | "*Hook for customizing `plsql-mode'." 172 | :type 'hook 173 | :group 'plsql) 174 | 175 | 176 | ;;;_ + Local Variables 177 | 178 | (defvar plsql-debug nil 179 | "If t then we rebuild everything on reload. Useful for debugging.") 180 | 181 | (eval-when-compile (setq plsql-debug t)) 182 | 183 | (defun plsql-toggle-debug () "Toggle value of plsql-debug." 184 | (interactive) 185 | (message "plsql-debug is %s" (setq plsql-debug (not plsql-debug)))) 186 | 187 | (defun plsql-reset (variable) 188 | "t iff VARIABLE should be reset" 189 | (or plsql-debug 190 | (null variable) 191 | (string-equal variable ""))) 192 | 193 | (defvar plsql-indent-function-stack nil 194 | "Record the indent functions use to indent the current line.") 195 | 196 | (defvar plsql-white-space-re "[ \t\n\r()]" 197 | "Regular expression matching whitespace or anything that delimits 198 | identifiers.") 199 | 200 | ;; this includes () since they can delimit 201 | (when (plsql-reset plsql-white-space-re) 202 | (setq plsql-white-space-re "[ \t\n\r()]" )) 203 | 204 | ;;;_ + Predicates 205 | 206 | (defun plsql-in-comment-p (&optional limit) 207 | "Return non-nil iff point is in a comment. 208 | LIMIT is a point before the current point used to limit the partial parse." 209 | (or (looking-at "/\\*") 210 | (nth 4 (parse-partial-sexp limit (point))))) 211 | 212 | (defun plsql-in-string-p (&optional limit) 213 | "Return non-nil iff point is in a string. 214 | LIMIT is a point before the current point used to limit the partial parse." 215 | (nth 3 (parse-partial-sexp limit (point)))) 216 | 217 | ;; alternatively using font-lock faces is even faster 218 | 219 | (defun plsql-comment-face-p (&optional limit) 220 | "Return non-nil iff text at point is displayed in a `font-lock-comment-face'." 221 | (eq (get-text-property (point) 'face) 'font-lock-comment-face)) 222 | 223 | (defun plsql-string-face-p (&optional limit) 224 | "Return non-nil iff text at point is displayed in a `font-lock-string-face'." 225 | (eq (get-text-property (point) 'face) 'font-lock-string-face)) 226 | 227 | (defvar plsql-in-comment-predicate 'plsql-in-comment-p 228 | "Function used to determine if point is inside a comment.") 229 | 230 | (defvar plsql-in-string-predicate 'plsql-in-string-p 231 | "Function used to determine if point is inside a string.") 232 | 233 | ;;;_ + RegExp Search 234 | 235 | ;; defining the following functions in-line give a small increase in speed 236 | 237 | (defsubst plsql-re-search-backward 238 | (regexp &optional bound no-error count limit) 239 | 240 | "Search backwards for REGEXP ignoring comments and strings. 241 | Optional parameters BOUND, NO-ERROR and COUNT behave as for `re-search-backward'. 242 | LIMIT is used to bound partial parse and is assumed to be before the current point." 243 | 244 | ;; cant think of any better logic than this 245 | 246 | (let ((not-found t) ;; invert the sense to minimise calls to `not' 247 | (limit (or limit 1))) 248 | (while (and not-found 249 | (re-search-backward regexp bound no-error count)) 250 | (forward-char 1) ;; point is on first char of match 251 | (unless (or (funcall plsql-in-comment-predicate limit) 252 | (funcall plsql-in-string-predicate limit)) 253 | (setq not-found nil)) 254 | (forward-char -1) ;; point is before first char of match 255 | ) 256 | 257 | (not not-found))) 258 | 259 | (defsubst plsql-re-search-forward 260 | (regexp &optional bound no-error count limit) 261 | 262 | "Search forwards for REGEXP ignoring comments and strings. 263 | Optional parameters BOUND, NO-ERROR and COUNT behave as for `re-search-forward'. 264 | LIMIT is used to bound partial parse and is assumed to be before the current point." 265 | 266 | (let ((not-found t) ;; invert the sense to minimise calls to `not' 267 | (limit (or limit 1))) 268 | (while (and not-found 269 | (re-search-forward regexp bound no-error count)) 270 | 271 | (forward-char -1) ;; point is on last char of match 272 | (unless (or (funcall plsql-in-comment-predicate limit) 273 | (funcall plsql-in-string-predicate limit)) 274 | (setq not-found nil)) 275 | (forward-char 1) ;; point is after last char of match 276 | ) 277 | (not not-found))) 278 | 279 | ;;;_ + Statement Level 280 | 281 | ;; There are a few special sub-statement forms that will require slightly 282 | ;; different indentation. 283 | ;; 284 | ;; a) expressions in parentheses e.g. function parameters 285 | ;; b) SQL (block) statements e.g. SELECT statements in a cursor 286 | ;; c) statements that span more then one line. 287 | 288 | (defvar plsql-leading-identifier-re "" 289 | "Regexp that will match a PL/SQL identifier." ) 290 | 291 | (when (plsql-reset plsql-leading-identifier-re) 292 | (setq plsql-leading-identifier-re "^[ \t\n\r]*\\([_#:$a-z,A-Z]+\\)")) 293 | 294 | ;; SYMBOL TABLE? 295 | 296 | (defvar plsql-sql-statement-re "" 297 | "Regexp matching key terms indicating that we are within an SQL statement.") 298 | 299 | ;; Borrowed this from sql-indent.el 300 | ;; plus some from Oracle 9i SQL reference 301 | ;; removed some that conflict with some PL/SQL keywords 302 | ;; do we need all these? 303 | 304 | ;; Exclude words longer that 8 characters. 305 | 306 | ;; This RE is usually referenced by looking at with point at the 307 | ;; beginning of the indentation so we can we don't need to check for 308 | ;; leading word boundary. 309 | 310 | (when (plsql-reset plsql-sql-statement-re) 311 | (setq plsql-sql-statement-re 312 | (concat 313 | "\\(" 314 | (regexp-opt 315 | (list 316 | "access" "add" "all" "alter" "analyse" "and" "any" "as" "asc" 317 | ;; "associate" 318 | "audit" 319 | ;; "authorization" 320 | "avg" "between" 321 | "by" 322 | ;; "char" 323 | "check" "cluster" 324 | ;; "close" 325 | ;; "cobol" 326 | "column" "comment" "commit" "compress" "connect" "continue" 327 | ;; "count" 328 | "create" "current" 329 | ;; "date" "decimal" 330 | "declare" 331 | ;; "default" 332 | "delete" 333 | "desc" "distinct" 334 | ;; "double" 335 | "drop" 336 | ;; "else" 337 | "escape" 338 | ;; "exclusive" 339 | "exec" "exists" "explain" 340 | ;; "fetch" 341 | ;; "file" 342 | "float" 343 | ;; "for" 344 | "foreign" "fortran" "found" "from" "go" 345 | ;; "goto" 346 | "grant" "group" "having" "identified" "immediate" "in" "increment" 347 | "index" 348 | ;; "indicator" 349 | "initial" "insert" "integer" 350 | ;; "intersect" 351 | "into" 352 | ;; "is" 353 | "key" 354 | ;; "language" 355 | "level" "like" "lock" "long" "max" 356 | ;; "maxextents" 357 | "min" "minus" 358 | ;; "mlslabel" 359 | "mode" "modify" "module" "noaudit" 360 | ;; "nocompress" 361 | "not" "nowait" "null" 362 | ;; "number" 363 | "numeric" "of" "offline" "on" "online" 364 | ;; "open" 365 | "option" "or" "order" 366 | ;; "pascal" 367 | "pctfree" "pli" 368 | ;; "precision" 369 | "primary" "prior" 370 | ;; "privileges" 371 | "public" 372 | ;; "raw" 373 | ;; "references" 374 | "rename" "resource" "revoke" "revoke" "rollback" 375 | "row" "rowid" "rownum" "rows" 376 | ;; "savepoint" 377 | "schema" "section" "select" "session" "set" "share" "size" 378 | ;; "smallint" 379 | "some" "sqlcode" "sqlerror" "start" 380 | ;; "successful" "sum" 381 | "synonym" "sysdate" "table" 382 | ;; "then" 383 | "to" "trigger" "truncate" 384 | "uid" "union" "unique" "update" "user" "validate" "values" 385 | ;;"varchar" "varchar2" 386 | "view" "whenever" "where" "with" "work" 387 | )) 388 | "\\)" 389 | plsql-white-space-re 390 | ))) 391 | 392 | (defsubst plsql-sql-statement-adjust () 393 | "If we are inside an SQL statement returns extra indentation required to line 394 | the last character of the SQL keyword that starts each line." 395 | 396 | ;; this is called solely to determine the real indent level of the 397 | ;; statement preceding the active line. 398 | 399 | (when plsql-debug 400 | (add-to-list 'plsql-indent-function-stack 'plsql-sql-statement-adjust 'append)) 401 | 402 | (save-excursion 403 | (back-to-indentation) 404 | (if (looking-at plsql-sql-statement-re) 405 | (- 8 ;; length of the longest keyword 406 | (length (thing-at-point 'word))) 407 | 0))) 408 | 409 | (defvar plsql-extra-indent-re "" 410 | "A couple of situations in which we might like to add a little indentation.") 411 | 412 | (when (plsql-reset plsql-extra-indent-re) 413 | (setq plsql-extra-indent-re 414 | (concat 415 | "\\b" ;; open word boundary 416 | ;; "\\(" ;; open group of alternatives 417 | (regexp-opt 418 | (list 419 | "in" 420 | "default" 421 | ) t ) 422 | ;; "\\)" ;; close group of alternatives 423 | plsql-white-space-re) ;; minimal whitespace after key word 424 | )) 425 | 426 | (defun plsql-statement-level-adjust (limit indent) 427 | "Adjust the proposed INDENT column at the statement level. 428 | LIMIT marks the beginning of the current statement. Here we deal with the 429 | indentation of SQL keywords starting an SQL block , variable declaration 430 | keywords, and multiline statements" 431 | 432 | ;; PRE CONDITION: 433 | ;; called after a `back-to-indentation' 434 | ;; POST CONDITION: 435 | 436 | (when plsql-debug 437 | (add-to-list 'plsql-indent-function-stack 438 | 'plsql-statement-level-adjust 'append)) 439 | 440 | (cond 441 | ((looking-at plsql-sql-statement-re) 442 | ;; line up last character of leading SQL keywords 443 | (+ indent 444 | (- 8 ;; length of CONNECT 445 | (length (thing-at-point 'word))))) 446 | 447 | ((looking-at plsql-extra-indent-re) 448 | ;; so add some extra indentation 449 | (+ indent plsql-indent)) 450 | 451 | ;; some times a section starter involves two words which we may 452 | ;; want to put on different lines e.g. if .. then 453 | ((looking-at (concat "then\\|loop\\|is\\|as" plsql-white-space-re)) 454 | (if (= (point) limit) ;; loop may also start a statement 455 | indent 456 | (save-excursion 457 | (goto-char limit) 458 | (current-indentation)))) 459 | 460 | ((save-excursion (plsql-re-search-backward "\n\\|\r" limit t nil limit)) 461 | ;; there a line break between point and the beginning of statement so 462 | ;; add some extra indentation. 463 | 464 | (+ indent plsql-indent)) 465 | (t indent) 466 | )) 467 | 468 | ;; (defun plsql-in-parenthesis-p () 469 | ;; (interactive) 470 | ;; (save-excursion 471 | ;; (let ((open 0)) 472 | ;; (while (and (< open 1) 473 | ;; (plsql-re-search-backward "(\\|)" nil t)) 474 | ;; (cond ((equal "(" (match-string 0)) 475 | ;; (setq open (+ open 1))) 476 | ;; (t (setq open (- open 1)) 477 | ;; 478 | ;; ))) 479 | ;; (if (> open 0) 480 | ;; (point) 481 | ;; nil) 482 | ;; ) 483 | ;; )) 484 | 485 | ;; throw and catch may well be our boys for this one 486 | ;; as might be recursion 487 | 488 | (defun plsql-in-parenthesis-p (limit) 489 | "If inside a parenthesis group, return the column of the open paren, 490 | else return nil. Search bound by LIMIT." 491 | (save-excursion 492 | (let ((parse-sexp-ignore-comments t)) 493 | 494 | ;; bottle-neck is really caused by this condition case 495 | 496 | ;; (result (condition-case nil 497 | ;; (scan-lists (point) -1 1) 498 | ;; (error nil)))) 499 | 500 | (nth 1 (parse-partial-sexp (point) limit))))) 501 | 502 | (defvar plsql-statement-end-re nil 503 | "Regexp matching any kind of statement end.") 504 | 505 | (when (plsql-reset plsql-statement-end-re) 506 | (setq plsql-statement-end-re 507 | (concat 508 | "\\(" ;; mark start of statement end 509 | 510 | "" ";" ;; generic PL/SQL statement end 511 | 512 | ;;; "\\|" "/" ;; generic SQL statement end 513 | ;;; *don't use since "/" is also used for divison in expressions* 514 | 515 | "\\|" ;;; some keywords can also be thought of as ending 516 | ;;; a statement so ... 517 | 518 | plsql-white-space-re ;;; minimal whitespace before key word -- 519 | ;;; needed to exclude words in identifiers 520 | 521 | ;; "\\(" 522 | (regexp-opt ;; optimisation actually does nothing:-) 523 | (list 524 | "loop" ;; delimits contents of a loop statement 525 | "then" ;; delimits contents of a conditional 526 | "else" ;; ends the previous sub-section, starts a new one 527 | "as" ;; dec section 528 | "is" ;; dec section 529 | 530 | ;;; "elsif" ;; *don't use since the following "then" is the real end* 531 | 532 | ) t) 533 | ;; "\\)" 534 | 535 | "\\)" ;;; mark end of statement excluding whitespace 536 | plsql-white-space-re ;;; minimal whitespace after key word -- 537 | ;;; needed to exclude words in identifiers 538 | ))) 539 | 540 | (defun plsql-in-sql-block-p (limit) 541 | "If inside (but not just before) an SQL block, return the adjusted 542 | position of the start of the block the block, otherwise return nil. 543 | Search bound by LIMIT." 544 | ;; PRE: point is at start of indentation 545 | (save-excursion 546 | (let ((start (point))) 547 | (when (or (plsql-re-search-backward plsql-statement-end-re 548 | limit t nil limit) 549 | ;; very first statement is a special case 550 | (and (= limit 1) (goto-char 1))) 551 | 552 | (when (and (plsql-re-search-forward 553 | ;; ensure we have a leading identifier boundary 554 | (concat plsql-white-space-re plsql-sql-statement-re) 555 | start t nil start) 556 | (progn ;; skip bracketed sql blocks 557 | (forward-char 1) 558 | (not (plsql-in-parenthesis-p limit)))) 559 | 560 | (back-to-indentation) 561 | (- (point) 562 | (plsql-sql-statement-adjust)) 563 | ))))) 564 | 565 | (defsubst plsql-indent-to-col (target-col) 566 | "Ensure current indentation is TARGET-COl, but avoid altering the buffer if 567 | no real change need be made." 568 | ;; assumes point is at start of indentation 569 | (unless (eq (current-indentation) target-col) 570 | (delete-horizontal-space) 571 | (indent-to target-col))) 572 | 573 | ;; An SQL block is a chain of SQL statements ending in either a ")" 574 | ;; parenthesis or a ";", wiht each SQL statement starting with a key word 575 | ;; match `plsql-sql-statement-re' 576 | 577 | (defun plsql-sql-block-indent (block-start) 578 | "Indent code inside sql block. BLOCK-start is the adjusted position of 579 | the start of the bock." 580 | ;; PRE CONDITION: 581 | ;; (1) POINT is at the current indentation of the line we are 582 | ;; indenting. 583 | ;; (2) LIMIT is the column start of the sql statement block 584 | ;; (3) We are not inside a comment or a string. 585 | 586 | ;; POST CONDITION: line indented correctly 587 | 588 | (when plsql-debug 589 | (add-to-list 'plsql-indent-function-stack 'plsql-sql-block-indent 'append)) 590 | 591 | (let ((block-col (save-excursion 592 | (goto-char block-start) 593 | (current-column)))) 594 | (if (looking-at plsql-sql-statement-re) 595 | (plsql-indent-to-col (+ block-col 596 | (- 8 ;; length of the longest keyword 597 | 598 | (length (thing-at-point 'word))))) 599 | 600 | ;; Indent to the same col as the first word following the previous 601 | ;; sql statement 602 | (plsql-indent-to-col (save-excursion 603 | (plsql-re-search-backward 604 | ;; ensure we have a leading identifier boundary 605 | (concat plsql-white-space-re plsql-sql-statement-re) 606 | block-start) 607 | (goto-char (match-end 1)) 608 | (skip-chars-forward " \t") 609 | (if (looking-at "[\n\r]") 610 | (+ block-col plsql-indent) 611 | (current-column))))))) 612 | 613 | (defun plsql-parenthesis-indent (limit) 614 | "Indent code inside parenthesis group. LIMIT is the start of the group." 615 | ;; PRE CONDITION: 616 | ;; (1) POINT is at the current indentation of the line we are 617 | ;; indenting. 618 | ;; (2) LIMIT is the start of the group 619 | ;; (3) We are not inside a comment or a string. 620 | 621 | ;; POST CONDITION: line indented correctly 622 | 623 | (when plsql-debug 624 | (add-to-list 'plsql-indent-function-stack 'plsql-paren-indent 'append)) 625 | 626 | (cond ((looking-at "(") ;; line break between statement start and open paren? 627 | (plsql-indent-to-col 628 | (save-excursion 629 | (plsql-re-search-backward plsql-leading-identifier-re limit t nil limit) 630 | ;; only fails if very first non-comment char is a ( 631 | (goto-char (or (match-beginning 1) 1)) 632 | (+ (current-column) plsql-indent)))) 633 | 634 | ((save-excursion ;; parentheses around sql block? 635 | (goto-char limit) 636 | (forward-char 1) 637 | (skip-chars-forward " \t") ;; non-line-ending whitespace 638 | ;; return column number of first non whitespace char after point. 639 | ;; making an adjustment for SQL statements 640 | (looking-at plsql-sql-statement-re)) 641 | (plsql-sql-block-indent (save-excursion 642 | (goto-char (- (match-end 1) 7)) 643 | (current-column)))) 644 | 645 | (t ;; plain old parentheses 646 | (plsql-indent-to-col (+ (save-excursion 647 | (goto-char limit) 648 | (forward-char 1) 649 | (skip-chars-forward " \t") 650 | (current-column)) 651 | (save-excursion 652 | (back-to-indentation) 653 | (if (looking-at plsql-extra-indent-re) 654 | plsql-indent 655 | 0))) 656 | )))) 657 | 658 | ;;;_ + Section Level 659 | ;; 660 | ;; A section contains a list of statements of the following form: 661 | ;; 662 | ;; WHITESPACE KEYWORD EXPRESSION; 663 | ;; 664 | ;; The following special cases also contain sub-sections 665 | ;; 666 | ;; if EXPRESSION then 667 | ;; SUB-SECTION 668 | ;; [else] 669 | ;; SUB-SECTION 670 | ;; [elsif EXPRESSION then] 671 | ;; SUB-SECTION 672 | ;; end if; 673 | ;; 674 | ;; [for | while] EXPRESSION loop 675 | ;; SUB-SECTION 676 | ;; end loop; 677 | ;; 678 | 679 | (defvar plsql-exec-sec-stmnt-end-re "" 680 | "Regexp to match the end of a statements in a execution section. 681 | The end of the 1st match should mark the boundary between statements.") 682 | 683 | ;; first call is after a call to `back-to-indentation' 684 | ;; second call immediately follows 685 | (when (plsql-reset plsql-exec-sec-stmnt-end-re) 686 | (setq plsql-exec-sec-stmnt-end-re 687 | (concat 688 | "\\(" ;; mark start of statement end 689 | 690 | "" ";" ;; generic PL/SQL statement end 691 | 692 | ;;; "\\|" "/" ;; generic SQL statement end 693 | ;;; *don't use since "/" is also used for divison in expressions* 694 | 695 | "\\|" ;;; some keywords can also be thought of as ending 696 | ;;; a statement so ... 697 | 698 | plsql-white-space-re ;;; minimal whitespace before key word -- 699 | ;;; needed to exclude words in identifiers 700 | 701 | ;; "\\(" 702 | (regexp-opt ;; optimisation actually does nothing:-) 703 | (list 704 | "loop" ;; delimits contents of a loop statement 705 | "then" ;; delimits contents of a conditional 706 | "else" ;; ends the previous sub-section, starts a new one 707 | 708 | ;;; "elsif" ;; *don't use since the following "then" is the real end* 709 | 710 | ) t) 711 | ;; "\\)" 712 | 713 | "\\)" ;;; mark end of statement excluding whitespace 714 | plsql-white-space-re ;;; minimal whitespace after key word -- 715 | ;;; needed to exclude words in identifiers 716 | ))) 717 | 718 | 719 | (defvar plsql-open-exec-sec-re "" 720 | "REGEXP matching words that start a new (sub-)section." ) 721 | 722 | ;; assume that this is used by `looking-at' after a call to `back-to-indentation' 723 | (when (plsql-reset plsql-open-exec-sec-re) 724 | (setq plsql-open-exec-sec-re 725 | (concat 726 | ;; "\\(" 727 | (regexp-opt 728 | (list "if" 729 | "else" ;; both an open and a close and a boundary 730 | "elsif" 731 | 732 | ;;; "when" ;; *case statements handled by `plsql-excep-sec-indent'* 733 | 734 | "for" 735 | "while" 736 | "loop" ;; infinite loops 737 | ) t) 738 | ;; "\\)" 739 | plsql-white-space-re ;; minimal whitespace after key word -- 740 | "" ;; needed to exclude words in identifiers 741 | ))) 742 | 743 | (defvar plsql-close-exec-sec-re "" 744 | "REGEXP matching words that end a (sub-)section." ) 745 | 746 | ;; assume that this is used by `looking-at' after a call to `back-to-indentation' 747 | (when (plsql-reset plsql-close-exec-sec-re) 748 | (setq plsql-close-exec-sec-re 749 | (concat 750 | "\\(" ;; open group of alternatives 751 | "\\b" ;; open word boundary 752 | (regexp-opt 753 | (list 754 | 755 | ;;; "when" ;; *case statements handled by `plsql-excep-sec-indent'* 756 | 757 | ;;; "begin" ;;; if we are looking at this then we should be in a 758 | ;;; declaration section 759 | 760 | "end" ;; 761 | 762 | "else" ;; end conditional 763 | "elsif" ;; end conditional 764 | 765 | "exception" ;; end the section WHAT ABOUT THIS GUY? 766 | 767 | )) 768 | plsql-white-space-re ;; minimal whitespace after key word -- 769 | "" ;; needed to exclude words in identifiers 770 | "\\|" 771 | "end;" ;; one special case 772 | "\\)" ;; close group of alternatives 773 | ))) 774 | 775 | (defun plsql-exec-sec-indent (limit) 776 | "Indent the current line given that it is inside an execution section. 777 | LIMIT provides a bound for searches and partial scans. The assumption 778 | is that the context of the current line can be completely determined 779 | without reference to any text above that point." 780 | 781 | ;; PRE CONDITION: 782 | ;; (1) POINT is at the current indentation of the line we are 783 | ;; indenting. 784 | ;; (2) The section-start at LIMIT is either: 785 | ;; 786 | ;; (a) 787 | ;; (b) 788 | ;; 789 | ;; POST CONDITION: current line is indented correctly. 790 | (when plsql-debug 791 | (add-to-list 'plsql-indent-function-stack 'plsql-exec-sec-indent 'append)) 792 | 793 | ;; limit has been set to the point before the keyword "begin" or "end". 794 | 795 | ;; we are indenting the first statement on current line so ... 796 | (back-to-indentation) 797 | 798 | ;; First determine some properties of the previous statement 799 | 800 | (let ((first-statement nil) 801 | (prev-indent 0) ;; indentation of the previous statement 802 | (prev-type 'plain) ;; either 'open, 'close, or 'plain 803 | (statement-start (point))) ;; mark the beginning of this statement 804 | 805 | (save-excursion 806 | 807 | ;; The previous statement will be delimited by two preceding 808 | ;; statement ends, unless of course we are before the 3rd following 809 | ;; the limit. 810 | 811 | ;;---1. back up one delimiter 812 | 813 | (if (plsql-re-search-backward 814 | plsql-exec-sec-stmnt-end-re limit t nil limit) 815 | (progn 816 | ;; Find the beginning of the statement we started on. 817 | ;; We have to do this in two steps because of potential 818 | ;; intervening comments. 819 | (save-excursion 820 | (goto-char (match-end 1)) ;; point is after statement delimiter 821 | (when (plsql-re-search-forward 822 | plsql-leading-identifier-re nil t nil limit) 823 | (setq statement-start (match-beginning 1)))) 824 | 825 | ;;---2. back up another delimiter 826 | 827 | (unless (plsql-re-search-backward 828 | plsql-exec-sec-stmnt-end-re limit t nil limit) 829 | 830 | ;; we failed so we must have started before the 3rd statement 831 | ;; after the limit 832 | 833 | (goto-char limit) 834 | (if (not (looking-at "end")) ;; point is before "begin" 835 | (forward-word 1) ;; set point after "begin" 836 | ;; point is before"end" 837 | (setq first-statement t) 838 | 839 | ;; oops! we really started from the 1st statement 840 | (setq prev-indent (- (current-indentation) 841 | (plsql-sql-statement-adjust)))) 842 | 843 | ) 844 | 845 | ;; set point before the first word of "previous statement" 846 | (unless first-statement 847 | (if (plsql-re-search-forward 848 | plsql-leading-identifier-re 849 | statement-start t nil limit) 850 | (progn 851 | (goto-char (match-beginning 1)) 852 | (setq prev-indent (- (current-indentation) 853 | (plsql-sql-statement-adjust))) 854 | (if (looking-at plsql-open-exec-sec-re) 855 | (setq prev-type 'open))) 856 | 857 | ;; Opps! First word of previous statement was block by 858 | ;; statement-start limit. The only case where I have seen 859 | ;; this happen is if whe are trying to align lik the 860 | ;; following: 861 | 862 | ;; if l_inplace = 0 and l_fullscreen = 0 863 | ;; then l_inplaceradio := 'inframe'; 864 | ;; elsif l_inplace = 3 and l_fullscreen = 0 865 | ;; then l_inplaceradio := 'inframe'; 866 | ;; else l_inplaceradio := 'fullscreen'; 867 | ;; end if; 868 | 869 | (setq prev-indent (- (current-indentation) 870 | (plsql-sql-statement-adjust))) 871 | (setq prev-type 'plain-force) 872 | ))) 873 | 874 | ;; so we started from the 1st statement of this section 875 | (goto-char limit) ;; point is before "begin" or "end" 876 | (if (looking-at "begin") 877 | (setq prev-type 'open)) ;; statement begins this section 878 | 879 | (setq prev-indent (current-indentation)) 880 | (forward-word 1) ;; point is after "begin" or "end" 881 | (if (plsql-re-search-forward 882 | plsql-leading-identifier-re 883 | nil t nil limit) 884 | (goto-char (match-beginning 1))) 885 | (setq statement-start (point)) 886 | )) 887 | 888 | ;; Now we know what the previous statement was so we can indent the 889 | ;; current line relative to it 890 | 891 | (if (looking-at plsql-close-exec-sec-re) 892 | (if (or (eq prev-type 'open) 893 | (eq prev-type 'plain-force)) 894 | (setq prev-type 'plain) ;; one-line sub-sections 895 | (setq prev-type 'close)) 896 | (if (eq prev-type 'plain-force) 897 | (setq prev-type 'plain))) 898 | 899 | (let ((new-indent 900 | (cond ((eq prev-type 'open) (+ prev-indent plsql-indent)) 901 | ((eq prev-type 'close) (- prev-indent plsql-indent)) 902 | (t prev-indent)))) 903 | 904 | ;; adjust if point is on or after the start of the statement 905 | ;; WHEN DOES THIS FAIL? 906 | (when (>= (point) statement-start) 907 | (setq new-indent (plsql-statement-level-adjust statement-start new-indent))) 908 | 909 | ;; avoid altering the buffer if no real change is made 910 | (unless (eq (current-indentation) new-indent) 911 | (delete-horizontal-space) 912 | (indent-to new-indent))) 913 | 914 | )) ;; -- end plsql-exec-sec-indent 915 | 916 | ;;;_ + Declaration Section 917 | 918 | (defvar plsql-dec-sec-stmnt-end-re "" 919 | "Regexp to match the end of a statements in a declaration section. 920 | The end of the 1st match should mark the boundary between statements.") 921 | 922 | (when (plsql-reset plsql-dec-sec-stmnt-end-re) 923 | (setq plsql-dec-sec-stmnt-end-re 924 | (concat 925 | "\\(" ;; mark start of statement end 926 | 927 | "" ";" ;; generic PLSQL statement end 928 | 929 | ;; "/" is also used for divison in expressions 930 | ;; "\\|" "/" ;; generic SQL statement end 931 | 932 | "\\|" ;; some keywords also end a statement so ... 933 | 934 | plsql-white-space-re ;; minimal whitespace before key word -- 935 | "" ;; needed to exclude words in identifiers 936 | 937 | ;; "\\(" 938 | (regexp-opt ;; optimisation actually does nothing:-) 939 | (list 940 | "as" ;; delimits variable declarations 941 | "is" ;; delimits variable declarations 942 | ) t) 943 | ;; "\\)" 944 | 945 | "\\)" ;; mark end of statement excluding whitespace 946 | plsql-white-space-re ;; minimal whitespace after key word -- 947 | "" ;; needed to exclude words in identifiers 948 | ))) 949 | 950 | 951 | (defvar plsql-dec-sec-fake-stmnt-end-re "" 952 | "Regexp matching the start of statements in a declaration section that are not 953 | always ended by something that matches `plsql-dec-sec-stmnt-end-re'. Will be 954 | compared with the first match of `plsql-leading-identifier-re'.") 955 | 956 | (when (plsql-reset plsql-dec-sec-fake-stmnt-end-re) 957 | (setq plsql-dec-sec-fake-stmnt-end-re 958 | (concat 959 | plsql-white-space-re ;; minimal whitespace before key word -- 960 | "" ;; needed to exclude words in identifiers 961 | (regexp-opt 962 | (list 963 | "cursor" 964 | "type" 965 | "subtype" 966 | "procedure" 967 | "function" 968 | )) 969 | plsql-white-space-re ;; minimal whitespace after key word -- 970 | "" ;; needed to exclude words in identifiers 971 | ))) 972 | 973 | (defvar plsql-open-dec-sec-re "" 974 | "REGEXP matching words that start a new declaration (sub-)section. 975 | Note that, since there is little difference between the specification section 976 | and the declaration section, we treat them the same way." ) 977 | 978 | (when (plsql-reset plsql-open-dec-sec-re) 979 | (setq plsql-open-dec-sec-re 980 | (concat 981 | "\\b" 982 | (regexp-opt 983 | (list "is" 984 | "as" 985 | "procedure" 986 | "function" 987 | "trigger" 988 | "declare" 989 | )) 990 | plsql-white-space-re ;; minimal whitespace after key word -- 991 | "" ;; needed to exclude words in identifiers 992 | ))) 993 | 994 | (defvar plsql-close-dec-sec-re "" 995 | "REGEXP matching words that end a declaration (sub-)section. 996 | Note that, since there is little difference between the specification section 997 | and the declaration section, we treat them the same way.") 998 | 999 | (when (plsql-reset plsql-close-dec-sec-re) 1000 | (setq plsql-close-dec-sec-re 1001 | (concat 1002 | "\\b" ;; open word boundary 1003 | "\\(" ;; open group of alternatives 1004 | (regexp-opt 1005 | (list 1006 | "begin" ;; start the actual section 1007 | "is" ;; both and open and a close 1008 | "as" 1009 | )) 1010 | plsql-white-space-re ;; minimal whitespace after key word -- 1011 | "" ;; needed to exclude words in identifiers 1012 | "\\|" 1013 | "end;" ;; one special case 1014 | "\\)" ;; close group of alternatives 1015 | ))) 1016 | 1017 | (defun plsql-dec-sec-indent (limit) 1018 | "Indent the current line given that it is inside declaration section." 1019 | 1020 | ;; PRE CONDITION: 1021 | ;; (1) POINT is at the current indentation of the line we are 1022 | ;; indenting. 1023 | ;; (2) The section-start at LIMIT is either: 1024 | ;; 1025 | ;; (a) 1026 | ;; (b) 1027 | ;; 1028 | ;; POST CONDITION: current line is indented correctly. 1029 | 1030 | (when plsql-debug 1031 | (add-to-list 'plsql-indent-function-stack 'plsql-dec-sec-indent 'append)) 1032 | 1033 | ;; This function is very similar to plsql-exec-sec-indent; separating it 1034 | ;; out, however, vastly simplifies the logic, in particular that relating 1035 | ;; to cursors and specification sections. 1036 | 1037 | ;; we are indenting the first statement on current line so ... 1038 | (back-to-indentation) 1039 | 1040 | ;; First determine some properties of the previous statement 1041 | 1042 | (let ((prev-indent 0) ;; indentation of the previous statement 1043 | (prev-type 'plain) ;; either 'open, 'close, or 'plain 1044 | (statement-start (point)));; mark beginning of this statement 1045 | 1046 | (save-excursion 1047 | 1048 | ;; The previous statement is delimited by two preceding statement 1049 | ;; ends, unless of course we are either the 1st statement in the 1050 | ;; section 1051 | 1052 | ;;---1. back up one delimiter 1053 | 1054 | (if (and (plsql-re-search-backward 1055 | plsql-dec-sec-stmnt-end-re limit t nil limit) 1056 | ;; e.g. "is" after a cursor is not a real statement delimiter 1057 | (if (save-excursion 1058 | (save-match-data 1059 | (and 1060 | (plsql-re-search-backward 1061 | plsql-dec-sec-stmnt-end-re limit t nil limit) 1062 | (plsql-re-search-forward 1063 | plsql-leading-identifier-re nil t nil limit) 1064 | (goto-char (match-beginning 1)) 1065 | (looking-at plsql-dec-sec-fake-stmnt-end-re)))) 1066 | (plsql-re-search-backward 1067 | plsql-dec-sec-stmnt-end-re limit t nil limit) 1068 | t)) 1069 | 1070 | (progn 1071 | ;; Find the statement start (we have to do this in two steps 1072 | ;; because of potential intervening comments). 1073 | (save-excursion 1074 | (goto-char (match-end 1)) ;; point is after statement delimiter 1075 | (when (plsql-re-search-forward 1076 | plsql-leading-identifier-re nil t nil limit) 1077 | (setq statement-start (match-beginning 1)))) 1078 | 1079 | ;;---2. back up another delimiter 1080 | 1081 | (if (and (plsql-re-search-backward 1082 | plsql-dec-sec-stmnt-end-re limit t nil limit) 1083 | ;; e.g. "is" after a "cursor" is not a real statement delimiter 1084 | (if (save-excursion 1085 | (and 1086 | (plsql-re-search-backward 1087 | plsql-dec-sec-stmnt-end-re limit t nil limit) 1088 | (plsql-re-search-forward 1089 | plsql-leading-identifier-re nil t nil limit) 1090 | (goto-char (match-beginning 1)) 1091 | (looking-at plsql-dec-sec-fake-stmnt-end-re))) 1092 | (plsql-re-search-backward 1093 | plsql-dec-sec-stmnt-end-re limit t nil limit) 1094 | t)) 1095 | 1096 | ;; so we started after the 2nd statement of this dec lock 1097 | (progn 1098 | (when (looking-at "[ \t\n\r]*[ia]s[ \t\n\r]") 1099 | ;; this way we end up treating specification sections 1100 | ;; the same as declaration sections 1101 | (forward-word 1)) 1102 | 1103 | ;; forward to next leading word 1104 | (when (plsql-re-search-forward 1105 | plsql-leading-identifier-re 1106 | statement-start t nil limit) 1107 | ;; if this fails we don't want any indentation:-) 1108 | (goto-char (match-beginning 1)) 1109 | (setq prev-indent (- (current-indentation) 1110 | (plsql-sql-statement-adjust))) 1111 | )) 1112 | 1113 | ;; so we started before the 3rd statement of this dec section 1114 | (goto-char limit) ;; point before "is" or "as" 1115 | (setq prev-type 'open) 1116 | (setq prev-indent (current-indentation)) 1117 | )) 1118 | 1119 | ;; so we started before the first "is" or "as" 1120 | (goto-char limit) 1121 | (back-to-indentation) 1122 | (setq prev-indent (current-indentation)) 1123 | (setq statement-start (point-max)) ;; don't do statement-level-indent 1124 | (setq prev-type 'open) 1125 | )) 1126 | 1127 | ;; Now we know what the previous statement was so we can indent the 1128 | ;; current line relative to it 1129 | 1130 | (if (looking-at plsql-close-dec-sec-re) 1131 | (if (eq prev-type 'open) 1132 | (setq prev-type 'plain) ;; one-line sub-sections 1133 | (setq prev-type 'close))) 1134 | 1135 | (let ((new-indent 1136 | (cond ((eq prev-type 'open) (+ prev-indent plsql-indent)) 1137 | ((eq prev-type 'close) (- prev-indent plsql-indent)) 1138 | (t prev-indent)))) 1139 | 1140 | ;; point is after the beginning of the statement 1141 | (when (>= (point) statement-start) 1142 | (setq new-indent (plsql-statement-level-adjust statement-start new-indent))) 1143 | 1144 | ;; avoid altering the buffer if no real change is made 1145 | (unless (eq (current-indentation) new-indent) 1146 | (delete-horizontal-space) 1147 | (indent-to new-indent))) 1148 | 1149 | )) ;;-- end plsql-dec-sec-indent 1150 | 1151 | 1152 | ;;;_ + Exception Section 1153 | 1154 | ;; And in the exception section: 1155 | ;; 1156 | ;; when ERROR-CODE then 1157 | ;; SUB-SECTION 1158 | ;; 1159 | ;; The CASE statement is another special case: indentation behaves just like 1160 | ;; an exception section. 1161 | 1162 | (defvar plsql-except-sec-stmnt-end-re "" 1163 | "Regexp to match the end of a statements in a case or exception section. 1164 | The end of the 1st match should mark the boundary between statements.") 1165 | 1166 | (when (plsql-reset plsql-except-sec-stmnt-end-re) 1167 | (setq plsql-except-sec-stmnt-end-re 1168 | (concat 1169 | "\\(" ;; mark start of statement end 1170 | 1171 | "" ";" ;; generic PLSQL statement end 1172 | 1173 | ;; "/" is also used for divison in expressions 1174 | ;; "\\|" "/" ;; generic SQL statement end 1175 | 1176 | "\\|" ;; some keywords also end a statement so ... 1177 | 1178 | plsql-white-space-re ;; minimal whitespace before key word -- 1179 | "" ;; needed to exclude words in identifiers 1180 | 1181 | ;; "\\(" 1182 | (regexp-opt ;; optimisation actually does nothing:-) 1183 | (list 1184 | "then" ;; delimits contents of a conditional 1185 | "else" ;; delimits contents of a conditional 1186 | ) t) 1187 | ;; "\\)" 1188 | 1189 | "\\)" ;; mark end of statement excluding whitespace 1190 | plsql-white-space-re ;; minimal whitespace after key word -- 1191 | "" ;; needed to exclude words in identifiers 1192 | ))) 1193 | 1194 | (defvar plsql-open-execp-sec-re "" 1195 | "REGEXP matching words that start a new case or exception (sub-)section." ) 1196 | 1197 | (when (plsql-reset plsql-open-execp-sec-re) 1198 | (setq plsql-open-execp-sec-re 1199 | (concat 1200 | ;; "\\(" 1201 | (regexp-opt 1202 | (list 1203 | "exception" 1204 | "case" 1205 | "when" ;; start 1206 | "then" ;; one liners 1207 | "if" 1208 | "else" 1209 | ) t) 1210 | ;; "\\)" 1211 | plsql-white-space-re ;; minimal whitespace after key word -- 1212 | "" ;; needed to exclude words in identifiers 1213 | ))) 1214 | 1215 | 1216 | (defvar plsql-close-except-sec-re "" 1217 | "REGEXP matching words that end a new case or exception (sub-)section.") 1218 | 1219 | (when (plsql-reset plsql-close-except-sec-re) 1220 | (setq plsql-close-except-sec-re 1221 | (concat 1222 | "\\(" ;; open group of alternatives 1223 | "\\b" ;; open word boundary 1224 | (regexp-opt 1225 | (list 1226 | "when" ;; end one, start another 1227 | "else" 1228 | )) 1229 | plsql-white-space-re ;; minimal whitespace after key word -- 1230 | "" ;; needed to exclude words in identifiers 1231 | "\\)" ;; close group of alternatives 1232 | ))) 1233 | 1234 | 1235 | (defvar plsql-block-terminator "" 1236 | "Regexp matching the term that terminates a block.") 1237 | 1238 | ;; assume we are after a call to `back-to-indentation' 1239 | 1240 | (when (plsql-reset plsql-block-terminator) 1241 | (setq plsql-block-terminator 1242 | (concat 1243 | "end" 1244 | "\\(" 1245 | ";" 1246 | "\\|" 1247 | plsql-white-space-re ;; minimal whitespace after key word -- 1248 | "" ;; needed to exclude words in identifiers 1249 | "\\)" 1250 | ))) 1251 | 1252 | (defun plsql-except-sec-indent (limit) 1253 | "Indent the current line given that it is inside a case or exception section." 1254 | 1255 | ;; PRE CONDITION: 1256 | ;; (1) POINT is at the current indentation of the line we are 1257 | ;; indenting. 1258 | ;; (2) The section-start at LIMIT is either: 1259 | ;; 1260 | ;; (a) 1261 | ;; (b) 1262 | ;; 1263 | ;; POST CONDITION: current line is indented correctly. 1264 | 1265 | (when plsql-debug 1266 | (add-to-list 'plsql-indent-function-stack 'plsql-except-sec-indent 'append)) 1267 | 1268 | ;; This function is very similar to plsql-exec-sec-indent; separating it 1269 | ;; out, however, vastly simplifies the logic, in particular that relating 1270 | ;; to ending a blocks that have exception sections. 1271 | 1272 | ;; we are indenting the first statement on current line so ... 1273 | (back-to-indentation) 1274 | 1275 | ;; First determine some properties of the previous statement 1276 | 1277 | (let ((prev-indent 0) ;; indentation of the previous statement 1278 | (prev-type 'plain) ;; either 'open, 'open-force', 'close, or 'plain 1279 | (statement-start (point))) ;; mark beginning of this statement 1280 | 1281 | (save-excursion 1282 | ;; first catch the "end of block" special case; 1283 | ;; this includes the "end of case statement" special case 1284 | (if (looking-at plsql-block-terminator) 1285 | (progn 1286 | ;; (setq statement-start (point)) ;; why? 1287 | (goto-char limit) 1288 | (setq prev-indent (current-indentation)) 1289 | (setq prev-type 'plain)) 1290 | 1291 | ;; The previous statement is delimited by two preceding statement ends, 1292 | ;; unless of course we are either the 1st or 2nd statement in the 1293 | ;; section 1294 | 1295 | ;;---1. back up one statement 1296 | 1297 | (if (plsql-re-search-backward 1298 | plsql-except-sec-stmnt-end-re limit t nil limit) 1299 | (progn 1300 | ;; Find the statement start (we have to do this in two steps 1301 | ;; because of potential intervening comments). 1302 | (save-excursion 1303 | (goto-char (match-end 1)) ;; point is after statement delimiter 1304 | (when (plsql-re-search-forward 1305 | plsql-leading-identifier-re nil t nil limit) 1306 | (setq statement-start (match-beginning 1)))) 1307 | 1308 | ;;--2. back up another statement above this line 1309 | (back-to-indentation) 1310 | (unless (plsql-re-search-backward 1311 | plsql-except-sec-stmnt-end-re limit t nil limit) 1312 | ;; we started from the 2nd statement of this section 1313 | (goto-char limit) ;; point before "begin" 1314 | (forward-word 1)) ;; point is after "begin" 1315 | 1316 | ;; set point before the first word of "previous statement" 1317 | (when (plsql-re-search-forward 1318 | plsql-leading-identifier-re statement-start t nil limit) 1319 | ;; if this fails we don't want any indentation:-) 1320 | (goto-char (match-beginning 1)) 1321 | (setq prev-indent (- (current-indentation) 1322 | (plsql-sql-statement-adjust))) 1323 | (if (looking-at plsql-open-execp-sec-re) 1324 | (setq prev-type 'open)) 1325 | )) 1326 | 1327 | ;; we started from the 1st statement of this section 1328 | (goto-char limit) 1329 | (skip-chars-forward " \t") ;; point is before "case" or "exception" 1330 | (setq prev-indent (current-indentation)) 1331 | 1332 | (if (not (looking-at "case")) 1333 | (forward-word 1) ;; point is after "exception" 1334 | (forward-word 1) ;; point is after case 1335 | 1336 | ;; This fails if you stick a comment between "case" and its 1337 | ;; identifier. This case is too rare (and silly) to really 1338 | ;; consider:-) 1339 | 1340 | (skip-chars-forward " \t") ;; point is before identifier 1341 | (skip-chars-forward "^ \t")) ;; point is after identifier 1342 | 1343 | (if (plsql-re-search-forward plsql-leading-identifier-re 1344 | nil t nil limit) 1345 | (goto-char (match-beginning 1))) 1346 | (setq statement-start (point)) 1347 | (setq prev-type 'open-force) ;; previous statement begun this section 1348 | )) 1349 | ) 1350 | 1351 | ;; Now we know what the previous statement was so we can indent the 1352 | ;; current line relative to it 1353 | 1354 | (if (and (looking-at plsql-close-except-sec-re)) 1355 | (if (eq prev-type 'open) 1356 | (setq prev-type 'plain) ;; one-line sub-sections 1357 | (if (eq prev-type 'open-force) ;; beginning of section never a one-liner 1358 | (setq prev-type 'open) 1359 | (setq prev-type 'close)))) 1360 | 1361 | (let ((new-indent 1362 | (cond ((eq prev-type 'open) (+ prev-indent plsql-indent)) 1363 | ((eq prev-type 'open-force) (+ prev-indent plsql-indent)) 1364 | ((eq prev-type 'close) (- prev-indent plsql-indent)) 1365 | (t prev-indent)))) 1366 | 1367 | ;; maybe add some extra 1368 | (when (>= (point) statement-start) 1369 | (setq new-indent (plsql-statement-level-adjust statement-start new-indent))) 1370 | 1371 | ;; avoid altering the buffer if no real change is made 1372 | (unless (eq (current-indentation) new-indent) 1373 | (delete-horizontal-space) 1374 | (indent-to new-indent))) 1375 | 1376 | )) ;; -- end plsql-except-sec-indent 1377 | 1378 | ;;;_ + Package Level 1379 | 1380 | (defun plsql-package-indent (limit) 1381 | "Indent the current line given that it is outside a module 1382 | definition. Really this is for indenting package variables, first 1383 | line of a module, and the \"begin\" at the start of a packages 1384 | executable block." 1385 | 1386 | ;; PRE CONDITION: 1387 | ;; (1) POINT is at the current indentation of the line we are 1388 | ;; indenting. 1389 | ;; (2) The section-start at LIMIT is either: 1390 | ;; (a) "end" NOT followed by "if", "loop", or "case" 1391 | ;; (b) "function" or "procedure" but before "is" or "as" 1392 | ;; (c) "package". 1393 | ;; 1394 | ;; POST CONDITION: current line is indented correctly. 1395 | 1396 | (when plsql-debug 1397 | (add-to-list 'plsql-indent-function-stack 'plsql-package-indent 'append)) 1398 | 1399 | (back-to-indentation) 1400 | (let ((new-indent 1401 | (save-excursion 1402 | (cond ((looking-at "\\[ \t\n\r]+\\|^[ \t]*$") 1403 | ;; we're starting the executable section of a package body 1404 | ;; containing object, so decrease the indentation level 1405 | (goto-char limit) 1406 | (- (current-indentation) plsql-indent)) 1407 | 1408 | ((looking-at "/\\|\\bend\\b") 1409 | ;; we're ending a package specification 1410 | 0) 1411 | 1412 | ((looking-at "--\\|\\b\\(procedure\\|function\\|trigger\\)\\b") 1413 | 1414 | ;; where doing a module specification 1415 | ;; hmm should really indent to previous in case 1416 | ;; its a local procedure (god does anyone actually do that?) 1417 | 1418 | plsql-indent) 1419 | 1420 | ((looking-at 1421 | "\\\\([ \t\n\r]+or[ \t\n\r]+replace\\)?[ \t\n\r]+\\(\\w+\\)") 1422 | ;; we're starting a new package body or spec 1423 | (if (string-match "procedure\\|function\\|trigger" 1424 | (match-string 1)) 1425 | plsql-indent 0)) 1426 | 1427 | ((looking-at "package") 1428 | ;; old fashioned new package body or spec 1429 | 0) 1430 | 1431 | (t 1432 | ;; else we are a package variable decalaration 1433 | nil))))) 1434 | 1435 | (if (not new-indent) 1436 | (plsql-dec-sec-indent limit) 1437 | (unless (= (current-indentation) new-indent) 1438 | (delete-horizontal-space) 1439 | (indent-to new-indent))) 1440 | )) 1441 | 1442 | ;;;_ + Top Level 1443 | 1444 | ;; First lets revise some PL/SQL syntax. 1445 | 1446 | ;; Anonymous PL/SQL blocks have the following form: 1447 | ;; 1448 | ;; declare 1449 | ;; SECTION --- variable declaration 1450 | ;; begin 1451 | ;; SECTION --- executable statements 1452 | ;; exception 1453 | ;; SECTION --- catch errors 1454 | ;; end; 1455 | ;; 1456 | ;; or minimally 1457 | ;; 1458 | ;; begin 1459 | ;; SECTION --- executable statements 1460 | ;; end; 1461 | 1462 | ;; Modules are essentially just named blocks --- instead of the keyword 1463 | ;; "declare" they have a "specification" which takes one of the following 1464 | ;; forms: 1465 | 1466 | ;; [create [or replace]] 1467 | ;; procedure NAME [body] [(ARGLIST)] 1468 | ;; is 1469 | ;; 1470 | ;; or 1471 | ;; 1472 | ;; [create [or replace]] 1473 | ;; function NAME [body] [(ARGLIST)] 1474 | ;; return TYPE 1475 | ;; is 1476 | ;; 1477 | ;; (Note: "as" is a synonym for "is") 1478 | 1479 | ;; Now since some indentation rules only apply to specific sections, the 1480 | ;; indentation logic can be vastly simplified by first establishing which 1481 | ;; section the current point is in. There are 4 sections: 1482 | ;; 1483 | ;; (1) specification 1484 | ;; (2) declaration 1485 | ;; (3) executable 1486 | ;; (4) exception 1487 | ;; 1488 | ;; In addition there is the case when we are outside of all these 1489 | ;; sections. package level/ module level. 1490 | ;; called only once but we build it outside to speed things up 1491 | (defvar plsql-top-level-flush-re "" 1492 | "Regexp matching the start of top level statements that should be eft flush.") 1493 | 1494 | (when (plsql-reset plsql-top-level-flush-re) 1495 | (setq plsql-top-level-flush-re 1496 | (regexp-opt 1497 | (list 1498 | "create" 1499 | "begin" 1500 | "declare" 1501 | "function" 1502 | "procedure" 1503 | "trigger" 1504 | "package" 1505 | ) 'word) 1506 | )) 1507 | 1508 | ;; Not c 1509 | (defun plsql-top-level-indent (limit) 1510 | "The last nail in the coffin for a really rare case. Everything 1511 | outside of a package body or spec is left flushed." 1512 | (when plsql-debug 1513 | (add-to-list 'plsql-indent-function-stack 'plsql-top-level-indent 'append)) 1514 | 1515 | (back-to-indentation) 1516 | (plsql-indent-to-col 1517 | (if (looking-at plsql-top-level-flush-re) 1518 | 0 ;; special case 1519 | (plsql-statement-level-adjust (point) 0)))) 1520 | ; 1521 | (defvar plsql-section-start-re "" 1522 | "Regexp matching the beginning of (or a boundary within) a new type 1523 | of section.") 1524 | 1525 | ;; called only once but we build it outside to speed things up 1526 | (when (plsql-reset plsql-section-start-re) 1527 | (setq plsql-section-start-re 1528 | (concat 1529 | "\\(?:" 1530 | "^\\|" 1531 | plsql-white-space-re ;; whitespace delimits keywords 1532 | "\\)" 1533 | ;; "\\(" 1534 | (regexp-opt 1535 | (list 1536 | "end" ;; special case: could be module, except, or exec 1537 | "begin" 1538 | "declare" 1539 | "function" 1540 | "procedure" 1541 | "trigger" 1542 | "package" 1543 | ;; NB: "exception" keyword is also a type so we can't use 1544 | ;; it as a section start keyword! 1545 | ) t) 1546 | ;; "\\)" 1547 | "\\(;" 1548 | "\\|" 1549 | plsql-white-space-re ;; whitespace delimits keywords 1550 | "\\)" 1551 | ))) 1552 | 1553 | ;; Not called. Could be customizable? 1554 | 1555 | (defun plsql-comment-indent (&optional start) 1556 | "Indent the current line at least as much as the previous." 1557 | 1558 | (when plsql-debug 1559 | (add-to-list 'plsql-indent-function-stack 'plsql-comment-indent 'append)) 1560 | 1561 | (let ((new-indent (save-excursion 1562 | (condition-case nil 1563 | (forward-line -1) 1564 | (error nil)) 1565 | (current-indentation)))) 1566 | (unless (> (current-indentation) new-indent) 1567 | (delete-horizontal-space) 1568 | (indent-to new-indent))) 1569 | ) 1570 | 1571 | ;; Modules and blocks can nest so the following needs to work as well: 1572 | 1573 | ;; create or replace package body NAME is 1574 | ;; 1575 | ;; procedure NAME 1576 | ;; is 1577 | ;; 1578 | ;; procedure NAME is 1579 | ;; begin 1580 | ;; SECTIONS 1581 | ;; end NAME; 1582 | ;; 1583 | ;; procedure NAME is 1584 | ;; begin 1585 | ;; SECTIONS 1586 | ;; end NAME; 1587 | ;; 1588 | ;; ... 1589 | ;; 1590 | ;; begin 1591 | ;; 1592 | ;; begin 1593 | ;; STATEMENT 1594 | ;; end; 1595 | ;; 1596 | ;; begin 1597 | ;; STATEMENT 1598 | ;; end; 1599 | ;; 1600 | ;; ... 1601 | ;; 1602 | ;; end NAME; 1603 | ;; 1604 | ;; end NAME; 1605 | 1606 | (defun plsql-after-end-indent (limit) 1607 | "Indent line following a an \"end\" keyword that begins at 1608 | LIMIT. Determines the context and passes the job on to the appropriate 1609 | indentation function." 1610 | 1611 | ;; PRE CONDITION: 1612 | ;; (1) POINT is at the current indentation of the line we are 1613 | ;; indenting. 1614 | ;; (2) The word starting at LIMIT is "end" 1615 | ;; 1616 | ;; POST CONDITION: current line is indented correctly. 1617 | 1618 | (when plsql-debug 1619 | (add-to-list 'plsql-indent-function-stack 'plsql-after-end-indent 'append)) 1620 | 1621 | (let (indent-function match) 1622 | ;; (if (looking-at "" 1623 | 1624 | (if (save-excursion 1625 | (goto-char (+ limit 3)) ;; after "end" 1626 | (skip-chars-forward " \t\n\r") ;; whitespace 1627 | (looking-at "\\(if\\|loop\\|case\\)[ \t\n\r]*;")) 1628 | ;; after a compound statement (most common case) 1629 | (setq indent-function 'plsql-exec/except-sec-indent) 1630 | 1631 | ;; find the previous section start with indentation level less 1632 | ;; than that of the "end" keyword 1633 | 1634 | (save-excursion 1635 | (goto-char limit) 1636 | (let ((end-indent (current-indentation))) 1637 | (while (and 1638 | (> end-indent 0) 1639 | (plsql-re-search-backward plsql-section-start-re nil t nil nil) 1640 | (goto-char (match-beginning 1)) 1641 | (>= (current-indentation) end-indent))))) 1642 | 1643 | (setq match (downcase (match-string 1))) 1644 | (setq limit (match-beginning 1)) ;; reset the limit 1645 | 1646 | (cond ((string-match "begin\\|end" match) 1647 | ;; after an anonymous block inside an executable section 1648 | ;; can we have anonymous blocks inside exception sections? 1649 | (setq indent-function 'plsql-exec-sec-indent)) 1650 | 1651 | ((or (string-equal "procedure" match) 1652 | (string-equal "function" match)) 1653 | ;; after a module declaration _inside_ a declaration section 1654 | (setq indent-function 'plsql-dec-sec-indent)) 1655 | 1656 | ((string-equal "package" match) 1657 | ;; after a module declaration _inside_ a package 1658 | (setq indent-function 'plsql-package-indent)) 1659 | 1660 | (t 1661 | ;; unpackaged block or 1662 | (message "unpackaged block") 1663 | (setq indent-function 'plsql-top-level-indent)) 1664 | )) 1665 | 1666 | (funcall indent-function limit))) 1667 | 1668 | 1669 | (defun plsql-exec/except-sec-indent (limit) 1670 | "Special case: exception keyword is also a type so we can't use it 1671 | as a section start keyword. We have to do this outside of the first 1672 | save-excursion of `plsql-indent'." 1673 | 1674 | ;; PRE CONDITION: 1675 | ;; (1) POINT is at the current indentation of the line we are 1676 | ;; indenting. 1677 | ;; (2) The section-start at LIMIT is either: 1678 | ;; 1679 | ;; (a) "begin", or 1680 | ;; (b) "end" followed by "if", "loop", or "case" 1681 | ;; 1682 | ;; so we are either in an executable or exception-like 1683 | ;; section. Since a "case" construct behaves just like an exception 1684 | ;; section, we pass that case (no pun intended:-) onto 1685 | ;; `plsql-except-sec-indent'. 1686 | 1687 | ;; 1688 | ;; POST CONDITION: current line is indented correctly. 1689 | 1690 | (when plsql-debug 1691 | (add-to-list 'plsql-indent-function-stack 'plsql-exec/except-sec-indent 'append)) 1692 | 1693 | (let ((indent-function 'plsql-exec-sec-indent)) ;; the default 1694 | 1695 | (save-excursion ;; override the default? 1696 | (back-to-indentation) 1697 | (when (plsql-re-search-backward "[ \t\n\r]\\(exception\\|case\\)[ \t\n\r]" 1698 | limit t nil limit) 1699 | (setq limit (match-beginning 1)) ;; new limit 1700 | (unless (and (string-equal "case" (match-string 1)) 1701 | (back-to-indentation) 1702 | (looking-at "end")) 1703 | ;; really inside an exception like section 1704 | (setq indent-function 'plsql-except-sec-indent)) 1705 | )) 1706 | (funcall indent-function limit) 1707 | )) 1708 | 1709 | 1710 | (defun plsql-indent () 1711 | "Indent the current line appropriate to the current structural unit." 1712 | (interactive) 1713 | 1714 | (let ((target-col nil) 1715 | (target-start nil) 1716 | (indent-function nil) 1717 | (limit 1) 1718 | (match nil) 1719 | (in-comment-p nil) 1720 | (in-string-p nil) 1721 | (case-fold-search t));; ignore case in searches 1722 | 1723 | ;; first guess at indentation context 1724 | ;; and set search limits 1725 | (save-excursion 1726 | (back-to-indentation) 1727 | (cond (;; skip comments 1728 | (and (funcall plsql-in-comment-predicate 1) 1729 | (not (looking-at "--"))) ;; pass this comment starter on 1730 | (setq in-comment-p t)) ;; do nothing for the moment 1731 | 1732 | (;; skip strings 1733 | (and (funcall plsql-in-string-predicate 1) 1734 | (not (looking-at "'"))) ;; pass on string starters 1735 | (setq in-string-p t)) ;; do nothing for the moment 1736 | 1737 | (;; indent after first block starter 1738 | (plsql-re-search-backward 1739 | plsql-section-start-re nil t nil nil) 1740 | 1741 | ;; determine section type and pass on to appropriate 1742 | ;; indentation function 1743 | (setq match (downcase (match-string 1))) 1744 | 1745 | ;; get a preliminary bound for searches and parsing 1746 | (setq limit (match-beginning 1)) 1747 | 1748 | (cond ((string-equal "end" match) ;; most common match 1749 | (setq indent-function 'plsql-after-end-indent)) 1750 | 1751 | ((string-equal "begin" match) 1752 | ;; inside an execution or exception type section 1753 | (setq indent-function 'plsql-exec/except-sec-indent)) 1754 | 1755 | ;; inside a declaration section 1756 | ((string-equal "declare" match) 1757 | (setq indent-function 'plsql-dec-sec-indent)) 1758 | 1759 | ;; inside either a declaration section or a 1760 | ;; package specification 1761 | ((or (string-equal "procedure" match) 1762 | (string-equal "function" match)) 1763 | 1764 | (plsql-re-search-forward 1765 | plsql-dec-sec-stmnt-end-re 1766 | nil t nil limit) 1767 | 1768 | (if (string-match "is\\|as" (or (match-string 1) "dummy")) 1769 | ;; it really a declaration section 1770 | (setq indent-function 'plsql-dec-sec-indent) 1771 | ;; no its really a package specification 1772 | (setq indent-function 'plsql-package-indent))) 1773 | 1774 | ;; inside a package definition but before any declarations 1775 | ((string-equal "package" match) 1776 | (setq indent-function 'plsql-package-indent)) 1777 | )) 1778 | 1779 | (t ;; indent before first block starter 1780 | ;; so we might be looking at block-less PL/SQL 1781 | (setq limit 1) 1782 | (cond ((plsql-re-search-backward 1783 | plsql-exec-sec-stmnt-end-re nil t nil nil) 1784 | ;; bare pl/sql 1785 | (setq indent-function 'plsql-exec/except-sec-indent)) 1786 | 1787 | ((looking-at plsql-section-start-re) 1788 | (setq indent-function 'plsql-package-indent)) 1789 | 1790 | (t ;; start of bare pl/sql 1791 | (setq indent-function 'plsql-top-level-indent) 1792 | ))) 1793 | )) 1794 | 1795 | (setq plsql-indent-function-stack nil) 1796 | ;; We deal with parentheses and sql blocks differently. We can't move these 1797 | ;; tests higher because they really need the search limit to be set first. 1798 | 1799 | (save-excursion 1800 | (back-to-indentation) 1801 | (when (and (not in-comment-p) (not in-string-p)) 1802 | (cond (;; parenthesis 1803 | (setq target-col (plsql-in-parenthesis-p limit)) 1804 | (setq indent-function 'plsql-parenthesis-indent) 1805 | (setq limit target-col)) 1806 | 1807 | (;; SQL block 1808 | (setq target-start (plsql-in-sql-block-p limit)) 1809 | (setq indent-function 'plsql-sql-block-indent) 1810 | (setq limit target-start)) 1811 | )) 1812 | 1813 | (when indent-function 1814 | (when plsql-debug 1815 | (add-to-list 'plsql-indent-function-stack indent-function 'append)) 1816 | (funcall indent-function limit))) 1817 | 1818 | ;; maybe reposition cursor to the start of the indentation 1819 | ;; (only happens when indenting a blank line) 1820 | (if (= (current-column) 0) (skip-chars-forward " \t")) 1821 | (when plsql-debug 1822 | ;; print a pseudo stack trace 1823 | (message "%s" 1824 | (mapconcat 1825 | (lambda (x) 1826 | (when x (substring (substring (symbol-name x) 6) 0 -7))) 1827 | plsql-indent-function-stack 1828 | " -> "))) 1829 | )) 1830 | 1831 | ;;;_ + Imenu 1832 | 1833 | (eval-and-compile (require 'imenu)) ;; quieten compiler 1834 | 1835 | (defvar plsql-imenu-title "Contents" 1836 | "*Title of the menu which will be added to the menu bar.") 1837 | 1838 | (defvar plsql-imenu-regexp "" 1839 | "*A regular expression matching a head line to be added to the menu.") 1840 | 1841 | (when (plsql-reset plsql-imenu-regexp) 1842 | (setq plsql-imenu-regexp 1843 | (concat 1844 | "\\(?:" 1845 | "^\\|" 1846 | plsql-white-space-re 1847 | ;; "[ \t\n]*" ;; leading whitespace 1848 | "\\)" 1849 | "\\(" 1850 | "" "package" 1851 | "\\|" "function" 1852 | "\\|" "procedure" 1853 | "\\)" 1854 | plsql-white-space-re ;; trailing whitespace 1855 | ))) 1856 | 1857 | ;; Thanks to Bret (?) for spotting this one 1858 | (eval-when-compile (require 'cl)) 1859 | 1860 | ;; This doesn't work? Why? 1861 | ;; 1862 | ;;(defun push (obj stack) 1863 | ;; "Poor man's push (rather than requiring 'cl)." 1864 | ;; (setf stack (cons obj stack))) 1865 | 1866 | ;; Make an index for imenu 1867 | (defun plsql-imenu-index () 1868 | "Return an table of contents for an PL/SQL buffer for use with Imenu." 1869 | (interactive) 1870 | (let ((case-fold-search t) 1871 | (toc-string nil) 1872 | (procedure-alist '()) 1873 | (variable-alist '()) 1874 | (function-alist '()) 1875 | (type-alist '()) 1876 | (package-alist '()) 1877 | (index-alist '()) 1878 | (in-body nil) 1879 | (body-defn nil) 1880 | (spec-defn nil) 1881 | (package nil) 1882 | prev-pos ;; dummy used by imenu-progress-message 1883 | match start end indent) 1884 | 1885 | (save-excursion 1886 | (goto-char (point-max)) 1887 | (imenu-progress-message prev-pos 0) 1888 | (while (and (plsql-re-search-backward plsql-imenu-regexp nil t) 1889 | (not (funcall plsql-in-string-predicate 1))) 1890 | 1891 | (imenu-progress-message prev-pos nil t) 1892 | (setq end (match-end 1) 1893 | start (match-beginning 1) 1894 | match (downcase (match-string 1))) 1895 | (goto-char start) 1896 | (beginning-of-line) 1897 | (setq indent (/ (current-indentation) plsql-indent)) 1898 | ;; extra indent for local modules 1899 | (setq indent (if (< indent 2) "" 1900 | (make-string (* (- indent 1) 2) ?\ ))) 1901 | (goto-char end) 1902 | (save-excursion 1903 | (skip-chars-forward "[ \t\n\r]+") 1904 | (when (looking-at "body") 1905 | (setq in-body t) 1906 | (forward-word 1) 1907 | (skip-chars-forward "[ \t\n\r]+")) 1908 | (setq toc-string (concat indent (thing-at-point 'symbol))) 1909 | (cond ((string-equal match "procedure") 1910 | (push (cons toc-string (point)) procedure-alist)) 1911 | ((string-equal match "function") 1912 | (push (cons toc-string (point)) function-alist)) 1913 | ((string-equal match "type") 1914 | (push (cons toc-string (point)) type-alist)) 1915 | 1916 | ((string-equal match "package") 1917 | 1918 | (if in-body 1919 | (progn 1920 | (setq package (concat toc-string" body")) 1921 | (setq body-defn t)) 1922 | (setq package (concat toc-string" spec")) 1923 | (setq spec-defn t)) 1924 | 1925 | ;; try to minimise the number of sublists 1926 | (if procedure-alist 1927 | (if (or function-alist type-alist) 1928 | (push (cons "Procedures" procedure-alist) package-alist) 1929 | (setq package-alist procedure-alist))) 1930 | 1931 | (if function-alist 1932 | (if (or procedure-alist type-alist) 1933 | (push (cons "Functions" function-alist) package-alist) 1934 | (setq package-alist function-alist))) 1935 | 1936 | (if type-alist 1937 | (if (or procedure-alist function-alist) 1938 | (push (cons "Types" type-alist) package-alist) 1939 | (setq package-alist type-alist))) 1940 | 1941 | (when package-alist ;; don't do it if empty 1942 | (push (cons package package-alist) index-alist)) 1943 | 1944 | (setq procedure-alist '() 1945 | function-alist '() 1946 | type-alist '() 1947 | package-alist '() 1948 | in-body nil)) 1949 | 1950 | (t 1951 | (push (cons toc-string (point)) variable-alist) 1952 | )) 1953 | ) 1954 | (goto-char start) 1955 | )) 1956 | (imenu-progress-message prev-pos 100) 1957 | 1958 | (unless package 1959 | ;; else objects are package less 1960 | (if procedure-alist 1961 | (if (or function-alist type-alist) 1962 | (push (cons "Procedures" procedure-alist) index-alist) 1963 | (setq index-alist procedure-alist))) 1964 | 1965 | (if function-alist 1966 | (if (or procedure-alist type-alist) 1967 | (push (cons "Functions" function-alist) index-alist) 1968 | (setq index-alist function-alist))) 1969 | 1970 | (if type-alist 1971 | (if (or procedure-alist function-alist) 1972 | (push (cons "Types" type-alist) index-alist) 1973 | (setq index-alist type-alist)))) 1974 | 1975 | (unless (and body-defn spec-defn) 1976 | ;; pop off the superfluous identifier 1977 | (setq index-alist (cdr (car index-alist)))) 1978 | 1979 | index-alist)) 1980 | 1981 | (defun plsql-imenu-setup () 1982 | "*Setup the variables to support imenu." 1983 | (interactive) 1984 | (setq imenu-case-fold-search t) 1985 | (setq imenu-sort-function nil) ;; sorting the menu defeats the purpose 1986 | (setq imenu-create-index-function 'plsql-imenu-index) 1987 | (imenu-add-to-menubar plsql-imenu-title) 1988 | ) 1989 | 1990 | ;;;_ + Align 1991 | 1992 | ;; Should I make so that anything that is highlighted will line up? 1993 | ;; Should we make a block anything inside ()? 1994 | 1995 | (eval-and-compile 1996 | 1997 | (defcustom plsql-align-rules-list '() "" 1998 | :group 'plsql 1999 | :type 'align-rules-list-type) 2000 | 2001 | ;; Should I make so that anything that is highlighted will line up? 2002 | ;; Should we make a block anything inside ()? 2003 | 2004 | (when (condition-case nil 2005 | (require 'align) 2006 | (error nil)) 2007 | 2008 | ;; these are way too slow to use with indent before aligning 2009 | (unless (and plsql-align-rules-list plsql-debug) 2010 | (setq plsql-align-rules-list 2011 | '( 2012 | (plsql-assignment 2013 | (regexp . "\\(\\s-*\\):=\\(\\s-*\\)") 2014 | (group . (1 2)) 2015 | (modes . '(plsql-mode)) 2016 | (repeat t) 2017 | (tab-stop . nil)) 2018 | 2019 | (plsql-arrorw 2020 | (regexp . "\\(\\s-*\\)=>\\(\\s-*\\)") 2021 | (group . (1 2)) 2022 | (modes . '(plsql-mode)) 2023 | (repeat t) 2024 | (tab-stop . nil)) 2025 | 2026 | (plsql-equals ;; exclude the previous two cases 2027 | (regexp . "\\(\\s-*[^:]\\)=\\([^>]\\s-*\\)") 2028 | (group . (1 2)) 2029 | (repeat t) 2030 | (tab-stop . nil) 2031 | (modes . '(plsql-mode))) 2032 | 2033 | (plsql-operator ;; watch out for comments 2034 | (regexp . "\\(\\s-*\\)[-+/]{1}\\(\\s-*\\)") 2035 | (group . (1 2)) 2036 | (repeat t) 2037 | (tab-stop . nil) 2038 | (modes . '(plsql-mode))) 2039 | 2040 | (plsql-keywords 2041 | (regexp . "\\(\\s-+\\)\\(in\\|default\\|number\\|varchar2\\|blob\\|raw\\)\\b") 2042 | (group 1) 2043 | (repeat t) 2044 | (case-fold t) 2045 | (tab-stop . nil) 2046 | (modes . '(plsql-mode))) 2047 | ) 2048 | )) 2049 | 2050 | (put 'plsql-align-rules-list 'risky-local-variable t) 2051 | (add-to-list 'align-c++-modes 'plsql-mode) ;; eg expression functions ... 2052 | (add-to-list 'align-sq-string-modes 'plsql-mode) 2053 | (add-to-list 'align-open-comment-modes 'plsql-mode) 2054 | 2055 | ;; Should we re-bind new-line-and-indent to align the current 2056 | ;; region? That sounds expensive. 2057 | )) 2058 | 2059 | ;;;_ + Font-Lock 2060 | 2061 | (defvar plsql-oracle-font-lock-fix-re "") 2062 | 2063 | (when (or (null plsql-oracle-font-lock-fix-re) plsql-debug) 2064 | (setq plsql-oracle-font-lock-fix-re 2065 | (list (cons 2066 | ;; these guys would otherwise be in font-lock-function-name-face 2067 | (concat 2068 | "\\b" "\\(" (regexp-opt 2069 | (list 2070 | "if" 2071 | "then" 2072 | "when" 2073 | "else" 2074 | "elsif" 2075 | "begin" 2076 | "end" 2077 | "loop" 2078 | "for" 2079 | "while" 2080 | "return" 2081 | "exit" 2082 | )) "\\)" "\\b") 2083 | 'font-lock-keyword-face) 2084 | (cons 2085 | (concat 2086 | "\\b" "\\(" (regexp-opt 2087 | (list 2088 | "true" 2089 | "false" 2090 | "number" 2091 | "raw" 2092 | )) "\\)" "\\b") 2093 | 'font-lock-type-face) 2094 | 2095 | (cons 2096 | (concat 2097 | "\\b" "\\(" (regexp-opt 2098 | (list 2099 | "open" 2100 | "fetch" 2101 | "close" 2102 | "count" 2103 | )) "\\)" "\\b") 2104 | 'font-lock-builtin-face) 2105 | 2106 | ;; types and properties 2107 | (cons 2108 | "%[_#:$a-z,A-Z]+" 2109 | 'font-lock-constant-face) 2110 | 2111 | ;; err should probably use an anchored highlight 2112 | (cons 2113 | (concat 2114 | "\\b" 2115 | "\\(" "function" 2116 | "\\|" "procedure" 2117 | "\\|" "package body" ;; ick damn those groupings 2118 | "\\|" "package" 2119 | "\\)" 2120 | plsql-white-space-re 2121 | "\\([_#:$a-z,A-Z]+\\)") 2122 | (list 2123 | '(1 font-lock-type-face) 2124 | '(2 font-lock-function-name-face)) 2125 | ) 2126 | 2127 | ;; this guy is bad 2128 | (cons 2129 | (concat 2130 | "\\b" "\\(" (regexp-opt 2131 | (list 2132 | "language" 2133 | )) "\\)" "\\b") 2134 | ''default) 2135 | ))) 2136 | 2137 | ;;;_ + Mode 2138 | 2139 | (eval-when-compile (require 'sql)) ;; quieten compiler 2140 | 2141 | (defun plsql-indent-region (beg end) 2142 | "Indent the region between BEG and END with a progress display." 2143 | (interactive "*r") 2144 | (goto-char beg) 2145 | (let* ((line-count (count-lines beg end)) 2146 | (lines-indented 0) 2147 | (lines-remaining line-count) 2148 | (endmark (copy-marker end))) 2149 | (while (< (point) endmark) 2150 | ;; Report % progress every every 40 lines 2151 | (when (> lines-indented 39) 2152 | (setq lines-remaining (- lines-remaining lines-indented) 2153 | lines-indented 0) 2154 | (message "Indenting region...(%d%%)" 2155 | (/ (* (- line-count lines-remaining) 100) line-count))) 2156 | (plsql-indent) 2157 | (forward-line 1) 2158 | (setq lines-indented (1+ lines-indented))) 2159 | (message "Indenting region...done"))) 2160 | 2161 | (defun plsql-mode () 2162 | "Programming support mode for PL/SQL code." 2163 | 2164 | (interactive) 2165 | (require 'sql) 2166 | 2167 | ;; (modify-syntax-entry ?# "w" sql-mode-syntax-table) 2168 | ;; (modify-syntax-entry ?_ "w" sql-mode-syntax-table) 2169 | ;; (modify-syntax-entry ?$ "w" sql-mode-syntax-table) 2170 | 2171 | (setq sql-mode-font-lock-keywords 2172 | (append plsql-oracle-font-lock-fix-re ;; override some bad bits 2173 | sql-mode-oracle-font-lock-keywords)) 2174 | (setq font-lock-mark-block-function 'mark-visible) 2175 | (sql-mode) 2176 | 2177 | (setq major-mode 'plsql-mode) 2178 | (setq mode-name "PL/SQL") 2179 | 2180 | (if plsql-uses-font-lock 2181 | (progn 2182 | (setq plsql-in-comment-predicate 'plsql-comment-face-p) 2183 | (setq plsql-in-string-predicate 'plsql-string-face-p)) 2184 | (setq plsql-in-comment-predicate 'plsql-in-comment-p) 2185 | (setq plsql-in-string-predicate 'plsql-in-string-p)) 2186 | 2187 | (plsql-imenu-setup) 2188 | 2189 | (set (make-local-variable 'indent-line-function) 'plsql-indent) 2190 | (set (make-local-variable 'indent-region-function) 'plsql-indent-region) 2191 | (set (make-local-variable 'align-mode-rules-list) 'plsql-align-rules-list) 2192 | (local-set-key [(return)] 'newline-and-indent) 2193 | (run-hooks 'plsql-mode-hook) 2194 | ) 2195 | 2196 | (provide 'plsql) 2197 | 2198 | ;;; plsql.el ends here 2199 | --------------------------------------------------------------------------------