;;; delphi.el --- major mode for editing Delphi source (Object Pascal) in Emacs
-;; Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007
-;; Free Software Foundation, Inc.
+;; Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+;; 2008, 2009, 2010 Free Software Foundation, Inc.
-;; Author: Ray Blaak <blaak@infomatch.com>
-;; Maintainer: FSF (Blaak's email addr bounces, Aug 2005)
+;; Authors: Ray Blaak <blaak@infomatch.com>,
+;; Simon South <ssouth@member.fsf.org>
+;; Maintainer: Simon South <ssouth@member.fsf.org>
;; Keywords: languages
;; 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.
+;; 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 3 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.
+;; 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
-;; GNU Emacs; see the file COPYING. If not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
(provide 'delphi)
-(eval-and-compile
- ;; Allow execution on pre Emacs 20 versions.
- (or (fboundp 'when)
- (defmacro when (test &rest body)
- `(if ,test (progn ,@body))))
- (or (fboundp 'unless)
- (defmacro unless (test &rest body)
- `(if (not ,test) (progn ,@body))))
- (or (fboundp 'defgroup)
- (defmacro defgroup (group val docs &rest group-attributes)
- `(defvar ,group ,val ,docs)))
- (or (fboundp 'defcustom)
- (defmacro defcustom (val-name val docs &rest custom-attributes)
- `(defvar ,val-name ,val ,docs)))
- (or (fboundp 'cadr)
- (defmacro cadr (list) `(car (cdr ,list))))
- (or (fboundp 'cddr)
- (defmacro cddr (list) `(cdr (cdr ,list))))
- (or (fboundp 'with-current-buffer)
- (defmacro with-current-buffer (buf &rest forms)
- `(save-excursion (set-buffer ,buf) ,@forms)))
- )
-
(defgroup delphi nil
"Major mode for editing Delphi source in Emacs."
:version "21.1"
(defconst delphi-decl-sections '(type const var label resourcestring)
"Denotes the start of a declaration section.")
+(defconst delphi-interface-types '(dispinterface interface)
+ "Interface types.")
+
(defconst delphi-class-types '(class object)
"Class types.")
-(defconst delphi-composite-types `(,@delphi-class-types record)
+(defconst delphi-composite-types
+ `(,@delphi-class-types ,@delphi-interface-types record)
"Types that contain declarations within them.")
(defconst delphi-unit-sections
(delphi-stmt-line-indent-of token delphi-indent-level))))
(defun delphi-composite-type-start (token last-token)
- ;; Returns true (actually the last-token) if the pair equals (= class) or (=
- ;; record), and nil otherwise.
+ ;; Returns true (actually the last-token) if the pair equals (= class), (=
+ ;; dispinterface), (= interface), (= object), or (= record), and nil
+ ;; otherwise.
(if (and (eq 'equals (delphi-token-kind token))
(delphi-is (delphi-token-kind last-token) delphi-composite-types))
last-token))
delphi-indent-level)))
;; In unit sections we indent right to the left.
- ((delphi-is token-kind delphi-unit-sections) (throw 'done 0))
+ ((delphi-is token-kind delphi-unit-sections)
+ (throw 'done
+ ;; Handle specially the case of "interface", which can be used
+ ;; to start either a unit section or an interface definition.
+ (if (delphi-is token-kind delphi-interface-types)
+ (progn
+ ;; Find the previous non-whitespace token.
+ (while (progn
+ (setq last-token token
+ token (delphi-previous-token token)
+ token-kind (delphi-token-kind token))
+ (and token
+ (delphi-is token-kind
+ delphi-whitespace))))
+ ;; If this token is an equals sign, "interface" is being
+ ;; used to start an interface definition and we should
+ ;; treat it as a composite type; otherwise, we should
+ ;; consider it the start of a unit section.
+ (if (and token (eq token-kind 'equals))
+ (delphi-line-indent-of last-token
+ delphi-indent-level)
+ 0))
+ 0)))
;; A previous terminator means we can stop.
((delphi-is token-kind delphi-previous-terminators)
;; Indent in from the expression.
(delphi-indent-of last-token delphi-indent-level))
- ;; No enclosing expression; use the previous statment's
+ ;; No enclosing expression; use the previous statement's
;; indent.
((delphi-previous-indent-of token)))))
(when delphi-newline-always-indents
;; Indent both the (now) previous and current line first.
(save-excursion
- (previous-line 1)
+ (forward-line -1)
(delphi-indent-line))
(delphi-indent-line)))
(defun delphi-tab ()
- "Indent the current line or insert a TAB, depending on the value of
-`delphi-tab-always-indents' and the current line position."
+ "Indent the region, when Transient Mark mode is enabled and the region is
+active. Otherwise, indent the current line or insert a TAB, depending on the
+value of `delphi-tab-always-indents' and the current line position."
(interactive)
- (if (or delphi-tab-always-indents ; We are always indenting
- ;; Or we are before the first non-space character on the line.
- (save-excursion (skip-chars-backward delphi-space-chars) (bolp)))
- (delphi-indent-line)
- (insert "\t")))
+ (cond ((use-region-p)
+ ;; If Transient Mark mode is enabled and the region is active, indent
+ ;; the entire region.
+ (indent-region (region-beginning) (region-end)))
+ ((or delphi-tab-always-indents
+ (save-excursion (skip-chars-backward delphi-space-chars) (bolp)))
+ ;; Otherwise, if we are configured always to indent (regardless of the
+ ;; point's position in the line) or we are before the first non-space
+ ;; character on the line, indent the line.
+ (delphi-indent-line))
+ (t
+ ;; Otherwise, insert a tab character.
+ (insert "\t"))))
(defun delphi-is-directory (path)
(unit-file (downcase unit)))
(catch 'done
;; Search for the file.
- (mapcar #'(lambda (file)
- (let ((path (concat dir "/" file)))
- (if (and (string= unit-file (downcase file))
- (delphi-is-file path))
- (throw 'done path))))
- files)
+ (mapc #'(lambda (file)
+ (let ((path (concat dir "/" file)))
+ (if (and (string= unit-file (downcase file))
+ (delphi-is-file path))
+ (throw 'done path))))
+ files)
;; Not found. Search subdirectories.
(when recurse
- (mapcar #'(lambda (subdir)
- (unless (member subdir '("." ".."))
- (let ((path (delphi-search-directory
- unit (concat dir "/" subdir) recurse)))
- (if path (throw 'done path)))))
- files))
+ (mapc #'(lambda (subdir)
+ (unless (member subdir '("." ".."))
+ (let ((path (delphi-search-directory
+ unit (concat dir "/" subdir) recurse)))
+ (if path (throw 'done path)))))
+ files))
;; Not found.
nil))))
((stringp delphi-search-path)
(delphi-find-unit-in-directory unit delphi-search-path))
- ((mapcar
+ ((mapc
#'(lambda (dir)
(let ((file (delphi-find-unit-in-directory unit dir)))
(if file (throw 'done file))))
(defvar delphi-debug-mode-map
(let ((kmap (make-sparse-keymap)))
- (mapcar #'(lambda (binding) (define-key kmap (car binding) (cadr binding)))
- '(("n" delphi-debug-goto-next-token)
- ("p" delphi-debug-goto-previous-token)
- ("t" delphi-debug-show-current-token)
- ("T" delphi-debug-tokenize-buffer)
- ("W" delphi-debug-tokenize-window)
- ("g" delphi-debug-goto-point)
- ("s" delphi-debug-show-current-string)
- ("a" delphi-debug-parse-buffer)
- ("w" delphi-debug-parse-window)
- ("f" delphi-debug-fontify-window)
- ("F" delphi-debug-fontify-buffer)
- ("r" delphi-debug-parse-region)
- ("c" delphi-debug-unparse-buffer)
- ("x" delphi-debug-show-is-stable)
- ))
+ (mapc #'(lambda (binding) (define-key kmap (car binding) (cadr binding)))
+ '(("n" delphi-debug-goto-next-token)
+ ("p" delphi-debug-goto-previous-token)
+ ("t" delphi-debug-show-current-token)
+ ("T" delphi-debug-tokenize-buffer)
+ ("W" delphi-debug-tokenize-window)
+ ("g" delphi-debug-goto-point)
+ ("s" delphi-debug-show-current-string)
+ ("a" delphi-debug-parse-buffer)
+ ("w" delphi-debug-parse-window)
+ ("f" delphi-debug-fontify-window)
+ ("F" delphi-debug-fontify-buffer)
+ ("r" delphi-debug-parse-region)
+ ("c" delphi-debug-unparse-buffer)
+ ("x" delphi-debug-show-is-stable)
+ ))
kmap)
"Keystrokes for delphi-mode debug commands.")
(defvar delphi-mode-map
(let ((kmap (make-sparse-keymap)))
- (mapcar #'(lambda (binding) (define-key kmap (car binding) (cadr binding)))
- (list '("\r" delphi-newline)
- '("\t" delphi-tab)
- '("\177" backward-delete-char-untabify)
-;; '("\C-cd" delphi-find-current-def)
-;; '("\C-cx" delphi-find-current-xdef)
-;; '("\C-cb" delphi-find-current-body)
- '("\C-cu" delphi-find-unit)
- '("\M-q" delphi-fill-comment)
- '("\M-j" delphi-new-comment-line)
- ;; Debug bindings:
- (list "\C-c\C-d" delphi-debug-mode-map)))
+ (mapc #'(lambda (binding) (define-key kmap (car binding) (cadr binding)))
+ (list '("\r" delphi-newline)
+ '("\t" delphi-tab)
+ '("\177" backward-delete-char-untabify)
+;; '("\C-cd" delphi-find-current-def)
+;; '("\C-cx" delphi-find-current-xdef)
+;; '("\C-cb" delphi-find-current-body)
+ '("\C-cu" delphi-find-unit)
+ '("\M-q" delphi-fill-comment)
+ '("\M-j" delphi-new-comment-line)
+ ;; Debug bindings:
+ (list "\C-c\C-d" delphi-debug-mode-map)))
kmap)
"Keymap used in Delphi mode.")
;;;###autoload
(defun delphi-mode (&optional skip-initial-parsing)
"Major mode for editing Delphi code. \\<delphi-mode-map>
-\\[delphi-tab]\t- Indents the current line for Delphi code.
+\\[delphi-tab]\t- Indents the current line (or region, if Transient Mark mode
+\t is enabled and the region is active) of Delphi code.
\\[delphi-find-unit]\t- Search for a Delphi source file.
\\[delphi-fill-comment]\t- Fill the current comment.
\\[delphi-new-comment-line]\t- If in a // comment, do a new comment line.
(set-syntax-table delphi-mode-syntax-table)
;; Buffer locals:
- (mapcar #'(lambda (var)
- (let ((var-symb (car var))
- (var-val (cadr var)))
- (make-local-variable var-symb)
- (set var-symb var-val)))
- (list '(indent-line-function delphi-indent-line)
- '(comment-indent-function delphi-indent-line)
- '(case-fold-search t)
- '(delphi-progress-last-reported-point nil)
- '(delphi-ignore-changes nil)
- (list 'font-lock-defaults delphi-font-lock-defaults)))
+ (mapc #'(lambda (var)
+ (let ((var-symb (car var))
+ (var-val (cadr var)))
+ (make-local-variable var-symb)
+ (set var-symb var-val)))
+ (list '(indent-line-function delphi-indent-line)
+ '(comment-indent-function delphi-indent-line)
+ '(case-fold-search t)
+ '(delphi-progress-last-reported-point nil)
+ '(delphi-ignore-changes nil)
+ (list 'font-lock-defaults delphi-font-lock-defaults)))
;; We need to keep track of changes to the buffer to determine if we need
;; to retokenize changed text.
(run-mode-hooks 'delphi-mode-hook))
-;;; arch-tag: 410e192d-e9b5-4397-ad62-12340fc3fa41
+;; arch-tag: 410e192d-e9b5-4397-ad62-12340fc3fa41
;;; delphi.el ends here