# HG changeset patch # User Richard M. Stallman # Date 760126496 0 # Node ID f5d12c7f056e3a5187750df658d888cae15669ad # Parent b087b24b26ea4071850631e0d5c4df2d83dee835 Many doc fixes. (pascal-get-beg-of-line, pascal-get-end-of-line): Use defsubst. Renamed from get-... (pascal-within-string): Use defsubst. (delete-whitespaces): Function deleted; callers use delete-horizontal-space instead. (pascal-string-diff): Renamed from string-diff. Complete rewrite. Added an outline- minor-mode and completion. diff -r b087b24b26ea -r f5d12c7f056e lisp/progmodes/pascal.el --- a/lisp/progmodes/pascal.el Tue Feb 01 17:46:53 1994 +0000 +++ b/lisp/progmodes/pascal.el Tue Feb 01 18:14:56 1994 +0000 @@ -1,74 +1,65 @@ ;;; pascal.el - Major mode for editing pascal source in emacs. -;;; Copyright (C) 1993 Free Software Foundation, Inc. +;;; Copyright (C) 1993, 1994 Free Software Foundation, Inc. -;; Author: Espen Skoglund (espensk@stud.cs.uit.no) -;; Keywords: languages +;;; Author: Espen Skoglund (espensk@stud.cs.uit.no) +;;; Keywords: languages -;; This file is part of GNU Emacs. +;;; This file is part of GNU Emacs. -;; 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 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 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. +;;; 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. -;; 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, 675 Mass Ave, Cambridge, MA 02139, USA. +;;; 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. ;;; Commentary: -;;; If you want to customize the pascal mode in your startup file, you -;;; can add these lines to your .emacs file (and remove the ;s at the -;;; beginning of the line): -;;; -;;; ;;; Pascal-mode custumization. -;;; (autoload 'pascal-mode "pascal-mode" nil t) -;;; (setq auto-mode-alist (append (list (cons "\\.p$" 'pascal-mode) -;;; (cons "\\.pas$" 'pascal-mode)) -;;; auto-mode-alist)) -;;; (setq pascal-mode-hook '(lambda () -;;; ;; User specifications -;;; (setq pascal-tab-always-indent t -;;; pascal-auto-newline nil -;;; pascal-auto-endcomments t -;;; pascal-indent-level 3 -;;; pascal-continued-expr 1 -;;; pascal-label-offset -2 -;;; pascal-case-offset 2 -;;; pascal-typedecl-indent 10 -;;; pascal-vardecl-indent 20))) - ;;; USAGE ;;; ===== -;;; If you have modified your startup file as described above, emacs -;;; should enter pascal-mode when you load a pascal source into emacs. -;;; If not, you will have to start pascal-mode manually: -;;; M-x load-library pascal-mode -;;; M-x pascal-mode -;;; When you have entered pascal-mode, you may get more info by pressing + +;;; 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 d +;;; C-h f + +;;; 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-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-seperator-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. But if you DO -;;; find any bugd, you may submitt them to: espensk@stud.cs.uit.no +;;; 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. + +;;; Code: -;;; LCD Archive Entry: -;;; pascal-mode|Espen Skoglund|espensk@stud.cs.uit.no| -;;; Major mode for editing Pascal code| -;;; 14-Sep-93|$Revision: 1.3 $|~/modes/pascal-mode.el.Z| - -(defconst pascal-mode-version "1.3" - "Version of this pascal mode.") +(defconst pascal-mode-version "2.1a" + "Version of `pascal-mode.el'.") (defvar pascal-mode-abbrev-table nil "Abbrev table in use in Pascal-mode buffers.") @@ -76,37 +67,60 @@ (defvar pascal-mode-map () "Keymap used in Pascal mode.") -(if (null pascal-mode-map) - (setq pascal-mode-map (make-sparse-keymap))) - -(define-key pascal-mode-map ";" 'electric-pascal-semi) -(define-key pascal-mode-map "." 'electric-pascal-dot) -(define-key pascal-mode-map ":" 'electric-pascal-colon) -(define-key pascal-mode-map "=" 'electric-pascal-equal) -(define-key pascal-mode-map "\r" 'electric-pascal-terminate-line) -(define-key pascal-mode-map "\t" 'electric-pascal-tab) -(define-key pascal-mode-map "\177" 'backward-delete-char-untabify) -(define-key pascal-mode-map "\C-\M-a" 'pascal-backward-to-beginning-of-function) -(define-key pascal-mode-map "\C-\M-e" 'pascal-forward-to-end-of-function) -(define-key pascal-mode-map "\C-\M-h" 'pascal-mark-function) -(define-key pascal-mode-map "\C-c\C-b" 'pascal-insert-block) -(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 "\M-*" 'pascal-star-comment) - +(if pascal-mode-map + () + (setq pascal-mode-map (make-sparse-keymap)) + (define-key pascal-mode-map ";" 'electric-pascal-semi-or-dot) + (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 "\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 "\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-*" '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) ;;; 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-c\C-l" 'pascal-downcase-keywords) -;;;(define-key pascal-mode-map "\C-c\C-u" 'pascal-upcase-keywords) -;;;(define-key pascal-mode-map "\C-c\C-c" 'pascal-capitalize-keywords) +; (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-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" + "type" "until" "var" "while" "with" + ;; The following are not standard in pascal, but widely used. + "get" "put" "input" "output" "read" "readln" "reset" "rewrite" "write" + "writeln")) -(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" "type" "until" "var" "while" "with" -;; The following are not standard in pascal, but widely used. -"get" "put" "input" "output" "read" "readln" "reset" "rewrite" "write" -"writeln")) +;;; +;;; Regular expressions used to calculate indent, etc. +;;; +(defconst pascal-symbol-re "\\<[a-zA-Z_][a-zA-Z_0-9.]*\\>") +(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-defun-re "\\<\\(function\\|procedure\\|program\\)\\>") +(defconst pascal-sub-block-re "\\<\\(if\\|else\\|while\\)\\>") +(defconst pascal-noindent-re "\\<\\(begin\\|end\\|until\\)\\>") +(defconst pascal-nosemi-re "\\<\\(begin\\|repeat\\|then\\|do\\|else\\)\\>") +(defconst pascal-autoindent-lines-re + "\\<\\(label\\|var\\|type\\|const\\|until\\|end\\|begin\\|repeat\\|else\\)\\>") + +;;; Strings used to mark beginning and end of excluded text +(defconst pascal-exclude-str-start "{-----\\/----- EXCLUDED -----\\/-----") +(defconst pascal-exclude-str-end " -----/\\----- EXCLUDED -----/\\-----}") (defvar pascal-mode-syntax-table nil "Syntax table in use in Pascal-mode buffers.") @@ -114,82 +128,137 @@ (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 ?) ". 4" 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 ?{ "<" 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 ?> "." 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) + (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 ?> "." 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)) -(defconst pascal-indent-level 3 +(defvar pascal-indent-level 3 "*Indentation of Pascal statements with respect to containing block.") -(defconst pascal-continued-expr 1 - "*Indentation of line that is a continued expression.") -(defconst pascal-label-offset -1 - "*Offset of Pascal label lines, case statements and record lines. -This is relative to usual indentation.") -(defconst pascal-case-offset 2 - "*Indentation after case statements.") -(defconst pascal-vardecl-indent 15 - "*Indentation (from the beginning of line to `:' of the declaration.") -(defconst pascal-typedecl-indent 10 - "*Indentation (from the beginning of line to `=' of the declaration.") -(defconst pascal-auto-newline nil - "*Non-nil means automatically newline after semicolons and `end'.") -(defconst pascal-tab-always-indent t - "*Non-nil means TAB in Pascal mode should always reindent the current line. -It does so regardless of where in the line point is -when the TAB command is used.") -(defconst pascal-auto-endcomments t - "*Non-nil means make a comment { ... } after the end for a case or function. -The name of the function or case is put between the braces.") +(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-toggle-completions nil + "*Non-nil means that \\\\[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 + '("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.") +(defvar 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.) +The procedures and variables defined within the Pascal program +will be completed runtime and should not be added to this list.") +(defvar pascal-seperator-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.") + +;;; +;;; Macros +;;; + +(defsubst pascal-get-beg-of-line (&optional arg) + (save-excursion + (beginning-of-line arg) + (point))) + +(defsubst pascal-get-end-of-line (&optional arg) + (save-excursion + (end-of-line arg) + (point))) + +(defun pascal-declaration-end () + (let ((nest 1)) + (while (and (> nest 0) + (re-search-forward + "[:=]\\|\\(\\\\)\\|\\(\\\\)" + (save-excursion (end-of-line 2) (point)) t)) + (cond ((match-beginning 1) (setq nest (1+ nest))) + ((match-beginning 2) (setq nest (1- nest))))))) + +(defun pascal-declaration-beg () + (let ((nest 1)) + (while (and (> nest 0) + (re-search-backward "[:=]\\|\\<\\(type\\|var\\|label\\|const\\)\\>\\|\\(\\\\)\\|\\(\\\\)" (pascal-get-beg-of-line -0) t)) + (cond ((match-beginning 1) (setq nest 0)) + ((match-beginning 2) (setq nest (1- nest))) + ((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))))) + ;;;###autoload (defun pascal-mode () - "Major mode for editing Pascal code. -Tab indents for Pascal code. -Delete converts tabs to spaces as it moves back. -\\{pascal-mode-map} -Variables controlling indentation style: - pascal-tab-always-indent (default t) + "Major mode for editing Pascal code. \\ +TAB indents for Pascal code. Delete converts tabs to spaces as it moves back. + +\\[pascal-complete-word] completes the word around current point with respect \ +to position in code +\\[pascal-show-completions] shows all possible completions at this point. + +Other useful functions are: + +\\[pascal-mark-defun]\t- Mark function. +\\[pascal-insert-block]\t- insert begin ... end; +\\[pascal-star-comment]\t- insert (* ... *) +\\[pascal-comment-area]\t- Put marked area in a comment, fixing nested comments. +\\[pascal-uncomment-area]\t- Uncomment an area commented with \ +\\[pascal-comment-area]. +\\[pascal-beg-of-defun]\t- Move to beginning of current function. +\\[pascal-end-of-defun]\t- Move to end of current function. +\\[pascal-goto-defun]\t- Goto function prompted for in the minibuffer. +\\[pascal-outline]\t- Enter pascal-outline-mode (see also pascal-outline). + +Variables controlling indentation/edit style: + + pascal-indent-level (default 3) + Indentation of Pascal statements with respect to containing block. + 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-tab-always-indent (defualt 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-newline (default nil) - Non-nil means automatically newline after semicolons and the punctation - mark after an end. - pascal-auto-endcomments (default t) - Non-nil means automatically set name of function or `case' in braces after - after the `end' if this end ends a function or a case block. - pascal-indent-level (default 3) - Indentation of Pascal statements within surrounding block. - pascal-continued-expr (default 1) - Indentation of a line that is a continued expression. - pascal-typedecl-indent (default 10) - Indentation to the `=' in type declarations. (Or constant declarations.) - pascal-vardecl-indent (default 20) - Indentation to the `:' in var declarations. - pascal-label-offset (default -1) - Extra indentation for line that is a label, case statement or part of - a record block. - pascal-case-offset (default 2) - Extra indent to the `:' in case statements. + 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. -The only auto indention this mode doesn't fully support is if there is a -case within a type declaration. However, this is seldom used. - -When typing text, you should not worry about to get right indentions, they -will be set when you hit return. The mode will also automatically delete the -whitespaces between `*' and `)' when ending a starcomment. +See also the user variables pascal-type-keywords, pascal-start-keywords and +pascal-seperator-keywords. Turning on Pascal mode calls the value of the variable pascal-mode-hook with no args, if that value is non-nil." @@ -202,26 +271,30 @@ (set-syntax-table pascal-mode-syntax-table) (make-local-variable 'indent-line-function) (setq indent-line-function 'pascal-indent-line) - (setq comment-indent-hook 'pascal-indent-within-comment) + (setq comment-indent-function 'pascal-indent-comment) (make-local-variable 'parse-sexp-ignore-comments) (setq parse-sexp-ignore-comments t) (make-local-variable 'case-fold-search) (setq case-fold-search t) (run-hooks 'pascal-mode-hook)) + + ;;; ;;; Electric functions ;;; - (defun electric-pascal-terminate-line () "Terminate line and indent next line." (interactive) + ;; First, check if current line should be indented (save-excursion (beginning-of-line) (skip-chars-forward " \t") - (if (looking-at "until\\b\\|end\\(\\b\\|;\\|\\.\\)\\|begin\\b\\|repeat\\b\\|else\\b") + (if (looking-at pascal-autoindent-lines-re) (pascal-indent-line))) + (delete-horizontal-space) ; Removes trailing whitespaces (newline) + ;; Indent next line (pascal-indent-line) ;; Maybe we should set some endcomments (if pascal-auto-endcomments @@ -231,33 +304,22 @@ (save-excursion (forward-line -1) (skip-chars-forward " \t") - (cond ((looking-at "\\*[ \t]*)") + (cond ((looking-at "\\*[ \t]+)") ;; Delete region between `*' and `)' if there is only whitespaces. (forward-char 1) - (pascal-delete-whitespaces)) + (delete-horizontal-space)) ((and (looking-at "(\\*\\|\\*[^)]") (not (save-excursion (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 setstar - (progn - (insert "*") - (pascal-indent-command)) - (pascal-indent-line)))) + (if (null setstar) + (pascal-indent-line) + (insert "* ")))) -(defun electric-pascal-semi () - "Insert ; character and correct this line's indention." - (interactive) - (insert last-command-char) - (save-excursion - (beginning-of-line) - (pascal-indent-line)) - (if pascal-auto-newline - (electric-pascal-terminate-line))) -(defun electric-pascal-dot () - "Insert a period and correct this line's indention." +(defun electric-pascal-semi-or-dot () + "Insert `;' or `.' character and reindent the line." (interactive) (insert last-command-char) (save-excursion @@ -267,48 +329,47 @@ (electric-pascal-terminate-line))) (defun electric-pascal-colon () - "Insert : and do all indentions except line indent on this line." + "Insert `:' and do all indentions except line indent on this line." (interactive) (insert last-command-char) - ;; Do nothing of within string. - (if (not (pascal-within-string)) - (progn - (if (save-excursion - (backward-char 2) - (looking-at "[0-9]")) - (save-excursion - (beginning-of-line) - (pascal-indent-line))) - (let ((pascal-tab-always-indent nil)) - (pascal-indent-command))))) - + ;; Do nothing if within string. + (if (pascal-within-string) + () + (save-excursion + (beginning-of-line) + (pascal-indent-line)) + (let ((pascal-tab-always-indent nil)) + (pascal-indent-command)))) + (defun electric-pascal-equal () - "Insert = and do indention if within type declaration." + "Insert `=', and do indention if within type declaration." (interactive) (insert last-command-char) - (if (eq (nth 1 (pascal-calculate-indent t)) 'decl) + (if (eq (car (pascal-calculate-indent)) 'declaration) (let ((pascal-tab-always-indent nil)) (pascal-indent-command)))) (defun electric-pascal-tab () - "Function called when tab is pressed." + "Function called when TAB is pressed in Pascal mode." (interactive) ;; Do nothing if within a string. - (if (not (pascal-within-string)) - ;; If pascal-tab-always-indent is set then indent the beginning of - ;; the line. - (progn - (if pascal-tab-always-indent - (save-excursion - (beginning-of-line) - (pascal-indent-line))) - (pascal-indent-command)))) + (if (pascal-within-string) + (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")) + (pascal-indent-command))) + + ;;; ;;; Interactive functions ;;; (defun pascal-insert-block () - "Insert begin ... end; block in the code with right indents." + "Insert Pascal begin ... end; block in the code with right indentation." (interactive) (pascal-indent-line) (insert "begin") @@ -320,35 +381,38 @@ (pascal-indent-line))) (defun pascal-star-comment () - "Insert star comment in the code." + "Insert Pascal star comment at point." (interactive) (pascal-indent-line) (insert "(*") (electric-pascal-terminate-line) (save-excursion (electric-pascal-terminate-line) - (pascal-delete-whitespaces) - (insert ")"))) + (delete-horizontal-space) + (insert ")")) + (insert " ")) -(defun pascal-mark-function () +(defun pascal-mark-defun () "Mark the current pascal function (or procedure). -Put the mark at the end of the function, and point at the beginning." +This puts the mark at the end, and point at the beginning." (interactive) (push-mark (point)) - (pascal-forward-to-end-of-function) + (pascal-end-of-defun) (push-mark (point)) - (pascal-backward-to-beginning-of-function) - (zmacs-activate-region)) + (pascal-beg-of-defun) + (if (fboundp 'zmacs-activate-region) + (zmacs-activate-region))) (defun pascal-comment-area (start end) - "Put the current region in a comment. -The comments that are in this area are -be changed so that `*)' becomes `!(*' and `}' becomes `!{'. These will -however be turned back to normal when the area is uncommented by pressing -\\[pascal-uncomment-area]. -The commented area starts with: `{---\\/---EXCLUDED---\\/---' , and ends with: -` ---/\\---EXCLUDED---/\\---}'. If these texts are changed, uncomment-area -will not be able to recognize them." + "Put the region into a Pascal comment. +The comments that are in this area are \"deformed\": +`*)' becomes `!(*' and `}' becomes `!{'. +These deformed comments are returned to normal if you use +\\[pascal-uncomment-area] to undo the commenting. + +The commented area starts with `pascal-exclude-str-start', and ends with +`pascal-include-str-end'. But if you change these variables, +\\[pascal-uncomment-area] won't recognize the comments." (interactive "r") (save-excursion ;; Insert start and endcomments @@ -357,12 +421,12 @@ (not (save-excursion (skip-chars-backward " \t") (bolp)))) (forward-line 1) (beginning-of-line)) - (insert " ---/\\---EXCLUDED---/\\---}") + (insert pascal-exclude-str-end) (setq end (point)) (newline) (goto-char start) (beginning-of-line) - (insert "{---\\/---EXCLUDED---\\/--- ") + (insert pascal-exclude-str-start) (newline) ;; Replace end-comments within commented area (goto-char end) @@ -374,9 +438,8 @@ (replace-match "!{" t t))))) (defun pascal-uncomment-area () - "Uncomment a commented area. -Change all deformed comments in this area back to normal. -This function does nothing if the pointer is not in a commented + "Uncomment a commented area; change deformed comments back to normal. +This command does nothing if the pointer is not in a commented area. See also `pascal-comment-area'." (interactive) (save-excursion @@ -384,9 +447,9 @@ (end (point))) ;; Find the boundaries of the comment (save-excursion - (setq start (progn (search-backward "{---\\/---EXCLUDED---\\/--" nil t) + (setq start (progn (search-backward pascal-exclude-str-start nil t) (point))) - (setq end (progn (search-forward "---/\\---EXCLUDED---/\\---}" nil t) + (setq end (progn (search-forward pascal-exclude-str-end nil t) (point)))) ;; Check if we're really inside a comment (if (or (equal start (point)) (<= end (point))) @@ -412,569 +475,923 @@ (end-of-line) (delete-region pos (1+ (point))))))))) -(defun pascal-backward-to-beginning-of-function () - "Move backwards to the beginning of this function or procedure." +(defun pascal-beg-of-defun () + "Move backward to the beginning of the current function or procedure." (interactive) - ;; Check if this is a - (if (save-excursion - (re-search-backward "\\") (>= max 0)) + (setq func (1- func))) + (setq nest (1+ nest) + max (max nest max))) + ((match-end 2) ; end|until + (if (and (= nest max) (>= max 0)) + (setq func (1+ func))) + (setq nest (1- nest))) + ((match-end 3) ; function|procedure + (if (= 0 func) + (throw 'found t) + (setq func (1- func))))))) + nil)) -(defun pascal-forward-to-end-of-function () - "Moves the point to the end of the function." +(defun pascal-end-of-defun () + "Move forward to the end of the current function or procedure." (interactive) - (if (not (looking-at "function\\b\\|procedure\\b")) - (pascal-backward-to-beginning-of-function)) - (if (bobp) - (end-of-buffer) - (progn - (let ((nest-depth 0) - (func-depth 1)) - (while (not (or (and (zerop nest-depth) (zerop func-depth)) (eobp))) - (forward-sexp 2) - (if (not (eobp)) - (progn - (backward-sexp 1) ; Move to the beginning of the next sexp - (cond ((looking-at "begin\\b\\|case\\b\\|record\\b") - (setq nest-depth (1+ nest-depth))) - ((looking-at "end\\(\\b\\|;\\|\\.\\)") - (setq nest-depth (1- nest-depth)) - (if (zerop nest-depth) - (setq func-depth (1- func-depth)))) - ((looking-at "function\\b\\|procedure\\b") - (setq func-depth (1+ func-depth))))))) - (end-of-line))))) + (if (looking-at "\\s ") + (forward-sexp 1)) + (if (not (looking-at pascal-defun-re)) + (pascal-beg-of-defun)) + (forward-char 1) + (let ((nest 0) (func 1) + (reg (concat pascal-beg-block-re "\\|" + pascal-end-block-re "\\|" + pascal-defun-re))) + (while (and (/= func 0) + (re-search-forward reg nil 'move)) + (cond ((let ((state (save-excursion + (parse-partial-sexp (point-min) (point))))) + (or (nth 3 state) (nth 4 state))) ; Inside string or comment + ()) + ((match-end 1) + (setq nest (1+ nest)) + (if (save-excursion + (goto-char (match-beginning 0)) + (looking-at "\\")) + (setq func (1+ func)))) + ((match-end 2) + (setq nest (1- nest)) + (if (= nest 0) + (setq func (1- func)))) + ((match-end 3) + (setq func (1+ func)))))) + (forward-line 1)) (defun pascal-downcase-keywords () - "Makes all Pascal keywords in the buffer lowercase." + "Downcase all Pascal keywords in the buffer." (interactive) (pascal-change-keywords 'downcase-word)) (defun pascal-upcase-keywords () - "Makes all Pascal keywords in the buffer uppercase." + "Upcase all Pascal keywords in the buffer." (interactive) (pascal-change-keywords 'upcase-word)) (defun pascal-capitalize-keywords () - "Makes all Pascal keywords in the buffer uppercase." + "Capitalize all Pascal keywords in the buffer." (interactive) (pascal-change-keywords 'capitalize-word)) +;; Change the keywords according to argument. (defun pascal-change-keywords (change-word) - "Change the keywords according to argument." (save-excursion - (beginning-of-buffer) - (while (re-search-forward (mapconcat - 'downcase pascal-keywords "\\>\\|\\<") nil t) - (funcall change-word -1)))) + (let ((keyword-re (concat "\\<\\(" + (mapconcat 'identity pascal-keywords "\\|") + "\\)\\>"))) + (goto-char (point-min)) + (while (re-search-forward keyword-re nil t) + (funcall change-word -1))))) + + ;;; ;;; Other functions ;;; -(defun pascal-delete-whitespaces () - "Deletes the whitespaces around the current point." - (interactive) - (let ((pos (progn (skip-chars-backward " \t") (point)))) - (skip-chars-forward " \t") - (delete-region pos (point)))) - -(defun pascal-get-beg-of-line () - (save-excursion - (beginning-of-line) - (point))) - -(defun pascal-get-end-of-line () - (save-excursion - (end-of-line) - (point))) - -(defun pascal-within-string () - "Return t if within string; nil otherwise." - (and (save-excursion (search-backward "\"" (pascal-get-beg-of-line) t)) - (save-excursion (not (search-backward "\"" (pascal-get-beg-of-line) t 2))))) - -(defun pascal-check-if-within-comment () - "If within a comment, return the correct indent. Return nil otherwise." - (let ((comstart (point)) - (comend (point))) - (save-excursion - (if (re-search-backward "(\\*\\|{" nil t) - (setq comstart (point)) - (setq comstart 0))) - (save-excursion - (if (re-search-backward "\\*)\\|}" nil t) - (setq comend (point)) - (setq comend 0))) - (if (< comend comstart) - (save-excursion - (goto-char comstart) - ;; Add 1 to indent if this is a starcomment - (if (looking-at "(\\*") - (1+ (current-column)) - (current-column))) - nil))) (defun pascal-set-auto-comments () - "Put { case } or { FUNNAME } on this line if appropriate after `end'." + "Insert `{ case }' or `{ NAME }' on this line if appropriate. +Insert `{ case }' if there is an `end' on the line which +ends a case block. Insert `{ NAME }' if there is an `end' +on the line which ends a function or procedure named NAME." (save-excursion (forward-line -1) (skip-chars-forward " \t") - (if (and (looking-at "end\\(\>\\|;\\)") + (if (and (looking-at "\\" - nil t) - (cond ((looking-at "var\\b") - (pascal-declindent-middle-of-line - ":" posii pascal-vardecl-indent)) - ((looking-at "type\\b\\|const\\b") - (pascal-declindent-middle-of-line - "=" posii pascal-typedecl-indent))))) - ((eq (nth 1 indent) 'function) - ;; Indent for parameterlist - ;; Done twice in case something has changed - (pascal-indent-parameter-list) - (pascal-indent-parameter-list)))) - ;; Go to the end of a line if rest of line contains only whitespaces - (if (save-excursion (skip-chars-forward " \t") (eolp)) - (end-of-line)))) + "Indent for special part of code." + (let* ((indent-str (pascal-calculate-indent)) + (type (car indent-str)) + (ind (car (cdr indent-str)))) + (cond ((eq type 'paramlist) + (pascal-indent-paramlist) + (pascal-indent-paramlist)) + ((eq type 'declaration) + (pascal-indent-declaration)) + ((and (eq type 'case) (not (looking-at "^[ \t]*$"))) + (pascal-indent-case))) + (if (looking-at "[ \t]+$") + (skip-chars-forward " \t")))) (defun pascal-indent-line () - "Indent current line as Pascal code." - (let ((indent (list 0 nil)) - (comindent 0) - beg (point)) - (save-excursion - (beginning-of-line) - (setq indent (pascal-calculate-indent))) - ;; If we are inside a comment, do special indent. - (if (setq comindent (pascal-check-if-within-comment)) - (pascal-indent-within-comment comindent) - ;; Skip the rest if we're not standing on the beginning of a line. - (if (save-excursion (skip-chars-backward " \t") (bolp)) - (progn - (beginning-of-line) - (pascal-delete-whitespaces) - ;; When to skip the ekstra indent: - ;; If we are standing at end or until. - ;; If we are in an if statement and standing at else, - ;; begin or repeat - ;; If we are in a with, while or for statement and standing - ;; at begin or end. - (cond ((or (or (looking-at "end\\b\\|until\\b") - (not (nth 1 indent))) - (and (eq (nth 1 indent) 'if) - (looking-at "begin\\b\\|\\repeat\\b\\|else\\b")) - (and (eq (nth 1 indent) 'whilewith) - (looking-at "begin\\b\\|\\repeat\\b"))) - (indent-to (car indent))) - ;; Continued expression - ((eq (nth 1 indent) 'contexp) - (indent-to (+ (car indent) pascal-continued-expr))) - ;; If this is a part of a case or record block, - ;; then modify the indent level. - ((or (eq (nth 1 indent) 'case) - (eq (nth 1 indent) 'record)) - (indent-to (+ (car indent) pascal-indent-level - pascal-label-offset))) - ;; If this is a label - don't indent. - ((looking-at "[0-9]*:") - (skip-chars-forward "0-9:") - (pascal-delete-whitespaces) - (indent-to (+ (car indent) pascal-indent-level))) - ;; If this is insde a parameter list, do special indent - ((eq (nth 1 indent) 'function) - (pascal-indent-parameter-list)) - ;; All other indents are set normaly. - (t - (indent-to (+ (car indent) pascal-indent-level))))))))) - -(defun pascal-calculate-indent (&optional arg) - "Search backward in code to find the right indent level. -Return a list containing: -1. Indent level -2. The indent keyword (begin, case etc.), or nil if backtracking failed. -If arg is non-nil, we do not search for continued expressions." - (let ((pascal-nest-depth 1) - (oldpos (save-excursion (forward-line -1) (end-of-line) (point))) - (samepos (point)) (if-is-set t) - (return-struct (list 0 nil)) (pos 0) - (contexpr nil) (after-contexpr (not arg)) - (case-fold-search t)) - (save-excursion - (while (and (not (zerop pascal-nest-depth)) - (not (bobp))) - (progn - (backward-sexp 1) - (if (save-excursion - (setq pos (point)) - (end-of-line) - (search-backward ";" pos t)) - (setq if-is-set nil - after-contexpr nil)) - (if (looking-at "then\\b\\|end\\b\\|else\\b\\|do\\b") - (setq after-contexpr nil)) + "Indent current line as a Pascal statement." + (let* ((indent-str (pascal-calculate-indent)) + (type (car indent-str)) + (ind (car (cdr indent-str)))) + (if (looking-at "^[0-9a-zA-Z]+[ \t]*:[^=]") + (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 ((looking-at "begin\\b\\|case\\b\\|record\\b\\|repeat\\b") - (setq pascal-nest-depth (1- pascal-nest-depth))) - ;; - ;; END | UNTIL - ((looking-at "end\\(\\b\\|;\\|\\.\\)\\|until\\b") - (setq if-is-set nil) - (if after-contexpr - (setq pascal-nest-depth 0 - contexpr t) - (setq pascal-nest-depth (1+ pascal-nest-depth)))) - ;; - ;; IF | ELSE | WITH | WHILE | FOR - ;; LABEL | CONST | TYPE | FUNCTION | PROCEDURE - ((or (and (looking-at "if\\b\\|else\\b\\|with\\b\\|while\\b\\|for\\b") - if-is-set) - (looking-at "label\\b\\|const\\b\\|type\\b\\|function\\b\\|procedure\\b")) - (setq pascal-nest-depth 0)) - ;; - ;; VAR - ((looking-at "var\\b") - ;; A `var' can be in a declaration part or parameter part - (let ((stpos 0) (edpos 0)) - (save-excursion - (if (not (re-search-backward - "\\<\\(function\\|procedure\\)\\>" nil t)) - (beginning-of-buffer)) - (setq stpos (save-excursion - (search-forward "(" nil t) (point))) - (setq edpos (save-excursion - (search-forward ")" nil t) (point)))) - (cond ((or (= stpos edpos) (< samepos stpos) - (and (> (point) edpos) (> edpos stpos))) - ;; This is really a declaration block!! - nil) - ((and (>= samepos stpos) (or (< samepos edpos) - (> stpos edpos))) - ;; Hmm... part of a parameter - (re-search-backward - "\\<\\(function\\|procedure\\)\\>" nil t)) - (t - ;; This is just after a parameter declaration - (forward-char 1))) - ;; We'll quit anyway - (setq pascal-nest-depth 0))) - ;; - ;; CONTINUED EXPRESSIONS - (after-contexpr - (save-excursion - ;; First, we have to be at the begining of a line - (if (and (progn (skip-chars-backward " \t") (bolp)) - ;; Blank lines don't count - (not (progn (skip-chars-forward " \t") (eolp))) - ;; But nonblank without ';' do - (not (search-forward ";" (pascal-get-end-of-line) t))) - (save-excursion - (forward-line -1) - (end-of-line) - (backward-sexp 1) - (if (or (looking-at "\\(do\\|then\\|of\\\|begin\\|repeat\\|else\\)\\>") - (progn - (skip-chars-forward "^; " (pascal-get-end-of-line)) - (equal (char-to-string (following-char)) - ";"))) - (setq pascal-nest-depth 0)) - (setq contexpr t))))) - ))) - (cond (contexpr - (setq return-struct (list (pascal-lstart-col) 'contexp))) - ((looking-at "begin\\b") - (setq return-struct (list (pascal-lstart-col) 'begin))) - ((looking-at "else\\b") - (setq return-struct (list (save-excursion - (re-search-backward "if\\b" nil t) - (pascal-lstart-col)) 'if)) - ;; Indent line in case this is a multiple if - (beginning-of-line) - (pascal-delete-whitespaces) - (indent-to (car return-struct))) - ((looking-at "if\\b") - (if (save-excursion - (narrow-to-region (pascal-get-beg-of-line) (point)) - (backward-sexp 1) - (widen) - (looking-at "else\\b")) - ;; Indent line if this is a multiple if - (progn - (beginning-of-line) - (pascal-delete-whitespaces) - (indent-to (save-excursion - (re-search-backward "if\\b" nil t) - (pascal-lstart-col))))) - ;; This could be a continued expression - (if (and after-contexpr - (not (save-excursion (re-search-forward - "then\\b" (pascal-get-end-of-line) t)))) - (setq return-struct (list (pascal-lstart-col) 'contexp)) - (setq return-struct (list (pascal-lstart-col) 'if)))) - ((looking-at "repeat\\b") - (setq return-struct (list (pascal-lstart-col) 'repeat))) - ((looking-at "case\\b") - (setq return-struct (list (current-column) 'case))) - ((looking-at "record\\b") - (setq return-struct (list (current-column) 'record))) - ((looking-at "while\\b\\|with\\b\\|for\\b") - ;; This could ba a continued expression - (if (and after-contexpr - (not (save-excursion (re-search-forward - "do\\b" (pascal-get-end-of-line) t)))) - (setq return-struct (list (pascal-lstart-col) 'contexp)) - (setq return-struct (list (current-column) 'whilewith)))) - ((looking-at "procedure\\b\\|function\\b") - ;; Make sure that this is a function with parameters, and - ;; that we are actually standing inside the paranthesis. - (let ((spos (save-excursion - (search-forward "(" samepos t) (point))) - (epos (save-excursion - (search-forward ")" samepos t) (point)))) - (if (and (>= samepos spos) (or (< samepos epos) - (> spos epos))) - (setq return-struct (list 0 'function)) - (setq return-struct (list 0 nil))))) - ((looking-at "var\\b\\|label\\b\\|const\\b\\|type\\b") - ;; Are we really in the declaration part?(Check for blank lines) - (if (< oldpos (point)) - (setq return-struct (list 0 'decl)) - (if (save-excursion - (not (re-search-forward "^[ \t]*$" oldpos t))) - (setq return-struct (list 0 'decl)) - (setq return-struct (list 0 nil))))) - (t - (setq return-struct (list 0 nil)))) - return-struct))) +(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)) + (state (save-excursion (parse-partial-sexp (point-min) (point)))) + (nest 0) (par 0) (complete nil) (blocked nil) + (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))))) + ;; Loop until correct indent is found + (while t + (backward-sexp 1) + (cond (;--Nest block outwards + (looking-at pascal-beg-block-re) + (if (= nest 0) + (cond ((looking-at "case\\>") + (setq blocked t) + (throw 'nesting 'case)) + ((looking-at "record\\>") + (throw 'nesting 'declaration)) + (t (setq blocked t) + (throw 'nesting 'block))) + (setq nest (1- nest)))) + (;--Nest block inwards + (looking-at pascal-end-block-re) + (setq complete t + nest (1+ nest))) + (;--Defun (or parameter list) + (looking-at pascal-defun-re) + (if (= 0 par) + (throw 'nesting 'defun) + (setq par 0) + (let ((n 0)) + (while (re-search-forward + "\\(\\\\)\\|\\" + oldpos t) + (if (match-end 1) + (setq n (1+ n)) (setq n (1- n)))) + (if (> n 0) + (throw 'nesting 'declaration) + (throw 'nesting 'paramlist))))) + (;--Declaration part + (looking-at pascal-declaration-re) + (if (or blocked + (save-excursion + (goto-char oldpos) + (forward-line -1) + (looking-at "^[ \t]*$"))) + (throw 'nesting 'unknown) + (throw 'nesting 'declaration))) + (;--If, else or while statement + (and (not complete) + (looking-at pascal-sub-block-re)) + (throw 'nesting 'block)) + (;--Found complete statement + (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 + (list 'contexp par) + (list type (pascal-indent-level)))))) -(defun pascal-lstart-col () - "Return the column of the beginning of the first command on the line." +(defun pascal-indent-level () + "Return the indent-level the current statement has. +Do not count labels, case-statements or records." (save-excursion (beginning-of-line) - (skip-chars-forward ":0-9") + (if (looking-at "[ \t]*[0-9a-zA-Z]+[ \t]*:[^=]") + (search-forward ":" nil t) + (if (looking-at ".*=[ \t]*record\\>") + (search-forward "=" nil t))) (skip-chars-forward " \t") (current-column))) -(defun pascal-indent-parameter-list () - "Indent this line as part of a parameter list in a function." - (let ((indents (pascal-get-highest-indents-in-parameterlist)) - (pos 0)) - (if (not (progn (beginning-of-line) - (search-forward "(" (pascal-get-end-of-line) t))) - (progn (beginning-of-line) - (skip-chars-forward " \t"))) - ;; Indent region in front of var - (skip-chars-forward " \t") - (pascal-delete-whitespaces) - (indent-to (nth 0 indents)) - (if (looking-at "var\\b") - (forward-char 3)) - ;; Indent parameternames - (pascal-delete-whitespaces) - (indent-to (nth 1 indents)) - (if (not (save-excursion (skip-chars-forward " \t") (eolp))) - (progn - ;; Indent colon - (if (search-forward ":" (pascal-get-end-of-line) t) - (backward-char 1) - (end-of-line)) - (pascal-delete-whitespaces) - (indent-to (nth 2 indents)) - ;; Indent after colon - (if (equal (following-char) ?:) - (progn - (forward-char 1) - (pascal-delete-whitespaces) - (indent-to (+ 2 (nth 2 indents))))))))) +(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-case () + "Indent within case statements." + (skip-chars-forward ": \t") + (let ((end (prog1 (point-marker) + (re-search-backward "\\" nil t))) + (beg (point)) + (ind 0)) + ;; Get right indent + (while (< (point) (marker-position end)) + (if (re-search-forward "^[ \t]*\\([^ \t]+\\)[ \t]*:" + (marker-position end) 'move) + (goto-char (match-end 1))) + (delete-horizontal-space) + (if (> (current-column) ind) + (setq ind (current-column))) + (beginning-of-line 2)) + (goto-char beg) + ;; Indent all case statements + (while (< (point) (marker-position end)) + (if (re-search-forward "^[ \t]*[^ \t]+[ \t]*:" + (marker-position end) 'move) + (forward-char -1)) + (indent-to (1+ ind)) + (if (/= (following-char) ?:) + () + (forward-char 1) + (delete-horizontal-space) + (insert " "))))) + +(defun pascal-indent-paramlist (&optional arg) + "Indent current line in parameterlist. +If optional arg is non-nil, just return the +indent of the current line in parameterlist." + (save-excursion + (let* ((oldpos (point)) + (stpos (progn (goto-char (scan-lists (point) -1 1)) (point))) + (stcol (1+ (current-column))) + (edpos (progn (pascal-declaration-end) + (search-backward ")" (pascal-get-beg-of-line) t) + (point))) + (usevar (re-search-backward "\\" stpos t))) + (if arg (progn + ;; If arg, just return indent + (goto-char oldpos) + (beginning-of-line) + (if (or (not usevar) (looking-at "[ \t]*var\\>")) + stcol (+ 4 stcol))) + (goto-char stpos) + (forward-char 1) + (delete-horizontal-space) + (if (and usevar (not (looking-at "var\\>"))) + (indent-to (+ 4 stcol))) + (pascal-indent-declaration nil stpos edpos))))) + +(defun pascal-indent-declaration (&optional arg start end) + "Indent current lines as declaration, lining up the `:'s or `='s." + (let ((pos (point-marker))) + (if (and (not (or arg start)) (not (pascal-declaration-beg))) + () + (let ((lineup (if (or (looking-at "\\\\|\\") arg start) + ":" "=")) + (stpos (if start start + (forward-word 2) (backward-word 1) (point))) + (edpos (set-marker (make-marker) + (if end end + (max (progn (pascal-declaration-end) + (point)) + pos)))) + ind) + + (goto-char stpos) + ;; Indent lines in record block + (if arg + (while (<= (point) (marker-position edpos)) + (beginning-of-line) + (delete-horizontal-space) + (if (looking-at "end\\>") + (indent-to arg) + (indent-to (+ arg pascal-indent-level))) + (forward-line 1))) + + ;; Do lineup + (setq ind (pascal-get-lineup-indent stpos edpos lineup)) + (goto-char stpos) + (while (<= (point) (marker-position edpos)) + (if (search-forward lineup (pascal-get-end-of-line) 'move) + (forward-char -1)) + (delete-horizontal-space) + (indent-to ind) + (if (not (looking-at lineup)) + (forward-line 1) ; No more indent if there is no : or = + (forward-char 1) + (delete-horizontal-space) + (insert " ") + ;; Indent record block + (if (looking-at "record\\>") + (pascal-indent-declaration (current-column))) + (forward-line 1))))) + + ;; If arg - move point + (if arg (forward-line -1) + (goto-char (marker-position 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 "\\|\\(\\\\)")) + nest) + (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) + ;; Skip record blocks + (if (match-beginning 1) + (pascal-declaration-end) + (save-excursion + (goto-char (match-beginning 0)) + (skip-chars-backward " \t") + (if (> (current-column) ind) + (setq ind (current-column))))))) + ;; In case no lineup was found + (if (> ind 0) + (1+ ind) + ;; No lineup-string found + (goto-char b) + (end-of-line) + (skip-chars-backward " \t") + (1+ (current-column)))))) + + -;; Get the indents to use in a parameterlist. -;; Returns: -;; 1. Indent to the beginning of the line. -;; 2. Indent to the beginning of the parameter names. -;; 3. Indent to the right colon position." -(defun pascal-get-highest-indents-in-parameterlist () +;;; +;;; Completion +;;; +(defun pascal-string-diff (str1 str2) + "Return index of first letter where STR1 and STR2 differs." + (catch 'done + (let ((diff 0)) + (while t + (if (or (> (1+ diff) (length str1)) + (> (1+ diff) (length str2))) + (throw 'done diff)) + (or (equal (aref str1 diff) (aref str2 diff)) + (throw 'done diff)) + (setq diff (1+ diff)))))) + +;; Calculate all possible completions for functions if argument is `function', +;; completions for procedures if argument is `procedure' or both functions and +;; procedures otherwise. + +(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_.]*\\)\\>")) + 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) + (progn (setq match (buffer-substring (match-beginning 2) + (match-end 2))) + (if (or (null predicate) + (funcall prdicate match)) + (setq all (cons match 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' + (let ((end (save-excursion (pascal-declaration-end) + (point))) + match) + ;; Traverse lines + (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\\)\\>\\)\\|" + 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 (re-search-forward "\\" (pascal-get-end-of-line) t) + (pascal-declaration-end) + (forward-line 1))))) + +(defun pascal-type-completion () + "Calculate all possible completions for types." + (let ((start (point)) + goon) + ;; Search for all reachable type declarations + (while (or (pascal-beg-of-defun) + (setq goon (not goon))) + (save-excursion + (if (and (< start (prog1 (save-excursion (pascal-end-of-defun) + (point)) + (forward-char 1))) + (re-search-forward + "\\\\|\\<\\(begin\\|function\\|proceudre\\)\\>" + start t) + (not (match-end 1))) + ;; Check current type declaration + (pascal-get-completion-decl)))))) + +(defun pascal-var-completion () + "Calculate all possible completions for variables (or constants)." + (let ((start (point)) + goon twice) + ;; Search for all reachable var declarations + (while (or (pascal-beg-of-defun) + (setq goon (not goon))) + (save-excursion + (if (> start (prog1 (save-excursion (pascal-end-of-defun) + (point)))) + () ; Declarations not reacable + (if (search-forward "(" (pascal-get-end-of-line) t) + ;; Check parameterlist + (pascal-get-completion-decl)) + (setq twice 2) + (while (>= (setq twice (1- twice)) 0) + (cond ((and (re-search-forward + (concat "\\<\\(var\\|const\\)\\>\\|" + "\\<\\(begin\\|function\\|procedure\\)\\>") + start t) + (not (match-end 2))) + ;; Check var/const declarations + (pascal-get-completion-decl)) + ((match-end 2) + (setq twice 0))))))))) + + +(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))))) + keyword-list)) + +;; Function passed to completing-read, try-completion or +;; all-completions to get completion on STR. If predicate is non-nil, +;; it must be a function to be called for every match to check if this +;; should really be a match. If flag is t, the function returns a list +;; of all possible completions. If it is nil it returns a string, the +;; longest possible completion, or t if STR is an exact match. If flag +;; is 'lambda, the function returns t if STR is an exact match, nil +;; otherwise. + +(defun pascal-completion (str predicate flag) (save-excursion - (let ((start (progn - (re-search-backward - "\\<\\(function\\|procedure\\)\\>" nil t) - (search-forward "(") - (current-column))) - (arglength 0) (vardecl nil) (done nil)) - (while (not (or done (eobp))) - (beginning-of-line) - (if (save-excursion - (re-search-forward "\\" (pascal-get-end-of-line) t)) - (setq vardecl t)) - (if (not (re-search-forward ":" (pascal-get-end-of-line) t)) - (setq done t)) - (skip-chars-backward ": \t") - (setq arglength (max arglength (current-column))) - (forward-line 1)) - (if vardecl - (list start (+ start 4) (1+ arglength)) - (list start start (1+ arglength)))))) + (let ((all nil)) + ;; Set buffer to use for searching labels. This should be set + ;; within functins which use pascal-completions + (set-buffer buffer-to-use) + + ;; Determine what should be completed + (let ((state (car (pascal-calculate-indent)))) + (cond (;--Within a declaration or parameterlist + (or (eq state 'declaration) (eq state 'paramlist) + (and (eq state 'defun) + (save-excursion + (re-search-backward ")[ \t]*:" (pascal-get-beg-of-line) t)))) + (if (or (eq state 'paramlist) (eq state 'defun)) + (pascal-beg-of-defun)) + (pascal-type-completion) + (pascal-keyword-completion pascal-type-keywords)) + (;--Starting a new statement + (and (not (eq state 'contexp)) + (save-excursion + (skip-chars-backward "a-zA-Z0-9_.") + (backward-sexp 1) + (or (looking-at pascal-nosemi-re) + (progn + (forward-sexp 1) + (looking-at "\\s *\\(;\\|:[^=]\\)"))))) + (save-excursion (pascal-var-completion)) + (pascal-func-completion 'procedure) + (pascal-keyword-completion pascal-start-keywords)) + (t;--Anywhere else + (save-excursion (pascal-var-completion)) + (pascal-func-completion 'function) + (pascal-keyword-completion pascal-seperator-keywords)))) + + ;; Now we have built a list of all matches. Give response to caller + (pascal-completion-response)))) -(defun pascal-declindent-middle-of-line (declkey endpos defaultindent) - "Indent declaration line." - (let ((decindent 0)) - (if (search-forward declkey endpos t) - (setq decindent (1- (current-column))) - (setq decindent defaultindent)) - (goto-char endpos) - (end-of-line) - (if (save-excursion (search-backward declkey endpos t)) - (progn (search-backward declkey) (skip-chars-backward " \t")) - (skip-chars-backward " \t")) - (pascal-delete-whitespaces) - (indent-to (max decindent (1+ (current-column)))) - ;; Indent after `declkey' - (if (looking-at declkey) - (progn - (forward-char 1) - (pascal-delete-whitespaces) - (indent-to (1+ (current-column))))))) - -(defun pascal-indent-within-comment (indent) - "Indent comments and/or indent text within comment." - (progn - ;; If we are at the beginning of the line, then we indent this line. - (if (save-excursion (skip-chars-backward " \t") (bolp)) +(defun pascal-completion-response () + (cond ((or (equal flag 'lambda) (null flag)) + ;; This was not called by all-completions + (if (null all) + ;; Return nil if there was no matching label + nil + ;; Get longest string common in the labels + (let* ((elm (cdr all)) + (match (car all)) + (min (length match)) + exact tmp) + (if (string= match str) + ;; Return t if first match was an exact match + (setq match t) + (while (not (null elm)) + ;; Find longest common string + (if (< (setq tmp (pascal-string-diff match (car elm))) min) + (progn + (setq min tmp) + (setq match (substring match 0 min)))) + ;; Terminate with match=t if this is an exact match + (if (string= (car elm) 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))) + nil + match)))) + ;; If flag is t, this was called by all-completions. Return + ;; list of all possible completions + (flag + all))) + +(defvar pascal-last-word-numb 0) +(defvar pascal-last-word-shown nil) +(defvar pascal-last-completions nil) + +(defun pascal-complete-word () + "Complete word at current point. +\(See also `pascal-toggle-completions', `pascal-type-keywords', +`pascal-start-keywords' and `pascal-seperator-keywords'.)" + (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)) + ;; The following variable is used in pascal-completion + (buffer-to-use (current-buffer)) + (allcomp (if (and pascal-toggle-completions + (string= pascal-last-word-shown str)) + pascal-last-completions + (all-completions str 'pascal-completion))) + (match (if pascal-toggle-completions + "" (try-completion + str (mapcar '(lambda (elm) (cons elm 0)) allcomp))))) + ;; Delete old string + (delete-region b e) + + ;; Toggle-completions inserts whole labels + (if pascal-toggle-completions (progn - (beginning-of-line) - (pascal-delete-whitespaces) - (indent-to indent)) - ;; Do nothing if we're not in a star comment. - (if (save-excursion - (beginning-of-line) - (skip-chars-forward " \t") - (looking-at "\\*\\|(\\*")) - (save-excursion - (beginning-of-line) - (search-forward "*") - (pascal-delete-whitespaces) - (indent-to (+ (current-column) 2))))))) + ;; Update entry number in list + (setq pascal-last-completions allcomp + pascal-last-word-numb + (if (>= pascal-last-word-numb (1- (length allcomp))) + 0 + (1+ pascal-last-word-numb))) + (setq pascal-last-word-shown (elt allcomp 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) + (message "(No match)"))) + ;; The other form of completion does not necessarly do that. + + ;; Insert match if found, or the original string if no match + (if (or (null match) (equal match 't)) + (progn (insert "" str) + (message "(No match)")) + (insert "" match)) + ;; Give message about current status of completion + (cond ((equal match 't) + (if (not (null (cdr allcomp))) + (message "(Complete but not unique)") + (message "(Sole completion)"))) + ;; Display buffer if the current completion didn't help + ;; on completing the label. + ((and (not (null (cdr allcomp))) (= (length str) (length match))) + (with-output-to-temp-buffer "*Completions*" + (display-completion-list allcomp)) + ;; Wait for a keypress. Then delete *Completion* window + (momentary-string-display "" (point)) + (delete-window (get-buffer-window (get-buffer "*Completions*"))) + ))))) + +(defun pascal-show-completions () + "Show all possible completions at current point." + (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)) + ;; The following variable is used in pascal-completion + (buffer-to-use (current-buffer)) + (allcomp (if (and pascal-toggle-completions + (string= pascal-last-word-shown str)) + pascal-last-completions + (all-completions str 'pascal-completion)))) + ;; Show possible completions in a temporary buffer. + (with-output-to-temp-buffer "*Completions*" + (display-completion-list allcomp)) + ;; Wait for a keypress. Then delete *Completion* window + (momentary-string-display "" (point)) + (delete-window (get-buffer-window (get-buffer "*Completions*"))))) + + +(defun pascal-get-default-symbol () + "Return symbol around current point as a string." + (save-excursion + (buffer-substring (progn + (skip-chars-backward " \t") + (skip-chars-backward "a-zA-Z0-9_") + (point)) + (progn + (skip-chars-forward "a-zA-Z0-9_") + (point))))) + +(defun pascal-build-defun-re (str &optional arg) + "Return function/procedure starting with STR as regular expression. +With optional second arg non-nil, STR is the complete name of the instruction." + (if arg + (concat "^\\(function\\|procedure\\)[ \t]+\\(" str "\\)\\>") + (concat "^\\(function\\|procedure\\)[ \t]+\\(" str "[a-zA-Z0-9_]*\\)\\>"))) + +;; Function passed to completing-read, try-completion or +;; all-completions to get completion on any function name. If +;; predicate is non-nil, it must be a function to be called for every +;; match to check if this should really be a match. If flag is t, the +;; function returns a list of all possible completions. If it is nil +;; it returns a string, the longest possible completion, or t if STR +;; 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) + (save-excursion + (let ((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) + + (let ((str 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))) + (goto-char (point-min)) + + ;; Build a list of all possible completions + (while (re-search-forward 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))))) + + ;; Now we have built a list of all matches. Give response to caller + (pascal-completion-response)))) + +(defun pascal-goto-defun () + "Move to specified Pascal function/procedure. +The default is a name found in the buffer around point." + (interactive) + (let* ((default (pascal-get-default-symbol)) + ;; The following variable is used in pascal-comp-function + (buffer-to-use (current-buffer)) + (default (if (pascal-comp-defun default nil 'lambda) + default "")) + (label (if (not (string= default "")) + ;; Do completion with default + (completing-read (concat "Label: (default " default ") ") + 'pascal-comp-defun nil t "") + ;; There is no default value. Complete without it + (completing-read "Label: " + 'pascal-comp-defun nil t "")))) + ;; If there was no response on prompt, use default value + (if (string= label "") + (setq label default)) + ;; Goto right place in buffer if label is not an empty string + (or (string= label "") + (progn + (goto-char (point-min)) + (re-search-forward (pascal-build-defun-re label t)) + (beginning-of-line))))) + + -(defun pascal-find-leading-case-colon () - "Return hpos of first colon after the case-of or record line. -If there's no such line, use the place where it ought to be." - (let ((pos (save-excursion - (beginning-of-line) - (skip-chars-forward " \t") - (point)))) - (save-excursion - (re-search-backward "\\<\\(case\\|record\\)\\>") - (forward-line 1) - (skip-chars-forward " \t") - (if (not (eq pos (point))) +;;; +;;; Pascal-outline-mode +;;; +(defvar pascal-outline-map nil "Keymap used in Pascal Outline mode.") + +(if pascal-outline-map + nil + (if (boundp 'set-keymap-name) + (set-keymap-name pascal-outline-map 'pascal-outline-map)) + (if (not (boundp 'set-keymap-parent)) + (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 "\C-cg" '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)) + +(defvar pascal-outline-mode nil "Non-nil while using Pascal Outline mode.") +(make-variable-buffer-local 'pascal-outline-mode) +(set-default 'pascal-outline-mode nil) +(if (not (assoc 'pascal-outline-mode minor-mode-alist)) + (setq minor-mode-alist (append minor-mode-alist + (list '(pascal-outline-mode " Outl"))))) + +(defun pascal-outline (&optional arg) + "Outline-line minor mode for Pascal mode. +When in Pascal Outline mode, portions +of the text being edited may be made invisible. \\ + +Pascal Outline mode provides some additional commands. + +\\[pascal-outline-prev-defun]\ +\t- Move to previous function/procedure, hiding everything else. +\\[pascal-outline-next-defun]\ +\t- Move to next function/procedure, hiding everything else. +\\[pascal-outline-goto-defun]\ +\t- Goto function/procedure prompted for in minibuffer, +\t hide all other functions. +\\[pascal-show-all]\t- Show the whole buffer. +\\[pascal-hide-other-defuns]\ +\t- Hide everything but the current function (function under the cursor). +\\[pascal-outline]\t- Leave pascal-outline-mode." + (interactive "P") + (setq pascal-outline-mode + (if (null arg) (not pascal-outline-mode) t)) + (if (boundp 'redraw-mode-line) + (redraw-mode-line)) + (if pascal-outline-mode + (progn + (setq selective-display t) + (use-local-map pascal-outline-map)) + (progn + (setq selective-display nil) + (pascal-show-all) + (use-local-map pascal-mode-map)))) + +(defun pascal-outline-change (b e flag) + (let ((modp (buffer-modified-p))) + (unwind-protect + (subst-char-in-region b e (if (= flag ?\n) ?\^M ?\n) flag) + (set-buffer-modified-p modp)))) + +(defun pascal-show-all () + "Show all of the text in the buffer." + (interactive) + (pascal-outline-change (point-min) (point-max) ?\n)) + +(defun pascal-hide-other-defuns () + "Show only the current defun." + (interactive) + (save-excursion + (let ((beg (progn (if (not (looking-at "\\(function\\|procedure\\)\\>")) + (pascal-beg-of-defun)) + (point))) + (end (progn (pascal-end-of-defun) + (backward-sexp 1) + (search-forward "\n\\|\^M" nil t) + (point))) + (opoint (point-min))) + (goto-char (point-min)) + + ;; Hide all functions before current function + (while (re-search-forward "^\\(function\\|procedure\\)\\>" beg 'move) + (pascal-outline-change opoint (1- (match-beginning 0)) ?\^M) + (setq opoint (point)) + ;; Functions may be nested + (if (> (progn (pascal-end-of-defun) (point)) beg) + (goto-char opoint))) + (if (> beg opoint) + (pascal-outline-change opoint (1- beg) ?\^M)) + + ;; Show current function + (pascal-outline-change beg end ?\n) + ;; Hide nested functions + (forward-char 1) + (while (re-search-forward "^\\(function\\|procedure\\)\\>" end 'move) + (setq opoint (point)) + (pascal-end-of-defun) + (pascal-outline-change opoint (point) ?\^M)) + + (goto-char end) + (setq opoint end) + + ;; Hide all function after current function + (while (re-search-forward "^\\(function\\|procedure\\)\\>" nil 'move) + (pascal-outline-change opoint (1- (match-beginning 0)) ?\^M) + (setq opoint (point)) + (pascal-end-of-defun)) + (pascal-outline-change opoint (point-max) ?\^M) + + ;; Hide main program + (if (< (progn (forward-line -1) (point)) end) (progn - (search-forward ":" (pascal-get-end-of-line) t) - (1- (current-column))) - (+ (current-column) pascal-case-offset))))) + (goto-char beg) + (pascal-end-of-defun) + (backward-sexp 1) + (pascal-outline-change (point) (point-max) ?\^M)))))) + +(defun pascal-outline-next-defun () + "Move to next function/procedure, hiding all others." + (interactive) + (pascal-end-of-defun) + (pascal-hide-other-defuns)) -(provide 'pascal) +(defun pascal-outline-prev-defun () + "Move to previous function/procedure, hiding all others." + (interactive) + (pascal-beg-of-defun) + (pascal-hide-other-defuns)) -;; pascal.el ends here. +(defun pascal-outline-goto-defun () + "Move to specified function/procedure, hiding all others." + (interactive) + (pascal-goto-defun) + (pascal-hide-other-defuns)) + +;;; pascal.el ends here