-;;; pascal.el - Major mode for editing pascal source in emacs.
+;;; pascal.el --- major mode for editing pascal source in Emacs
-;;; Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+;; Copyright (C) 1993, 94, 95, 96, 97, 98, 1999, 2000 Free Software Foundation, Inc.
-;;; Author: Espen Skoglund (espensk@stud.cs.uit.no)
-;;; Keywords: languages
+;; Author: Espen Skoglund <esk@gnu.org>
+;; Keywords: languages
-;;; This file is part of GNU Emacs.
+;; This file is part of GNU Emacs.
-;;; This program 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 of the License, or
-;;; (at your option) any later version.
+;; GNU Emacs 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.
-;;; This program 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.
+;; GNU Emacs 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 this program; if not, write to the Free Software
-;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+;; 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, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
;;; Commentary:
-;;; USAGE
-;;; =====
-
-;;; Emacs should enter Pascal mode when you find a Pascal source file.
-;;; When you have entered Pascal mode, you may get more info by pressing
-;;; C-h m. You may also get online help describing various functions by:
-;;; C-h f <Name of function you want described>
-
-;;; If you want to customize Pascal mode to fit you better, you may add
-;;; these lines (the values of the variables presented here are the defaults):
-;;;
-;;; ;; User customization for Pascal mode
-;;; (setq pascal-indent-level 3
-;;; pascal-case-indent 2
-;;; pascal-auto-newline nil
-;;; pascal-tab-always-indent t
-;;; pascal-auto-endcomments t
-;;; pascal-auto-lineup '(all)
-;;; pascal-toggle-completions nil
-;;; pascal-type-keywords '("array" "file" "packed" "char"
-;;; "integer" "real" "string" "record")
-;;; pascal-start-keywords '("begin" "end" "function" "procedure"
-;;; "repeat" "until" "while" "read" "readln"
-;;; "reset" "rewrite" "write" "writeln")
-;;; pascal-separator-keywords '("downto" "else" "mod" "div" "then"))
-
-;;; KNOWN BUGS / BUGREPORTS
-;;; =======================
-;;; As far as I know, there are no bugs in the current version of this
-;;; package. This may not be true however, since I never use this mode
-;;; myself and therefore would never notice them anyway. If you do
-;;; find any bugs, you may submit them to: espensk@stud.cs.uit.no
-;;; as well as to bug-gnu-emacs@prep.ai.mit.edu.
+;; USAGE
+;; =====
+
+;; Emacs should enter Pascal mode when you find a Pascal source file.
+;; When you have entered Pascal mode, you may get more info by pressing
+;; C-h m. You may also get online help describing various functions by:
+;; C-h f <Name of function you want described>
+
+;; If you want to customize Pascal mode to fit you better, you may add
+;; these lines (the values of the variables presented here are the defaults):
+;;
+;; ;; User customization for Pascal mode
+;; (setq pascal-indent-level 3
+;; pascal-case-indent 2
+;; pascal-auto-newline nil
+;; pascal-tab-always-indent t
+;; pascal-auto-endcomments t
+;; pascal-auto-lineup '(all)
+;; pascal-toggle-completions nil
+;; pascal-type-keywords '("array" "file" "packed" "char"
+;; "integer" "real" "string" "record")
+;; pascal-start-keywords '("begin" "end" "function" "procedure"
+;; "repeat" "until" "while" "read" "readln"
+;; "reset" "rewrite" "write" "writeln")
+;; pascal-separator-keywords '("downto" "else" "mod" "div" "then"))
+
+;; KNOWN BUGS / BUGREPORTS
+;; =======================
+;; As far as I know, there are no bugs in the current version of this
+;; package. This may not be true however, since I never use this mode
+;; myself and therefore would never notice them anyway. If you do
+;; find any bugs, you may submit them to: esk@gnu.org as well as to
+;; bug-gnu-emacs@gnu.org.
\f
;;; Code:
-(defconst pascal-mode-version "2.3"
- "Version of `pascal.el'.")
+(defgroup pascal nil
+ "Major mode for editing Pascal source in Emacs"
+ :group 'languages)
(defvar pascal-mode-abbrev-table nil
"Abbrev table in use in Pascal-mode buffers.")
(define-key pascal-mode-map "." 'electric-pascal-semi-or-dot)
(define-key pascal-mode-map ":" 'electric-pascal-colon)
(define-key pascal-mode-map "=" 'electric-pascal-equal)
+ (define-key pascal-mode-map "#" 'electric-pascal-hash)
(define-key pascal-mode-map "\r" 'electric-pascal-terminate-line)
(define-key pascal-mode-map "\t" 'electric-pascal-tab)
- (define-key pascal-mode-map "\e\t" 'pascal-complete-word)
- (define-key pascal-mode-map "\e?" 'pascal-show-completions)
+ (define-key pascal-mode-map "\M-\t" 'pascal-complete-word)
+ (define-key pascal-mode-map "\M-?" 'pascal-show-completions)
(define-key pascal-mode-map "\177" 'backward-delete-char-untabify)
- (define-key pascal-mode-map "\e\C-h" 'pascal-mark-defun)
- (define-key pascal-mode-map "\C-cb" 'pascal-insert-block)
+ (define-key pascal-mode-map "\M-\C-h" 'pascal-mark-defun)
+ (define-key pascal-mode-map "\C-c\C-b" 'pascal-insert-block)
(define-key pascal-mode-map "\M-*" 'pascal-star-comment)
(define-key pascal-mode-map "\C-c\C-c" 'pascal-comment-area)
(define-key pascal-mode-map "\C-c\C-u" 'pascal-uncomment-area)
- (define-key pascal-mode-map "\e\C-a" 'pascal-beg-of-defun)
- (define-key pascal-mode-map "\e\C-e" 'pascal-end-of-defun)
- (define-key pascal-mode-map "\C-cg" 'pascal-goto-defun)
- (define-key pascal-mode-map "\C-c\C-o" 'pascal-outline)
+ (define-key pascal-mode-map "\M-\C-a" 'pascal-beg-of-defun)
+ (define-key pascal-mode-map "\M-\C-e" 'pascal-end-of-defun)
+ (define-key pascal-mode-map "\C-c\C-d" 'pascal-goto-defun)
+ (define-key pascal-mode-map "\C-c\C-o" 'pascal-outline)
;;; A command to change the whole buffer won't be used terribly
;;; often, so no need for a key binding.
; (define-key pascal-mode-map "\C-cd" 'pascal-downcase-keywords)
; (define-key pascal-mode-map "\C-cu" 'pascal-upcase-keywords)
; (define-key pascal-mode-map "\C-cc" 'pascal-capitalize-keywords)
)
-
+
+(defvar pascal-imenu-generic-expression
+ '((nil "^[ \t]*\\(function\\|procedure\\)[ \t\n]+\\([a-zA-Z0-9_.:]+\\)" 2))
+ "Imenu expression for Pascal-mode. See `imenu-generic-expression'.")
+
(defvar pascal-keywords
- '("and" "array" "begin" "case" "const" "div" "do" "downto" "else" "end"
- "file" "for" "function" "goto" "if" "in" "label" "mod" "nil" "not" "of"
- "or" "packed" "procedure" "program" "record" "repeat" "set" "then" "to"
+ '("and" "array" "begin" "case" "const" "div" "do" "downto" "else" "end"
+ "file" "for" "function" "goto" "if" "in" "label" "mod" "nil" "not" "of"
+ "or" "packed" "procedure" "program" "record" "repeat" "set" "then" "to"
"type" "until" "var" "while" "with"
;; The following are not standard in pascal, but widely used.
"get" "put" "input" "output" "read" "readln" "reset" "rewrite" "write"
(defconst pascal-beg-block-re "\\<\\(begin\\|case\\|record\\|repeat\\)\\>")
(defconst pascal-end-block-re "\\<\\(end\\|until\\)\\>")
(defconst pascal-declaration-re "\\<\\(const\\|label\\|type\\|var\\)\\>")
+(defconst pascal-progbeg-re "\\<\\program\\>")
(defconst pascal-defun-re "\\<\\(function\\|procedure\\|program\\)\\>")
(defconst pascal-sub-block-re "\\<\\(if\\|else\\|for\\|while\\|with\\)\\>")
(defconst pascal-noindent-re "\\<\\(begin\\|end\\|until\\|else\\)\\>")
(if pascal-mode-syntax-table
()
(setq pascal-mode-syntax-table (make-syntax-table))
- (modify-syntax-entry ?\\ "\\" pascal-mode-syntax-table)
- (modify-syntax-entry ?( "()1" pascal-mode-syntax-table)
+ (modify-syntax-entry ?\\ "." pascal-mode-syntax-table)
+ (modify-syntax-entry ?( "()1" pascal-mode-syntax-table)
(modify-syntax-entry ?) ")(4" pascal-mode-syntax-table)
- (modify-syntax-entry ?* ". 23" pascal-mode-syntax-table)
+ (modify-syntax-entry ?* ". 23b" pascal-mode-syntax-table)
(modify-syntax-entry ?{ "<" pascal-mode-syntax-table)
(modify-syntax-entry ?} ">" pascal-mode-syntax-table)
(modify-syntax-entry ?+ "." pascal-mode-syntax-table)
(modify-syntax-entry ?> "." pascal-mode-syntax-table)
(modify-syntax-entry ?& "." pascal-mode-syntax-table)
(modify-syntax-entry ?| "." pascal-mode-syntax-table)
- (modify-syntax-entry ?_ "w" pascal-mode-syntax-table)
+ (modify-syntax-entry ?_ "_" pascal-mode-syntax-table)
(modify-syntax-entry ?\' "\"" pascal-mode-syntax-table))
-(defvar pascal-indent-level 3
- "*Indentation of Pascal statements with respect to containing block.")
-
-(defvar pascal-case-indent 2
- "*Indentation for case statements.")
-
-(defvar pascal-auto-newline nil
- "*Non-nil means automatically newline after simcolons and the punctation mark
-after an end.")
-
-(defvar pascal-tab-always-indent t
- "*Non-nil means TAB in Pascal mode should always reindent the current line,
-regardless of where in the line point is when the TAB command is used.")
-
-(defvar pascal-auto-endcomments t
- "*Non-nil means a comment { ... } is set after the ends which ends cases and
-functions. The name of the function or case will be set between the braces.")
-
-(defvar pascal-auto-lineup '(all)
+(defconst pascal-font-lock-keywords (purecopy
+ (list
+ '("^[ \t]*\\(function\\|pro\\(cedure\\|gram\\)\\)\\>[ \t]*\\([a-z]\\)"
+ 1 font-lock-keyword-face)
+ '("^[ \t]*\\(function\\|pro\\(cedure\\|gram\\)\\)\\>[ \t]*\\([a-z][a-z0-9_]*\\)"
+ 3 font-lock-function-name-face t)
+; ("type" "const" "real" "integer" "char" "boolean" "var"
+; "record" "array" "file")
+ (cons (concat "\\<\\(array\\|boolean\\|c\\(har\\|onst\\)\\|file\\|"
+ "integer\\|re\\(al\\|cord\\)\\|type\\|var\\)\\>")
+ 'font-lock-type-face)
+ '("\\<\\(label\\|external\\|forward\\)\\>" . font-lock-constant-face)
+ '("\\<\\([0-9]+\\)[ \t]*:" 1 font-lock-function-name-face)
+; ("of" "to" "for" "if" "then" "else" "case" "while"
+; "do" "until" "and" "or" "not" "in" "with" "repeat" "begin" "end")
+ (concat "\\<\\("
+ "and\\|begin\\|case\\|do\\|e\\(lse\\|nd\\)\\|for\\|i[fn]\\|"
+ "not\\|o[fr]\\|repeat\\|t\\(hen\\|o\\)\\|until\\|w\\(hile\\|ith\\)"
+ "\\)\\>")
+ '("\\<\\(goto\\)\\>[ \t]*\\([0-9]+\\)?"
+ 1 font-lock-keyword-face)
+ '("\\<\\(goto\\)\\>[ \t]*\\([0-9]+\\)?"
+ 2 font-lock-keyword-face t)))
+ "Additional expressions to highlight in Pascal mode.")
+(put 'pascal-mode 'font-lock-defaults '(pascal-font-lock-keywords nil t))
+
+(defcustom pascal-indent-level 3
+ "*Indentation of Pascal statements with respect to containing block."
+ :type 'integer
+ :group 'pascal)
+
+(defcustom pascal-case-indent 2
+ "*Indentation for case statements."
+ :type 'integer
+ :group 'pascal)
+
+(defcustom pascal-auto-newline nil
+ "*Non-nil means automatically insert newlines in certain cases.
+These include after semicolons and after the punctuation mark after an `end'."
+ :type 'boolean
+ :group 'pascal)
+
+(defcustom pascal-indent-nested-functions t
+ "*Non-nil means nested functions are indented."
+ :type 'boolean
+ :group 'pascal)
+
+(defcustom pascal-tab-always-indent t
+ "*Non-nil means TAB in Pascal mode should always reindent the current line.
+If this is nil, TAB inserts a tab if it is at the end of the line
+and follows non-whitespace text."
+ :type 'boolean
+ :group 'pascal)
+
+(defcustom pascal-auto-endcomments t
+ "*Non-nil means automatically insert comments after certain `end's.
+Specifically, this is done after the ends of cases statements and functions.
+The name of the function or case is included between the braces."
+ :type 'boolean
+ :group 'pascal)
+
+(defcustom pascal-auto-lineup '(all)
"*List of contexts where auto lineup of :'s or ='s should be done.
Elements can be of type: 'paramlist', 'declaration' or 'case', which will
do auto lineup in parameterlist, declarations or case-statements
respectively. The word 'all' will do all lineups. '(case paramlist) for
instance will do lineup in case-statements and parameterlist, while '(all)
-will do all lineups.")
-
-(defvar pascal-toggle-completions nil
- "*Non-nil means that \\<pascal-mode-map>\\[pascal-complete-label] should \
-not display a completion buffer when
-the label couldn't be completed, but instead toggle the possible completions
-with repeated \\[pascal-complete-label]'s.")
-
-(defvar pascal-type-keywords
+will do all lineups."
+ :type '(set :extra-offset 8
+ (const :tag "Everything" all)
+ (const :tag "Parameter lists" paramlist)
+ (const :tag "Decalrations" declaration)
+ (const :tag "Case statements" case))
+ :group 'pascal)
+
+(defcustom pascal-toggle-completions nil
+ "*Non-nil means \\<pascal-mode-map>\\[pascal-complete-word] should try all possible completions one by one.
+Repeated use of \\[pascal-complete-word] will show you all of them.
+Normally, when there is more than one possible completion,
+it displays a list of all possible completions."
+ :type 'boolean
+ :group 'pascal)
+
+(defcustom pascal-type-keywords
'("array" "file" "packed" "char" "integer" "real" "string" "record")
"*Keywords for types used when completing a word in a declaration or parmlist.
-\(eg. integer, real, char.) The types defined within the Pascal program
-will be completed runtime, and should not be added to this list.")
+These include integer, real, char, etc.
+The types defined within the Pascal program
+are handled in another way, and should not be added to this list."
+ :type '(repeat (string :tag "Keyword"))
+ :group 'pascal)
-(defvar pascal-start-keywords
+(defcustom pascal-start-keywords
'("begin" "end" "function" "procedure" "repeat" "until" "while"
"read" "readln" "reset" "rewrite" "write" "writeln")
"*Keywords to complete when standing at the first word of a statement.
-\(eg. begin, repeat, until, readln.)
+These are keywords such as begin, repeat, until, readln.
The procedures and variables defined within the Pascal program
-will be completed runtime and should not be added to this list.")
+are handled in another way, and should not be added to this list."
+ :type '(repeat (string :tag "Keyword"))
+ :group 'pascal)
-(defvar pascal-separator-keywords
+(defcustom pascal-separator-keywords
'("downto" "else" "mod" "div" "then")
"*Keywords to complete when NOT standing at the first word of a statement.
-\(eg. downto, else, mod, then.)
-Variables and function names defined within the
-Pascal program are completed runtime and should not be added to this list.")
+These are keywords such as downto, else, mod, then.
+Variables and function names defined within the Pascal program
+are handled in another way, and should not be added to this list."
+ :type '(repeat (string :tag "Keyword"))
+ :group 'pascal)
+
;;;
;;; Macros
(defun pascal-declaration-end ()
(let ((nest 1))
(while (and (> nest 0)
- (re-search-forward
- "[:=]\\|\\(\\<record\\>\\)\\|\\(\\<end\\>\\)"
+ (re-search-forward
+ "[:=]\\|\\(\\<record\\>\\)\\|\\(\\<end\\>\\)"
(save-excursion (end-of-line 2) (point)) t))
(cond ((match-beginning 1) (setq nest (1+ nest)))
- ((match-beginning 2) (setq nest (1- nest)))))))
+ ((match-beginning 2) (setq nest (1- nest)))
+ ((looking-at "[^(\n]+)") (setq nest 0))))))
(defun pascal-declaration-beg ()
((match-beginning 3) (setq nest (1+ nest)))))
(= nest 0)))
-
+
(defsubst pascal-within-string ()
(save-excursion
(nth 3 (parse-partial-sexp (pascal-get-beg-of-line) (point)))))
Variables controlling indentation/edit style:
- pascal-indent-level (default 3)
+ pascal-indent-level (default 3)
Indentation of Pascal statements with respect to containing block.
- pascal-case-indent (default 2)
+ pascal-case-indent (default 2)
Indentation for case statements.
- pascal-auto-newline (default nil)
- Non-nil means automatically newline after simcolons and the punctation mark
- after an end.
+ pascal-auto-newline (default nil)
+ Non-nil means automatically newline after semicolons and the punctuation
+ mark after an end.
+ pascal-indent-nested-functions (default t)
+ Non-nil means nested functions are indented.
pascal-tab-always-indent (default t)
Non-nil means TAB in Pascal mode should always reindent the current line,
regardless of where in the line point is when the TAB command is used.
- pascal-auto-endcomments (default t)
+ pascal-auto-endcomments (default t)
Non-nil means a comment { ... } is set after the ends which ends cases and
functions. The name of the function or case will be set between the braces.
- pascal-auto-lineup (default t)
- List of contexts where auto lineup of :'s or ='s hould be done.
+ pascal-auto-lineup (default t)
+ List of contexts where auto lineup of :'s or ='s should be done.
See also the user variables pascal-type-keywords, pascal-start-keywords and
pascal-separator-keywords.
(set-syntax-table pascal-mode-syntax-table)
(make-local-variable 'indent-line-function)
(setq indent-line-function 'pascal-indent-line)
+ (make-local-variable 'comment-indent-function)
(setq comment-indent-function 'pascal-indent-comment)
(make-local-variable 'parse-sexp-ignore-comments)
- (setq parse-sexp-ignore-comments t)
+ (setq parse-sexp-ignore-comments nil)
+ (make-local-variable 'blink-matching-paren-dont-ignore-comments)
+ (setq blink-matching-paren-dont-ignore-comments t)
(make-local-variable 'case-fold-search)
(setq case-fold-search t)
+ (make-local-variable 'comment-start)
+ (setq comment-start "{")
+ (make-local-variable 'comment-start-skip)
+ (setq comment-start-skip "(\\*+ *\\|{ *")
+ (make-local-variable 'comment-end)
+ (setq comment-end "}")
+ ;; Font lock support
+ (make-local-variable 'font-lock-defaults)
+ (setq font-lock-defaults '(pascal-font-lock-keywords nil t))
+ ;; Imenu support
+ (make-local-variable 'imenu-generic-expression)
+ (setq imenu-generic-expression pascal-imenu-generic-expression)
+ (setq imenu-case-fold-search t)
(run-hooks 'pascal-mode-hook))
\f
(search-forward "*)" (pascal-get-end-of-line) t))))
(setq setstar t))))
;; If last line was a star comment line then this one shall be too.
- (if (null setstar)
+ (if (null setstar)
(pascal-indent-line)
(insert "* "))))
(let ((pascal-tab-always-indent nil))
(pascal-indent-command))))
+(defun electric-pascal-hash ()
+ "Insert `#', and indent to column 0 if this is a CPP directive."
+ (interactive)
+ (insert last-command-char)
+ (if (save-excursion (beginning-of-line) (looking-at "^[ \t]*#"))
+ (save-excursion (beginning-of-line)
+ (delete-horizontal-space))))
+
(defun electric-pascal-tab ()
"Function called when TAB is pressed in Pascal mode."
(interactive)
- ;; Do nothing if within a string.
- (if (pascal-within-string)
+ ;; Do nothing if within a string or in a CPP directive.
+ (if (or (pascal-within-string)
+ (and (not (bolp))
+ (save-excursion (beginning-of-line) (eq (following-char) ?#))))
(insert "\t")
;; If pascal-tab-always-indent, indent the beginning of the line.
(if pascal-tab-always-indent
(save-excursion
(beginning-of-line)
(pascal-indent-line))
- (insert "\t"))
+ (if (save-excursion
+ (skip-chars-backward " \t")
+ (bolp))
+ (pascal-indent-line)
+ (insert "\t")))
(pascal-indent-command)))
\f
(defun pascal-insert-block ()
"Insert Pascal begin ... end; block in the code with right indentation."
(interactive)
- (pascal-indent-line)
(insert "begin")
(electric-pascal-terminate-line)
(save-excursion
- (electric-pascal-terminate-line)
+ (newline)
(insert "end;")
(beginning-of-line)
(pascal-indent-line)))
(if (not (looking-at (concat "\\s \\|\\s)\\|" pascal-defun-re)))
(forward-sexp 1))
(let ((nest 0) (max -1) (func 0)
- (reg (concat pascal-beg-block-re "\\|"
+ (reg (concat pascal-beg-block-re "\\|"
pascal-end-block-re "\\|"
pascal-defun-re)))
(while (re-search-backward reg nil 'move)
(pascal-beg-of-defun))
(forward-char 1)
(let ((nest 0) (func 1)
- (reg (concat pascal-beg-block-re "\\|"
+ (reg (concat pascal-beg-block-re "\\|"
pascal-end-block-re "\\|"
pascal-defun-re)))
(while (and (/= func 0)
(defun pascal-end-of-statement ()
"Move forward to end of current statement."
(interactive)
- (let ((nest 0) pos
+ (let ((parse-sexp-ignore-comments t)
+ (nest 0) pos
(regexp (concat "\\(" pascal-beg-block-re "\\)\\|\\("
pascal-end-block-re "\\)")))
(if (not (looking-at "[ \t\n]")) (forward-sexp -1))
(catch 'found
(while t
(re-search-forward regexp nil 'move)
- (setq nest (if (match-end 1)
+ (setq nest (if (match-end 1)
(1+ nest)
(1- nest)))
(cond ((eobp)
(defconst pascal-indent-alist
'((block . (+ ind pascal-indent-level))
(case . (+ ind pascal-case-indent))
+ (caseblock . ind) (cpp . 0)
(declaration . (+ ind pascal-indent-level))
(paramlist . (pascal-indent-paramlist t))
- (comment . (pascal-indent-comment t))
+ (comment . (pascal-indent-comment))
(defun . ind) (contexp . ind)
- (unknown . 0) (string . 0)))
+ (unknown . ind) (string . 0) (progbeg . 0)))
(defun pascal-indent-command ()
"Indent for special part of code."
(let* ((indent-str (pascal-calculate-indent))
(type (car indent-str))
(ind (car (cdr indent-str))))
- (if (looking-at "^[0-9a-zA-Z]+[ \t]*:[^=]")
+ ;; Labels should not be indented.
+ (if (and (looking-at "^[0-9a-zA-Z]+[ \t]*:[^=]")
+ (not (eq type 'declaration)))
(search-forward ":" nil t))
(delete-horizontal-space)
- ;; Some thing should not be indented
- (if (or (and (eq type 'declaration) (looking-at pascal-declaration-re))
- (looking-at pascal-defun-re))
- ()
- ;; Other things should have no extra indent
- (if (looking-at pascal-noindent-re)
- (indent-to ind)
- ;; But most lines are treated this way:
- (indent-to (eval (cdr (assoc type pascal-indent-alist))))
- ))))
+ (cond (; Some things should not be indented
+ (or (and (eq type 'declaration) (looking-at pascal-declaration-re))
+ (eq type 'cpp))
+ ())
+ (; Other things should have no extra indent
+ (looking-at pascal-noindent-re)
+ (indent-to ind))
+ (; Nested functions should be indented
+ (looking-at pascal-defun-re)
+ (if (and pascal-indent-nested-functions
+ (eq type 'defun))
+ (indent-to (+ ind pascal-indent-level))
+ (indent-to ind)))
+ (; But most lines are treated this way
+ (indent-to (eval (cdr (assoc type pascal-indent-alist))))
+ ))))
(defun pascal-calculate-indent ()
"Calculate the indent of the current Pascal line.
Return a list of two elements: (INDENT-TYPE INDENT-LEVEL)."
(save-excursion
- (let* ((oldpos (point))
+ (let* ((parse-sexp-ignore-comments t)
+ (oldpos (point))
(state (save-excursion (parse-partial-sexp (point-min) (point))))
- (nest 0) (par 0) (complete nil)
- (elsed (looking-at "[ \t]*else\\>"))
+ (nest 0) (par 0) (complete (looking-at "[ \t]*end\\>"))
+ (elsed (looking-at "[ \t]*else\\>")) (funccnt 0)
+ (did-func (looking-at "[ \t]*\\(procedure\\|function\\)\\>"))
(type (catch 'nesting
;; Check if inside a string, comment or parenthesis
(cond ((nth 3 state) (throw 'nesting 'string))
((nth 4 state) (throw 'nesting 'comment))
((> (car state) 0)
(goto-char (scan-lists (point) -1 (car state)))
- (setq par (1+ (current-column)))))
+ (setq par (1+ (current-column))))
+ ((save-excursion (beginning-of-line)
+ (eq (following-char) ?#))
+ (throw 'nesting 'cpp)))
;; Loop until correct indent is found
(while t
(backward-sexp 1)
- (cond (;--Nest block outwards
+ (cond (;--Escape from case statements
+ (and (looking-at "[A-Za-z0-9]+[ \t]*:[^=]")
+ (not complete)
+ (save-excursion (skip-chars-backward " \t")
+ (bolp))
+ (= (save-excursion
+ (end-of-line) (backward-sexp) (point))
+ (point))
+ (> (save-excursion (goto-char oldpos)
+ (beginning-of-line)
+ (point))
+ (point)))
+ (throw 'nesting 'caseblock))
+ (;--Beginning of program
+ (looking-at pascal-progbeg-re)
+ (throw 'nesting 'progbeg))
+ (;--No known statements
+ (bobp)
+ (throw 'nesting 'progbeg))
+ (;--Nest block outwards
(looking-at pascal-beg-block-re)
(if (= nest 0)
(cond ((looking-at "case\\>")
((looking-at "record\\>")
(throw 'nesting 'declaration))
(t (throw 'nesting 'block)))
+ (if (and (looking-at "record\\>") (= nest 1))
+ (setq funccnt (1- funccnt)))
(setq nest (1- nest))))
(;--Nest block inwards
(looking-at pascal-end-block-re)
(if (and (looking-at "end\\s ")
elsed (not complete))
(throw 'nesting 'block))
+ (if (= nest 0)
+ (setq funccnt (1+ funccnt)))
(setq complete t
nest (1+ nest)))
(;--Defun (or parameter list)
- (looking-at pascal-defun-re)
+ (and (looking-at pascal-defun-re)
+ (progn (setq funccnt (1- funccnt)
+ did-func t)
+ (or (bolp) (< funccnt 0))))
+ ;; Prevent searching whole buffer
+ (if (and (bolp) (>= funccnt 0))
+ (throw 'nesting 'progbeg))
(if (= 0 par)
(throw 'nesting 'defun)
(setq par 0)
(throw 'nesting 'declaration)
(throw 'nesting 'paramlist)))))
(;--Declaration part
- (looking-at pascal-declaration-re)
+ (and (looking-at pascal-declaration-re)
+ (not did-func)
+ (= funccnt 0))
(if (save-excursion
(goto-char oldpos)
(forward-line -1)
(save-excursion (forward-sexp 1)
(= (following-char) ?\;))
(setq complete t))
- (;--No known statements
- (bobp)
- (throw 'nesting 'unknown))
)))))
+
;; Return type of block and indent level.
- (if (> par 0) ; Unclosed Parenthesis
+ (if (> par 0) ; Unclosed Parenthesis
(list 'contexp par)
(list type (pascal-indent-level))))))
(skip-chars-forward " \t")
(current-column)))
-(defun pascal-indent-comment (&optional arg)
- "Indent current line as comment.
-If optional arg is non-nil, just return the
-column number the line should be indented to."
- (let* ((stcol (save-excursion
- (re-search-backward "(\\*\\|{" nil t)
- (1+ (current-column)))))
- (if arg stcol
- (delete-horizontal-space)
- (indent-to stcol))))
+(defun pascal-indent-comment ()
+ "Return indent for current comment."
+ (save-excursion
+ (re-search-backward "\\((\\*\\)\\|{" nil t)
+ (if (match-beginning 1)
+ (1+ (current-column))
+ (current-column))))
(defun pascal-indent-case ()
"Indent within case statements."
- (skip-chars-forward ": \t")
- (let ((end (prog2
+ (let ((savepos (point-marker))
+ (end (prog2
(end-of-line)
(point-marker)
(re-search-backward "\\<case\\>" nil t)))
(beg (point)) oldpos
(ind 0))
;; Get right indent
- (while (< (point) (marker-position end))
- (if (re-search-forward
+ (while (< (point) end)
+ (if (re-search-forward
"^[ \t]*[^ \t,:]+[ \t]*\\(,[ \t]*[^ \t,:]+[ \t]*\\)*:"
(marker-position end) 'move)
(forward-char -1))
- (delete-horizontal-space)
- (if (> (current-column) ind)
- (setq ind (current-column)))
- (pascal-end-of-statement))
+ (if (< (point) end)
+ (progn
+ (delete-horizontal-space)
+ (if (> (current-column) ind)
+ (setq ind (current-column)))
+ (pascal-end-of-statement))))
(goto-char beg)
(setq oldpos (marker-position end))
;; Indent all case statements
- (while (< (point) (marker-position end))
+ (while (< (point) end)
(if (re-search-forward
- "^[ \t]*[^ \t,:]+[ \t]*\\(,[ \t]*[^ \t,:]+[ \t]*\\)*:"
+ "^[ \t]*[^][ \t,\\.:]+[ \t]*\\(,[ \t]*[^ \t,:]+[ \t]*\\)*:"
(marker-position end) 'move)
(forward-char -1))
(indent-to (1+ ind))
(insert " "))
(setq oldpos (point))
(pascal-end-of-statement))
- (goto-char oldpos)))
+ (goto-char savepos)))
(defun pascal-indent-paramlist (&optional arg)
"Indent current line in parameterlist.
(let* ((oldpos (point))
(stpos (progn (goto-char (scan-lists (point) -1 1)) (point)))
(stcol (1+ (current-column)))
- (edpos (progn (pascal-declaration-end)
+ (edpos (progn (pascal-declaration-end)
(search-backward ")" (pascal-get-beg-of-line) t)
(point)))
(usevar (re-search-backward "\\<var\\>" stpos t)))
(let ((pos (point-marker)))
(if (and (not (or arg start)) (not (pascal-declaration-beg)))
()
- (let ((lineup (if (or (looking-at "\\<var\\>\\|\\<record\\>") arg start)
+ (let ((lineup (if (or (looking-at "\\<var\\>\\|\\<record\\>") arg start)
":" "="))
(stpos (if start start
(forward-word 2) (backward-word 1) (point)))
(goto-char stpos)
;; Indent lines in record block
(if arg
- (while (<= (point) (marker-position edpos))
+ (while (<= (point) edpos)
(beginning-of-line)
(delete-horizontal-space)
(if (looking-at "end\\>")
;; Do lineup
(setq ind (pascal-get-lineup-indent stpos edpos lineup))
(goto-char stpos)
- (while (<= (point) (marker-position edpos))
+ (while (and (<= (point) edpos) (not (eobp)))
(if (search-forward lineup (pascal-get-end-of-line) 'move)
(forward-char -1))
(delete-horizontal-space)
;; If arg - move point
(if arg (forward-line -1)
- (goto-char (marker-position pos)))))
+ (goto-char pos))))
; "Return the indent level that will line up several lines within the region
;from b to e nicely. The lineup string is str."
(defun pascal-get-lineup-indent (b e str)
(save-excursion
(let ((ind 0)
- (reg (concat str "\\|\\(\\<record\\>\\)"))
- nest)
+ (reg (concat str "\\|\\(\\<record\\>\\)\\|" pascal-defun-re)))
(goto-char b)
;; Get rightmost position
(while (< (point) e)
- (setq nest 1)
- (if (re-search-forward reg (min e (pascal-get-end-of-line 2)) 'move)
- (progn
- ;; Skip record blocks
- (if (match-beginning 1)
- (pascal-declaration-end)
- (progn
- (goto-char (match-beginning 0))
- (skip-chars-backward " \t")
- (if (> (current-column) ind)
- (setq ind (current-column)))
- (goto-char (match-end 0)))))))
+ (and (re-search-forward reg (min e (pascal-get-end-of-line 2)) 'move)
+ (cond ((match-beginning 1)
+ ;; Skip record blocks
+ (pascal-declaration-end))
+ ((match-beginning 2)
+ ;; We have entered a new procedure. Exit.
+ (goto-char e))
+ (t
+ (goto-char (match-beginning 0))
+ (skip-chars-backward " \t")
+ (if (> (current-column) ind)
+ (setq ind (current-column)))
+ (goto-char (match-end 0))
+ (end-of-line)
+ ))))
;; In case no lineup was found
(if (> ind 0)
(1+ ind)
(end-of-line)
(skip-chars-backward " \t")
(1+ (current-column))))))
-
+
\f
;;;
;;; Completion
;;;
+(defvar pascal-str nil)
+(defvar pascal-all nil)
+(defvar pascal-pred nil)
+(defvar pascal-buffer-to-use nil)
+(defvar pascal-flag nil)
+
(defun pascal-string-diff (str1 str2)
"Return index of first letter where STR1 and STR2 differs."
(catch 'done
(defun pascal-func-completion (type)
;; Build regular expression for function/procedure names
- (if (string= str "")
- (setq str "[a-zA-Z_]"))
- (let ((str (concat (cond ((eq type 'procedure) "\\<\\(procedure\\)\\s +")
- ((eq type 'function) "\\<\\(function\\)\\s +")
- (t "\\<\\(function\\|procedure\\)\\s +"))
- "\\<\\(" str "[a-zA-Z0-9_.]*\\)\\>"))
+ (if (string= pascal-str "")
+ (setq pascal-str "[a-zA-Z_]"))
+ (let ((pascal-str (concat (cond
+ ((eq type 'procedure) "\\<\\(procedure\\)\\s +")
+ ((eq type 'function) "\\<\\(function\\)\\s +")
+ (t "\\<\\(function\\|procedure\\)\\s +"))
+ "\\<\\(" pascal-str "[a-zA-Z0-9_.]*\\)\\>"))
match)
-
+
(if (not (looking-at "\\<\\(function\\|procedure\\)\\>"))
(re-search-backward "\\<\\(function\\|procedure\\)\\>" nil t))
(forward-char 1)
;; Search through all reachable functions
(while (pascal-beg-of-defun)
- (if (re-search-forward str (pascal-get-end-of-line) t)
+ (if (re-search-forward pascal-str (pascal-get-end-of-line) t)
(progn (setq match (buffer-substring (match-beginning 2)
(match-end 2)))
- (if (or (null predicate)
- (funcall prdicate match))
- (setq all (cons match all)))))
+ (if (or (null pascal-pred)
+ (funcall pascal-pred match))
+ (setq pascal-all (cons match pascal-all)))))
(goto-char (match-beginning 0)))))
(defun pascal-get-completion-decl ()
;; Macro for searching through current declaration (var, type or const)
- ;; for matches of `str' and adding the occurence tp `all'
+ ;; for matches of `str' and adding the occurrence to `all'
(let ((end (save-excursion (pascal-declaration-end)
(point)))
match)
(while (< (point) end)
(if (re-search-forward "[:=]" (pascal-get-end-of-line) t)
;; Traverse current line
- (while (and (re-search-backward
- (concat "\\((\\|\\<\\(var\\|type\\|const\\)\\>\\)\\|"
+ (while (and (re-search-backward
+ (concat "\\((\\|\\<\\(var\\|type\\|const\\)\\>\\)\\|"
pascal-symbol-re)
(pascal-get-beg-of-line) t)
(not (match-end 1)))
(setq match (buffer-substring (match-beginning 0) (match-end 0)))
- (if (string-match (concat "\\<" str) match)
- (if (or (null predicate)
- (funcall predicate match))
- (setq all (cons match all))))))
+ (if (string-match (concat "\\<" pascal-str) match)
+ (if (or (null pascal-pred)
+ (funcall pascal-pred match))
+ (setq pascal-all (cons match pascal-all))))))
(if (re-search-forward "\\<record\\>" (pascal-get-end-of-line) t)
(pascal-declaration-end)
(forward-line 1)))))
(save-excursion
(if (> start (prog1 (save-excursion (pascal-end-of-defun)
(point))))
- () ; Declarations not reacable
+ () ; Declarations not reachable
(if (search-forward "(" (pascal-get-end-of-line) t)
;; Check parameterlist
(pascal-get-completion-decl))
(defun pascal-keyword-completion (keyword-list)
"Give list of all possible completions of keywords in KEYWORD-LIST."
- (mapcar '(lambda (s)
- (if (string-match (concat "\\<" str) s)
- (if (or (null predicate)
- (funcall predicate s))
- (setq all (cons s all)))))
+ (mapcar '(lambda (s)
+ (if (string-match (concat "\\<" pascal-str) s)
+ (if (or (null pascal-pred)
+ (funcall pascal-pred s))
+ (setq pascal-all (cons s pascal-all)))))
keyword-list))
;; Function passed to completing-read, try-completion or
;; is 'lambda, the function returns t if STR is an exact match, nil
;; otherwise.
-(defun pascal-completion (str predicate flag)
+(defun pascal-completion (pascal-str pascal-pred pascal-flag)
(save-excursion
- (let ((all nil))
+ (let ((pascal-all nil))
;; Set buffer to use for searching labels. This should be set
- ;; within functins which use pascal-completions
- (set-buffer buffer-to-use)
+ ;; within functions which use pascal-completions
+ (set-buffer pascal-buffer-to-use)
;; Determine what should be completed
(let ((state (car (pascal-calculate-indent))))
(save-excursion (pascal-var-completion))
(pascal-func-completion 'function)
(pascal-keyword-completion pascal-separator-keywords))))
-
+
;; Now we have built a list of all matches. Give response to caller
(pascal-completion-response))))
(defun pascal-completion-response ()
- (cond ((or (equal flag 'lambda) (null flag))
+ (cond ((or (equal pascal-flag 'lambda) (null pascal-flag))
;; This was not called by all-completions
- (if (null all)
+ (if (null pascal-all)
;; Return nil if there was no matching label
nil
;; Get longest string common in the labels
- (let* ((elm (cdr all))
- (match (car all))
+ (let* ((elm (cdr pascal-all))
+ (match (car pascal-all))
(min (length match))
- exact tmp)
- (if (string= match str)
+ tmp)
+ (if (string= match pascal-str)
;; Return t if first match was an exact match
(setq match t)
(while (not (null elm))
(setq min tmp)
(setq match (substring match 0 min))))
;; Terminate with match=t if this is an exact match
- (if (string= (car elm) str)
+ (if (string= (car elm) pascal-str)
(progn
(setq match t)
(setq elm nil))
(setq elm (cdr elm)))))
;; If this is a test just for exact match, return nil ot t
- (if (and (equal flag 'lambda) (not (equal match 't)))
+ (if (and (equal pascal-flag 'lambda) (not (equal match 't)))
nil
match))))
;; If flag is t, this was called by all-completions. Return
;; list of all possible completions
- (flag
- all)))
+ (pascal-flag
+ pascal-all)))
(defvar pascal-last-word-numb 0)
(defvar pascal-last-word-shown nil)
(interactive)
(let* ((b (save-excursion (skip-chars-backward "a-zA-Z0-9_") (point)))
(e (save-excursion (skip-chars-forward "a-zA-Z0-9_") (point)))
- (str (buffer-substring b e))
+ (pascal-str (buffer-substring b e))
;; The following variable is used in pascal-completion
- (buffer-to-use (current-buffer))
+ (pascal-buffer-to-use (current-buffer))
(allcomp (if (and pascal-toggle-completions
- (string= pascal-last-word-shown str))
+ (string= pascal-last-word-shown pascal-str))
pascal-last-completions
- (all-completions str 'pascal-completion)))
+ (all-completions pascal-str 'pascal-completion)))
(match (if pascal-toggle-completions
"" (try-completion
- str (mapcar '(lambda (elm) (cons elm 0)) allcomp)))))
+ pascal-str (mapcar '(lambda (elm)
+ (cons elm 0)) allcomp)))))
;; Delete old string
(delete-region b e)
(progn
;; Update entry number in list
(setq pascal-last-completions allcomp
- pascal-last-word-numb
+ pascal-last-word-numb
(if (>= pascal-last-word-numb (1- (length allcomp)))
0
(1+ pascal-last-word-numb)))
;; Display next match or same string if no match was found
(if (not (null allcomp))
(insert "" pascal-last-word-shown)
- (insert "" str)
+ (insert "" pascal-str)
(message "(No match)")))
- ;; The other form of completion does not necessarly do that.
+ ;; The other form of completion does not necessarily do that.
;; Insert match if found, or the original string if no match
(if (or (null match) (equal match 't))
- (progn (insert "" str)
+ (progn (insert "" pascal-str)
(message "(No match)"))
(insert "" match))
;; Give message about current status of completion
(if (not (null (cdr allcomp)))
(message "(Complete but not unique)")
(message "(Sole completion)")))
- ;; Display buffer if the current completion didn't help
+ ;; Display buffer if the current completion didn't help
;; on completing the label.
- ((and (not (null (cdr allcomp))) (= (length str) (length match)))
+ ((and (not (null (cdr allcomp))) (= (length pascal-str)
+ (length match)))
(with-output-to-temp-buffer "*Completions*"
(display-completion-list allcomp))
;; Wait for a keypress. Then delete *Completion* window
(interactive)
(let* ((b (save-excursion (skip-chars-backward "a-zA-Z0-9_") (point)))
(e (save-excursion (skip-chars-forward "a-zA-Z0-9_") (point)))
- (str (buffer-substring b e))
+ (pascal-str (buffer-substring b e))
;; The following variable is used in pascal-completion
- (buffer-to-use (current-buffer))
+ (pascal-buffer-to-use (current-buffer))
(allcomp (if (and pascal-toggle-completions
- (string= pascal-last-word-shown str))
+ (string= pascal-last-word-shown pascal-str))
pascal-last-completions
- (all-completions str 'pascal-completion))))
+ (all-completions pascal-str 'pascal-completion))))
;; Show possible completions in a temporary buffer.
(with-output-to-temp-buffer "*Completions*"
(display-completion-list allcomp))
;; is an exact match. If flag is 'lambda, the function returns t if
;; STR is an exact match, nil otherwise.
-(defun pascal-comp-defun (str predicate flag)
+(defun pascal-comp-defun (pascal-str pascal-pred pascal-flag)
(save-excursion
- (let ((all nil)
+ (let ((pascal-all nil)
match)
;; Set buffer to use for searching labels. This should be set
- ;; within functins which use pascal-completions
- (set-buffer buffer-to-use)
+ ;; within functions which use pascal-completions
+ (set-buffer pascal-buffer-to-use)
- (let ((str str))
+ (let ((pascal-str pascal-str))
;; Build regular expression for functions
- (if (string= str "")
- (setq str (pascal-build-defun-re "[a-zA-Z_]"))
- (setq str (pascal-build-defun-re str)))
+ (if (string= pascal-str "")
+ (setq pascal-str (pascal-build-defun-re "[a-zA-Z_]"))
+ (setq pascal-str (pascal-build-defun-re pascal-str)))
(goto-char (point-min))
-
+
;; Build a list of all possible completions
- (while (re-search-forward str nil t)
+ (while (re-search-forward pascal-str nil t)
(setq match (buffer-substring (match-beginning 2) (match-end 2)))
- (if (or (null predicate)
- (funcall predicate match))
- (setq all (cons match all)))))
+ (if (or (null pascal-pred)
+ (funcall pascal-pred match))
+ (setq pascal-all (cons match pascal-all)))))
;; Now we have built a list of all matches. Give response to caller
(pascal-completion-response))))
(interactive)
(let* ((default (pascal-get-default-symbol))
;; The following variable is used in pascal-comp-function
- (buffer-to-use (current-buffer))
+ (pascal-buffer-to-use (current-buffer))
(default (if (pascal-comp-defun default nil 'lambda)
default ""))
(label (if (not (string= default ""))
(setq pascal-outline-map (copy-keymap pascal-mode-map))
(setq pascal-outline-map (make-sparse-keymap))
(set-keymap-parent pascal-outline-map pascal-mode-map))
- (define-key pascal-outline-map "\e\C-a" 'pascal-outline-prev-defun)
- (define-key pascal-outline-map "\e\C-e" 'pascal-outline-next-defun)
+ (define-key pascal-outline-map "\M-\C-a" 'pascal-outline-prev-defun)
+ (define-key pascal-outline-map "\M-\C-e" 'pascal-outline-next-defun)
(define-key pascal-outline-map "\C-c\C-d" 'pascal-outline-goto-defun)
(define-key pascal-outline-map "\C-c\C-s" 'pascal-show-all)
(define-key pascal-outline-map "\C-c\C-h" 'pascal-hide-other-defuns))
(pascal-show-all)
(use-local-map pascal-mode-map))))
-(defun pascal-outline-change (b e flag)
+(defun pascal-outline-change (b e pascal-flag)
(let ((modp (buffer-modified-p)))
(unwind-protect
- (subst-char-in-region b e (if (= flag ?\n) ?\^M ?\n) flag)
+ (subst-char-in-region b e (if (= pascal-flag ?\n)
+ ?\^M ?\n) pascal-flag)
(set-buffer-modified-p modp))))
(defun pascal-show-all ()
(pascal-goto-defun)
(pascal-hide-other-defuns))
+(provide 'pascal)
+
;;; pascal.el ends here