]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/octave-mod.el
Convert consecutive FSF copyright years to ranges.
[gnu-emacs] / lisp / progmodes / octave-mod.el
index 0fb4356051b1886577db9fff7733ccd018439f94..7ec4cf3d94771aa74a50c13a50550d47b0df3dfb 100644 (file)
@@ -1,7 +1,6 @@
 ;;; octave-mod.el --- editing Octave source files under Emacs
 
-;; Copyright (C) 1997, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-;; Free Software Foundation, Inc.
+;; Copyright (C) 1997, 2001-2011  Free Software Foundation, Inc.
 
 ;; Author: Kurt Hornik <Kurt.Hornik@wu-wien.ac.at>
 ;; Author: John Eaton <jwe@octave.org>
@@ -179,29 +178,28 @@ parenthetical grouping.")
         '(3 font-lock-function-name-face nil t)))
   "Additional Octave expressions to highlight.")
 
-(defvar octave-font-lock-syntactic-keywords
+(defun octave-syntax-propertize-function (start end)
+  (goto-char start)
+  (octave-syntax-propertize-sqs end)
+  (funcall (syntax-propertize-rules
   ;; Try to distinguish the string-quotes from the transpose-quotes.
-  '(("[[({,; ]\\('\\)" (1 "\"'"))
-    (octave-font-lock-close-quotes)))
-
-(defun octave-font-lock-close-quotes (limit)
-  "Fix the syntax-table of the closing quotes of single-quote strings."
-  ;; Freely inspired from perl-font-lock-special-syntactic-constructs.
-  (let ((state (syntax-ppss)))
-    (while (< (point) limit)
-      (cond
-       ((eq (nth 3 state) ?\')
+            ("[[({,; ]\\('\\)"
+             (1 (prog1 "\"'" (octave-syntax-propertize-sqs end)))))
+           (point) end))
+
+(defun octave-syntax-propertize-sqs (end)
+  "Propertize the content/end of single-quote strings."
+  (when (eq (nth 3 (syntax-ppss)) ?\')
         ;; A '..' string.
-        (save-excursion
-          (when (and (or (looking-at "\\('\\)")
-                         (re-search-forward "[^\\]\\(?:\\\\\\\\\\)*\\('\\)"
-                                            nil t))
-                     (not (eobp)))
+    (when (re-search-forward
+           "\\(?:\\=\\|[^']\\)\\(?:''\\)*\\('\\)\\($\\|[^']\\)" end 'move)
+      (goto-char (match-beginning 2))
+            (when (eq (char-before (match-beginning 1)) ?\\)
+              ;; Backslash cannot escape a single quote.
+              (put-text-property (1- (match-beginning 1)) (match-beginning 1)
+                                 'syntax-table (string-to-syntax ".")))
             (put-text-property (match-beginning 1) (match-end 1)
-                               'syntax-table (string-to-syntax "\"'"))))))
-
-      (setq state (parse-partial-sexp (point) limit nil nil state
-                                     'syntax-table)))))
+                         'syntax-table (string-to-syntax "\"'")))))
 
 (defcustom inferior-octave-buffer "*Inferior Octave*"
   "Name of buffer for running an inferior Octave process."
@@ -213,9 +211,6 @@ parenthetical grouping.")
 (defvar octave-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map "`" 'octave-abbrev-start)
-    (define-key map ";" 'octave-electric-semi)
-    (define-key map " " 'octave-electric-space)
-    (define-key map "\n" 'octave-reindent-then-newline-and-indent)
     (define-key map "\e\n" 'octave-indent-new-comment-line)
     (define-key map "\M-\C-q" 'octave-indent-defun)
     (define-key map "\C-c\C-b" 'octave-submit-bug-report)
@@ -320,16 +315,6 @@ parenthetical grouping.")
     table)
   "Syntax table in use in `octave-mode' buffers.")
 
-(defcustom octave-auto-indent nil
-  "Non-nil means indent line after a semicolon or space in Octave mode."
-  :type 'boolean
-  :group 'octave)
-
-(defcustom octave-auto-newline nil
-  "Non-nil means automatically newline after a semicolon in Octave mode."
-  :type 'boolean
-  :group 'octave)
-
 (defcustom octave-blink-matching-block t
   "Control the blinking of matching Octave block keywords.
 Non-nil means show matching begin of block when inserting a space,
@@ -342,29 +327,6 @@ newline or semicolon after an else or end keyword."
   :type 'integer
   :group 'octave)
 
-(defvar octave-block-else-regexp
-  (concat "\\<\\("
-         (mapconcat 'identity octave-else-keywords "\\|")
-         "\\)\\>"))
-(defvar octave-block-end-regexp
-  (concat "\\<\\("
-         (mapconcat 'identity octave-end-keywords "\\|")
-         "\\)\\>"))
-(defvar octave-block-else-or-end-regexp
-  (concat octave-block-else-regexp "\\|" octave-block-end-regexp))
-(defvar octave-block-match-alist
-  '(("do" . ("until"))
-    ("for" . ("end" "endfor"))
-    ("function" . ("end" "endfunction"))
-    ("if" . ("else" "elseif" "end" "endif"))
-    ("switch" . ("case" "otherwise" "end" "endswitch"))
-    ("try" . ("catch" "end" "end_try_catch"))
-    ("unwind_protect" . ("unwind_protect_cleanup" "end" "end_unwind_protect"))
-    ("while" . ("end" "endwhile")))
-  "Alist with Octave's matching block keywords.
-Has Octave's begin keywords as keys and a list of the matching else or
-end keywords as associated values.")
-
 (defvar octave-block-comment-start
   (concat (make-string 2 octave-comment-char) " ")
   "String to insert to start a new Octave comment on an empty line.")
@@ -435,49 +397,48 @@ Non-nil means always go to the next Octave code line after sending."
     ;; could be convenient to treat it as one.
     (assoc "...")))
 
-(defconst octave-smie-op-levels
-  (smie-prec2-levels
+(defconst octave-smie-bnf-table
+  '((atom)
+    ;; We can't distinguish the first element in a sequence with
+    ;; precedence grammars, so we can't distinguish the condition
+    ;; if the `if' from the subsequent body, for example.
+    ;; This has to be done later in the indentation rules.
+    (exp (exp "\n" exp)
+         ;; We need to mention at least one of the operators in this part
+         ;; of the grammar: if the BNF and the operator table have
+         ;; no overlap, SMIE can't know how they relate.
+         (exp ";" exp)
+         ("try" exp "catch" exp "end_try_catch")
+         ("try" exp "catch" exp "end")
+         ("unwind_protect" exp
+          "unwind_protect_cleanup" exp "end_unwind_protect")
+         ("unwind_protect" exp "unwind_protect_cleanup" exp "end")
+         ("for" exp "endfor")
+         ("for" exp "end")
+         ("do" exp "until" atom)
+         ("while" exp "endwhile")
+         ("while" exp "end")
+         ("if" exp "endif")
+         ("if" exp "else" exp "endif")
+         ("if" exp "elseif" exp "else" exp "endif")
+         ("if" exp "elseif" exp "elseif" exp "else" exp "endif")
+         ("if" exp "elseif" exp "elseif" exp "else" exp "end")
+         ("switch" exp "case" exp "endswitch")
+         ("switch" exp "case" exp "otherwise" exp "endswitch")
+         ("switch" exp "case" exp "case" exp "otherwise" exp "endswitch")
+         ("switch" exp "case" exp "case" exp "otherwise" exp "end")
+         ("function" exp "endfunction")
+         ("function" exp "end"))
+    ;; (fundesc (atom "=" atom))
+    ))
+
+(defconst octave-smie-grammar
+  (smie-prec2->grammar
    (smie-merge-prec2s
-    (smie-bnf-precedence-table
-     '((atom)
-       ;; We can't distinguish the first element in a sequence with
-       ;; precedence grammars, so we can't distinguish the condition
-       ;; if the `if' from the subsequent body, for example.
-       ;; This has to be done later in the indentation rules.
-       (exp (exp "\n" exp)
-            ;; We need to mention at least one of the operators in this part
-            ;; of the grammar: if the BNF and the operator table have
-            ;; no overlap, SMIE can't know how they relate.
-            (exp ";" exp)
-            ("try" exp "catch" exp "end_try_catch")
-            ("try" exp "catch" exp "end")
-            ("unwind_protect" exp
-             "unwind_protect_cleanup" exp "end_unwind_protect")
-            ("unwind_protect" exp "unwind_protect_cleanup" exp "end")
-            ("for" exp "endfor")
-            ("for" exp "end")
-            ("do" exp "until" atom)
-            ("while" exp "endwhile")
-            ("while" exp "end")
-            ("if" exp "endif")
-            ("if" exp "else" exp "endif")
-            ("if" exp "elseif" exp "else" exp "endif")
-            ("if" exp "elseif" exp "elseif" exp "else" exp "endif")
-            ("if" exp "elseif" exp "elseif" exp "else" exp "end")
-            ("switch" exp "case" exp "endswitch")
-            ("switch" exp "case" exp "otherwise" exp "endswitch")
-            ("switch" exp "case" exp "case" exp "otherwise" exp "endswitch")
-            ("switch" exp "case" exp "case" exp "otherwise" exp "end")
-            ("function" exp "endfunction")
-            ("function" exp "end"))
-       ;; (fundesc (atom "=" atom))
-       )
-     '((assoc "\n" ";")))
-
-    (smie-precs-precedence-table
-     (append octave-operator-table
-             '((nonassoc " -dummy- "))) ;Bogus anchor at the end.
-     ))))
+    (smie-bnf->prec2 octave-smie-bnf-table
+                     '((assoc "\n" ";")))
+
+    (smie-precs->prec2 octave-operator-table))))
 
 ;; Tokenizing needs to be refined so that ";;" is treated as two
 ;; tokens and also so as to recognize the \n separator (and
@@ -542,15 +503,26 @@ Non-nil means always go to the next Octave code line after sending."
    (t
     (smie-default-forward-token))))
 
-(defconst octave-smie-indent-rules
-  '((";"
-     (:parent ("function" "if" "while" "else" "elseif" "for" "otherwise"
-               "case" "try" "catch" "unwind_protect" "unwind_protect_cleanup")
-      ;; FIXME: don't hardcode 2.
-      (+ parent octave-block-offset))
-     ;; (:parent "switch" 4) ;For (invalid) code between switch and case.
-     0)
-    ((:before . "case") octave-block-offset)))
+(defun octave-smie-rules (kind token)
+  (pcase (cons kind token)
+    ;; We could set smie-indent-basic instead, but that would have two
+    ;; disadvantages:
+    ;; - changes to octave-block-offset wouldn't take effect immediately.
+    ;; - edebug wouldn't show the use of this variable.
+    (`(:elem . basic) octave-block-offset)
+    ;; Since "case" is in the same BNF rules as switch..end, SMIE by default
+    ;; aligns it with "switch".
+    (`(:before . "case") (if (not (smie-rule-sibling-p)) octave-block-offset))
+    (`(:after . ";")
+     (if (smie-rule-parent-p "function" "if" "while" "else" "elseif" "for"
+                             "otherwise" "case" "try" "catch" "unwind_protect"
+                             "unwind_protect_cleanup")
+         (smie-rule-parent octave-block-offset)
+       ;; For (invalid) code between switch and case.
+       ;; (if (smie-parent-p "switch") 4)
+       0))))
+
+(defvar electric-layout-rules)
 
 ;;;###autoload
 (define-derived-mode octave-mode prog-mode "Octave"
@@ -581,14 +553,6 @@ Keybindings
 Variables you can use to customize Octave mode
 ==============================================
 
-`octave-auto-indent'
-  Non-nil means indent current line after a semicolon or space.
-  Default is nil.
-
-`octave-auto-newline'
-  Non-nil means auto-insert a newline and indent after a semicolon.
-  Default is nil.
-
 `octave-blink-matching-block'
   Non-nil means show matching begin of block when inserting a space,
   newline or semicolon after an else or end keyword.  Default is t.
@@ -638,17 +602,21 @@ already added.  You just need to add a description of the problem,
 including a reproducible test case and send the message."
   (setq local-abbrev-table octave-abbrev-table)
 
-  (smie-setup octave-smie-op-levels octave-smie-indent-rules)
+  (smie-setup octave-smie-grammar #'octave-smie-rules
+              :forward-token  #'octave-smie-forward-token
+              :backward-token #'octave-smie-backward-token)
   (set (make-local-variable 'smie-indent-basic) 'octave-block-offset)
-  (set (make-local-variable 'smie-backward-token-function)
-       'octave-smie-backward-token)
-  (set (make-local-variable 'smie-forward-token-function)
-       'octave-smie-forward-token)
-  (set (make-local-variable 'forward-sexp-function)
-       'smie-forward-sexp-command)
-  (set (make-local-variable 'smie-closer-alist)
-       (mapcar (lambda (elem) (cons (car elem) (car (last elem))))
-               octave-block-match-alist))
+
+    (set (make-local-variable 'smie-blink-matching-triggers)
+       (cons ?\; smie-blink-matching-triggers))
+  (unless octave-blink-matching-block
+    (remove-hook 'post-self-insert-hook #'smie-blink-matching-open 'local))
+
+  (set (make-local-variable 'electric-indent-chars)
+       (cons ?\; electric-indent-chars))
+  ;; IIUC matlab-mode takes the opposite approach: it makes RET insert
+  ;; a ";" at those places where it's correct (i.e. outside of parens).
+  (set (make-local-variable 'electric-layout-rules) '((?\; . after)))
 
   (set (make-local-variable 'comment-start) octave-comment-start)
   (set (make-local-variable 'comment-end) "")
@@ -671,9 +639,10 @@ including a reproducible test case and send the message."
   (set (make-local-variable 'normal-auto-fill-function) 'octave-auto-fill)
 
   (set (make-local-variable 'font-lock-defaults)
-       '(octave-font-lock-keywords nil nil nil nil
-         (font-lock-syntactic-keywords . octave-font-lock-syntactic-keywords)
-         (parse-sexp-lookup-properties . t)))
+       '(octave-font-lock-keywords))
+
+  (set (make-local-variable 'syntax-propertize-function)
+       #'octave-syntax-propertize-function)
 
   (set (make-local-variable 'imenu-generic-expression)
        octave-mode-imenu-generic-expression)
@@ -685,8 +654,7 @@ including a reproducible test case and send the message."
        'octave-beginning-of-defun)
 
   (easy-menu-add octave-mode-menu)
-  (octave-initialize-completions)
-  (run-mode-hooks 'octave-mode-hook))
+  (octave-initialize-completions))
 
 (defvar info-lookup-mode)
 
@@ -748,7 +716,7 @@ The new line is properly indented."
     (error "Cannot split a code line inside a string"))
    (t
     (insert (concat " " octave-continuation-string))
-    (octave-reindent-then-newline-and-indent))))
+    (reindent-then-newline-and-indent))))
 
 (defun octave-indent-defun ()
   "Properly indent the Octave function which contains point."
@@ -841,59 +809,11 @@ The block marked is the one that contains point or follows point."
   (unless (or (looking-at "\\s(")
               (save-excursion
                 (let* ((token (funcall smie-forward-token-function))
-                       (level (assoc token smie-op-levels)))
+                       (level (assoc token smie-grammar)))
                   (and level (null (cadr level))))))
     (backward-up-list 1))
   (mark-sexp))
 
-(defun octave-blink-matching-block-open ()
-  "Blink the matching Octave begin block keyword.
-If point is right after an Octave else or end type block keyword, move
-cursor momentarily to the corresponding begin keyword.
-Signal an error if the keywords are incompatible."
-  (interactive)
-  (let (bb-keyword bb-arg eb-keyword pos eol)
-    (if (and (octave-not-in-string-or-comment-p)
-            (looking-at "\\>")
-            (save-excursion
-              (skip-syntax-backward "w")
-              (octave-looking-at-kw octave-block-else-or-end-regexp)))
-       (save-excursion
-         (cond
-          ((match-end 1)
-           (setq eb-keyword
-                 (buffer-substring-no-properties
-                  (match-beginning 1) (match-end 1)))
-           (backward-up-list 1))
-          ((match-end 2)
-           (setq eb-keyword
-                 (buffer-substring-no-properties
-                  (match-beginning 2) (match-end 2)))
-           (backward-sexp 1)))
-         (setq pos (match-end 0)
-               bb-keyword
-               (buffer-substring-no-properties
-                (match-beginning 0) pos)
-               pos (+ pos 1)
-               eol (line-end-position)
-               bb-arg
-               (save-excursion
-                 (save-restriction
-                   (goto-char pos)
-                   (while (and (skip-syntax-forward "^<" eol)
-                               (octave-in-string-p)
-                               (not (forward-char 1))))
-                   (skip-syntax-backward " ")
-                   (buffer-substring-no-properties pos (point)))))
-         (if (member eb-keyword
-                     (cdr (assoc bb-keyword octave-block-match-alist)))
-             (progn
-               (message "Matches `%s %s'" bb-keyword bb-arg)
-               (if (pos-visible-in-window-p)
-                   (sit-for blink-matching-delay)))
-           (error "Block keywords `%s' and `%s' do not match"
-                  bb-keyword eb-keyword))))))
-
 (defun octave-beginning-of-defun (&optional arg)
   "Move backward to the beginning of an Octave function.
 With positive ARG, do it that many times.  Negative argument -N means
@@ -1077,53 +997,6 @@ variables."
   (apply 'completion-in-region (octave-completion-at-point-function)))
 \f
 ;;; Electric characters && friends
-(defun octave-reindent-then-newline-and-indent ()
-  "Reindent current Octave line, insert newline, and indent the new line.
-If Abbrev mode is on, expand abbrevs first."
-  ;; FIXME: None of this is Octave-specific.
-  (interactive)
-  (if abbrev-mode (expand-abbrev))
-  (if octave-blink-matching-block
-      (octave-blink-matching-block-open))
-  (reindent-then-newline-and-indent))
-
-(defun octave-electric-semi ()
-  "Insert a semicolon in Octave mode.
-Maybe expand abbrevs and blink matching block open keywords.
-Reindent the line if `octave-auto-indent' is non-nil.
-Insert a newline if `octave-auto-newline' is non-nil."
-  (interactive)
-  (if (not (octave-not-in-string-or-comment-p))
-      (insert ";")
-    (if abbrev-mode (expand-abbrev))
-    (if octave-blink-matching-block
-       (octave-blink-matching-block-open))
-    (if octave-auto-indent
-       (indent-according-to-mode))
-    (insert ";")
-    (if octave-auto-newline
-       (newline-and-indent))))
-
-(defun octave-electric-space ()
-  "Insert a space in Octave mode.
-Maybe expand abbrevs and blink matching block open keywords.
-Reindent the line if `octave-auto-indent' is non-nil."
-  (interactive)
-  (setq last-command-event ? )
-  (if (and octave-auto-indent
-          (not (octave-not-in-string-or-comment-p)))
-      (progn
-       (indent-according-to-mode)
-       (self-insert-command 1))
-    (if abbrev-mode (expand-abbrev))
-    (if octave-blink-matching-block
-       (octave-blink-matching-block-open))
-    (if (and octave-auto-indent
-            (save-excursion
-              (skip-syntax-backward " ")
-              (not (bolp))))
-       (indent-according-to-mode))
-    (self-insert-command 1)))
 
 (defun octave-abbrev-start ()
   "Start entering an Octave abbreviation.
@@ -1281,8 +1154,6 @@ code line."
     octave-maintainer-address
     (concat "Emacs version " emacs-version)
     (list
-     'octave-auto-indent
-     'octave-auto-newline
      'octave-blink-matching-block
      'octave-block-offset
      'octave-comment-char
@@ -1296,5 +1167,4 @@ code line."
 
 (provide 'octave-mod)
 
-;; arch-tag: 05f1ce09-be87-4c00-803e-4919ffa26c23
 ;;; octave-mod.el ends here