diff options
| -rw-r--r-- | etc/NEWS | 3 | ||||
| -rw-r--r-- | lisp/progmodes/sh-script.el | 1454 |
2 files changed, 27 insertions, 1430 deletions
| @@ -86,6 +86,9 @@ shows equivalent key bindings for all commands that have them. | |||
| 86 | * Changes in Specialized Modes and Packages in Emacs 28.1 | 86 | * Changes in Specialized Modes and Packages in Emacs 28.1 |
| 87 | 87 | ||
| 88 | --- | 88 | --- |
| 89 | ** The old non-SMIE indentation of 'sh-mode' has been removed. | ||
| 90 | |||
| 91 | --- | ||
| 89 | ** The sb-image.el library is now marked obsolete. | 92 | ** The sb-image.el library is now marked obsolete. |
| 90 | This file was a compatibility kludge which is no longer needed. | 93 | This file was a compatibility kludge which is no longer needed. |
| 91 | 94 | ||
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index cc6d5b46ed2..a241a1e69ba 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el | |||
| @@ -64,61 +64,10 @@ | |||
| 64 | ;; * Indent right half sh-basic-offset | 64 | ;; * Indent right half sh-basic-offset |
| 65 | ;; / Indent left half sh-basic-offset. | 65 | ;; / Indent left half sh-basic-offset. |
| 66 | ;; | 66 | ;; |
| 67 | ;; There are 4 commands to help set the indentation variables: | ||
| 68 | ;; | ||
| 69 | ;; `sh-show-indent' | ||
| 70 | ;; This shows what variable controls the indentation of the current | ||
| 71 | ;; line and its value. | ||
| 72 | ;; | ||
| 73 | ;; `sh-set-indent' | ||
| 74 | ;; This allows you to set the value of the variable controlling the | ||
| 75 | ;; current line's indentation. You can enter a number or one of a | ||
| 76 | ;; number of special symbols to denote the value of sh-basic-offset, | ||
| 77 | ;; or its negative, or half it, or twice it, etc. If you've used | ||
| 78 | ;; cc-mode this should be familiar. If you forget which symbols are | ||
| 79 | ;; valid simply press C-h at the prompt. | ||
| 80 | ;; | ||
| 81 | ;; `sh-learn-line-indent' | ||
| 82 | ;; Simply make the line look the way you want it, then invoke this | ||
| 83 | ;; command. It will set the variable to the value that makes the line | ||
| 84 | ;; indent like that. If called with a prefix argument then it will set | ||
| 85 | ;; the value to one of the symbols if applicable. | ||
| 86 | ;; | ||
| 87 | ;; `sh-learn-buffer-indent' | ||
| 88 | ;; This is the deluxe function! It "learns" the whole buffer (use | ||
| 89 | ;; narrowing if you want it to process only part). It outputs to a | ||
| 90 | ;; buffer *indent* any conflicts it finds, and all the variables it has | ||
| 91 | ;; learned. This buffer is a sort of Occur mode buffer, allowing you to | ||
| 92 | ;; easily find where something was set. It is popped to automatically | ||
| 93 | ;; if there are any conflicts found or if `sh-popup-occur-buffer' is | ||
| 94 | ;; non-nil. | ||
| 95 | ;; `sh-indent-comment' will be set if all comments follow the same | ||
| 96 | ;; pattern; if they don't it will be set to nil. | ||
| 97 | ;; Whether `sh-basic-offset' is set is determined by variable | ||
| 98 | ;; `sh-learn-basic-offset'. | ||
| 99 | ;; | ||
| 100 | ;; Unfortunately, `sh-learn-buffer-indent' can take a long time to run | ||
| 101 | ;; (e.g. if there are large case statements). Perhaps it does not make | ||
| 102 | ;; sense to run it on large buffers: if lots of lines have different | ||
| 103 | ;; indentation styles it will produce a lot of diagnostics in the | ||
| 104 | ;; *indent* buffer; if there is a consistent style then running | ||
| 105 | ;; `sh-learn-buffer-indent' on a small region of the buffer should | ||
| 106 | ;; suffice. | ||
| 107 | ;; | ||
| 108 | ;; Saving indentation values | 67 | ;; Saving indentation values |
| 109 | ;; ------------------------- | 68 | ;; ------------------------- |
| 110 | ;; After you've learned the values in a buffer, how to you remember | 69 | ;; After you've learned the values in a buffer, how to you remember them? |
| 111 | ;; them? Originally I had hoped that `sh-learn-buffer-indent' | 70 | ;; There is a minimal way of being able to save indentation values and |
| 112 | ;; would make this unnecessary; simply learn the values when you visit | ||
| 113 | ;; the buffer. | ||
| 114 | ;; You can do this automatically like this: | ||
| 115 | ;; (add-hook 'sh-set-shell-hook #'sh-learn-buffer-indent) | ||
| 116 | ;; | ||
| 117 | ;; However... `sh-learn-buffer-indent' is extremely slow, | ||
| 118 | ;; especially on large-ish buffer. Also, if there are conflicts the | ||
| 119 | ;; "last one wins" which may not produce the desired setting. | ||
| 120 | ;; | ||
| 121 | ;; So...There is a minimal way of being able to save indentation values and | ||
| 122 | ;; to reload them in another buffer or at another point in time. | 71 | ;; to reload them in another buffer or at another point in time. |
| 123 | ;; | 72 | ;; |
| 124 | ;; Use `sh-name-style' to give a name to the indentation settings of | 73 | ;; Use `sh-name-style' to give a name to the indentation settings of |
| @@ -132,7 +81,7 @@ | |||
| 132 | ;; Indentation variables - buffer local or global? | 81 | ;; Indentation variables - buffer local or global? |
| 133 | ;; ---------------------------------------------- | 82 | ;; ---------------------------------------------- |
| 134 | ;; I think that often having them buffer-local makes sense, | 83 | ;; I think that often having them buffer-local makes sense, |
| 135 | ;; especially if one is using `sh-learn-buffer-indent'. However, if | 84 | ;; especially if one is using `smie-config-guess'. However, if |
| 136 | ;; a user sets values using customization, these changes won't appear | 85 | ;; a user sets values using customization, these changes won't appear |
| 137 | ;; to work if the variables are already local! | 86 | ;; to work if the variables are already local! |
| 138 | ;; | 87 | ;; |
| @@ -175,18 +124,10 @@ | |||
| 175 | ;; - Indenting many lines is slow. It currently does each line | 124 | ;; - Indenting many lines is slow. It currently does each line |
| 176 | ;; independently, rather than saving state information. | 125 | ;; independently, rather than saving state information. |
| 177 | ;; | 126 | ;; |
| 178 | ;; - `sh-learn-buffer-indent' is extremely slow. | ||
| 179 | ;; | ||
| 180 | ;; - "case $x in y) echo ;; esac)" the last ) is mis-identified as being | ||
| 181 | ;; part of a case-pattern. You need to add a semi-colon after "esac" to | ||
| 182 | ;; coerce sh-script into doing the right thing. | ||
| 183 | ;; | ||
| 184 | ;; - "echo $z in ps | head)" the last ) is mis-identified as being part of | 127 | ;; - "echo $z in ps | head)" the last ) is mis-identified as being part of |
| 185 | ;; a case-pattern. You need to put the "in" between quotes to coerce | 128 | ;; a case-pattern. You need to put the "in" between quotes to coerce |
| 186 | ;; sh-script into doing the right thing. | 129 | ;; sh-script into doing the right thing. |
| 187 | ;; | 130 | ;; |
| 188 | ;; - A line starting with "}>foo" is not indented like "} >foo". | ||
| 189 | ;; | ||
| 190 | ;; Richard Sharman <rsharman@pobox.com> June 1999. | 131 | ;; Richard Sharman <rsharman@pobox.com> June 1999. |
| 191 | 132 | ||
| 192 | ;;; Code: | 133 | ;;; Code: |
| @@ -474,10 +415,10 @@ This is buffer-local in every such buffer.") | |||
| 474 | (define-key map "\C-c\C-i" 'sh-if) | 415 | (define-key map "\C-c\C-i" 'sh-if) |
| 475 | (define-key map "\C-c\C-f" 'sh-for) | 416 | (define-key map "\C-c\C-f" 'sh-for) |
| 476 | (define-key map "\C-c\C-c" 'sh-case) | 417 | (define-key map "\C-c\C-c" 'sh-case) |
| 477 | (define-key map "\C-c?" 'sh-show-indent) | 418 | (define-key map "\C-c?" #'smie-config-show-indent) |
| 478 | (define-key map "\C-c=" 'sh-set-indent) | 419 | (define-key map "\C-c=" #'smie-config-set-indent) |
| 479 | (define-key map "\C-c<" 'sh-learn-line-indent) | 420 | (define-key map "\C-c<" #'smie-config-set-indent) |
| 480 | (define-key map "\C-c>" 'sh-learn-buffer-indent) | 421 | (define-key map "\C-c>" #'smie-config-guess) |
| 481 | (define-key map "\C-c\C-\\" 'sh-backslash-region) | 422 | (define-key map "\C-c\C-\\" 'sh-backslash-region) |
| 482 | 423 | ||
| 483 | (define-key map "\C-c+" 'sh-add) | 424 | (define-key map "\C-c+" 'sh-add) |
| @@ -493,17 +434,14 @@ This is buffer-local in every such buffer.") | |||
| 493 | (define-key map [remap backward-sentence] 'sh-beginning-of-command) | 434 | (define-key map [remap backward-sentence] 'sh-beginning-of-command) |
| 494 | (define-key map [remap forward-sentence] 'sh-end-of-command) | 435 | (define-key map [remap forward-sentence] 'sh-end-of-command) |
| 495 | (define-key map [menu-bar sh-script] (cons "Sh-Script" menu-map)) | 436 | (define-key map [menu-bar sh-script] (cons "Sh-Script" menu-map)) |
| 496 | (define-key menu-map [sh-learn-buffer-indent] | 437 | (define-key menu-map [smie-config-guess] |
| 497 | '(menu-item "Learn buffer indentation" sh-learn-buffer-indent | 438 | '(menu-item "Learn buffer indentation" smie-config-guess |
| 498 | :help "Learn how to indent the buffer the way it currently is.")) | 439 | :help "Learn how to indent the buffer the way it currently is.")) |
| 499 | (define-key menu-map [sh-learn-line-indent] | 440 | (define-key menu-map [smie-config-show-indent] |
| 500 | '(menu-item "Learn line indentation" sh-learn-line-indent | 441 | '(menu-item "Show indentation" smie-config-show-indent |
| 501 | :help "Learn how to indent a line as it currently is indented")) | ||
| 502 | (define-key menu-map [sh-show-indent] | ||
| 503 | '(menu-item "Show indentation" sh-show-indent | ||
| 504 | :help "Show the how the current line would be indented")) | 442 | :help "Show the how the current line would be indented")) |
| 505 | (define-key menu-map [sh-set-indent] | 443 | (define-key menu-map [smie-config-set-indent] |
| 506 | '(menu-item "Set indentation" sh-set-indent | 444 | '(menu-item "Set indentation" smie-config-set-indent |
| 507 | :help "Set the indentation for the current line")) | 445 | :help "Set the indentation for the current line")) |
| 508 | 446 | ||
| 509 | (define-key menu-map [sh-pair] | 447 | (define-key menu-map [sh-pair] |
| @@ -1196,20 +1134,8 @@ and command `sh-reset-indent-vars-to-global-values'." | |||
| 1196 | :options '(sh-electric-here-document-mode) | 1134 | :options '(sh-electric-here-document-mode) |
| 1197 | :group 'sh-script) | 1135 | :group 'sh-script) |
| 1198 | 1136 | ||
| 1199 | (defcustom sh-learn-basic-offset nil | ||
| 1200 | "When `sh-guess-basic-offset' should learn `sh-basic-offset'. | ||
| 1201 | |||
| 1202 | nil mean: never. | ||
| 1203 | t means: only if there seems to be an obvious value. | ||
| 1204 | Anything else means: whenever we have a \"good guess\" as to the value." | ||
| 1205 | :type '(choice | ||
| 1206 | (const :tag "Never" nil) | ||
| 1207 | (const :tag "Only if sure" t) | ||
| 1208 | (const :tag "If have a good guess" usually)) | ||
| 1209 | :group 'sh-indentation) | ||
| 1210 | |||
| 1211 | (defcustom sh-popup-occur-buffer nil | 1137 | (defcustom sh-popup-occur-buffer nil |
| 1212 | "Controls when `sh-learn-buffer-indent' pops the `*indent*' buffer. | 1138 | "Controls when `smie-config-guess' pops the `*indent*' buffer. |
| 1213 | If t it is always shown. If nil, it is shown only when there | 1139 | If t it is always shown. If nil, it is shown only when there |
| 1214 | are conflicts." | 1140 | are conflicts." |
| 1215 | :type '(choice | 1141 | :type '(choice |
| @@ -1217,14 +1143,6 @@ are conflicts." | |||
| 1217 | (const :tag "Always" t)) | 1143 | (const :tag "Always" t)) |
| 1218 | :group 'sh-indentation) | 1144 | :group 'sh-indentation) |
| 1219 | 1145 | ||
| 1220 | (defcustom sh-blink t | ||
| 1221 | "If non-nil, `sh-show-indent' shows the line indentation is relative to. | ||
| 1222 | The position on the line is not necessarily meaningful. | ||
| 1223 | In some cases the line will be the matching keyword, but this is not | ||
| 1224 | always the case." | ||
| 1225 | :type 'boolean | ||
| 1226 | :group 'sh-indentation) | ||
| 1227 | |||
| 1228 | (defcustom sh-first-lines-indent 0 | 1146 | (defcustom sh-first-lines-indent 0 |
| 1229 | "The indentation of the first non-blank non-comment line. | 1147 | "The indentation of the first non-blank non-comment line. |
| 1230 | Usually 0 meaning first column. | 1148 | Usually 0 meaning first column. |
| @@ -1567,11 +1485,9 @@ following commands are available, based on the current shell's syntax: | |||
| 1567 | \\[sh-while] while loop | 1485 | \\[sh-while] while loop |
| 1568 | 1486 | ||
| 1569 | For sh and rc shells indentation commands are: | 1487 | For sh and rc shells indentation commands are: |
| 1570 | \\[sh-show-indent] Show the variable controlling this line's indentation. | 1488 | \\[smie-config-show-indent] Show the rules controlling this line's indentation. |
| 1571 | \\[sh-set-indent] Set then variable controlling this line's indentation. | 1489 | \\[smie-config-set-indent] Change the rules controlling this line's indentation. |
| 1572 | \\[sh-learn-line-indent] Change the indentation variable so this line | 1490 | \\[smie-config-guess] Try to tweak the indentation rules so the |
| 1573 | would indent to the way it currently is. | ||
| 1574 | \\[sh-learn-buffer-indent] Set the indentation variables so the | ||
| 1575 | buffer indents as it currently is indented. | 1491 | buffer indents as it currently is indented. |
| 1576 | 1492 | ||
| 1577 | 1493 | ||
| @@ -1738,13 +1654,6 @@ This adds rules for comments and assignments." | |||
| 1738 | 1654 | ||
| 1739 | (require 'smie) | 1655 | (require 'smie) |
| 1740 | 1656 | ||
| 1741 | ;; The SMIE code should generally be preferred, but it currently does not obey | ||
| 1742 | ;; the various indentation custom-vars, and it misses some important features | ||
| 1743 | ;; of the old code, mostly: sh-learn-line/buffer-indent, sh-show-indent, | ||
| 1744 | ;; sh-name/save/load-style. | ||
| 1745 | (defvar sh-use-smie t | ||
| 1746 | "Whether to use the SMIE code for navigation and indentation.") | ||
| 1747 | |||
| 1748 | (defun sh-smie--keyword-p () | 1657 | (defun sh-smie--keyword-p () |
| 1749 | "Non-nil if we're at a keyword position. | 1658 | "Non-nil if we're at a keyword position. |
| 1750 | A keyword position is one where if we're looking at something that looks | 1659 | A keyword position is one where if we're looking at something that looks |
| @@ -2279,60 +2188,6 @@ Point should be before the newline." | |||
| 2279 | (defvar sh-regexp-for-done nil | 2188 | (defvar sh-regexp-for-done nil |
| 2280 | "A buffer-local regexp to match opening keyword for done.") | 2189 | "A buffer-local regexp to match opening keyword for done.") |
| 2281 | 2190 | ||
| 2282 | (defvar sh-kw-alist nil | ||
| 2283 | "A buffer-local, since it is shell-type dependent, list of keywords.") | ||
| 2284 | |||
| 2285 | ;; ( key-word first-on-this on-prev-line ) | ||
| 2286 | ;; This is used to set `sh-kw-alist' which is a list of sublists each | ||
| 2287 | ;; having 3 elements: | ||
| 2288 | ;; a keyword | ||
| 2289 | ;; a rule to check when the keyword appears on "this" line | ||
| 2290 | ;; a rule to check when the keyword appears on "the previous" line | ||
| 2291 | ;; The keyword is usually a string and is the first word on a line. | ||
| 2292 | ;; If this keyword appears on the line whose indentation is to be | ||
| 2293 | ;; calculated, the rule in element 2 is called. If this returns | ||
| 2294 | ;; non-zero, the resulting point (which may be changed by the rule) | ||
| 2295 | ;; is used as the default indentation. | ||
| 2296 | ;; If it returned false or the keyword was not found in the table, | ||
| 2297 | ;; then the keyword from the previous line is looked up and the rule | ||
| 2298 | ;; in element 3 is called. In this case, however, | ||
| 2299 | ;; `sh-get-indent-info' does not stop but may keep going and test | ||
| 2300 | ;; other keywords against rules in element 3. This is because the | ||
| 2301 | ;; preceding line could have, for example, an opening "if" and an | ||
| 2302 | ;; opening "while" keyword and we need to add the indentation offsets | ||
| 2303 | ;; for both. | ||
| 2304 | ;; | ||
| 2305 | (defconst sh-kw | ||
| 2306 | '((sh | ||
| 2307 | ("if" nil sh-handle-prev-if) | ||
| 2308 | ("elif" sh-handle-this-else sh-handle-prev-else) | ||
| 2309 | ("else" sh-handle-this-else sh-handle-prev-else) | ||
| 2310 | ("fi" sh-handle-this-fi sh-handle-prev-fi) | ||
| 2311 | ("then" sh-handle-this-then sh-handle-prev-then) | ||
| 2312 | ("(" nil sh-handle-prev-open) | ||
| 2313 | ("{" nil sh-handle-prev-open) | ||
| 2314 | ("[" nil sh-handle-prev-open) | ||
| 2315 | ("}" sh-handle-this-close nil) | ||
| 2316 | (")" sh-handle-this-close nil) | ||
| 2317 | ("]" sh-handle-this-close nil) | ||
| 2318 | ("case" nil sh-handle-prev-case) | ||
| 2319 | ("esac" sh-handle-this-esac sh-handle-prev-esac) | ||
| 2320 | (case-label nil sh-handle-after-case-label) ;; ??? | ||
| 2321 | (";;" nil sh-handle-prev-case-alt-end) ;; ??? | ||
| 2322 | (";;&" nil sh-handle-prev-case-alt-end) ;Like ";;" with diff semantics. | ||
| 2323 | (";&" nil sh-handle-prev-case-alt-end) ;Like ";;" with diff semantics. | ||
| 2324 | ("done" sh-handle-this-done sh-handle-prev-done) | ||
| 2325 | ("do" sh-handle-this-do sh-handle-prev-do)) | ||
| 2326 | |||
| 2327 | ;; Note: we don't need specific stuff for bash and zsh shells; | ||
| 2328 | ;; the regexp `sh-regexp-for-done' handles the extra keywords | ||
| 2329 | ;; these shells use. | ||
| 2330 | (rc | ||
| 2331 | ("{" nil sh-handle-prev-open) | ||
| 2332 | ("}" sh-handle-this-close nil) | ||
| 2333 | ("case" sh-handle-this-rc-case sh-handle-prev-rc-case)))) | ||
| 2334 | |||
| 2335 | |||
| 2336 | 2191 | ||
| 2337 | (defun sh-set-shell (shell &optional no-query-flag insert-flag) | 2192 | (defun sh-set-shell (shell &optional no-query-flag insert-flag) |
| 2338 | "Set this buffer's shell to SHELL (a string). | 2193 | "Set this buffer's shell to SHELL (a string). |
| @@ -2400,16 +2255,6 @@ whose value is the shell name (don't quote it)." | |||
| 2400 | (funcall mksym "rules") | 2255 | (funcall mksym "rules") |
| 2401 | :forward-token (funcall mksym "forward-token") | 2256 | :forward-token (funcall mksym "forward-token") |
| 2402 | :backward-token (funcall mksym "backward-token"))) | 2257 | :backward-token (funcall mksym "backward-token"))) |
| 2403 | (unless sh-use-smie | ||
| 2404 | (setq-local sh-kw-alist (sh-feature sh-kw)) | ||
| 2405 | (let ((regexp (sh-feature sh-kws-for-done))) | ||
| 2406 | (if regexp | ||
| 2407 | (setq-local sh-regexp-for-done | ||
| 2408 | (sh-mkword-regexpr (regexp-opt regexp t))))) | ||
| 2409 | (message "setting up indent stuff") | ||
| 2410 | ;; sh-mode has already made indent-line-function local | ||
| 2411 | ;; but do it in case this is called before that. | ||
| 2412 | (setq-local indent-line-function #'sh-indent-line)) | ||
| 2413 | (if sh-make-vars-local | 2258 | (if sh-make-vars-local |
| 2414 | (sh-make-vars-local)) | 2259 | (sh-make-vars-local)) |
| 2415 | (message "Indentation setup for shell type %s" sh-shell)) | 2260 | (message "Indentation setup for shell type %s" sh-shell)) |
| @@ -2564,11 +2409,6 @@ region, clear header." | |||
| 2564 | (eq -1 (% (save-excursion (skip-chars-backward "\\\\")) 2))) | 2409 | (eq -1 (% (save-excursion (skip-chars-backward "\\\\")) 2))) |
| 2565 | 2410 | ||
| 2566 | ;; Indentation stuff. | 2411 | ;; Indentation stuff. |
| 2567 | (defun sh-must-support-indent () | ||
| 2568 | "Signal an error if the shell type for this buffer is not supported. | ||
| 2569 | Also, the buffer must be in Shell-script mode." | ||
| 2570 | (unless sh-indent-supported-here | ||
| 2571 | (error "This buffer's shell does not support indentation through Emacs"))) | ||
| 2572 | 2412 | ||
| 2573 | (defun sh-make-vars-local () | 2413 | (defun sh-make-vars-local () |
| 2574 | "Make the indentation variables local to this buffer. | 2414 | "Make the indentation variables local to this buffer. |
| @@ -2589,654 +2429,12 @@ Then, if variable `sh-make-vars-local' is non-nil, make them local." | |||
| 2589 | (if sh-make-vars-local | 2429 | (if sh-make-vars-local |
| 2590 | (mapcar 'make-local-variable sh-var-list))) | 2430 | (mapcar 'make-local-variable sh-var-list))) |
| 2591 | 2431 | ||
| 2592 | |||
| 2593 | ;; Theoretically these are only needed in shell and derived modes. | ||
| 2594 | ;; However, the routines which use them are only called in those modes. | ||
| 2595 | (defconst sh-special-keywords "then\\|do") | ||
| 2596 | |||
| 2597 | (defun sh-help-string-for-variable (var) | ||
| 2598 | "Construct a string for `sh-read-variable' when changing variable VAR ." | ||
| 2599 | (let ((msg (documentation-property var 'variable-documentation)) | ||
| 2600 | (msg2 "")) | ||
| 2601 | (unless (memq var '(sh-first-lines-indent sh-indent-comment)) | ||
| 2602 | (setq msg2 | ||
| 2603 | (format "\n | ||
| 2604 | You can enter a number (positive to increase indentation, | ||
| 2605 | negative to decrease indentation, zero for no change to indentation). | ||
| 2606 | |||
| 2607 | Or, you can enter one of the following symbols which are relative to | ||
| 2608 | the value of variable `sh-basic-offset' | ||
| 2609 | which in this buffer is currently %s. | ||
| 2610 | |||
| 2611 | \t%s." | ||
| 2612 | sh-basic-offset | ||
| 2613 | (mapconcat (lambda (x) | ||
| 2614 | (nth (1- (length x)) x)) | ||
| 2615 | sh-symbol-list "\n\t")))) | ||
| 2616 | (concat | ||
| 2617 | ;; The following shows the global not the local value! | ||
| 2618 | ;; (format "Current value of %s is %s\n\n" var (symbol-value var)) | ||
| 2619 | msg msg2))) | ||
| 2620 | |||
| 2621 | (defun sh-read-variable (var) | ||
| 2622 | "Read a new value for indentation variable VAR." | ||
| 2623 | (let ((minibuffer-help-form `(sh-help-string-for-variable | ||
| 2624 | (quote ,var))) | ||
| 2625 | val) | ||
| 2626 | (setq val (read-from-minibuffer | ||
| 2627 | (format "New value for %s (press %s for help): " | ||
| 2628 | var (single-key-description help-char)) | ||
| 2629 | (format "%s" (symbol-value var)) | ||
| 2630 | nil t)) | ||
| 2631 | val)) | ||
| 2632 | |||
| 2633 | |||
| 2634 | |||
| 2635 | (defun sh-in-comment-or-string (start) | 2432 | (defun sh-in-comment-or-string (start) |
| 2636 | "Return non-nil if START is in a comment or string." | 2433 | "Return non-nil if START is in a comment or string." |
| 2637 | (save-excursion | 2434 | (save-excursion |
| 2638 | (let ((state (syntax-ppss start))) | 2435 | (let ((state (syntax-ppss start))) |
| 2639 | (or (nth 3 state) (nth 4 state))))) | 2436 | (or (nth 3 state) (nth 4 state))))) |
| 2640 | 2437 | ||
| 2641 | (defun sh-goto-matching-if () | ||
| 2642 | "Go to the matching if for a fi. | ||
| 2643 | This handles nested if..fi pairs." | ||
| 2644 | (let ((found (sh-find-prev-matching "\\bif\\b" "\\bfi\\b" 1))) | ||
| 2645 | (if found | ||
| 2646 | (goto-char found)))) | ||
| 2647 | |||
| 2648 | |||
| 2649 | ;; Functions named sh-handle-this-XXX are called when the keyword on the | ||
| 2650 | ;; line whose indentation is being handled contain XXX; | ||
| 2651 | ;; those named sh-handle-prev-XXX are when XXX appears on the previous line. | ||
| 2652 | |||
| 2653 | (defun sh-handle-prev-if () | ||
| 2654 | (list '(+ sh-indent-after-if))) | ||
| 2655 | |||
| 2656 | (defun sh-handle-this-else () | ||
| 2657 | (if (sh-goto-matching-if) | ||
| 2658 | ;; (list "aligned to if") | ||
| 2659 | (list "aligned to if" '(+ sh-indent-for-else)) | ||
| 2660 | nil | ||
| 2661 | )) | ||
| 2662 | |||
| 2663 | (defun sh-handle-prev-else () | ||
| 2664 | (if (sh-goto-matching-if) | ||
| 2665 | (list '(+ sh-indent-after-if)) | ||
| 2666 | )) | ||
| 2667 | |||
| 2668 | (defun sh-handle-this-fi () | ||
| 2669 | (if (sh-goto-matching-if) | ||
| 2670 | (list "aligned to if" '(+ sh-indent-for-fi)) | ||
| 2671 | nil | ||
| 2672 | )) | ||
| 2673 | |||
| 2674 | (defun sh-handle-prev-fi () | ||
| 2675 | ;; Why do we have this rule? Because we must go back to the if | ||
| 2676 | ;; to get its indent. We may continue back from there. | ||
| 2677 | ;; We return nil because we don't have anything to add to result, | ||
| 2678 | ;; the side affect of setting align-point is all that matters. | ||
| 2679 | ;; we could return a comment (a string) but I can't think of a good one... | ||
| 2680 | (sh-goto-matching-if) | ||
| 2681 | nil) | ||
| 2682 | |||
| 2683 | (defun sh-handle-this-then () | ||
| 2684 | (let ((p (sh-goto-matching-if))) | ||
| 2685 | (if p | ||
| 2686 | (list '(+ sh-indent-for-then)) | ||
| 2687 | ))) | ||
| 2688 | |||
| 2689 | (defun sh-handle-prev-then () | ||
| 2690 | (let ((p (sh-goto-matching-if))) | ||
| 2691 | (if p | ||
| 2692 | (list '(+ sh-indent-after-if)) | ||
| 2693 | ))) | ||
| 2694 | |||
| 2695 | (defun sh-handle-prev-open () | ||
| 2696 | (save-excursion | ||
| 2697 | (let ((x (sh-prev-stmt))) | ||
| 2698 | (if (and x | ||
| 2699 | (progn | ||
| 2700 | (goto-char x) | ||
| 2701 | (or | ||
| 2702 | (looking-at "function\\b") | ||
| 2703 | (looking-at "\\s-*\\S-+\\s-*()") | ||
| 2704 | ))) | ||
| 2705 | (list '(+ sh-indent-after-function)) | ||
| 2706 | (list '(+ sh-indent-after-open))) | ||
| 2707 | ))) | ||
| 2708 | |||
| 2709 | (defun sh-handle-this-close () | ||
| 2710 | (forward-char 1) ;; move over ")" | ||
| 2711 | (if (sh-safe-forward-sexp -1) | ||
| 2712 | (list "aligned to opening paren"))) | ||
| 2713 | |||
| 2714 | (defun sh-goto-matching-case () | ||
| 2715 | (let ((found (sh-find-prev-matching "\\bcase\\b" "\\besac\\b" 1))) | ||
| 2716 | (if found (goto-char found)))) | ||
| 2717 | |||
| 2718 | (defun sh-handle-prev-case () | ||
| 2719 | ;; This is typically called when point is on same line as a case | ||
| 2720 | ;; we shouldn't -- and can't find prev-case | ||
| 2721 | (if (looking-at ".*\\<case\\>") | ||
| 2722 | (list '(+ sh-indent-for-case-label)) | ||
| 2723 | (error "We don't seem to be on a line with a case"))) ;; debug | ||
| 2724 | |||
| 2725 | (defun sh-handle-this-esac () | ||
| 2726 | (if (sh-goto-matching-case) | ||
| 2727 | (list "aligned to matching case"))) | ||
| 2728 | |||
| 2729 | (defun sh-handle-prev-esac () | ||
| 2730 | (if (sh-goto-matching-case) | ||
| 2731 | (list "matching case"))) | ||
| 2732 | |||
| 2733 | (defun sh-handle-after-case-label () | ||
| 2734 | (if (sh-goto-matching-case) | ||
| 2735 | (list '(+ sh-indent-for-case-alt)))) | ||
| 2736 | |||
| 2737 | (defun sh-handle-prev-case-alt-end () | ||
| 2738 | (if (sh-goto-matching-case) | ||
| 2739 | (list '(+ sh-indent-for-case-label)))) | ||
| 2740 | |||
| 2741 | (defun sh-safe-forward-sexp (&optional arg) | ||
| 2742 | "Try and do a `forward-sexp', but do not error. | ||
| 2743 | Return new point if successful, nil if an error occurred." | ||
| 2744 | (condition-case nil | ||
| 2745 | (progn | ||
| 2746 | (forward-sexp (or arg 1)) | ||
| 2747 | (point)) ;; return point if successful | ||
| 2748 | (error | ||
| 2749 | (sh-debug "oops!(1) %d" (point)) | ||
| 2750 | nil))) ;; return nil if fail | ||
| 2751 | |||
| 2752 | (defun sh-goto-match-for-done () | ||
| 2753 | (let ((found (sh-find-prev-matching sh-regexp-for-done sh-re-done 1))) | ||
| 2754 | (if found | ||
| 2755 | (goto-char found)))) | ||
| 2756 | |||
| 2757 | (defun sh-handle-this-done () | ||
| 2758 | (if (sh-goto-match-for-done) | ||
| 2759 | (list "aligned to do stmt" '(+ sh-indent-for-done)))) | ||
| 2760 | |||
| 2761 | (defun sh-handle-prev-done () | ||
| 2762 | (if (sh-goto-match-for-done) | ||
| 2763 | (list "previous done"))) | ||
| 2764 | |||
| 2765 | (defun sh-handle-this-do () | ||
| 2766 | (if (sh-goto-match-for-done) | ||
| 2767 | (list '(+ sh-indent-for-do)))) | ||
| 2768 | |||
| 2769 | (defun sh-handle-prev-do () | ||
| 2770 | (cond | ||
| 2771 | ((save-restriction | ||
| 2772 | (narrow-to-region (point) (line-beginning-position)) | ||
| 2773 | (sh-goto-match-for-done)) | ||
| 2774 | (sh-debug "match for done found on THIS line") | ||
| 2775 | (list '(+ sh-indent-after-loop-construct))) | ||
| 2776 | ((sh-goto-match-for-done) | ||
| 2777 | (sh-debug "match for done found on PREV line") | ||
| 2778 | (list '(+ sh-indent-after-do))) | ||
| 2779 | (t | ||
| 2780 | (message "match for done NOT found") | ||
| 2781 | nil))) | ||
| 2782 | |||
| 2783 | ;; for rc: | ||
| 2784 | (defun sh-find-prev-switch () | ||
| 2785 | "Find the line for the switch keyword matching this line's case keyword." | ||
| 2786 | (re-search-backward "\\<switch\\>" nil t)) | ||
| 2787 | |||
| 2788 | (defun sh-handle-this-rc-case () | ||
| 2789 | (if (sh-find-prev-switch) | ||
| 2790 | (list '(+ sh-indent-after-switch)) | ||
| 2791 | ;; (list '(+ sh-indent-for-case-label)) | ||
| 2792 | nil)) | ||
| 2793 | |||
| 2794 | (defun sh-handle-prev-rc-case () | ||
| 2795 | (list '(+ sh-indent-after-case))) | ||
| 2796 | |||
| 2797 | (defun sh-check-rule (n thing) | ||
| 2798 | (let ((rule (nth n (assoc thing sh-kw-alist))) | ||
| 2799 | (val nil)) | ||
| 2800 | (if rule | ||
| 2801 | (progn | ||
| 2802 | (setq val (funcall rule)) | ||
| 2803 | (sh-debug "rule (%d) for %s at %d is %s\n-> returned %s" | ||
| 2804 | n thing (point) rule val))) | ||
| 2805 | val)) | ||
| 2806 | |||
| 2807 | |||
| 2808 | (defun sh-get-indent-info () | ||
| 2809 | "Return indent-info for this line. | ||
| 2810 | This is a list. nil means the line is to be left as is. | ||
| 2811 | Otherwise it contains one or more of the following sublists: | ||
| 2812 | \(t NUMBER) NUMBER is the base location in the buffer that indentation is | ||
| 2813 | relative to. If present, this is always the first of the | ||
| 2814 | sublists. The indentation of the line in question is | ||
| 2815 | derived from the indentation of this point, possibly | ||
| 2816 | modified by subsequent sublists. | ||
| 2817 | \(+ VAR) | ||
| 2818 | \(- VAR) Get the value of variable VAR and add to or subtract from | ||
| 2819 | the indentation calculated so far. | ||
| 2820 | \(= VAR) Get the value of variable VAR and *replace* the | ||
| 2821 | indentation with its value. This only occurs for | ||
| 2822 | special variables such as `sh-indent-comment'. | ||
| 2823 | STRING This is ignored for the purposes of calculating | ||
| 2824 | indentation, it is printed in certain cases to help show | ||
| 2825 | what the indentation is based on." | ||
| 2826 | ;; See comments before `sh-kw'. | ||
| 2827 | (save-excursion | ||
| 2828 | (let ((have-result nil) | ||
| 2829 | this-kw | ||
| 2830 | val | ||
| 2831 | (result nil) | ||
| 2832 | (align-point nil) | ||
| 2833 | prev-line-end x) | ||
| 2834 | (beginning-of-line) | ||
| 2835 | ;; Note: setting result to t means we are done and will return nil. | ||
| 2836 | ;;(This function never returns just t.) | ||
| 2837 | (cond | ||
| 2838 | ((or (nth 3 (syntax-ppss (point))) | ||
| 2839 | (eq (get-text-property (point) 'face) 'sh-heredoc)) | ||
| 2840 | ;; String continuation -- don't indent | ||
| 2841 | (setq result t) | ||
| 2842 | (setq have-result t)) | ||
| 2843 | ((looking-at "\\s-*#") ; was (equal this-kw "#") | ||
| 2844 | (if (bobp) | ||
| 2845 | (setq result t) ;; return nil if 1st line! | ||
| 2846 | (setq result (list '(= sh-indent-comment))) | ||
| 2847 | ;; we still need to get previous line in case | ||
| 2848 | ;; sh-indent-comment is t (indent as normal) | ||
| 2849 | (setq align-point (sh-prev-line nil)) | ||
| 2850 | (setq have-result nil) | ||
| 2851 | )) | ||
| 2852 | ) ;; cond | ||
| 2853 | |||
| 2854 | (unless have-result | ||
| 2855 | ;; Continuation lines are handled specially | ||
| 2856 | (if (sh-this-is-a-continuation) | ||
| 2857 | (progn | ||
| 2858 | (setq result | ||
| 2859 | (if (save-excursion | ||
| 2860 | (beginning-of-line) | ||
| 2861 | (not (memq (char-before (- (point) 2)) '(?\s ?\t)))) | ||
| 2862 | ;; By convention, if the continuation \ is not | ||
| 2863 | ;; preceded by a SPC or a TAB it means that the line | ||
| 2864 | ;; is cut at a place where spaces cannot be freely | ||
| 2865 | ;; added/removed. I.e. do not indent the line. | ||
| 2866 | (list '(= nil)) | ||
| 2867 | ;; We assume the line being continued is already | ||
| 2868 | ;; properly indented... | ||
| 2869 | ;; (setq prev-line-end (sh-prev-line)) | ||
| 2870 | (setq align-point (sh-prev-line nil)) | ||
| 2871 | (list '(+ sh-indent-for-continuation)))) | ||
| 2872 | (setq have-result t)) | ||
| 2873 | (beginning-of-line) | ||
| 2874 | (skip-chars-forward " \t") | ||
| 2875 | (setq this-kw (sh-get-kw))) | ||
| 2876 | |||
| 2877 | ;; Handle "this" keyword: first word on the line we're | ||
| 2878 | ;; calculating indentation info for. | ||
| 2879 | (if this-kw | ||
| 2880 | (if (setq val (sh-check-rule 1 this-kw)) | ||
| 2881 | (progn | ||
| 2882 | (setq align-point (point)) | ||
| 2883 | (sh-debug | ||
| 2884 | "this - setting align-point to %d" align-point) | ||
| 2885 | (setq result (append result val)) | ||
| 2886 | (setq have-result t) | ||
| 2887 | ;; set prev-line to continue processing remainder | ||
| 2888 | ;; of this line as a previous line | ||
| 2889 | (setq prev-line-end (point)) | ||
| 2890 | )))) | ||
| 2891 | |||
| 2892 | (unless have-result | ||
| 2893 | (setq prev-line-end (sh-prev-line 'end))) | ||
| 2894 | |||
| 2895 | (if prev-line-end | ||
| 2896 | (save-excursion | ||
| 2897 | ;; We start off at beginning of this line. | ||
| 2898 | ;; Scan previous statements while this is <= | ||
| 2899 | ;; start of previous line. | ||
| 2900 | (goto-char prev-line-end) | ||
| 2901 | (setq x t) | ||
| 2902 | (while (and x (setq x (sh-prev-thing))) | ||
| 2903 | (sh-debug "at %d x is: %s result is: %s" (point) x result) | ||
| 2904 | (cond | ||
| 2905 | ((and (equal x ")") | ||
| 2906 | (equal (get-text-property (1- (point)) 'syntax-table) | ||
| 2907 | sh-st-punc)) | ||
| 2908 | (sh-debug "Case label) here") | ||
| 2909 | (setq x 'case-label) | ||
| 2910 | (if (setq val (sh-check-rule 2 x)) | ||
| 2911 | (progn | ||
| 2912 | (setq result (append result val)) | ||
| 2913 | (setq align-point (point)))) | ||
| 2914 | (or (bobp) | ||
| 2915 | (forward-char -1)) | ||
| 2916 | (skip-chars-forward "*0-9?[]a-z") | ||
| 2917 | ) | ||
| 2918 | ((string-match "[])}]" x) | ||
| 2919 | (setq x (sh-safe-forward-sexp -1)) | ||
| 2920 | (if x | ||
| 2921 | (progn | ||
| 2922 | (setq align-point (point)) | ||
| 2923 | (setq result (append result | ||
| 2924 | (list "aligned to opening paren"))) | ||
| 2925 | ))) | ||
| 2926 | ((string-match "[[({]" x) | ||
| 2927 | (sh-debug "Checking special thing: %s" x) | ||
| 2928 | (if (setq val (sh-check-rule 2 x)) | ||
| 2929 | (setq result (append result val))) | ||
| 2930 | (forward-char -1) | ||
| 2931 | (setq align-point (point))) | ||
| 2932 | ((string-match "[\"'`]" x) | ||
| 2933 | (sh-debug "Skipping back for %s" x) | ||
| 2934 | ;; this was oops-2 | ||
| 2935 | (setq x (sh-safe-forward-sexp -1))) | ||
| 2936 | ((stringp x) | ||
| 2937 | (sh-debug "Checking string %s at %s" x (point)) | ||
| 2938 | (if (setq val (sh-check-rule 2 x)) | ||
| 2939 | ;; (or (eq t (car val)) | ||
| 2940 | ;; (eq t (car (car val)))) | ||
| 2941 | (setq result (append result val))) | ||
| 2942 | ;; not sure about this test Wed Jan 27 23:48:35 1999 | ||
| 2943 | (setq align-point (point)) | ||
| 2944 | (unless (bolp) | ||
| 2945 | (forward-char -1))) | ||
| 2946 | (t | ||
| 2947 | (error "Don't know what to do with %s" x)) | ||
| 2948 | ) | ||
| 2949 | ) ;; while | ||
| 2950 | (sh-debug "result is %s" result) | ||
| 2951 | ) | ||
| 2952 | (sh-debug "No prev line!") | ||
| 2953 | (sh-debug "result: %s align-point: %s" result align-point) | ||
| 2954 | ) | ||
| 2955 | |||
| 2956 | (if align-point | ||
| 2957 | ;; was: (setq result (append result (list (list t align-point)))) | ||
| 2958 | (setq result (append (list (list t align-point)) result)) | ||
| 2959 | ) | ||
| 2960 | (sh-debug "result is now: %s" result) | ||
| 2961 | |||
| 2962 | (or result | ||
| 2963 | (setq result (list (if prev-line-end | ||
| 2964 | (list t prev-line-end) | ||
| 2965 | (list '= 'sh-first-lines-indent))))) | ||
| 2966 | |||
| 2967 | (if (eq result t) | ||
| 2968 | (setq result nil)) | ||
| 2969 | (sh-debug "result is: %s" result) | ||
| 2970 | result | ||
| 2971 | ) ;; let | ||
| 2972 | )) | ||
| 2973 | |||
| 2974 | |||
| 2975 | (defun sh-get-indent-var-for-line (&optional info) | ||
| 2976 | "Return the variable controlling indentation for this line. | ||
| 2977 | If there is not [just] one such variable, return a string | ||
| 2978 | indicating the problem. | ||
| 2979 | If INFO is supplied it is used, else it is calculated." | ||
| 2980 | (let ((var nil) | ||
| 2981 | (result nil) | ||
| 2982 | (reason nil) | ||
| 2983 | sym elt) | ||
| 2984 | (or info | ||
| 2985 | (setq info (sh-get-indent-info))) | ||
| 2986 | (if (null info) | ||
| 2987 | (setq result "this line to be left as is") | ||
| 2988 | (while (and info (null result)) | ||
| 2989 | (setq elt (car info)) | ||
| 2990 | (cond | ||
| 2991 | ((stringp elt) | ||
| 2992 | (setq reason elt) | ||
| 2993 | ) | ||
| 2994 | ((not (listp elt)) | ||
| 2995 | (error "sh-get-indent-var-for-line invalid elt: %s" elt)) | ||
| 2996 | ;; so it is a list | ||
| 2997 | ((eq t (car elt)) | ||
| 2998 | ) ;; nothing | ||
| 2999 | ((symbolp (setq sym (nth 1 elt))) | ||
| 3000 | ;; A bit of a kludge - when we see the sh-indent-comment | ||
| 3001 | ;; ignore other variables. Otherwise it is tricky to | ||
| 3002 | ;; "learn" the comment indentation. | ||
| 3003 | (if (eq var 'sh-indent-comment) | ||
| 3004 | (setq result var) | ||
| 3005 | (if var | ||
| 3006 | (setq result | ||
| 3007 | "this line is controlled by more than 1 variable.") | ||
| 3008 | (setq var sym)))) | ||
| 3009 | (t | ||
| 3010 | (error "sh-get-indent-var-for-line invalid list elt: %s" elt))) | ||
| 3011 | (setq info (cdr info)) | ||
| 3012 | )) | ||
| 3013 | (or result | ||
| 3014 | (setq result var)) | ||
| 3015 | (or result | ||
| 3016 | (setq result reason)) | ||
| 3017 | (if (null result) | ||
| 3018 | ;; e.g. just had (t POS) | ||
| 3019 | (setq result "line has default indentation")) | ||
| 3020 | result)) | ||
| 3021 | |||
| 3022 | |||
| 3023 | |||
| 3024 | ;; Finding the previous line isn't trivial. | ||
| 3025 | ;; We must *always* go back one more and see if that is a continuation | ||
| 3026 | ;; line -- it is the PREVIOUS line which is continued, not the one | ||
| 3027 | ;; we are going to! | ||
| 3028 | ;; Also, we want to treat a whole "here document" as one big line, | ||
| 3029 | ;; because we may want to align to the beginning of it. | ||
| 3030 | ;; | ||
| 3031 | ;; What we do: | ||
| 3032 | ;; - go back to previous non-empty line | ||
| 3033 | ;; - if this is in a here-document, go to the beginning of it | ||
| 3034 | ;; - while previous line is continued, go back one line | ||
| 3035 | (defun sh-prev-line (&optional end) | ||
| 3036 | "Back to end of previous non-comment non-empty line. | ||
| 3037 | Go to beginning of logical line unless END is non-nil, in which case | ||
| 3038 | we go to the end of the previous line and do not check for continuations." | ||
| 3039 | (save-excursion | ||
| 3040 | (beginning-of-line) | ||
| 3041 | (forward-comment (- (point-max))) | ||
| 3042 | (unless end (beginning-of-line)) | ||
| 3043 | (when (and (not (bobp)) | ||
| 3044 | (eq (get-text-property (1- (point)) 'face) 'sh-heredoc)) | ||
| 3045 | (let ((p1 (previous-single-property-change (1- (point)) 'face))) | ||
| 3046 | (when p1 | ||
| 3047 | (goto-char p1) | ||
| 3048 | (if end | ||
| 3049 | (end-of-line) | ||
| 3050 | (beginning-of-line))))) | ||
| 3051 | (unless end | ||
| 3052 | ;; we must check previous lines to see if they are continuation lines | ||
| 3053 | ;; if so, we must return position of first of them | ||
| 3054 | (while (and (sh-this-is-a-continuation) | ||
| 3055 | (>= 0 (forward-line -1)))) | ||
| 3056 | (beginning-of-line) | ||
| 3057 | (skip-chars-forward " \t")) | ||
| 3058 | (point))) | ||
| 3059 | |||
| 3060 | |||
| 3061 | (defun sh-prev-stmt () | ||
| 3062 | "Return the address of the previous stmt or nil." | ||
| 3063 | ;; This is used when we are trying to find a matching keyword. | ||
| 3064 | ;; Searching backward for the keyword would certainly be quicker, but | ||
| 3065 | ;; it is hard to remove "false matches" -- such as if the keyword | ||
| 3066 | ;; appears in a string or quote. This way is slower, but (I think) safer. | ||
| 3067 | (interactive) | ||
| 3068 | (save-excursion | ||
| 3069 | (let ((going t) | ||
| 3070 | (start (point)) | ||
| 3071 | (found nil) | ||
| 3072 | (prev nil)) | ||
| 3073 | (skip-chars-backward " \t;|&({[") | ||
| 3074 | (while (and (not found) | ||
| 3075 | (not (bobp)) | ||
| 3076 | going) | ||
| 3077 | ;; Do a backward-sexp if possible, else backup bit by bit... | ||
| 3078 | (if (sh-safe-forward-sexp -1) | ||
| 3079 | (progn | ||
| 3080 | (if (looking-at sh-special-keywords) | ||
| 3081 | (progn | ||
| 3082 | (setq found prev)) | ||
| 3083 | (setq prev (point)) | ||
| 3084 | )) | ||
| 3085 | ;; backward-sexp failed | ||
| 3086 | (if (zerop (skip-chars-backward " \t()[]{};`'")) | ||
| 3087 | (forward-char -1)) | ||
| 3088 | (if (bolp) | ||
| 3089 | (let ((back (sh-prev-line nil))) | ||
| 3090 | (if back | ||
| 3091 | (goto-char back) | ||
| 3092 | (setq going nil))))) | ||
| 3093 | (unless found | ||
| 3094 | (skip-chars-backward " \t") | ||
| 3095 | (if (or (and (bolp) (not (sh-this-is-a-continuation))) | ||
| 3096 | (eq (char-before) ?\;) | ||
| 3097 | (looking-at "\\s-*[|&]")) | ||
| 3098 | (setq found (point))))) | ||
| 3099 | (if found | ||
| 3100 | (goto-char found)) | ||
| 3101 | (if found | ||
| 3102 | (progn | ||
| 3103 | (skip-chars-forward " \t|&({[") | ||
| 3104 | (setq found (point)))) | ||
| 3105 | (if (>= (point) start) | ||
| 3106 | (progn | ||
| 3107 | (debug "We didn't move!") | ||
| 3108 | (setq found nil)) | ||
| 3109 | (or found | ||
| 3110 | (sh-debug "Did not find prev stmt."))) | ||
| 3111 | found))) | ||
| 3112 | |||
| 3113 | |||
| 3114 | (defun sh-get-word () | ||
| 3115 | "Get a shell word skipping whitespace from point." | ||
| 3116 | (interactive) | ||
| 3117 | (skip-chars-forward "\t ") | ||
| 3118 | (let ((start (point))) | ||
| 3119 | (while | ||
| 3120 | (if (looking-at "[\"'`]") | ||
| 3121 | (sh-safe-forward-sexp) | ||
| 3122 | ;; (> (skip-chars-forward "^ \t\n\"'`") 0) | ||
| 3123 | (> (skip-chars-forward "-_$[:alnum:]") 0) | ||
| 3124 | )) | ||
| 3125 | (buffer-substring start (point)) | ||
| 3126 | )) | ||
| 3127 | |||
| 3128 | (defun sh-prev-thing () | ||
| 3129 | "Return the previous thing this logical line." | ||
| 3130 | ;; This is called when `sh-get-indent-info' is working backwards on | ||
| 3131 | ;; the previous line(s) finding what keywords may be relevant for | ||
| 3132 | ;; indenting. It moves over sexps if possible, and will stop | ||
| 3133 | ;; on a ; and at the beginning of a line if it is not a continuation | ||
| 3134 | ;; line. | ||
| 3135 | ;; | ||
| 3136 | ;; Added a kludge for ";;" | ||
| 3137 | ;; Possible return values: | ||
| 3138 | ;; nil - nothing | ||
| 3139 | ;; a string - possibly a keyword | ||
| 3140 | ;; | ||
| 3141 | (if (bolp) | ||
| 3142 | nil | ||
| 3143 | (let ((start (point)) | ||
| 3144 | (min-point (if (sh-this-is-a-continuation) | ||
| 3145 | (sh-prev-line nil) | ||
| 3146 | (line-beginning-position)))) | ||
| 3147 | (skip-chars-backward " \t;" min-point) | ||
| 3148 | (if (looking-at "\\s-*;[;&]") | ||
| 3149 | ;; (message "Found ;; !") | ||
| 3150 | ";;" | ||
| 3151 | (skip-chars-backward "^)}];\"'`({[" min-point) | ||
| 3152 | (let ((c (if (> (point) min-point) (char-before)))) | ||
| 3153 | (sh-debug "stopping at %d c is %s start=%d min-point=%d" | ||
| 3154 | (point) c start min-point) | ||
| 3155 | (if (not (memq c '(?\n nil ?\;))) | ||
| 3156 | ;; c -- return a string | ||
| 3157 | (char-to-string c) | ||
| 3158 | ;; Return the leading keyword of the "command" we supposedly | ||
| 3159 | ;; skipped over. Maybe we skipped too far (e.g. past a `do' or | ||
| 3160 | ;; `then' that precedes the actual command), so check whether | ||
| 3161 | ;; we're looking at such a keyword and if so, move back forward. | ||
| 3162 | (let ((boundary (point)) | ||
| 3163 | kwd next) | ||
| 3164 | (while | ||
| 3165 | (progn | ||
| 3166 | ;; Skip forward over white space newline and \ at eol. | ||
| 3167 | (skip-chars-forward " \t\n\\\\" start) | ||
| 3168 | (if (>= (point) start) | ||
| 3169 | (progn | ||
| 3170 | (sh-debug "point: %d >= start: %d" (point) start) | ||
| 3171 | nil) | ||
| 3172 | (if next (setq boundary next)) | ||
| 3173 | (sh-debug "Now at %d start=%d" (point) start) | ||
| 3174 | (setq kwd (sh-get-word)) | ||
| 3175 | (if (member kwd (sh-feature sh-leading-keywords)) | ||
| 3176 | (progn | ||
| 3177 | (setq next (point)) | ||
| 3178 | t) | ||
| 3179 | nil)))) | ||
| 3180 | (goto-char boundary) | ||
| 3181 | kwd))))))) | ||
| 3182 | |||
| 3183 | |||
| 3184 | (defun sh-this-is-a-continuation () | ||
| 3185 | "Return non-nil if current line is a continuation of previous line." | ||
| 3186 | (save-excursion | ||
| 3187 | (and (zerop (forward-line -1)) | ||
| 3188 | (looking-at ".*\\\\$") | ||
| 3189 | (not (nth 4 (parse-partial-sexp (match-beginning 0) (match-end 0) | ||
| 3190 | nil nil nil t)))))) | ||
| 3191 | |||
| 3192 | (defun sh-get-kw (&optional where and-move) | ||
| 3193 | "Return first word of line from WHERE. | ||
| 3194 | If AND-MOVE is non-nil then move to end of word." | ||
| 3195 | (let ((start (point))) | ||
| 3196 | (if where | ||
| 3197 | (goto-char where)) | ||
| 3198 | (prog1 | ||
| 3199 | (buffer-substring (point) | ||
| 3200 | (progn (skip-chars-forward "^ \t\n;&|")(point))) | ||
| 3201 | (unless and-move | ||
| 3202 | (goto-char start))))) | ||
| 3203 | |||
| 3204 | (defun sh-find-prev-matching (open close &optional depth) | ||
| 3205 | "Find a matching token for a set of opening and closing keywords. | ||
| 3206 | This takes into account that there may be nested open..close pairings. | ||
| 3207 | OPEN and CLOSE are regexps denoting the tokens to be matched. | ||
| 3208 | Optional parameter DEPTH (usually 1) says how many to look for." | ||
| 3209 | (let ((parse-sexp-ignore-comments t) | ||
| 3210 | (forward-sexp-function nil) | ||
| 3211 | prev) | ||
| 3212 | (setq depth (or depth 1)) | ||
| 3213 | (save-excursion | ||
| 3214 | (condition-case nil | ||
| 3215 | (while (and | ||
| 3216 | (/= 0 depth) | ||
| 3217 | (not (bobp)) | ||
| 3218 | (setq prev (sh-prev-stmt))) | ||
| 3219 | (goto-char prev) | ||
| 3220 | (save-excursion | ||
| 3221 | (if (looking-at "\\\\\n") | ||
| 3222 | (progn | ||
| 3223 | (forward-char 2) | ||
| 3224 | (skip-chars-forward " \t"))) | ||
| 3225 | (cond | ||
| 3226 | ((looking-at open) | ||
| 3227 | (setq depth (1- depth)) | ||
| 3228 | (sh-debug "found open at %d - depth = %d" (point) depth)) | ||
| 3229 | ((looking-at close) | ||
| 3230 | (setq depth (1+ depth)) | ||
| 3231 | (sh-debug "found close - depth = %d" depth)) | ||
| 3232 | (t | ||
| 3233 | )))) | ||
| 3234 | (error nil)) | ||
| 3235 | (if (eq depth 0) | ||
| 3236 | prev ;; (point) | ||
| 3237 | nil) | ||
| 3238 | ))) | ||
| 3239 | |||
| 3240 | 2438 | ||
| 3241 | (defun sh-var-value (var &optional ignore-error) | 2439 | (defun sh-var-value (var &optional ignore-error) |
| 3242 | "Return the value of variable VAR, interpreting symbols. | 2440 | "Return the value of variable VAR, interpreting symbols. |
| @@ -3268,620 +2466,16 @@ IGNORE-ERROR is non-nil." | |||
| 3268 | "Don't know how to handle %s's value of %s" var val) | 2466 | "Don't know how to handle %s's value of %s" var val) |
| 3269 | 0)))) | 2467 | 0)))) |
| 3270 | 2468 | ||
| 3271 | (defun sh-set-var-value (var value &optional no-symbol) | 2469 | (define-obsolete-function-alias 'sh-show-indent |
| 3272 | "Set variable VAR to VALUE. | 2470 | #'smie-config-show-indent "28.1") |
| 3273 | Unless optional argument NO-SYMBOL is non-nil, then if VALUE is | ||
| 3274 | can be represented by a symbol then do so." | ||
| 3275 | (cond | ||
| 3276 | (no-symbol | ||
| 3277 | (set var value)) | ||
| 3278 | ((= value sh-basic-offset) | ||
| 3279 | (set var '+)) | ||
| 3280 | ((= value (- sh-basic-offset)) | ||
| 3281 | (set var '-)) | ||
| 3282 | ((eq value (* 2 sh-basic-offset)) | ||
| 3283 | (set var '++)) | ||
| 3284 | ((eq value (* 2 (- sh-basic-offset))) | ||
| 3285 | (set var '--)) | ||
| 3286 | ((eq value (/ sh-basic-offset 2)) | ||
| 3287 | (set var '*)) | ||
| 3288 | ((eq value (/ (- sh-basic-offset) 2)) | ||
| 3289 | (set var '/)) | ||
| 3290 | (t | ||
| 3291 | (set var value))) | ||
| 3292 | ) | ||
| 3293 | |||
| 3294 | |||
| 3295 | (defun sh-calculate-indent (&optional info) | ||
| 3296 | "Return the indentation for the current line. | ||
| 3297 | If INFO is supplied it is used, else it is calculated from current line." | ||
| 3298 | (let ((ofs 0) | ||
| 3299 | (base-value 0) | ||
| 3300 | elt a b val) | ||
| 3301 | (or info | ||
| 3302 | (setq info (sh-get-indent-info))) | ||
| 3303 | (when info | ||
| 3304 | (while info | ||
| 3305 | (sh-debug "info: %s ofs=%s" info ofs) | ||
| 3306 | (setq elt (car info)) | ||
| 3307 | (cond | ||
| 3308 | ((stringp elt)) ;; do nothing? | ||
| 3309 | ((listp elt) | ||
| 3310 | (setq a (car (car info))) | ||
| 3311 | (setq b (nth 1 (car info))) | ||
| 3312 | (cond | ||
| 3313 | ((eq a t) | ||
| 3314 | (save-excursion | ||
| 3315 | (goto-char b) | ||
| 3316 | (setq val (current-indentation))) | ||
| 3317 | (setq base-value val)) | ||
| 3318 | ((symbolp b) | ||
| 3319 | (setq val (sh-var-value b)) | ||
| 3320 | (cond | ||
| 3321 | ((eq a '=) | ||
| 3322 | (cond | ||
| 3323 | ((null val) | ||
| 3324 | ;; no indentation | ||
| 3325 | ;; set info to nil so we stop immediately | ||
| 3326 | (setq base-value nil ofs nil info nil)) | ||
| 3327 | ((eq val t) (setq ofs 0)) ;; indent as normal line | ||
| 3328 | (t | ||
| 3329 | ;; The following assume the (t POS) come first! | ||
| 3330 | (setq ofs val base-value 0) | ||
| 3331 | (setq info nil)))) ;; ? stop now | ||
| 3332 | ((eq a '+) (setq ofs (+ ofs val))) | ||
| 3333 | ((eq a '-) (setq ofs (- ofs val))) | ||
| 3334 | (t | ||
| 3335 | (error "sh-calculate-indent invalid a a=%s b=%s" a b)))) | ||
| 3336 | (t | ||
| 3337 | (error "sh-calculate-indent invalid elt: a=%s b=%s" a b)))) | ||
| 3338 | (t | ||
| 3339 | (error "sh-calculate-indent invalid elt %s" elt))) | ||
| 3340 | (sh-debug "a=%s b=%s val=%s base-value=%s ofs=%s" | ||
| 3341 | a b val base-value ofs) | ||
| 3342 | (setq info (cdr info))) | ||
| 3343 | ;; return value: | ||
| 3344 | (sh-debug "at end: base-value: %s ofs: %s" base-value ofs) | ||
| 3345 | |||
| 3346 | (cond | ||
| 3347 | ((or (null base-value)(null ofs)) | ||
| 3348 | nil) | ||
| 3349 | ((and (numberp base-value)(numberp ofs)) | ||
| 3350 | (sh-debug "base (%d) + ofs (%d) = %d" | ||
| 3351 | base-value ofs (+ base-value ofs)) | ||
| 3352 | (+ base-value ofs)) ;; return value | ||
| 3353 | (t | ||
| 3354 | (error "sh-calculate-indent: Help. base-value=%s ofs=%s" | ||
| 3355 | base-value ofs) | ||
| 3356 | nil))))) | ||
| 3357 | 2471 | ||
| 2472 | (define-obsolete-function-alias 'sh-set-indent #'smie-config-set-indent "28.1") | ||
| 3358 | 2473 | ||
| 3359 | (defun sh-indent-line () | 2474 | (define-obsolete-function-alias 'sh-learn-line-indent |
| 3360 | "Indent the current line." | 2475 | #'smie-config-set-indent "28.1") |
| 3361 | (interactive) | ||
| 3362 | (let ((indent (sh-calculate-indent)) | ||
| 3363 | (pos (- (point-max) (point)))) | ||
| 3364 | (when indent | ||
| 3365 | (beginning-of-line) | ||
| 3366 | (skip-chars-forward " \t") | ||
| 3367 | (indent-line-to indent) | ||
| 3368 | ;; If initial point was within line's indentation, | ||
| 3369 | ;; position after the indentation. Else stay at same point in text. | ||
| 3370 | (if (> (- (point-max) pos) (point)) | ||
| 3371 | (goto-char (- (point-max) pos)))))) | ||
| 3372 | |||
| 3373 | |||
| 3374 | (defun sh-blink (blinkpos &optional msg) | ||
| 3375 | "Move cursor momentarily to BLINKPOS and display MSG." | ||
| 3376 | ;; We can get here without it being a number on first line | ||
| 3377 | (if (numberp blinkpos) | ||
| 3378 | (save-excursion | ||
| 3379 | (goto-char blinkpos) | ||
| 3380 | (if msg (message "%s" msg) (message nil)) | ||
| 3381 | (sit-for blink-matching-delay)) | ||
| 3382 | (if msg (message "%s" msg) (message nil)))) | ||
| 3383 | |||
| 3384 | (defun sh-show-indent (arg) | ||
| 3385 | "Show how the current line would be indented. | ||
| 3386 | This tells you which variable, if any, controls the indentation of | ||
| 3387 | this line. | ||
| 3388 | If optional arg ARG is non-null (called interactively with a prefix), | ||
| 3389 | a pop up window describes this variable. | ||
| 3390 | If variable `sh-blink' is non-nil then momentarily go to the line | ||
| 3391 | we are indenting relative to, if applicable." | ||
| 3392 | (interactive "P") | ||
| 3393 | (sh-must-support-indent) | ||
| 3394 | (if sh-use-smie | ||
| 3395 | (smie-config-show-indent) | ||
| 3396 | (let* ((info (sh-get-indent-info)) | ||
| 3397 | (var (sh-get-indent-var-for-line info)) | ||
| 3398 | (curr-indent (current-indentation)) | ||
| 3399 | val msg) | ||
| 3400 | (if (stringp var) | ||
| 3401 | (message "%s" (setq msg var)) | ||
| 3402 | (setq val (sh-calculate-indent info)) | ||
| 3403 | |||
| 3404 | (if (eq curr-indent val) | ||
| 3405 | (setq msg (format "%s is %s" var (symbol-value var))) | ||
| 3406 | (setq msg | ||
| 3407 | (if val | ||
| 3408 | (format "%s (%s) would change indent from %d to: %d" | ||
| 3409 | var (symbol-value var) curr-indent val) | ||
| 3410 | (format "%s (%s) would leave line as is" | ||
| 3411 | var (symbol-value var))) | ||
| 3412 | )) | ||
| 3413 | (if (and arg var) | ||
| 3414 | (describe-variable var))) | ||
| 3415 | (if sh-blink | ||
| 3416 | (let ((info (sh-get-indent-info))) | ||
| 3417 | (if (and info (listp (car info)) | ||
| 3418 | (eq (car (car info)) t)) | ||
| 3419 | (sh-blink (nth 1 (car info)) msg) | ||
| 3420 | (message "%s" msg))) | ||
| 3421 | (message "%s" msg)) | ||
| 3422 | ))) | ||
| 3423 | 2476 | ||
| 3424 | (defun sh-set-indent () | 2477 | (define-obsolete-function-alias 'sh-learn-buffer-indent |
| 3425 | "Set the indentation for the current line. | 2478 | #'smie-config-guess "28.1") |
| 3426 | If the current line is controlled by an indentation variable, prompt | ||
| 3427 | for a new value for it." | ||
| 3428 | (interactive) | ||
| 3429 | (sh-must-support-indent) | ||
| 3430 | (if sh-use-smie | ||
| 3431 | (smie-config-set-indent) | ||
| 3432 | (let* ((info (sh-get-indent-info)) | ||
| 3433 | (var (sh-get-indent-var-for-line info)) | ||
| 3434 | val old-val indent-val) | ||
| 3435 | (if (stringp var) | ||
| 3436 | (message "Cannot set indent - %s" var) | ||
| 3437 | (setq old-val (symbol-value var)) | ||
| 3438 | (setq val (sh-read-variable var)) | ||
| 3439 | (condition-case nil | ||
| 3440 | (progn | ||
| 3441 | (set var val) | ||
| 3442 | (setq indent-val (sh-calculate-indent info)) | ||
| 3443 | (if indent-val | ||
| 3444 | (message "Variable: %s Value: %s would indent to: %d" | ||
| 3445 | var (symbol-value var) indent-val) | ||
| 3446 | (message "Variable: %s Value: %s would leave line as is." | ||
| 3447 | var (symbol-value var))) | ||
| 3448 | ;; I'm not sure about this, indenting it now? | ||
| 3449 | ;; No. Because it would give the impression that an undo would | ||
| 3450 | ;; restore thing, but the value has been altered. | ||
| 3451 | ;; (sh-indent-line) | ||
| 3452 | ) | ||
| 3453 | (error | ||
| 3454 | (set var old-val) | ||
| 3455 | (message "Bad value for %s, restoring to previous value %s" | ||
| 3456 | var old-val) | ||
| 3457 | (sit-for 1) | ||
| 3458 | nil)) | ||
| 3459 | )))) | ||
| 3460 | |||
| 3461 | |||
| 3462 | (defun sh-learn-line-indent (arg) | ||
| 3463 | "Learn how to indent a line as it currently is indented. | ||
| 3464 | |||
| 3465 | If there is an indentation variable which controls this line's indentation, | ||
| 3466 | then set it to a value which would indent the line the way it | ||
| 3467 | presently is. | ||
| 3468 | |||
| 3469 | If the value can be represented by one of the symbols then do so | ||
| 3470 | unless optional argument ARG (the prefix when interactive) is non-nil." | ||
| 3471 | (interactive "*P") | ||
| 3472 | (sh-must-support-indent) | ||
| 3473 | (if sh-use-smie | ||
| 3474 | (smie-config-set-indent) | ||
| 3475 | ;; I'm not sure if we show allow learning on an empty line. | ||
| 3476 | ;; Though it might occasionally be useful I think it usually | ||
| 3477 | ;; would just be confusing. | ||
| 3478 | (if (save-excursion | ||
| 3479 | (beginning-of-line) | ||
| 3480 | (looking-at "\\s-*$")) | ||
| 3481 | (message "sh-learn-line-indent ignores empty lines.") | ||
| 3482 | (let* ((info (sh-get-indent-info)) | ||
| 3483 | (var (sh-get-indent-var-for-line info)) | ||
| 3484 | ival sval diff new-val | ||
| 3485 | (no-symbol arg) | ||
| 3486 | (curr-indent (current-indentation))) | ||
| 3487 | (cond | ||
| 3488 | ((stringp var) | ||
| 3489 | (message "Cannot learn line - %s" var)) | ||
| 3490 | ((eq var 'sh-indent-comment) | ||
| 3491 | ;; This is arbitrary... | ||
| 3492 | ;; - if curr-indent is 0, set to curr-indent | ||
| 3493 | ;; - else if it has the indentation of a "normal" line, | ||
| 3494 | ;; then set to t | ||
| 3495 | ;; - else set to curr-indent. | ||
| 3496 | (setq sh-indent-comment | ||
| 3497 | (if (= curr-indent 0) | ||
| 3498 | 0 | ||
| 3499 | (let* ((sh-indent-comment t) | ||
| 3500 | (val2 (sh-calculate-indent info))) | ||
| 3501 | (if (= val2 curr-indent) | ||
| 3502 | t | ||
| 3503 | curr-indent)))) | ||
| 3504 | (message "%s set to %s" var (symbol-value var)) | ||
| 3505 | ) | ||
| 3506 | ((numberp (setq sval (sh-var-value var))) | ||
| 3507 | (setq ival (sh-calculate-indent info)) | ||
| 3508 | (setq diff (- curr-indent ival)) | ||
| 3509 | |||
| 3510 | (sh-debug "curr-indent: %d ival: %d diff: %d var:%s sval %s" | ||
| 3511 | curr-indent ival diff var sval) | ||
| 3512 | (setq new-val (+ sval diff)) | ||
| 3513 | ;; I commented out this because someone might want to replace | ||
| 3514 | ;; a value of `+' with the current value of sh-basic-offset | ||
| 3515 | ;; or vice-versa. | ||
| 3516 | ;;(if (= 0 diff) | ||
| 3517 | ;; (message "No change needed!") | ||
| 3518 | (sh-set-var-value var new-val no-symbol) | ||
| 3519 | (message "%s set to %s" var (symbol-value var)) | ||
| 3520 | ) | ||
| 3521 | (t | ||
| 3522 | (debug) | ||
| 3523 | (message "Cannot change %s" var))))))) | ||
| 3524 | |||
| 3525 | |||
| 3526 | |||
| 3527 | (defun sh-mark-init (buffer) | ||
| 3528 | "Initialize a BUFFER to be used by `sh-mark-line'." | ||
| 3529 | (with-current-buffer (get-buffer-create buffer) | ||
| 3530 | (erase-buffer) | ||
| 3531 | (occur-mode))) | ||
| 3532 | |||
| 3533 | |||
| 3534 | (defun sh-mark-line (message point buffer &optional add-linenum occur-point) | ||
| 3535 | "Insert MESSAGE referring to location POINT in current buffer into BUFFER. | ||
| 3536 | Buffer BUFFER is in `occur-mode'. | ||
| 3537 | If ADD-LINENUM is non-nil the message is preceded by the line number. | ||
| 3538 | If OCCUR-POINT is non-nil then the line is marked as a new occurrence | ||
| 3539 | so that `occur-next' and `occur-prev' will work." | ||
| 3540 | (let ((m1 (make-marker)) | ||
| 3541 | start | ||
| 3542 | (line "")) | ||
| 3543 | (when point | ||
| 3544 | (set-marker m1 point (current-buffer)) | ||
| 3545 | (if add-linenum | ||
| 3546 | (setq line (format "%d: " (1+ (count-lines 1 point)))))) | ||
| 3547 | (save-excursion | ||
| 3548 | (if (get-buffer buffer) | ||
| 3549 | (set-buffer (get-buffer buffer)) | ||
| 3550 | (set-buffer (get-buffer-create buffer)) | ||
| 3551 | (occur-mode) | ||
| 3552 | ) | ||
| 3553 | (goto-char (point-max)) | ||
| 3554 | (setq start (point)) | ||
| 3555 | (let ((inhibit-read-only t)) | ||
| 3556 | (insert line) | ||
| 3557 | (if occur-point | ||
| 3558 | (setq occur-point (point))) | ||
| 3559 | (insert message) | ||
| 3560 | (if point | ||
| 3561 | (add-text-properties | ||
| 3562 | start (point) | ||
| 3563 | '(mouse-face highlight | ||
| 3564 | help-echo "mouse-2: go to the line where I learned this"))) | ||
| 3565 | (insert "\n") | ||
| 3566 | (when point | ||
| 3567 | (put-text-property start (point) 'occur-target m1) | ||
| 3568 | (if occur-point | ||
| 3569 | (put-text-property start occur-point | ||
| 3570 | 'occur-match t)) | ||
| 3571 | ))))) | ||
| 3572 | |||
| 3573 | ;; Is this really worth having? | ||
| 3574 | (defvar sh-learned-buffer-hook nil | ||
| 3575 | "An abnormal hook, called with an alist of learned variables.") | ||
| 3576 | ;; Example of how to use sh-learned-buffer-hook | ||
| 3577 | ;; | ||
| 3578 | ;; (defun what-i-learned (list) | ||
| 3579 | ;; (let ((p list)) | ||
| 3580 | ;; (with-current-buffer "*scratch*" | ||
| 3581 | ;; (goto-char (point-max)) | ||
| 3582 | ;; (insert "(setq\n") | ||
| 3583 | ;; (while p | ||
| 3584 | ;; (insert (format " %s %s \n" | ||
| 3585 | ;; (nth 0 (car p)) (nth 1 (car p)))) | ||
| 3586 | ;; (setq p (cdr p))) | ||
| 3587 | ;; (insert ")\n") | ||
| 3588 | ;; ))) | ||
| 3589 | ;; | ||
| 3590 | ;; (add-hook 'sh-learned-buffer-hook #'what-i-learned) | ||
| 3591 | |||
| 3592 | |||
| 3593 | ;; Originally this was sh-learn-region-indent (beg end) | ||
| 3594 | ;; However, in practice this was awkward so I changed it to | ||
| 3595 | ;; use the whole buffer. Use narrowing if need be. | ||
| 3596 | (defun sh-learn-buffer-indent (&optional arg) | ||
| 3597 | "Learn how to indent the buffer the way it currently is. | ||
| 3598 | |||
| 3599 | If `sh-use-smie' is non-nil, call `smie-config-guess'. | ||
| 3600 | Otherwise, run the sh-script specific indent learning command, as | ||
| 3601 | described below. | ||
| 3602 | |||
| 3603 | Output in buffer \"*indent*\" shows any lines which have conflicting | ||
| 3604 | values of a variable, and the final value of all variables learned. | ||
| 3605 | When called interactively, pop to this buffer automatically if | ||
| 3606 | there are any discrepancies. | ||
| 3607 | |||
| 3608 | If no prefix ARG is given, then variables are set to numbers. | ||
| 3609 | If a prefix arg is given, then variables are set to symbols when | ||
| 3610 | applicable -- e.g. to symbol `+' if the value is that of the | ||
| 3611 | basic indent. | ||
| 3612 | If a positive numerical prefix is given, then `sh-basic-offset' | ||
| 3613 | is set to the prefix's numerical value. | ||
| 3614 | Otherwise, sh-basic-offset may or may not be changed, according | ||
| 3615 | to the value of variable `sh-learn-basic-offset'. | ||
| 3616 | |||
| 3617 | Abnormal hook `sh-learned-buffer-hook' if non-nil is called when the | ||
| 3618 | function completes. The function is abnormal because it is called | ||
| 3619 | with an alist of variables learned. | ||
| 3620 | |||
| 3621 | This command can often take a long time to run." | ||
| 3622 | (interactive "P") | ||
| 3623 | (sh-must-support-indent) | ||
| 3624 | (if sh-use-smie | ||
| 3625 | (smie-config-guess) | ||
| 3626 | (save-excursion | ||
| 3627 | (goto-char (point-min)) | ||
| 3628 | (let ((learned-var-list nil) | ||
| 3629 | (out-buffer "*indent*") | ||
| 3630 | (num-diffs 0) | ||
| 3631 | previous-set-info | ||
| 3632 | (max 17) | ||
| 3633 | vec | ||
| 3634 | msg | ||
| 3635 | (comment-col nil) ;; number if all same, t if seen diff values | ||
| 3636 | (comments-always-default t) ;; nil if we see one not default | ||
| 3637 | initial-msg | ||
| 3638 | (specified-basic-offset (and arg (numberp arg) | ||
| 3639 | (> arg 0))) | ||
| 3640 | (linenum 0) | ||
| 3641 | suggested) | ||
| 3642 | (setq vec (make-vector max 0)) | ||
| 3643 | (sh-mark-init out-buffer) | ||
| 3644 | |||
| 3645 | (if specified-basic-offset | ||
| 3646 | (progn | ||
| 3647 | (setq sh-basic-offset arg) | ||
| 3648 | (setq initial-msg | ||
| 3649 | (format "Using specified sh-basic-offset of %d" | ||
| 3650 | sh-basic-offset))) | ||
| 3651 | (setq initial-msg | ||
| 3652 | (format "Initial value of sh-basic-offset: %s" | ||
| 3653 | sh-basic-offset))) | ||
| 3654 | |||
| 3655 | (while (< (point) (point-max)) | ||
| 3656 | (setq linenum (1+ linenum)) | ||
| 3657 | ;; (if (zerop (% linenum 10)) | ||
| 3658 | (message "line %d" linenum) | ||
| 3659 | ;; ) | ||
| 3660 | (unless (looking-at "\\s-*$") ;; ignore empty lines! | ||
| 3661 | (let* ((sh-indent-comment t) ;; info must return default indent | ||
| 3662 | (info (sh-get-indent-info)) | ||
| 3663 | (var (sh-get-indent-var-for-line info)) | ||
| 3664 | sval ival diff new-val | ||
| 3665 | (curr-indent (current-indentation))) | ||
| 3666 | (cond | ||
| 3667 | ((null var) | ||
| 3668 | nil) | ||
| 3669 | ((stringp var) | ||
| 3670 | nil) | ||
| 3671 | ((numberp (setq sval (sh-var-value var 'no-error))) | ||
| 3672 | ;; the numberp excludes comments since sval will be t. | ||
| 3673 | (setq ival (sh-calculate-indent)) | ||
| 3674 | (setq diff (- curr-indent ival)) | ||
| 3675 | (setq new-val (+ sval diff)) | ||
| 3676 | (sh-set-var-value var new-val 'no-symbol) | ||
| 3677 | (unless (looking-at "\\s-*#") ;; don't learn from comments | ||
| 3678 | (if (setq previous-set-info (assoc var learned-var-list)) | ||
| 3679 | (progn | ||
| 3680 | ;; it was already there, is it same value ? | ||
| 3681 | (unless (eq (symbol-value var) | ||
| 3682 | (nth 1 previous-set-info)) | ||
| 3683 | (sh-mark-line | ||
| 3684 | (format "Variable %s was set to %s" | ||
| 3685 | var (symbol-value var)) | ||
| 3686 | (point) out-buffer t t) | ||
| 3687 | (sh-mark-line | ||
| 3688 | (format " but was previously set to %s" | ||
| 3689 | (nth 1 previous-set-info)) | ||
| 3690 | (nth 2 previous-set-info) out-buffer t) | ||
| 3691 | (setq num-diffs (1+ num-diffs)) | ||
| 3692 | ;; (delete previous-set-info learned-var-list) | ||
| 3693 | (setcdr previous-set-info | ||
| 3694 | (list (symbol-value var) (point))) | ||
| 3695 | ) | ||
| 3696 | ) | ||
| 3697 | (setq learned-var-list | ||
| 3698 | (append (list (list var (symbol-value var) | ||
| 3699 | (point))) | ||
| 3700 | learned-var-list))) | ||
| 3701 | (if (numberp new-val) | ||
| 3702 | (progn | ||
| 3703 | (sh-debug | ||
| 3704 | "This line's indent value: %d" new-val) | ||
| 3705 | (if (< new-val 0) | ||
| 3706 | (setq new-val (- new-val))) | ||
| 3707 | (if (< new-val max) | ||
| 3708 | (aset vec new-val (1+ (aref vec new-val)))))) | ||
| 3709 | )) | ||
| 3710 | ((eq var 'sh-indent-comment) | ||
| 3711 | (unless (= curr-indent (sh-calculate-indent info)) | ||
| 3712 | ;; this is not the default indentation | ||
| 3713 | (setq comments-always-default nil) | ||
| 3714 | (if comment-col ;; then we have see one before | ||
| 3715 | (or (eq comment-col curr-indent) | ||
| 3716 | (setq comment-col t)) ;; seen a different one | ||
| 3717 | (setq comment-col curr-indent)) | ||
| 3718 | )) | ||
| 3719 | (t | ||
| 3720 | (sh-debug "Cannot learn this line!!!") | ||
| 3721 | )) | ||
| 3722 | (sh-debug | ||
| 3723 | "at %s learned-var-list is %s" (point) learned-var-list) | ||
| 3724 | )) | ||
| 3725 | (forward-line 1) | ||
| 3726 | ) ;; while | ||
| 3727 | (if sh-debug | ||
| 3728 | (progn | ||
| 3729 | (setq msg (format | ||
| 3730 | "comment-col = %s comments-always-default = %s" | ||
| 3731 | comment-col comments-always-default)) | ||
| 3732 | ;; (message msg) | ||
| 3733 | (sh-mark-line msg nil out-buffer))) | ||
| 3734 | (cond | ||
| 3735 | ((eq comment-col 0) | ||
| 3736 | (setq msg "\nComments are all in 1st column.\n")) | ||
| 3737 | (comments-always-default | ||
| 3738 | (setq msg "\nComments follow default indentation.\n") | ||
| 3739 | (setq comment-col t)) | ||
| 3740 | ((numberp comment-col) | ||
| 3741 | (setq msg (format "\nComments are in col %d." comment-col))) | ||
| 3742 | (t | ||
| 3743 | (setq msg "\nComments seem to be mixed, leaving them as is.\n") | ||
| 3744 | (setq comment-col nil) | ||
| 3745 | )) | ||
| 3746 | (sh-debug msg) | ||
| 3747 | (sh-mark-line msg nil out-buffer) | ||
| 3748 | |||
| 3749 | (sh-mark-line initial-msg nil out-buffer t t) | ||
| 3750 | |||
| 3751 | (setq suggested (sh-guess-basic-offset vec)) | ||
| 3752 | |||
| 3753 | (if (and suggested (not specified-basic-offset)) | ||
| 3754 | (let ((new-value | ||
| 3755 | (cond | ||
| 3756 | ;; t => set it if we have a single value as a number | ||
| 3757 | ((and (eq sh-learn-basic-offset t) (numberp suggested)) | ||
| 3758 | suggested) | ||
| 3759 | ;; other non-nil => set it if only one value was found | ||
| 3760 | (sh-learn-basic-offset | ||
| 3761 | (if (numberp suggested) | ||
| 3762 | suggested | ||
| 3763 | (if (= (length suggested) 1) | ||
| 3764 | (car suggested)))) | ||
| 3765 | (t | ||
| 3766 | nil)))) | ||
| 3767 | (if new-value | ||
| 3768 | (progn | ||
| 3769 | (setq learned-var-list | ||
| 3770 | (append (list (list 'sh-basic-offset | ||
| 3771 | (setq sh-basic-offset new-value) | ||
| 3772 | (point-max))) | ||
| 3773 | learned-var-list)) | ||
| 3774 | ;; Not sure if we need to put this line in, since | ||
| 3775 | ;; it will appear in the "Learned variable settings". | ||
| 3776 | (sh-mark-line | ||
| 3777 | (format "Changed sh-basic-offset to: %d" sh-basic-offset) | ||
| 3778 | nil out-buffer)) | ||
| 3779 | (sh-mark-line | ||
| 3780 | (if (listp suggested) | ||
| 3781 | (format "Possible value(s) for sh-basic-offset: %s" | ||
| 3782 | (mapconcat 'int-to-string suggested " ")) | ||
| 3783 | (format "Suggested sh-basic-offset: %d" suggested)) | ||
| 3784 | nil out-buffer)))) | ||
| 3785 | |||
| 3786 | |||
| 3787 | (setq learned-var-list | ||
| 3788 | (append (list (list 'sh-indent-comment comment-col (point-max))) | ||
| 3789 | learned-var-list)) | ||
| 3790 | (setq sh-indent-comment comment-col) | ||
| 3791 | (let ((name (buffer-name))) | ||
| 3792 | (sh-mark-line "\nLearned variable settings:" nil out-buffer) | ||
| 3793 | (if arg | ||
| 3794 | ;; Set learned variables to symbolic rather than numeric | ||
| 3795 | ;; values where possible. | ||
| 3796 | (dolist (learned-var (reverse learned-var-list)) | ||
| 3797 | (let ((var (car learned-var)) | ||
| 3798 | (val (nth 1 learned-var))) | ||
| 3799 | (when (and (not (eq var 'sh-basic-offset)) | ||
| 3800 | (numberp val)) | ||
| 3801 | (sh-set-var-value var val))))) | ||
| 3802 | (dolist (learned-var (reverse learned-var-list)) | ||
| 3803 | (let ((var (car learned-var))) | ||
| 3804 | (sh-mark-line (format " %s %s" var (symbol-value var)) | ||
| 3805 | (nth 2 learned-var) out-buffer))) | ||
| 3806 | (with-current-buffer out-buffer | ||
| 3807 | (goto-char (point-min)) | ||
| 3808 | (let ((inhibit-read-only t)) | ||
| 3809 | (insert | ||
| 3810 | (format "Indentation values for buffer %s.\n" name) | ||
| 3811 | (format "%d indentation variable%s different values%s\n\n" | ||
| 3812 | num-diffs | ||
| 3813 | (if (= num-diffs 1) | ||
| 3814 | " has" "s have") | ||
| 3815 | (if (zerop num-diffs) | ||
| 3816 | "." ":")))))) | ||
| 3817 | (run-hook-with-args 'sh-learned-buffer-hook learned-var-list) | ||
| 3818 | (and (called-interactively-p 'any) | ||
| 3819 | (or sh-popup-occur-buffer (> num-diffs 0)) | ||
| 3820 | (pop-to-buffer out-buffer)))))) | ||
| 3821 | |||
| 3822 | (defun sh-guess-basic-offset (vec) | ||
| 3823 | "See if we can determine a reasonable value for `sh-basic-offset'. | ||
| 3824 | This is experimental, heuristic and arbitrary! | ||
| 3825 | Argument VEC is a vector of information collected by | ||
| 3826 | `sh-learn-buffer-indent'. | ||
| 3827 | Return values: | ||
| 3828 | number - there appears to be a good single value | ||
| 3829 | list of numbers - no obvious one, here is a list of one or more | ||
| 3830 | reasonable choices | ||
| 3831 | nil - we couldn't find a reasonable one." | ||
| 3832 | (let* ((max (1- (length vec))) | ||
| 3833 | (i 1) | ||
| 3834 | (totals (make-vector max 0))) | ||
| 3835 | (while (< i max) | ||
| 3836 | (cl-incf (aref totals i) (* 4 (aref vec i))) | ||
| 3837 | (if (zerop (% i 2)) | ||
| 3838 | (cl-incf (aref totals i) (aref vec (/ i 2)))) | ||
| 3839 | (if (< (* i 2) max) | ||
| 3840 | (cl-incf (aref totals i) (aref vec (* i 2)))) | ||
| 3841 | (setq i (1+ i))) | ||
| 3842 | |||
| 3843 | (let ((x nil) | ||
| 3844 | (result nil) | ||
| 3845 | tot sum p) | ||
| 3846 | (setq i 1) | ||
| 3847 | (while (< i max) | ||
| 3848 | (if (/= (aref totals i) 0) | ||
| 3849 | (push (cons i (aref totals i)) x)) | ||
| 3850 | (setq i (1+ i))) | ||
| 3851 | |||
| 3852 | (setq x (sort (nreverse x) (lambda (a b) (> (cdr a) (cdr b))))) | ||
| 3853 | (setq tot (apply '+ (append totals nil))) | ||
| 3854 | (sh-debug (format "vec: %s\ntotals: %s\ntot: %d" | ||
| 3855 | vec totals tot)) | ||
| 3856 | (cond | ||
| 3857 | ((zerop (length x)) | ||
| 3858 | (message "no values!")) ;; we return nil | ||
| 3859 | ((= (length x) 1) | ||
| 3860 | (message "only value is %d" (car (car x))) | ||
| 3861 | (setq result (car (car x)))) ;; return single value | ||
| 3862 | ((> (cdr (car x)) (/ tot 2)) | ||
| 3863 | ;; 1st is > 50% | ||
| 3864 | (message "basic-offset is probably %d" (car (car x))) | ||
| 3865 | (setq result (car (car x)))) ;; again, return a single value | ||
| 3866 | ((>= (cdr (car x)) (* 2 (cdr (car (cdr x))))) | ||
| 3867 | ;; 1st is >= 2 * 2nd | ||
| 3868 | (message "basic-offset could be %d" (car (car x))) | ||
| 3869 | (setq result (car (car x)))) | ||
| 3870 | ((>= (+ (cdr (car x))(cdr (car (cdr x)))) (/ tot 2)) | ||
| 3871 | ;; 1st & 2nd together >= 50% - return a list | ||
| 3872 | (setq p x sum 0 result nil) | ||
| 3873 | (while (and p | ||
| 3874 | (<= (setq sum (+ sum (cdr (car p)))) (/ tot 2))) | ||
| 3875 | (setq result (append result (list (car (car p))))) | ||
| 3876 | (setq p (cdr p))) | ||
| 3877 | (message "Possible choices for sh-basic-offset: %s" | ||
| 3878 | (mapconcat 'int-to-string result " "))) | ||
| 3879 | (t | ||
| 3880 | (message "No obvious value for sh-basic-offset. Perhaps %d" | ||
| 3881 | (car (car x))) | ||
| 3882 | ;; result is nil here | ||
| 3883 | )) | ||
| 3884 | result))) | ||
| 3885 | 2479 | ||
| 3886 | ;; ======================================================================== | 2480 | ;; ======================================================================== |
| 3887 | 2481 | ||