diff options
| -rw-r--r-- | lisp/progmodes/sh-script.el | 2702 |
1 files changed, 2599 insertions, 103 deletions
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index c666e0e2fee..e1f636f8238 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | ;; Copyright (C) 1993, 94, 95, 96, 97, 1999 by Free Software Foundation, Inc. | 3 | ;; Copyright (C) 1993, 94, 95, 96, 97, 1999 by Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | ;; Author: Daniel Pfeiffer <occitan@esperanto.org> | 5 | ;; Author: Daniel Pfeiffer <occitan@esperanto.org> |
| 6 | ;; Version: 2.0e | 6 | ;; Version: 2.0f |
| 7 | ;; Maintainer: FSF | 7 | ;; Maintainer: FSF |
| 8 | ;; Keywords: languages, unix | 8 | ;; Keywords: languages, unix |
| 9 | 9 | ||
| @@ -38,20 +38,179 @@ | |||
| 38 | ;; - Variables in `"' strings aren't fontified because there's no way of | 38 | ;; - Variables in `"' strings aren't fontified because there's no way of |
| 39 | ;; syntactically distinguishing those from `'' strings. | 39 | ;; syntactically distinguishing those from `'' strings. |
| 40 | 40 | ||
| 41 | ;; Indentation | ||
| 42 | ;; =========== | ||
| 43 | ;; Indentation for rc and es modes is very limited, but for Bourne shells | ||
| 44 | ;; and its derivatives it is quite customizable. | ||
| 45 | ;; | ||
| 46 | ;; The following description applies to sh and derived shells (bash, | ||
| 47 | ;; zsh, ...). | ||
| 48 | ;; | ||
| 49 | ;; There are various customization variables which allow tailoring to | ||
| 50 | ;; a wide variety of styles. Most of these variables are named | ||
| 51 | ;; sh-indent-for-XXX and sh-indent-after-XXX. For example. | ||
| 52 | ;; sh-indent-after-if controls the indenting of a line following | ||
| 53 | ;; an if statement, and sh-indent-for-fi controls the indentation | ||
| 54 | ;; of the line containing the fi. | ||
| 55 | ;; | ||
| 56 | ;; You can set each to a numeric value, but it is often more convenient | ||
| 57 | ;; to a symbol such as `+' which uses the value of variable `sh-basic-offset'. | ||
| 58 | ;; By changing this one variable you can increase or decrease how much | ||
| 59 | ;; indentation there is. Valid symbols: | ||
| 60 | ;; | ||
| 61 | ;; + Indent right by sh-basic-offset | ||
| 62 | ;; - Indent left by sh-basic-offset | ||
| 63 | ;; ++ Indent right twice sh-basic-offset | ||
| 64 | ;; -- Indent left twice sh-basic-offset | ||
| 65 | ;; * Indent right half sh-basic-offset | ||
| 66 | ;; / Indent left half sh-basic-offset. | ||
| 67 | ;; | ||
| 68 | ;; There are 4 commands to help set the indentation variables: | ||
| 69 | ;; | ||
| 70 | ;; `sh-show-indent' | ||
| 71 | ;; This shows what variable controls the indentation of the current | ||
| 72 | ;; line and its value. | ||
| 73 | ;; | ||
| 74 | ;; `sh-set-indent' | ||
| 75 | ;; This allows you to set the value of the variable controlling the | ||
| 76 | ;; current line's indentation. You can enter a number or one of a | ||
| 77 | ;; number of special symbols to denote the value of sh-basic-offset, | ||
| 78 | ;; or its negative, or half it, or twice it, etc. If you've used | ||
| 79 | ;; cc-mode this should be familiar. If you forget which symbols are | ||
| 80 | ;; valid simply press C-h at the prompt. | ||
| 81 | ;; | ||
| 82 | ;; `sh-learn-line-indent' | ||
| 83 | ;; Simply make the line look the way you want it, then invoke this | ||
| 84 | ;; command. It will set the variable to the value that makes the line | ||
| 85 | ;; indent like that. If called with a prefix argument then it will set | ||
| 86 | ;; the value to one of the symbols if applicable. | ||
| 87 | ;; | ||
| 88 | ;; `sh-learn-buffer-indent' | ||
| 89 | ;; This is the deluxe function! It "learns" the whole buffer (use | ||
| 90 | ;; narrowing if you want it to process only part). It outputs to a | ||
| 91 | ;; buffer *indent* any conflicts it finds, and all the variables it has | ||
| 92 | ;; learned. This buffer is a sort of Occur mode buffer, allowing you to | ||
| 93 | ;; easily find where something was set. It is popped to automatically | ||
| 94 | ;; if there are any conflicts found or if `sh-popup-occur-buffer' is | ||
| 95 | ;; non-nil. | ||
| 96 | ;; `sh-indent-comment' will be set if all comments follow the same | ||
| 97 | ;; pattern; if they don't it will be set to nil. | ||
| 98 | ;; Whether `sh-basic-offset' is set is determined by variable | ||
| 99 | ;; `sh-learn-basic-offset'. | ||
| 100 | ;; | ||
| 101 | ;; Unfortunately, `sh-learn-buffer-indent' can take a long time to run | ||
| 102 | ;; (e.g. if there are large case statements). Perhaps it does not make | ||
| 103 | ;; sense to run it on large buffers: if lots of lines have different | ||
| 104 | ;; indentation styles it will produce a lot of diagnostics in the | ||
| 105 | ;; *indent* buffer; if there is a consistent style then running | ||
| 106 | ;; `sh-learn-buffer-indent' on a small region of the buffer should | ||
| 107 | ;; suffice. | ||
| 108 | ;; | ||
| 109 | ;; Saving indentation values | ||
| 110 | ;; ------------------------- | ||
| 111 | ;; After you've learned the values in a buffer, how to you remember | ||
| 112 | ;; them? Originally I had hoped that `sh-learn-buffer-indent' | ||
| 113 | ;; would make this unnecessary; simply learn the values when you visit | ||
| 114 | ;; the buffer. | ||
| 115 | ;; You can do this automatically like this: | ||
| 116 | ; (add-hook 'sh-set-shell-hook 'sh-learn-buffer-indent) | ||
| 117 | ;; | ||
| 118 | ;; However... `sh-learn-buffer-indent' is extremely slow, | ||
| 119 | ;; especially on large-ish buffer. Also, if there are conflicts the | ||
| 120 | ;; "last one wins" which may not produce the desired setting. | ||
| 121 | ;; | ||
| 122 | ;; So...There is a minimal way of being able to save indentation values and | ||
| 123 | ;; to reload them in another buffer or at another point in time. | ||
| 124 | ;; | ||
| 125 | ;; Use `sh-name-style' to give a name to the indentation settings of | ||
| 126 | ;; the current buffer. | ||
| 127 | ;; Use `sh-load-style' to load indentation settings for the current | ||
| 128 | ;; buffer from a specific style. | ||
| 129 | ;; Use `sh-save-styles-to-buffer' to write all the styles to a buffer | ||
| 130 | ;; in lisp code. You can then store it in a file and later use | ||
| 131 | ;; `load-file' to load it. | ||
| 132 | ;; | ||
| 133 | ;; Indentation variables - buffer local or global? | ||
| 134 | ;; ---------------------------------------------- | ||
| 135 | ;; I think that often having them buffer-local makes sense, | ||
| 136 | ;; especially if one is using `sh-learn-buffer-indent'. However, if | ||
| 137 | ;; a user sets values using customization, these changes won't appear | ||
| 138 | ;; to work if the variables are already local! | ||
| 139 | ;; | ||
| 140 | ;; To get round this, there is a variable `sh-make-vars-local' and 2 | ||
| 141 | ;; functions: `sh-make-vars-local' and `sh-reset-indent-vars-to-global-values'. | ||
| 142 | ;; | ||
| 143 | ;; If `sh-make-vars-local' is non-nil, then these variables become | ||
| 144 | ;; buffer local when the mode is established. | ||
| 145 | ;; If this is nil, then the variables are global. At any time you | ||
| 146 | ;; can make them local with the command `sh-make-vars-local'. | ||
| 147 | ;; Conversely, to update with the global values you can use the | ||
| 148 | ;; command `sh-reset-indent-vars-to-global-values'. | ||
| 149 | ;; | ||
| 150 | ;; This may be awkward, but the intent is to cover all cases. | ||
| 151 | ;; | ||
| 152 | ;; Awkward things, pitfalls | ||
| 153 | ;; ------------------------ | ||
| 154 | ;; Indentation for a sh script is complicated for a number of reasons: | ||
| 155 | ;; | ||
| 156 | ;; 1. You can't format by simply looking at symbols, you need to look | ||
| 157 | ;; at keywords. [This is not the case for rc and es shells.] | ||
| 158 | ;; 2. The character ")" is used both as a matched pair "(" ... ")" and | ||
| 159 | ;; as a stand-alone symbol (in a case alternative). This makes | ||
| 160 | ;; things quite tricky! | ||
| 161 | ;; 3. Here-documents in a script should be treated "as is", and when | ||
| 162 | ;; they terminate we want to revert to the indentation of the line | ||
| 163 | ;; containing the "<<" symbol. | ||
| 164 | ;; 4. A line may be continued using the "\". | ||
| 165 | ;; 5. The character "#" (outside a string) normally starts a comment, | ||
| 166 | ;; but it doesn't in the sequence "$#"! | ||
| 167 | ;; | ||
| 168 | ;; To try and address points 2 3 and 5 I used a feature that cperl mode | ||
| 169 | ;; uses, that of a text's syntax property. This, however, has 2 | ||
| 170 | ;; disadvantages: | ||
| 171 | ;; 1. We need to scan the buffer to find which ")" symbols belong to a | ||
| 172 | ;; case alternative, to find any here documents, and handle "$#". | ||
| 173 | ;; 2. Setting the text property makes the buffer modified. If the | ||
| 174 | ;; buffer is read-only buffer we have to cheat and bypass the read-only | ||
| 175 | ;; status. This is for cases where the buffer started read-only buffer | ||
| 176 | ;; but the user issued `toggle-read-only'. | ||
| 177 | ;; | ||
| 178 | ;; Bugs | ||
| 179 | ;; ---- | ||
| 180 | ;; - Here-documents are marked with text properties face and syntax | ||
| 181 | ;; table. This serves 2 purposes: stopping indentation while inside | ||
| 182 | ;; them, and moving over them when finding the previous line to | ||
| 183 | ;; indent to. However, if font-lock mode is active when there is | ||
| 184 | ;; any change inside the here-document font-lock clears that | ||
| 185 | ;; property. This causes several problems: lines after the here-doc | ||
| 186 | ;; will not be re-indentation properly, words in the here-doc region | ||
| 187 | ;; may be fontified, and indentation may occur within the | ||
| 188 | ;; here-document. | ||
| 189 | ;; I'm not sure how to fix this, perhaps using the point-entered | ||
| 190 | ;; property. Anyway, if you use font lock and change a | ||
| 191 | ;; here-document, I recommend using M-x sh-rescan-buffer after the | ||
| 192 | ;; changes are made. Similarly, when using higlight-changes-mode, | ||
| 193 | ;; changes inside a here-document may confuse shell indenting, but again | ||
| 194 | ;; using `sh-rescan-buffer' should fix them. | ||
| 195 | ;; | ||
| 196 | ;; - Indenting many lines is slow. It currently does each line | ||
| 197 | ;; independently, rather than saving state information. | ||
| 198 | ;; | ||
| 199 | ;; - `sh-learn-buffer-indent' is extremely slow. | ||
| 200 | ;; | ||
| 201 | ;; Richard Sharman <rsharman@pobox.com> June 1999. | ||
| 202 | |||
| 41 | ;;; Code: | 203 | ;;; Code: |
| 42 | 204 | ||
| 43 | ;; page 1: variables and settings | 205 | ;; page 1: variables and settings |
| 44 | ;; page 2: mode-command and utility functions | 206 | ;; page 2: indentation stuff |
| 45 | ;; page 3: statement syntax-commands for various shells | 207 | ;; page 3: mode-command and utility functions |
| 46 | ;; page 4: various other commands | 208 | ;; page 4: statement syntax-commands for various shells |
| 209 | ;; page 5: various other commands | ||
| 47 | 210 | ||
| 48 | (require 'executable) | 211 | (require 'executable) |
| 49 | 212 | ||
| 50 | (defvar sh-mode-hook nil | ||
| 51 | "*Hook run by `sh-mode'.") | ||
| 52 | 213 | ||
| 53 | (defvar sh-set-shell-hook nil | ||
| 54 | "*Hook run by `sh-set-shell'.") | ||
| 55 | 214 | ||
| 56 | (defgroup sh nil | 215 | (defgroup sh nil |
| 57 | "Shell programming utilities" | 216 | "Shell programming utilities" |
| @@ -182,7 +341,7 @@ See `sh-feature'." | |||
| 182 | :type '(repeat (cons (symbol :tag "Shell") | 341 | :type '(repeat (cons (symbol :tag "Shell") |
| 183 | regexp)) | 342 | regexp)) |
| 184 | :group 'sh-script | 343 | :group 'sh-script |
| 185 | :version "20.3") | 344 | :version "20.4") |
| 186 | 345 | ||
| 187 | (defvar sh-shell-variables nil | 346 | (defvar sh-shell-variables nil |
| 188 | "Alist of shell variable names that should be included in completion. | 347 | "Alist of shell variable names that should be included in completion. |
| @@ -277,6 +436,10 @@ the car and cdr are the same symbol.") | |||
| 277 | (define-key map "\C-c\C-i" 'sh-if) | 436 | (define-key map "\C-c\C-i" 'sh-if) |
| 278 | (define-key map "\C-c\C-f" 'sh-for) | 437 | (define-key map "\C-c\C-f" 'sh-for) |
| 279 | (define-key map "\C-c\C-c" 'sh-case) | 438 | (define-key map "\C-c\C-c" 'sh-case) |
| 439 | (define-key map "\C-c?" 'sh-show-indent) | ||
| 440 | (define-key map "\C-c=" 'sh-set-indent) | ||
| 441 | (define-key map "\C-c<" 'sh-learn-line-indent) | ||
| 442 | (define-key map "\C-c>" 'sh-learn-buffer-indent) | ||
| 280 | 443 | ||
| 281 | (define-key map "=" 'sh-assignment) | 444 | (define-key map "=" 'sh-assignment) |
| 282 | (define-key map "\C-c+" 'sh-add) | 445 | (define-key map "\C-c+" 'sh-add) |
| @@ -289,8 +452,10 @@ the car and cdr are the same symbol.") | |||
| 289 | (define-key map "'" 'skeleton-pair-insert-maybe) | 452 | (define-key map "'" 'skeleton-pair-insert-maybe) |
| 290 | (define-key map "`" 'skeleton-pair-insert-maybe) | 453 | (define-key map "`" 'skeleton-pair-insert-maybe) |
| 291 | (define-key map "\"" 'skeleton-pair-insert-maybe) | 454 | (define-key map "\"" 'skeleton-pair-insert-maybe) |
| 455 | (define-key map ")" 'sh-electric-rparen) | ||
| 456 | (define-key map "<" 'sh-electric-less) | ||
| 457 | (define-key map "#" 'sh-electric-hash) | ||
| 292 | 458 | ||
| 293 | (define-key map "\t" 'sh-indent-line) | ||
| 294 | (substitute-key-definition 'complete-tag 'comint-dynamic-complete | 459 | (substitute-key-definition 'complete-tag 'comint-dynamic-complete |
| 295 | map (current-global-map)) | 460 | map (current-global-map)) |
| 296 | (substitute-key-definition 'newline-and-indent 'sh-newline-and-indent | 461 | (substitute-key-definition 'newline-and-indent 'sh-newline-and-indent |
| @@ -378,7 +543,7 @@ sign. See `sh-feature'." | |||
| 378 | 543 | ||
| 379 | 544 | ||
| 380 | (defvar sh-header-marker nil | 545 | (defvar sh-header-marker nil |
| 381 | "When non-`nil' is the end of header for prepending by \\[sh-execute-region]. | 546 | "When non-nil is the end of header for prepending by \\[sh-execute-region]. |
| 382 | That command is also used for setting this variable.") | 547 | That command is also used for setting this variable.") |
| 383 | 548 | ||
| 384 | 549 | ||
| @@ -659,6 +824,343 @@ See `sh-feature'.") | |||
| 659 | ;; efficiently. So we only do it properly for `#' in variable references and | 824 | ;; efficiently. So we only do it properly for `#' in variable references and |
| 660 | ;; do it efficiently by anchoring the regexp to the left. | 825 | ;; do it efficiently by anchoring the regexp to the left. |
| 661 | '(("\\${?[^}#\n\t ]*\\(##?\\)" 1 (1 . nil)))) | 826 | '(("\\${?[^}#\n\t ]*\\(##?\\)" 1 (1 . nil)))) |
| 827 | |||
| 828 | (defgroup sh-indentation nil | ||
| 829 | "Variables controlling indentation in shell scripts. | ||
| 830 | |||
| 831 | Note: customizing these variables will not affect existing buffers if | ||
| 832 | `sh-make-vars-local' is no-nil. See the documentation for | ||
| 833 | variable `sh-make-vars-local', command `sh-make-vars-local' | ||
| 834 | and command `sh-reset-indent-vars-to-global-values'." | ||
| 835 | :group 'sh-script) | ||
| 836 | |||
| 837 | |||
| 838 | (defcustom sh-set-shell-hook nil | ||
| 839 | "*Hook run by `sh-set-shell'." | ||
| 840 | :type 'hook | ||
| 841 | :group 'sh-script) | ||
| 842 | |||
| 843 | (defcustom sh-mode-hook nil | ||
| 844 | "*Hook run by `sh-mode'." | ||
| 845 | :type 'hook | ||
| 846 | :group 'sh-script) | ||
| 847 | |||
| 848 | (defcustom sh-learn-basic-offset nil | ||
| 849 | "*When `sh-guess-basic-offset' should learn `sh-basic-offset'. | ||
| 850 | |||
| 851 | nil mean: never. | ||
| 852 | t means: only if there seems to be an obvious value. | ||
| 853 | Anything else means: whenever we have a \"good guess\" as to the value." | ||
| 854 | :type '(choice | ||
| 855 | (const :tag "Never" nil) | ||
| 856 | (const :tag "Only if sure" t) | ||
| 857 | (const :tag "If have a good guess" usually) | ||
| 858 | ) | ||
| 859 | :group 'sh-indentation) | ||
| 860 | |||
| 861 | (defcustom sh-popup-occur-buffer nil | ||
| 862 | "*Controls when `sh-learn-buffer-indent' poos the *indent* buffer. | ||
| 863 | If t it is always shown. If nil, it is shown only when there | ||
| 864 | are conflicts." | ||
| 865 | :type '(choice | ||
| 866 | (const :tag "Only when there are conflicts." nil) | ||
| 867 | (const :tag "Always" t) | ||
| 868 | ) | ||
| 869 | :group 'sh-indentation) | ||
| 870 | |||
| 871 | (defcustom sh-blink t | ||
| 872 | "*If non-nil, `sh-show-indent' shows the line indentation is relative to. | ||
| 873 | The position on the line is not necessarily meaningful. | ||
| 874 | In some cases the line will be the matching keyword, but this is not | ||
| 875 | always the case." | ||
| 876 | :type 'boolean | ||
| 877 | :group 'sh-indentation) | ||
| 878 | |||
| 879 | (defcustom sh-first-lines-indent 0 | ||
| 880 | "*The indentation of the first non-blank non-comment line. | ||
| 881 | Usually 0 meaning first column. | ||
| 882 | Can be set to a number, or to nil which means leave it as is." | ||
| 883 | :type '(choice | ||
| 884 | (const :tag "Leave as is" nil) | ||
| 885 | (integer :tag "Column number" | ||
| 886 | :menu-tag "Indent to this col (0 means first col)" ) | ||
| 887 | ) | ||
| 888 | :group 'sh-indentation) | ||
| 889 | |||
| 890 | |||
| 891 | (defcustom sh-basic-offset 4 | ||
| 892 | "*The default indentation incrementation. | ||
| 893 | This value is used for the + and - symbols in an indentation variable." | ||
| 894 | :type 'integer | ||
| 895 | :group 'sh-indentation) | ||
| 896 | |||
| 897 | (defcustom sh-indent-comment nil | ||
| 898 | "*How a comment line is to be indented. | ||
| 899 | nil means leave it as it is; | ||
| 900 | t means indent it as a normal line, aligning it to previous non-blank | ||
| 901 | non-comment line; | ||
| 902 | a number means align to that column, e.g. 0 means fist column." | ||
| 903 | :type '(choice | ||
| 904 | (const :tag "Leave as is." nil) | ||
| 905 | (const :tag "Indent as a normal line." t) | ||
| 906 | (integer :menu-tag "Indent to this col (0 means first col)." | ||
| 907 | :tag "Indent to column number.") ) | ||
| 908 | :group 'sh-indentation) | ||
| 909 | |||
| 910 | |||
| 911 | (defvar sh-debug nil | ||
| 912 | "Enable lots of debug messages - if function `sh-debug' is enabled.") | ||
| 913 | |||
| 914 | |||
| 915 | ;; Uncomment this defun and comment the defmacro for debugging. | ||
| 916 | ;; (defun sh-debug (&rest args) | ||
| 917 | ;; "For debugging: display message ARGS if variable SH-DEBUG is non-nil." | ||
| 918 | ;; (if sh-debug | ||
| 919 | ;; (apply 'message args))) | ||
| 920 | (defmacro sh-debug (&rest args)) | ||
| 921 | |||
| 922 | (setq sh-symbol-list | ||
| 923 | '( | ||
| 924 | (const :tag "+ " :value + | ||
| 925 | :menu-tag "+ Indent right by sh-basic-offset") | ||
| 926 | (const :tag "- " :value - | ||
| 927 | :menu-tag "- Indent left by sh-basic-offset") | ||
| 928 | (const :tag "++" :value ++ | ||
| 929 | :menu-tag "++ Indent right twice sh-basic-offset") | ||
| 930 | (const :tag "--" :value -- | ||
| 931 | :menu-tag "-- Indent left twice sh-basic-offset") | ||
| 932 | (const :tag "* " :value * | ||
| 933 | :menu-tag "* Indent right half sh-basic-offset") | ||
| 934 | (const :tag "/ " :value / | ||
| 935 | :menu-tag "/ Indent left half sh-basic-offset") | ||
| 936 | )) | ||
| 937 | |||
| 938 | (defcustom sh-indent-for-else 0 | ||
| 939 | "*How much to indent an else relative to an if. Usually 0." | ||
| 940 | :type `(choice | ||
| 941 | (integer :menu-tag "A number (positive=>indent right)" | ||
| 942 | :tag "A number") | ||
| 943 | (const :tag "--") ;; separator! | ||
| 944 | ,@ sh-symbol-list | ||
| 945 | ) | ||
| 946 | :group 'sh-indentation) | ||
| 947 | |||
| 948 | (setq sh-number-or-symbol-list | ||
| 949 | (append (list '( | ||
| 950 | integer :menu-tag "A number (positive=>indent right)" | ||
| 951 | :tag "A number") | ||
| 952 | '(const :tag "--") ;; separator | ||
| 953 | ) | ||
| 954 | sh-symbol-list)) | ||
| 955 | |||
| 956 | (defcustom sh-indent-for-fi 0 | ||
| 957 | "*How much to indent a fi relative to an if. Usually 0." | ||
| 958 | :type `(choice ,@ sh-number-or-symbol-list ) | ||
| 959 | :group 'sh-indentation) | ||
| 960 | |||
| 961 | (defcustom sh-indent-for-done '0 | ||
| 962 | "*How much to indent a done relative to its matching stmt. Usually 0." | ||
| 963 | :type `(choice ,@ sh-number-or-symbol-list ) | ||
| 964 | :group 'sh-indentation) | ||
| 965 | |||
| 966 | (defcustom sh-indent-after-else '+ | ||
| 967 | "*How much to indent a statement after an else statement." | ||
| 968 | :type `(choice ,@ sh-number-or-symbol-list ) | ||
| 969 | :group 'sh-indentation) | ||
| 970 | |||
| 971 | (defcustom sh-indent-after-if '+ | ||
| 972 | "*How much to indent a statement after an if statement. | ||
| 973 | This includes lines after else and elif statements, too, but | ||
| 974 | does not affect then else elif or fi statements themselves." | ||
| 975 | :type `(choice ,@ sh-number-or-symbol-list ) | ||
| 976 | :group 'sh-indentation) | ||
| 977 | |||
| 978 | (defcustom sh-indent-for-then '+ | ||
| 979 | "*How much to indent an then relative to an if." | ||
| 980 | :type `(choice ,@ sh-number-or-symbol-list ) | ||
| 981 | :group 'sh-indentation) | ||
| 982 | |||
| 983 | (defcustom sh-indent-for-do '* | ||
| 984 | "*How much to indent a do statement. | ||
| 985 | This is relative to the statement before the do, i.e. the | ||
| 986 | while until or for statement." | ||
| 987 | :type `(choice ,@ sh-number-or-symbol-list) | ||
| 988 | :group 'sh-indentation) | ||
| 989 | |||
| 990 | (defcustom sh-indent-after-do '* | ||
| 991 | "*How much to indent a line after a do statement. | ||
| 992 | This is used when the do is the first word of the line. | ||
| 993 | This is relative to the statement before the do, e.g. a | ||
| 994 | while for repeat or select statement." | ||
| 995 | :type `(choice ,@ sh-number-or-symbol-list) | ||
| 996 | :group 'sh-indentation) | ||
| 997 | |||
| 998 | (defcustom sh-indent-after-loop-construct '+ | ||
| 999 | "*How much to indent a statement after a loop construct. | ||
| 1000 | |||
| 1001 | This variable is used when the keyword \"do\" is on the same line as the | ||
| 1002 | loop statement (e.g. \"until\", \"while\" or \"for\"). | ||
| 1003 | If the do is on a line by itself, then `sh-indent-after-do' is used instead." | ||
| 1004 | :type `(choice ,@ sh-number-or-symbol-list) | ||
| 1005 | :group 'sh-indentation) | ||
| 1006 | |||
| 1007 | |||
| 1008 | (defcustom sh-indent-after-done 0 | ||
| 1009 | "*How much to indent a statement after a \"done\" keyword. | ||
| 1010 | Normally this is 0, which aligns the \"done\" to the matching | ||
| 1011 | looping construct line. | ||
| 1012 | Setting it non-zero allows you to have the \"do\" statement on a line | ||
| 1013 | by itself and align the done under to do." | ||
| 1014 | :type `(choice ,@ sh-number-or-symbol-list) | ||
| 1015 | :group 'sh-indentation) | ||
| 1016 | |||
| 1017 | (defcustom sh-indent-for-case-label '+ | ||
| 1018 | "*How much to indent a case label statement. | ||
| 1019 | This is relative to the line containing the case statement." | ||
| 1020 | :type `(choice ,@ sh-number-or-symbol-list) | ||
| 1021 | :group 'sh-indentation) | ||
| 1022 | |||
| 1023 | (defcustom sh-indent-for-case-alt '++ | ||
| 1024 | "*How much to indent statements after the case label. | ||
| 1025 | This is relative to the line containing the case statement." | ||
| 1026 | :type `(choice ,@ sh-number-or-symbol-list) | ||
| 1027 | :group 'sh-indentation) | ||
| 1028 | |||
| 1029 | |||
| 1030 | (defcustom sh-indent-for-continuation '+ | ||
| 1031 | "*How much to indent for a continuation statement." | ||
| 1032 | :type `(choice ,@ sh-number-or-symbol-list) | ||
| 1033 | :group 'sh-indentation) | ||
| 1034 | |||
| 1035 | (defcustom sh-indent-after-open '+ | ||
| 1036 | "*How much to indent after a line with an opening parenthesis or brace. | ||
| 1037 | For an open paren after a function `sh-indent-after-function' is used." | ||
| 1038 | :type `(choice ,@ sh-number-or-symbol-list) | ||
| 1039 | :group 'sh-indentation) | ||
| 1040 | |||
| 1041 | (defcustom sh-indent-after-function '+ | ||
| 1042 | "*How much to indent after a function line." | ||
| 1043 | :type `(choice ,@ sh-number-or-symbol-list) | ||
| 1044 | :group 'sh-indentation) | ||
| 1045 | |||
| 1046 | ;; These 2 are for the rc shell: | ||
| 1047 | |||
| 1048 | (defcustom sh-indent-after-switch '+ | ||
| 1049 | "*How much to indent a case statement relative to the switch statement. | ||
| 1050 | This is for the rc shell." | ||
| 1051 | :type `(choice ,@ sh-number-or-symbol-list) | ||
| 1052 | :group 'sh-indentation) | ||
| 1053 | |||
| 1054 | (defcustom sh-indent-after-case '+ | ||
| 1055 | "*How much to indent a statement relative to the case statement. | ||
| 1056 | This is for the rc shell." | ||
| 1057 | :type `(choice ,@ sh-number-or-symbol-list) | ||
| 1058 | :group 'sh-indentation) | ||
| 1059 | |||
| 1060 | (defface sh-heredoc-face | ||
| 1061 | '((((class color) | ||
| 1062 | (background dark)) | ||
| 1063 | (:foreground "yellow" :bold t)) | ||
| 1064 | (((class color) | ||
| 1065 | (background light)) | ||
| 1066 | (:foreground "tan" )) | ||
| 1067 | (t | ||
| 1068 | (:bold t))) | ||
| 1069 | "Face to show a here-document" | ||
| 1070 | :group 'sh-indentation) | ||
| 1071 | |||
| 1072 | (defface sh-st-face | ||
| 1073 | '((((class color) | ||
| 1074 | (background dark)) | ||
| 1075 | (:foreground "yellow" :bold t)) | ||
| 1076 | (((class color) | ||
| 1077 | (background light)) | ||
| 1078 | (:foreground "tan" )) | ||
| 1079 | (t | ||
| 1080 | (:bold t))) | ||
| 1081 | "Face to show characters with special syntax properties." | ||
| 1082 | :group 'sh-indentation) | ||
| 1083 | |||
| 1084 | |||
| 1085 | ;; Internal use - not designed to be changed by the user: | ||
| 1086 | |||
| 1087 | ;; These are used for the syntax table stuff (derived from cperl-mode). | ||
| 1088 | ;; Note: parse-sexp-lookup-properties must be set to t for it to work. | ||
| 1089 | (defconst sh-here-doc-syntax '(15)) ;; generic string | ||
| 1090 | (defconst sh-st-punc '(1)) | ||
| 1091 | (defconst sh-special-syntax sh-st-punc) | ||
| 1092 | |||
| 1093 | (defun sh-mkword-regexpr (word) | ||
| 1094 | "Make a regexp which matches WORD as a word. | ||
| 1095 | This specifically excludes an occurance of WORD followed by | ||
| 1096 | punctuation characters like '-'." | ||
| 1097 | (concat word "\\([^-a-z0-9_]\\|$\\)")) | ||
| 1098 | |||
| 1099 | (defun sh-mkword-regexp (word) | ||
| 1100 | "Make a regexp which matches WORD as a word. | ||
| 1101 | This specifically excludes an occurance of WORD followed by | ||
| 1102 | or preceded by punctuation characters like '-'." | ||
| 1103 | (concat "\\(^\\|[^-a-z0-9_]\\)" word "\\([^-a-z0-9_]\\|$\\)")) | ||
| 1104 | |||
| 1105 | (setq sh-re-done (sh-mkword-regexpr "done")) | ||
| 1106 | |||
| 1107 | |||
| 1108 | (defconst sh-kws-for-done | ||
| 1109 | '( | ||
| 1110 | (sh . ( "while" "until" "for" ) ) | ||
| 1111 | (bash . ( "while" "until" "for" "select" ) ) | ||
| 1112 | (ksh88 . ( "while" "until" "for" "select" ) ) | ||
| 1113 | (zsh . ( "while" "until" "for" "repeat" "select" ) ) | ||
| 1114 | ) | ||
| 1115 | "Which keywords can match the word `done' in this shell." | ||
| 1116 | ) | ||
| 1117 | |||
| 1118 | |||
| 1119 | (defconst sh-indent-supported | ||
| 1120 | '( | ||
| 1121 | (sh . t) | ||
| 1122 | (csh . nil) | ||
| 1123 | (rc . t) | ||
| 1124 | ) | ||
| 1125 | "Shell types that shell indenting can do something with." | ||
| 1126 | ) | ||
| 1127 | |||
| 1128 | (defconst sh-electric-rparen-needed | ||
| 1129 | '( | ||
| 1130 | (sh . t)) | ||
| 1131 | "Non-nil if the shell type needs an electric handling of case alternatives." | ||
| 1132 | ) | ||
| 1133 | |||
| 1134 | (defconst sh-var-list | ||
| 1135 | '( | ||
| 1136 | sh-basic-offset sh-first-lines-indent sh-indent-after-case | ||
| 1137 | sh-indent-after-do sh-indent-after-done | ||
| 1138 | sh-indent-after-else | ||
| 1139 | sh-indent-after-if | ||
| 1140 | sh-indent-after-loop-construct | ||
| 1141 | sh-indent-after-open | ||
| 1142 | sh-indent-comment | ||
| 1143 | sh-indent-for-case-alt | ||
| 1144 | sh-indent-for-case-label | ||
| 1145 | sh-indent-for-continuation | ||
| 1146 | sh-indent-for-do | ||
| 1147 | sh-indent-for-done | ||
| 1148 | sh-indent-for-else | ||
| 1149 | sh-indent-for-fi | ||
| 1150 | sh-indent-for-then | ||
| 1151 | ) | ||
| 1152 | "A list of variables used by script mode to control indentation. | ||
| 1153 | This list is used when switching between buffer-local and global | ||
| 1154 | values of variables, and for the commands using indenation styles.") | ||
| 1155 | |||
| 1156 | (defvar sh-make-vars-local t | ||
| 1157 | "*Controls whether indentation variables are local to the buffer. | ||
| 1158 | If non-nil, indentation variables are made local initially. | ||
| 1159 | If nil, you can later make the variables local by invoking | ||
| 1160 | command `sh-make-vars-local'. | ||
| 1161 | The default is t because I assume that in one Emacs session one is | ||
| 1162 | frequently editing existing scripts with different styles.") | ||
| 1163 | |||
| 662 | 1164 | ||
| 663 | ;; mode-command and utility functions | 1165 | ;; mode-command and utility functions |
| 664 | 1166 | ||
| @@ -693,6 +1195,15 @@ following commands are available, based on the current shell's syntax: | |||
| 693 | \\[sh-until] until loop | 1195 | \\[sh-until] until loop |
| 694 | \\[sh-while] while loop | 1196 | \\[sh-while] while loop |
| 695 | 1197 | ||
| 1198 | For sh and rc shells indentation commands are: | ||
| 1199 | \\[sh-show-indent] Show the variable controlling this line's indentation. | ||
| 1200 | \\[sh-set-indent] Set then variable controlling this line's indentation. | ||
| 1201 | \\[sh-learn-line-indent] Change the indentation variable so this line | ||
| 1202 | would indent to the way it currently is. | ||
| 1203 | \\[sh-learn-buffer-indent] Set the indentation variables so the | ||
| 1204 | buffer indents as it currently is indendeted. | ||
| 1205 | |||
| 1206 | |||
| 696 | \\[backward-delete-char-untabify] Delete backward one position, even if it was a tab. | 1207 | \\[backward-delete-char-untabify] Delete backward one position, even if it was a tab. |
| 697 | \\[sh-newline-and-indent] Delete unquoted space and indent new line same as this one. | 1208 | \\[sh-newline-and-indent] Delete unquoted space and indent new line same as this one. |
| 698 | \\[sh-end-of-command] Go to end of successive commands. | 1209 | \\[sh-end-of-command] Go to end of successive commands. |
| @@ -734,9 +1245,10 @@ with your script for an edit-interpret-debug cycle." | |||
| 734 | (make-local-variable 'sh-shell-variables) | 1245 | (make-local-variable 'sh-shell-variables) |
| 735 | (make-local-variable 'sh-shell-variables-initialized) | 1246 | (make-local-variable 'sh-shell-variables-initialized) |
| 736 | (make-local-variable 'imenu-generic-expression) | 1247 | (make-local-variable 'imenu-generic-expression) |
| 1248 | (make-local-variable 'sh-electric-rparen-needed-here) | ||
| 1249 | (make-local-variable 'sh-indent-supported-here) | ||
| 737 | (setq major-mode 'sh-mode | 1250 | (setq major-mode 'sh-mode |
| 738 | mode-name "Shell-script" | 1251 | mode-name "Shell-script" |
| 739 | indent-line-function 'sh-indent-line | ||
| 740 | ;; not very clever, but enables wrapping skeletons around regions | 1252 | ;; not very clever, but enables wrapping skeletons around regions |
| 741 | indent-region-function (lambda (b e) | 1253 | indent-region-function (lambda (b e) |
| 742 | (save-excursion | 1254 | (save-excursion |
| @@ -765,7 +1277,9 @@ with your script for an edit-interpret-debug cycle." | |||
| 765 | skeleton-further-elements '((< '(- (min sh-indentation | 1277 | skeleton-further-elements '((< '(- (min sh-indentation |
| 766 | (current-column))))) | 1278 | (current-column))))) |
| 767 | skeleton-filter 'sh-feature | 1279 | skeleton-filter 'sh-feature |
| 768 | skeleton-newline-indent-rigidly t) | 1280 | skeleton-newline-indent-rigidly t |
| 1281 | sh-electric-rparen-needed-here nil | ||
| 1282 | sh-indent-supported-here nil) | ||
| 769 | (make-local-variable 'parse-sexp-ignore-comments) | 1283 | (make-local-variable 'parse-sexp-ignore-comments) |
| 770 | (setq parse-sexp-ignore-comments t) | 1284 | (setq parse-sexp-ignore-comments t) |
| 771 | ;; Parse or insert magic number for exec, and set all variables depending | 1285 | ;; Parse or insert magic number for exec, and set all variables depending |
| @@ -783,8 +1297,7 @@ with your script for an edit-interpret-debug cycle." | |||
| 783 | (progn | 1297 | (progn |
| 784 | ;; If we don't know the shell for this file, set the syntax | 1298 | ;; If we don't know the shell for this file, set the syntax |
| 785 | ;; table anyway, for the user's normal choice of shell. | 1299 | ;; table anyway, for the user's normal choice of shell. |
| 786 | (set-syntax-table (or (sh-feature sh-mode-syntax-table) | 1300 | (set-syntax-table (sh-feature sh-mode-syntax-table)) |
| 787 | (standard-syntax-table))) | ||
| 788 | ;; And avoid indent-new-comment-line (at least) losing. | 1301 | ;; And avoid indent-new-comment-line (at least) losing. |
| 789 | (setq comment-start-skip "#+[\t ]*")))) | 1302 | (setq comment-start-skip "#+[\t ]*")))) |
| 790 | (run-hooks 'sh-mode-hook)) | 1303 | (run-hooks 'sh-mode-hook)) |
| @@ -872,6 +1385,34 @@ Calls the value of `sh-set-shell-hook' if set." | |||
| 872 | ; (and (boundp 'font-lock-mode) | 1385 | ; (and (boundp 'font-lock-mode) |
| 873 | ; font-lock-mode | 1386 | ; font-lock-mode |
| 874 | ; (font-lock-mode (font-lock-mode 0))) | 1387 | ; (font-lock-mode (font-lock-mode 0))) |
| 1388 | (if (setq sh-indent-supported-here (sh-feature sh-indent-supported)) | ||
| 1389 | (progn | ||
| 1390 | (message "Setting up indent for shell type %s" sh-shell) | ||
| 1391 | (make-local-variable 'sh-kw-alist) | ||
| 1392 | (make-local-variable 'sh-regexp-for-done) | ||
| 1393 | (make-local-variable 'parse-sexp-lookup-properties) | ||
| 1394 | (setq sh-electric-rparen-needed-here | ||
| 1395 | (sh-feature sh-electric-rparen-needed)) | ||
| 1396 | (setq parse-sexp-lookup-properties t) | ||
| 1397 | (sh-scan-buffer) | ||
| 1398 | (setq sh-kw-alist (sh-feature sh-kw)) | ||
| 1399 | (let ((regexp (sh-feature sh-kws-for-done))) | ||
| 1400 | (if regexp | ||
| 1401 | (setq sh-regexp-for-done | ||
| 1402 | (sh-mkword-regexpr (regexp-opt regexp t))))) | ||
| 1403 | (message "setting up indent stuff") | ||
| 1404 | ;; sh-mode has already made indent-line-function local | ||
| 1405 | ;; but do it in case this is called before that. | ||
| 1406 | (make-local-variable 'indent-line-function) | ||
| 1407 | (setq indent-line-function 'sh-indent-line) | ||
| 1408 | ;; This is very inefficient, but this at least makes indent-region work: | ||
| 1409 | (make-local-variable 'indent-region-function) | ||
| 1410 | (setq indent-region-function nil) | ||
| 1411 | (if sh-make-vars-local | ||
| 1412 | (sh-make-vars-local)) | ||
| 1413 | (message "Indentation setup for shell type %s" sh-shell)) | ||
| 1414 | (message "No indentation for this shell type.") | ||
| 1415 | (setq indent-line-function 'sh-basic-indent-line)) | ||
| 875 | (run-hooks 'sh-set-shell-hook)) | 1416 | (run-hooks 'sh-set-shell-hook)) |
| 876 | 1417 | ||
| 877 | 1418 | ||
| @@ -990,7 +1531,7 @@ in ALIST." | |||
| 990 | skeleton) | 1531 | skeleton) |
| 991 | 1532 | ||
| 992 | 1533 | ||
| 993 | (defun sh-indent-line () | 1534 | (defun sh-basic-indent-line () |
| 994 | "Indent a line for Sh mode (shell script mode). | 1535 | "Indent a line for Sh mode (shell script mode). |
| 995 | Indent as far as preceding non-empty line, then by steps of `sh-indentation'. | 1536 | Indent as far as preceding non-empty line, then by steps of `sh-indentation'. |
| 996 | Lines containing only comments are considered empty." | 1537 | Lines containing only comments are considered empty." |
| @@ -1061,6 +1602,1947 @@ region, clear header." | |||
| 1061 | "Is point preceded by an odd number of backslashes?" | 1602 | "Is point preceded by an odd number of backslashes?" |
| 1062 | (eq -1 (% (save-excursion (skip-chars-backward "\\\\")) 2))) | 1603 | (eq -1 (% (save-excursion (skip-chars-backward "\\\\")) 2))) |
| 1063 | 1604 | ||
| 1605 | ;; Indentation stuff. | ||
| 1606 | (defun sh-must-be-shell-mode () | ||
| 1607 | "Signal an error if not in Shell-script mode." | ||
| 1608 | (unless (eq major-mode 'sh-mode) | ||
| 1609 | (error "This buffer is not in Shell-script mode"))) | ||
| 1610 | |||
| 1611 | (defun sh-must-support-indent () | ||
| 1612 | "*Signal an error if the shell type for this buffer is not supported. | ||
| 1613 | Also, the buffer must be in Shell-script mode." | ||
| 1614 | (sh-must-be-shell-mode) | ||
| 1615 | (unless sh-indent-supported-here | ||
| 1616 | (error "This buffer's shell type is not supported for this command"))) | ||
| 1617 | |||
| 1618 | (defun sh-make-vars-local () | ||
| 1619 | "Make the indentation variables local to this buffer. | ||
| 1620 | Normally they already are local. This command is provided in case | ||
| 1621 | variable `sh-make-vars-local' has been set to nil. | ||
| 1622 | |||
| 1623 | To revert all these variables to the global values, use | ||
| 1624 | command `sh-reset-indent-vars-to-global-values'." | ||
| 1625 | (interactive) | ||
| 1626 | (sh-must-be-shell-mode) | ||
| 1627 | (mapcar 'make-local-variable sh-var-list) | ||
| 1628 | (message "Indentation variable are now local.")) | ||
| 1629 | |||
| 1630 | (defun sh-reset-indent-vars-to-global-values () | ||
| 1631 | "Reset local indenatation variables to the global values. | ||
| 1632 | Then, if variable `sh-make-vars-local' is non-nil, make them local." | ||
| 1633 | (interactive) | ||
| 1634 | (sh-must-be-shell-mode) | ||
| 1635 | (mapcar 'kill-local-variable sh-var-list) | ||
| 1636 | (if sh-make-vars-local | ||
| 1637 | (mapcar 'make-local-variable sh-var-list))) | ||
| 1638 | |||
| 1639 | |||
| 1640 | (defvar sh-kw-alist nil | ||
| 1641 | "A buffer-local, since it is shell-type dependent, list of keywords.") | ||
| 1642 | |||
| 1643 | (defvar sh-regexp-for-done nil | ||
| 1644 | "A buffer-local regexp to match opening keyword for done.") | ||
| 1645 | |||
| 1646 | ;; Theoretically these are only needed in shell and derived modes. | ||
| 1647 | ;; However, the routines which use them are only called in those modes. | ||
| 1648 | (defconst sh-special-keywords "then\\|do") | ||
| 1649 | |||
| 1650 | ;; ( key-word first-on-this on-prev-line ) | ||
| 1651 | ;; This is used to set `sh-kw-alist' which is a list of sublists each | ||
| 1652 | ;; having 3 elements: | ||
| 1653 | ;; a keyword | ||
| 1654 | ;; a rule to check when the keyword apepars on "this" line | ||
| 1655 | ;; a rule to check when the keyword apepars on "the previous" line | ||
| 1656 | ;; The keyword is usually a string and is the first word on a line. | ||
| 1657 | ;; If this keyword appears on the line whose indenation is to be | ||
| 1658 | ;; calculated, the rule in element 2 is called. If this returns | ||
| 1659 | ;; non-zero, the resulting point (which may be changed by the rule) | ||
| 1660 | ;; is used as the default indentation. | ||
| 1661 | ;; If it returned false or the keyword was not found in the table, | ||
| 1662 | ;; then the keyword from the previous line is looked up and the rule | ||
| 1663 | ;; in element 3 is called. In this case, however, | ||
| 1664 | ;; `sh-get-indent-info' does not stop but may keepp going and test | ||
| 1665 | ;; other keywords against rules in element 3. This is because the | ||
| 1666 | ;; precending line could have, for example, an opening "if" and an | ||
| 1667 | ;; opening "while" keyword and we need to add the indentation offsets | ||
| 1668 | ;; for both. | ||
| 1669 | ;; | ||
| 1670 | (defconst sh-kw | ||
| 1671 | '( | ||
| 1672 | (sh | ||
| 1673 | ( "if" | ||
| 1674 | nil | ||
| 1675 | sh-handle-prev-if ) | ||
| 1676 | ( "elif" | ||
| 1677 | sh-handle-this-else | ||
| 1678 | sh-handle-prev-else ) | ||
| 1679 | ( "else" | ||
| 1680 | sh-handle-this-else | ||
| 1681 | sh-handle-prev-else ) | ||
| 1682 | ( "fi" | ||
| 1683 | sh-handle-this-fi | ||
| 1684 | sh-handle-prev-fi ) | ||
| 1685 | ( "then" | ||
| 1686 | sh-handle-this-then | ||
| 1687 | sh-handle-prev-then ) | ||
| 1688 | ( "(" | ||
| 1689 | nil | ||
| 1690 | sh-handle-prev-open ) | ||
| 1691 | ( "{" | ||
| 1692 | nil | ||
| 1693 | sh-handle-prev-open ) | ||
| 1694 | ( "[" | ||
| 1695 | nil | ||
| 1696 | sh-handle-prev-open ) | ||
| 1697 | ( "}" | ||
| 1698 | sh-handle-this-close | ||
| 1699 | nil ) | ||
| 1700 | ( ")" | ||
| 1701 | sh-handle-this-close | ||
| 1702 | nil ) | ||
| 1703 | ( "]" | ||
| 1704 | sh-handle-this-close | ||
| 1705 | nil ) | ||
| 1706 | ( "case" | ||
| 1707 | nil | ||
| 1708 | sh-handle-prev-case ) | ||
| 1709 | ( "esac" | ||
| 1710 | sh-handle-this-esac | ||
| 1711 | sh-handle-prev-esac ) | ||
| 1712 | ( case-label | ||
| 1713 | nil ;; ??? | ||
| 1714 | sh-handle-after-case-label ) | ||
| 1715 | ( ";;" | ||
| 1716 | nil ;; ??? | ||
| 1717 | sh-handle-prev-case-alt-end ;; ?? | ||
| 1718 | ) | ||
| 1719 | ( "done" | ||
| 1720 | sh-handle-this-done | ||
| 1721 | sh-handle-prev-done ) | ||
| 1722 | ( "do" | ||
| 1723 | sh-handle-this-do | ||
| 1724 | sh-handle-prev-do ) | ||
| 1725 | ) ;; end of sh | ||
| 1726 | |||
| 1727 | ;; Note: we don't need specific stuff for bash and zsh shells; | ||
| 1728 | ;; the regexp `sh-regexp-for-done' handles the extra keywords | ||
| 1729 | ;; these shells use. | ||
| 1730 | (rc | ||
| 1731 | ( "{" | ||
| 1732 | nil | ||
| 1733 | sh-handle-prev-open ) | ||
| 1734 | ( "}" | ||
| 1735 | sh-handle-this-close | ||
| 1736 | nil ) | ||
| 1737 | ( "case" | ||
| 1738 | sh-handle-this-rc-case | ||
| 1739 | sh-handle-prev-rc-case ) | ||
| 1740 | ) ;; end of rc | ||
| 1741 | )) | ||
| 1742 | |||
| 1743 | |||
| 1744 | (defun sh-help-string-for-variable (var) | ||
| 1745 | "Construct a string for `sh-read-variable' when changing variable VAR ." | ||
| 1746 | (let ((msg (documentation-property var 'variable-documentation)) | ||
| 1747 | (msg2 "")) | ||
| 1748 | (unless (or | ||
| 1749 | (eq var 'sh-first-lines-indent) | ||
| 1750 | (eq var 'sh-indent-comment)) | ||
| 1751 | (setq msg2 | ||
| 1752 | (format "\n | ||
| 1753 | You can enter a number (positive to increase indentenation, | ||
| 1754 | negative to decrease indentation, zero for no change to indentnation). | ||
| 1755 | |||
| 1756 | Or, you can enter one of the following symbols which are relative to | ||
| 1757 | the value of variable `sh-basic-offset' | ||
| 1758 | which in this buffer is currently %s. | ||
| 1759 | |||
| 1760 | \t%s." | ||
| 1761 | sh-basic-offset | ||
| 1762 | (mapconcat '(lambda (x) | ||
| 1763 | (nth (1- (length x)) x) ) | ||
| 1764 | sh-symbol-list "\n\t") | ||
| 1765 | ))) | ||
| 1766 | |||
| 1767 | (concat | ||
| 1768 | ;; The following shows the global not the local value! | ||
| 1769 | ;; (format "Current value of %s is %s\n\n" var (symbol-value var)) | ||
| 1770 | msg msg2))) | ||
| 1771 | |||
| 1772 | (defun sh-read-variable (var) | ||
| 1773 | "Read a new value for indentation variable VAR." | ||
| 1774 | (interactive "*variable? ") ;; to test | ||
| 1775 | (let ((minibuffer-help-form `(sh-help-string-for-variable | ||
| 1776 | (quote ,var))) | ||
| 1777 | val) | ||
| 1778 | (setq val (read-from-minibuffer | ||
| 1779 | (format "New value for %s (press %s for help): " | ||
| 1780 | var (single-key-description help-char)) | ||
| 1781 | (format "%s" (symbol-value var)) | ||
| 1782 | nil t)) | ||
| 1783 | val)) | ||
| 1784 | |||
| 1785 | |||
| 1786 | |||
| 1787 | (defun sh-in-comment-or-string (start) | ||
| 1788 | "Return non-nil if START is in a comment or string." | ||
| 1789 | (save-excursion | ||
| 1790 | (let (state) | ||
| 1791 | (beginning-of-line) | ||
| 1792 | (setq state (parse-partial-sexp (point) start nil nil nil t)) | ||
| 1793 | (or (nth 3 state)(nth 4 state))))) | ||
| 1794 | |||
| 1795 | (defun sh-goto-matching-if () | ||
| 1796 | "Go to the matching if for a fi. | ||
| 1797 | This handles nested if..fi pairs." | ||
| 1798 | (let ((found (sh-find-prev-matching "\\bif\\b" "\\bfi\\b" 1))) | ||
| 1799 | (if found | ||
| 1800 | (goto-char found)))) | ||
| 1801 | |||
| 1802 | |||
| 1803 | ;; Functions named sh-handle-this-XXX are called when the keyword on the | ||
| 1804 | ;; line whose indentation is being handled contain XXX; | ||
| 1805 | ;; those named sh-handle-prev-XXX are when XXX appears on the prevoius line. | ||
| 1806 | |||
| 1807 | (defun sh-handle-prev-if () | ||
| 1808 | (list '(+ sh-indent-after-if))) | ||
| 1809 | |||
| 1810 | (defun sh-handle-this-else () | ||
| 1811 | (if (sh-goto-matching-if) | ||
| 1812 | ;; (list "aligned to if") | ||
| 1813 | (list "aligned to if" '(+ sh-indent-for-else)) | ||
| 1814 | nil | ||
| 1815 | )) | ||
| 1816 | |||
| 1817 | (defun sh-handle-prev-else () | ||
| 1818 | (if (sh-goto-matching-if) | ||
| 1819 | (list '(+ sh-indent-after-if)) | ||
| 1820 | )) | ||
| 1821 | |||
| 1822 | (defun sh-handle-this-fi () | ||
| 1823 | (if (sh-goto-matching-if) | ||
| 1824 | (list "aligned to if" '(+ sh-indent-for-fi)) | ||
| 1825 | nil | ||
| 1826 | )) | ||
| 1827 | |||
| 1828 | (defun sh-handle-prev-fi () | ||
| 1829 | ;; Why do we have this rule? Because we must go back to the if | ||
| 1830 | ;; to get its indent. We may continue back from there. | ||
| 1831 | ;; We return nil because we don't have anything to add to result, | ||
| 1832 | ;; the side affect of setting align-point is all that matters. | ||
| 1833 | ;; we could return a comment (a string) but I can't think of a good one... | ||
| 1834 | (sh-goto-matching-if) | ||
| 1835 | nil) | ||
| 1836 | |||
| 1837 | (defun sh-handle-this-then () | ||
| 1838 | (let ((p (sh-goto-matching-if))) | ||
| 1839 | (if p | ||
| 1840 | (list '(+ sh-indent-for-then)) | ||
| 1841 | ))) | ||
| 1842 | |||
| 1843 | (defun sh-handle-prev-then () | ||
| 1844 | (let ((p (sh-goto-matching-if))) | ||
| 1845 | (if p | ||
| 1846 | (list '(+ sh-indent-after-if)) | ||
| 1847 | ))) | ||
| 1848 | |||
| 1849 | (defun sh-handle-prev-open () | ||
| 1850 | (save-excursion | ||
| 1851 | (let ((x (sh-prev-stmt))) | ||
| 1852 | (if (and x | ||
| 1853 | (progn | ||
| 1854 | (goto-char x) | ||
| 1855 | (or | ||
| 1856 | (looking-at "function\\b") | ||
| 1857 | (looking-at "\\s-*\\S-+\\s-*()") | ||
| 1858 | ))) | ||
| 1859 | (list '(+ sh-indent-after-function)) | ||
| 1860 | (list '(+ sh-indent-after-open))) | ||
| 1861 | ))) | ||
| 1862 | |||
| 1863 | (defun sh-handle-this-close () | ||
| 1864 | (forward-char 1) ;; move over ")" | ||
| 1865 | (let ((p (sh-safe-backward-sexp))) | ||
| 1866 | (if p | ||
| 1867 | (list "aligned to opening paren") | ||
| 1868 | nil | ||
| 1869 | ))) | ||
| 1870 | |||
| 1871 | (defun sh-goto-matching-case () | ||
| 1872 | (let ((found (sh-find-prev-matching "\\bcase\\b" "\\besac\\b" 1))) | ||
| 1873 | (if found | ||
| 1874 | (goto-char found)))) | ||
| 1875 | |||
| 1876 | (defun sh-handle-prev-case () | ||
| 1877 | ;; This is typically called when point is on same line as a case | ||
| 1878 | ;; we shouldn't -- and can't find prev-case | ||
| 1879 | (if (looking-at ".*\\bcase\\b") | ||
| 1880 | (list '(+ sh-indent-for-case-label)) | ||
| 1881 | (error "We don't see to be on a line with a case") ;; debug | ||
| 1882 | )) | ||
| 1883 | |||
| 1884 | (defun sh-handle-this-esac () | ||
| 1885 | (let ((p (sh-goto-matching-case))) | ||
| 1886 | (if p | ||
| 1887 | (list "aligned to matching case") | ||
| 1888 | nil | ||
| 1889 | ))) | ||
| 1890 | |||
| 1891 | |||
| 1892 | (defun sh-handle-prev-esac () | ||
| 1893 | (let ((p (sh-goto-matching-case))) | ||
| 1894 | (if p | ||
| 1895 | (list "matching case") | ||
| 1896 | nil | ||
| 1897 | ))) | ||
| 1898 | |||
| 1899 | (defun sh-handle-after-case-label () | ||
| 1900 | (let ((p (sh-goto-matching-case))) | ||
| 1901 | (if p | ||
| 1902 | (list '( + sh-indent-for-case-alt )) | ||
| 1903 | nil | ||
| 1904 | ))) | ||
| 1905 | |||
| 1906 | (defun sh-handle-prev-case-alt-end () | ||
| 1907 | (let ((p (sh-goto-matching-case))) | ||
| 1908 | (if p | ||
| 1909 | (list '( + sh-indent-for-case-label )) | ||
| 1910 | nil | ||
| 1911 | ))) | ||
| 1912 | |||
| 1913 | (defun sh-safe-backward-sexp () | ||
| 1914 | "Try and do a `backward-sexp', but do not error. | ||
| 1915 | Return new point if successful, nil if an error occurred." | ||
| 1916 | (condition-case nil | ||
| 1917 | (progn | ||
| 1918 | (backward-sexp 1) | ||
| 1919 | (point) ;; return point if successful | ||
| 1920 | ) | ||
| 1921 | (error | ||
| 1922 | (sh-debug "oops!(0) %d" (point)) | ||
| 1923 | nil ;; return nil if fail | ||
| 1924 | ))) | ||
| 1925 | |||
| 1926 | (defun sh-safe-forward-sexp () | ||
| 1927 | "Try and do a `forward-sexp', but do not error. | ||
| 1928 | Return new point if successful, nil if an error occurred." | ||
| 1929 | (condition-case nil | ||
| 1930 | (progn | ||
| 1931 | (forward-sexp 1) | ||
| 1932 | (point) ;; return point if successful | ||
| 1933 | ) | ||
| 1934 | (error | ||
| 1935 | (sh-debug "oops!(1) %d" (point)) | ||
| 1936 | nil ;; return nil if fail | ||
| 1937 | ))) | ||
| 1938 | |||
| 1939 | (defun sh-goto-match-for-done () | ||
| 1940 | (let ((found (sh-find-prev-matching sh-regexp-for-done sh-re-done 1))) | ||
| 1941 | (if found | ||
| 1942 | (goto-char found)))) | ||
| 1943 | |||
| 1944 | (defun sh-handle-this-done () | ||
| 1945 | (if (sh-goto-match-for-done) | ||
| 1946 | (list "aligned to do stmt" '(+ sh-indent-for-done)) | ||
| 1947 | nil | ||
| 1948 | )) | ||
| 1949 | |||
| 1950 | (defun sh-handle-prev-done () | ||
| 1951 | (if (sh-goto-match-for-done) | ||
| 1952 | (list "previous done") | ||
| 1953 | nil | ||
| 1954 | )) | ||
| 1955 | |||
| 1956 | (defun sh-handle-this-do () | ||
| 1957 | (let ( (p (sh-goto-match-for-done)) ) | ||
| 1958 | (if p | ||
| 1959 | (list '(+ sh-indent-for-do)) | ||
| 1960 | nil | ||
| 1961 | ))) | ||
| 1962 | |||
| 1963 | (defun sh-handle-prev-do () | ||
| 1964 | (let ( (p) ) | ||
| 1965 | (cond | ||
| 1966 | ((save-restriction | ||
| 1967 | (narrow-to-region | ||
| 1968 | (point) | ||
| 1969 | (save-excursion | ||
| 1970 | (beginning-of-line) | ||
| 1971 | (point))) | ||
| 1972 | (sh-goto-match-for-done)) | ||
| 1973 | (sh-debug "match for done found on THIS line") | ||
| 1974 | (list '(+ sh-indent-after-loop-construct))) | ||
| 1975 | ((sh-goto-match-for-done) | ||
| 1976 | (sh-debug "match for done found on PREV line") | ||
| 1977 | (list '(+ sh-indent-after-do))) | ||
| 1978 | (t | ||
| 1979 | (message "match for done NOT found") | ||
| 1980 | nil)))) | ||
| 1981 | |||
| 1982 | ;; for rc: | ||
| 1983 | (defun sh-find-prev-switch () | ||
| 1984 | "Find the line for the switch keyword matching this line's case keyword." | ||
| 1985 | (re-search-backward "\\bswitch\\b" nil t)) | ||
| 1986 | |||
| 1987 | (defun sh-handle-this-rc-case () | ||
| 1988 | (if (sh-find-prev-switch) | ||
| 1989 | (list '(+ sh-indent-after-switch)) | ||
| 1990 | ;; (list '(+ sh-indent-for-case-label)) | ||
| 1991 | nil)) | ||
| 1992 | |||
| 1993 | (defun sh-handle-prev-rc-case () | ||
| 1994 | (list '(+ sh-indent-after-case))) | ||
| 1995 | |||
| 1996 | (defun sh-check-rule (n thing) | ||
| 1997 | (let ((rule (nth n (assoc thing sh-kw-alist))) | ||
| 1998 | (val nil)) | ||
| 1999 | (if rule | ||
| 2000 | (progn | ||
| 2001 | (setq val (funcall rule)) | ||
| 2002 | (sh-debug "rule (%d) for %s at %d is %s\n-> returned %s" | ||
| 2003 | n thing (point) rule val))) | ||
| 2004 | val)) | ||
| 2005 | |||
| 2006 | |||
| 2007 | (defun sh-get-indent-info () | ||
| 2008 | "Return indent-info for this line. | ||
| 2009 | This is a list. nil means the line is to be left as is. | ||
| 2010 | Otherwise it contains one or more of the following sublists: | ||
| 2011 | \(t NUMBER\) NUMBER is the base location in the buffer that indendation is | ||
| 2012 | relative to. If present, this is always the first of the | ||
| 2013 | sublists. The indentation of the line in question is | ||
| 2014 | derived from the indentation of this point, possibly | ||
| 2015 | modified by subsequent sublists. | ||
| 2016 | \(+ VAR\) | ||
| 2017 | \(- VAR\) Get the value of variable VAR and add to or subtract from | ||
| 2018 | the indentation calculated so far. | ||
| 2019 | \(= VAR\) Get the value of variable VAR and *replace* the | ||
| 2020 | indentation with itss value. This only occurs for | ||
| 2021 | special variables such as `sh-indent-comment'. | ||
| 2022 | STRING This is ignored for the purposes of calculating | ||
| 2023 | indentation, it is printed in certain cases to help show | ||
| 2024 | what the indentation is based on." | ||
| 2025 | ;; See comments before `sh-kw'. | ||
| 2026 | (save-excursion | ||
| 2027 | (let ((prev-kw nil) | ||
| 2028 | (prev-stmt nil) | ||
| 2029 | (have-result nil) | ||
| 2030 | depth-bol depth-eol | ||
| 2031 | this-kw | ||
| 2032 | (state nil) | ||
| 2033 | state-bol | ||
| 2034 | (depth-prev-bol nil) | ||
| 2035 | start | ||
| 2036 | func val | ||
| 2037 | (result nil) | ||
| 2038 | prev-lines-indent | ||
| 2039 | (prev-list nil) | ||
| 2040 | (this-list nil) | ||
| 2041 | (align-point nil) | ||
| 2042 | prev-line-end x) | ||
| 2043 | (beginning-of-line) | ||
| 2044 | ;; Note: setting result to t means we are done and will return nil. | ||
| 2045 | ;;( This function never returns just t.) | ||
| 2046 | (cond | ||
| 2047 | ((equal (get-text-property (point) 'syntax-table) sh-here-doc-syntax) | ||
| 2048 | (setq result t) | ||
| 2049 | (setq have-result t)) | ||
| 2050 | ((looking-at "\\s-*#") ; was (equal this-kw "#") | ||
| 2051 | (if (bobp) | ||
| 2052 | (setq result t);; return nil if 1st line! | ||
| 2053 | (setq result (list '(= sh-indent-comment))) | ||
| 2054 | ;; we still need to get previous line in case | ||
| 2055 | ;; sh-indent-comnent is t (indent as normal) | ||
| 2056 | (setq align-point (sh-prev-line nil)) | ||
| 2057 | (setq have-result nil) | ||
| 2058 | )) | ||
| 2059 | );; cond | ||
| 2060 | |||
| 2061 | (unless have-result | ||
| 2062 | ;; Continuation lines are handled specially | ||
| 2063 | (if (sh-this-is-a-continuation) | ||
| 2064 | (progn | ||
| 2065 | ;; We assume the line being continued is already | ||
| 2066 | ;; properly indented... | ||
| 2067 | ;; (setq prev-line-end (sh-prev-line)) | ||
| 2068 | (setq align-point (sh-prev-line nil)) | ||
| 2069 | (setq result (list '(+ sh-indent-for-continuation))) | ||
| 2070 | (setq have-result t)) | ||
| 2071 | (beginning-of-line) | ||
| 2072 | (skip-chars-forward " \t") | ||
| 2073 | (setq this-kw (sh-get-kw))) | ||
| 2074 | |||
| 2075 | ;; Handle "this" keyword: first word on the line we're | ||
| 2076 | ;; calculating indentation info for. | ||
| 2077 | (if this-kw | ||
| 2078 | (if (setq val (sh-check-rule 1 this-kw)) | ||
| 2079 | (progn | ||
| 2080 | (setq align-point (point)) | ||
| 2081 | (sh-debug | ||
| 2082 | "this - setting align-point to %d" align-point) | ||
| 2083 | (setq result (append result val)) | ||
| 2084 | (setq have-result t) | ||
| 2085 | ;; set prev-line to continue processing remainder | ||
| 2086 | ;; of this line as a previous l ine | ||
| 2087 | (setq prev-line-end (point)) | ||
| 2088 | )))) | ||
| 2089 | |||
| 2090 | (unless have-result | ||
| 2091 | (setq prev-line-end (sh-prev-line 'end))) | ||
| 2092 | |||
| 2093 | (if prev-line-end | ||
| 2094 | (save-excursion | ||
| 2095 | ;; We start off at beginning of this line. | ||
| 2096 | ;; Scan previous statements while this is <= | ||
| 2097 | ;; start of previous line. | ||
| 2098 | (setq start (point));; for debug only | ||
| 2099 | (goto-char prev-line-end) | ||
| 2100 | (setq x t) | ||
| 2101 | (while (and x (setq x (sh-prev-thing))) | ||
| 2102 | (sh-debug "at %d x is: %s result is: %s" (point) x result) | ||
| 2103 | (cond | ||
| 2104 | ((and (equal x ")") | ||
| 2105 | (equal (get-text-property (1- (point)) 'syntax-table) | ||
| 2106 | sh-special-syntax)) | ||
| 2107 | (sh-debug "Case label) here") | ||
| 2108 | (setq x 'case-label) | ||
| 2109 | (if (setq val (sh-check-rule 2 x)) | ||
| 2110 | (progn | ||
| 2111 | (setq result (append result val)) | ||
| 2112 | (setq align-point (point)))) | ||
| 2113 | (forward-char -1) | ||
| 2114 | (skip-chars-forward "[a-z0-9]*?") | ||
| 2115 | ) | ||
| 2116 | ((string-match "[])}]" x) | ||
| 2117 | (setq x (sh-safe-backward-sexp)) | ||
| 2118 | (if x | ||
| 2119 | (progn | ||
| 2120 | (setq align-point (point)) | ||
| 2121 | (setq result (append result | ||
| 2122 | (list "aligned to opening paren"))) | ||
| 2123 | ))) | ||
| 2124 | ((string-match "[[({]" x) | ||
| 2125 | (sh-debug "Checking special thing: %s" x) | ||
| 2126 | (if (setq val (sh-check-rule 2 x)) | ||
| 2127 | (setq result (append result val))) | ||
| 2128 | (forward-char -1) | ||
| 2129 | (setq align-point (point))) | ||
| 2130 | ((string-match "[\"'`]" x) | ||
| 2131 | (sh-debug "Skipping back for %s" x) | ||
| 2132 | ;; this was oops-2 | ||
| 2133 | (setq x (sh-safe-backward-sexp))) | ||
| 2134 | ((stringp x) | ||
| 2135 | (sh-debug "Checking string %s at %s" x (point)) | ||
| 2136 | (if (setq val (sh-check-rule 2 x)) | ||
| 2137 | ;; (or (eq t (car val)) | ||
| 2138 | ;; (eq t (car (car val)))) | ||
| 2139 | (setq result (append result val))) | ||
| 2140 | ;; not sure about this test Wed Jan 27 23:48:35 1999 | ||
| 2141 | (setq align-point (point)) | ||
| 2142 | (unless (bolp) | ||
| 2143 | (forward-char -1))) | ||
| 2144 | (t | ||
| 2145 | (error "Don't know what to do with %s" x)) | ||
| 2146 | ) | ||
| 2147 | );; while | ||
| 2148 | (sh-debug "result is %s" result) | ||
| 2149 | ) | ||
| 2150 | (sh-debug "No prev line!") | ||
| 2151 | (sh-debug "result: %s align-point: %s" result align-point) | ||
| 2152 | ) | ||
| 2153 | |||
| 2154 | (if align-point | ||
| 2155 | ;; was: (setq result (append result (list (list t align-point)))) | ||
| 2156 | (setq result (append (list (list t align-point)) result)) | ||
| 2157 | ) | ||
| 2158 | (sh-debug "result is now: %s" result) | ||
| 2159 | |||
| 2160 | (or result | ||
| 2161 | (if prev-line-end | ||
| 2162 | (setq result (list (list t prev-line-end))) | ||
| 2163 | (setq result (list (list '= 'sh-first-lines-indent))) | ||
| 2164 | )) | ||
| 2165 | |||
| 2166 | (if (eq result t) | ||
| 2167 | (setq result nil)) | ||
| 2168 | (sh-debug "result is: %s" result) | ||
| 2169 | result | ||
| 2170 | );; let | ||
| 2171 | )) | ||
| 2172 | |||
| 2173 | |||
| 2174 | (defun sh-get-indent-var-for-line (&optional info) | ||
| 2175 | "Return the variable controlling indentation for this line. | ||
| 2176 | If there is not [just] one such variable, return a string | ||
| 2177 | indicating the problem. | ||
| 2178 | If INFO is supplied it is used, else it is calculated." | ||
| 2179 | (let ((var nil) | ||
| 2180 | (result nil) | ||
| 2181 | (reason nil) | ||
| 2182 | sym elt) | ||
| 2183 | (or info | ||
| 2184 | (setq info (sh-get-indent-info))) | ||
| 2185 | (if (null info) | ||
| 2186 | (setq result "this line to be left as is") | ||
| 2187 | (while (and info (null result)) | ||
| 2188 | (setq elt (car info)) | ||
| 2189 | (cond | ||
| 2190 | ((stringp elt) | ||
| 2191 | (setq reason elt) | ||
| 2192 | ) | ||
| 2193 | ((not (listp elt)) | ||
| 2194 | (error "sh-get-indent-var-for-line invalid elt: %s" elt)) | ||
| 2195 | ;; so it is a list | ||
| 2196 | ((eq t (car elt)) | ||
| 2197 | );; nothing | ||
| 2198 | ((symbolp (setq sym (nth 1 elt))) | ||
| 2199 | ;; A bit of a kludge - when we see the sh-indent-comment | ||
| 2200 | ;; ignore other variables. Otherwise it is tricky to | ||
| 2201 | ;; "learn" the comment indentation. | ||
| 2202 | (if (eq var 'sh-indent-comment) | ||
| 2203 | (setq result var) | ||
| 2204 | (if var | ||
| 2205 | (setq result | ||
| 2206 | "this line is controlled by more than 1 variable.") | ||
| 2207 | (setq var sym)))) | ||
| 2208 | (t | ||
| 2209 | (error "sh-get-indent-var-for-line invalid list elt: %s" elt))) | ||
| 2210 | (setq info (cdr info)) | ||
| 2211 | )) | ||
| 2212 | (or result | ||
| 2213 | (setq result var)) | ||
| 2214 | (or result | ||
| 2215 | (setq result reason)) | ||
| 2216 | (if (null result) | ||
| 2217 | ;; e.g. just had (t POS) | ||
| 2218 | (setq result "line has default indentation")) | ||
| 2219 | result)) | ||
| 2220 | |||
| 2221 | |||
| 2222 | |||
| 2223 | ;; Finding the previous line isn't trivial. | ||
| 2224 | ;; We must *always* go back one more and see if that is a continuation | ||
| 2225 | ;; line -- it is the PREVIOUS line which is continued, not the one | ||
| 2226 | ;; we are going to! | ||
| 2227 | ;; Also, we want to treat a whole "here document" as one big line, | ||
| 2228 | ;; because we may want to a align to the beginning of it. | ||
| 2229 | ;; | ||
| 2230 | ;; What we do: | ||
| 2231 | ;; - go back a line, if empty repeat | ||
| 2232 | ;; - (we are now at a previous non empty line) | ||
| 2233 | ;; - save this | ||
| 2234 | ;; - if this is in a here-document, go to the beginning of it | ||
| 2235 | ;; and save that | ||
| 2236 | ;; - go back one more physcial line and see if it is a continuation line | ||
| 2237 | ;; - if yes, save it and repeat | ||
| 2238 | ;; - if no, go back to where we last saved. | ||
| 2239 | (defun sh-prev-line (&optional end) | ||
| 2240 | "Back to end of previous non-comment non-empty line. | ||
| 2241 | Go to beginning of logical line unless END is non-nil, in which case | ||
| 2242 | we go to the end of the previous line and do not check for continuations." | ||
| 2243 | (sh-must-be-shell-mode) | ||
| 2244 | (let ((going t) | ||
| 2245 | (last-contin-line nil) | ||
| 2246 | (result nil) | ||
| 2247 | bol eol state) | ||
| 2248 | (save-excursion | ||
| 2249 | (beginning-of-line) | ||
| 2250 | (while (and going | ||
| 2251 | (not (bobp)) | ||
| 2252 | (>= 0 (forward-line -1)) | ||
| 2253 | ) | ||
| 2254 | (setq bol (point)) | ||
| 2255 | (end-of-line) | ||
| 2256 | (setq eol (point)) | ||
| 2257 | (save-restriction | ||
| 2258 | (setq state (parse-partial-sexp bol eol nil nil nil t)) | ||
| 2259 | (if (nth 4 state) | ||
| 2260 | (setq eol (nth 8 state))) | ||
| 2261 | (narrow-to-region bol eol) | ||
| 2262 | (goto-char bol) | ||
| 2263 | (cond | ||
| 2264 | ((looking-at "\\s-*$")) | ||
| 2265 | (t | ||
| 2266 | (if end | ||
| 2267 | (setq result eol) | ||
| 2268 | (setq result bol)) | ||
| 2269 | (setq going nil)) | ||
| 2270 | ))) | ||
| 2271 | (if (and result | ||
| 2272 | (equal (get-text-property (1- result) 'syntax-table) | ||
| 2273 | sh-here-doc-syntax)) | ||
| 2274 | (let ((p1 (previous-single-property-change | ||
| 2275 | (1- result) 'syntax-table))) | ||
| 2276 | (if p1 | ||
| 2277 | (progn | ||
| 2278 | (goto-char p1) | ||
| 2279 | (forward-line -1) | ||
| 2280 | (if end | ||
| 2281 | (end-of-line)) | ||
| 2282 | (setq result (point))) | ||
| 2283 | ))) | ||
| 2284 | (unless end | ||
| 2285 | ;; we must check previous lines to see if they are continuation lines | ||
| 2286 | ;; if so, we must return position of first of them | ||
| 2287 | (while (and (sh-this-is-a-continuation) | ||
| 2288 | (>= 0 (forward-line -1))) | ||
| 2289 | (setq result (point))) | ||
| 2290 | (if result | ||
| 2291 | (progn | ||
| 2292 | (goto-char result) | ||
| 2293 | (beginning-of-line) | ||
| 2294 | (skip-chars-forward " \t") | ||
| 2295 | (setq result (point)) | ||
| 2296 | ))) | ||
| 2297 | ) ;; save-excursion | ||
| 2298 | result | ||
| 2299 | )) | ||
| 2300 | |||
| 2301 | |||
| 2302 | (defun sh-prev-stmt () | ||
| 2303 | "Return the address of the previous stmt or nil." | ||
| 2304 | ;; This is used when we are trying to find a matching keyword. | ||
| 2305 | ;; Searching backward for the keyword would certainly be quicker, but | ||
| 2306 | ;; it is hard to remove "false matches" -- such as if the keyword | ||
| 2307 | ;; appears in a string or quote. This way is slower, but (I think) safer. | ||
| 2308 | (interactive) | ||
| 2309 | (save-excursion | ||
| 2310 | (let ((going t) | ||
| 2311 | (start (point)) | ||
| 2312 | (found nil) | ||
| 2313 | (prev nil)) | ||
| 2314 | (skip-chars-backward " \t;|&({[") | ||
| 2315 | (while (and (not found) | ||
| 2316 | (not (bobp)) | ||
| 2317 | going) | ||
| 2318 | ;; Do a backward-sexp if possible, else backup bit by bit... | ||
| 2319 | (if (sh-safe-backward-sexp) | ||
| 2320 | (progn | ||
| 2321 | (if (looking-at sh-special-keywords) | ||
| 2322 | (progn | ||
| 2323 | (setq found prev)) | ||
| 2324 | (setq prev (point)) | ||
| 2325 | )) | ||
| 2326 | ;; backward-sexp failed | ||
| 2327 | (if (zerop (skip-chars-backward " \t()[\]{};`'")) | ||
| 2328 | (forward-char -1)) | ||
| 2329 | (if (bolp) | ||
| 2330 | (let ((back (sh-prev-line nil))) | ||
| 2331 | (if back | ||
| 2332 | (goto-char back) | ||
| 2333 | (setq going nil))))) | ||
| 2334 | (unless found | ||
| 2335 | (skip-chars-backward " \t") | ||
| 2336 | (if (or (and (bolp) (not (sh-this-is-a-continuation))) | ||
| 2337 | (eq (char-before) ?\;) | ||
| 2338 | (looking-at "\\s-*[|&]")) | ||
| 2339 | (setq found (point))))) | ||
| 2340 | (if found | ||
| 2341 | (goto-char found)) | ||
| 2342 | (if found | ||
| 2343 | (progn | ||
| 2344 | (skip-chars-forward " \t|&({[") | ||
| 2345 | (setq found (point)))) | ||
| 2346 | (if (>= (point) start) | ||
| 2347 | (progn | ||
| 2348 | (debug "We didn't move!") | ||
| 2349 | (setq found nil)) | ||
| 2350 | (or found | ||
| 2351 | (sh-debug "Did not find prev stmt."))) | ||
| 2352 | found | ||
| 2353 | ))) | ||
| 2354 | |||
| 2355 | |||
| 2356 | (defun sh-get-word () | ||
| 2357 | "Get a shell word skipping whitespace from point." | ||
| 2358 | (interactive) | ||
| 2359 | (skip-chars-forward "\t ") | ||
| 2360 | (let ((start (point))) | ||
| 2361 | (while | ||
| 2362 | (if (looking-at "[\"'`]") | ||
| 2363 | (sh-safe-forward-sexp) | ||
| 2364 | ;; (> (skip-chars-forward "^ \t\n\"'`") 0) | ||
| 2365 | (> (skip-chars-forward "-_a-zA-Z\$0-9") 0) | ||
| 2366 | )) | ||
| 2367 | (buffer-substring start (point)) | ||
| 2368 | )) | ||
| 2369 | |||
| 2370 | (defun sh-prev-thing () | ||
| 2371 | "Return the previous thing this logical line." | ||
| 2372 | ;; This is called when `sh-get-indent-info' is working backwards on | ||
| 2373 | ;; the previous line(s) finding what keywords may be relevant for | ||
| 2374 | ;; indenting. It moves over sexps if possible, and will stop | ||
| 2375 | ;; on a ; and at the beginning of a line if it is not a continuation | ||
| 2376 | ;; line. | ||
| 2377 | ;; | ||
| 2378 | ;; Added a kludge for ";;" | ||
| 2379 | ;; Possible return values: | ||
| 2380 | ;; nil - nothing | ||
| 2381 | ;; a string - possibly a keyword | ||
| 2382 | ;; | ||
| 2383 | (if (bolp) | ||
| 2384 | nil | ||
| 2385 | (let ((going t) | ||
| 2386 | c n | ||
| 2387 | min-point | ||
| 2388 | (start (point)) | ||
| 2389 | (found nil)) | ||
| 2390 | (save-restriction | ||
| 2391 | (narrow-to-region | ||
| 2392 | (if (sh-this-is-a-continuation) | ||
| 2393 | (setq min-point (sh-prev-line nil)) | ||
| 2394 | (save-excursion | ||
| 2395 | (beginning-of-line) | ||
| 2396 | (setq min-point (point)))) | ||
| 2397 | (point)) | ||
| 2398 | (skip-chars-backward " \t;") | ||
| 2399 | (unless (looking-at "\\s-*;;") | ||
| 2400 | (skip-chars-backward "^)}];\"'`({[") | ||
| 2401 | (setq c (char-before)))) | ||
| 2402 | (sh-debug "stopping at %d c is %s start=%d min-point=%d" | ||
| 2403 | (point) c start min-point) | ||
| 2404 | (if (< (point) min-point) | ||
| 2405 | (error "point %d < min-point %d" (point) min-point)) | ||
| 2406 | (cond | ||
| 2407 | ((looking-at "\\s-*;;") | ||
| 2408 | ;; (message "Found ;; !") | ||
| 2409 | ";;") | ||
| 2410 | ((or (eq c ?\n) | ||
| 2411 | (eq c nil) | ||
| 2412 | (eq c ?\;)) | ||
| 2413 | (save-excursion | ||
| 2414 | ;; skip forward over white space newline and \ at eol | ||
| 2415 | (skip-chars-forward " \t\n\\\\") | ||
| 2416 | (sh-debug "Now at %d start=%d" (point) start) | ||
| 2417 | (if (>= (point) start) | ||
| 2418 | (progn | ||
| 2419 | (sh-debug "point: %d >= start: %d" (point) start) | ||
| 2420 | nil) | ||
| 2421 | (sh-get-word)) | ||
| 2422 | )) | ||
| 2423 | (t | ||
| 2424 | ;; c -- return a string | ||
| 2425 | (char-to-string c) | ||
| 2426 | )) | ||
| 2427 | ))) | ||
| 2428 | |||
| 2429 | |||
| 2430 | (defun sh-this-is-a-continuation () | ||
| 2431 | "Return non-nil if current line is a continuation of previous line." | ||
| 2432 | (let ((result nil) | ||
| 2433 | bol eol state) | ||
| 2434 | (save-excursion | ||
| 2435 | (if (and (zerop (forward-line -1)) | ||
| 2436 | (looking-at ".*\\\\$")) | ||
| 2437 | (progn | ||
| 2438 | (setq bol (point)) | ||
| 2439 | (end-of-line) | ||
| 2440 | (setq eol (point)) | ||
| 2441 | (setq state (parse-partial-sexp bol eol nil nil nil t)) | ||
| 2442 | (unless (nth 4 state) | ||
| 2443 | (setq result t)) | ||
| 2444 | ))))) | ||
| 2445 | |||
| 2446 | (defun sh-get-kw (&optional where and-move) | ||
| 2447 | "Return first word of line from WHERE. | ||
| 2448 | If AND-MOVE is non-nil then move to end of word." | ||
| 2449 | (let ((start (point))) | ||
| 2450 | (if where | ||
| 2451 | (goto-char where)) | ||
| 2452 | (prog1 | ||
| 2453 | (buffer-substring (point) | ||
| 2454 | (progn (skip-chars-forward "^ \t\n;")(point))) | ||
| 2455 | (unless and-move | ||
| 2456 | (goto-char start))) | ||
| 2457 | )) | ||
| 2458 | |||
| 2459 | (defun sh-find-prev-matching (open close &optional depth) | ||
| 2460 | "Find a matching token for a set of opening and closing keywords. | ||
| 2461 | This takes into account that there may be nested open..close pairings. | ||
| 2462 | OPEN and CLOSE are regexps denoting the tokens to be matched. | ||
| 2463 | Optional parameter DEPTH (usually 1) says how many to look for." | ||
| 2464 | (let ((parse-sexp-ignore-comments t) | ||
| 2465 | prev) | ||
| 2466 | (setq depth (or depth 1)) | ||
| 2467 | (save-excursion | ||
| 2468 | (condition-case nil | ||
| 2469 | (while (and | ||
| 2470 | (/= 0 depth) | ||
| 2471 | (not (bobp)) | ||
| 2472 | (setq prev (sh-prev-stmt))) | ||
| 2473 | (goto-char prev) | ||
| 2474 | (save-excursion | ||
| 2475 | (if (looking-at "\\\\\n") | ||
| 2476 | (progn | ||
| 2477 | (forward-char 2) | ||
| 2478 | (skip-chars-forward " \t"))) | ||
| 2479 | (cond | ||
| 2480 | ((looking-at open) | ||
| 2481 | (setq depth (1- depth)) | ||
| 2482 | (sh-debug "found open at %d - depth = %d" (point) depth)) | ||
| 2483 | ((looking-at close) | ||
| 2484 | (setq depth (1+ depth)) | ||
| 2485 | (sh-debug "found close - depth = %d" depth)) | ||
| 2486 | (t | ||
| 2487 | )))) | ||
| 2488 | (error nil)) | ||
| 2489 | (if (eq depth 0) | ||
| 2490 | prev ;; (point) | ||
| 2491 | nil) | ||
| 2492 | ))) | ||
| 2493 | |||
| 2494 | |||
| 2495 | (defun sh-var-value (var &optional ignore-error) | ||
| 2496 | "Return the value of variable VAR, interpreting symbols. | ||
| 2497 | It can also return t or nil. | ||
| 2498 | If an illegal value is found, throw an error unless Optional argument | ||
| 2499 | IGNORE-ERROR is non-nil." | ||
| 2500 | (let ((val (symbol-value var))) | ||
| 2501 | (cond | ||
| 2502 | ((numberp val) | ||
| 2503 | val) | ||
| 2504 | ((eq val t) | ||
| 2505 | val) | ||
| 2506 | ((null val) | ||
| 2507 | val) | ||
| 2508 | ((eq val '+) | ||
| 2509 | sh-basic-offset) | ||
| 2510 | ((eq val '-) | ||
| 2511 | (- sh-basic-offset)) | ||
| 2512 | ((eq val '++) | ||
| 2513 | (* 2 sh-basic-offset)) | ||
| 2514 | ((eq val '--) | ||
| 2515 | (* 2 (- sh-basic-offset))) | ||
| 2516 | ((eq val '*) | ||
| 2517 | (/ sh-basic-offset 2)) | ||
| 2518 | ((eq val '/) | ||
| 2519 | (/ (- sh-basic-offset) 2)) | ||
| 2520 | (t | ||
| 2521 | (if ignore-error | ||
| 2522 | (progn | ||
| 2523 | (message "Don't konw how to handle %s's value of %s" var val) | ||
| 2524 | 0) | ||
| 2525 | (error "Don't know how to handle %s's value of %s" var val)) | ||
| 2526 | )))) | ||
| 2527 | |||
| 2528 | (defun sh-set-var-value (var value &optional no-symbol) | ||
| 2529 | "Set variable VAR to VALUE. | ||
| 2530 | Unless optional argument NO-SYMBOL is non-nil, then if VALUE is | ||
| 2531 | can be represented by a symbol then do so." | ||
| 2532 | (cond | ||
| 2533 | (no-symbol | ||
| 2534 | (set var value)) | ||
| 2535 | ((= value sh-basic-offset) | ||
| 2536 | (set var '+)) | ||
| 2537 | ((= value (- sh-basic-offset)) | ||
| 2538 | (set var '-)) | ||
| 2539 | ((eq value (* 2 sh-basic-offset)) | ||
| 2540 | (set var '++)) | ||
| 2541 | ((eq value (* 2 (- sh-basic-offset))) | ||
| 2542 | (set var '--)) | ||
| 2543 | ((eq value (/ sh-basic-offset 2)) | ||
| 2544 | (set var '*)) | ||
| 2545 | ((eq value (/ (- sh-basic-offset) 2)) | ||
| 2546 | (set var '/)) | ||
| 2547 | (t | ||
| 2548 | (set var value))) | ||
| 2549 | ) | ||
| 2550 | |||
| 2551 | |||
| 2552 | (defun sh-calculate-indent (&optional info) | ||
| 2553 | "Return the indentation for the current line. | ||
| 2554 | If INFO is supplied it is used, else it is calculated from current line." | ||
| 2555 | (let ( | ||
| 2556 | (ofs 0) | ||
| 2557 | (base-value 0) | ||
| 2558 | elt a b var val) | ||
| 2559 | (or info | ||
| 2560 | (setq info (sh-get-indent-info))) | ||
| 2561 | (if (null info) | ||
| 2562 | nil | ||
| 2563 | (while info | ||
| 2564 | (sh-debug "info: %s ofs=%s" info ofs) | ||
| 2565 | (setq elt (car info)) | ||
| 2566 | (cond | ||
| 2567 | ((stringp elt) | ||
| 2568 | ;; do nothing? | ||
| 2569 | ) | ||
| 2570 | ((listp elt) | ||
| 2571 | (setq a (car (car info))) | ||
| 2572 | (setq b (nth 1 (car info))) | ||
| 2573 | (cond | ||
| 2574 | ((eq a t) | ||
| 2575 | (save-excursion | ||
| 2576 | (goto-char b) | ||
| 2577 | (setq val (current-indentation))) | ||
| 2578 | (setq base-value val)) | ||
| 2579 | ((symbolp b) | ||
| 2580 | (setq val (sh-var-value b)) | ||
| 2581 | (cond | ||
| 2582 | ((eq a '=) | ||
| 2583 | (cond | ||
| 2584 | ((null val) | ||
| 2585 | ;; no indentation | ||
| 2586 | ;; set info to nil so we stop immediately | ||
| 2587 | (setq base-value nil ofs nil info nil)) | ||
| 2588 | ((eq val t) | ||
| 2589 | ;; indent as normal line | ||
| 2590 | (setq ofs 0)) | ||
| 2591 | (t | ||
| 2592 | ;; The following assume the (t POS) come first! | ||
| 2593 | (setq ofs val base-value 0) | ||
| 2594 | (setq info nil) ;; ? stop now | ||
| 2595 | )) | ||
| 2596 | ) | ||
| 2597 | ((eq a '+) | ||
| 2598 | (setq ofs (+ ofs val))) | ||
| 2599 | ((eq a '-) | ||
| 2600 | (setq ofs (- ofs val))) | ||
| 2601 | (t | ||
| 2602 | (error "sh-calculate-indent invalid a a=%s b=%s" a b)))) | ||
| 2603 | (t | ||
| 2604 | (error "sh-calculate-indent invalid elt: a=%s b=%s" a b))) | ||
| 2605 | ) | ||
| 2606 | (t | ||
| 2607 | (error "sh-calculate-indent invalid elt %s" elt)) | ||
| 2608 | ) | ||
| 2609 | (sh-debug "a=%s b=%s val=%s base-value=%s ofs=%s" | ||
| 2610 | a b val base-value ofs) | ||
| 2611 | (setq info (cdr info)) | ||
| 2612 | ) | ||
| 2613 | ;; return value: | ||
| 2614 | (sh-debug "at end: base-value: %s ofs: %s" base-value ofs) | ||
| 2615 | |||
| 2616 | (cond | ||
| 2617 | ((or (null base-value)(null ofs)) | ||
| 2618 | nil) | ||
| 2619 | ((and (numberp base-value)(numberp ofs)) | ||
| 2620 | (sh-debug "base (%d) + ofs (%d) = %d" | ||
| 2621 | base-value ofs (+ base-value ofs)) | ||
| 2622 | (+ base-value ofs)) ;; return value | ||
| 2623 | (t | ||
| 2624 | (error "sh-calculate-indent: Help. base-value=%s ofs=%s" | ||
| 2625 | base-value ofs) | ||
| 2626 | nil)) | ||
| 2627 | ))) | ||
| 2628 | |||
| 2629 | |||
| 2630 | (defun sh-indent-line () | ||
| 2631 | "Indent the current line." | ||
| 2632 | (interactive) | ||
| 2633 | (sh-must-be-shell-mode) | ||
| 2634 | (let ((indent (sh-calculate-indent)) shift-amt beg end | ||
| 2635 | (pos (- (point-max) (point)))) | ||
| 2636 | (if indent | ||
| 2637 | (progn | ||
| 2638 | (beginning-of-line) | ||
| 2639 | (setq beg (point)) | ||
| 2640 | (skip-chars-forward " \t") | ||
| 2641 | (setq shift-amt (- indent (current-column))) | ||
| 2642 | (if (zerop shift-amt) | ||
| 2643 | nil | ||
| 2644 | (delete-region beg (point)) | ||
| 2645 | (indent-to indent)) | ||
| 2646 | ;; If initial point was within line's indentation, | ||
| 2647 | ;; position after the indentation. Else stay at same point in text. | ||
| 2648 | (if (> (- (point-max) pos) (point)) | ||
| 2649 | (goto-char (- (point-max) pos))) | ||
| 2650 | )))) | ||
| 2651 | |||
| 2652 | |||
| 2653 | (defun sh-blink (blinkpos &optional msg) | ||
| 2654 | "Move cursor momentarily to BLINKPOS and display MSG." | ||
| 2655 | ;; We can get here without it being a number on first line | ||
| 2656 | (if (numberp blinkpos) | ||
| 2657 | (save-excursion | ||
| 2658 | (goto-char blinkpos) | ||
| 2659 | (message msg) | ||
| 2660 | (sit-for blink-matching-delay)) | ||
| 2661 | (message msg) | ||
| 2662 | )) | ||
| 2663 | |||
| 2664 | (defun sh-show-indent (arg) | ||
| 2665 | "Show the how the currently line would be indented. | ||
| 2666 | This tells you which variable, if any, controls the indentation of | ||
| 2667 | this line. | ||
| 2668 | If optional arg ARG is non-null (called interactively with a prefix), | ||
| 2669 | a pop up window describes this variable. | ||
| 2670 | If variable `sh-blink' is non-nil then momentarily go to the line | ||
| 2671 | we are indenting relative to, if applicable." | ||
| 2672 | (interactive "P") | ||
| 2673 | (sh-must-support-indent) | ||
| 2674 | (let* ((info (sh-get-indent-info)) | ||
| 2675 | (var (sh-get-indent-var-for-line info)) | ||
| 2676 | val msg | ||
| 2677 | (curr-indent (current-indentation)) | ||
| 2678 | ) | ||
| 2679 | (if (stringp var) | ||
| 2680 | (message (setq msg var)) | ||
| 2681 | (setq val (sh-calculate-indent info)) | ||
| 2682 | |||
| 2683 | (if (eq curr-indent val) | ||
| 2684 | (setq msg (format "%s is %s" var (symbol-value var))) | ||
| 2685 | (setq msg | ||
| 2686 | (if val | ||
| 2687 | (format "%s (%s) would change indent from %d to: %d" | ||
| 2688 | var (symbol-value var) curr-indent val) | ||
| 2689 | (format "%s (%s) would leave line as is" | ||
| 2690 | var (symbol-value var))) | ||
| 2691 | )) | ||
| 2692 | (if (and arg var) | ||
| 2693 | (describe-variable var))) | ||
| 2694 | (if sh-blink | ||
| 2695 | (let ((info (sh-get-indent-info))) | ||
| 2696 | (if (and info (listp (car info)) | ||
| 2697 | (eq (car (car info)) t)) | ||
| 2698 | (sh-blink (nth 1 (car info)) msg) | ||
| 2699 | (message msg))) | ||
| 2700 | (message msg)) | ||
| 2701 | )) | ||
| 2702 | |||
| 2703 | (defun sh-set-indent () | ||
| 2704 | "Set the indentation for the current line. | ||
| 2705 | If the current line is controlled by an indentation variable, prompt | ||
| 2706 | for a new value for it." | ||
| 2707 | (interactive) | ||
| 2708 | (sh-must-support-indent) | ||
| 2709 | (let* ((info (sh-get-indent-info)) | ||
| 2710 | (var (sh-get-indent-var-for-line info)) | ||
| 2711 | val val0 new-val old-val indent-val) | ||
| 2712 | (if (stringp var) | ||
| 2713 | (message (format "Cannot set indent - %s" var)) | ||
| 2714 | (setq old-val (symbol-value var)) | ||
| 2715 | (setq val (sh-read-variable var)) | ||
| 2716 | (condition-case nil | ||
| 2717 | (progn | ||
| 2718 | (set var val) | ||
| 2719 | (setq indent-val (sh-calculate-indent info)) | ||
| 2720 | (if indent-val | ||
| 2721 | (message "Variable: %s Value: %s would indent to: %d" | ||
| 2722 | var (symbol-value var) indent-val) | ||
| 2723 | (message "Variable: %s Value: %s would leave line as is." | ||
| 2724 | var (symbol-value var))) | ||
| 2725 | ;; I'm not sure about this, indenting it now? | ||
| 2726 | ;; No. Because it would give the impression that an undo would | ||
| 2727 | ;; restore thing, but the value has been altered. | ||
| 2728 | ;; (sh-indent-line) | ||
| 2729 | ) | ||
| 2730 | (error | ||
| 2731 | (set var old-val) | ||
| 2732 | (message "Bad value for %s, restoring to previous value %s" | ||
| 2733 | var old-val) | ||
| 2734 | (sit-for 1) | ||
| 2735 | nil)) | ||
| 2736 | ))) | ||
| 2737 | |||
| 2738 | |||
| 2739 | (defun sh-learn-line-indent (arg) | ||
| 2740 | "Learn how to indent a line as it currently is indented. | ||
| 2741 | |||
| 2742 | If there is an indentation variable which controls this line's indentation, | ||
| 2743 | then set it to a value which would indent the line the way it | ||
| 2744 | presently is. | ||
| 2745 | |||
| 2746 | If the value can be represented by one of the symbols then do so | ||
| 2747 | unless optional argument ARG (the prefix when interactive) is non-nil." | ||
| 2748 | (interactive "*P") | ||
| 2749 | (sh-must-support-indent) | ||
| 2750 | ;; I'm not sure if we show allow learning on an empty line. | ||
| 2751 | ;; Though it might occasionally be useful I think it usually | ||
| 2752 | ;; would just be confusing. | ||
| 2753 | (if (save-excursion | ||
| 2754 | (beginning-of-line) | ||
| 2755 | (looking-at "\\s-*$")) | ||
| 2756 | (message "sh-learn-line-indent ignores empty lines.") | ||
| 2757 | (let* ((info (sh-get-indent-info)) | ||
| 2758 | (var (sh-get-indent-var-for-line info)) | ||
| 2759 | ival sval diff new-val | ||
| 2760 | (no-symbol arg) | ||
| 2761 | (curr-indent (current-indentation))) | ||
| 2762 | (cond | ||
| 2763 | ((stringp var) | ||
| 2764 | (message (format "Cannot learn line - %s" var))) | ||
| 2765 | ((eq var 'sh-indent-comment) | ||
| 2766 | ;; This is arbitrary... | ||
| 2767 | ;; - if curr-indent is 0, set to curr-indent | ||
| 2768 | ;; - else if it has the indentation of a "normal" line, | ||
| 2769 | ;; then set to t | ||
| 2770 | ;; - else set to curr-indent. | ||
| 2771 | (setq sh-indent-comment | ||
| 2772 | (if (= curr-indent 0) | ||
| 2773 | 0 | ||
| 2774 | (let* ((sh-indent-comment t) | ||
| 2775 | (val2 (sh-calculate-indent info))) | ||
| 2776 | (if (= val2 curr-indent) | ||
| 2777 | t | ||
| 2778 | curr-indent)))) | ||
| 2779 | (message "%s set to %s" var (symbol-value var)) | ||
| 2780 | ) | ||
| 2781 | ((numberp (setq sval (sh-var-value var))) | ||
| 2782 | (setq ival (sh-calculate-indent info)) | ||
| 2783 | (setq diff (- curr-indent ival)) | ||
| 2784 | |||
| 2785 | (sh-debug "curr-indent: %d ival: %d diff: %d var:%s sval %s" | ||
| 2786 | curr-indent ival diff var sval) | ||
| 2787 | (setq new-val (+ sval diff)) | ||
| 2788 | ;;; I commented out this because someone might want to replace | ||
| 2789 | ;;; a value of `+' with the current value of sh-basic-offset | ||
| 2790 | ;;; or vice-versa. | ||
| 2791 | ;;; (if (= 0 diff) | ||
| 2792 | ;;; (message "No change needed!") | ||
| 2793 | (sh-set-var-value var new-val no-symbol) | ||
| 2794 | (message "%s set to %s" var (symbol-value var)) | ||
| 2795 | ) | ||
| 2796 | (t | ||
| 2797 | (debug) | ||
| 2798 | (message "Cannot change %s" var)) | ||
| 2799 | )))) | ||
| 2800 | |||
| 2801 | |||
| 2802 | |||
| 2803 | (defun sh-mark-init (buffer) | ||
| 2804 | "Initialize a BUFFER to be used by `sh-mark-line'." | ||
| 2805 | (let ((main-buffer (current-buffer))) | ||
| 2806 | (save-excursion | ||
| 2807 | (set-buffer (get-buffer-create buffer)) | ||
| 2808 | (erase-buffer) | ||
| 2809 | (occur-mode) | ||
| 2810 | (setq occur-buffer main-buffer) | ||
| 2811 | ))) | ||
| 2812 | |||
| 2813 | |||
| 2814 | (defun sh-mark-line (message point buffer &optional add-linenum occur-point) | ||
| 2815 | "Insert MESSAGE referring to location POINT in current buffer into BUFFER. | ||
| 2816 | Buffer BUFFER is in `occur-mode'. | ||
| 2817 | If ADD-LINENUM is non-nil the message is preceded by the line number. | ||
| 2818 | If OCCUR-POINT is non-nil then the line is marked as a new occurence | ||
| 2819 | so that `occur-next' and `occur-prev' will work." | ||
| 2820 | (let ((m1 (make-marker)) | ||
| 2821 | (main-buffer (current-buffer)) | ||
| 2822 | start | ||
| 2823 | (line "") ) | ||
| 2824 | (if point | ||
| 2825 | (progn | ||
| 2826 | (set-marker m1 point (current-buffer)) | ||
| 2827 | (if add-linenum | ||
| 2828 | (setq line (format "%d: " (1+ (count-lines 1 point))))))) | ||
| 2829 | (save-excursion | ||
| 2830 | (if (get-buffer buffer) | ||
| 2831 | (set-buffer (get-buffer buffer)) | ||
| 2832 | (set-buffer (get-buffer-create buffer)) | ||
| 2833 | (occur-mode) | ||
| 2834 | (setq occur-buffer main-buffer) | ||
| 2835 | ) | ||
| 2836 | (goto-char (point-max)) | ||
| 2837 | (setq start (point)) | ||
| 2838 | (insert line) | ||
| 2839 | (if occur-point | ||
| 2840 | (setq occur-point (point))) | ||
| 2841 | (insert message) | ||
| 2842 | (if point | ||
| 2843 | (put-text-property start (point) 'mouse-face 'highlight)) | ||
| 2844 | (insert "\n") | ||
| 2845 | (if point | ||
| 2846 | (progn | ||
| 2847 | (put-text-property start (point) 'occur m1) | ||
| 2848 | (if occur-point | ||
| 2849 | (put-text-property occur-point (1+ occur-point) | ||
| 2850 | 'occur-point t)) | ||
| 2851 | )) | ||
| 2852 | ))) | ||
| 2853 | |||
| 2854 | |||
| 2855 | |||
| 2856 | ;; Is this really worth having? | ||
| 2857 | (defvar sh-learned-buffer-hook nil | ||
| 2858 | "*An abnormal hook, called with an alist of leared variables.") | ||
| 2859 | ;;; Example of how to use sh-learned-buffer-hook | ||
| 2860 | ;; | ||
| 2861 | ;; (defun what-i-learned (list) | ||
| 2862 | ;; (let ((p list)) | ||
| 2863 | ;; (save-excursion | ||
| 2864 | ;; (set-buffer "*scratch*") | ||
| 2865 | ;; (goto-char (point-max)) | ||
| 2866 | ;; (insert "(setq\n") | ||
| 2867 | ;; (while p | ||
| 2868 | ;; (insert (format " %s %s \n" | ||
| 2869 | ;; (nth 0 (car p)) (nth 1 (car p)))) | ||
| 2870 | ;; (setq p (cdr p))) | ||
| 2871 | ;; (insert ")\n") | ||
| 2872 | ;; ))) | ||
| 2873 | ;; | ||
| 2874 | ;; (add-hook 'sh-learned-buffer-hook 'what-i-learned) | ||
| 2875 | |||
| 2876 | |||
| 2877 | ;; Originally this was sh-learn-region-indent (beg end) | ||
| 2878 | ;; However, in practise this was awkward so I changed it to | ||
| 2879 | ;; use the whole buffer. Use narrowing if needbe. | ||
| 2880 | (defun sh-learn-buffer-indent (&optional arg) | ||
| 2881 | "Learn how to indent the buffer the way it currently is. | ||
| 2882 | |||
| 2883 | Output in buffer \"*indent*\" shows any lines which have conflicting | ||
| 2884 | values of a variable, and the final value of all variables learnt. | ||
| 2885 | This buffer is popped to automatically if there are any discrepencies. | ||
| 2886 | |||
| 2887 | If no prefix ARG is given, then variables are set to numbers. | ||
| 2888 | If a prefix arg is given, then variables are set to symbols when | ||
| 2889 | applicable -- e.g. to symbol `+' if the value is that of the | ||
| 2890 | basic indent. | ||
| 2891 | If a positive numerical prefix is given, then `sh-basic-offset' | ||
| 2892 | is set to the prefix's numerical value. | ||
| 2893 | Otherwise, sh-basic-offset may or may not be changed, according | ||
| 2894 | to the value of variable `sh-learn-basic-offset'. | ||
| 2895 | |||
| 2896 | Abnormal hook `sh-learned-buffer-hook' if non-nil is called when the | ||
| 2897 | function completes. The function is abnormal because it is called | ||
| 2898 | with an alist of variables learnt. This feature may be changed or | ||
| 2899 | removed in the future. | ||
| 2900 | |||
| 2901 | This command can often take a long time to run." | ||
| 2902 | (interactive "P") | ||
| 2903 | (sh-must-support-indent) | ||
| 2904 | (save-excursion | ||
| 2905 | (goto-char (point-min)) | ||
| 2906 | (let ((learned-var-list nil) | ||
| 2907 | (out-buffer "*indent*") | ||
| 2908 | (num-diffs 0) | ||
| 2909 | last-pos | ||
| 2910 | previous-set-info | ||
| 2911 | (max 17) | ||
| 2912 | vec | ||
| 2913 | msg | ||
| 2914 | (comment-col nil) ;; number if all same, t if seen diff values | ||
| 2915 | (comments-always-default t) ;; nil if we see one not default | ||
| 2916 | initial-msg | ||
| 2917 | (specified-basic-offset (and arg (numberp arg) | ||
| 2918 | (> arg 0))) | ||
| 2919 | (linenum 0) | ||
| 2920 | suggested) | ||
| 2921 | (setq vec (make-vector max 0)) | ||
| 2922 | (sh-mark-init out-buffer) | ||
| 2923 | |||
| 2924 | (if specified-basic-offset | ||
| 2925 | (progn | ||
| 2926 | (setq sh-basic-offset arg) | ||
| 2927 | (setq initial-msg | ||
| 2928 | (format "Using specified sh-basic-offset of %d" | ||
| 2929 | sh-basic-offset))) | ||
| 2930 | (setq initial-msg | ||
| 2931 | (format "Initial value of sh-basic-offset: %s" | ||
| 2932 | sh-basic-offset))) | ||
| 2933 | |||
| 2934 | (while (< (point) (point-max)) | ||
| 2935 | (setq linenum (1+ linenum)) | ||
| 2936 | ;; (if (zerop (% linenum 10)) | ||
| 2937 | (message "line %d" linenum) | ||
| 2938 | ;; ) | ||
| 2939 | (unless (looking-at "\\s-*$") ;; ignore empty lines! | ||
| 2940 | (let* ((sh-indent-comment t) ;; info must return default indent | ||
| 2941 | (info (sh-get-indent-info)) | ||
| 2942 | (var (sh-get-indent-var-for-line info)) | ||
| 2943 | sval ival diff new-val | ||
| 2944 | (curr-indent (current-indentation))) | ||
| 2945 | (cond | ||
| 2946 | ((null var) | ||
| 2947 | nil) | ||
| 2948 | ((stringp var) | ||
| 2949 | nil) | ||
| 2950 | ((numberp (setq sval (sh-var-value var 'no-error))) | ||
| 2951 | ;; the numberp excludes comments since sval will be t. | ||
| 2952 | (setq ival (sh-calculate-indent)) | ||
| 2953 | (setq diff (- curr-indent ival)) | ||
| 2954 | (setq new-val (+ sval diff)) | ||
| 2955 | (sh-set-var-value var new-val 'no-symbol) | ||
| 2956 | (unless (looking-at "\\s-*#");; don't learn from comments | ||
| 2957 | (if (setq previous-set-info (assoc var learned-var-list)) | ||
| 2958 | (progn | ||
| 2959 | ;; it was already there, is it same value ? | ||
| 2960 | (unless (eq (symbol-value var) | ||
| 2961 | (nth 1 previous-set-info)) | ||
| 2962 | (sh-mark-line | ||
| 2963 | (format "Variable %s was set to %s" | ||
| 2964 | var (symbol-value var)) | ||
| 2965 | (point) out-buffer t t) | ||
| 2966 | (sh-mark-line | ||
| 2967 | (format " but was previously set to %s" | ||
| 2968 | (nth 1 previous-set-info)) | ||
| 2969 | (nth 2 previous-set-info) out-buffer t) | ||
| 2970 | (setq num-diffs (1+ num-diffs)) | ||
| 2971 | ;; (delete previous-set-info learned-var-list) | ||
| 2972 | (setcdr previous-set-info | ||
| 2973 | (list (symbol-value var) (point))) | ||
| 2974 | ) | ||
| 2975 | ) | ||
| 2976 | (setq learned-var-list | ||
| 2977 | (append (list (list var (symbol-value var) | ||
| 2978 | (point))) | ||
| 2979 | learned-var-list))) | ||
| 2980 | (if (numberp new-val) | ||
| 2981 | (progn | ||
| 2982 | (sh-debug | ||
| 2983 | "This line's indent value: %d" new-val) | ||
| 2984 | (if (< new-val 0) | ||
| 2985 | (setq new-val (- new-val))) | ||
| 2986 | (if (< new-val max) | ||
| 2987 | (aset vec new-val (1+ (aref vec new-val)))))) | ||
| 2988 | )) | ||
| 2989 | ((eq var 'sh-indent-comment) | ||
| 2990 | (unless (= curr-indent (sh-calculate-indent info)) | ||
| 2991 | ;; this is not the default indentation | ||
| 2992 | (setq comments-always-default nil) | ||
| 2993 | (if comment-col;; then we have see one before | ||
| 2994 | (or (eq comment-col curr-indent) | ||
| 2995 | (setq comment-col t));; seen a different one | ||
| 2996 | (setq comment-col curr-indent)) | ||
| 2997 | )) | ||
| 2998 | (t | ||
| 2999 | (sh-debug "Cannot learn this line!!!") | ||
| 3000 | )) | ||
| 3001 | (sh-debug | ||
| 3002 | "at %s learned-var-list is %s" (point) learned-var-list) | ||
| 3003 | )) | ||
| 3004 | (forward-line 1) | ||
| 3005 | ) ;; while | ||
| 3006 | (if sh-debug | ||
| 3007 | (progn | ||
| 3008 | (setq msg (format | ||
| 3009 | "comment-col = %s comments-always-default = %s" | ||
| 3010 | comment-col comments-always-default)) | ||
| 3011 | ;; (message msg) | ||
| 3012 | (sh-mark-line msg nil out-buffer))) | ||
| 3013 | (cond | ||
| 3014 | ((eq comment-col 0) | ||
| 3015 | (setq msg "\nComments are all in 1st column.\n")) | ||
| 3016 | (comments-always-default | ||
| 3017 | (setq msg "\nComments follow default indentation.\n") | ||
| 3018 | (setq comment-col t)) | ||
| 3019 | ((numberp comment-col) | ||
| 3020 | (setq msg (format "\nComments are in col %d." comment-col))) | ||
| 3021 | (t | ||
| 3022 | (setq msg "\nComments seem to be mixed, leaving them as is.\n") | ||
| 3023 | (setq comment-col nil) | ||
| 3024 | )) | ||
| 3025 | (sh-debug msg) | ||
| 3026 | (sh-mark-line msg nil out-buffer) | ||
| 3027 | |||
| 3028 | (sh-mark-line initial-msg nil out-buffer t t) | ||
| 3029 | |||
| 3030 | (setq suggested (sh-guess-basic-offset vec)) | ||
| 3031 | |||
| 3032 | (if (and suggested (not specified-basic-offset)) | ||
| 3033 | (let ((new-value | ||
| 3034 | (cond | ||
| 3035 | ;; t => set it if we have a single value as a number | ||
| 3036 | ((and (eq sh-learn-basic-offset t) (numberp suggested)) | ||
| 3037 | suggested) | ||
| 3038 | ;; other non-nil => set it if only one value was found | ||
| 3039 | (sh-learn-basic-offset | ||
| 3040 | (if (numberp suggested) | ||
| 3041 | suggested | ||
| 3042 | (if (= (length suggested) 1) | ||
| 3043 | (car suggested)))) | ||
| 3044 | (t | ||
| 3045 | nil)))) | ||
| 3046 | (if new-value | ||
| 3047 | (progn | ||
| 3048 | (setq learned-var-list | ||
| 3049 | (append (list (list 'sh-basic-offset | ||
| 3050 | (setq sh-basic-offset new-value) | ||
| 3051 | (point-max))) | ||
| 3052 | learned-var-list)) | ||
| 3053 | ;; Not sure if we need to put this line in, since | ||
| 3054 | ;; it will appear in the "Learned variable settings". | ||
| 3055 | (sh-mark-line | ||
| 3056 | (format "Changed sh-basic-offset to: %d" sh-basic-offset) | ||
| 3057 | nil out-buffer)) | ||
| 3058 | (sh-mark-line | ||
| 3059 | (if (listp suggested) | ||
| 3060 | (format "Possible value(s) for sh-basic-offset: %s" | ||
| 3061 | (mapconcat 'int-to-string suggested " ")) | ||
| 3062 | (format "Suggested sh-basic-offset: %d" suggested)) | ||
| 3063 | nil out-buffer)))) | ||
| 3064 | |||
| 3065 | |||
| 3066 | (setq learned-var-list | ||
| 3067 | (append (list (list 'sh-indent-comment comment-col (point-max))) | ||
| 3068 | learned-var-list)) | ||
| 3069 | (setq sh-indent-comment comment-col) | ||
| 3070 | (let ((name (buffer-name)) | ||
| 3071 | (lines (if (and (eq (point-min) 1) | ||
| 3072 | (eq (point-max) (1+ (buffer-size)))) | ||
| 3073 | "" | ||
| 3074 | (format "lines %d to %d of " | ||
| 3075 | (1+ (count-lines 1 (point-min))) | ||
| 3076 | (1+ (count-lines 1 (point-max)))))) | ||
| 3077 | ) | ||
| 3078 | (sh-mark-line "\nLearned variable settings:" nil out-buffer) | ||
| 3079 | (if arg | ||
| 3080 | ;; Set learned variables to symbolic rather than numeric | ||
| 3081 | ;; values where possible. | ||
| 3082 | (progn | ||
| 3083 | (let ((p (reverse learned-var-list)) | ||
| 3084 | var val) | ||
| 3085 | (while p | ||
| 3086 | (setq var (car (car p))) | ||
| 3087 | (setq val (nth 1 (car p))) | ||
| 3088 | (cond | ||
| 3089 | ((eq var 'sh-basic-offset) | ||
| 3090 | ) | ||
| 3091 | ((numberp val) | ||
| 3092 | (sh-set-var-value var val)) | ||
| 3093 | (t | ||
| 3094 | )) | ||
| 3095 | (setq p (cdr p)) | ||
| 3096 | )))) | ||
| 3097 | (let ((p (reverse learned-var-list)) | ||
| 3098 | var) | ||
| 3099 | (while p | ||
| 3100 | (setq var (car (car p))) | ||
| 3101 | (sh-mark-line (format " %s %s" var (symbol-value var)) | ||
| 3102 | (nth 2 (car p)) out-buffer) | ||
| 3103 | (setq p (cdr p)))) | ||
| 3104 | (save-excursion | ||
| 3105 | (set-buffer out-buffer) | ||
| 3106 | (goto-char (point-min)) | ||
| 3107 | (insert | ||
| 3108 | (format "Indentation values for buffer %s.\n" name) | ||
| 3109 | (format "%d indentation variable%s different values%s\n\n" | ||
| 3110 | num-diffs | ||
| 3111 | (if (= num-diffs 1) | ||
| 3112 | " has" "s have") | ||
| 3113 | (if (zerop num-diffs) | ||
| 3114 | "." ":")) | ||
| 3115 | ))) | ||
| 3116 | ;; Are abnormal hooks considered bad form? | ||
| 3117 | (run-hook-with-args 'sh-learned-buffer-hook learned-var-list) | ||
| 3118 | (if (or sh-popup-occur-buffer (> num-diffs 0)) | ||
| 3119 | (pop-to-buffer out-buffer)) | ||
| 3120 | ))) | ||
| 3121 | |||
| 3122 | (defun sh-guess-basic-offset (vec) | ||
| 3123 | "See if we can determine a reasonbable value for `sh-basic-offset'. | ||
| 3124 | This is experimental, heuristic and arbitrary! | ||
| 3125 | Argument VEC is a vector of information collected by | ||
| 3126 | `sh-learn-buffer-indent'. | ||
| 3127 | Return values: | ||
| 3128 | number - there appears to be a good single value | ||
| 3129 | list of numbers - no obvious one, here is a list of one or more | ||
| 3130 | reasonable choices | ||
| 3131 | nil - we couldn't find a reasonable one." | ||
| 3132 | (let* ((max (1- (length vec))) | ||
| 3133 | (i 1) | ||
| 3134 | (totals (make-vector max 0)) | ||
| 3135 | (return nil) | ||
| 3136 | j) | ||
| 3137 | (while (< i max) | ||
| 3138 | (aset totals i (+ (aref totals i) (* 4 (aref vec i)))) | ||
| 3139 | (setq j (/ i 2)) | ||
| 3140 | (if (zerop (% i 2)) | ||
| 3141 | (aset totals i (+ (aref totals i) (aref vec (/ i 2))))) | ||
| 3142 | (if (< (* i 2) max) | ||
| 3143 | (aset totals i (+ (aref totals i) (aref vec (* i 2))))) | ||
| 3144 | (setq i (1+ i)) | ||
| 3145 | ) | ||
| 3146 | (let ((x nil) | ||
| 3147 | (result nil) | ||
| 3148 | tot sum p) | ||
| 3149 | (setq i 1) | ||
| 3150 | (while (< i max) | ||
| 3151 | (if (/= (aref totals i) 0) | ||
| 3152 | (setq x (append x (list (cons i (aref totals i)))))) | ||
| 3153 | (setq i (1+ i))) | ||
| 3154 | |||
| 3155 | (setq x (sort x '(lambda (a b) | ||
| 3156 | (> (cdr a)(cdr b))))) | ||
| 3157 | (setq tot (apply '+ (append totals nil))) | ||
| 3158 | (sh-debug (format "vec: %s\ntotals: %s\ntot: %d" | ||
| 3159 | vec totals tot)) | ||
| 3160 | (cond | ||
| 3161 | ((zerop (length x)) | ||
| 3162 | (message "no values!")) ;; we return nil | ||
| 3163 | ((= (length x) 1) | ||
| 3164 | (message "only value is %d" (car (car x))) | ||
| 3165 | (setq result (car (car x)))) ;; return single value | ||
| 3166 | ((> (cdr (car x)) (/ tot 2)) | ||
| 3167 | ;; 1st is > 50% | ||
| 3168 | (message "basic-offset is probably %d" (car (car x))) | ||
| 3169 | (setq result (car (car x)))) ;; again, return a single value | ||
| 3170 | ((>= (cdr (car x)) (* 2 (cdr (car (cdr x))))) | ||
| 3171 | ;; 1st is >= 2 * 2nd | ||
| 3172 | (message "basic-offset could be %d" (car (car x))) | ||
| 3173 | (setq result (car (car x)))) | ||
| 3174 | ((>= (+ (cdr (car x))(cdr (car (cdr x)))) (/ tot 2)) | ||
| 3175 | ;; 1st & 2nd together >= 50% - return a list | ||
| 3176 | (setq p x sum 0 result nil) | ||
| 3177 | (while (and p | ||
| 3178 | (<= (setq sum (+ sum (cdr (car p)))) (/ tot 2))) | ||
| 3179 | (setq result (append result (list (car (car p))))) | ||
| 3180 | (setq p (cdr p))) | ||
| 3181 | (message "Possible choices for sh-basic-offset: %s" | ||
| 3182 | (mapconcat 'int-to-string result " "))) | ||
| 3183 | (t | ||
| 3184 | (message "No obvious value for sh-basic-offset. Perhaps %d" | ||
| 3185 | (car (car x))) | ||
| 3186 | ;; result is nil here | ||
| 3187 | )) | ||
| 3188 | result | ||
| 3189 | ))) | ||
| 3190 | |||
| 3191 | |||
| 3192 | (defun sh-do-nothing (a b c) | ||
| 3193 | ;; checkdoc-params: (a b c) | ||
| 3194 | "A dummy function to prevent font-lock from re-fontifying a change. | ||
| 3195 | Otherwise, we fontify something and font-lock overwrites it." | ||
| 3196 | ) | ||
| 3197 | |||
| 3198 | (defun sh-set-char-syntax (where new-prop) | ||
| 3199 | "Set the character's syntax table property at WHERE to be NEW-PROP." | ||
| 3200 | (or where | ||
| 3201 | (setq where (point))) | ||
| 3202 | (let ((font-lock-fontify-region-function 'sh-do-nothing)) | ||
| 3203 | (put-text-property where (1+ where) 'syntax-table new-prop) | ||
| 3204 | (add-text-properties where (1+ where) | ||
| 3205 | '(face sh-st-face rear-nonsticky t)) | ||
| 3206 | )) | ||
| 3207 | |||
| 3208 | |||
| 3209 | (defun sh-check-paren-in-case () | ||
| 3210 | "Make syntax class of case label's right parenthesis not close parenthesis. | ||
| 3211 | If this parenthesis is a case alternative, set its syntax class to a word." | ||
| 3212 | (let ((start (point)) | ||
| 3213 | state prev-line) | ||
| 3214 | ;; First test if this is a possible candidate, the first "(" or ")" | ||
| 3215 | ;; on the line; then, if go, check prev line is ;; or case. | ||
| 3216 | (save-excursion | ||
| 3217 | (beginning-of-line) | ||
| 3218 | ;; stop at comment or when depth becomes -1 | ||
| 3219 | (setq state (parse-partial-sexp (point) start -1 nil nil t)) | ||
| 3220 | (if (and | ||
| 3221 | (= (car state) -1) | ||
| 3222 | (= (point) start) | ||
| 3223 | (setq prev-line (sh-prev-line nil))) | ||
| 3224 | (progn | ||
| 3225 | (goto-char prev-line) | ||
| 3226 | (beginning-of-line) | ||
| 3227 | ;; (setq case-stmt-start (point)) | ||
| 3228 | ;; (if (looking-at "\\(^\\s-*case[^-a-z0-9_]\\|[^#]*;;\\s-*$\\)") | ||
| 3229 | (if (sh-search-word "\\(case\\|;;\\)" start) | ||
| 3230 | (sh-set-char-syntax (1- start) sh-special-syntax) | ||
| 3231 | )))))) | ||
| 3232 | |||
| 3233 | (defun sh-electric-rparen () | ||
| 3234 | "Insert a right parethese, and check if it is a case alternative. | ||
| 3235 | If so, its syntax class is set to word, and its text proerty | ||
| 3236 | is set to have face `sh-st-face'." | ||
| 3237 | (interactive) | ||
| 3238 | (insert ")") | ||
| 3239 | (if sh-electric-rparen-needed-here | ||
| 3240 | (sh-check-paren-in-case))) | ||
| 3241 | |||
| 3242 | (defun sh-electric-hash () | ||
| 3243 | "Insert a hash, but check it is preceded by \"$\". | ||
| 3244 | If so, it is given a syntax type of comment. | ||
| 3245 | Its text proerty has face `sh-st-face'." | ||
| 3246 | (interactive) | ||
| 3247 | (let ((pos (point))) | ||
| 3248 | (insert "#") | ||
| 3249 | (if (eq (char-before pos) ?$) | ||
| 3250 | (sh-set-char-syntax pos sh-st-punc)))) | ||
| 3251 | |||
| 3252 | (defun sh-electric-less (arg) | ||
| 3253 | "Insert a \"<\" and see if this is the start of a here-document. | ||
| 3254 | If so, the syntax class is set so that it will not be automatically | ||
| 3255 | reindented. | ||
| 3256 | Argument ARG if non-nil disables this test." | ||
| 3257 | (interactive "*P") | ||
| 3258 | (let ((p1 (point)) p2 p3) | ||
| 3259 | (sh-maybe-here-document arg) ;; call the original fn in sh-script.el. | ||
| 3260 | (setq p2 (point)) | ||
| 3261 | (if (/= (+ p1 (prefix-numeric-value arg)) p2) | ||
| 3262 | (save-excursion | ||
| 3263 | (forward-line 1) | ||
| 3264 | (end-of-line) | ||
| 3265 | (setq p3 (point)) | ||
| 3266 | (sh-set-here-doc-region p2 p3)) | ||
| 3267 | ))) | ||
| 3268 | |||
| 3269 | (defun sh-set-here-doc-region (start end) | ||
| 3270 | "Mark a here-document from START to END so that it will not be reindented." | ||
| 3271 | (interactive "r") | ||
| 3272 | ;; Make the whole thing have syntax type word... | ||
| 3273 | ;; That way sexp movement doens't worry about any parentheses. | ||
| 3274 | ;; A disadvantage of this is we can't use forward-word within a | ||
| 3275 | ;; here-doc, which is annoying. | ||
| 3276 | (let ((font-lock-fontify-region-function 'sh-do-nothing)) | ||
| 3277 | (put-text-property start end 'syntax-table sh-here-doc-syntax) | ||
| 3278 | (put-text-property start end 'face 'sh-heredoc-face) | ||
| 3279 | (put-text-property (1- end) end 'rear-nonsticky t) | ||
| 3280 | (put-text-property start (1+ start) 'front-sticky t) | ||
| 3281 | )) | ||
| 3282 | |||
| 3283 | (defun sh-remove-our-text-properties () | ||
| 3284 | "Remove text properties relating to right parentheses and here documents." | ||
| 3285 | (interactive) | ||
| 3286 | (save-excursion | ||
| 3287 | (goto-char (point-min)) | ||
| 3288 | (while (not (eobp)) | ||
| 3289 | (let ((plist (text-properties-at (point))) | ||
| 3290 | (next-change | ||
| 3291 | (or (next-single-property-change (point) 'syntax-table | ||
| 3292 | (current-buffer) ) | ||
| 3293 | (point-max)))) | ||
| 3294 | ;; Process text from point to NEXT-CHANGE... | ||
| 3295 | (if (get-text-property (point) 'syntax-table) | ||
| 3296 | (progn | ||
| 3297 | (sh-debug "-- removing props from %d to %d --" | ||
| 3298 | (point) next-change) | ||
| 3299 | (remove-text-properties (point) next-change | ||
| 3300 | '(syntax-table nil)) | ||
| 3301 | (remove-text-properties (point) next-change '(face nil)) | ||
| 3302 | )) | ||
| 3303 | (goto-char next-change))) | ||
| 3304 | )) | ||
| 3305 | |||
| 3306 | (defun sh-search-word (word &optional limit) | ||
| 3307 | "Search forward for regexp WORD occuring as a word not in string nor comment. | ||
| 3308 | If found, returns non nil with the match available in \(match-string 2\). | ||
| 3309 | Yes 2, not 1, since we build a regexp to guard against false matches | ||
| 3310 | such as matching \"a-case\" when we are searching for \"case\". | ||
| 3311 | If not found, it returns nil. | ||
| 3312 | The search maybe limited by optional argument LIMIT." | ||
| 3313 | (interactive "sSearch for: ") | ||
| 3314 | (let ((found nil) | ||
| 3315 | ;; Cannot use \\b here since it matches "-" and "_" | ||
| 3316 | (regexp (sh-mkword-regexp word)) | ||
| 3317 | start state where) | ||
| 3318 | (setq start (point)) | ||
| 3319 | (while (and (setq start (point)) | ||
| 3320 | (not found) | ||
| 3321 | (re-search-forward regexp limit t)) | ||
| 3322 | ;; Found str; check it is not in a comment or string. | ||
| 3323 | (setq state | ||
| 3324 | ;; Stop on comment: | ||
| 3325 | (parse-partial-sexp start (point) nil nil nil 'syntax_table)) | ||
| 3326 | (if (setq where (nth 8 state)) | ||
| 3327 | ;; in comment or string | ||
| 3328 | (if (= where -1) | ||
| 3329 | (setq found (point)) | ||
| 3330 | (if (eq (char-after where) ?#) | ||
| 3331 | (end-of-line) | ||
| 3332 | (goto-char where) | ||
| 3333 | (unless (sh-safe-forward-sexp) | ||
| 3334 | ;; If the above fails we must either give up or | ||
| 3335 | ;; move forward and try again. | ||
| 3336 | (forward-line 1)) | ||
| 3337 | )) | ||
| 3338 | ;; not in comment or string, so accept it | ||
| 3339 | (setq found (point)) | ||
| 3340 | )) | ||
| 3341 | found | ||
| 3342 | )) | ||
| 3343 | |||
| 3344 | (defun sh-scan-case () | ||
| 3345 | "Scan a case statement for right parens belonging to case alternatives. | ||
| 3346 | Mark each as having syntax `sh-special-syntax'. | ||
| 3347 | Called from scan-buff. If ok, return non-nil." | ||
| 3348 | (let (end | ||
| 3349 | state | ||
| 3350 | (depth 1) ;; we are called at a "case" | ||
| 3351 | (start (point)) | ||
| 3352 | (return t)) | ||
| 3353 | ;; We enter here at a case statement | ||
| 3354 | ;; First, find limits of the case. | ||
| 3355 | (while (and (> depth 0) | ||
| 3356 | (sh-search-word "\\(case\\|esac\\)")) | ||
| 3357 | (if (equal (match-string 2) "case") | ||
| 3358 | (setq depth (1+ depth)) | ||
| 3359 | (setq depth (1- depth)))) | ||
| 3360 | ;; (message "end of search for esac at %d depth=%d" (point) depth) | ||
| 3361 | (setq end (point)) | ||
| 3362 | (goto-char start) | ||
| 3363 | ;; if we found the esac, then fix all appropriate ')'s in the region | ||
| 3364 | (if (zerop depth) | ||
| 3365 | (progn | ||
| 3366 | (while (< (point) end) | ||
| 3367 | ;; search for targetdepth of -1 meaning extra right paren | ||
| 3368 | (setq state (parse-partial-sexp (point) end -1 nil nil nil)) | ||
| 3369 | (if (and (= (car state) -1) | ||
| 3370 | (= (char-before) ?\))) | ||
| 3371 | (progn | ||
| 3372 | ;; (message "At %d state is %s" (point) state) | ||
| 3373 | ;; (message "Fixing %d" (point)) | ||
| 3374 | (sh-set-char-syntax (1- (point)) sh-special-syntax) | ||
| 3375 | ;; we could advance to the next ";;" perhaps | ||
| 3376 | ) | ||
| 3377 | ;; (message "? Not found at %d" (point)) ; ok, could be "]" | ||
| 3378 | )) | ||
| 3379 | (goto-char end)) | ||
| 3380 | (message "No matching esac for case at %d" start) | ||
| 3381 | (setq return nil) | ||
| 3382 | ) | ||
| 3383 | return | ||
| 3384 | )) | ||
| 3385 | |||
| 3386 | |||
| 3387 | (defun sh-scan-buffer () | ||
| 3388 | "Scan a sh buffer for case statements and here-documents. | ||
| 3389 | |||
| 3390 | For each case alternative found, mark its \")\" with a text property | ||
| 3391 | so that its syntax class is no longer a close parenthesis character. | ||
| 3392 | |||
| 3393 | Each here-document is also marked so that it is effectively immune | ||
| 3394 | from indenation changes." | ||
| 3395 | ;; Do not call this interactively, call `sh-rescan-buffer' instead. | ||
| 3396 | (sh-must-be-shell-mode) | ||
| 3397 | (let ((n 0) | ||
| 3398 | (initial-buffer-modified-p (buffer-modified-p)) | ||
| 3399 | start end where label ws) | ||
| 3400 | (save-excursion | ||
| 3401 | (goto-char (point-min)) | ||
| 3402 | ;; 1. Scan for ")" in case statements. | ||
| 3403 | (while (and ;; (re-search-forward "^[^#]*\\bcase\\b" nil t) | ||
| 3404 | (sh-search-word "\\(case\\|esac\\)") | ||
| 3405 | ;; (progn (message "Found a case at %d" (point)) t) | ||
| 3406 | (sh-scan-case))) | ||
| 3407 | ;; 2. Scan for here docs | ||
| 3408 | (goto-char (point-min)) | ||
| 3409 | ;; while (re-search-forward "<<\\(-?\\)\\(\\s-*\\)\\(.*\\)$" nil t) | ||
| 3410 | (while (re-search-forward "<<\\(-?\\)" nil t) | ||
| 3411 | (unless (sh-in-comment-or-string (match-beginning 0)) | ||
| 3412 | ;; (setq label (match-string 3)) | ||
| 3413 | (setq label (sh-get-word)) | ||
| 3414 | (if (string= (match-string 1) "-") | ||
| 3415 | ;; if <<- then we allow whitespace | ||
| 3416 | (setq ws "\\s-*") | ||
| 3417 | ;; otherwise we don't | ||
| 3418 | (setq ws "")) | ||
| 3419 | (while (string-match "['\"\\]" label) | ||
| 3420 | (setq label (replace-match "" nil nil label))) | ||
| 3421 | (if (setq n (string-match "\\s-+$" label)) | ||
| 3422 | (setq label (substring label 0 n))) | ||
| 3423 | (forward-line 1) | ||
| 3424 | ;; the line containing the << could be continued... | ||
| 3425 | (while (sh-this-is-a-continuation) | ||
| 3426 | (forward-line 1)) | ||
| 3427 | (setq start (point)) | ||
| 3428 | (if (re-search-forward (concat "^" ws (regexp-quote label) | ||
| 3429 | "\\s-*$") | ||
| 3430 | nil t) | ||
| 3431 | (sh-set-here-doc-region start (point)) | ||
| 3432 | (sh-debug "missing here-doc delimiter `%s'" label)))) | ||
| 3433 | ;; 3. Scan for $# -- make the "#" a punctuation not a comment | ||
| 3434 | (goto-char (point-min)) | ||
| 3435 | (let (state) | ||
| 3436 | (while (and (not (eobp)) | ||
| 3437 | (setq state (parse-partial-sexp | ||
| 3438 | (1+ (point))(point-max) nil nil nil t)) | ||
| 3439 | (nth 4 state)) | ||
| 3440 | (goto-char (nth 8 state)) | ||
| 3441 | (sh-debug "At %d %s" (point) (eq (char-before) ?$)) | ||
| 3442 | (if (eq (char-before) ?$) | ||
| 3443 | (sh-set-char-syntax (point) sh-st-punc) ;; not a comment! | ||
| 3444 | (end-of-line) ;; if this *was* a comment, ignore rest of line! | ||
| 3445 | ))) | ||
| 3446 | ;; 4. Hide these changes from making a previously unmodified | ||
| 3447 | ;; buffer into a modified buffer. | ||
| 3448 | (if sh-debug | ||
| 3449 | (if initial-buffer-modified-p | ||
| 3450 | (message "buffer was initially modified") | ||
| 3451 | (message | ||
| 3452 | "buffer not initially modified - so clearing modified flag"))) | ||
| 3453 | (set-buffer-modified-p initial-buffer-modified-p) | ||
| 3454 | ))) | ||
| 3455 | |||
| 3456 | (defun sh-rescan-buffer () | ||
| 3457 | "Rescan the buffer for case alternative parentheses and here documents." | ||
| 3458 | (interactive) | ||
| 3459 | (if (eq major-mode 'sh-mode) | ||
| 3460 | (let ((inhibit-read-only t)) | ||
| 3461 | (sh-remove-our-text-properties) | ||
| 3462 | (message "Re-scanning buffer...") | ||
| 3463 | (sh-scan-buffer) | ||
| 3464 | (message "Re-scanning buffer...done") | ||
| 3465 | ))) | ||
| 3466 | |||
| 3467 | ;; ======================================================================== | ||
| 3468 | |||
| 3469 | ;; Styles -- a quick and dirty way of saving the indenation settings. | ||
| 3470 | |||
| 3471 | (defvar sh-styles-alist nil | ||
| 3472 | "A list of all known shell indentation styles.") | ||
| 3473 | |||
| 3474 | (defun sh-name-style (name &optional confirm-overwrite) | ||
| 3475 | "Name the current indentation settings as a style called NAME. | ||
| 3476 | If this name exists, the command will prompt whether it should be | ||
| 3477 | overwritten if | ||
| 3478 | - - it was called interactively with a prefix argument, or | ||
| 3479 | - - called non-interactively with optional CONFIRM-OVERWRITE non-nil." | ||
| 3480 | ;; (interactive "sName for this style: ") | ||
| 3481 | (interactive | ||
| 3482 | (list | ||
| 3483 | (read-from-minibuffer "Name for this style? " ) | ||
| 3484 | (not current-prefix-arg))) | ||
| 3485 | (let ((slist (list name)) | ||
| 3486 | (p sh-var-list) | ||
| 3487 | var style) | ||
| 3488 | (while p | ||
| 3489 | (setq var (car p)) | ||
| 3490 | (setq slist (append slist (list (cons var (symbol-value var))))) | ||
| 3491 | (setq p (cdr p))) | ||
| 3492 | (if (setq style (assoc name sh-styles-alist)) | ||
| 3493 | (if (or (not confirm-overwrite) | ||
| 3494 | (y-or-n-p "This style exists. Overwrite it? ")) | ||
| 3495 | (progn | ||
| 3496 | (message "Updating style %s" name) | ||
| 3497 | (setcdr style (cdr slist))) | ||
| 3498 | (message "Not changing style %s" name)) | ||
| 3499 | (message "Creating new style %s" name) | ||
| 3500 | (setq sh-styles-alist (append sh-styles-alist | ||
| 3501 | (list slist))) | ||
| 3502 | ))) | ||
| 3503 | |||
| 3504 | (defun sh-load-style (name) | ||
| 3505 | "Set shell indentation values for this buffer from those in style NAME." | ||
| 3506 | (interactive (list (completing-read | ||
| 3507 | "Which style to use for this buffer? " | ||
| 3508 | sh-styles-alist nil t))) | ||
| 3509 | (let ((sl (assoc name sh-styles-alist))) | ||
| 3510 | (if (null sl) | ||
| 3511 | (error "sh-load-style - style %s not known" name) | ||
| 3512 | (setq sl (cdr sl)) | ||
| 3513 | (while sl | ||
| 3514 | (set (car (car sl)) (cdr (car sl))) | ||
| 3515 | (setq sl (cdr sl)) | ||
| 3516 | )))) | ||
| 3517 | |||
| 3518 | (defun sh-save-styles-to-buffer (buff) | ||
| 3519 | "Save all current styles in elisp to buffer BUFF. | ||
| 3520 | This is always added to the end of the buffer." | ||
| 3521 | (interactive (list | ||
| 3522 | (read-from-minibuffer "Buffer to save styles in? " "*scratch*"))) | ||
| 3523 | ;; This is an attempt to sort of pretty print it... | ||
| 3524 | (save-excursion | ||
| 3525 | (set-buffer (get-buffer-create buff)) | ||
| 3526 | (goto-char (point-max)) | ||
| 3527 | (insert "\n") | ||
| 3528 | (let ((p sh-styles-alist) q) | ||
| 3529 | (insert "(setq sh-styles-alist '(\n") | ||
| 3530 | (while p | ||
| 3531 | (setq q (car p)) | ||
| 3532 | (insert " ( " (prin1-to-string (car q)) "\n") | ||
| 3533 | (setq q (cdr q)) | ||
| 3534 | (while q | ||
| 3535 | (insert " "(prin1-to-string (car q)) "\n") | ||
| 3536 | (setq q (cdr q))) | ||
| 3537 | (insert " )\n") | ||
| 3538 | (setq p (cdr p)) | ||
| 3539 | ) | ||
| 3540 | (insert "))\n") | ||
| 3541 | ))) | ||
| 3542 | |||
| 3543 | |||
| 3544 | |||
| 3545 | |||
| 1064 | ;; statement syntax-commands for various shells | 3546 | ;; statement syntax-commands for various shells |
| 1065 | 3547 | ||
| 1066 | ;; You are welcome to add the syntax or even completely new statements as | 3548 | ;; You are welcome to add the syntax or even completely new statements as |
| @@ -1083,47 +3565,52 @@ region, clear header." | |||
| 1083 | < < "endsw") | 3565 | < < "endsw") |
| 1084 | (es) | 3566 | (es) |
| 1085 | (rc "expression: " | 3567 | (rc "expression: " |
| 1086 | "switch( " str " ) {" \n | 3568 | > "switch( " str " ) {" \n |
| 1087 | > "case " (read-string "pattern: ") \n | 3569 | > "case " (read-string "pattern: ") \n |
| 1088 | > _ \n | 3570 | > _ \n |
| 1089 | ( "other pattern, %s: " | 3571 | ( "other pattern, %s: " |
| 1090 | < "case " str \n | 3572 | "case " str > \n |
| 1091 | > _ \n) | 3573 | > _ \n) |
| 1092 | < "case *" \n | 3574 | "case *" > \n |
| 1093 | > _ \n | 3575 | > _ \n |
| 1094 | resume: | 3576 | resume: |
| 1095 | < < ?}) | 3577 | ?} > ) |
| 1096 | (sh "expression: " | 3578 | (sh "expression: " |
| 1097 | "case " str " in" \n | 3579 | > "case " str " in" \n |
| 1098 | > (read-string "pattern: ") ?\) \n | 3580 | > (read-string "pattern: ") |
| 3581 | '(sh-electric-rparen) | ||
| 3582 | \n | ||
| 1099 | > _ \n | 3583 | > _ \n |
| 1100 | ";;" \n | 3584 | ";;" \n |
| 1101 | ( "other pattern, %s: " | 3585 | ( "other pattern, %s: " |
| 1102 | < str ?\) \n | 3586 | > str '(sh-electric-rparen) \n |
| 1103 | > _ \n | 3587 | > _ \n |
| 1104 | ";;" \n) | 3588 | ";;" \n) |
| 1105 | < "*)" \n | 3589 | > "*" '(sh-electric-rparen) \n |
| 1106 | > _ \n | 3590 | > _ \n |
| 1107 | resume: | 3591 | resume: |
| 1108 | < < "esac")) | 3592 | "esac" > )) |
| 1109 | 3593 | ||
| 1110 | (define-skeleton sh-for | 3594 | (define-skeleton sh-for |
| 1111 | "Insert a for loop. See `sh-feature'." | 3595 | "Insert a for loop. See `sh-feature'." |
| 1112 | (csh eval sh-modify sh | 3596 | (csh eval sh-modify sh |
| 1113 | 1 "foreach " | 3597 | 1 "" |
| 1114 | 3 " ( " | 3598 | 2 "foreach " |
| 1115 | 5 " )" | 3599 | 4 " ( " |
| 1116 | 15 "end") | 3600 | 6 " )" |
| 3601 | 15 '< | ||
| 3602 | 16 "end" | ||
| 3603 | ) | ||
| 1117 | (es eval sh-modify rc | 3604 | (es eval sh-modify rc |
| 1118 | 3 " = ") | 3605 | 4 " = ") |
| 1119 | (rc eval sh-modify sh | 3606 | (rc eval sh-modify sh |
| 1120 | 1 "for( " | 3607 | 2 "for( " |
| 1121 | 5 " ) {" | 3608 | 6 " ) {" |
| 1122 | 15 ?}) | 3609 | 15 ?} ) |
| 1123 | (sh "Index variable: " | 3610 | (sh "Index variable: " |
| 1124 | "for " str " in " _ "; do" \n | 3611 | > "for " str " in " _ "; do" \n |
| 1125 | > _ | ?$ & (sh-remember-variable str) \n | 3612 | > _ | ?$ & (sh-remember-variable str) \n |
| 1126 | < "done")) | 3613 | "done" > )) |
| 1127 | 3614 | ||
| 1128 | 3615 | ||
| 1129 | 3616 | ||
| @@ -1137,34 +3624,34 @@ region, clear header." | |||
| 1137 | "@ " str "++" \n | 3624 | "@ " str "++" \n |
| 1138 | < "end") | 3625 | < "end") |
| 1139 | (es eval sh-modify rc | 3626 | (es eval sh-modify rc |
| 1140 | 3 " =") | 3627 | 4 " =") |
| 1141 | (ksh88 "Index variable: " | 3628 | (ksh88 "Index variable: " |
| 1142 | "integer " str "=0" \n | 3629 | > "integer " str "=0" \n |
| 1143 | "while (( ( " str " += 1 ) <= " | 3630 | > "while (( ( " str " += 1 ) <= " |
| 1144 | (read-string "upper limit: ") | 3631 | (read-string "upper limit: ") |
| 1145 | " )); do" \n | 3632 | " )); do" \n |
| 1146 | > _ ?$ (sh-remember-variable str) \n | 3633 | > _ ?$ (sh-remember-variable str) > \n |
| 1147 | < "done") | 3634 | "done" > ) |
| 1148 | (posix "Index variable: " | 3635 | (posix "Index variable: " |
| 1149 | str "=1" \n | 3636 | > str "=1" \n |
| 1150 | "while [ $" str " -le " | 3637 | "while [ $" str " -le " |
| 1151 | (read-string "upper limit: ") | 3638 | (read-string "upper limit: ") |
| 1152 | " ]; do" \n | 3639 | " ]; do" \n |
| 1153 | > _ ?$ str \n | 3640 | > _ ?$ str \n |
| 1154 | str ?= (sh-add (sh-remember-variable str) 1) \n | 3641 | str ?= (sh-add (sh-remember-variable str) 1) \n |
| 1155 | < "done") | 3642 | "done" > ) |
| 1156 | (rc "Index variable: " | 3643 | (rc "Index variable: " |
| 1157 | "for( " str " in" " `{awk 'BEGIN { for( i=1; i<=" | 3644 | > "for( " str " in" " `{awk 'BEGIN { for( i=1; i<=" |
| 1158 | (read-string "upper limit: ") | 3645 | (read-string "upper limit: ") |
| 1159 | "; i++ ) print i }'}) {" \n | 3646 | "; i++ ) print i }'`}) {" \n |
| 1160 | > _ ?$ (sh-remember-variable str) \n | 3647 | > _ ?$ (sh-remember-variable str) \n |
| 1161 | < ?}) | 3648 | ?} >) |
| 1162 | (sh "Index variable: " | 3649 | (sh "Index variable: " |
| 1163 | "for " str " in `awk 'BEGIN { for( i=1; i<=" | 3650 | > "for " str " in `awk 'BEGIN { for( i=1; i<=" |
| 1164 | (read-string "upper limit: ") | 3651 | (read-string "upper limit: ") |
| 1165 | "; i++ ) print i }'`; do" \n | 3652 | "; i++ ) print i }'`; do" \n |
| 1166 | > _ ?$ (sh-remember-variable str) \n | 3653 | > _ ?$ (sh-remember-variable str) \n |
| 1167 | < "done")) | 3654 | "done" > )) |
| 1168 | 3655 | ||
| 1169 | 3656 | ||
| 1170 | (defun sh-shell-initialize-variables () | 3657 | (defun sh-shell-initialize-variables () |
| @@ -1264,46 +3751,51 @@ t means to return a list of all possible completions of STRING. | |||
| 1264 | resume: | 3751 | resume: |
| 1265 | < "endif") | 3752 | < "endif") |
| 1266 | (es "condition: " | 3753 | (es "condition: " |
| 1267 | "if { " str " } {" \n | 3754 | > "if { " str " } {" \n |
| 1268 | > _ \n | 3755 | > _ \n |
| 1269 | ( "other condition, %s: " | 3756 | ( "other condition, %s: " |
| 1270 | < "} { " str " } {" \n | 3757 | "} { " str " } {" > \n |
| 1271 | > _ \n) | 3758 | > _ \n) |
| 1272 | < "} {" \n | 3759 | "} {" > \n |
| 1273 | > _ \n | 3760 | > _ \n |
| 1274 | resume: | 3761 | resume: |
| 1275 | < ?}) | 3762 | ?} > ) |
| 1276 | (rc eval sh-modify csh | 3763 | (rc "condition: " |
| 1277 | 3 " ) {" | 3764 | > "if( " str " ) {" \n |
| 1278 | 8 '( "other condition, %s: " | 3765 | > _ \n |
| 1279 | < "} else if( " str " ) {" \n | 3766 | ( "other condition, %s: " |
| 3767 | "} else if( " str " ) {" > \n | ||
| 1280 | > _ \n) | 3768 | > _ \n) |
| 1281 | 10 "} else {" | 3769 | "} else {" > \n |
| 1282 | 17 ?}) | 3770 | > _ \n |
| 3771 | resume: | ||
| 3772 | ?} > | ||
| 3773 | ) | ||
| 1283 | (sh "condition: " | 3774 | (sh "condition: " |
| 1284 | '(setq input (sh-feature sh-test)) | 3775 | '(setq input (sh-feature sh-test)) |
| 1285 | "if " str "; then" \n | 3776 | > "if " str "; then" \n |
| 1286 | > _ \n | 3777 | > _ \n |
| 1287 | ( "other condition, %s: " | 3778 | ( "other condition, %s: " |
| 1288 | < "elif " str "; then" \n | 3779 | > "elif " str "; then" > \n |
| 1289 | > _ \n) | 3780 | > \n) |
| 1290 | < "else" \n | 3781 | "else" > \n |
| 1291 | > _ \n | 3782 | > \n |
| 1292 | resume: | 3783 | resume: |
| 1293 | < "fi")) | 3784 | "fi" > )) |
| 1294 | 3785 | ||
| 1295 | 3786 | ||
| 1296 | 3787 | ||
| 1297 | (define-skeleton sh-repeat | 3788 | (define-skeleton sh-repeat |
| 1298 | "Insert a repeat loop definition. See `sh-feature'." | 3789 | "Insert a repeat loop definition. See `sh-feature'." |
| 1299 | (es nil | 3790 | (es nil |
| 1300 | "forever {" \n | 3791 | > "forever {" \n |
| 1301 | > _ \n | 3792 | > _ \n |
| 1302 | < ?}) | 3793 | ?} > ) |
| 1303 | (zsh "factor: " | 3794 | (zsh "factor: " |
| 1304 | "repeat " str "; do"\n | 3795 | > "repeat " str "; do" > \n |
| 1305 | > _ \n | 3796 | > \n |
| 1306 | < "done")) | 3797 | "done" > )) |
| 3798 | |||
| 1307 | ;;;(put 'sh-repeat 'menu-enable '(sh-feature sh-repeat)) | 3799 | ;;;(put 'sh-repeat 'menu-enable '(sh-feature sh-repeat)) |
| 1308 | 3800 | ||
| 1309 | 3801 | ||
| @@ -1311,9 +3803,11 @@ t means to return a list of all possible completions of STRING. | |||
| 1311 | (define-skeleton sh-select | 3803 | (define-skeleton sh-select |
| 1312 | "Insert a select statement. See `sh-feature'." | 3804 | "Insert a select statement. See `sh-feature'." |
| 1313 | (ksh88 "Index variable: " | 3805 | (ksh88 "Index variable: " |
| 1314 | "select " str " in " _ "; do" \n | 3806 | > "select " str " in " _ "; do" \n |
| 1315 | > ?$ str \n | 3807 | > ?$ str \n |
| 1316 | < "done")) | 3808 | "done" > ) |
| 3809 | (bash eval sh-append ksh88) | ||
| 3810 | ) | ||
| 1317 | ;;;(put 'sh-select 'menu-enable '(sh-feature sh-select)) | 3811 | ;;;(put 'sh-select 'menu-enable '(sh-feature sh-select)) |
| 1318 | 3812 | ||
| 1319 | 3813 | ||
| @@ -1330,21 +3824,22 @@ t means to return a list of all possible completions of STRING. | |||
| 1330 | "exit:\n" | 3824 | "exit:\n" |
| 1331 | "rm $tmp* >&/dev/null" >) | 3825 | "rm $tmp* >&/dev/null" >) |
| 1332 | (es (file-name-nondirectory (buffer-file-name)) | 3826 | (es (file-name-nondirectory (buffer-file-name)) |
| 1333 | "local( signals = $signals sighup sigint; tmp = /tmp/" str ".$pid ) {" \n | 3827 | > "local( signals = $signals sighup sigint; tmp = /tmp/" str |
| 3828 | ".$pid ) {" \n | ||
| 1334 | > "catch @ e {" \n | 3829 | > "catch @ e {" \n |
| 1335 | > "rm $tmp^* >[2]/dev/null" \n | 3830 | > "rm $tmp^* >[2]/dev/null" \n |
| 1336 | "throw $e" \n | 3831 | "throw $e" \n |
| 1337 | < "} {" \n | 3832 | "} {" > \n |
| 1338 | > _ \n | 3833 | _ \n |
| 1339 | < ?} \n | 3834 | ?} > \n |
| 1340 | < ?}) | 3835 | ?} > ) |
| 1341 | (ksh88 eval sh-modify sh | 3836 | (ksh88 eval sh-modify sh |
| 1342 | 6 "EXIT") | 3837 | 7 "EXIT") |
| 1343 | (rc (file-name-nondirectory (buffer-file-name)) | 3838 | (rc (file-name-nondirectory (buffer-file-name)) |
| 1344 | "tmp = /tmp/" str ".$pid" \n | 3839 | > "tmp = /tmp/" str ".$pid" \n |
| 1345 | "fn sigexit { rm $tmp^* >[2]/dev/null }") | 3840 | "fn sigexit { rm $tmp^* >[2]/dev/null }") |
| 1346 | (sh (file-name-nondirectory (buffer-file-name)) | 3841 | (sh (file-name-nondirectory (buffer-file-name)) |
| 1347 | "TMP=${TMPDIR:-/tmp}/" str ".$$" \n | 3842 | > "TMP=${TMPDIR:-/tmp}/" str ".$$" \n |
| 1348 | "trap \"rm $TMP* 2>/dev/null\" " ?0)) | 3843 | "trap \"rm $TMP* 2>/dev/null\" " ?0)) |
| 1349 | 3844 | ||
| 1350 | 3845 | ||
| @@ -1353,9 +3848,9 @@ t means to return a list of all possible completions of STRING. | |||
| 1353 | "Insert an until loop. See `sh-feature'." | 3848 | "Insert an until loop. See `sh-feature'." |
| 1354 | (sh "condition: " | 3849 | (sh "condition: " |
| 1355 | '(setq input (sh-feature sh-test)) | 3850 | '(setq input (sh-feature sh-test)) |
| 1356 | "until " str "; do" \n | 3851 | > "until " str "; do" \n |
| 1357 | > _ \n | 3852 | > _ \n |
| 1358 | < "done")) | 3853 | "done" > )) |
| 1359 | ;;;(put 'sh-until 'menu-enable '(sh-feature sh-until)) | 3854 | ;;;(put 'sh-until 'menu-enable '(sh-feature sh-until)) |
| 1360 | 3855 | ||
| 1361 | 3856 | ||
| @@ -1363,20 +3858,24 @@ t means to return a list of all possible completions of STRING. | |||
| 1363 | (define-skeleton sh-while | 3858 | (define-skeleton sh-while |
| 1364 | "Insert a while loop. See `sh-feature'." | 3859 | "Insert a while loop. See `sh-feature'." |
| 1365 | (csh eval sh-modify sh | 3860 | (csh eval sh-modify sh |
| 1366 | 2 "while( " | 3861 | 2 "" |
| 1367 | 4 " )" | 3862 | 3 "while( " |
| 1368 | 10 "end") | 3863 | 5 " )" |
| 1369 | (es eval sh-modify rc | 3864 | 10 '< |
| 1370 | 2 "while { " | 3865 | 11 "end" ) |
| 1371 | 4 " } {") | 3866 | (es eval sh-modify sh |
| 1372 | (rc eval sh-modify csh | 3867 | 3 "while { " |
| 1373 | 4 " ) {" | 3868 | 5 " } {" |
| 1374 | 10 ?}) | 3869 | 10 ?} ) |
| 3870 | (rc eval sh-modify sh | ||
| 3871 | 3 "while( " | ||
| 3872 | 5 " ) {" | ||
| 3873 | 10 ?} ) | ||
| 1375 | (sh "condition: " | 3874 | (sh "condition: " |
| 1376 | '(setq input (sh-feature sh-test)) | 3875 | '(setq input (sh-feature sh-test)) |
| 1377 | "while " str "; do" \n | 3876 | > "while " str "; do" \n |
| 1378 | > _ \n | 3877 | > _ \n |
| 1379 | < "done")) | 3878 | "done" > )) |
| 1380 | 3879 | ||
| 1381 | 3880 | ||
| 1382 | 3881 | ||
| @@ -1416,9 +3915,8 @@ option followed by a colon `:' if the option accepts an argument." | |||
| 1416 | (posix eval sh-modify sh | 3915 | (posix eval sh-modify sh |
| 1417 | 18 "$(basename $0)") | 3916 | 18 "$(basename $0)") |
| 1418 | (sh "optstring: " | 3917 | (sh "optstring: " |
| 1419 | "while getopts :" str " OPT; do" \n | 3918 | > "while getopts :" str " OPT; do" \n |
| 1420 | > "case $OPT in" \n | 3919 | > "case $OPT in" \n |
| 1421 | > > | ||
| 1422 | '(setq v1 (append (vconcat str) nil)) | 3920 | '(setq v1 (append (vconcat str) nil)) |
| 1423 | ( (prog1 (if v1 (char-to-string (car v1))) | 3921 | ( (prog1 (if v1 (char-to-string (car v1))) |
| 1424 | (if (eq (nth 1 v1) ?:) | 3922 | (if (eq (nth 1 v1) ?:) |
| @@ -1426,10 +3924,10 @@ option followed by a colon `:' if the option accepts an argument." | |||
| 1426 | v2 "\"$OPTARG\"") | 3924 | v2 "\"$OPTARG\"") |
| 1427 | (setq v1 (cdr v1) | 3925 | (setq v1 (cdr v1) |
| 1428 | v2 nil))) | 3926 | v2 nil))) |
| 1429 | < str "|+" str ?\) \n | 3927 | > str "|+" str '(sh-electric-rparen) \n |
| 1430 | > _ v2 \n | 3928 | > _ v2 \n |
| 1431 | ";;" \n) | 3929 | > ";;" \n) |
| 1432 | < "*)" \n | 3930 | > "*" '(sh-electric-rparen) \n |
| 1433 | > "echo" " \"usage: " "`basename $0`" | 3931 | > "echo" " \"usage: " "`basename $0`" |
| 1434 | " [+-" '(setq v1 (point)) str | 3932 | " [+-" '(setq v1 (point)) str |
| 1435 | '(save-excursion | 3933 | '(save-excursion |
| @@ -1437,9 +3935,10 @@ option followed by a colon `:' if the option accepts an argument." | |||
| 1437 | (replace-match " ARG] [+-" t t))) | 3935 | (replace-match " ARG] [+-" t t))) |
| 1438 | (if (eq (preceding-char) ?-) -5) | 3936 | (if (eq (preceding-char) ?-) -5) |
| 1439 | "] [--] ARGS...\"" \n | 3937 | "] [--] ARGS...\"" \n |
| 1440 | "exit 2" \n | 3938 | "exit 2" > \n |
| 1441 | < < "esac" \n | 3939 | "esac" > |
| 1442 | < "done" \n | 3940 | \n "done" |
| 3941 | > \n | ||
| 1443 | "shift " (sh-add "OPTIND" -1))) | 3942 | "shift " (sh-add "OPTIND" -1))) |
| 1444 | 3943 | ||
| 1445 | 3944 | ||
| @@ -1508,15 +4007,12 @@ The document is bounded by `sh-here-document-word'." | |||
| 1508 | (point))) | 4007 | (point))) |
| 1509 | (newline)))) | 4008 | (newline)))) |
| 1510 | 4009 | ||
| 1511 | |||
| 1512 | |||
| 1513 | (defun sh-beginning-of-command () | 4010 | (defun sh-beginning-of-command () |
| 1514 | "Move point to successive beginnings of commands." | 4011 | "Move point to successive beginnings of commands." |
| 1515 | (interactive) | 4012 | (interactive) |
| 1516 | (if (re-search-backward sh-beginning-of-command nil t) | 4013 | (if (re-search-backward sh-beginning-of-command nil t) |
| 1517 | (goto-char (match-beginning 2)))) | 4014 | (goto-char (match-beginning 2)))) |
| 1518 | 4015 | ||
| 1519 | |||
| 1520 | (defun sh-end-of-command () | 4016 | (defun sh-end-of-command () |
| 1521 | "Move point to successive ends of commands." | 4017 | "Move point to successive ends of commands." |
| 1522 | (interactive) | 4018 | (interactive) |
| @@ -1525,4 +4021,4 @@ The document is bounded by `sh-here-document-word'." | |||
| 1525 | 4021 | ||
| 1526 | (provide 'sh-script) | 4022 | (provide 'sh-script) |
| 1527 | 4023 | ||
| 1528 | ;; sh-script.el ends here | 4024 | ;;; sh-script.el ends here |