help-octave
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Emacs mode for octave


From: John Eaton
Subject: Emacs mode for octave
Date: Wed, 8 Nov 1995 09:50:53 -0600

Hr. Meier <address@hidden> wrote:

: does anybody know if there exists an emacs-mode for octave ?

There is one distributed with Octave 1.1.1 that is based on a Mablab
mode, but it was never very complete and is no longer being
maintained.  Octave 1.2 will have a completely new Octave mode.  My
current version is appended below.

Thanks,

jwe

-------------------------------cut here-------------------------------
;; octave-mode.el --- Octave mode for GNU Emacs

;;; Copyright (c) 1986, 1993, 1994, 1995 Free Software Foundation, Inc.

;; Author: John W. Eaton <address@hidden>
;; Maintainer: address@hidden
;; Version 0.0 (Sept 29 1995)
;; Keywords: languages

;; This file is not yet a part of GNU Emacs.  It is part of Octave.

;; Octave is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; Octave is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
;; 02111-1307, USA.

;;; Commentary:

;; Octave mode is based on Fortran mode written by Michael D. Prange
;; <address@hidden>.

;;; Bugs to address@hidden

(defconst octave-mode-version "version 0.2")

;;; Code:

(defvar octave-stmt-indent 2
  "*Extra indentation applied to statements in block structures.")

(defvar octave-auto-newline nil
  "*Non nil means auto-insert newline and indent after semicolons.")

;; These next two should maybe be described someone makes it work again.

(defvar octave-comment-indent-style 'relative
  "*nil forces comment lines not to be touched,
'relative indents to current Octave indentation.  Default is 'relative")

(defvar octave-comment-column 32
  "Column to start in-line comments.")

(defvar octave-comment-start "#"
  "*Delimiter inserted to start new comment.")

(defvar octave-comment-line-start "#"
  "*Delimiter inserted to start comment on a new line.")

(defvar octave-comment-indent-char ?\ 
  "*Single-character inserted for Octave comment indentation.
Normally a space.")

(defvar octave-blink-matching-blocks t
  "*Non-nil causes \\[octave-indent-line] on end statements to blink the 
beginning of the block.")

(defvar octave-continuation-indent 4
  "*Extra indentation applied to Octave continuation lines.")

(defvar octave-continuation-string "\\"
  "Character string used for Octave continuation lines.
Normally \\.")

(defvar octave-comment-region "#$$$ "
  "*String inserted by \\[octave-comment-region]\
 at start of each line in region.")

(defvar octave-mode-abbrev-table nil)

(defvar octave-startup-message t
  "*Non-nil displays a startup message when Octave mode is first called.")

(defvar octave-mode-syntax-table nil
  "Syntax table in use in Octave mode buffers.")

(defvar octave-mode-map ()
  "Keymap used in Octave mode.")
(if octave-mode-map
    ()
  (setq octave-mode-map (make-sparse-keymap))
  (define-key octave-mode-map "`" 'octave-abbrev-start)
  (define-key octave-mode-map ";" 'octave-electric-semi)
  (define-key octave-mode-map "\C-c;" 'octave-comment-region)
  (define-key octave-mode-map "\C-c:" 'octave-un-comment-region)
  (define-key octave-mode-map "\e\C-a" 'octave-beginning-of-subprogram)
  (define-key octave-mode-map "\e\C-e" 'octave-end-of-subprogram)
  (define-key octave-mode-map "\e;" 'octave-indent-comment)
  (define-key octave-mode-map "\e\C-h" 'octave-mark-subprogram)
  (define-key octave-mode-map "\e\n" 'octave-split-line)
  (define-key octave-mode-map "\n" 'octave-indent-new-line)
  (define-key octave-mode-map "\e\C-q" 'octave-indent-subprogram)
  (define-key octave-mode-map "\C-c\C-p" 'octave-previous-statement)
  (define-key octave-mode-map "\C-c\C-n" 'octave-next-statement)
  (define-key octave-mode-map "\t" 'octave-indent-line))

;; menus

(require 'easymenu)

(easy-menu-define octave-mode-menu octave-mode-map
    "Menu used in Octave mode."
  (list "Octave"
        ["Mark Subprogram"              octave-mark-subprogram t]
        ["Indent Subprogram"            octave-indent-subprogram t]
        ["Beginning of Subprogram"      octave-beginning-of-subprogram t]
        ["End of Subprogram"            octave-end-of-subprogram t]
        "-"
        ["Previous Statement"           octave-previous-statement t]
        ["Next Statement"               octave-next-statement t]
        "-"
        ["Comment Region"               octave-comment-region t]
        ["Uncomment Region"             octave-un-comment-region t]
        "-"
        ["Split Line at Point"          octave-split-line t]
        ["Newline and Indent"           octave-indent-new-line t]
        ["Indent Line"                  octave-indent-line t]
        "-"
        ["Describe Octave mode"         describe-mode t]))

(easy-menu-add octave-mode-menu octave-mode-map)

;; Some of these definitions are probably way too simple.

(defvar octave-continuation-regexp
  ".*\\(\\\\\\|\\.\\.\\.\\)[ \t]*\\([#%].*\\)?$")

(defvar octave-if-stmt-regexp "\\bif\\b")
(defvar octave-endif-stmt-regexp "\\bendif\\b")

(defvar octave-func-stmt-regexp "\\bfunction\\b")
(defvar octave-endfunc-stmt-regexp "\\bendfunction\\b")

(defvar octave-for-stmt-regexp "\\bfor\\b")
(defvar octave-endfor-stmt-regexp "\\bendfor\\b")

(defvar octave-try-stmt-regexp "\\btry\\b")
(defvar octave-endtry-stmt-regexp "\\bend_try_catch\\b")

(defvar octave-unwind-stmt-regexp "\\bunwind_protect\\b")
(defvar octave-endunwind-stmt-regexp "\\bend_unwind_protect\\b")

(defvar octave-while-stmt-regexp "\\bwhile\\b")
(defvar octave-endwhile-stmt-regexp "\\bendwhile\\b")

(defvar octave-end-block-kw
  "\\bend\\(for\\|function\\|if\\|_try_catch\\|_unwind_protect\\|while\\)?\\b")

(defvar octave-begin-block-kw
  "\\b\\(for\\|function\\|if\\|try\\|unwind_protect\\|while\\)\\b")

(defconst bug-octave-mode "address@hidden"
  "Address of mailing list for Octave mode bugs.")

(defconst octave-comment-start-skip "[#%][ \t]*")

(defconst octave-comment-line-start-skip "^[ \t]*[#%]")

(if octave-mode-syntax-table
    ()
  (setq octave-mode-syntax-table (make-syntax-table))
  (modify-syntax-entry ?\r " " octave-mode-syntax-table)
  (modify-syntax-entry ?+ "." octave-mode-syntax-table)
  (modify-syntax-entry ?- "." octave-mode-syntax-table)
  (modify-syntax-entry ?= "." octave-mode-syntax-table)
  (modify-syntax-entry ?* "." octave-mode-syntax-table)
  (modify-syntax-entry ?/ "." octave-mode-syntax-table)
  (modify-syntax-entry ?> "." octave-mode-syntax-table)
  (modify-syntax-entry ?< "." octave-mode-syntax-table)
  (modify-syntax-entry ?& "." octave-mode-syntax-table)
  (modify-syntax-entry ?| "." octave-mode-syntax-table)
  (modify-syntax-entry ?! "." octave-mode-syntax-table)
  (modify-syntax-entry ?\\ "\\" octave-mode-syntax-table)
  (modify-syntax-entry ?\' "." octave-mode-syntax-table)
  (modify-syntax-entry ?\` "w" octave-mode-syntax-table)  ; for abbrevs
  (modify-syntax-entry ?\" "\"" octave-mode-syntax-table)
  (modify-syntax-entry ?. "w" octave-mode-syntax-table)
  (modify-syntax-entry ?_ "w" octave-mode-syntax-table)
  (modify-syntax-entry ?\% "<" octave-mode-syntax-table)
  (modify-syntax-entry ?\# "<" octave-mode-syntax-table)
  (modify-syntax-entry ?\n ">" octave-mode-syntax-table))

;;;###autoload
(defun octave-mode ()
  "Major mode for editing Octave code.

This mode makes it easier to write Octave code by helping with
indentation, doing some of the typing for you (with abbrevs-mode) and
by showing keywords, comments, strings, etc. in different faces (with
font-lock mode on terminals that support it).

Octave itself is a high-level language, primarily intended for
numerical computations.  It provides a convenient command line
interface for solving linear and nonlinear problems numerically.
Function definitions can also be stored in files, and it can be used
in a batch mode (which is why you need this mode!).

The latest released version of Octave is always available via
anonymous ftp from bevo.che.wisc.edu in the directory /pub/octave.
Complete source and binaries for several popular systems are
available.

\\[octave-indent-line] indents the current Octave line correctly. 
For this to work well, you should use the specific forms of end
statements (endif, endfor, endwhile, etc., and not just `end').

Type ;? or ;\\[help-command] to display a list of built-in abbrevs for
Octave keywords.

Keybindings
===========

\\{octave-mode-map}

Variables you can use to customize Octave mode
==============================================

octave-stmt-indent
  Extra indentation applied to statements in block structures.
  Default value is 2.

octave-auto-newline
  Non nil means auto-insert newline and indent after semicolons are
  typed.  The default value is nil.

octave-comment-start
  Delimiter inserted to start new comment.  Default value is \"#\".

octave-comment-line-start
  Delimiter inserted to start comment on a new line.  Default value
  is \"#\". 

octave-comment-indent-char
  Single-character inserted for Octave comment indentation.  Default
  value is a space.

octave-blink-matching-blocks
  Non-nil causes \\[octave-indent-line] on end statements to blink the
  beginning of the block.  Default value is t.

octave-continuation-indent
  Extra indentation applied to Octave continuation lines.  Default
  value is 4

octave-continuation-string
  String used for Octave continuation lines.  Normally \"\\\".

octave-comment-region
  String inserted by \\[octave-comment-region]\ at start of each line
  in region.  Default value is \"#$$$ \".

octave-startup-message
  Non-nil displays a startup message when Octave mode is first called.

Turning on Octave  mode calls the value of the variable `octave-mode-hook'
with no args, if that value is non-nil.

To begin using this mode for all .m files that you edit, add the
following lines to your .emacs file:

(autoload 'octave-mode \"octave\" nil t)
(setq auto-mode-alist (cons '(\"\\\\.m$\" . octave-mode) auto-mode-alist))

To turn on the abbrevs, auto-fill and font-lock features
automatically, also add the following lines to your .emacs file:

(setq octave-mode-hook
      (list 'turn-on-abbrevs 'turn-on-auto-fill
            (lambda () (if (eq window-system 'x)
                           (progn
                             (font-lock-mode))))))

See the Emacs manual for more information about how to customize font
lock mode."
  (interactive)

  (kill-all-local-variables)

  (if octave-startup-message
      (message
       "Octave mode %s.  Bugs to %s" octave-mode-version bug-octave-mode))
  (setq octave-startup-message nil)

  (setq local-abbrev-table octave-mode-abbrev-table)

  (set-syntax-table octave-mode-syntax-table)

  (make-local-variable 'font-lock-defaults)
  (setq font-lock-defaults '(octave-font-lock-keywords nil nil))

  (make-local-variable 'indent-line-function)
  (setq indent-line-function 'octave-indent-line)

  (make-local-variable 'comment-indent-function)
  (setq comment-indent-function 'octave-comment-hook)

  (make-local-variable 'comment-start-skip)
  (setq comment-start-skip octave-comment-start-skip)

  (use-local-map octave-mode-map)

  (setq mode-name "Octave")

  (setq major-mode 'octave-mode)

  (run-hooks 'octave-mode-hook))

;; Functions for indenting comments.

(defun octave-comment-hook ()
  (if (not (looking-at "\\s<"))
      (octave-calculate-indent)
    (skip-chars-backward " \t")
    (max (+ 1 (current-column)) octave-comment-column)))

(defun octave-indent-comment ()
  "Align or create comment on current line.
Existing comments of all types are recognized and aligned.
Otherwise, a separate-line comment is inserted, on this line
or on a new line inserted before this line if this line is not blank."
  (interactive)
  (beginning-of-line)
  (cond ((save-excursion
           (beginning-of-line)
           (looking-at octave-comment-line-start-skip))
         (let ((icol 0) bol eol)
           (save-excursion
             (while (and (= 0 (forward-line -1))
                         (looking-at "^[ \t]*$")))
             (progn
               (setq eol (save-excursion (end-of-line) (point)))
               (while (and (setq icol (re-search-forward "[#%]" eol t))
                           (octave-is-in-string-p (point))))))
           (if icol
               (progn
                 (save-excursion
                   (goto-char icol)
                   (setq icol (- (current-column) 1)))
                 (delete-horizontal-space)
                 (indent-to icol))
             (delete-horizontal-space)
             (indent-to (octave-calculate-indent))))
         (if (looking-at "[#%][ \t]*$")
             (end-of-line)))
        ;; catches any inline comment and leaves point after
        ;; octave-comment-start-skip
        ((octave-find-comment-start-skip)
         (if octave-comment-start-skip
             (progn (goto-char (match-beginning 0))
                    (if (not (= (current-column) (octave-comment-hook)))
                        (progn (delete-horizontal-space)
                               (indent-to (octave-comment-hook)))))
           (end-of-line)))        ; otherwise goto end of line or sth else?
        ;; No existing comment.  Insert separate-line comment, making
        ;; a new line if necessary. 
        (t
         (if (looking-at "^[ \t]*$")
             (delete-horizontal-space)
           (beginning-of-line)
           (insert "\n")
           (forward-char -1))
         (insert octave-comment-line-start)
         (insert-char octave-comment-indent-char
                      (- (octave-calculate-indent) (current-column))))))

(defun octave-comment-region (beg-region end-region arg)
  "Comments every line in the region.
Puts octave-comment-region at the beginning of every line in the region.
BEG-REGION and END-REGION are args which specify the region boundaries.
With non-nil ARG, uncomments the region."
  (interactive "*r\nP")
  (let ((end-region-mark (make-marker)) (save-point (point-marker)))
    (set-marker end-region-mark end-region)
    (goto-char beg-region)
    (beginning-of-line)
    (if (not arg)                       ;comment the region
        (progn (insert octave-comment-region)
               (while (and  (= (forward-line 1) 0)
                            (< (point) end-region-mark))
                 (insert octave-comment-region)))
      (let ((com (regexp-quote octave-comment-region))) ;uncomment the region
        (if (looking-at com)
            (delete-region (point) (match-end 0)))
        (while (and  (= (forward-line 1) 0)
                     (< (point) end-region-mark))
          (if (looking-at com)

              (delete-region (point) (match-end 0))))))
    (goto-char save-point)
    (set-marker end-region-mark nil)
    (set-marker save-point nil)))

(defun octave-un-comment-region (beg end)
  "Uncomments every line in the region."
  (interactive "*r")
  (octave-comment-region beg end 1))

(defun octave-split-line ()
  "Break line at point and insert continuation marker."
  (interactive)
  (delete-horizontal-space)
  (cond
   ((save-excursion
      (beginning-of-line)
      (looking-at octave-comment-line-start-skip))
    (insert "\n" octave-comment-line-start " "))
   ((octave-is-in-string-p (point))
    (insert "\\\n "))
   (t
    (progn
      (insert octave-continuation-string)
      (insert "\n")
      (octave-indent-line)))))

(defun delete-horizontal-regexp (chars)
  "Delete all characters in CHARS around point.
CHARS is like the inside of a [...] in a regular expression
except that ] is never special and \ quotes ^, - or \."
  (interactive "*s")
  (skip-chars-backward chars)
  (delete-region (point) (progn (skip-chars-forward chars) (point))))

(defun octave-beginning-of-subprogram ()
  "Moves point to the beginning of the current Octave subprogram."
  (interactive)
  (let ((case-fold-search t))
    (beginning-of-line -1)
    (re-search-backward octave-func-stmt-regexp nil 'move)))

(defun octave-end-of-subprogram ()
  "Moves point to the end of the current Octave subprogram."
  (interactive)
  (let ((case-fold-search t))
    (beginning-of-line 2)
    (re-search-forward octave-endfunc-stmt-regexp nil 'move)
    (goto-char (match-beginning 0))
    (forward-line 1)))

(defun octave-mark-subprogram ()
  "Put mark at end of Octave subprogram, point at beginning.
The marks are pushed."
  (interactive)
  (octave-end-of-subprogram)
  (push-mark (point))
  (octave-beginning-of-subprogram))

(defun octave-previous-statement ()
  "Moves point to beginning of the previous Octave statement.
Returns `first-statement' if that statement is the first
non-comment Octave statement in the file, and nil otherwise."
  (interactive)
  (let (not-first-statement)
    (beginning-of-line)
    (while (and (setq not-first-statement (= (forward-line -1) 0))
                (or (looking-at octave-comment-line-start-skip)
                    (looking-at "[ \t]*$")
                    (save-excursion
                      (forward-line -1)
                      (looking-at octave-continuation-regexp))
                    (looking-at
                     (concat "[ \t]*"  octave-comment-start-skip)))))
  (if (not not-first-statement)
      'first-statement)))

(defun octave-next-statement ()
  "Moves point to beginning of the next Octave statement.
Returns `last-statement' if that statement is the last
non-comment Octave statement in the file, and nil otherwise."
  (interactive)
  (let (not-last-statement)
    (beginning-of-line)
    (while (and (setq not-last-statement
                      (and (= (forward-line 1) 0)
                           (not (eobp))))
                (or (looking-at octave-comment-line-start-skip)
                    (save-excursion
                      (forward-line -1)
                      (looking-at octave-continuation-regexp))
                    (looking-at "[ \t]*$")
                    (looking-at
                     (concat "[ \t]*" octave-comment-start-skip)))))
    (if (not not-last-statement)
        'last-statement)))

;; Functions for marking if-endif, for-endfor, while-endwhile,
;; function-endfunction, unwind_protect-end_unwind_protect, and
;; try-end_try_catch blocks.

(defun octave-blink-matching-if ()
  ;; From a Octave endif statement, blink the matching if statement.
  (interactive)
  (octave-blink-matching-block
   octave-if-stmt-regexp octave-endif-stmt-regexp))

(defun octave-mark-if ()
  "Put mark at end of Octave if-endif construct, point at beginning.
The marks are pushed."
  (interactive)
  (octave-mark-block octave-if-stmt-regexp octave-endif-stmt-regexp))

(defun octave-blink-matching-function ()
  ;; From a Octave endfunction statement, blink the matching function
  ;; statement.
  (interactive)
  (octave-blink-matching-block
   octave-func-stmt-regexp octave-endfunc-stmt-regexp))

(defun octave-mark-function ()
  "Put mark at end of Octave function-endfunction construct, point at beg.
The marks are pushed."
  (interactive)
  (octave-mark-block octave-func-stmt-regexp octave-endfunc-stmt-regexp))

(defun octave-blink-matching-for ()
  ;; From a Octave endfor statement, blink the matching for statement.
  (interactive)
  (octave-blink-matching-block
   octave-for-stmt-regexp octave-endfor-stmt-regexp))

(defun octave-mark-for ()
  "Put mark at end of Octave for-endfor construct, point at beginning.
The marks are pushed."
  (interactive)
  (octave-mark-block octave-for-stmt-regexp octave-endfor-stmt-regexp))

(defun octave-blink-matching-try ()
  ;; From a Octave end_try_catch statement, blink the matching try statement.
  (interactive)
  (octave-blink-matching-block
   octave-try-stmt-regexp octave-endtry-stmt-regexp))

(defun octave-mark-try ()
  "Put mark at end of Octave try-endtry construct, point at beginning.
The marks are pushed."
  (interactive)
  (octave-mark-block octave-try-stmt-regexp octave-endtry-stmt-regexp))

(defun octave-blink-matching-unwind ()
  ;; From a Octave end_unwind_protect statement, blink the matching
  ;; unwind_protect statement.
  (interactive)
  (octave-blink-matching-block
   octave-unwind-stmt-regexp octave-endunwind-stmt-regexp))

(defun octave-mark-unwind ()
  "Put mark at end of Octave unwind construct, point at beginning.
The marks are pushed."
  (interactive)
  (octave-mark-block octave-unwind-stmt-regexp octave-endunwind-stmt-regexp))

(defun octave-blink-matching-while ()
  ;; From a Octave endwhile statement, blink the matching while statement.
  (interactive)
  (octave-blink-matching-block
   octave-while-stmt-regexp octave-endwhile-stmt-regexp))

(defun octave-mark-while ()
  "Put mark at end of Octave while-endwhile construct, point at beginning.
The marks are pushed."
  (interactive)
  (octave-mark-block octave-while-stmt-regexp octave-endwhile-stmt-regexp))

;; The functions that do all the work.

(defun octave-blink-matching-block (bb-re eb-re)
  (let ((top-of-window (window-start)) matching-block
        (endblock-point (point)) message)
    (if (save-excursion (beginning-of-line)
                        (skip-chars-forward" \t")
                        (looking-at eb-re))
        (progn
          (if (not (setq matching-block (octave-beginning-block bb-re eb-re)))
              (setq message "No beginning found for this block.")
            (if (< matching-block top-of-window)
                (save-excursion
                  (goto-char matching-block)
                  (beginning-of-line)
                  (setq message
                        (concat "Matches "
                                (buffer-substring
                                 (point) (progn (end-of-line) (point))))))))
          (if message
              (message "%s" message)
            (goto-char matching-block)
            (sit-for 1)
            (goto-char endblock-point))))))

(defun octave-mark-block (bb-re eb-re)
  "Put mark at end of Octave FOR-ENDFOR construct, point at beginning.
The marks are pushed."
  (interactive)
  (let (endblock-point block-point)
    (if (setq endblock-point (octave-end-block bb-re eb-re))
        (if (not (setq block-point (octave-beginning-block bb-re eb-re)))
            (message "No beginning found for this block.")
          ;; Set mark, move point.
          (goto-char endblock-point)
          (if (looking-at (concat eb-re "[ \t]*\\([%#].*\\)?"))
              (forward-line 1)
            (forward-char 5))
          (push-mark)
          (goto-char block-point)
          (beginning-of-line)))))

(defun octave-end-block (bb-re eb-re)
  (if (save-excursion (beginning-of-line)
                      (skip-chars-forward " \t")
                      (looking-at eb-re))
      ;; Sitting on one.
      (match-beginning 0)
    ;; Search for one.
    (save-excursion
      (let ((count 1))
        (while (and (not (= count 0))
                    (not (eq (octave-next-statement) 'last-statement))
                    ;; Keep local to subprogram
                    (not (looking-at octave-endfunc-stmt-regexp)))

          (skip-chars-forward " \t")
          (cond ((looking-at eb-re)
                 (setq count (- count 1)))
                ((looking-at bb-re)
                 (setq count (+ count 1)))))
        (and (= count 0)
             ;; All pairs accounted for.
             (point))))))

(defun octave-beginning-block (bb-re eb-re)
  (if (save-excursion (beginning-of-line)
                      (skip-chars-forward " \t")
                      (looking-at bb-re))
      ;; Sitting on one.
      (match-beginning 0)
    ;; Search for one.
    (save-excursion
      (let ((count 1))
        (while (and (not (= count 0))
                    (not (eq (octave-previous-statement) 'first-statement))
                    ;; Keep local to subprogram
                    (not (looking-at octave-endfunc-stmt-regexp)))

          (skip-chars-forward " \t")
          (cond ((looking-at bb-re)
                 (setq count (- count 1)))
                ((looking-at eb-re)
                 (setq count (+ count 1)))))

        (and (= count 0)
             ;; All pairs accounted for.
             (point))))))

(defun octave-indent-line ()
  "Indents current Octave line based on its contents and on previous lines."
  (interactive)
  (let ((cfi (octave-calculate-indent))
        (prev-indent (current-indentation))
        (prev-point (point))
        new-indent new-point new-eol new-bol new-indent-point)
    (if (save-excursion
          (beginning-of-line)
          (and (not (looking-at octave-comment-line-start-skip))
               (not (octave-find-comment-start-skip))))
        (progn
          (beginning-of-line)
          (delete-horizontal-space)
;;
;; There must be a better way!
;;
          (setq new-indent (indent-to cfi))
          (setq new-point (+ prev-point (- new-indent prev-indent)))
          (setq new-eol (save-excursion (end-of-line) (point)))
          (setq new-bol (save-excursion (beginning-of-line) (point)))
          (setq new-indent-point (+ new-bol new-indent))
          (cond
           ((> prev-point new-eol)
            (goto-char new-eol))
           ((< new-point new-indent-point)
            (goto-char new-indent-point))
           (t
            (goto-char new-point))))
      (octave-indent-comment))
    (if (and auto-fill-function
             (> (save-excursion (end-of-line) (current-column)) fill-column))
        (save-excursion
          (end-of-line)
          (octave-do-auto-fill)))
    (if octave-blink-matching-blocks
        (progn
          (octave-blink-matching-if)
          (octave-blink-matching-for)
          (octave-blink-matching-try)
          (octave-blink-matching-unwind)
          (octave-blink-matching-while)
          (octave-blink-matching-function)))))

(defun octave-indent-new-line ()
  "Reindent the current Octave line, insert a newline and indent the newline.
An abbrev before point is expanded if `abbrev-mode' is non-nil."
  (interactive)
  (if abbrev-mode (expand-abbrev))
  (save-excursion
    (beginning-of-line)
    (skip-chars-forward " \t")
    (if (or (looking-at "end")  ;Reindent only where it is most
            (looking-at "else") ;likely to be necessary
            (looking-at octave-continuation-regexp))
        (octave-indent-line)))
  (end-of-line)
  (newline)
  (octave-indent-line))

(defun octave-indent-subprogram ()
  "Properly indents the Octave subprogram which contains point."
  (interactive)
  (save-excursion
    (octave-mark-subprogram)
    (message "Indenting function...")
    (indent-region (point) (mark) nil))
  (message "Indenting function...done."))

;; XELSE says whether to increment or decrement the count if we are
;; looking at an else-type statement.
;;
;; XEND says whether to decrement the count if we are looking at an
;; end-type statement.

(defun octave-calc-indent-this-line (xelse xend)
  (let ((icol 0)
        (have-if 0)
        (have-func 0)
        (have-uwp 0)
        (have-tc 0))
    (save-excursion
      (beginning-of-line)
      (while (< (point) (save-excursion (end-of-line) (point)))
        (cond
         ((and (looking-at "\\bif\\b")
               (not (octave-is-in-string-p (point))))
          (progn
            (setq icol (+ icol octave-stmt-indent))
            (setq have-if (+ have-if 1))))
         ((and (looking-at "\\btry\\b")
               (not (octave-is-in-string-p (point))))
          (progn
            (setq icol (+ icol octave-stmt-indent))
            (setq have-tc (+ have-tc 1))))
         ((and (looking-at "\\bunwind_protect\\b")
               (not (octave-is-in-string-p (point))))
          (progn
            (setq icol (+ icol octave-stmt-indent))
            (setq have-uwp (+ have-uwp 1))))
         ((and (looking-at "\\bcatch\\b")
               (not (octave-is-in-string-p (point))))
          (progn
            (if (eq have-tc 0)
                (setq icol (+ icol (* xelse octave-stmt-indent))))))
         ((and (looking-at "\\b\\(else\\|elseif\\)\\b")
               (not (octave-is-in-string-p (point))))
          (progn
            (if (eq have-if 0)
                (setq icol (+ icol (* xelse octave-stmt-indent))))))
         ((and (looking-at "\\bunwind_protect_cleanup\\b")
               (not (octave-is-in-string-p (point))))
          (progn
            (if (eq have-uwp 0)
                (setq icol (+ icol (* xelse octave-stmt-indent))))))
         ((and (looking-at "\\bend")
                (not (octave-is-in-string-p (point))))
          (progn
            (setq icol (- icol (* xend octave-stmt-indent)))
            (cond
             ((and (> have-if 0) (looking-at "\\bendif\\b"))
              (setq have-if (- have-if 1)))
             ((and (> have-func 0) (looking-at "\\bendfunction\\b"))
              (setq have-func (- have-func 1)))
             ((and (> have-tc 0) (looking-at "\\bend_try_catch\\b"))
              (setq have-tc (- have-tc 1)))
             ((and (> have-uwp 0) (looking-at "\\bend_unwind_protect\\b"))
              (setq have-uwp (- have-uwp 1))))))
         ((and (looking-at octave-begin-block-kw)
                (not (octave-is-in-string-p (point))))
          (setq icol (+ icol octave-stmt-indent))))
        (forward-char)))
    icol))

(defun octave-calculate-indent ()
  "Calculates the Octave indent column based on previous lines."
  (let ((indent-col 0)
        first-statement)
    (save-excursion
      (setq first-statement (octave-previous-statement))
      (if (not first-statement)
          (progn
            (skip-chars-forward " \t")
            (setq indent-col (+ (octave-current-line-indentation)
                                (octave-calc-indent-this-line 1 0))))))
    ;; This fixes things if the line we are on is and else- or
    ;; end-type statement.

    (if (save-excursion
          (beginning-of-line)
          (skip-chars-forward " \t")
          (looking-at "\\(end\\|else\\|catch\\|unwind_protect_cleanup\\)"))
        (setq indent-col (+ indent-col
                            (octave-calc-indent-this-line -1 1))))
    indent-col))

(defun octave-electric-semi ()
  (interactive)
  (if (and (not (octave-is-in-string-p (point))) octave-auto-newline)
      (progn
        (insert ";")
        (octave-indent-new-line))
    (insert ";")))

(defun octave-current-line-indentation ()
  "Indentation of current line.  For comment lines, returns
indentation of the first non-indentation text within the comment."
  (save-excursion
    (beginning-of-line)
    (if (looking-at octave-comment-line-start-skip)
        (progn
          (goto-char (match-end 0))
          (skip-chars-forward (char-to-string octave-comment-indent-char)))
      (skip-chars-forward " \t"))
    ;; Move past whitespace.
    (skip-chars-forward " \t")
    (current-column)))

(defun octave-find-comment-start-skip ()
  "Move to past `octave-comment-start-skip' found on current line.
Return t if `octave-comment-start-skip' found, nil if not."
;;; In order to move point only if octave-comment-start-skip is found,
;;; this one uses a lot of save-excursions.  Note that re-search-forward
;;; moves point even if octave-comment-start-skip is inside a string-constant.
;;; Some code expects certain values for match-beginning and end
  (interactive)
  (if (save-excursion
        (re-search-forward octave-comment-start-skip
                           (save-excursion (end-of-line) (point)) t))
      (let ((save-match-beginning (match-beginning 0))
            (save-match-end (match-end 0)))
        (if (octave-is-in-string-p (match-beginning 0))
            (save-excursion
              (goto-char save-match-end)
              (octave-find-comment-start-skip)) ; recurse to end of line
          (goto-char save-match-beginning)
          (re-search-forward octave-comment-start-skip
                             (save-excursion (end-of-line) (point)) t)
          (goto-char (match-end 0))
          t))
    nil))

;;;From: address@hidden (Simon Marshall)
;;; Find the next % or # not in a string.
(defun octave-match-comment (limit)
  (let (found)
    (while (and (setq found (re-search-forward "[#%]" limit t))
                (octave-is-in-string-p (point))))
    (if (not found)
        nil
      ;; Cheaper than `looking-at' "[#%].*".
      (store-match-data
       (list (1- (point)) (progn (end-of-line) (min (point) limit))))
      t)))

;;;From: address@hidden (Ralf Fassel)
;;; Test if TAB format continuation lines work.
(defun octave-is-in-string-p (where)
  "Return non-nil if POS (a buffer position) is inside a Octave string,
nil else."
  (save-excursion
    (goto-char where)
    (cond
     ((bolp) nil)                       ; bol is never inside a string
     ((save-excursion                   ; comment lines too
        (beginning-of-line)(looking-at octave-comment-line-start-skip)) nil)
     (t (let (;; ok, serious now. Init some local vars:
              (parse-state '(0 nil nil nil nil nil 0))
              (quoted-comment-start (if comment-start
                                        (regexp-quote comment-start)))
              (not-done t)
              parse-limit
              end-of-line
              )
          ;; move to start of current statement
          (octave-next-statement)
          (octave-previous-statement)
          ;; now parse up to WHERE
          (while not-done
            (if (or ;; skip to next line if:
                 ;; - comment line?
                 (looking-at octave-comment-line-start-skip)
                 ;; - at end of line?
                 (eolp))
                ;; get around a bug in forward-line in versions <= 18.57
                (if (or (> (forward-line 1) 0) (eobp))
                    (setq not-done nil))
              ;; else:
              ;; if we are at beginning of code line, skip any
              ;; whitespace, labels and tab continuation markers.
              (if (bolp) (skip-chars-forward " \t"))
              ;; if we are in column <= 5 now, check for continuation char
              (cond ((= 5 (current-column)) (forward-char 1))
                    ((and (< (current-column) 5)
                          (equal octave-continuation-string
                                 (char-to-string (following-char)))
                          (forward-char 1))))
              ;; find out parse-limit from here
              (setq end-of-line (save-excursion (end-of-line)(point)))
              (setq parse-limit (min where end-of-line))
              ;; now parse if still in limits
              (if (< (point) where)
                  (setq parse-state (parse-partial-sexp
                                     (point) parse-limit nil nil parse-state))
                (setq not-done nil))
              ))
          ;; result is
          (nth 3 parse-state))))))

(defun octave-auto-fill-mode (arg)
  "Toggle octave-auto-fill mode.
With ARG, turn `octave-auto-fill' mode on iff ARG is positive.
In `octave-auto-fill' mode, inserting a space at a column beyond `fill-column'
automatically breaks the line at a previous space."
  (interactive "P")
  (prog1 (setq auto-fill-function
               (if (if (null arg)
                       (not auto-fill-function)
                     (> (prefix-numeric-value arg) 0))
                   'octave-indent-line
                 nil))
    (force-mode-line-update)))

(defun octave-do-auto-fill ()
  (interactive)
  (let* ((opoint (point))
         (bol (save-excursion (beginning-of-line) (point)))
         (eol (save-excursion (end-of-line) (point)))
         (bos (min eol (+ bol (octave-current-line-indentation))))
         (quote
          (save-excursion
            (goto-char bol)
            (if (looking-at octave-comment-line-start-skip)
                nil                     ; OK to break quotes on comment lines.
              (move-to-column fill-column)
              (cond ((octave-is-in-string-p (point))
                     (save-excursion (re-search-backward "[^']'[^']" bol t)
                                     (1+ (point))))
                    (t nil)))))
         ;;
         ;; decide where to split the line. If a position for a quoted
         ;; string was found above then use that, else break the line
         ;; before the last delimiter.
         ;; Delimiters are whitespace, commas, and operators.
         ;; Will break before a pair of *'s.
         ;;
         (fill-point
          (or quote
              (save-excursion
                (move-to-column (1+ fill-column))
                (skip-chars-backward "^ \t\n,'+-/*=)")
                (if (<= (point) (1+ bos))
                    (progn
                      (move-to-column (1+ fill-column))
;;;what is this doing???
                      (if (not (re-search-forward "[\t\n,'+-/*)=]" eol t))
                          (goto-char bol))))
                (if (bolp)
                    (re-search-forward "[ \t]" opoint t)
                  (forward-char -1)
                  (if (looking-at "'")
                      (forward-char 1)
                    (skip-chars-backward " \t\*")))
                (1+ (point)))))
         )
    ;; if we are in an in-line comment, don't break unless the
    ;; line of code is longer than it should be. Otherwise
    ;; break the line at the column computed above.
    ;;
    ;; Need to use octave-find-comment-start-skip to make sure that
    ;; quoted # or %'s don't prevent a break.
    (if (not (or (save-excursion
                   (if (and (re-search-backward
                             octave-comment-start-skip bol t)
                            (not (octave-is-in-string-p (point))))
                       (progn
                         (skip-chars-backward " \t")
                         (< (current-column) (1+ fill-column)))))
                 (save-excursion
                   (goto-char fill-point)
                   (bolp))))
        (if (> (save-excursion
                 (goto-char fill-point) (current-column))
               (1+ fill-column))
            (progn (goto-char fill-point)
                   (octave-break-line))
          (save-excursion
            (if (> (save-excursion
                     (goto-char fill-point)
                     (current-column))
                   (+ (octave-calculate-indent) octave-continuation-indent))
                (progn
                  (goto-char fill-point)
                  (octave-break-line))))))
    ))

(defun octave-break-line ()
  (interactive)
  (let ((opoint (point))
        (bol (save-excursion (beginning-of-line) (point)))
        (eol (save-excursion (end-of-line) (point)))
        (comment-string nil))
    (save-excursion
      (if (and octave-comment-start-skip
               (save-excursion
                 (beginning-of-line)
                 (octave-find-comment-start-skip)))
          (progn
            (re-search-backward octave-comment-line-start-skip bol t)
            (setq comment-string (buffer-substring (point) eol))
            (delete-region (point) eol))))

    (if comment-string
        (save-excursion
          (goto-char bol)
          (end-of-line)
          (delete-horizontal-space)
          (insert octave-comment-start)))))

;; Abbrevs.

(if octave-mode-abbrev-table
    ()
  (let ((ac abbrevs-changed))
    (define-abbrev-table 'octave-mode-abbrev-table ())
    (define-abbrev octave-mode-abbrev-table  "`a" "all_va_args" nil)
    (define-abbrev octave-mode-abbrev-table  "`b" "break" nil)
    (define-abbrev octave-mode-abbrev-table  "`ca" "catch" nil)
    (define-abbrev octave-mode-abbrev-table  "`c" "continue" nil)
    (define-abbrev octave-mode-abbrev-table  "`el" "else" nil)
    (define-abbrev octave-mode-abbrev-table  "`eli" "elseif" nil)
    (define-abbrev octave-mode-abbrev-table  "`et" "end_try_catch" nil)
    (define-abbrev octave-mode-abbrev-table  "`eu" "end_unwind_protect" nil)
    (define-abbrev octave-mode-abbrev-table  "`ef" "endfor" nil)
    (define-abbrev octave-mode-abbrev-table  "`efu" "endfunction" nil)
    (define-abbrev octave-mode-abbrev-table  "`ei" "endif" nil)
    (define-abbrev octave-mode-abbrev-table  "`ew" "endwhile" nil)
    (define-abbrev octave-mode-abbrev-table  "`f" "for" nil)
    (define-abbrev octave-mode-abbrev-table  "`fu" "function" nil)
    (define-abbrev octave-mode-abbrev-table  "`gl" "global" nil)
    (define-abbrev octave-mode-abbrev-table  "`gp" "gplot" nil)
    (define-abbrev octave-mode-abbrev-table  "`gs" "gsplot" nil)
    (define-abbrev octave-mode-abbrev-table  "`if" "if ()" nil)
    (define-abbrev octave-mode-abbrev-table  "`rp" "replot" nil)
    (define-abbrev octave-mode-abbrev-table  "`r" "return" nil)
    (define-abbrev octave-mode-abbrev-table  "`t" "try" nil)
    (define-abbrev octave-mode-abbrev-table  "`up" "unwind_protect" nil)
    (define-abbrev octave-mode-abbrev-table  "`upc" "unwind_protect_cleanup" 
nil)
    (define-abbrev octave-mode-abbrev-table  "`w" "while ()" nil)
    (setq abbrevs-changed ac)))

(defun octave-abbrev-start ()
  "Typing `\\[help-command] or `? lists all the Octave abbrevs. 
Any other key combination is executed normally."
  (interactive)
  (let (c)
    (insert last-command-char)
    (if (or (eq (setq c (read-event)) ??) ;; insert char if not equal to `?'
            (eq c help-char))
        (octave-abbrev-help)
      (setq unread-command-events (list c)))))

(defun octave-abbrev-help ()
  "List the currently defined abbrevs in Octave mode."
  (interactive)
  (message "Listing abbrev table...")
  (display-buffer (octave-prepare-abbrev-list-buffer))
  (message "Listing abbrev table...done"))

(defun octave-prepare-abbrev-list-buffer ()
  (save-excursion
    (set-buffer (get-buffer-create "*Abbrevs*"))
    (erase-buffer)
    (insert-abbrev-table-description 'octave-mode-abbrev-table t)
    (goto-char (point-min))
    (set-buffer-modified-p nil)
    (edit-abbrevs-mode))
  (get-buffer-create "*Abbrevs*"))

;; Font-lock stuff.

;; Regexps (for the Fortran mode that this was originally based on)
;; done by address@hidden with help from Ulrik Dickow <address@hidden> and
;; probably others Si's forgotten about (sorry).

(defvar octave-font-lock-keywords-1
  (let ((comment-chars "%#"))
    (list
     ;; Fontify comments.
     (cons (concat "[" comment-chars "].*$")
           'font-lock-comment-face)
     ;; Function declarations.
     (list "\\<\\(function\\)\\>[ \t]*\\(\\sw+\\)?"
           '(1 font-lock-keyword-face)
           '(2 font-lock-function-name-face nil t))))
  "For consideration as a value of `octave-font-lock-keywords'.
This does fairly subdued highlighting.")

(defvar octave-font-lock-keywords
  (append octave-font-lock-keywords-1
          (let ((fkeywords
                 
"\\(all_va_args\\|break\\|catch\\|continue\\|else\\|elseif\\|end_try_catch\\|end_unwind_protect\\|endfor\\|endfunction\\|endif\\|endwhile\\|end\\|for\\|function\\|global\\|gplot\\|gsplot\\|if\\|replot\\|return\\|try\\|unwind_protect\\|unwind_protect_cleanup\\|while\\)")
                (flogicals
                 "\\(&&\\|||\\|<=\\|>=\\|==\\|<\\|>\\|!=\\|!\\)"))
            (list
             ;; Fontify all builtin keywords.
             (cons (concat "\\<\\(" fkeywords "\\)\\>")
                   'font-lock-keyword-face)
             ;; Fontify all builtin operators.
             (cons (concat "\\(" flogicals "\\)")
                   'font-lock-reference-face))))
  "For consideration as a value of `octave-font-lock-keywords'.
This does a lot more highlighting.")

(provide 'octave-mode)

;; Compile this file when saving it:

;;; Local Variables:
;;; after-save-hook: ((lambda () (byte-compile-file buffer-file-name)))
;;; End:


reply via email to

[Prev in Thread] Current Thread [Next in Thread]