diff options
| author | Stefan Monnier | 2015-09-29 15:13:44 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2015-09-29 15:13:44 -0400 |
| commit | 90a6f8d0741eb5391c204f059c845c361e615b49 (patch) | |
| tree | 30446e80abf227e60bc319a3c63bf79b260fc3d7 | |
| parent | 1fea2f3b743da1db666be7ce34904b74319d3f83 (diff) | |
| download | emacs-90a6f8d0741eb5391c204f059c845c361e615b49.tar.gz emacs-90a6f8d0741eb5391c204f059c845c361e615b49.zip | |
* lisp/progmodes/octave.el: Use grammar more; Handle enumeration fun
Remove redundant :group keyword args.
(octave-begin-keywords, octave-else-keywords, octave-end-keywords):
Remove variables.
(octave-operator-table, octave-smie-bnf-table): Use let-when-compile to
turn them into compile-time variables.
Auto-generate the "foo ... end" rules from the "foo ... endfoo" rules.
Add rules for break, continue, return, global, and persistent.
Refine the rule for "until".
(octave-smie--funcall-p, octave-smie--end-index-p)
(octave-smie--in-parens-p): New functions.
(octave-smie-backward-token, octave-smie-forward-token): Use them to
distinguish the "enumeration" function and the "end" index from
their corresponding keywords.
(octave--block-offset-keywords): New constant.
(octave-smie-rules): Use it. Adjust rules for new global/persistent parsing.
(octave-reserved-words): Redefine using octave-smie-grammar.
(octave-font-lock-keywords): Use octave-smie--funcall-p and
octave-smie--end-index-p.
| -rw-r--r-- | lisp/progmodes/octave.el | 316 | ||||
| -rw-r--r-- | test/indent/octave.m | 22 |
2 files changed, 186 insertions, 152 deletions
diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el index 70a2b1ab5ad..b54b88dccb5 100644 --- a/lisp/progmodes/octave.el +++ b/lisp/progmodes/octave.el | |||
| @@ -82,25 +82,6 @@ Used in `octave-mode' and `inferior-octave-mode' buffers.") | |||
| 82 | (defvar octave-comment-start-skip "\\(^\\|\\S<\\)\\(?:%!\\|\\s<+\\)\\s-*" | 82 | (defvar octave-comment-start-skip "\\(^\\|\\S<\\)\\(?:%!\\|\\s<+\\)\\s-*" |
| 83 | "Octave-specific `comment-start-skip' (which see).") | 83 | "Octave-specific `comment-start-skip' (which see).") |
| 84 | 84 | ||
| 85 | (defvar octave-begin-keywords | ||
| 86 | '("classdef" "do" "enumeration" "events" "for" "function" "if" "methods" | ||
| 87 | "parfor" "properties" "switch" "try" "unwind_protect" "while")) | ||
| 88 | |||
| 89 | (defvar octave-else-keywords | ||
| 90 | '("case" "catch" "else" "elseif" "otherwise" "unwind_protect_cleanup")) | ||
| 91 | |||
| 92 | (defvar octave-end-keywords | ||
| 93 | '("endclassdef" "endenumeration" "endevents" "endfor" "endfunction" "endif" | ||
| 94 | "endmethods" "endparfor" "endproperties" "endswitch" "end_try_catch" | ||
| 95 | "end_unwind_protect" "endwhile" "until" "end")) | ||
| 96 | |||
| 97 | (defvar octave-reserved-words | ||
| 98 | (append octave-begin-keywords | ||
| 99 | octave-else-keywords | ||
| 100 | octave-end-keywords | ||
| 101 | '("break" "continue" "global" "persistent" "return")) | ||
| 102 | "Reserved words in Octave.") | ||
| 103 | |||
| 104 | (defvar octave-function-header-regexp | 85 | (defvar octave-function-header-regexp |
| 105 | (concat "^\\s-*\\_<\\(function\\)\\_>" | 86 | (concat "^\\s-*\\_<\\(function\\)\\_>" |
| 106 | "\\([^=;(\n]*=[ \t]*\\|[ \t]*\\)\\(\\(?:\\w\\|\\s_\\)+\\)\\_>") | 87 | "\\([^=;(\n]*=[ \t]*\\|[ \t]*\\)\\(\\(?:\\w\\|\\s_\\)+\\)\\_>") |
| @@ -231,20 +212,17 @@ parenthetical grouping.") | |||
| 231 | (defcustom octave-font-lock-texinfo-comment t | 212 | (defcustom octave-font-lock-texinfo-comment t |
| 232 | "Control whether to highlight the texinfo comment block." | 213 | "Control whether to highlight the texinfo comment block." |
| 233 | :type 'boolean | 214 | :type 'boolean |
| 234 | :group 'octave | ||
| 235 | :version "24.4") | 215 | :version "24.4") |
| 236 | 216 | ||
| 237 | (defcustom octave-blink-matching-block t | 217 | (defcustom octave-blink-matching-block t |
| 238 | "Control the blinking of matching Octave block keywords. | 218 | "Control the blinking of matching Octave block keywords. |
| 239 | Non-nil means show matching begin of block when inserting a space, | 219 | Non-nil means show matching begin of block when inserting a space, |
| 240 | newline or semicolon after an else or end keyword." | 220 | newline or semicolon after an else or end keyword." |
| 241 | :type 'boolean | 221 | :type 'boolean) |
| 242 | :group 'octave) | ||
| 243 | 222 | ||
| 244 | (defcustom octave-block-offset 2 | 223 | (defcustom octave-block-offset 2 |
| 245 | "Extra indentation applied to statements in Octave block structures." | 224 | "Extra indentation applied to statements in Octave block structures." |
| 246 | :type 'integer | 225 | :type 'integer) |
| 247 | :group 'octave) | ||
| 248 | 226 | ||
| 249 | (defvar octave-block-comment-start | 227 | (defvar octave-block-comment-start |
| 250 | (concat (make-string 2 octave-comment-char) " ") | 228 | (concat (make-string 2 octave-comment-char) " ") |
| @@ -252,8 +230,7 @@ newline or semicolon after an else or end keyword." | |||
| 252 | 230 | ||
| 253 | (defcustom octave-continuation-offset 4 | 231 | (defcustom octave-continuation-offset 4 |
| 254 | "Extra indentation applied to Octave continuation lines." | 232 | "Extra indentation applied to Octave continuation lines." |
| 255 | :type 'integer | 233 | :type 'integer) |
| 256 | :group 'octave) | ||
| 257 | 234 | ||
| 258 | (eval-and-compile | 235 | (eval-and-compile |
| 259 | (defconst octave-continuation-marker-regexp "\\\\\\|\\.\\.\\.")) | 236 | (defconst octave-continuation-marker-regexp "\\\\\\|\\.\\.\\.")) |
| @@ -274,109 +251,135 @@ newline or semicolon after an else or end keyword." | |||
| 274 | 251 | ||
| 275 | (defcustom octave-mode-hook nil | 252 | (defcustom octave-mode-hook nil |
| 276 | "Hook to be run when Octave mode is started." | 253 | "Hook to be run when Octave mode is started." |
| 277 | :type 'hook | 254 | :type 'hook) |
| 278 | :group 'octave) | ||
| 279 | 255 | ||
| 280 | (defcustom octave-send-show-buffer t | 256 | (defcustom octave-send-show-buffer t |
| 281 | "Non-nil means display `inferior-octave-buffer' after sending to it." | 257 | "Non-nil means display `inferior-octave-buffer' after sending to it." |
| 282 | :type 'boolean | 258 | :type 'boolean) |
| 283 | :group 'octave) | ||
| 284 | 259 | ||
| 285 | (defcustom octave-send-line-auto-forward t | 260 | (defcustom octave-send-line-auto-forward t |
| 286 | "Control auto-forward after sending to the inferior Octave process. | 261 | "Control auto-forward after sending to the inferior Octave process. |
| 287 | Non-nil means always go to the next Octave code line after sending." | 262 | Non-nil means always go to the next Octave code line after sending." |
| 288 | :type 'boolean | 263 | :type 'boolean) |
| 289 | :group 'octave) | ||
| 290 | 264 | ||
| 291 | (defcustom octave-send-echo-input t | 265 | (defcustom octave-send-echo-input t |
| 292 | "Non-nil means echo input sent to the inferior Octave process." | 266 | "Non-nil means echo input sent to the inferior Octave process." |
| 293 | :type 'boolean | 267 | :type 'boolean) |
| 294 | :group 'octave) | ||
| 295 | 268 | ||
| 296 | 269 | ||
| 297 | ;;; SMIE indentation | 270 | ;;; SMIE indentation |
| 298 | 271 | ||
| 299 | (require 'smie) | 272 | (require 'smie) |
| 300 | 273 | ||
| 301 | ;; Use '__operators__' in Octave REPL to get a full list. | 274 | (let-when-compile |
| 302 | (defconst octave-operator-table | 275 | ((operator-table |
| 303 | '((assoc ";" "\n") (assoc ",") ; The doc claims they have equal precedence!? | 276 | ;; Use '__operators__' in Octave REPL to get a full list? |
| 304 | (right "=" "+=" "-=" "*=" "/=") | 277 | '((assoc ";" "\n") (assoc ",") ;The doc says they have equal precedence!? |
| 305 | (assoc "&&") (assoc "||") ; The doc claims they have equal precedence!? | 278 | (right "=" "+=" "-=" "*=" "/=") |
| 306 | (assoc "&") (assoc "|") ; The doc claims they have equal precedence!? | 279 | (assoc "&&") (assoc "||") ; The doc claims they have equal precedence!? |
| 307 | (nonassoc "<" "<=" "==" ">=" ">" "!=" "~=") | 280 | (assoc "&") (assoc "|") ; The doc claims they have equal precedence!? |
| 308 | (nonassoc ":") ;No idea what this is. | 281 | (nonassoc "<" "<=" "==" ">=" ">" "!=" "~=") |
| 309 | (assoc "+" "-") | 282 | (nonassoc ":") ;No idea what this is. |
| 310 | (assoc "*" "/" "\\" ".\\" ".*" "./") | 283 | (assoc "+" "-") |
| 311 | (nonassoc "'" ".'") | 284 | (assoc "*" "/" "\\" ".\\" ".*" "./") |
| 312 | (nonassoc "++" "--" "!" "~") ;And unary "+" and "-". | 285 | (nonassoc "'" ".'") |
| 313 | (right "^" "**" ".^" ".**") | 286 | (nonassoc "++" "--" "!" "~") ;And unary "+" and "-". |
| 314 | ;; It's not really an operator, but for indentation purposes it | 287 | (right "^" "**" ".^" ".**") |
| 315 | ;; could be convenient to treat it as one. | 288 | ;; It's not really an operator, but for indentation purposes it |
| 316 | (assoc "..."))) | 289 | ;; could be convenient to treat it as one. |
| 317 | 290 | (assoc "..."))) | |
| 318 | (defconst octave-smie-bnf-table | 291 | |
| 319 | '((atom) | 292 | (matchedrules |
| 320 | ;; We can't distinguish the first element in a sequence with | 293 | ;; We can't distinguish the first element in a sequence with |
| 321 | ;; precedence grammars, so we can't distinguish the condition | 294 | ;; precedence grammars, so we can't distinguish the condition |
| 322 | ;; if the `if' from the subsequent body, for example. | 295 | ;; of the `if' from the subsequent body, for example. |
| 323 | ;; This has to be done later in the indentation rules. | 296 | ;; This has to be done later in the indentation rules. |
| 324 | (exp (exp "\n" exp) | 297 | '(("try" exp "catch" exp "end_try_catch") |
| 325 | ;; We need to mention at least one of the operators in this part | 298 | ("unwind_protect" exp |
| 326 | ;; of the grammar: if the BNF and the operator table have | 299 | "unwind_protect_cleanup" exp "end_unwind_protect") |
| 327 | ;; no overlap, SMIE can't know how they relate. | 300 | ("for" exp "endfor") |
| 328 | (exp ";" exp) | 301 | ("parfor" exp "endparfor") |
| 329 | ("try" exp "catch" exp "end_try_catch") | 302 | ("while" exp "endwhile") |
| 330 | ("try" exp "catch" exp "end") | 303 | ("if" exp "endif") |
| 331 | ("unwind_protect" exp | 304 | ("if" exp "else" exp "endif") |
| 332 | "unwind_protect_cleanup" exp "end_unwind_protect") | 305 | ("if" exp "elseif" exp "else" exp "endif") |
| 333 | ("unwind_protect" exp "unwind_protect_cleanup" exp "end") | 306 | ("if" exp "elseif" exp "elseif" exp "else" exp "endif") |
| 334 | ("for" exp "endfor") | 307 | ("switch" exp "case" exp "endswitch") |
| 335 | ("for" exp "end") | 308 | ("switch" exp "case" exp "otherwise" exp "endswitch") |
| 336 | ("parfor" exp "endparfor") | 309 | ("switch" exp "case" exp "case" exp "otherwise" exp "endswitch") |
| 337 | ("parfor" exp "end") | 310 | ("function" exp "endfunction") |
| 338 | ("do" exp "until" atom) | 311 | ("enumeration" exp "endenumeration") |
| 339 | ("while" exp "endwhile") | 312 | ("events" exp "endevents") |
| 340 | ("while" exp "end") | 313 | ("methods" exp "endmethods") |
| 341 | ("if" exp "endif") | 314 | ("properties" exp "endproperties") |
| 342 | ("if" exp "else" exp "endif") | 315 | ("classdef" exp "endclassdef") |
| 343 | ("if" exp "elseif" exp "else" exp "endif") | 316 | )) |
| 344 | ("if" exp "elseif" exp "elseif" exp "else" exp "endif") | 317 | |
| 345 | ("if" exp "elseif" exp "elseif" exp "else" exp "end") | 318 | (bnf-table |
| 346 | ("switch" exp "case" exp "endswitch") | 319 | `((atom) |
| 347 | ("switch" exp "case" exp "otherwise" exp "endswitch") | 320 | ;; FIXME: We don't parse these declarations correctly since |
| 348 | ("switch" exp "case" exp "case" exp "otherwise" exp "endswitch") | 321 | ;; SMIE *really* likes to parse "a b = 2 c" as "(a b) = (2 c)". |
| 349 | ("switch" exp "case" exp "case" exp "otherwise" exp "end") | 322 | ;; IOW to do it right, we'd need to change octave-smie-*ward-token |
| 350 | ("function" exp "endfunction") | 323 | ;; so that the spaces between vars in var-decls are lexed as |
| 351 | ("function" exp "end") | 324 | ;; something like ",". |
| 352 | ("enumeration" exp "endenumeration") | 325 | ;; Doesn't seem worth the trouble/slowdown for now. |
| 353 | ("enumeration" exp "end") | 326 | ;; We could hack smie-rules so as to work around the bad parse, |
| 354 | ("events" exp "endevents") | 327 | ;; but even that doesn't seem worth the trouble. |
| 355 | ("events" exp "end") | 328 | (var-decls (atom "=" atom)) ;; (var-decls "," var-decls) |
| 356 | ("methods" exp "endmethods") | 329 | (single-exp (atom "=" atom)) |
| 357 | ("methods" exp "end") | 330 | (exp (exp "\n" exp) |
| 358 | ("properties" exp "endproperties") | 331 | ;; We need to mention at least one of the operators in this part |
| 359 | ("properties" exp "end") | 332 | ;; of the grammar: if the BNF and the operator table have |
| 360 | ("classdef" exp "endclassdef") | 333 | ;; no overlap, SMIE can't know how they relate. |
| 361 | ("classdef" exp "end")) | 334 | (exp ";" exp) |
| 362 | ;; (fundesc (atom "=" atom)) | 335 | ("do" exp "until" single-exp) |
| 363 | )) | 336 | ,@matchedrules |
| 337 | ;; For every rule that ends in "endfoo", add a corresponding | ||
| 338 | ;; rule which uses "end" instead. | ||
| 339 | ,@(mapcar (lambda (rule) (nconc (butlast rule) '("end"))) | ||
| 340 | matchedrules) | ||
| 341 | ("global" var-decls) ("persistent" var-decls) | ||
| 342 | ;; These aren't super-important, but having them here | ||
| 343 | ;; makes it easier to extract all keywords. | ||
| 344 | ("break") ("continue") ("return") | ||
| 345 | ;; The following rules do not correspond to valid code AFAIK, | ||
| 346 | ;; but they lead to a grammar that degrades more gracefully | ||
| 347 | ;; on incomplete/incorrect code. It also helps us in | ||
| 348 | ;; computing octave--block-offset-keywords. | ||
| 349 | ("try" exp "end") ("unwind_protect" exp "end") | ||
| 350 | ) | ||
| 351 | ;; (fundesc (atom "=" atom)) | ||
| 352 | ))) | ||
| 364 | 353 | ||
| 365 | (defconst octave-smie-grammar | 354 | (defconst octave-smie-grammar |
| 366 | (smie-prec2->grammar | 355 | (eval-when-compile |
| 367 | (smie-merge-prec2s | 356 | (smie-prec2->grammar |
| 368 | (smie-bnf->prec2 octave-smie-bnf-table | 357 | (smie-merge-prec2s |
| 369 | '((assoc "\n" ";"))) | 358 | (smie-bnf->prec2 bnf-table '((assoc "\n" ";"))) |
| 359 | (smie-precs->prec2 operator-table))))) | ||
| 370 | 360 | ||
| 371 | (smie-precs->prec2 octave-operator-table)))) | 361 | (defconst octave-operator-regexp |
| 362 | (eval-when-compile | ||
| 363 | (regexp-opt (remove "\n" (apply #'append | ||
| 364 | (mapcar #'cdr operator-table))))))) | ||
| 372 | 365 | ||
| 373 | ;; Tokenizing needs to be refined so that ";;" is treated as two | 366 | ;; Tokenizing needs to be refined so that ";;" is treated as two |
| 374 | ;; tokens and also so as to recognize the \n separator (and | 367 | ;; tokens and also so as to recognize the \n separator (and |
| 375 | ;; corresponding continuation lines). | 368 | ;; corresponding continuation lines). |
| 376 | 369 | ||
| 377 | (defconst octave-operator-regexp | 370 | (defun octave-smie--funcall-p () |
| 378 | (regexp-opt (remove "\n" (apply 'append | 371 | "Return non-nil if we're in an expression context. Moves point." |
| 379 | (mapcar 'cdr octave-operator-table))))) | 372 | (looking-at "[ \t]*(")) |
| 373 | |||
| 374 | (defun octave-smie--end-index-p () | ||
| 375 | (let ((ppss (syntax-ppss))) | ||
| 376 | (and (nth 1 ppss) | ||
| 377 | (memq (char-after (nth 1 ppss)) '(?\( ?\[ ?\{))))) | ||
| 378 | |||
| 379 | (defun octave-smie--in-parens-p () | ||
| 380 | (let ((ppss (syntax-ppss))) | ||
| 381 | (and (nth 1 ppss) | ||
| 382 | (eq ?\( (char-after (nth 1 ppss)))))) | ||
| 380 | 383 | ||
| 381 | (defun octave-smie-backward-token () | 384 | (defun octave-smie-backward-token () |
| 382 | (let ((pos (point))) | 385 | (let ((pos (point))) |
| @@ -390,10 +393,7 @@ Non-nil means always go to the next Octave code line after sending." | |||
| 390 | (forward-comment (- (point))) | 393 | (forward-comment (- (point))) |
| 391 | nil) | 394 | nil) |
| 392 | t) | 395 | t) |
| 393 | ;; Ignore it if it's within parentheses. | 396 | (not (octave-smie--in-parens-p))) |
| 394 | (let ((ppss (syntax-ppss))) | ||
| 395 | (not (and (nth 1 ppss) | ||
| 396 | (eq ?\( (char-after (nth 1 ppss))))))) | ||
| 397 | (skip-chars-forward " \t") | 397 | (skip-chars-forward " \t") |
| 398 | ;; Why bother distinguishing \n and ;? | 398 | ;; Why bother distinguishing \n and ;? |
| 399 | ";") ;;"\n" | 399 | ";") ;;"\n" |
| @@ -403,7 +403,15 @@ Non-nil means always go to the next Octave code line after sending." | |||
| 403 | (goto-char (match-beginning 0)) | 403 | (goto-char (match-beginning 0)) |
| 404 | (match-string-no-properties 0)) | 404 | (match-string-no-properties 0)) |
| 405 | (t | 405 | (t |
| 406 | (smie-default-backward-token))))) | 406 | (let ((tok (smie-default-backward-token))) |
| 407 | (cond | ||
| 408 | ((equal tok "enumeration") | ||
| 409 | (if (save-excursion (smie-default-forward-token) | ||
| 410 | (octave-smie--funcall-p)) | ||
| 411 | "enumeration (function)" | ||
| 412 | tok)) | ||
| 413 | ((equal tok "end") (if (octave-smie--end-index-p) "end (index)" tok)) | ||
| 414 | (t tok))))))) | ||
| 407 | 415 | ||
| 408 | (defun octave-smie-forward-token () | 416 | (defun octave-smie-forward-token () |
| 409 | (skip-chars-forward " \t") | 417 | (skip-chars-forward " \t") |
| @@ -417,10 +425,7 @@ Non-nil means always go to the next Octave code line after sending." | |||
| 417 | (not (or (save-excursion (skip-chars-backward " \t") | 425 | (not (or (save-excursion (skip-chars-backward " \t") |
| 418 | ;; Only add implicit ; when needed. | 426 | ;; Only add implicit ; when needed. |
| 419 | (or (bolp) (eq (char-before) ?\;))) | 427 | (or (bolp) (eq (char-before) ?\;))) |
| 420 | ;; Ignore it if it's within parentheses. | 428 | (octave-smie--in-parens-p)))) |
| 421 | (let ((ppss (syntax-ppss))) | ||
| 422 | (and (nth 1 ppss) | ||
| 423 | (eq ?\( (char-after (nth 1 ppss)))))))) | ||
| 424 | (if (eolp) (forward-char 1) (forward-comment 1)) | 429 | (if (eolp) (forward-char 1) (forward-comment 1)) |
| 425 | ;; Why bother distinguishing \n and ;? | 430 | ;; Why bother distinguishing \n and ;? |
| 426 | ";") ;;"\n" | 431 | ";") ;;"\n" |
| @@ -436,7 +441,25 @@ Non-nil means always go to the next Octave code line after sending." | |||
| 436 | (goto-char (match-end 0)) | 441 | (goto-char (match-end 0)) |
| 437 | (match-string-no-properties 0)) | 442 | (match-string-no-properties 0)) |
| 438 | (t | 443 | (t |
| 439 | (smie-default-forward-token)))) | 444 | (let ((tok (smie-default-forward-token))) |
| 445 | (cond | ||
| 446 | ((equal tok "enumeration") | ||
| 447 | (if (octave-smie--funcall-p) | ||
| 448 | "enumeration (function)" | ||
| 449 | tok)) | ||
| 450 | ((equal tok "end") (if (octave-smie--end-index-p) "end (index)" tok)) | ||
| 451 | (t tok)))))) | ||
| 452 | |||
| 453 | (defconst octave--block-offset-keywords | ||
| 454 | (let* ((end-prec (nth 1 (assoc "end" octave-smie-grammar))) | ||
| 455 | (end-matchers | ||
| 456 | (delq nil | ||
| 457 | (mapcar (lambda (x) (if (eq end-prec (nth 2 x)) (car x))) | ||
| 458 | octave-smie-grammar)))) | ||
| 459 | ;; Not sure if it would harm to keep "switch", but the previous code | ||
| 460 | ;; excluded it, presumably because there shouldn't be any code on | ||
| 461 | ;; the lines between "switch" and "case". | ||
| 462 | (delete "switch" end-matchers))) | ||
| 440 | 463 | ||
| 441 | (defun octave-smie-rules (kind token) | 464 | (defun octave-smie-rules (kind token) |
| 442 | (pcase (cons kind token) | 465 | (pcase (cons kind token) |
| @@ -445,15 +468,12 @@ Non-nil means always go to the next Octave code line after sending." | |||
| 445 | ;; - changes to octave-block-offset wouldn't take effect immediately. | 468 | ;; - changes to octave-block-offset wouldn't take effect immediately. |
| 446 | ;; - edebug wouldn't show the use of this variable. | 469 | ;; - edebug wouldn't show the use of this variable. |
| 447 | (`(:elem . basic) octave-block-offset) | 470 | (`(:elem . basic) octave-block-offset) |
| 471 | (`(:list-intro . ,(or "global" "persistent")) t) | ||
| 448 | ;; Since "case" is in the same BNF rules as switch..end, SMIE by default | 472 | ;; Since "case" is in the same BNF rules as switch..end, SMIE by default |
| 449 | ;; aligns it with "switch". | 473 | ;; aligns it with "switch". |
| 450 | (`(:before . "case") (if (not (smie-rule-sibling-p)) octave-block-offset)) | 474 | (`(:before . "case") (if (not (smie-rule-sibling-p)) octave-block-offset)) |
| 451 | (`(:after . ";") | 475 | (`(:after . ";") |
| 452 | (if (smie-rule-parent-p "classdef" "events" "enumeration" "function" "if" | 476 | (if (apply #'smie-rule-parent-p octave--block-offset-keywords) |
| 453 | "while" "else" "elseif" "for" "parfor" | ||
| 454 | "properties" "methods" "otherwise" "case" | ||
| 455 | "try" "catch" "unwind_protect" | ||
| 456 | "unwind_protect_cleanup") | ||
| 457 | (smie-rule-parent octave-block-offset) | 477 | (smie-rule-parent octave-block-offset) |
| 458 | ;; For (invalid) code between switch and case. | 478 | ;; For (invalid) code between switch and case. |
| 459 | ;; (if (smie-rule-parent-p "switch") 4) | 479 | ;; (if (smie-rule-parent-p "switch") 4) |
| @@ -473,28 +493,33 @@ Non-nil means always go to the next Octave code line after sending." | |||
| 473 | (comment-choose-indent))))) | 493 | (comment-choose-indent))))) |
| 474 | 494 | ||
| 475 | 495 | ||
| 496 | (defvar octave-reserved-words | ||
| 497 | (delq nil | ||
| 498 | (mapcar (lambda (x) | ||
| 499 | (setq x (car x)) | ||
| 500 | (and (stringp x) (string-match "\\`[[:alpha:]]" x) x)) | ||
| 501 | octave-smie-grammar)) | ||
| 502 | "Reserved words in Octave.") | ||
| 503 | |||
| 476 | (defvar octave-font-lock-keywords | 504 | (defvar octave-font-lock-keywords |
| 477 | (list | 505 | (list |
| 478 | ;; Fontify all builtin keywords. | 506 | ;; Fontify all builtin keywords. |
| 479 | (cons (concat "\\_<\\(" | 507 | (cons (concat "\\_<" (regexp-opt octave-reserved-words) "\\_>") |
| 480 | (regexp-opt octave-reserved-words) | ||
| 481 | "\\)\\_>") | ||
| 482 | 'font-lock-keyword-face) | 508 | 'font-lock-keyword-face) |
| 483 | ;; Note: 'end' also serves as the last index in an indexing expression. | 509 | ;; Note: 'end' also serves as the last index in an indexing expression, |
| 510 | ;; and 'enumerate' is also a function. | ||
| 484 | ;; Ref: http://www.mathworks.com/help/matlab/ref/end.html | 511 | ;; Ref: http://www.mathworks.com/help/matlab/ref/end.html |
| 512 | ;; Ref: http://www.mathworks.com/help/matlab/ref/enumeration.html | ||
| 485 | (list (lambda (limit) | 513 | (list (lambda (limit) |
| 486 | (while (re-search-forward "\\_<end\\_>" limit 'move) | 514 | (while (re-search-forward "\\_<en\\(?:d\\|umeratio\\(n\\)\\)\\_>" |
| 515 | limit 'move) | ||
| 487 | (let ((beg (match-beginning 0)) | 516 | (let ((beg (match-beginning 0)) |
| 488 | (end (match-end 0))) | 517 | (end (match-end 0))) |
| 489 | (unless (octave-in-string-or-comment-p) | 518 | (unless (octave-in-string-or-comment-p) |
| 490 | (condition-case nil | 519 | (when (if (match-end 1) |
| 491 | (progn | 520 | (octave-smie--funcall-p) |
| 492 | (goto-char beg) | 521 | (octave-smie--end-index-p)) |
| 493 | (backward-up-list) | 522 | (put-text-property beg end 'face nil))))) |
| 494 | (when (memq (char-after) '(?\( ?\[ ?\{)) | ||
| 495 | (put-text-property beg end 'face nil)) | ||
| 496 | (goto-char end)) | ||
| 497 | (error (goto-char end)))))) | ||
| 498 | nil)) | 523 | nil)) |
| 499 | ;; Fontify all operators. | 524 | ;; Fontify all operators. |
| 500 | (cons octave-operator-regexp 'font-lock-builtin-face) | 525 | (cons octave-operator-regexp 'font-lock-builtin-face) |
| @@ -609,27 +634,23 @@ Key bindings: | |||
| 609 | 634 | ||
| 610 | (defcustom inferior-octave-program "octave" | 635 | (defcustom inferior-octave-program "octave" |
| 611 | "Program invoked by `inferior-octave'." | 636 | "Program invoked by `inferior-octave'." |
| 612 | :type 'string | 637 | :type 'string) |
| 613 | :group 'octave) | ||
| 614 | 638 | ||
| 615 | (defcustom inferior-octave-buffer "*Inferior Octave*" | 639 | (defcustom inferior-octave-buffer "*Inferior Octave*" |
| 616 | "Name of buffer for running an inferior Octave process." | 640 | "Name of buffer for running an inferior Octave process." |
| 617 | :type 'string | 641 | :type 'string) |
| 618 | :group 'octave) | ||
| 619 | 642 | ||
| 620 | (defcustom inferior-octave-prompt | 643 | (defcustom inferior-octave-prompt |
| 621 | ;; For Octave >= 3.8, default is always 'octave', see | 644 | ;; For Octave >= 3.8, default is always 'octave', see |
| 622 | ;; http://hg.savannah.gnu.org/hgweb/octave/rev/708173343c50 | 645 | ;; http://hg.savannah.gnu.org/hgweb/octave/rev/708173343c50 |
| 623 | "\\(?:^octave\\(?:.bin\\|.exe\\)?\\(?:-[.0-9]+\\)?\\(?::[0-9]+\\)?\\|^debug\\|^\\)>+ " | 646 | "\\(?:^octave\\(?:.bin\\|.exe\\)?\\(?:-[.0-9]+\\)?\\(?::[0-9]+\\)?\\|^debug\\|^\\)>+ " |
| 624 | "Regexp to match prompts for the inferior Octave process." | 647 | "Regexp to match prompts for the inferior Octave process." |
| 625 | :type 'regexp | 648 | :type 'regexp) |
| 626 | :group 'octave) | ||
| 627 | 649 | ||
| 628 | (defcustom inferior-octave-prompt-read-only comint-prompt-read-only | 650 | (defcustom inferior-octave-prompt-read-only comint-prompt-read-only |
| 629 | "If non-nil, the Octave prompt is read only. | 651 | "If non-nil, the Octave prompt is read only. |
| 630 | See `comint-prompt-read-only' for details." | 652 | See `comint-prompt-read-only' for details." |
| 631 | :type 'boolean | 653 | :type 'boolean |
| 632 | :group 'octave | ||
| 633 | :version "24.4") | 654 | :version "24.4") |
| 634 | 655 | ||
| 635 | (defcustom inferior-octave-startup-file | 656 | (defcustom inferior-octave-startup-file |
| @@ -639,7 +660,6 @@ See `comint-prompt-read-only' for details." | |||
| 639 | The contents of this file are sent to the inferior Octave process on | 660 | The contents of this file are sent to the inferior Octave process on |
| 640 | startup." | 661 | startup." |
| 641 | :type '(choice (const :tag "None" nil) file) | 662 | :type '(choice (const :tag "None" nil) file) |
| 642 | :group 'octave | ||
| 643 | :version "24.4") | 663 | :version "24.4") |
| 644 | 664 | ||
| 645 | (defcustom inferior-octave-startup-args '("-i" "--no-line-editing") | 665 | (defcustom inferior-octave-startup-args '("-i" "--no-line-editing") |
| @@ -647,13 +667,11 @@ startup." | |||
| 647 | For example, for suppressing the startup message and using `traditional' | 667 | For example, for suppressing the startup message and using `traditional' |
| 648 | mode, include \"-q\" and \"--traditional\"." | 668 | mode, include \"-q\" and \"--traditional\"." |
| 649 | :type '(repeat string) | 669 | :type '(repeat string) |
| 650 | :group 'octave | ||
| 651 | :version "24.4") | 670 | :version "24.4") |
| 652 | 671 | ||
| 653 | (defcustom inferior-octave-mode-hook nil | 672 | (defcustom inferior-octave-mode-hook nil |
| 654 | "Hook to be run when Inferior Octave mode is started." | 673 | "Hook to be run when Inferior Octave mode is started." |
| 655 | :type 'hook | 674 | :type 'hook) |
| 656 | :group 'octave) | ||
| 657 | 675 | ||
| 658 | (defcustom inferior-octave-error-regexp-alist | 676 | (defcustom inferior-octave-error-regexp-alist |
| 659 | '(("error:\\s-*\\(.*?\\) at line \\([0-9]+\\), column \\([0-9]+\\)" | 677 | '(("error:\\s-*\\(.*?\\) at line \\([0-9]+\\), column \\([0-9]+\\)" |
| @@ -663,8 +681,7 @@ mode, include \"-q\" and \"--traditional\"." | |||
| 663 | "Value for `compilation-error-regexp-alist' in inferior octave." | 681 | "Value for `compilation-error-regexp-alist' in inferior octave." |
| 664 | :version "24.4" | 682 | :version "24.4" |
| 665 | :type '(repeat (choice (symbol :tag "Predefined symbol") | 683 | :type '(repeat (choice (symbol :tag "Predefined symbol") |
| 666 | (sexp :tag "Error specification"))) | 684 | (sexp :tag "Error specification")))) |
| 667 | :group 'octave) | ||
| 668 | 685 | ||
| 669 | (defvar inferior-octave-compilation-font-lock-keywords | 686 | (defvar inferior-octave-compilation-font-lock-keywords |
| 670 | '(("\\_<PASS\\_>" . compilation-info-face) | 687 | '(("\\_<PASS\\_>" . compilation-info-face) |
| @@ -995,7 +1012,6 @@ directory and makes this the current buffer's default directory." | |||
| 995 | (defcustom inferior-octave-minimal-columns 80 | 1012 | (defcustom inferior-octave-minimal-columns 80 |
| 996 | "The minimal column width for the inferior Octave process." | 1013 | "The minimal column width for the inferior Octave process." |
| 997 | :type 'integer | 1014 | :type 'integer |
| 998 | :group 'octave | ||
| 999 | :version "24.4") | 1015 | :version "24.4") |
| 1000 | 1016 | ||
| 1001 | (defvar inferior-octave-last-column-width nil) | 1017 | (defvar inferior-octave-last-column-width nil) |
| @@ -1180,8 +1196,7 @@ q: Don't fix\n" func file)) | |||
| 1180 | 1196 | ||
| 1181 | (defface octave-function-comment-block | 1197 | (defface octave-function-comment-block |
| 1182 | '((t (:inherit font-lock-doc-face))) | 1198 | '((t (:inherit font-lock-doc-face))) |
| 1183 | "Face used to highlight function comment block." | 1199 | "Face used to highlight function comment block.") |
| 1184 | :group 'octave) | ||
| 1185 | 1200 | ||
| 1186 | (eval-when-compile (require 'texinfo)) | 1201 | (eval-when-compile (require 'texinfo)) |
| 1187 | 1202 | ||
| @@ -1602,7 +1617,6 @@ code line." | |||
| 1602 | :type '(choice (const :tag "Automatic" auto) | 1617 | :type '(choice (const :tag "Automatic" auto) |
| 1603 | (const :tag "One Line" oneline) | 1618 | (const :tag "One Line" oneline) |
| 1604 | (const :tag "Multi Line" multiline)) | 1619 | (const :tag "Multi Line" multiline)) |
| 1605 | :group 'octave | ||
| 1606 | :version "24.4") | 1620 | :version "24.4") |
| 1607 | 1621 | ||
| 1608 | ;; (FN SIGNATURE1 SIGNATURE2 ...) | 1622 | ;; (FN SIGNATURE1 SIGNATURE2 ...) |
| @@ -1661,7 +1675,6 @@ code line." | |||
| 1661 | (defcustom octave-help-buffer "*Octave Help*" | 1675 | (defcustom octave-help-buffer "*Octave Help*" |
| 1662 | "Buffer name for `octave-help'." | 1676 | "Buffer name for `octave-help'." |
| 1663 | :type 'string | 1677 | :type 'string |
| 1664 | :group 'octave | ||
| 1665 | :version "24.4") | 1678 | :version "24.4") |
| 1666 | 1679 | ||
| 1667 | ;; Used in a mode derived from help-mode. | 1680 | ;; Used in a mode derived from help-mode. |
| @@ -1786,7 +1799,6 @@ sentence." | |||
| 1786 | "A list of directories for Octave sources. | 1799 | "A list of directories for Octave sources. |
| 1787 | If the environment variable OCTAVE_SRCDIR is set, it is searched first." | 1800 | If the environment variable OCTAVE_SRCDIR is set, it is searched first." |
| 1788 | :type '(repeat directory) | 1801 | :type '(repeat directory) |
| 1789 | :group 'octave | ||
| 1790 | :version "24.4") | 1802 | :version "24.4") |
| 1791 | 1803 | ||
| 1792 | (defun octave-source-directories () | 1804 | (defun octave-source-directories () |
diff --git a/test/indent/octave.m b/test/indent/octave.m index a7041462f7f..4758f9933cb 100644 --- a/test/indent/octave.m +++ b/test/indent/octave.m | |||
| @@ -1,6 +1,19 @@ | |||
| 1 | ## -*- mode: octave; coding: utf-8 -*- | 1 | ## -*- mode: octave; coding: utf-8 -*- |
| 2 | 0; # Don't make this a function file | 2 | 0; # Don't make this a function file |
| 3 | function res = tcomp (fn) | 3 | function res = tcomp (fn) |
| 4 | |||
| 5 | global x y ... | ||
| 6 | z1 z2 | ||
| 7 | persistent x y ... | ||
| 8 | z1 z2 | ||
| 9 | global x y = 2 ... | ||
| 10 | z1 z2 # FIXME | ||
| 11 | |||
| 12 | do | ||
| 13 | something | ||
| 14 | until x = ... | ||
| 15 | y | ||
| 16 | |||
| 4 | %% res = tcomp (fn) | 17 | %% res = tcomp (fn) |
| 5 | %% imports components and rearranges them. | 18 | %% imports components and rearranges them. |
| 6 | 19 | ||
| @@ -10,6 +23,15 @@ function res = tcomp (fn) | |||
| 10 | 23 | ||
| 11 | data = dlmread(fn, 3, 0); | 24 | data = dlmread(fn, 3, 0); |
| 12 | 25 | ||
| 26 | enumeration | ||
| 27 | first (1) | ||
| 28 | second (2) | ||
| 29 | end | ||
| 30 | |||
| 31 | y = enumeration (x); #Beware: "enumeration" can also be a function! | ||
| 32 | y = foo(enumeration (x), | ||
| 33 | 2); #Beware: "enumeration" can also be a function! | ||
| 34 | |||
| 13 | x = data(:,2:end); | 35 | x = data(:,2:end); |
| 14 | y = 'hello'; | 36 | y = 'hello'; |
| 15 | z = y'; | 37 | z = y'; |