aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tools/emacs
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/tools/emacs
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/tools/emacs')
-rw-r--r--lib/tools/emacs/AUTHORS15
-rw-r--r--lib/tools/emacs/Makefile84
-rw-r--r--lib/tools/emacs/README48
-rw-r--r--lib/tools/emacs/erlang-eunit.el254
-rw-r--r--lib/tools/emacs/erlang-start.el116
-rw-r--r--lib/tools/emacs/erlang.el6651
-rw-r--r--lib/tools/emacs/erlang_appwiz.el1345
-rw-r--r--lib/tools/emacs/internal_doc/emacs.sgml3258
-rw-r--r--lib/tools/emacs/tags.361
l---------lib/tools/emacs/tags.erl1
-rw-r--r--lib/tools/emacs/test.erl.indented536
-rw-r--r--lib/tools/emacs/test.erl.orig536
-rw-r--r--lib/tools/emacs/vsn.mk3
13 files changed, 12908 insertions, 0 deletions
diff --git a/lib/tools/emacs/AUTHORS b/lib/tools/emacs/AUTHORS
new file mode 100644
index 0000000000..b5f426ba81
--- /dev/null
+++ b/lib/tools/emacs/AUTHORS
@@ -0,0 +1,15 @@
+Original Authors:
+The Erlang emacs mode was written by Anders Lindgren.
+
+Contributors:
+Luke Gorrie
+Dave Love
+
+Maintainers:
+Sverker Wiberg
+Kent Boortz
+Bj�rn Gustavsson
+
+Currently maintained by:
+Ingela Anderton Andin
+Dan Gudmundsson \ No newline at end of file
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
new file mode 100644
index 0000000000..7249263992
--- /dev/null
+++ b/lib/tools/emacs/Makefile
@@ -0,0 +1,84 @@
+# ``The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved via the world wide web at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id$
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(TOOLS_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/tools-$(VSN)
+
+# ----------------------------------------------------
+# Common Macros
+# ----------------------------------------------------
+
+MAN_FILES= \
+ tags.3
+
+EMACS_FILES= \
+ erlang-start \
+ erlang-eunit \
+ erlang
+
+README_FILES= README
+
+EL_FILES = $(EMACS_FILES:%=%.el)
+
+ELC_FILES = $(EMACS_FILES:%=%.elc)
+
+TEST_FILES = test.erl.indented test.erl.orig
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES) $(EL_FILES)
+
+clean:
+ rm -f $(TARGET_FILES) $(ELC_FILES)
+ rm -f errs core *~
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/emacs
+ $(INSTALL_DATA) $(EL_FILES) $(README_FILES) $(TEST_FILES) \
+ $(RELSYSDIR)/emacs
+
+ifeq ($(DOCTYPE),pdf)
+release_docs_spec:
+else
+ifeq ($(DOCTYPE),ps)
+release_docs_spec:
+else
+release_docs_spec: docs
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
+ $(INSTALL_DATA) $(MAN_FILES) $(RELEASE_PATH)/man/man3
+endif
+endif
diff --git a/lib/tools/emacs/README b/lib/tools/emacs/README
new file mode 100644
index 0000000000..ca068d04c4
--- /dev/null
+++ b/lib/tools/emacs/README
@@ -0,0 +1,48 @@
+User configuration notes
+========================
+
+Below is a quick guide to necessary configurations for getting
+started with the Erlang mode for Emacs. Please refer to the
+Users guide and reference manual in the documentation for the
+Erlang/OTP application tools for more information.
+
+
+For UNIX users
+--------------
+
+To set up the Erlang Emacs mode on UNIX systems, edit/create the file
+.emacs in the your home directory.
+
+Below is a complete example of what should be added to a user's .emacs
+provided that OTP is installed in the directory /usr/local/otp:
+
+ (setq load-path (cons "/usr/local/otp/lib/tools-<ToolsVer>/emacs"
+ load-path))
+ (setq erlang-root-dir "/usr/local/otp")
+ (setq exec-path (cons "/usr/local/otp/bin" exec-path))
+ (require 'erlang-start)
+
+
+For Windows users
+-----------------
+
+To set up the Erlang Emacs mode on Windows systems, edit/create the
+file .emacs, the location of the file depends on the configuration of
+the system. If the HOME environment variable is set, Emacs will look
+for the .emacs file in the directory indicated by the HOME
+variable. If HOME is not set, Emacs will look for the .emacs file in
+C:\.
+
+Below is a complete example of what should be added to a user's .emacs
+provided that OTP is installed in the directory C:\Program
+Files\erl-<Ver>:
+
+ (setq load-path (cons "C:/Program Files/erl<Ver>/lib/tools-<ToolsVer>/emacs"
+ load-path))
+ (setq erlang-root-dir "C:/Program Files/erl<Ver>")
+ (setq exec-path (cons "C:/Program Files/erl<Ver>/bin" exec-path))
+ (require 'erlang-start)
+
+
+
+
diff --git a/lib/tools/emacs/erlang-eunit.el b/lib/tools/emacs/erlang-eunit.el
new file mode 100644
index 0000000000..05528aee6d
--- /dev/null
+++ b/lib/tools/emacs/erlang-eunit.el
@@ -0,0 +1,254 @@
+;;
+;; %CopyrightBegin%
+;;
+;; Copyright Ericsson AB 2009. All Rights Reserved.
+;;
+;; The contents of this file are subject to the Erlang Public License,
+;; Version 1.1, (the "License"); you may not use this file except in
+;; compliance with the License. You should have received a copy of the
+;; Erlang Public License along with this software. If not, it can be
+;; retrieved online at http://www.erlang.org/.
+;;
+;; Software distributed under the License is distributed on an "AS IS"
+;; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+;; the License for the specific language governing rights and limitations
+;; under the License.
+;;
+;; %CopyrightEnd%
+;;;
+;;; Purpose: Provide EUnit utilities.
+;;;
+;;; Author: Klas Johansson
+
+(defvar erlang-eunit-separate-src-and-test-directories t
+ "*Whether or not to keep source and EUnit test files in separate directories")
+
+;;;
+;;; Switch between src/EUnit test buffers
+;;;
+(defun erlang-eunit-toggle-src-and-test-file-other-window ()
+ "Switch to the src file if the EUnit test file is the current
+buffer and vice versa"
+ (interactive)
+ (if (erlang-eunit-test-file-p buffer-file-name)
+ (erlang-eunit-open-src-file-other-window buffer-file-name)
+ (erlang-eunit-open-test-file-other-window buffer-file-name)))
+
+;;;
+;;; Open the EUnit test file which corresponds to a src file
+;;;
+(defun erlang-eunit-open-test-file-other-window (src-file-path)
+ "Open the EUnit test file which corresponds to a src file"
+ (find-file-other-window (erlang-eunit-test-filename src-file-path)))
+
+
+;;;
+;;; Open the src file which corresponds to the an EUnit test file
+;;;
+(defun erlang-eunit-open-src-file-other-window (test-file-path)
+ "Open the src file which corresponds to the an EUnit test file"
+ (find-file-other-window (erlang-eunit-src-filename test-file-path)))
+
+;;; Return the name and path of the EUnit test file
+;;, (input may be either the source filename itself or the EUnit test filename)
+(defun erlang-eunit-test-filename (file-path)
+ (erlang-eunit-rewrite-filename file-path "test" "_tests"))
+
+;;; Return the name and path of the source file
+;;, (input may be either the source filename itself or the EUnit test filename)
+(defun erlang-eunit-src-filename (file-path)
+ (erlang-eunit-rewrite-filename file-path "src" ""))
+
+;;; Rewrite a filename from the src or test filename to the other
+(defun erlang-eunit-rewrite-filename (orig-file-path dest-dirname dest-suffix)
+ (let* ((root-dir-name (erlang-eunit-file-root-dir-name orig-file-path))
+ (src-module-name (erlang-eunit-source-module-name orig-file-path))
+ (dest-base-name (concat src-module-name dest-suffix ".erl"))
+ (dest-dir-name-1 (file-name-directory orig-file-path))
+ (dest-dir-name-2 (filename-join root-dir-name dest-dirname))
+ (dest-file-name-1 (filename-join dest-dir-name-1 dest-base-name))
+ (dest-file-name-2 (filename-join dest-dir-name-2 dest-base-name)))
+ ;; This function tries to be a bit intelligent:
+ ;; * if there already is a test (or source) file in the same
+ ;; directory as a source (or test) file, it'll be picked
+ ;; * if there already is a test (or source) file in a separate
+ ;; test (or src) directory, it'll be picked
+ ;; * otherwise it'll resort to whatever alternative (same or
+ ;; separate directories) that the user has chosen
+ (cond ((file-readable-p dest-file-name-1)
+ dest-file-name-1)
+ ((file-readable-p dest-file-name-2)
+ dest-file-name-2)
+ (erlang-eunit-separate-src-and-test-directories
+ dest-file-name-2)
+ (t
+ dest-file-name-1))))
+
+;;; Checks whether a file is a EUnit test file or not
+(defun erlang-eunit-test-file-p (file-path)
+ (erlang-eunit-string-match-p "^\\(.+\\)_tests.erl$" file-path))
+
+;;; Return the module name of the source file
+;;; /tmp/foo/src/x.erl --> x
+;;; /tmp/foo/test/x_tests.erl --> x
+(defun erlang-eunit-source-module-name (file-path)
+ (interactive)
+ (let* ((file-name (file-name-nondirectory file-path))
+ (base-name (file-name-sans-extension file-name)))
+ (if (string-match "^\\(.+\\)_tests$" base-name)
+ (substring base-name (match-beginning 1) (match-end 1))
+ base-name)))
+
+;;; Return the directory name which is common to both src and test
+;;; /tmp/foo/src/x.erl --> /tmp/foo
+;;; /tmp/foo/test/x_tests.erl --> /tmp/foo
+(defun erlang-eunit-file-root-dir-name (file-path)
+ (erlang-eunit-dir-parent-dirname (file-name-directory file-path)))
+
+;;; Return the parent directory name of a directory
+;;; /tmp/foo/ --> /tmp
+;;; /tmp/foo --> /tmp
+(defun erlang-eunit-dir-parent-dirname (dir-name)
+ (file-name-directory (directory-file-name dir-name)))
+
+;;; Older emacsen don't have string-match-p.
+(defun erlang-eunit-string-match-p (regexp string &optional start)
+ (if (fboundp 'string-match-p) ;; appeared in emacs 23
+ (string-match-p regexp string start)
+ (save-match-data ;; fallback for earlier versions of emacs
+ (string-match regexp string start))))
+
+;;; Join filenames
+(defun filename-join (dir file)
+ (if (or (= (elt file 0) ?/)
+ (= (car (last (append dir nil))) ?/))
+ (concat dir file)
+ (concat dir "/" file)))
+
+;;; Run EUnit tests for the current module
+(defun erlang-eunit-run-tests ()
+ "Run the EUnit test suite for the current module.
+
+With prefix arg, runs tests with the verbose flag set."
+ (interactive)
+ (let* ((module-name (erlang-add-quotes-if-needed
+ (erlang-eunit-source-module-name buffer-file-name)))
+ (opts (if current-prefix-arg ", [verbose]" ""))
+ (command (format "eunit:test(%s%s)." module-name opts)))
+ (erlang-eunit-inferior-erlang-send-command command)))
+
+;;; Compile source and EUnit test file and finally run EUnit tests for
+;;; the current module
+(defun erlang-eunit-compile-and-run-tests ()
+ "Compile the source and test files and run the EUnit test suite.
+
+With prefix arg, compiles for debug and runs tests with the verbose flag set."
+ (interactive)
+ (let ((src-filename (erlang-eunit-src-filename buffer-file-name))
+ (test-filename (erlang-eunit-test-filename buffer-file-name)))
+
+ ;; The purpose of out-maneuvering `save-some-buffers', as is done
+ ;; below, is to ask the question about saving buffers only once,
+ ;; instead of possibly several: one for each file to compile,
+ ;; for instance for both x.erl and x_tests.erl.
+ (save-some-buffers)
+ (flet ((save-some-buffers (&optional any) nil))
+
+ ;; Compilation of the source file is mandatory (the file must
+ ;; exist, otherwise the procedure is aborted). Compilation of the
+ ;; test file on the other hand, is optional, since eunit tests may
+ ;; be placed in the source file instead. Any compilation error
+ ;; will prevent the subsequent steps to be run (hence the `and')
+ (and (erlang-eunit-compile-file src-filename)
+ (if (file-readable-p test-filename)
+ (erlang-eunit-compile-file test-filename)
+ t)
+ (erlang-eunit-run-tests)))))
+
+(defun erlang-eunit-compile-file (file-path)
+ (if (file-readable-p file-path)
+ (save-excursion
+ (set-buffer (find-file-noselect file-path))
+ (erlang-compile)
+ (erlang-eunit-last-compilation-successful-p))
+ (let ((msg (format "Could not read %s" file-path)))
+ (erlang-eunit-inferior-erlang-send-command
+ (format "%% WARNING: %s" msg))
+ (error msg))))
+
+(defun erlang-eunit-last-compilation-successful-p ()
+ (save-excursion
+ (set-buffer inferior-erlang-buffer)
+ (goto-char compilation-parsing-end)
+ (erlang-eunit-all-list-elems-fulfill-p
+ (lambda (re) (let ((continue t)
+ (result t))
+ (while continue ; ignore warnings, stop at errors
+ (if (re-search-forward re (point-max) t)
+ (if (erlang-eunit-is-compilation-warning)
+ t
+ (setq result nil)
+ (setq continue nil))
+ (setq result t)
+ (setq continue nil)))
+ result))
+ (mapcar (lambda (e) (car e)) erlang-error-regexp-alist))))
+
+(defun erlang-eunit-is-compilation-warning ()
+ (erlang-eunit-string-match-p
+ "[0-9]+: Warning:"
+ (buffer-substring (line-beginning-position) (line-end-position))))
+
+(defun erlang-eunit-all-list-elems-fulfill-p (pred list)
+ (let ((matches-p t))
+ (while (and list matches-p)
+ (if (not (funcall pred (car list)))
+ (setq matches-p nil))
+ (setq list (cdr list)))
+ matches-p))
+
+;;; Evaluate a command in an erlang buffer
+(defun erlang-eunit-inferior-erlang-send-command (command)
+ "Evaluate a command in an erlang buffer."
+ (interactive "P")
+ (inferior-erlang-prepare-for-input)
+ (inferior-erlang-send-command command)
+ (sit-for 0) ;; redisplay
+ (inferior-erlang-wait-prompt))
+
+
+;;;====================================================================
+;;; Key bindings
+;;;====================================================================
+
+(defvar erlang-eunit-toggle-src-and-test-file-other-window-key "\C-c\C-et"
+ "*Key to which the `erlang-eunit-toggle-src-and-test-file-other-window'
+function will be bound.")
+(defvar erlang-eunit-compile-and-run-tests-key "\C-c\C-ek"
+ "*Key to which the `erlang-eunit-compile-and-run-tests'
+function will be bound.")
+
+(defun erlang-eunit-add-key-bindings ()
+ (erlang-eunit-ensure-keymap-for-key
+ erlang-eunit-toggle-src-and-test-file-other-window-key)
+ (local-set-key erlang-eunit-toggle-src-and-test-file-other-window-key
+ 'erlang-eunit-toggle-src-and-test-file-other-window)
+ (erlang-eunit-ensure-keymap-for-key
+ erlang-eunit-compile-and-run-tests-key)
+ (local-set-key erlang-eunit-compile-and-run-tests-key
+ 'erlang-eunit-compile-and-run-tests))
+
+(defun erlang-eunit-ensure-keymap-for-key (key-seq)
+ (let ((prefix-keys (butlast (append key-seq nil)))
+ (prefix-seq ""))
+ (while prefix-keys
+ (setq prefix-seq (concat prefix-seq (make-string 1 (car prefix-keys))))
+ (setq prefix-keys (cdr prefix-keys))
+ (if (not (keymapp (lookup-key (current-local-map) prefix-seq)))
+ (local-set-key prefix-seq (make-sparse-keymap))))))
+
+(add-hook 'erlang-mode-hook 'erlang-eunit-add-key-bindings)
+
+
+(provide 'erlang-eunit)
+;; erlang-eunit ends here
diff --git a/lib/tools/emacs/erlang-start.el b/lib/tools/emacs/erlang-start.el
new file mode 100644
index 0000000000..542e81f24c
--- /dev/null
+++ b/lib/tools/emacs/erlang-start.el
@@ -0,0 +1,116 @@
+;; erlang-start.el --- Load this file to initialize the Erlang package.
+
+;; Copyright (C) 1998 Ericsson Telecom AB
+
+;; Author: Anders Lindgren
+;; Version: 2.3
+;; Keywords: erlang, languages, processes
+;; Created: 1996-09-18
+;; Date: 1998-03-16
+
+;;; Commentary:
+
+;; Introduction:
+;; ------------
+;;
+;; This package provides support for the programming language Erlang.
+;; The package provides an editing mode with lots of bells and
+;; whistles, compilation support, and it makes it possible for the
+;; user to start Erlang shells that run inside Emacs.
+;;
+;; See the Erlang distribution for full documentation of this package.
+
+;; Installation:
+;; ------------
+;;
+;; Place this file in Emacs load path, byte-compile it, and add the
+;; following line to the appropriate init file:
+;;
+;; (require 'erlang-start)
+;;
+;; The full documentation contains much more extensive description of
+;; the installation procedure.
+
+;; Reporting Bugs:
+;; --------------
+;;
+;; Please send bug reports to the following email address:
+;;
+;; Please state as exactly as possible:
+;; - Version number of Erlang Mode (see the menu), Emacs, Erlang,
+;; and of any other relevant software.
+;; - What the expected result was.
+;; - What you did, preferably in a repeatable step-by-step form.
+;; - A description of the unexpected result.
+;; - Relevant pieces of Erlang code causing the problem.
+;; - Personal Emacs customisations, if any.
+;;
+;; Should the Emacs generate an error, please set the emacs variable
+;; `debug-on-error' to `t'. Repeat the error and enclose the debug
+;; information in your bug-report.
+;;
+;; To set the variable you can use the following command:
+;; M-x set-variable RET debug-on-error RET t RET
+
+;;; Code:
+
+;;
+;; Declare functions in "erlang.el".
+;;
+
+(autoload 'erlang-mode "erlang" "Major mode for editing Erlang code." t)
+(autoload 'erlang-version "erlang"
+ "Return the current version of Erlang mode." t)
+(autoload 'erlang-shell "erlang" "Start a new Erlang shell." t)
+(autoload 'run-erlang "erlang" "Start a new Erlang shell." t)
+
+(autoload 'erlang-compile "erlang"
+ "Compile Erlang module in current buffer." t)
+
+(autoload 'erlang-man-module "erlang"
+ "Find manual page for MODULE." t)
+(autoload 'erlang-man-function "erlang"
+ "Find manual page for NAME, where NAME is module:function." t)
+
+(autoload 'erlang-find-tag "erlang"
+ "Like `find-tag'. Capable of retreiving Erlang modules.")
+(autoload 'erlang-find-tag-other-window "erlang"
+ "Like `find-tag-other-window'. Capable of retreiving Erlang modules.")
+
+
+;;
+;; Associate files extensions ".erl" and ".hrl" with Erlang mode.
+;;
+
+(let ((a '("\\.erl\\'" . erlang-mode))
+ (b '("\\.hrl\\'" . erlang-mode)))
+ (or (assoc (car a) auto-mode-alist)
+ (setq auto-mode-alist (cons a auto-mode-alist)))
+ (or (assoc (car b) auto-mode-alist)
+ (setq auto-mode-alist (cons b auto-mode-alist))))
+
+
+;;
+;; Ignore files ending in ".jam", ".vee", and ".beam" when performing
+;; file completion.
+;;
+
+(let ((erl-ext '(".jam" ".vee" ".beam")))
+ (while erl-ext
+ (let ((cie completion-ignored-extensions))
+ (while (and cie (not (string-equal (car cie) (car erl-ext))))
+ (setq cie (cdr cie)))
+ (if (null cie)
+ (setq completion-ignored-extensions
+ (cons (car erl-ext) completion-ignored-extensions))))
+ (setq erl-ext (cdr erl-ext))))
+
+
+;;
+;; The end.
+;;
+
+(provide 'erlang-start)
+
+;; erlang-start.el ends here.
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
new file mode 100644
index 0000000000..f623e3a1ee
--- /dev/null
+++ b/lib/tools/emacs/erlang.el
@@ -0,0 +1,6651 @@
+;; erlang.el --- Major modes for editing and running Erlang
+;; %CopyrightBegin%
+;;
+;; Copyright Ericsson AB 1996-2009. All Rights Reserved.
+;;
+;; The contents of this file are subject to the Erlang Public License,
+;; Version 1.1, (the "License"); you may not use this file except in
+;; compliance with the License. You should have received a copy of the
+;; Erlang Public License along with this software. If not, it can be
+;; retrieved online at http://www.erlang.org/.
+;;
+;; Software distributed under the License is distributed on an "AS IS"
+;; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+;; the License for the specific language governing rights and limitations
+;; under the License.
+;;
+;; %CopyrightEnd%
+;;
+;; Copyright (C) 2004 Free Software Foundation, Inc.
+;; Author: Anders Lindgren
+;; Keywords: erlang, languages, processes
+
+;; Lars Thors�n's modifications of 2000-06-07 included.
+;; The original version of this package was written by Robert Virding.
+;;
+;;; Commentary:
+
+;; Introduction:
+;; ------------
+;;
+;; This package provides support for the programming language Erlang.
+;; The package provides an editing mode with lots of bells and
+;; whistles, compilation support, and it makes it possible for the
+;; user to start Erlang shells that run inside Emacs.
+;;
+;; See the Erlang distribution for full documentation of this package.
+
+;; Installation:
+;; ------------
+;;
+;; Place this file in Emacs load path, byte-compile it, and add the
+;; following line to the appropriate init file:
+;;
+;; (require 'erlang-start)
+;;
+;; The full documentation contains much more extensive description of
+;; the installation procedure.
+
+;; Reporting Bugs:
+;; --------------
+;;
+;; Please send bug reports to the following email address:
+;; or if you have a patch suggestion to:
+;; Please state as exactly as possible:
+;; - Version number of Erlang Mode (see the menu), Emacs, Erlang,
+;; and of any other relevant software.
+;; - What the expected result was.
+;; - What you did, preferably in a repeatable step-by-step form.
+;; - A description of the unexpected result.
+;; - Relevant pieces of Erlang code causing the problem.
+;; - Personal Emacs customisations, if any.
+;;
+;; Should the Emacs generate an error, please set the Emacs variable
+;; `debug-on-error' to `t'. Repeat the error and enclose the debug
+;; information in your bug-report.
+;;
+;; To set the variable you can use the following command:
+;; M-x set-variable RET debug-on-error RET t RET
+;;; Code:
+
+;; Variables:
+
+(defconst erlang-version "2.6.1"
+ "The version number of Erlang mode.")
+
+(defvar erlang-root-dir nil
+ "The directory where the Erlang system is installed.
+The name should not contain the trailing slash.
+
+Should this variable be nil, no manual pages will show up in the
+Erlang mode menu.")
+
+(eval-and-compile
+ (defconst erlang-emacs-major-version
+ (if (boundp 'emacs-major-version)
+ emacs-major-version
+ (string-match "\\([0-9]+\\)\\.\\([0-9]+\\)" emacs-version)
+ (erlang-string-to-int (substring emacs-version
+ (match-beginning 1) (match-end 1))))
+ "Major version number of Emacs."))
+
+(eval-and-compile
+ (defconst erlang-emacs-minor-version
+ (if (boundp 'emacs-minor-version)
+ emacs-minor-version
+ (string-match "\\([0-9]+\\)\\.\\([0-9]+\\)" emacs-version)
+ (erlang-string-to-int (substring emacs-version
+ (match-beginning 2) (match-end 2))))
+ "Minor version number of Emacs."))
+
+(defconst erlang-xemacs-p (string-match "Lucid\\|XEmacs" emacs-version)
+ "Non-nil when running under XEmacs or Lucid Emacs.")
+
+(defvar erlang-xemacs-popup-menu '("Erlang Mode Commands" . nil)
+ "Common popup menu for all buffers in Erlang mode.
+
+This variable is destructively modified every time the Erlang menu
+is modified. The effect is that all changes take effect in all
+buffers in Erlang mode, just like under GNU Emacs.
+
+Never EVER set this variable!")
+
+(defvar erlang-menu-items '(erlang-menu-base-items
+ erlang-menu-skel-items
+ erlang-menu-shell-items
+ erlang-menu-compile-items
+ erlang-menu-man-items
+ erlang-menu-personal-items
+ erlang-menu-version-items)
+ "*List of menu item list to combine to create Erlang mode menu.
+
+External programs which temporarily add menu items to the Erlang mode
+menu may use this variable. Please use the function `add-hook' to add
+items.
+
+Please call the function `erlang-menu-init' after every change to this
+variable.")
+
+(defvar erlang-menu-base-items
+ '(("Indent"
+ (("Indent Line" erlang-indent-command)
+ ("Indent Region " erlang-indent-region
+ (if erlang-xemacs-p (mark) mark-active))
+ ("Indent Clause" erlang-indent-clause)
+ ("Indent Function" erlang-indent-function)
+ ("Indent Buffer" erlang-indent-current-buffer)))
+ ("Edit"
+ (("Fill Comment" erlang-fill-paragraph)
+ ("Comment Region" comment-region
+ (if erlang-xemacs-p (mark) mark-active))
+ ("Uncomment Region" erlang-uncomment-region
+ (if erlang-xemacs-p (mark) mark-active))
+ nil
+ ("Beginning of Function" erlang-beginning-of-function)
+ ("End of Function" erlang-end-of-function)
+ ("Mark Function" erlang-mark-function)
+ nil
+ ("Beginning of Clause" erlang-beginning-of-clause)
+ ("End of Clause" erlang-end-of-clause)
+ ("Mark Clause" erlang-mark-clause)
+ nil
+ ("New Clause" erlang-generate-new-clause)
+ ("Clone Arguments" erlang-clone-arguments)
+ nil
+ ("Align Arrows" erlang-align-arrows)))
+ ("Syntax Highlighting"
+ (("Level 4" erlang-font-lock-level-4)
+ ("Level 3" erlang-font-lock-level-3)
+ ("Level 2" erlang-font-lock-level-2)
+ ("Level 1" erlang-font-lock-level-1)
+ ("Off" erlang-font-lock-level-0)))
+ ("TAGS"
+ (("Find Tag" find-tag)
+ ("Find Next Tag" erlang-find-next-tag)
+ ;("Find Regexp" find-tag-regexp)
+ ("Complete Word" erlang-complete-tag)
+ ("Tags Apropos" tags-apropos)
+ ("Search Files" tags-search))))
+ "Description of menu used in Erlang mode.
+
+This variable must be a list. The elements are either nil representing
+a horizontal line or a list with two or three elements. The first is
+the name of the menu item, the second is the function to call, or a
+submenu, on the same same form as ITEMS. The third optional argument
+is an expression which is evaluated every time the menu is displayed.
+Should the expression evaluate to nil the menu item is ghosted.
+
+Example:
+ '((\"Func1\" function-one)
+ (\"SubItem\"
+ ((\"Yellow\" function-yellow)
+ (\"Blue\" function-blue)))
+ nil
+ (\"Region Function\" spook-function midnight-variable))
+
+Call the function `erlang-menu-init' after modifying this variable.")
+
+(defvar erlang-menu-shell-items
+ '(nil
+ ("Shell"
+ (("Start New Shell" erlang-shell)
+ ("Display Shell" erlang-shell-display))))
+ "Description of the Shell menu used by Erlang mode.
+
+Please see the documentation of `erlang-menu-base-items'.")
+
+(defvar erlang-menu-compile-items
+ '(("Compile"
+ (("Compile Buffer" erlang-compile)
+ ("Display Result" erlang-compile-display)
+ ("Next Error" erlang-next-error))))
+ "Description of the Compile menu used by Erlang mode.
+
+Please see the documentation of `erlang-menu-base-items'.")
+
+(defvar erlang-menu-version-items
+ '(nil
+ ("Version" erlang-version))
+ "Description of the version menu used in Erlang mode.")
+
+(defvar erlang-menu-personal-items nil
+ "Description of personal menu items used in Erlang mode.
+
+Please see the variable `erlang-menu-base-items' for a description
+of the format.")
+
+(defvar erlang-menu-man-items nil
+ "The menu containing man pages.
+
+The format of the menu should be compatible with `erlang-menu-base-items'.
+This variable is added to the list of Erlang menus stored in
+`erlang-menu-items'.")
+
+(defvar erlang-menu-skel-items '()
+ "Description of the menu containing the skeleton entries.
+The menu is in the form described by the variable `erlang-menu-base-items'.")
+
+(defvar erlang-mode-hook nil
+ "*Functions to run when Erlang mode is activated.
+
+This hook is used to change the behaviour of Erlang mode. It is
+normally used by the user to personalise the programming environment.
+When used in a site init file, it could be used to customise Erlang
+mode for all users on the system.
+
+The functions added to this hook are run every time Erlang mode is
+started. See also `erlang-load-hook', a hook which is run once,
+when Erlang mode is loaded into Emacs, and `erlang-shell-mode-hook'
+which is run every time a new inferior Erlang shell is started.
+
+To use a hook, create an Emacs lisp function to perform your actions
+and add the function to the hook by calling `add-hook'.
+
+The following example binds the key sequence C-c C-c to the command
+`erlang-compile' (normally bound to C-c C-k). The example also
+activates Font Lock mode to fontify the buffer and adds a menu
+containing all functions defined in the current buffer.
+
+To use the example, copy the following lines to your `~/.emacs' file:
+
+ (add-hook 'erlang-mode-hook 'my-erlang-mode-hook)
+
+ (defun my-erlang-mode-hook ()
+ (local-set-key \"\\C-c\\C-c\" 'erlang-compile)
+ (if window-system
+ (progn
+ (setq font-lock-maximum-decoration t)
+ (font-lock-mode 1)))
+ (if (and window-system (fboundp 'imenu-add-to-menubar))
+ (imenu-add-to-menubar \"Imenu\")))")
+
+(defvar erlang-load-hook nil
+ "*Functions to run when Erlang mode is loaded.
+
+This hook is used to change the behaviour of Erlang mode. It is
+normally used by the user to personalise the programming environment.
+When used in a site init file, it could be used to customize Erlang
+mode for all users on the system.
+
+The difference between this hook and `erlang-mode-hook' and
+`erlang-shell-mode-hook' is that the functions in this hook
+is only called once, when the Erlang mode is loaded into Emacs
+the first time.
+
+Natural actions for the functions added to this hook are actions which
+only should be performed once, and actions which should be performed
+before starting Erlang mode. For example, a number of variables are
+used by Erlang mode before `erlang-mode-hook' is run.
+
+The following example sets the variable `erlang-root-dir' so that the
+manual pages can be retrieved (note that you must set the value of
+`erlang-root-dir' to match the location of Erlang on your system):
+
+ (add-hook 'erlang-load-hook 'my-erlang-load-hook)
+
+ (defun my-erlang-load-hook ()
+ (setq erlang-root-dir \"/usr/local/erlang\"))")
+
+(defvar erlang-new-file-hook nil
+ "Functions to run when a new Erlang source file is being edited.
+
+A useful function is `tempo-template-erlang-normal-header'.
+\(This function only exists when the `tempo' package is available.)")
+
+(defvar erlang-check-module-name 'ask
+ "*Non-nil means check that module name and file name agrees when saving.
+
+If the value of this variable is the atom `ask', the user is
+prompted. If the value is t the source is silently changed.")
+
+(defvar erlang-electric-commands
+ '(erlang-electric-comma
+ erlang-electric-semicolon
+ erlang-electric-gt)
+ "*List of activated electric commands.
+
+The list should contain the electric commands which should be active.
+Currently, the available electric commands are:
+ `erlang-electric-comma'
+ `erlang-electric-semicolon'
+ `erlang-electric-gt'
+ `erlang-electric-newline'
+
+Should the variable be bound to t, all electric commands
+are activated.
+
+To deactivate all electric commands, set this variable to nil.")
+
+(defvar erlang-electric-newline-inhibit t
+ "*Set to non-nil to inhibit newline after electric command.
+
+This is useful since a lot of people press return after executing an
+electric command.
+
+In order to work, the command must also be in the
+list `erlang-electric-newline-inhibit-list'.
+
+Note that commands in this list are required to set the variable
+`erlang-electric-newline-inhibit' to nil when the newline shouldn't be
+inhibited.")
+
+(defvar erlang-electric-newline-inhibit-list
+ '(erlang-electric-semicolon
+ erlang-electric-comma
+ erlang-electric-gt)
+ "*Commands which can inhibit the next newline.")
+
+(defvar erlang-electric-semicolon-insert-blank-lines nil
+ "*Number of blank lines inserted before header, or nil.
+
+This variable controls the behaviour of `erlang-electric-semicolon'
+when a new function header is generated. When nil, no blank line is
+inserted between the current line and the new header. When bound to a
+number it represents the number of blank lines which should be
+inserted.")
+
+(defvar erlang-electric-semicolon-criteria
+ '(erlang-next-lines-empty-p
+ erlang-at-keyword-end-p
+ erlang-at-end-of-function-p)
+ "*List of functions controlling `erlang-electric-semicolon'.
+The functions in this list are called, in order, whenever a semicolon
+is typed. Each function in the list is called with no arguments,
+and should return one of the following values:
+
+ nil -- no determination made, continue checking
+ 'stop -- do not create prototype for next line
+ (anything else) -- insert prototype, and stop checking
+
+If every function in the list is called with no determination made,
+then no prototype is inserted.
+
+The test is performed by the function `erlang-test-criteria-list'.")
+
+(defvar erlang-electric-comma-criteria
+ '(erlang-stop-when-inside-argument-list
+ erlang-stop-when-at-guard
+ erlang-next-lines-empty-p
+ erlang-at-keyword-end-p
+ erlang-at-end-of-clause-p
+ erlang-at-end-of-function-p)
+ "*List of functions controlling `erlang-electric-comma'.
+The functions in this list are called, in order, whenever a comma
+is typed. Each function in the list is called with no arguments,
+and should return one of the following values:
+
+ nil -- no determination made, continue checking
+ 'stop -- do not create prototype for next line
+ (anything else) -- insert prototype, and stop checking
+
+If every function in the list is called with no determination made,
+then no prototype is inserted.
+
+The test is performed by the function `erlang-test-criteria-list'.")
+
+(defvar erlang-electric-arrow-criteria
+ '(erlang-next-lines-empty-p
+ erlang-at-end-of-function-p)
+ "*List of functions controlling the arrow aspect of `erlang-electric-gt'.
+The functions in this list are called, in order, whenever a `>'
+is typed. Each function in the list is called with no arguments,
+and should return one of the following values:
+
+ nil -- no determination made, continue checking
+ 'stop -- do not create prototype for next line
+ (anything else) -- insert prototype, and stop checking
+
+If every function in the list is called with no determination made,
+then no prototype is inserted.
+
+The test is performed by the function `erlang-test-criteria-list'.")
+
+(defvar erlang-electric-newline-criteria
+ '(t)
+ "*List of functions controlling `erlang-electric-newline'.
+
+The electric newline commands indents the next line. Should the
+current line begin with a comment the comment start is copied to
+the newly created line.
+
+The functions in this list are called, in order, whenever a comma
+is typed. Each function in the list is called with no arguments,
+and should return one of the following values:
+
+ nil -- no determination made, continue checking
+ 'stop -- do not create prototype for next line
+ (anything else) -- trigger the electric command.
+
+If every function in the list is called with no determination made,
+then no prototype is inserted. Should the atom t be a member of the
+list, it is treated as a function triggering the electric command.
+
+The test is performed by the function `erlang-test-criteria-list'.")
+
+(defvar erlang-next-lines-empty-threshold 2
+ "*Number of blank lines required to activate an electric command.
+
+Actually, this value controls the behaviour of the function
+`erlang-next-lines-empty-p' which normally is a member of the
+criteria lists controlling the electric commands. (Please see
+the variables `erlang-electric-semicolon-criteria' and
+`erlang-electric-comma-criteria'.)
+
+The variable is bound to a threshold value, a number, representing the
+number of lines which must be empty.
+
+Setting this variable to zero, electric commands will always be
+triggered by `erlang-next-lines-empty-p', unless inhibited by other
+rules.
+
+Should this variable be nil, `erlang-next-lines-empty-p' will never
+trigger an electric command. The same effect would be reached if the
+function `erlang-next-lines-empty-p' would be removed from the criteria
+lists.
+
+Note that even if `erlang-next-lines-empty-p' should not trigger an
+electric command, other functions in the criteria list could.")
+
+(defvar erlang-new-clause-with-arguments nil
+ "*Non-nil means that the arguments are cloned when a clause is generated.
+
+A new function header can be generated by calls to the function
+`erlang-generate-new-clause' and by use of the electric semicolon.")
+
+(defvar erlang-compile-use-outdir t
+ "*When nil, go to the directory containing source file when compiling.
+
+This is a workaround for a bug in the `outdir' option of compile. If the
+outdir is not in the current load path, Erlang doesn't load the object
+module after it has been compiled.
+
+To activate the workaround, place the following in your `~/.emacs' file:
+ (setq erlang-compile-use-outdir nil)")
+
+(defvar erlang-indent-level 4
+ "*Indentation of Erlang calls/clauses within blocks.")
+
+(defvar erlang-indent-guard 2
+ "*Indentation of Erlang guards.")
+
+(defvar erlang-argument-indent 2
+ "*Indentation of the first argument in a function call.
+When nil, indent to the column after the `(' of the
+function.")
+
+(defvar erlang-tab-always-indent t
+ "*Non-nil means TAB in Erlang mode should always re-indent the current line,
+regardless of where in the line point is when the TAB command is used.")
+
+(defvar erlang-error-regexp-alist
+ '(("^\\([^:( \t\n]+\\)[:(][ \t]*\\([0-9]+\\)[:) \t]" . (1 2)))
+ "*Patterns for matching Erlang errors.")
+
+(defvar erlang-man-inhibit (eq system-type 'windows-nt)
+ "Inhibit the creation of the Erlang Manual Pages menu.
+
+The Windows distribution of Erlang does not include man pages, hence
+there is no attempt to create the menu.")
+
+(defvar erlang-man-dirs
+ '(("Man - Commands" "/man/man1" t)
+ ("Man - Modules" "/man/man3" t)
+ ("Man - Files" "/man/man4" t)
+ ("Man - Applications" "/man/man6" t))
+ "*The man directories displayed in the Erlang menu.
+
+Each item in the list should be a list with three elements, the first
+the name of the menu, the second the directory, and the last a flag.
+Should the flag the nil, the directory is absolute, should it be non-nil
+the directory is relative to the variable `erlang-root-dir'.")
+
+(defvar erlang-man-max-menu-size 35
+ "*The maximum number of menu items in one menu allowed.")
+
+(defvar erlang-man-display-function 'erlang-man-display
+ "*Function used to display man page.
+
+The function is called with one argument, the name of the file
+containing the man page. Use this variable when the default
+function, `erlang-man-display', does not work on your system.")
+
+(defvar erlang-compile-extra-opts '()
+ "*Additional options to the compilation command.
+This is an elisp list of options. Each option can be either:
+- an atom
+- a dotted pair
+- a string
+Example: '(bin_opt_info (i . \"/path1/include\") (i . \"/path2/include\"))")
+
+(eval-and-compile
+ (defvar erlang-regexp-modern-p
+ (if (> erlang-emacs-major-version 21) t nil)
+ "Non-nil when this version of Emacs uses a modern version of regexp.
+Supporting \_< and \_> This is determined by checking the version of Emacs used."))
+
+(eval-and-compile
+ (defconst erlang-atom-quoted-regexp
+ "'\\(?:[^\\']\\|\\(?:\\\\.\\)\\)*'"
+ "Regexp describing a single-quoted atom"))
+
+(eval-and-compile
+ (defconst erlang-atom-regular-regexp
+ (if erlang-regexp-modern-p
+ "\\_<[[:lower:]]\\(?:\\sw\\|\\s_\\)*\\_>"
+ "\\<[[:lower:]]\\(?:\\sw\\|\\s_\\)*\\>")
+ "Regexp describing a regular (non-quoted) atom"))
+
+(eval-and-compile
+ (defconst erlang-atom-regexp
+ (concat "\\(" erlang-atom-quoted-regexp "\\|"
+ erlang-atom-regular-regexp "\\)")
+ "Regexp describing an Erlang atom."))
+
+(eval-and-compile
+ (defconst erlang-atom-regexp-matches 1
+ "Number of regexp parenthesis pairs in `erlang-atom-regexp'.
+
+This is used to determine parenthesis matches in complex regexps which
+contains `erlang-atom-regexp'."))
+
+
+(eval-and-compile
+ (defconst erlang-variable-regexp
+ (if erlang-regexp-modern-p
+ "\\_<\\([[:upper:]_]\\(?:\\sw\\|\\s_\\)*\\)\\_>"
+ "\\<\\([[:upper:]_]\\(?:\\sw\\|\\s_\\)*\\)\\>")
+ "Regexp which should match an Erlang variable.
+
+The regexp must be surrounded with a pair of regexp parentheses."))
+
+(eval-and-compile
+ (defconst erlang-variable-regexp-matches 1
+ "Number of regexp parenthesis pairs in `erlang-variable-regexp'.
+
+This is used to determine matches in complex regexps which contains
+`erlang-variable-regexp'."))
+
+
+(eval-and-compile
+ (defun erlang-regexp-opt (strings &optional paren)
+ "Like `regexp-opt', except if PAREN is `symbols', then the
+resulting regexp is surrounded by \\_< and \\_>."
+ (if (eq paren 'symbols)
+ (if erlang-regexp-modern-p
+ (concat "\\_<" (regexp-opt strings t) "\\_>")
+ (concat "\\<" (regexp-opt strings t) "\\>"))
+ (regexp-opt strings paren))))
+
+
+(eval-and-compile
+ (defvar erlang-keywords
+ '("after"
+ "begin"
+ "catch"
+ "case"
+ "cond"
+ "end"
+ "fun"
+ "if"
+ "let"
+ "of"
+ "query"
+ "receive"
+ "try"
+ "when")
+ "Erlang reserved keywords"))
+
+(eval-and-compile
+ (defconst erlang-keywords-regexp (erlang-regexp-opt erlang-keywords 'symbols)))
+
+(eval-and-compile
+ (defvar erlang-operators
+ '("and"
+ "andalso"
+ "band"
+ "bnot"
+ "bor"
+ "bsl"
+ "bsr"
+ "bxor"
+ "div"
+ "not"
+ "or"
+ "orelse"
+ "rem"
+ "xor")
+ "Erlang operators"))
+;; What about these?
+;; '+' '-' '*' '/' '>', '>=', '<', '=<', '=:=', '==', '=/=', '/='
+
+(eval-and-compile
+ (defconst erlang-operators-regexp (erlang-regexp-opt erlang-operators 'symbols)))
+
+
+(eval-and-compile
+ (defvar erlang-guards
+ '("is_atom"
+ "is_binary"
+ "is_bitstring"
+ "is_boolean"
+ "is_float"
+ "is_function"
+ "is_integer"
+ "is_list"
+ "is_number"
+ "is_pid"
+ "is_port"
+ "is_record"
+ "is_reference"
+ "is_tuple"
+ "atom"
+ "binary"
+ "bitstring"
+ "boolean"
+ ;;"float" ; Not included to avoid clashes with the bif float/1
+ "function"
+ "integer"
+ "list"
+ "number"
+ "pid"
+ "port"
+ "record"
+ "reference"
+ "tuple")
+ "Erlang guards"))
+
+(eval-and-compile
+ (defconst erlang-guards-regexp (erlang-regexp-opt erlang-guards 'symbols)))
+
+
+(eval-and-compile
+ (defvar erlang-predefined-types
+ '("any"
+ "arity"
+ "byte"
+ "char"
+ "cons"
+ "deep_string"
+ "maybe_improper_list"
+ "mfa"
+ "nil"
+ "none"
+ "non_neg_integer"
+ "nonempty_list"
+ "nonempty_improper_list"
+ "nonempty_maybe_improper_list"
+ "string"
+ "timeout")
+ "Erlang type specs types"))
+
+(eval-and-compile
+ (defconst erlang-predefined-types-regexp
+ (erlang-regexp-opt erlang-predefined-types 'symbols)))
+
+
+(eval-and-compile
+ (defvar erlang-int-bifs
+ '("abs"
+ "adler32"
+ "adler32_combine"
+ "alive"
+ "apply"
+ "atom_to_binary"
+ "atom_to_list"
+ "binary_to_atom"
+ "binary_to_existing_atom"
+ "binary_to_list"
+ "binary_to_term"
+ "bit_size"
+ "bitstring_to_list"
+ "byte_size"
+ "check_process_code"
+ "contact_binary"
+ "crc32"
+ "crc32_combine"
+ "date"
+ "decode_packet"
+ "delete_module"
+ "disconnect_node"
+ "element"
+ "erase"
+ "exit"
+ "float"
+ "float_to_list"
+ "garbage_collect"
+ "get"
+ "get_keys"
+ "group_leader"
+ "halt"
+ "hd"
+ "integer_to_list"
+ "internal_bif"
+ "iolist_size"
+ "iolist_to_binary"
+ "is_alive"
+ "is_atom"
+ "is_binary"
+ "is_bitstring"
+ "is_boolean"
+ "is_float"
+ "is_function"
+ "is_integer"
+ "is_list"
+ "is_number"
+ "is_pid"
+ "is_port"
+ "is_process_alive"
+ "is_record"
+ "is_reference"
+ "is_tuple"
+ "length"
+ "link"
+ "list_to_atom"
+ "list_to_binary"
+ "list_to_bitstring"
+ "list_to_existing_atom"
+ "list_to_float"
+ "list_to_integer"
+ "list_to_pid"
+ "list_to_tuple"
+ "load_module"
+ "make_ref"
+ "module_loaded"
+ "monitor_node"
+ "node"
+ "node_link"
+ "node_unlink"
+ "nodes"
+ "notalive"
+ "now"
+ "open_port"
+ "pid_to_list"
+ "port_close"
+ "port_command"
+ "port_connect"
+ "port_control"
+ "pre_loaded"
+ "process_flag"
+ "process_info"
+ "processes"
+ "purge_module"
+ "put"
+ "register"
+ "registered"
+ "round"
+ "self"
+ "setelement"
+ "size"
+ "spawn"
+ "spawn_link"
+ "spawn_monitor"
+ "spawn_opt"
+ "split_binary"
+ "statistics"
+ "term_to_binary"
+ "time"
+ "throw"
+ "tl"
+ "trunc"
+ "tuple_size"
+ "tuple_to_list"
+ "unlink"
+ "unregister"
+ "whereis")
+ "Erlang built-in functions (BIFs)"))
+
+(eval-and-compile
+ (defconst erlang-int-bif-regexp (erlang-regexp-opt erlang-int-bifs 'symbols)))
+
+
+(eval-and-compile
+ (defvar erlang-ext-bifs
+ '("append_element"
+ "bump_reductions"
+ "cancel_timer"
+ "demonitor"
+ "display"
+ "fun_info"
+ "fun_to_list"
+ "function_exported"
+ "get_cookie"
+ "get_stacktrace"
+ "hash"
+ "integer_to_list"
+ "is_builtin"
+ "list_to_integer"
+ "loaded"
+ "localtime"
+ "localtime_to_universaltime"
+ "make_tuple"
+ "max"
+ "md5"
+ "md5_final"
+ "md5_init"
+ "md5_update"
+ "memory"
+ "min"
+ "monitor"
+ "monitor_node"
+ "phash"
+ "phash2"
+ "port_call"
+ "port_info"
+ "port_to_list"
+ "ports"
+ "process_display"
+ "read_timer"
+ "ref_to_list"
+ "resume_process"
+ "send"
+ "send_after"
+ "send_nosuspend"
+ "set_cookie"
+ "start_timer"
+ "suspend_process"
+ "system_flag"
+ "system_info"
+ "system_monitor"
+ "system_profile"
+ "trace"
+ "trace_delivered"
+ "trace_info"
+ "trace_pattern"
+ "universaltime"
+ "universaltime_to_localtime"
+ "yield")
+ "Erlang built-in functions (BIFs) that needs erlang: prefix"))
+
+(eval-and-compile
+ (defconst erlang-ext-bif-regexp
+ (erlang-regexp-opt (append erlang-int-bifs erlang-ext-bifs) 'symbols)))
+
+
+(defvar erlang-defun-prompt-regexp (concat "^" erlang-atom-regexp "\\s *(")
+ "Regexp which should match beginning of a clause.")
+
+(defvar erlang-file-name-extension-regexp "\\.[eh]rl$"
+ "*Regexp which should match an Erlang file name.
+
+This regexp is used when an Erlang module name is extracted from the
+name of an Erlang source file.
+
+The regexp should only match the section of the file name which should
+be excluded from the module name.
+
+To match all files set this variable to \"\\\\(\\\\..*\\\\|\\\\)$\".
+The matches all except the extension. This is useful if the Erlang
+tags system should interpret tags on the form `module:tag' for
+files written in other languages than Erlang.")
+
+(defvar erlang-inferior-shell-split-window t
+ "*If non-nil, when starting an inferior shell, split windows.
+If nil, the inferior shell replaces the window. This is the traditional
+behaviour.")
+
+(defvar erlang-mode-map nil
+ "*Keymap used in Erlang mode.")
+(defvar erlang-mode-abbrev-table nil
+ "Abbrev table in use in Erlang-mode buffers.")
+(defvar erlang-mode-syntax-table nil
+ "Syntax table in use in Erlang-mode buffers.")
+
+(defconst inferior-erlang-use-cmm (boundp 'minor-mode-overriding-map-alist)
+ "Non-nil means use `compilation-minor-mode' in Erlang shell.")
+
+;; Tempo skeleton templates:
+
+(defvar erlang-tempo-tags nil
+ "Tempo tags for erlang mode")
+
+(defvar erlang-skel
+ '(("If" "if" erlang-skel-if)
+ ("Case" "case" erlang-skel-case)
+ ("Receive" "receive" erlang-skel-receive)
+ ("Receive After" "after" erlang-skel-receive-after)
+ ("Receive Loop" "loop" erlang-skel-receive-loop)
+ ("Module" "module" erlang-skel-module)
+ ("Author" "author" erlang-skel-author)
+ ()
+ ("Small Header" "small-header"
+ erlang-skel-small-header erlang-skel-header)
+ ("Normal Header" "normal-header"
+ erlang-skel-normal-header erlang-skel-header)
+ ("Large Header" "large-header"
+ erlang-skel-large-header erlang-skel-header)
+ ()
+ ("Small Server" "small-server"
+ erlang-skel-small-server erlang-skel-header)
+ ()
+ ("Application" "application"
+ erlang-skel-application erlang-skel-header)
+ ("Supervisor" "supervisor"
+ erlang-skel-supervisor erlang-skel-header)
+ ("supervisor_bridge" "supervisor-bridge"
+ erlang-skel-supervisor-bridge erlang-skel-header)
+ ("gen_server" "generic-server"
+ erlang-skel-generic-server erlang-skel-header)
+ ("gen_event" "gen-event"
+ erlang-skel-gen-event erlang-skel-header)
+ ("gen_fsm" "gen-fsm"
+ erlang-skel-gen-fsm erlang-skel-header)
+ ("Library module" "gen-lib"
+ erlang-skel-lib erlang-skel-header)
+ ("Corba callback" "gen-corba-cb"
+ erlang-skel-corba-callback erlang-skel-header)
+ ("Small Common Test suite" "ct-test-suite-s"
+ erlang-skel-ct-test-suite-s erlang-skel-header)
+ ("Large Common Test suite" "ct-test-suite-l"
+ erlang-skel-ct-test-suite-l erlang-skel-header)
+ ("Erlang TS test suite" "ts-test-suite"
+ erlang-skel-ts-test-suite erlang-skel-header)
+ )
+ "*Description of all skeleton templates.
+Both functions and menu entries will be created.
+
+Each entry in `erlang-skel' should be a list with three or four
+elements, or the empty list.
+
+The first element is the name which shows up in the menu. The second
+is the `tempo' identifier (The string \"erlang-\" will be added in
+front of it). The third is the skeleton descriptor, a variable
+containing `tempo' attributes as described in the function
+`tempo-define-template'. The optional fourth elements denotes a
+function which should be called when the menu is selected.
+
+Functions corresponding to every template will be created. The name
+of the function will be `tempo-template-erlang-X' where `X' is the
+tempo identifier as specified in the second argument of the elements
+in this list.
+
+A list with zero elements means that the a horizontal line should
+be placed in the menu.")
+
+;; In XEmacs `user-mail-address' returns "[email protected] (Foo Bar)" ARGH!
+;; What's wrong with that? RFC 822 says it's legal. [sverkerw]
+;; This needs to use the customized value. If that's not sane, things like
+;; add-log will lose anyhow. Avoid it if there _is_ a paren.
+(defvar erlang-skel-mail-address
+ (if (or (not user-mail-address) (string-match "(" user-mail-address))
+ (concat (user-login-name) "@"
+ (or (and (boundp 'mail-host-address)
+ mail-host-address)
+ (system-name)))
+ user-mail-address)
+ "Mail address of the user.")
+
+;; Expression templates:
+(defvar erlang-skel-case
+ '((erlang-skel-skip-blank) o >
+ "case " p " of" n> p "_ ->" n> p "ok" n> "end" p)
+ "*The skeleton of a `case' expression.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-if
+ '((erlang-skel-skip-blank) o >
+ "if" n> p " ->" n> p "ok" n> "end" p)
+ "The skeleton of an `if' expression.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-receive
+ '((erlang-skel-skip-blank) o >
+ "receive" n> p "_ ->" n> p "ok" n> "end" p)
+ "*The skeleton of a `receive' expression.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-receive-after
+ '((erlang-skel-skip-blank) o >
+ "receive" n> p "_ ->" n> p "ok" n> "after " p "T ->" n>
+ p "ok" n> "end" p)
+ "*The skeleton of a `receive' expression with an `after' clause.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-receive-loop
+ '(& o "loop(" p ") ->" n> "receive" n> p "_ ->" n>
+ "loop(" p ")" n> "end.")
+ "*The skeleton of a simple `receive' loop.
+Please see the function `tempo-define-template'.")
+
+
+;; Attribute templates
+
+(defvar erlang-skel-module
+ '(& "-module("
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name))
+ ")." n)
+ "*The skeleton of a `module' attribute.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-author
+ '(& "-author('" erlang-skel-mail-address "')." n)
+ "*The skeleton of a `author' attribute.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-vc nil
+ "*The skeleton template to generate a version control attribute.
+The default is to insert nothing. Example of usage:
+
+ (setq erlang-skel-vc '(& \"-rcs(\\\"$\Id: $ \\\").\") n)
+
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-export
+ '(& "-export([" n> "])." n)
+ "*The skeleton of an `export' attribute.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-import
+ '(& "%%-import(Module, [Function/Arity, ...])." n)
+ "*The skeleton of an `import' attribute.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-compile nil
+ ;; '(& "%%-compile(export_all)." n)
+ "*The skeleton of a `compile' attribute.
+Please see the function `tempo-define-template'.")
+
+
+;; Comment templates.
+
+(defvar erlang-skel-date-function 'erlang-skel-dd-mmm-yyyy
+ "*Function which returns date string.
+Look in the module `time-stamp' for a battery of functions.")
+
+(defvar erlang-skel-copyright-comment '()
+ "*The template for a copyright line in the header, normally empty.
+This variable should be bound to a `tempo' template, for example:
+ '(& \"%%% Copyright (C) 2000, Yoyodyne, Inc.\" n)
+
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-created-comment
+ '(& "%%% Created : " (funcall erlang-skel-date-function) " by "
+ (user-full-name) " <" erlang-skel-mail-address ">" n)
+ "*The template for the \"Created:\" comment line.")
+
+(defvar erlang-skel-author-comment
+ '(& "%%% Author : " (user-full-name) " <" erlang-skel-mail-address ">" n)
+ "*The template for creating the \"Author:\" line in the header.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-file-comment
+ '(& "%%% File : " (file-name-nondirectory buffer-file-name) n)
+"*The template for creating the \"Module:\" line in the header.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-small-header
+ '(o (erlang-skel-include erlang-skel-module)
+ ;; erlang-skel-author)
+ n
+ (erlang-skel-include erlang-skel-compile
+ ;; erlang-skel-export
+ erlang-skel-vc))
+ "*The template of a small header without any comments.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-normal-header
+ '(o (erlang-skel-include erlang-skel-copyright-comment
+ erlang-skel-file-comment
+ erlang-skel-author-comment)
+ "%%% Description : " p n
+ (erlang-skel-include erlang-skel-created-comment) n
+ (erlang-skel-include erlang-skel-small-header) n)
+ "*The template of a normal header.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-large-header
+ '(o (erlang-skel-separator)
+ (erlang-skel-include erlang-skel-copyright-comment
+ erlang-skel-file-comment
+ erlang-skel-author-comment)
+ "%%% Description : " p n
+ "%%%" n
+ (erlang-skel-include erlang-skel-created-comment)
+ (erlang-skel-separator)
+ (erlang-skel-include erlang-skel-small-header) )
+ "*The template of a large header.
+Please see the function `tempo-define-template'.")
+
+
+;; Server templates.
+
+(defvar erlang-skel-small-server
+ '((erlang-skel-include erlang-skel-large-header)
+ "-export([start/0,init/1])." n n n
+ "start() ->" n> "spawn(" (erlang-get-module-from-file-name)
+ ", init, [self()])." n n
+ "init(From) ->" n>
+ "loop(From)." n n
+ "loop(From) ->" n>
+ "receive" n>
+ p "_ ->" n>
+ "loop(From)" n>
+ "end."
+ )
+ "*Template of a small server.
+Please see the function `tempo-define-template'.")
+
+;; Behaviour templates.
+
+(defvar erlang-skel-application
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(application)." n n
+ "%% Application callbacks" n
+ "-export([start/2, stop/1])." n n
+ (erlang-skel-double-separator 2)
+ "%% Application callbacks" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Function: start(Type, StartArgs) -> {ok, Pid} |" n
+ "%% {ok, Pid, State} |" n
+ "%% {error, Reason}" n
+ "%% Description: This function is called whenever an application " n
+ "%% is started using application:start/1,2, and should start the processes" n
+ "%% of the application. If the application is structured according to the" n
+ "%% OTP design principles as a supervision tree, this means starting the" n
+ "%% top supervisor of the tree." n
+ (erlang-skel-separator 2)
+ "start(_Type, StartArgs) ->" n>
+ "case 'TopSupervisor':start_link(StartArgs) of" n>
+ "{ok, Pid} -> " n>
+ "{ok, Pid};" n>
+ "Error ->" n>
+ "Error" n>
+ "end." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: stop(State) -> void()" n
+ "%% Description: This function is called whenever an application" n
+ "%% has stopped. It is intended to be the opposite of Module:start/2 and" n
+ "%% should do any necessary cleaning up. The return value is ignored. "n
+ (erlang-skel-separator 2)
+ "stop(_State) ->" n>
+ "ok." n
+ n
+ (erlang-skel-double-separator 2)
+ "%% Internal functions" n
+ (erlang-skel-double-separator 2)
+ )
+ "*The template of an application behaviour.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-supervisor
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(supervisor)." n n
+
+ "%% API" n
+ "-export([start_link/0])." n n
+
+ "%% Supervisor callbacks" n
+ "-export([init/1])." n n
+
+ "-define(SERVER, ?MODULE)." n n
+
+ (erlang-skel-double-separator 2)
+ "%% API functions" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}" n
+ "%% Description: Starts the supervisor" n
+ (erlang-skel-separator 2)
+ "start_link() ->" n>
+ "supervisor:start_link({local, ?SERVER}, ?MODULE, [])." n
+ n
+ (erlang-skel-double-separator 2)
+ "%% Supervisor callbacks" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Func: init(Args) -> {ok, {SupFlags, [ChildSpec]}} |" n
+ "%% ignore |" n
+ "%% {error, Reason}" n
+ "%% Description: Whenever a supervisor is started using "n
+ "%% supervisor:start_link/[2,3], this function is called by the new process "n
+ "%% to find out about restart strategy, maximum restart frequency and child "n
+ "%% specifications." n
+ (erlang-skel-separator 2)
+ "init([]) ->" n>
+ "AChild = {'AName',{'AModule',start_link,[]}," n>
+ "permanent,2000,worker,['AModule']}," n>
+ "{ok,{{one_for_all,0,1}, [AChild]}}." n
+ n
+ (erlang-skel-double-separator 2)
+ "%% Internal functions" n
+ (erlang-skel-double-separator 2)
+ )
+ "*The template of an supervisor behaviour.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-supervisor-bridge
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(supervisor_bridge)." n n
+
+ "%% API" n
+ "-export([start_link/0])." n n
+
+ "%% supervisor_bridge callbacks" n
+ "-export([init/1, terminate/2])." n n
+
+ "-define(SERVER, ?MODULE)." n n
+
+ "-record(state, {})." n n
+
+ (erlang-skel-double-separator 2)
+ "%% API" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}" n
+ "%% Description: Starts the supervisor bridge" n
+ (erlang-skel-separator 2)
+ "start_link() ->" n>
+ "supervisor_bridge:start_link({local, ?SERVER}, ?MODULE, [])." n
+ n
+ (erlang-skel-double-separator 2)
+ "%% supervisor_bridge callbacks" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Funcion: init(Args) -> {ok, Pid, State} |" n
+ "%% ignore |" n
+ "%% {error, Reason} " n
+ "%% Description:Creates a supervisor_bridge process, linked to the calling" n
+ "%% process, which calls Module:init/1 to start the subsystem. To ensure a" n
+ "%% synchronized start-up procedure, this function does not return until" n
+ "%% Module:init/1 has returned. " n
+ (erlang-skel-separator 2)
+ "init([]) ->" n>
+ "case 'AModule':start_link() of" n>
+ "{ok, Pid} ->" n>
+ "{ok, Pid, #state{}};" n>
+ "Error ->" n>
+ "Error" n>
+ "end." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: terminate(Reason, State) -> void()" n
+ "%% Description:This function is called by the supervisor_bridge when it is"n
+ "%% about to terminate. It should be the opposite of Module:init/1 and stop"n
+ "%% the subsystem and do any necessary cleaning up.The return value is ignored."
+ (erlang-skel-separator 2)
+ "terminate(Reason, State) ->" n>
+ "'AModule':stop()," n>
+ "ok." n
+ n
+ (erlang-skel-double-separator 2)
+ "%% Internal functions" n
+ (erlang-skel-double-separator 2)
+ )
+ "*The template of an supervisor_bridge behaviour.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-generic-server
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(gen_server)." n n
+
+ "%% API" n
+ "-export([start_link/0])." n n
+
+ "%% gen_server callbacks" n
+ "-export([init/1, handle_call/3, handle_cast/2, "
+ "handle_info/2," n>
+ "terminate/2, code_change/3])." n n
+
+ "-record(state, {})." n n
+
+ (erlang-skel-double-separator 2)
+ "%% API" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}" n
+ "%% Description: Starts the server" n
+ (erlang-skel-separator 2)
+ "start_link() ->" n>
+ "gen_server:start_link({local, ?SERVER}, ?MODULE, [], [])." n
+ n
+ (erlang-skel-double-separator 2)
+ "%% gen_server callbacks" n
+ (erlang-skel-double-separator 2)
+ n
+ (erlang-skel-separator 2)
+ "%% Function: init(Args) -> {ok, State} |" n
+ "%% {ok, State, Timeout} |" n
+ "%% ignore |" n
+ "%% {stop, Reason}" n
+ "%% Description: Initiates the server" n
+ (erlang-skel-separator 2)
+ "init([]) ->" n>
+ "{ok, #state{}}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: "
+ "%% handle_call(Request, From, State) -> {reply, Reply, State} |" n
+ "%% {reply, Reply, State, Timeout} |" n
+ "%% {noreply, State} |" n
+ "%% {noreply, State, Timeout} |" n
+ "%% {stop, Reason, Reply, State} |" n
+ "%% {stop, Reason, State}" n
+ "%% Description: Handling call messages" n
+ (erlang-skel-separator 2)
+ "handle_call(_Request, _From, State) ->" n>
+ "Reply = ok," n>
+ "{reply, Reply, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: handle_cast(Msg, State) -> {noreply, State} |" n
+ "%% {noreply, State, Timeout} |" n
+ "%% {stop, Reason, State}" n
+ "%% Description: Handling cast messages" n
+
+ (erlang-skel-separator 2)
+ "handle_cast(_Msg, State) ->" n>
+ "{noreply, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: handle_info(Info, State) -> {noreply, State} |" n
+ "%% {noreply, State, Timeout} |" n
+ "%% {stop, Reason, State}" n
+ "%% Description: Handling all non call/cast messages" n
+ (erlang-skel-separator 2)
+ "handle_info(_Info, State) ->" n>
+ "{noreply, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: terminate(Reason, State) -> void()" n
+ "%% Description: This function is called by a gen_server when it is about to"n
+ "%% terminate. It should be the opposite of Module:init/1 and do any necessary"n
+ "%% cleaning up. When it returns, the gen_server terminates with Reason." n
+ "%% The return value is ignored." n
+
+ (erlang-skel-separator 2)
+ "terminate(_Reason, _State) ->" n>
+ "ok." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}" n
+ "%% Description: Convert process state when code is changed" n
+ (erlang-skel-separator 2)
+ "code_change(_OldVsn, State, _Extra) ->" n>
+ "{ok, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%%% Internal functions" n
+ (erlang-skel-separator 2)
+ )
+ "*The template of a generic server.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-gen-event
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(gen_event)." n
+
+ "%% API" n
+ "-export([start_link/0, add_handler/0])." n n
+
+ "%% gen_event callbacks" n
+ "-export([init/1, handle_event/2, handle_call/2, " n>
+ "handle_info/2, terminate/2, code_change/3])." n n
+
+ "-record(state, {})." n n
+
+ (erlang-skel-double-separator 2)
+ "%% gen_event callbacks" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Function: start_link() -> {ok,Pid} | {error,Error} " n
+ "%% Description: Creates an event manager." n
+ (erlang-skel-separator 2)
+ "start_link() ->" n>
+ "gen_event:start_link({local, ?SERVER}). " n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: add_handler() -> ok | {'EXIT',Reason} | term()" n
+ "%% Description: Adds an event handler" n
+ (erlang-skel-separator 2)
+ "add_handler() ->" n>
+ "gen_event:add_handler(?SERVER, ?MODULE, [])." n
+ n
+ (erlang-skel-double-separator 2)
+ "%% gen_event callbacks" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Function: init(Args) -> {ok, State}" n
+ "%% Description: Whenever a new event handler is added to an event manager,"n
+ "%% this function is called to initialize the event handler." n
+ (erlang-skel-separator 2)
+ "init([]) ->" n>
+ "{ok, #state{}}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: "n
+ "%% handle_event(Event, State) -> {ok, State} |" n
+ "%% {swap_handler, Args1, State1, Mod2, Args2} |"n
+ "%% remove_handler" n
+ "%% Description:Whenever an event manager receives an event sent using"n
+ "%% gen_event:notify/2 or gen_event:sync_notify/2, this function is called for"n
+ "%% each installed event handler to handle the event. "n
+ (erlang-skel-separator 2)
+ "handle_event(_Event, State) ->" n>
+ "{ok, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: " n
+ "%% handle_call(Request, State) -> {ok, Reply, State} |" n
+ "%% {swap_handler, Reply, Args1, State1, "n
+ "%% Mod2, Args2} |" n
+ "%% {remove_handler, Reply}" n
+ "%% Description: Whenever an event manager receives a request sent using"n
+ "%% gen_event:call/3,4, this function is called for the specified event "n
+ "%% handler to handle the request."n
+ (erlang-skel-separator 2)
+ "handle_call(_Request, State) ->" n>
+ "Reply = ok," n>
+ "{ok, Reply, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: " n
+ "%% handle_info(Info, State) -> {ok, State} |" n
+ "%% {swap_handler, Args1, State1, Mod2, Args2} |" n
+ "%% remove_handler" n
+ "%% Description: This function is called for each installed event handler when"n
+ "%% an event manager receives any other message than an event or a synchronous"n
+ "%% request (or a system message)."n
+ (erlang-skel-separator 2)
+ "handle_info(_Info, State) ->" n>
+ "{ok, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: terminate(Reason, State) -> void()" n
+ "%% Description:Whenever an event handler is deleted from an event manager,"n
+ "%% this function is called. It should be the opposite of Module:init/1 and "n
+ "%% do any necessary cleaning up. " n
+ (erlang-skel-separator 2)
+ "terminate(_Reason, _State) ->" n>
+ "ok." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState} " n
+ "%% Description: Convert process state when code is changed" n
+ (erlang-skel-separator 2)
+ "code_change(_OldVsn, State, _Extra) ->" n>
+ "{ok, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%%% Internal functions" n
+ (erlang-skel-separator 2)
+ )
+ "*The template of a gen_event.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-gen-fsm
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(gen_fsm)." n n
+
+ "%% API" n
+ "-export([start_link/0])." n n
+
+ "%% gen_fsm callbacks" n
+ "-export([init/1, state_name/2, state_name/3, handle_event/3," n>
+ "handle_sync_event/4, handle_info/3, terminate/3, code_change/4])." n n
+
+ "-record(state, {})." n n
+
+ (erlang-skel-double-separator 2)
+ "%% API" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Function: start_link() -> ok,Pid} | ignore | {error,Error}" n
+ "%% Description:Creates a gen_fsm process which calls Module:init/1 to"n
+ "%% initialize. To ensure a synchronized start-up procedure, this function" n
+ "%% does not return until Module:init/1 has returned. " n
+ (erlang-skel-separator 2)
+ "start_link() ->" n>
+ "gen_fsm:start_link({local, ?SERVER}, ?MODULE, [], [])." n
+ n
+ (erlang-skel-double-separator 2)
+ "%% gen_fsm callbacks" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Function: init(Args) -> {ok, StateName, State} |" n
+ "%% {ok, StateName, State, Timeout} |" n
+ "%% ignore |" n
+ "%% {stop, StopReason} " n
+ "%% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or"n
+ "%% gen_fsm:start_link/3,4, this function is called by the new process to "n
+ "%% initialize. " n
+ (erlang-skel-separator 2)
+ "init([]) ->" n>
+ "{ok, state_name, #state{}}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: "n
+ "%% state_name(Event, State) -> {next_state, NextStateName, NextState}|" n
+ "%% {next_state, NextStateName, " n
+ "%% NextState, Timeout} |" n
+ "%% {stop, Reason, NewState}" n
+ "%% Description:There should be one instance of this function for each possible"n
+ "%% state name. Whenever a gen_fsm receives an event sent using" n
+ "%% gen_fsm:send_event/2, the instance of this function with the same name as"n
+ "%% the current state name StateName is called to handle the event. It is also "n
+ "%% called if a timeout occurs. " n
+ (erlang-skel-separator 2)
+ "state_name(_Event, State) ->" n>
+ "{next_state, state_name, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function:" n
+ "%% state_name(Event, From, State) -> {next_state, NextStateName, NextState} |"n
+ "%% {next_state, NextStateName, " n
+ "%% NextState, Timeout} |" n
+ "%% {reply, Reply, NextStateName, NextState}|"n
+ "%% {reply, Reply, NextStateName, " n
+ "%% NextState, Timeout} |" n
+ "%% {stop, Reason, NewState}|" n
+ "%% {stop, Reason, Reply, NewState}" n
+ "%% Description: There should be one instance of this function for each" n
+ "%% possible state name. Whenever a gen_fsm receives an event sent using" n
+ "%% gen_fsm:sync_send_event/2,3, the instance of this function with the same"n
+ "%% name as the current state name StateName is called to handle the event." n
+ (erlang-skel-separator 2)
+ "state_name(_Event, _From, State) ->" n>
+ "Reply = ok," n>
+ "{reply, Reply, state_name, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: " n
+ "%% handle_event(Event, StateName, State) -> {next_state, NextStateName, "n
+ "%% NextState} |" n
+ "%% {next_state, NextStateName, "n
+ "%% NextState, Timeout} |" n
+ "%% {stop, Reason, NewState}" n
+ "%% Description: Whenever a gen_fsm receives an event sent using"n
+ "%% gen_fsm:send_all_state_event/2, this function is called to handle"n
+ "%% the event." n
+ (erlang-skel-separator 2)
+ "handle_event(_Event, StateName, State) ->" n>
+ "{next_state, StateName, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: " n
+ "%% handle_sync_event(Event, From, StateName, "n
+ "%% State) -> {next_state, NextStateName, NextState} |" n
+ "%% {next_state, NextStateName, NextState, " n
+ "%% Timeout} |" n
+ "%% {reply, Reply, NextStateName, NextState}|" n
+ "%% {reply, Reply, NextStateName, NextState, " n
+ "%% Timeout} |" n
+ "%% {stop, Reason, NewState} |" n
+ "%% {stop, Reason, Reply, NewState}" n
+ "%% Description: Whenever a gen_fsm receives an event sent using"n
+ "%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle"n
+ "%% the event."n
+ (erlang-skel-separator 2)
+ "handle_sync_event(Event, From, StateName, State) ->" n>
+ "Reply = ok," n>
+ "{reply, Reply, StateName, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: " n
+ "%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}|" n
+ "%% {next_state, NextStateName, NextState, "n
+ "%% Timeout} |" n
+ "%% {stop, Reason, NewState}" n
+ "%% Description: This function is called by a gen_fsm when it receives any"n
+ "%% other message than a synchronous or asynchronous event"n
+ "%% (or a system message)." n
+ (erlang-skel-separator 2)
+ "handle_info(_Info, StateName, State) ->" n>
+ "{next_state, StateName, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: terminate(Reason, StateName, State) -> void()" n
+ "%% Description:This function is called by a gen_fsm when it is about"n
+ "%% to terminate. It should be the opposite of Module:init/1 and do any"n
+ "%% necessary cleaning up. When it returns, the gen_fsm terminates with"n
+ "%% Reason. The return value is ignored." n
+ (erlang-skel-separator 2)
+ "terminate(_Reason, _StateName, _State) ->" n>
+ "ok." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function:" n
+ "%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}" n
+ "%% Description: Convert process state when code is changed" n
+ (erlang-skel-separator 2)
+ "code_change(_OldVsn, StateName, State, _Extra) ->" n>
+ "{ok, StateName, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%%% Internal functions" n
+ (erlang-skel-separator 2)
+ )
+ "*The template of a gen_fsm.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-lib
+ '((erlang-skel-include erlang-skel-large-header)
+
+ "%% API" n
+ "-export([])." n n
+
+ (erlang-skel-double-separator 2)
+ "%% API" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Function: " n
+ "%% Description:" n
+ (erlang-skel-separator 2)
+ n
+ (erlang-skel-double-separator 2)
+ "%% Internal functions" n
+ (erlang-skel-double-separator 2)
+ )
+ "*The template of a library module.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-corba-callback
+ '((erlang-skel-include erlang-skel-large-header)
+ "%% Include files" n n
+
+ "%% API" n
+ "-export([])." n n
+
+ "%% Corba callbacks" n
+ "-export([init/1, terminate/2, code_change/3])." n n
+
+ "-record(state, {})." n n
+
+ (erlang-skel-double-separator 2)
+ "%% Corba callbacks" n
+ (erlang-skel-double-separator 2)
+ (erlang-skel-separator 2)
+ "%% Function: init(Args) -> {ok, State} |" n
+ "%% {ok, State, Timeout} |" n
+ "%% ignore |" n
+ "%% {stop, Reason}" n
+ "%% Description: Initiates the server" n
+ (erlang-skel-separator 2)
+ "init([]) ->" n>
+ "{ok, #state{}}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: terminate(Reason, State) -> void()" n
+ "%% Description: Shutdown the server" n
+ (erlang-skel-separator 2)
+ "terminate(_Reason, _State) ->" n>
+ "ok." n
+ n
+ (erlang-skel-separator 2)
+ "%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState} " n
+ "%% Description: Convert process state when code is changed" n
+ (erlang-skel-separator 2)
+ "code_change(_OldVsn, State, _Extra) ->" n>
+ "{ok, State}." n
+ n
+ (erlang-skel-double-separator 2)
+ "%% Internal functions" n
+ (erlang-skel-double-separator 2)
+ )
+ "*The template of a library module.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-ts-test-suite
+ '((erlang-skel-include erlang-skel-large-header)
+ "%% Note: This directive should only be used in test suites." n
+ "-compile(export_all)." n n
+
+ "-include(\"test_server.hrl\")." n n
+
+ (erlang-skel-separator 2)
+ "%% TEST SERVER CALLBACK FUNCTIONS" n
+ (erlang-skel-separator 2)
+ n
+ (erlang-skel-separator 2)
+ "%% Function: init_per_suite(Config0) -> Config1 | {skip,Reason}" n
+ "%%" n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% A list of key/value pairs, holding the test case configuration." n
+ "%% Reason = term()" n
+ "%% The reason for skipping the suite." n
+ "%%" n
+ "%% Description: Initialization before the suite." n
+ "%%" n
+ "%% Note: This function is free to add any key/value pairs to the Config" n
+ "%% variable, but should NOT alter/remove any existing entries." n
+ (erlang-skel-separator 2)
+ "init_per_suite(Config) ->" n >
+ "Config." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: end_per_suite(Config) -> void()" n
+ "%%" n
+ "%% Config = [tuple()]" n
+ "%% A list of key/value pairs, holding the test case configuration." n
+ "%%" n
+ "%% Description: Cleanup after the suite." n
+ (erlang-skel-separator 2)
+ "end_per_suite(_Config) ->" n >
+ "ok." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: init_per_testcase(TestCase, Config0) -> Config1 |" n
+ "%% {skip,Reason}" n
+ "%% TestCase = atom()" n
+ "%% Name of the test case that is about to run." n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% A list of key/value pairs, holding the test case configuration." n
+ "%% Reason = term()" n
+ "%% The reason for skipping the test case." n
+ "%%" n
+ "%% Description: Initialization before each test case." n
+ "%%" n
+ "%% Note: This function is free to add any key/value pairs to the Config" n
+ "%% variable, but should NOT alter/remove any existing entries." n
+ (erlang-skel-separator 2)
+ "init_per_testcase(_TestCase, Config) ->" n >
+ "Config." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: end_per_testcase(TestCase, Config) -> void()" n
+ "%%" n
+ "%% TestCase = atom()" n
+ "%% Name of the test case that is finished." n
+ "%% Config = [tuple()]" n
+ "%% A list of key/value pairs, holding the test case configuration." n
+ "%%" n
+ "%% Description: Cleanup after each test case." n
+ (erlang-skel-separator 2)
+ "end_per_testcase(_TestCase, _Config) ->" n >
+ "ok."n n
+
+ (erlang-skel-separator 2)
+ "%% Function: all(Clause) -> Descr | Spec | {skip,Reason}" n
+ "%%" n
+ "%% Clause = doc | suite" n
+ "%% Indicates expected return value." n
+ "%% Descr = [string()] | []" n
+ "%% String that describes the test suite." n
+ "%% Spec = [TestCase]" n
+ "%% A test specification." n
+ "%% TestCase = ConfCase | atom()" n
+ "%% Configuration case, or the name of a test case function." n
+ "%% ConfCase = {conf,Init,Spec,End} |" n
+ "%% {conf,Properties,Init,Spec,End}" n
+ "%% Init = End = {Mod,Func} | Func" n
+ "%% Initialization and cleanup function." n
+ "%% Mod = Func = atom()" n
+ "%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]" n
+ "%% Execution properties of the test cases (may be combined)." n
+ "%% Shuffle = shuffle | {shuffle,Seed}" n
+ "%% To get cases executed in random order." n
+ "%% Seed = {integer(),integer(),integer()}" n
+ "%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |" n
+ "%% repeat_until_any_ok | repeat_until_any_fail" n
+ "%% To get execution of cases repeated." n
+ "%% N = integer() | forever" n
+ "%% Reason = term()" n
+ "%% The reason for skipping the test suite." n
+ "%%" n
+ "%% Description: Returns a description of the test suite when" n
+ "%% Clause == doc, and a test specification (list" n
+ "%% of the conf and test cases in the suite) when" n
+ "%% Clause == suite." n
+ (erlang-skel-separator 2)
+ "all(doc) -> " n >
+ "[\"Describe the main purpose of this suite\"];" n n
+ "all(suite) -> " n >
+ "[a_test_case]." n n
+ n
+ (erlang-skel-separator 2)
+ "%% TEST CASES" n
+ (erlang-skel-separator 2)
+ n
+ (erlang-skel-separator 2)
+ "%% Function: TestCase(Arg) -> Descr | Spec | ok | exit() | {skip,Reason}" n
+ "%%" n
+ "%% Arg = doc | suite | Config" n
+ "%% Indicates expected behaviour and return value." n
+ "%% Config = [tuple()]" n
+ "%% A list of key/value pairs, holding the test case configuration." n
+ "%% Descr = [string()] | []" n
+ "%% String that describes the test case." n
+ "%% Spec = [tuple()] | []" n
+ "%% A test specification, see all/1." n
+ "%% Reason = term()" n
+ "%% The reason for skipping the test case." n
+ "%%" n
+ "%% Description: Test case function. Returns a description of the test" n
+ "%% case (doc), then returns a test specification (suite)," n
+ "%% or performs the actual test (Config)." n
+ (erlang-skel-separator 2)
+ "a_test_case(doc) -> " n >
+ "[\"Describe the main purpose of this test case\"];" n n
+ "a_test_case(suite) -> " n >
+ "[];" n n
+ "a_test_case(Config) when is_list(Config) -> " n >
+ "ok." n
+ )
+ "*The template of a library module.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-ct-test-suite-l
+ '((erlang-skel-include erlang-skel-large-header)
+ "%% Note: This directive should only be used in test suites." n
+ "-compile(export_all)." n n
+
+ "-include(\"ct.hrl\")." n n
+
+ (erlang-skel-separator 2)
+ "%% COMMON TEST CALLBACK FUNCTIONS" n
+ (erlang-skel-separator 2)
+ n
+ (erlang-skel-separator 2)
+ "%% Function: suite() -> Info" n
+ "%%" n
+ "%% Info = [tuple()]" n
+ "%% List of key/value pairs." n
+ "%%" n
+ "%% Description: Returns list of tuples to set default properties" n
+ "%% for the suite." n
+ "%%" n
+ "%% Note: The suite/0 function is only meant to be used to return" n
+ "%% default data values, not perform any other operations." n
+ (erlang-skel-separator 2)
+ "suite() ->" n >
+ "[{timetrap,{minutes,10}}]." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: init_per_suite(Config0) ->" n
+ "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n
+ "%%" n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% A list of key/value pairs, holding the test case configuration." n
+ "%% Reason = term()" n
+ "%% The reason for skipping the suite." n
+ "%%" n
+ "%% Description: Initialization before the suite." n
+ "%%" n
+ "%% Note: This function is free to add any key/value pairs to the Config" n
+ "%% variable, but should NOT alter/remove any existing entries." n
+ (erlang-skel-separator 2)
+ "init_per_suite(Config) ->" n >
+ "Config." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}" n
+ "%%" n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% A list of key/value pairs, holding the test case configuration." n
+ "%%" n
+ "%% Description: Cleanup after the suite." n
+ (erlang-skel-separator 2)
+ "end_per_suite(_Config) ->" n >
+ "ok." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: init_per_group(GroupName, Config0) ->" n
+ "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n
+ "%%" n
+ "%% GroupName = atom()" n
+ "%% Name of the test case group that is about to run." n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% A list of key/value pairs, holding configuration data for the group." n
+ "%% Reason = term()" n
+ "%% The reason for skipping all test cases and subgroups in the group." n
+ "%%" n
+ "%% Description: Initialization before each test case group." n
+ (erlang-skel-separator 2)
+ "init_per_group(_GroupName, Config) ->" n >
+ "Config." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: end_per_group(GroupName, Config0) ->" n
+ "%% void() | {save_config,Config1}" n
+ "%%" n
+ "%% GroupName = atom()" n
+ "%% Name of the test case group that is finished." n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% A list of key/value pairs, holding configuration data for the group." n
+ "%%" n
+ "%% Description: Cleanup after each test case group." n
+ (erlang-skel-separator 2)
+ "end_per_group(_GroupName, _Config) ->" n >
+ "ok." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: init_per_testcase(TestCase, Config0) ->" n
+ "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n
+ "%%" n
+ "%% TestCase = atom()" n
+ "%% Name of the test case that is about to run." n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% A list of key/value pairs, holding the test case configuration." n
+ "%% Reason = term()" n
+ "%% The reason for skipping the test case." n
+ "%%" n
+ "%% Description: Initialization before each test case." n
+ "%%" n
+ "%% Note: This function is free to add any key/value pairs to the Config" n
+ "%% variable, but should NOT alter/remove any existing entries." n
+ (erlang-skel-separator 2)
+ "init_per_testcase(_TestCase, Config) ->" n >
+ "Config." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: end_per_testcase(TestCase, Config0) ->" n
+ "%% void() | {save_config,Config1} | {fail,Reason}" n
+ "%%" n
+ "%% TestCase = atom()" n
+ "%% Name of the test case that is finished." n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% A list of key/value pairs, holding the test case configuration." n
+ "%% Reason = term()" n
+ "%% The reason for failing the test case." n
+ "%%" n
+ "%% Description: Cleanup after each test case." n
+ (erlang-skel-separator 2)
+ "end_per_testcase(_TestCase, _Config) ->" n >
+ "ok." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: groups() -> [Group]" n
+ "%%" n
+ "%% Group = {GroupName,Properties,GroupsAndTestCases}" n
+ "%% GroupName = atom()" n
+ "%% The name of the group." n
+ "%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]" n
+ "%% Group properties that may be combined." n
+ "%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]" n
+ "%% TestCase = atom()" n
+ "%% The name of a test case." n
+ "%% Shuffle = shuffle | {shuffle,Seed}" n
+ "%% To get cases executed in random order." n
+ "%% Seed = {integer(),integer(),integer()}" n
+ "%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |" n
+ "%% repeat_until_any_ok | repeat_until_any_fail" n
+ "%% To get execution of cases repeated." n
+ "%% N = integer() | forever" n
+ "%%" n
+ "%% Description: Returns a list of test case group definitions." n
+ (erlang-skel-separator 2)
+ "groups() ->" n >
+ "[]." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: all() -> GroupsAndTestCases | {skip,Reason}" n
+ "%%" n
+ "%% GroupsAndTestCases = [{group,GroupName} | TestCase]" n
+ "%% GroupName = atom()" n
+ "%% Name of a test case group." n
+ "%% TestCase = atom()" n
+ "%% Name of a test case." n
+ "%% Reason = term()" n
+ "%% The reason for skipping all groups and test cases." n
+ "%%" n
+ "%% Description: Returns the list of groups and test cases that" n
+ "%% are to be executed." n
+ (erlang-skel-separator 2)
+ "all() -> " n >
+ "[my_test_case]." n n
+
+ n
+ (erlang-skel-separator 2)
+ "%% TEST CASES" n
+ (erlang-skel-separator 2)
+ n
+
+ (erlang-skel-separator 2)
+ "%% Function: TestCase() -> Info" n
+ "%%" n
+ "%% Info = [tuple()]" n
+ "%% List of key/value pairs." n
+ "%%" n
+ "%% Description: Test case info function - returns list of tuples to set" n
+ "%% properties for the test case." n
+ "%%" n
+ "%% Note: This function is only meant to be used to return a list of" n
+ "%% values, not perform any other operations." n
+ (erlang-skel-separator 2)
+ "my_test_case() -> " n >
+ "[]." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: TestCase(Config0) ->" n
+ "%% ok | exit() | {skip,Reason} | {comment,Comment} |" n
+ "%% {save_config,Config1} | {skip_and_save,Reason,Config1}" n
+ "%%" n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% A list of key/value pairs, holding the test case configuration." n
+ "%% Reason = term()" n
+ "%% The reason for skipping the test case." n
+ "%% Comment = term()" n
+ "%% A comment about the test case that will be printed in the html log." n
+ "%%" n
+ "%% Description: Test case function. (The name of it must be specified in" n
+ "%% the all/0 list or in a test case group for the test case" n
+ "%% to be executed)." n
+ (erlang-skel-separator 2)
+ "my_test_case(_Config) -> " n >
+ "ok." n
+ )
+ "*The template of a library module.
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-ct-test-suite-s
+ '((erlang-skel-include erlang-skel-large-header)
+ "-compile(export_all)." n n
+
+ "-include(\"ct.hrl\")." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: suite() -> Info" n
+ "%% Info = [tuple()]" n
+ (erlang-skel-separator 2)
+ "suite() ->" n >
+ "[{timetrap,{seconds,30}}]." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: init_per_suite(Config0) ->" n
+ "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% Reason = term()" n
+ (erlang-skel-separator 2)
+ "init_per_suite(Config) ->" n >
+ "Config." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}" n
+ "%% Config0 = Config1 = [tuple()]" n
+ (erlang-skel-separator 2)
+ "end_per_suite(_Config) ->" n >
+ "ok." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: init_per_group(GroupName, Config0) ->" n
+ "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n
+ "%% GroupName = atom()" n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% Reason = term()" n
+ (erlang-skel-separator 2)
+ "init_per_group(_GroupName, Config) ->" n >
+ "Config." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: end_per_group(GroupName, Config0) ->" n
+ "%% void() | {save_config,Config1}" n
+ "%% GroupName = atom()" n
+ "%% Config0 = Config1 = [tuple()]" n
+ (erlang-skel-separator 2)
+ "end_per_group(_GroupName, _Config) ->" n >
+ "ok." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: init_per_testcase(TestCase, Config0) ->" n
+ "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n
+ "%% TestCase = atom()" n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% Reason = term()" n
+ (erlang-skel-separator 2)
+ "init_per_testcase(_TestCase, Config) ->" n >
+ "Config." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: end_per_testcase(TestCase, Config0) ->" n
+ "%% void() | {save_config,Config1} | {fail,Reason}" n
+ "%% TestCase = atom()" n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% Reason = term()" n
+ (erlang-skel-separator 2)
+ "end_per_testcase(_TestCase, _Config) ->" n >
+ "ok." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: groups() -> [Group]" n
+ "%% Group = {GroupName,Properties,GroupsAndTestCases}" n
+ "%% GroupName = atom()" n
+ "%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]" n
+ "%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]" n
+ "%% TestCase = atom()" n
+ "%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}" n
+ "%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |" n
+ "%% repeat_until_any_ok | repeat_until_any_fail" n
+ "%% N = integer() | forever" n
+ (erlang-skel-separator 2)
+ "groups() ->" n >
+ "[]." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: all() -> GroupsAndTestCases | {skip,Reason}" n
+ "%% GroupsAndTestCases = [{group,GroupName} | TestCase]" n
+ "%% GroupName = atom()" n
+ "%% TestCase = atom()" n
+ "%% Reason = term()" n
+ (erlang-skel-separator 2)
+ "all() -> " n >
+ "[my_test_case]." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: TestCase() -> Info" n
+ "%% Info = [tuple()]" n
+ (erlang-skel-separator 2)
+ "my_test_case() -> " n >
+ "[]." n n
+
+ (erlang-skel-separator 2)
+ "%% Function: TestCase(Config0) ->" n
+ "%% ok | exit() | {skip,Reason} | {comment,Comment} |" n
+ "%% {save_config,Config1} | {skip_and_save,Reason,Config1}" n
+ "%% Config0 = Config1 = [tuple()]" n
+ "%% Reason = term()" n
+ "%% Comment = term()" n
+ (erlang-skel-separator 2)
+ "my_test_case(_Config) -> " n >
+ "ok." n
+ )
+ "*The template of a library module.
+Please see the function `tempo-define-template'.")
+
+;; Font-lock variables
+
+;; The next few variables define different Erlang font-lock patterns.
+;; They could be appended to form a custom font-lock appearance.
+;;
+;; The function `erlang-font-lock-set-face' could be used to change
+;; the face of a pattern.
+;;
+;; Note that Erlang strings and atoms are highlighted with using
+;; syntactic analysis.
+
+(defvar erlang-font-lock-keywords-function-header
+ (list
+ (list (concat "^" erlang-atom-regexp "\\s-*(")
+ 1 'font-lock-function-name-face t))
+ "Font lock keyword highlighting a function header.")
+
+(defvar erlang-font-lock-keywords-int-bifs
+ (list
+ (list (concat erlang-int-bif-regexp "\\s-*(")
+ 1 'font-lock-builtin-face))
+ "Font lock keyword highlighting built in functions.")
+
+(defvar erlang-font-lock-keywords-ext-bifs
+ (list
+ (list (concat "\\<\\(erlang\\)\\s-*:\\s-*" erlang-ext-bif-regexp "\\s-*(")
+ '(1 'font-lock-builtin-face)
+ '(2 'font-lock-builtin-face)))
+ "Font lock keyword highlighting built in functions.")
+
+(defvar erlang-font-lock-keywords-int-function-calls
+ (list
+ (list (concat erlang-atom-regexp "\\s-*(")
+ 1 'font-lock-type-face))
+ "Font lock keyword highlighting an internal function call.")
+
+(defvar erlang-font-lock-keywords-ext-function-calls
+ (list
+ (list (concat erlang-atom-regexp "\\s-*:\\s-*"
+ erlang-atom-regexp "\\s-*(")
+ '(1 'font-lock-type-face)
+ '(2 'font-lock-type-face)))
+ "Font lock keyword highlighting an external function call.")
+
+(defvar erlang-font-lock-keywords-fun-n
+ (list
+ (list (concat "\\(" erlang-atom-regexp "/[0-9]+\\)")
+ 1 'font-lock-type-face))
+ "Font lock keyword highlighting a fun descriptor in F/N format.")
+
+(defvar erlang-font-lock-keywords-operators
+ (list
+ (list erlang-operators-regexp
+ 1 'font-lock-builtin-face))
+ "Font lock keyword highlighting Erlang operators.")
+
+(defvar erlang-font-lock-keywords-dollar
+ (list
+ (list "\\(\\$\\([^\\]\\|\\\\\\([^0-7^\n]\\|[0-7]+\\|\\^[a-zA-Z]\\)\\)\\)"
+ 1 'font-lock-constant-face))
+ "Font lock keyword highlighting numbers in ASCII form (e.g. $A).")
+
+(defvar erlang-font-lock-keywords-arrow
+ (list
+ (list "->\\(\\s \\|$\\)" 1 'font-lock-function-name-face))
+ "Font lock keyword highlighting clause arrow.")
+
+(defvar erlang-font-lock-keywords-lc
+ (list
+ (list "\\(<-\\|<=\\|||\\)\\(\\s \\|$\\)" 1 'font-lock-keyword-face))
+ "Font lock keyword highlighting list comprehension operators.")
+
+(defvar erlang-font-lock-keywords-keywords
+ (list
+ (list erlang-keywords-regexp 1 'font-lock-keyword-face))
+ "Font lock keyword highlighting Erlang keywords.")
+
+(defvar erlang-font-lock-keywords-attr
+ (list
+ (list (concat "^\\(-" erlang-atom-regexp "\\)\\(\\s-\\|\\.\\|(\\)")
+ 1 (if (boundp 'font-lock-preprocessor-face)
+ 'font-lock-preprocessor-face
+ 'font-lock-function-name-face)))
+ "Font lock keyword highlighting attributes.")
+
+(defvar erlang-font-lock-keywords-quotes
+ (list
+ (list "`\\([-+a-zA-Z0-9_:*][-+a-zA-Z0-9_:*]+\\)'"
+ 1
+ 'font-lock-keyword-face
+ t))
+ "Font lock keyword highlighting words in single quotes in comments.
+
+This is not the highlighting of Erlang strings and atoms, which
+are highlighted by syntactic analysis.")
+
+(defvar erlang-font-lock-keywords-guards
+ (list
+ (list (concat "[^:]" erlang-guards-regexp "\\s-*(")
+ 1 'font-lock-builtin-face))
+ "Font lock keyword highlighting guards.")
+
+(defvar erlang-font-lock-keywords-predefined-types
+ (list
+ (list (concat "[^:]" erlang-predefined-types-regexp "\\s-*(")
+ 1 'font-lock-builtin-face))
+ "Font lock keyword highlighting predefined types.")
+
+
+(defvar erlang-font-lock-keywords-macros
+ (list
+ (list (concat "?\\s-*\\(" erlang-atom-regexp
+ "\\|" erlang-variable-regexp "\\)")
+ 1 'font-lock-type-face)
+ (list (concat "^\\(-\\(?:define\\|ifn?def\\)\\)\\s-*(\\s-*\\(" erlang-atom-regexp
+ "\\|" erlang-variable-regexp "\\)")
+ (list 1 'font-lock-preprocessor-face t)
+ (list 3 'font-lock-type-face t t))
+ (list "^-e\\(lse\\|ndif\\)\\>" 0 'font-lock-preprocessor-face t))
+ "Font lock keyword highlighting macros.
+This must be placed in front of `erlang-font-lock-keywords-vars'.")
+
+(defvar erlang-font-lock-keywords-records
+ (list
+ (list (concat "#\\s *" erlang-atom-regexp)
+ 1 'font-lock-type-face)
+ ;; Don't highlight numerical constants.
+ (list (if erlang-regexp-modern-p
+ "\\_<[0-9]+#\\([0-9a-zA-Z]+\\)"
+ "\\<[0-9]+#\\([0-9a-zA-Z]+\\)")
+ 1 nil t)
+ (list (concat "^-record\\s-*(\\s-*" erlang-atom-regexp)
+ 1 'font-lock-type-face))
+ "Font lock keyword highlighting Erlang records.
+This must be placed in front of `erlang-font-lock-keywords-vars'.")
+
+(defvar erlang-font-lock-keywords-vars
+ (list
+ (list (concat "[^#]" erlang-variable-regexp) ; no numerical constants
+ 1 'font-lock-variable-name-face))
+ "Font lock keyword highlighting Erlang variables.
+Must be preceded by `erlang-font-lock-keywords-macros' to work properly.")
+
+(defvar erlang-font-lock-descr-string
+ "Font-lock keywords used by Erlang Mode.
+
+There exists three levels of Font Lock keywords for Erlang:
+ `erlang-font-lock-keywords-1' - Function headers and reserved keywords.
+ `erlang-font-lock-keywords-2' - Bifs, guards and `single quotes'.
+ `erlang-font-lock-keywords-3' - Variables, macros and records.
+ `erlang-font-lock-keywords-4' - Function names, Funs, LCs (not Atoms)
+
+To use a specific level, please set the variable
+`font-lock-maximum-decoration' to the appropriate level. Note that the
+variable must be set before Erlang mode is activated.
+
+Example:
+ (setq font-lock-maximum-decoration 2)")
+
+(defvar erlang-font-lock-keywords-1
+ (append erlang-font-lock-keywords-function-header
+ erlang-font-lock-keywords-dollar
+ erlang-font-lock-keywords-arrow
+ erlang-font-lock-keywords-keywords
+ )
+ ;; DocStringOrig: erlang-font-lock-keywords
+ erlang-font-lock-descr-string)
+
+(defvar erlang-font-lock-keywords-2
+ (append erlang-font-lock-keywords-1
+ erlang-font-lock-keywords-int-bifs
+ erlang-font-lock-keywords-ext-bifs
+ erlang-font-lock-keywords-attr
+ erlang-font-lock-keywords-quotes
+ erlang-font-lock-keywords-guards
+ )
+ ;; DocStringCopy: erlang-font-lock-keywords
+ erlang-font-lock-descr-string)
+
+(defvar erlang-font-lock-keywords-3
+ (append erlang-font-lock-keywords-2
+ erlang-font-lock-keywords-operators
+ erlang-font-lock-keywords-macros
+ erlang-font-lock-keywords-records
+ erlang-font-lock-keywords-vars
+ erlang-font-lock-keywords-predefined-types
+ )
+ ;; DocStringCopy: erlang-font-lock-keywords
+ erlang-font-lock-descr-string)
+
+(defvar erlang-font-lock-keywords-4
+ (append erlang-font-lock-keywords-3
+ erlang-font-lock-keywords-int-function-calls
+ erlang-font-lock-keywords-ext-function-calls
+ erlang-font-lock-keywords-fun-n
+ erlang-font-lock-keywords-lc
+ )
+ ;; DocStringCopy: erlang-font-lock-keywords
+ erlang-font-lock-descr-string)
+
+(defvar erlang-font-lock-keywords erlang-font-lock-keywords-4
+ ;; DocStringCopy: erlang-font-lock-keywords
+ erlang-font-lock-descr-string)
+
+(defvar erlang-font-lock-syntax-table nil
+ "Syntax table used by Font Lock mode.
+
+The difference between this and the standard Erlang Mode
+syntax table is that `_' is treated as part of words by
+this syntax table.
+
+Unfortunately, XEmacs hasn't got support for a special Font
+Lock syntax table. The effect is that `apply' in the atom
+`foo_apply' will be highlighted as a bif.")
+
+
+;;; Avoid errors while compiling this file.
+
+;; `eval-when-compile' is not defined in Emacs 18. We define it as a
+;; no-op.
+(or (fboundp 'eval-when-compile)
+ (defmacro eval-when-compile (&rest rest) nil))
+
+;; These umm...functions are new in Emacs 20. And, yes, until version
+;; 19.27 Emacs backquotes were this ugly.
+
+(or (fboundp 'unless)
+ (defmacro unless (condition &rest body)
+ "(unless CONDITION BODY...): If CONDITION is false, do BODY, else return nil."
+ `((if (, condition) nil ,@body))))
+
+(or (fboundp 'when)
+ (defmacro when (condition &rest body)
+ "(when CONDITION BODY...): If CONDITION is true, do BODY, else return nil."
+ `((if (, condition) (progn ,@body) nil))))
+
+(or (fboundp 'char-before)
+ (defmacro char-before (&optional pos)
+ "Return the character in the current buffer just before POS."
+ `( (char-after (1- (or ,pos (point)))))))
+
+;; defvar some obsolete variables, which we still support for
+;; backwardscompatibility reasons.
+(eval-when-compile
+ (defvar comment-indent-hook)
+ (defvar dabbrev-case-fold-search)
+ (defvar tempo-match-finder)
+ (defvar compilation-menu-map)
+ (defvar next-error-last-buffer))
+
+(eval-when-compile
+ (if (or (featurep 'bytecomp)
+ (featurep 'byte-compile))
+ (progn
+ (cond ((string-match "Lucid\\|XEmacs" emacs-version)
+ (put 'comment-indent-hook 'byte-obsolete-variable nil)
+ ;; Do not warn for unused variables
+ ;; when compiling under XEmacs.
+ (setq byte-compile-warnings
+ '(free-vars unresolved callargs redefine))))
+ (require 'comint)
+ (require 'tempo)
+ (require 'compile))))
+
+
+(defun erlang-version ()
+ "Return the current version of Erlang mode."
+ (interactive)
+ (if (interactive-p)
+ (message "Erlang mode version %s, written by Anders Lindgren"
+ erlang-version))
+ erlang-version)
+
+
+;;;###autoload
+(defun erlang-mode ()
+ "Major mode for editing Erlang source files in Emacs.
+It knows about syntax and comment, it can indent code, it is capable
+of fontifying the source file, the TAGS commands are aware of Erlang
+modules, and the Erlang man pages can be accessed.
+
+Should this module, \"erlang.el\", be installed properly, Erlang mode
+is activated whenever an Erlang source or header file is loaded into
+Emacs. To indicate this, the mode line should contain the word
+\"Erlang\".
+
+The main feature of Erlang mode is indentation, press TAB and the
+current line will be indented correctly.
+
+Comments starting with only one `%' are indented to the column stored
+in the variable `comment-column'. Comments starting with two `%':s
+are indented with the same indentation as code. Comments starting
+with at least three `%':s are indented to the first column.
+
+However, Erlang mode contains much more, this is a list of the most
+useful commands:
+ TAB - Indent the line.
+ C-c C-q - Indent current function.
+ M-; - Create a comment at the end of the line.
+ M-q - Fill a comment, i.e. wrap lines so that they (hopefully)
+ will look better.
+ M-a - Goto the beginning of an Erlang clause.
+ M-C-a - Ditto for function.
+ M-e - Goto the end of an Erlang clause.
+ M-C-e - Ditto for function.
+ M-h - Mark current Erlang clause.
+ M-C-h - Ditto for function.
+ C-c C-z - Start, or switch to, an inferior Erlang shell.
+ C-c C-k - Compile current file.
+ C-x ` - Next error.
+ , - Electric comma.
+ ; - Electric semicolon.
+
+Erlang mode check the name of the file against the module name when
+saving, whenever a mismatch occurs Erlang mode offers to modify the
+source.
+
+The variable `erlang-electric-commands' controls the electric
+commands. To deactivate all of them, set it to nil.
+
+There exists a large number of commands and variables in the Erlang
+module. Please press `M-x apropos RET erlang RET' to see a complete
+list. Press `C-h f name-of-function RET' and `C-h v name-of-variable
+RET'to see the full description of functions and variables,
+respectively.
+
+On entry to this mode the contents of the hook `erlang-mode-hook' is
+executed.
+
+Please see the beginning of the file `erlang.el' for more information
+and examples of hooks.
+
+Other commands:
+\\{erlang-mode-map}"
+ (interactive)
+ (kill-all-local-variables)
+ (setq major-mode 'erlang-mode)
+ (setq mode-name "Erlang")
+ (erlang-syntax-table-init)
+ (erlang-keymap-init)
+ (erlang-electric-init)
+ (erlang-menu-init)
+ (erlang-mode-variables)
+ (erlang-check-module-name-init)
+ (erlang-add-compilation-alist erlang-error-regexp-alist)
+ (erlang-man-init)
+ (erlang-tags-init)
+ (erlang-font-lock-init)
+ (erlang-skel-init)
+ (tempo-use-tag-list 'erlang-tempo-tags)
+ (run-hooks 'erlang-mode-hook)
+ (if (zerop (buffer-size))
+ (run-hooks 'erlang-new-file-hook))
+ ;; Doesn't exist in Emacs v21.4; required by Emacs v23.
+ (if (boundp 'after-change-major-mode-hook)
+ (run-hooks 'after-change-major-mode-hook)))
+
+
+(defun erlang-syntax-table-init ()
+ (if (null erlang-mode-syntax-table)
+ (let ((table (make-syntax-table)))
+ (modify-syntax-entry ?\n ">" table)
+ (modify-syntax-entry ?\" "\"" table)
+ (modify-syntax-entry ?# "." table)
+;; (modify-syntax-entry ?$ "\\" table) ;; Creates problems with indention afterwards
+;; (modify-syntax-entry ?$ "'" table) ;; Creates syntax highlighting and indention problems
+ (modify-syntax-entry ?$ "/" table) ;; Misses the corner case "string that ends with $"
+ ;; we have to live with that for now..it is the best alternative
+ ;; that can be worked around with "string hat ends with \$"
+ (modify-syntax-entry ?% "<" table)
+ (modify-syntax-entry ?& "." table)
+ (modify-syntax-entry ?\' "\"" table)
+ (modify-syntax-entry ?* "." table)
+ (modify-syntax-entry ?+ "." table)
+ (modify-syntax-entry ?- "." table)
+ (modify-syntax-entry ?/ "." table)
+ (modify-syntax-entry ?: "." table)
+ (modify-syntax-entry ?< "." table)
+ (modify-syntax-entry ?= "." table)
+ (modify-syntax-entry ?> "." table)
+ (modify-syntax-entry ?\\ "\\" table)
+ (modify-syntax-entry ?_ "_" table)
+ (modify-syntax-entry ?| "." table)
+ (modify-syntax-entry ?^ "'" table)
+
+ ;; Pseudo bit-syntax: Latin1 double angle quotes as parens.
+ ;;(modify-syntax-entry ?\253 "(?\273" table)
+ ;;(modify-syntax-entry ?\273 ")?\253" table)
+
+ (setq erlang-mode-syntax-table table)))
+
+ (set-syntax-table erlang-mode-syntax-table))
+
+
+(defun erlang-keymap-init ()
+ (if erlang-mode-map
+ nil
+ (setq erlang-mode-map (make-sparse-keymap))
+ (erlang-mode-commands erlang-mode-map))
+ (use-local-map erlang-mode-map))
+
+
+(defun erlang-mode-commands (map)
+ (unless (boundp 'indent-line-function)
+ (define-key map "\t" 'erlang-indent-command))
+ (define-key map ";" 'erlang-electric-semicolon)
+ (define-key map "," 'erlang-electric-comma)
+ (define-key map "<" 'erlang-electric-lt)
+ (define-key map ">" 'erlang-electric-gt)
+ (define-key map "\C-m" 'erlang-electric-newline)
+ (if (not (boundp 'delete-key-deletes-forward))
+ (define-key map "\177" 'backward-delete-char-untabify)
+ (define-key map [(backspace)] 'backward-delete-char-untabify))
+ ;;(unless (boundp 'fill-paragraph-function)
+ (define-key map "\M-q" 'erlang-fill-paragraph)
+ (unless (boundp 'beginning-of-defun-function)
+ (define-key map "\M-\C-a" 'erlang-beginning-of-function)
+ (define-key map "\M-\C-e" 'erlang-end-of-function)
+ (define-key map '(meta control h) 'erlang-mark-function)) ; Xemacs
+ (define-key map "\M-\t" 'erlang-complete-tag)
+ (define-key map "\C-c\M-\t" 'tempo-complete-tag)
+ (define-key map "\M-+" 'erlang-find-next-tag)
+ (define-key map "\C-c\M-a" 'erlang-beginning-of-clause)
+ (define-key map "\C-c\M-b" 'tempo-backward-mark)
+ (define-key map "\C-c\M-e" 'erlang-end-of-clause)
+ (define-key map "\C-c\M-f" 'tempo-forward-mark)
+ (define-key map "\C-c\M-h" 'erlang-mark-clause)
+ (define-key map "\C-c\C-c" 'comment-region)
+ (define-key map "\C-c\C-j" 'erlang-generate-new-clause)
+ (define-key map "\C-c\C-k" 'erlang-compile)
+ (define-key map "\C-c\C-l" 'erlang-compile-display)
+ (define-key map "\C-c\C-s" 'erlang-show-syntactic-information)
+ (define-key map "\C-c\C-q" 'erlang-indent-function)
+ (define-key map "\C-c\C-u" 'erlang-uncomment-region)
+ (define-key map "\C-c\C-y" 'erlang-clone-arguments)
+ (define-key map "\C-c\C-a" 'erlang-align-arrows)
+ (define-key map "\C-c\C-z" 'erlang-shell-display)
+ (unless inferior-erlang-use-cmm
+ (define-key map "\C-x`" 'erlang-next-error)))
+
+
+(defun erlang-electric-init ()
+ ;; Set up electric character functions to work with
+ ;; delsel/pending-del mode. Also, set up text properties for bit
+ ;; syntax handling.
+ (mapc #'(lambda (cmd)
+ (put cmd 'delete-selection t) ;for delsel (Emacs)
+ (put cmd 'pending-delete t)) ;for pending-del (XEmacs)
+ '(erlang-electric-semicolon
+ erlang-electric-comma
+ erlang-electric-gt))
+
+ (put 'bitsyntax-open-outer 'syntax-table '(4 . ?>))
+ (put 'bitsyntax-open-outer 'rear-nonsticky '(category))
+ (put 'bitsyntax-open-inner 'rear-nonsticky '(category))
+ (put 'bitsyntax-close-inner 'rear-nonsticky '(category))
+ (put 'bitsyntax-close-outer 'syntax-table '(5 . ?<))
+ (put 'bitsyntax-close-outer 'rear-nonsticky '(category))
+ (make-local-variable 'parse-sexp-lookup-properties)
+ (setq parse-sexp-lookup-properties 't))
+
+
+(defun erlang-mode-variables ()
+ (or erlang-mode-abbrev-table
+ (define-abbrev-table 'erlang-mode-abbrev-table ()))
+ (setq local-abbrev-table erlang-mode-abbrev-table)
+ (make-local-variable 'paragraph-start)
+ (setq paragraph-start (concat "^$\\|" page-delimiter))
+ (make-local-variable 'paragraph-separate)
+ (setq paragraph-separate paragraph-start)
+ (make-local-variable 'paragraph-ignore-fill-prefix)
+ (setq paragraph-ignore-fill-prefix t)
+ (make-local-variable 'require-final-newline)
+ (setq require-final-newline t)
+ (make-local-variable 'defun-prompt-regexp)
+ (setq defun-prompt-regexp erlang-defun-prompt-regexp)
+ (make-local-variable 'comment-start)
+ (setq comment-start "%")
+ (make-local-variable 'comment-start-skip)
+ (setq comment-start-skip "%+\\s *")
+ (make-local-variable 'comment-column)
+ (setq comment-column 48)
+ (make-local-variable 'indent-line-function)
+ (setq indent-line-function 'erlang-indent-command)
+ (make-local-variable 'indent-region-function)
+ (setq indent-region-function 'erlang-indent-region)
+ (set (make-local-variable 'comment-indent-function) 'erlang-comment-indent)
+ (if (<= erlang-emacs-major-version 18)
+ (set (make-local-variable 'comment-indent-hook) 'erlang-comment-indent))
+ (set (make-local-variable 'parse-sexp-ignore-comments) t)
+ (set (make-local-variable 'dabbrev-case-fold-search) nil)
+ (set (make-local-variable 'imenu-prev-index-position-function)
+ 'erlang-beginning-of-function)
+ (set (make-local-variable 'imenu-extract-index-name-function)
+ 'erlang-get-function-name)
+ (set (make-local-variable 'tempo-match-finder)
+ "[^-a-zA-Z0-9_]\\([-a-zA-Z0-9_]*\\)\\=")
+ (set (make-local-variable 'beginning-of-defun-function)
+ 'erlang-beginning-of-function)
+ (set (make-local-variable 'end-of-defun-function) 'erlang-end-of-function)
+ (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil)
+ (set (make-local-variable 'fill-paragraph-function) 'erlang-fill-paragraph)
+ (set (make-local-variable 'comment-add) 1)
+ (set (make-local-variable 'outline-regexp) "[[:lower:]0-9_]+ *(.*) *-> *$")
+ (set (make-local-variable 'outline-level) (lambda () 1))
+ (set (make-local-variable 'add-log-current-defun-function)
+ 'erlang-current-defun))
+
+
+;; Compilation.
+;;
+;; The following code is compatible with the standard package `compilation',
+;; making it possible to go to errors using `erlang-next-error' (or just
+;; `next-error' in Emacs 21).
+;;
+;; The normal `compile' command works of course. For best result, please
+;; execute `make' with the `-w' flag.
+;;
+;; Please see the variables named `compiling-..' above.
+
+(defun erlang-add-compilation-alist (alist)
+ (require 'compile)
+ (cond ((boundp 'compilation-error-regexp-alist) ; Emacs 19
+ (while alist
+ (or (assoc (car (car alist)) compilation-error-regexp-alist)
+ (setq compilation-error-regexp-alist
+ (cons (car alist) compilation-error-regexp-alist)))
+ (setq alist (cdr alist))))
+ ((boundp 'compilation-error-regexp)
+ ;; Emacs 18, Only one regexp is allowed.
+ (funcall (symbol-function 'set)
+ 'compilation-error-regexp (car (car alist))))))
+
+(defun erlang-font-lock-init ()
+ "Initialize Font Lock for Erlang mode."
+ (or erlang-font-lock-syntax-table
+ (setq erlang-font-lock-syntax-table
+ (let ((table (copy-syntax-table erlang-mode-syntax-table)))
+ (modify-syntax-entry ?_ "w" table)
+ table)))
+ (set (make-local-variable 'font-lock-syntax-table)
+ erlang-font-lock-syntax-table)
+ (set (make-local-variable 'font-lock-beginning-of-syntax-function)
+ 'erlang-beginning-of-clause)
+ (make-local-variable 'font-lock-keywords)
+ (let ((level (cond ((boundp 'font-lock-maximum-decoration)
+ (symbol-value 'font-lock-maximum-decoration))
+ ((boundp 'font-lock-use-maximal-decoration)
+ (symbol-value 'font-lock-use-maximal-decoration))
+ (t nil))))
+ (if (consp level)
+ (setq level (cdr-safe (or (assq 'erlang-mode level)
+ (assq t level)))))
+ ;; `level' can here be:
+ ;; A number - The fontification level
+ ;; nil - Use the default
+ ;; t - Use maximum
+ (cond ((eq level nil)
+ (set 'font-lock-keywords erlang-font-lock-keywords))
+ ((eq level 1)
+ (set 'font-lock-keywords erlang-font-lock-keywords-1))
+ ((eq level 2)
+ (set 'font-lock-keywords erlang-font-lock-keywords-2))
+ ((eq level 3)
+ (set 'font-lock-keywords erlang-font-lock-keywords-3))
+ (t
+ (set 'font-lock-keywords erlang-font-lock-keywords-4))))
+
+ ;; Modern font-locks can handle the above much more elegantly:
+ (set (make-local-variable 'font-lock-defaults)
+ '((erlang-font-lock-keywords erlang-font-lock-keywords-1
+ erlang-font-lock-keywords-2
+ erlang-font-lock-keywords-3
+ erlang-font-lock-keywords-4)
+ nil nil ((?_ . "w")) erlang-beginning-of-clause
+ (font-lock-mark-block-function . erlang-mark-clause))))
+
+
+
+;; Useful when defining your own keywords.
+(defun erlang-font-lock-set-face (ks &rest faces)
+ "Replace the face components in a list of keywords.
+
+The first argument, KS, is a list of keywords. The rest of the
+arguments are expressions to replace the face information with. The
+first expression replaces the face of the first keyword, the second
+expression the second keyword etc.
+
+Should an expression be nil, the face of the corresponding keyword is
+not changed.
+
+Should fewer expressions than keywords be given, the last expression
+is used for all remaining keywords.
+
+Normally, the expressions are just atoms representing the new face.
+They could however be more complex, returning different faces in
+different situations.
+
+This function only handles keywords with elements on the forms:
+ (REGEXP NUMBER FACE)
+ (REGEXP NUMBER FACE OVERWRITE)
+
+This could be used when defining your own special font-lock setup, e.g:
+
+\(setq my-font-lock-keywords
+ (append erlang-font-lock-keywords-function-header
+ erlang-font-lock-keywords-dollar
+ (erlang-font-lock-set-face
+ erlang-font-lock-keywords-macros 'my-neon-green-face)
+ (erlang-font-lock-set-face
+ erlang-font-lock-keywords-lc 'my-deep-red 'my-light-red)
+ erlang-font-lock-keywords-attr))
+
+For a more elaborate example, please see the beginning of the file
+`erlang.el'."
+ (let ((res '()))
+ (while ks
+ (let* ((regexp (car (car ks)))
+ (number (car (cdr (car ks))))
+ (new-face (if (and faces (car faces))
+ (car faces)
+ (car (cdr (cdr (car ks))))))
+ (overwrite (car (cdr (cdr (cdr (car ks))))))
+ (new-keyword (list regexp number new-face)))
+ (if overwrite (nconc new-keyword (list overwrite)))
+ (setq res (cons new-keyword res))
+ (setq ks (cdr ks))
+ (if (and faces (cdr faces))
+ (setq faces (cdr faces)))))
+ (nreverse res)))
+
+
+(defun erlang-font-lock-level-0 ()
+ ;; DocStringOrig: font-cmd
+ "Unfontify current buffer."
+ (interactive)
+ (font-lock-mode 0))
+
+
+(defun erlang-font-lock-level-1 ()
+ ;; DocStringCopy: font-cmd
+ "Fontify current buffer at level 1.
+This highlights function headers, reserved keywords, strings and comments."
+ (interactive)
+ (require 'font-lock)
+ (set 'font-lock-keywords erlang-font-lock-keywords-1)
+ (font-lock-mode 1)
+ (funcall (symbol-function 'font-lock-fontify-buffer)))
+
+
+(defun erlang-font-lock-level-2 ()
+ ;; DocStringCopy: font-cmd
+ "Fontify current buffer at level 2.
+This highlights level 1 features (see `erlang-font-lock-level-1')
+plus bifs, guards and `single quotes'."
+ (interactive)
+ (require 'font-lock)
+ (set 'font-lock-keywords erlang-font-lock-keywords-2)
+ (font-lock-mode 1)
+ (funcall (symbol-function 'font-lock-fontify-buffer)))
+
+
+(defun erlang-font-lock-level-3 ()
+ ;; DocStringCopy: font-cmd
+ "Fontify current buffer at level 3.
+This highlights level 2 features (see `erlang-font-lock-level-2')
+plus variables, macros and records."
+ (interactive)
+ (require 'font-lock)
+ (set 'font-lock-keywords erlang-font-lock-keywords-3)
+ (font-lock-mode 1)
+ (funcall (symbol-function 'font-lock-fontify-buffer)))
+
+(defun erlang-font-lock-level-4 ()
+ ;; DocStringCopy: font-cmd
+ "Fontify current buffer at level 4.
+This highlights level 3 features (see `erlang-font-lock-level-2')
+plus variables, macros and records."
+ (interactive)
+ (require 'font-lock)
+ (set 'font-lock-keywords erlang-font-lock-keywords-4)
+ (font-lock-mode 1)
+ (funcall (symbol-function 'font-lock-fontify-buffer)))
+
+
+(defun erlang-menu-init ()
+ "Init menus for Erlang mode.
+
+The variable `erlang-menu-items' contain a description of the Erlang
+mode menu. Normally, the list contains atoms, representing variables
+bound to pieces of the menu.
+
+Personal extensions could be added to `erlang-menu-personal-items'.
+
+This function should be called if any variable describing the
+menu configuration is changed."
+ (erlang-menu-install "Erlang" erlang-menu-items erlang-mode-map t))
+
+
+(defun erlang-menu-install (name items keymap &optional popup)
+ "Install a menu in Emacs or XEmacs based on an abstract description.
+
+NAME is the name of the menu.
+
+ITEMS is a list. The elements are either nil representing a horizontal
+line or a list with two or three elements. The first is the name of
+the menu item, the second the function to call, or a submenu, on the
+same same form as ITEMS. The third optional element is an expression
+which is evaluated every time the menu is displayed. Should the
+expression evaluate to nil the menu item is ghosted.
+
+KEYMAP is the keymap to add to menu to. (When using XEmacs, the menu
+will only be visible when this menu is the global, the local, or an
+activate minor mode keymap.)
+
+If POPUP is non-nil, the menu is bound to the XEmacs `mode-popup-menu'
+variable, i.e. it will popup when pressing the right mouse button.
+
+Please see the variable `erlang-menu-base-items'."
+ (cond (erlang-xemacs-p
+ (let ((menu (erlang-menu-xemacs name items keymap)))
+ ;; We add the menu to the global menubar.
+ ;;(funcall (symbol-function 'set-buffer-menubar)
+ ;; (symbol-value 'current-menubar))
+ (funcall (symbol-function 'add-submenu) nil menu)
+ (setcdr erlang-xemacs-popup-menu (cdr menu))
+ (if (and popup (boundp 'mode-popup-menu))
+ (funcall (symbol-function 'set)
+ 'mode-popup-menu erlang-xemacs-popup-menu))))
+ ((>= erlang-emacs-major-version 19)
+ (define-key keymap (vector 'menu-bar (intern name))
+ (erlang-menu-make-keymap name items)))
+ (t nil)))
+
+
+(defun erlang-menu-make-keymap (name items)
+ "Build a menu for Emacs 19."
+ (let ((menumap (funcall (symbol-function 'make-sparse-keymap)
+ name))
+ (count 0)
+ id def first second third)
+ (setq items (reverse items))
+ (while items
+ ;; Replace any occurrence of atoms by their value.
+ (while (and items (atom (car items)) (not (null (car items))))
+ (if (and (boundp (car items))
+ (listp (symbol-value (car items))))
+ (setq items (append (reverse (symbol-value (car items)))
+ (cdr items)))
+ (setq items (cdr items))))
+ (setq first (car-safe (car items)))
+ (setq second (car-safe (cdr-safe (car items))))
+ (setq third (car-safe (cdr-safe (cdr-safe (car items)))))
+ (cond ((null first)
+ (setq count (+ count 1))
+ (setq id (intern (format "separator-%d" count)))
+ (setq def '("--" . nil)))
+ ((and (consp second) (eq (car second) 'lambda))
+ (setq count (+ count 1))
+ (setq id (intern (format "lambda-%d" count)))
+ (setq def (cons first second)))
+ ((symbolp second)
+ (setq id second)
+ (setq def (cons first second)))
+ (t
+ (setq count (+ count 1))
+ (setq id (intern (format "submenu-%d" count)))
+ (setq def (erlang-menu-make-keymap first second))))
+ (define-key menumap (vector id) def)
+ (if third
+ (put id 'menu-enable third))
+ (setq items (cdr items)))
+ (cons name menumap)))
+
+
+(defun erlang-menu-xemacs (name items &optional keymap)
+ "Build a menu for XEmacs."
+ (let ((res '())
+ first second third entry)
+ (while items
+ ;; Replace any occurrence of atoms by their value.
+ (while (and items (atom (car items)) (not (null (car items))))
+ (if (and (boundp (car items))
+ (listp (symbol-value (car items))))
+ (setq items (append (reverse (symbol-value (car items)))
+ (cdr items)))
+ (setq items (cdr items))))
+ (setq first (car-safe (car items)))
+ (setq second (car-safe (cdr-safe (car items))))
+ (setq third (car-safe (cdr-safe (cdr-safe (car items)))))
+ (cond ((null first)
+ (setq res (cons "------" res)))
+ ((symbolp second)
+ (setq res (cons (vector first second (or third t)) res)))
+ ((and (consp second) (eq (car second) 'lambda))
+ (setq res (cons (vector first (list 'call-interactively second)
+ (or third t)) res)))
+ (t
+ (setq res (cons (cons first
+ (cdr (erlang-menu-xemacs
+ first second)))
+ res))))
+ (setq items (cdr items)))
+ (setq res (reverse res))
+ ;; When adding a menu to a minor-mode keymap under Emacs,
+ ;; it disappears when the mode is disabled. The expression
+ ;; generated below imitates this behaviour.
+ ;; (This could be expressed much clearer using backquotes,
+ ;; but I don't want to pull in every package.)
+ (if keymap
+ (let ((expr (list 'or
+ (list 'eq keymap 'global-map)
+ (list 'eq keymap (list 'current-local-map))
+ (list 'symbol-value
+ (list 'car-safe
+ (list 'rassq
+ keymap
+ 'minor-mode-map-alist))))))
+ (setq res (cons ':included (cons expr res)))))
+ (cons name res)))
+
+
+(defun erlang-menu-substitute (items alist)
+ "Substitute functions in menu described by ITEMS.
+
+The menu ITEMS is updated destructively.
+
+ALIST is list of pairs where the car is the old function and cdr the new."
+ (let (first second pair)
+ (while items
+ (setq first (car-safe (car items)))
+ (setq second (car-safe (cdr-safe (car items))))
+ (cond ((null first))
+ ((symbolp second)
+ (setq pair (and second (assq second alist)))
+ (if pair
+ (setcar (cdr (car items)) (cdr pair))))
+ ((and (consp second) (eq (car second) 'lambda)))
+ (t
+ (erlang-menu-substitute second alist)))
+ (setq items (cdr items)))))
+
+
+(defun erlang-menu-add-above (entry above items)
+ "Add menu ENTRY above menu entry ABOVE in menu ITEMS.
+Do nothing if the items already should be in the menu.
+Should ABOVE not be in the list, the entry is added at
+the bottom of the menu.
+
+The new menu is returned. No guarantee is given that the original
+menu is left unchanged.
+
+The equality test is performed by `eq'.
+
+Example: (erlang-menu-add-above 'my-erlang-menu-items
+ 'erlang-menu-man-items)"
+ (erlang-menu-add-below entry above items t))
+
+
+(defun erlang-menu-add-below (entry below items &optional above-p)
+ "Add menu ENTRY below menu items BELOW in the Erlang menu.
+Do nothing if the items already should be in the menu.
+Should BELOW not be in the list, items is added at the bottom
+of the menu.
+
+The new menu is returned. No guarantee is given that the original
+menu is left unchanged.
+
+The equality test is performed by `eq'.
+
+Example:
+
+\(setq erlang-menu-items
+ (erlang-menu-add-below 'my-erlang-menu-items
+ 'erlang-menu-base-items
+ erlang-menu-items))"
+ (if (memq entry items)
+ items ; Return the original menu.
+ (let ((head '())
+ (done nil)
+ res)
+ (while (not done)
+ (cond ((null items)
+ (setq res (append head (list entry)))
+ (setq done t))
+ ((eq below (car items))
+ (setq res
+ (if above-p
+ (append head (cons entry items))
+ (append head (cons (car items)
+ (cons entry (cdr items))))))
+ (setq done t))
+ (t
+ (setq head (append head (list (car items))))
+ (setq items (cdr items)))))
+ res)))
+
+(defun erlang-menu-delete (entry items)
+ "Delete ENTRY from menu ITEMS.
+
+The new menu is returned. No guarantee is given that the original
+menu is left unchanged."
+ (delq entry items))
+
+;; Man code:
+
+(defun erlang-man-init ()
+ "Add menus containing the manual pages of the Erlang.
+
+The variable `erlang-man-dirs' contains entries describing
+the location of the manual pages."
+ (interactive)
+ (if erlang-man-inhibit
+ ()
+ (setq erlang-menu-man-items
+ '(nil
+ ("Man - Function" erlang-man-function)))
+ (if erlang-man-dirs
+ (setq erlang-menu-man-items
+ (append erlang-menu-man-items
+ (erlang-man-make-top-menu erlang-man-dirs))))
+ (setq erlang-menu-items
+ (erlang-menu-add-above 'erlang-menu-man-items
+ 'erlang-menu-version-items
+ erlang-menu-items))
+ (erlang-menu-init)))
+
+
+(defun erlang-man-uninstall ()
+ "Remove the man pages from the Erlang mode."
+ (interactive)
+ (setq erlang-menu-items
+ (erlang-menu-delete 'erlang-menu-man-items erlang-menu-items))
+ (erlang-menu-init))
+
+
+;; The man menu is a hierarchal structure, with the manual sections
+;; at the top, described by `erlang-man-dirs'. The next level could
+;; either be the manual pages if not to many, otherwise it is an index
+;; menu whose submenus will contain up to `erlang-man-max-menu-size'
+;; manual pages.
+
+(defun erlang-man-make-top-menu (dir-list)
+ "Create one menu entry per element of DIR-LIST.
+The format is described in the documentation of `erlang-man-dirs'."
+ (let ((menu '())
+ dir)
+ (while dir-list
+ (setq dir (cond ((nth 2 (car dir-list))
+ ;; Relative to `erlang-root-dir'.
+ (and (stringp erlang-root-dir)
+ (concat erlang-root-dir (nth 1 (car dir-list)))))
+ (t
+ ;; Absolute
+ (nth 1 (car dir-list)))))
+ (if (and dir
+ (file-readable-p dir))
+ (setq menu (cons (list (car (car dir-list))
+ (erlang-man-make-middle-menu
+ (erlang-man-get-files dir)))
+ menu)))
+ (setq dir-list (cdr dir-list)))
+ ;; Should no menus be found, generate a menu item which
+ ;; will display a help text, when selected.
+ (if menu
+ (nreverse menu)
+ '(("Man Pages"
+ (("Error! Why?" erlang-man-describe-error)))))))
+
+
+;; Should the menu be to long, let's split it into a number of
+;; smaller menus. Warning, this code contains beautiful
+;; destructive operations!
+(defun erlang-man-make-middle-menu (filelist)
+ "Create the second level menu from FILELIST.
+
+Should the list be longer than `erlang-man-max-menu-size', a tree of
+menus is created."
+ (if (<= (length filelist) erlang-man-max-menu-size)
+ (erlang-man-make-menu filelist)
+ (let ((menu '())
+ (filelist (copy-sequence filelist))
+ segment submenu pair)
+ (while filelist
+ (setq pair (nthcdr (- erlang-man-max-menu-size 1) filelist))
+ (setq segment filelist)
+ (if (null pair)
+ (setq filelist nil)
+ (setq filelist (cdr pair))
+ (setcdr pair nil))
+ (setq submenu (erlang-man-make-menu segment))
+ (setq menu (cons (list (concat (car (car submenu))
+ " -- "
+ (car (car (reverse submenu))))
+ submenu)
+ menu)))
+ (nreverse menu))))
+
+
+(defun erlang-man-make-menu (filelist)
+ "Make a leaf menu based on FILELIST."
+ (let ((menu '())
+ item)
+ (while filelist
+ (setq item (erlang-man-make-menu-item (car filelist)))
+ (if item
+ (setq menu (cons item menu)))
+ (setq filelist (cdr filelist)))
+ (nreverse menu)))
+
+
+(defun erlang-man-make-menu-item (file)
+ "Create a menu item containing the name of the man page."
+ (and (string-match ".+/\\([^/]+\\)\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$" file)
+ (let ((page (substring file (match-beginning 1) (match-end 1))))
+ (list (capitalize page)
+ (list 'lambda '()
+ '(interactive)
+ (list 'funcall 'erlang-man-display-function
+ file))))))
+
+
+(defun erlang-man-get-files (dir)
+ "Return files in directory DIR."
+ (directory-files dir t ".+\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?\\'"))
+
+
+(defun erlang-man-module (&optional module)
+ "Find manual page for MODULE, defaults to module of function under point.
+This function is aware of imported functions."
+ (interactive
+ (list (let* ((mod (car-safe (erlang-get-function-under-point)))
+ (input (read-string
+ (format "Manual entry for module%s: "
+ (if (or (null mod) (string= mod ""))
+ ""
+ (format " (default %s)" mod))))))
+ (if (string= input "")
+ mod
+ input))))
+ (or module (setq module (car (erlang-get-function-under-point))))
+ (if (or (null module) (string= module ""))
+ (error "No Erlang module name given"))
+ (let ((dir-list erlang-man-dirs)
+ (pat (concat "/" (regexp-quote module) "\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$"))
+ (file nil)
+ file-list)
+ (while (and dir-list (null file))
+ (setq file-list (erlang-man-get-files
+ (if (nth 2 (car dir-list))
+ (concat erlang-root-dir (nth 1 (car dir-list)))
+ (nth 1 (car dir-list)))))
+ (while (and file-list (null file))
+ (if (string-match pat (car file-list))
+ (setq file (car file-list)))
+ (setq file-list (cdr file-list)))
+ (setq dir-list (cdr dir-list)))
+ (if file
+ (funcall erlang-man-display-function file)
+ (error "No manual page for module %s found" module))))
+
+
+;; Warning, the function `erlang-man-function' is a hack!
+;; It links itself into the man code in a non-clean way. I have
+;; chosen to keep it since it provides a very useful functionality
+;; which is not possible to achieve using a clean approach.
+;; / AndersL
+
+(defvar erlang-man-function-name nil
+ "Name of function for last `erlang-man-function' call.
+Used for communication between `erlang-man-function' and the
+patch to `Man-notify-when-ready'.")
+
+(defun erlang-man-function (&optional name)
+ "Find manual page for NAME, where NAME is module:function.
+The entry for `function' is displayed.
+
+This function is aware of imported functions."
+ (interactive
+ (list (let* ((mod-func (erlang-get-function-under-point))
+ (mod (car-safe mod-func))
+ (func (nth 1 mod-func))
+ (input (read-string
+ (format
+ "Manual entry for `module:func' or `module'%s: "
+ (if (or (null mod) (string= mod ""))
+ ""
+ (format " (default %s:%s)" mod func))))))
+ (if (string= input "")
+ (if (and mod func)
+ (concat mod ":" func)
+ mod)
+ input))))
+ ;; Emacs 18 doesn't provide `man'...
+ (condition-case nil
+ (require 'man)
+ (error nil))
+ (let ((modname nil)
+ (funcname nil))
+ (cond ((null name)
+ (let ((mod-func (erlang-get-function-under-point)))
+ (setq modname (car-safe mod-func))
+ (setq funcname (nth 1 mod-func))))
+ ((string-match ":" name)
+ (setq modname (substring name 0 (match-beginning 0)))
+ (setq funcname (substring name (match-end 0) nil)))
+ ((stringp name)
+ (setq modname name)))
+ (if (or (null modname) (string= modname ""))
+ (error "No Erlang module name given"))
+ (cond ((fboundp 'Man-notify-when-ready)
+ ;; Emacs 19: The man command could possibly start an
+ ;; asynchronous process, i.e. we must hook ourselves into
+ ;; the system to be activated when the man-process
+ ;; terminates.
+ (if (null funcname)
+ ()
+ (erlang-man-patch-notify)
+ (setq erlang-man-function-name funcname))
+ (condition-case nil
+ (erlang-man-module modname)
+ (error (setq erlang-man-function-name nil))))
+ (t
+ (erlang-man-module modname)
+ (if funcname
+ (erlang-man-find-function
+ (or (get-buffer "*Manual Entry*") ; Emacs 18
+ (current-buffer)) ; XEmacs
+ funcname))))))
+
+
+;; Should the defadvice be at the top level, the package `advice' would
+;; be required. Now it is only required when this functionality
+;; is used. (Emacs 19 specific.)
+(defun erlang-man-patch-notify ()
+ "Patch the function `Man-notify-when-ready' to search for function.
+The variable `erlang-man-function-name' is assumed to be bound to
+the function name, or to nil.
+
+The reason for patching a function is that under Emacs 19, the man
+command is executed asynchronously."
+ (condition-case nil
+ (require 'advice)
+ ;; This should never happened since this is only called when
+ ;; running under Emacs 19.
+ (error (error (concat "This command needs the package `advice', "
+ "please upgrade your Emacs."))))
+ (require 'man)
+ (defadvice Man-notify-when-ready
+ (after erlang-Man-notify-when-ready activate)
+ "Set point at the documentation of the function name in
+`erlang-man-function-name' when the man page is displayed."
+ (if erlang-man-function-name
+ (erlang-man-find-function (ad-get-arg 0) erlang-man-function-name))
+ (setq erlang-man-function-name nil)))
+
+
+(defun erlang-man-find-function (buf func)
+ "Find manual page for function in `erlang-man-function-name' in buffer BUF."
+ (if func
+ (let ((win (get-buffer-window buf)))
+ (if win
+ (progn
+ (set-buffer buf)
+ (goto-char (point-min))
+ (if (re-search-forward
+ (concat "^[ \t]+" func " ?(")
+ (point-max) t)
+ (progn
+ (forward-word -1)
+ (set-window-point win (point)))
+ (message "Could not find function `%s'" func)))))))
+
+
+(defun erlang-man-display (file)
+ "Display FILE as a `man' file.
+This is the default manual page display function.
+The variables `erlang-man-display-function' contains the function
+to be used."
+ ;; Emacs 18 doesn't `provide' man.
+ (condition-case nil
+ (require 'man)
+ (error nil))
+ (if file
+ (let ((process-environment (copy-sequence process-environment)))
+ (if (string-match "\\(.*\\)/man[^/]*/\\([^.]+\\)\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$" file)
+ (let ((dir (substring file (match-beginning 1) (match-end 1)))
+ (page (substring file (match-beginning 2) (match-end 2))))
+ (if (fboundp 'setenv)
+ (setenv "MANPATH" dir)
+ ;; Emacs 18
+ (setq process-environment (cons (concat "MANPATH=" dir)
+ process-environment)))
+ (cond ((not (and (not erlang-xemacs-p)
+ (= erlang-emacs-major-version 19)
+ (< erlang-emacs-minor-version 29)))
+ (manual-entry page))
+ (t
+ ;; Emacs 19.28 and earlier versions of 19:
+ ;; The manual-entry command unconditionally prompts
+ ;; the user :-(
+ (funcall (symbol-function 'Man-getpage-in-background)
+ page))))
+ (error "Can't find man page for %s\n" file)))))
+
+
+(defun erlang-man-describe-error ()
+ "Describe why the manual pages weren't found."
+ (interactive)
+ (with-output-to-temp-buffer "*Erlang Man Error*"
+ (princ "Normally, this menu should contain Erlang manual pages.
+
+In order to find the manual pages, the variable `erlang-root-dir'
+should be bound to the name of the directory containing the Erlang
+installation. The name should not include the final slash.
+
+Practically, you should add a line on the following form to
+your ~/.emacs, or ask your system administrator to add it to
+the site init file:
+ (setq erlang-root-dir \"/the/erlang/root/dir/goes/here\")
+
+For example:
+ (setq erlang-root-dir \"/usr/local/erlang\")
+
+After installing the line, kill and restart Emacs, or restart Erlang
+mode with the command `M-x erlang-mode RET'.")))
+
+;; Skeleton code:
+
+;; This code is based on the package `tempo' which is part of modern
+;; Emacsen. (GNU Emacs 19.25 (?) and XEmacs 19.14.)
+
+(defun erlang-skel-init ()
+ "Generate the skeleton functions and menu items.
+The variable `erlang-skel' contains the name and descriptions of
+all skeletons.
+
+The skeleton routines are based on the `tempo' package. Should this
+package not be present, this function does nothing."
+ (interactive)
+ (condition-case nil
+ (require 'tempo)
+ (error t))
+ (if (featurep 'tempo)
+ (let ((skel erlang-skel)
+ (menu '()))
+ (while skel
+ (cond ((null (car skel))
+ (setq menu (cons nil menu)))
+ (t
+ (funcall (symbol-function 'tempo-define-template)
+ (concat "erlang-" (nth 1 (car skel)))
+ ;; The tempo template used contains an `include'
+ ;; function call only, hence changes to the
+ ;; variables describing the templates take effect
+ ;; immdiately.
+ (list (list 'erlang-skel-include (nth 2 (car skel))))
+ (nth 1 (car skel))
+ (car (car skel))
+ 'erlang-tempo-tags)
+ (setq menu (cons (erlang-skel-make-menu-item
+ (car skel)) menu))))
+ (setq skel (cdr skel)))
+ (setq erlang-menu-skel-items
+ (list nil (list "Skeletons" (nreverse menu))))
+ (setq erlang-menu-items
+ (erlang-menu-add-above 'erlang-menu-skel-items
+ 'erlang-menu-version-items
+ erlang-menu-items))
+ (erlang-menu-init))))
+
+(defun erlang-skel-make-menu-item (skel)
+ (let ((func (intern (concat "tempo-template-erlang-" (nth 1 skel)))))
+ (cond ((null (nth 3 skel))
+ (list (car skel) func))
+ (t
+ (list (car skel)
+ (list 'lambda '()
+ '(interactive)
+ (list 'funcall
+ (list 'quote (nth 3 skel))
+ (list 'quote func))))))))
+
+;; Functions designed to be added to the skeleton menu.
+;; (Not normally used)
+(defun erlang-skel-insert (func)
+ "Insert skeleton generated by FUNC and goto first tempo mark."
+ (save-excursion (funcall func))
+ (funcall (symbol-function 'tempo-forward-mark)))
+
+(defun erlang-skel-header (func)
+ "Insert the header generated by FUNC at the beginning of the buffer."
+ (goto-char (point-min))
+ (save-excursion (funcall func))
+ (funcall (symbol-function 'tempo-forward-mark)))
+
+
+;; Functions used inside the skeleton descriptions.
+(defun erlang-skel-skip-blank ()
+ (skip-chars-backward " \t")
+ nil)
+
+(defun erlang-skel-include (&rest args)
+ "Include a template inside another template.
+
+Example of use, assuming that `erlang-skel-func' is defined:
+
+ (defvar foo-skeleton '(\"%%% New function:\"
+ (erlang-skel-include erlang-skel-func)))
+
+Technically, this function returns the `tempo' attribute`(l ...)' which
+can contain other `tempo' attributes. Please see the function
+`tempo-define-template' for a description of the `(l ...)' attribute."
+ (let ((res '())
+ entry)
+ (while args
+ (setq entry (car args))
+ (while entry
+ (setq res (cons (car entry) res))
+ (setq entry (cdr entry)))
+ (setq args (cdr args)))
+ (cons 'l (nreverse res))))
+
+(defvar erlang-skel-separator-length 70)
+
+(defun erlang-skel-separator (&optional percent)
+ "Return a comment separator."
+ (let ((percent (or percent 3)))
+ (concat (make-string percent ?%)
+ (make-string (- erlang-skel-separator-length percent) ?-)
+ "\n")))
+
+(defun erlang-skel-double-separator (&optional percent)
+ "Return a comment separator."
+ (let ((percent (or percent 3)))
+ (concat (make-string percent ?%)
+ (make-string (- erlang-skel-separator-length percent) ?=)
+ "\n")))
+
+(defun erlang-skel-dd-mmm-yyyy ()
+ "Return the current date as a string in \"DD Mon YYYY\" form.
+The first character of DD is space if the value is less than 10."
+ (let ((date (current-time-string)))
+ (format "%2d %s %s"
+ (erlang-string-to-int (substring date 8 10))
+ (substring date 4 7)
+ (substring date -4))))
+
+;; Indentation code:
+
+(defun erlang-indent-command (&optional whole-exp)
+ "Indent current line as Erlang code.
+With argument, indent any additional lines of the same clause
+rigidly along with this one."
+ (interactive "P")
+ (if whole-exp
+ ;; If arg, always indent this line as Erlang
+ ;; and shift remaining lines of clause the same amount.
+ (let ((shift-amt (erlang-indent-line))
+ beg end)
+ (save-excursion
+ (if erlang-tab-always-indent
+ (beginning-of-line))
+ (setq beg (point))
+ (erlang-end-of-clause 1)
+ (setq end (point))
+ (goto-char beg)
+ (forward-line 1)
+ (setq beg (point)))
+ (if (> end beg)
+ (indent-code-rigidly beg end shift-amt "\n")))
+ (if (and (not erlang-tab-always-indent)
+ (save-excursion
+ (skip-chars-backward " \t")
+ (not (bolp))))
+ (insert-tab)
+ (erlang-indent-line))))
+
+
+(defun erlang-indent-line ()
+ "Indent current line as Erlang code.
+Return the amount the indentation changed by."
+ (let ((pos (- (point-max) (point)))
+ indent beg
+ shift-amt)
+ (beginning-of-line 1)
+ (setq beg (point))
+ (skip-chars-forward " \t")
+ (cond ((looking-at "%")
+ (setq indent (funcall comment-indent-function))
+ (setq shift-amt (- indent (current-column))))
+ (t
+ (setq indent (erlang-calculate-indent))
+ (cond ((null indent)
+ (setq indent (current-indentation)))
+ ((eq indent t)
+ ;; This should never occur here.
+ (error "Erlang mode error"))
+ ;;((= (char-syntax (following-char)) ?\))
+ ;; (setq indent (1- indent)))
+ )
+ (setq shift-amt (- indent (current-column)))))
+ (if (zerop shift-amt)
+ nil
+ (delete-region beg (point))
+ (indent-to indent))
+ ;; If initial point was within line's indentation, position
+ ;; after the indentation. Else stay at same point in text.
+ (if (> (- (point-max) pos) (point))
+ (goto-char (- (point-max) pos)))
+ shift-amt))
+
+
+(defun erlang-indent-region (beg end)
+ "Indent region of Erlang code.
+
+This is automagically called by the user level function `indent-region'."
+ (interactive "r")
+ (save-excursion
+ (let ((case-fold-search nil)
+ (continue t)
+ (from-end (- (point-max) end))
+ indent-point;; The beginning of the current line
+ indent;; The indent amount
+ state)
+ (goto-char beg)
+ (beginning-of-line)
+ (setq indent-point (point))
+ (erlang-beginning-of-clause)
+ ;; Parse the Erlang code from the beginning of the clause to
+ ;; the beginning of the region.
+ (while (< (point) indent-point)
+ (setq state (erlang-partial-parse (point) indent-point state)))
+ ;; Indent every line in the region
+ (while continue
+ (goto-char indent-point)
+ (skip-chars-forward " \t")
+ (cond ((looking-at "%")
+ ;; Do not use our stack to help the user to customize
+ ;; comment indentation.
+ (setq indent (funcall comment-indent-function)))
+ ((looking-at "$")
+ ;; Don't indent empty lines.
+ (setq indent 0))
+ (t
+ (setq indent
+ (save-excursion
+ (erlang-calculate-stack-indent (point) state)))
+ (cond ((null indent)
+ (setq indent (current-indentation)))
+ ((eq indent t)
+ ;; This should never occur here.
+ (error "Erlang mode error"))
+ ;;((= (char-syntax (following-char)) ?\))
+ ;; (setq indent (1- indent)))
+ )))
+ (if (zerop (- indent (current-column)))
+ nil
+ (delete-region indent-point (point))
+ (indent-to indent))
+ ;; Find the next line in the region
+ (goto-char indent-point)
+ (save-excursion
+ (forward-line 1)
+ (setq indent-point (point)))
+ (if (>= from-end (- (point-max) indent-point))
+ (setq continue nil)
+ (while (< (point) indent-point)
+ (setq state (erlang-partial-parse
+ (point) indent-point state))))))))
+
+
+(defun erlang-indent-current-buffer ()
+ "Indent current buffer as Erlang code."
+ (interactive)
+ (save-excursion
+ (save-restriction
+ (widen)
+ (erlang-indent-region (point-min) (point-max)))))
+
+
+(defun erlang-indent-function ()
+ "Indent current Erlang function."
+ (interactive)
+ (save-excursion
+ (let ((end (progn (erlang-end-of-function 1) (point)))
+ (beg (progn (erlang-beginning-of-function 1) (point))))
+ (erlang-indent-region beg end))))
+
+
+(defun erlang-indent-clause ()
+ "Indent current Erlang clause."
+ (interactive)
+ (save-excursion
+ (let ((end (progn (erlang-end-of-clause 1) (point)))
+ (beg (progn (erlang-beginning-of-clause 1) (point))))
+ (erlang-indent-region beg end))))
+
+
+(defmacro erlang-push (x stack) (list 'setq stack (list 'cons x stack)))
+(defmacro erlang-pop (stack) (list 'setq stack (list 'cdr stack)))
+;; Would much prefer to make caddr a macro but this clashes.
+(defun erlang-caddr (x) (car (cdr (cdr x))))
+
+
+(defun erlang-calculate-indent (&optional parse-start)
+ "Compute appropriate indentation for current line as Erlang code.
+Return nil if line starts inside string, t if in a comment."
+ (save-excursion
+ (let ((indent-point (point))
+ (case-fold-search nil)
+ (state nil))
+ (if parse-start
+ (goto-char parse-start)
+ (erlang-beginning-of-clause))
+ (while (< (point) indent-point)
+ (setq state (erlang-partial-parse (point) indent-point state)))
+ (erlang-calculate-stack-indent indent-point state))))
+
+(defun erlang-show-syntactic-information ()
+ "Show syntactic information for current line."
+
+ (interactive)
+
+ (save-excursion
+ (let ((starting-point (point))
+ (case-fold-search nil)
+ (state nil))
+ (erlang-beginning-of-clause)
+ (while (< (point) starting-point)
+ (setq state (erlang-partial-parse (point) starting-point state)))
+ (message "%S" state))))
+
+
+(defun erlang-partial-parse (from to &optional state)
+ "Parse Erlang syntax starting at FROM until TO, with an optional STATE.
+Value is list (stack token-start token-type in-what)."
+ (goto-char from) ; Start at the beginning
+ (erlang-skip-blank to)
+ (let ((cs (char-syntax (following-char)))
+ (stack (car state))
+ (token (point))
+ in-what)
+ (cond
+
+ ;; Done: Return previous state.
+ ((>= token to)
+ (setq token (nth 1 state))
+ (setq cs (nth 2 state))
+ (setq in-what (nth 3 state)))
+
+ ;; Word constituent: check and handle keywords.
+ ((= cs ?w)
+ (cond ((looking-at "\\(end\\|after\\)[^_a-zA-Z0-9]")
+ ;; Must pop top icr layer, `after' will push a new
+ ;; layer next.
+ (progn
+ (while (and stack (eq (car (car stack)) '->))
+ (erlang-pop stack))
+ (if (and stack (memq (car (car stack)) '(icr begin fun try)))
+ (erlang-pop stack))))
+ ((looking-at "catch.*of")
+ t)
+ ((looking-at "catch\\s *\\($\\|%\\|.*->\\)")
+ ;; Must pop top icr layer, `catch' in try/catch
+ ;;will push a new layer next.
+ (progn
+ (while (and stack (eq (car (car stack)) '->))
+ (erlang-pop stack))
+ (if (and stack (memq (car (car stack)) '(icr begin try)))
+ (erlang-pop stack))))
+ )
+ (cond ((looking-at "\\(if\\|case\\|receive\\)[^_a-zA-Z0-9]")
+ ;; Must push a new icr (if/case/receive) layer.
+ (erlang-push (list 'icr token (current-column)) stack))
+ ((looking-at "\\(try\\|after\\)[^_a-zA-Z0-9]")
+ ;; Must handle separately, try catch or try X of -> catch
+ ;; same for `after', it could be
+ ;; receive after Time -> X end, or
+ ;; try after X end
+ (erlang-push (list 'try token (current-column)) stack))
+ ((looking-at "\\(of\\)[^_a-zA-Z0-9]")
+ ;; Must handle separately, try X of -> catch
+ (if (and stack (eq (car (car stack)) 'try))
+ (let ((try-column (nth 2 (car stack))))
+ (erlang-pop stack)
+ (erlang-push (list 'icr token try-column) stack))))
+
+ ((looking-at "\\(fun\\)[^_a-zA-Z0-9]")
+ ;; Push a new layer if we are defining a `fun'
+ ;; expression, not when we are refering an existing
+ ;; function. 'fun's defines are only indented one level now.
+ (if (save-excursion
+ (goto-char (match-end 1))
+ (erlang-skip-blank to)
+ (eq (following-char) ?\())
+ (erlang-push (list 'fun token (current-column)) stack)))
+ ((looking-at "\\(begin\\|query\\)[^_a-zA-Z0-9]")
+ (erlang-push (list 'begin token (current-column)) stack))
+ ;; Normal when case
+ ;;((looking-at "when\\s ")
+ ;;((looking-at "when\\s *\\($\\|%\\)")
+ ((looking-at "when[^_a-zA-Z0-9]")
+ (erlang-push (list 'when token (current-column)) stack))
+ ((looking-at "catch.*of")
+ t)
+ ((looking-at "catch\\s *\\($\\|%\\|.*->\\)")
+ (erlang-push (list 'icr token (current-column)) stack))
+ ;;(erlang-push (list '-> token (current-column)) stack))
+ ;;((looking-at "^of$")
+ ;; (erlang-push (list 'icr token (current-column)) stack)
+ ;;(erlang-push (list '-> token (current-column)) stack))
+ )
+ (forward-sexp 1))
+ ;; String: Try to skip over it. (Catch error if not complete.)
+ ((= cs ?\")
+ (condition-case nil
+ (progn
+ (forward-sexp 1)
+ (if (> (point) to)
+ (progn
+ (setq in-what 'string)
+ (goto-char to))))
+ (error
+ (setq in-what 'string)
+ (goto-char to))))
+
+ ;; Expression prefix e.i. $ or ^ (Note ^ can be in the character
+ ;; literal $^ or part of string and $ outside of a string denotes
+ ;; a character literal)
+ ((= cs ?')
+ (cond
+ ((= (following-char) ?\") ;; $ or ^ was the last char in a string
+ (forward-char 1))
+ (t
+ ;; Maybe a character literal, quote the next char to avoid
+ ;; situations as $" being seen as the begining of a string.
+ ;; Note the quoting something in the middle of a string is harmless.
+ (quote (following-char))
+ (forward-char 1))))
+
+ ;; Symbol constituent or punctuation
+
+ ((memq cs '(?. ?_))
+ (cond
+
+ ;; Clause end
+ ((= (following-char) ?\;)
+ (if (and stack (and (eq (car (car stack)) 'when)
+ (eq (car (car (cdr (cdr stack)))) 'spec)))
+ (erlang-pop stack))
+ (if (and stack (eq (car (car stack)) '->))
+ (erlang-pop stack))
+ (forward-char 1))
+
+ ;; Parameter separator
+ ((looking-at ",")
+ (forward-char 1)
+ (if (and stack (eq (car (car stack)) '::))
+ ;; Type or spec
+ (erlang-pop stack)))
+
+ ;; Function end
+ ((looking-at "\\.\\(\\s \\|\n\\|\\s<\\)")
+ (setq stack nil)
+ (forward-char 1))
+
+ ;; Function head
+ ((looking-at "->")
+ (if (and stack (eq (car (car stack)) 'when))
+ (erlang-pop stack))
+ (erlang-push (list '-> token (current-column)) stack)
+ (forward-char 2))
+
+ ;; List-comprehension divider
+ ((looking-at "||")
+ (erlang-push (list '|| token (current-column)) stack)
+ (forward-char 2))
+
+ ;; Bit-syntax open paren
+ ((looking-at "<<")
+ (erlang-push (list '<< token (current-column)) stack)
+ (forward-char 2))
+
+ ;; Bbit-syntax close paren
+ ((looking-at ">>")
+ (while (memq (car (car stack)) '(|| ->))
+ (erlang-pop stack))
+ (cond ((eq (car (car stack)) '<<)
+ (erlang-pop stack))
+ ((memq (car (car stack)) '(icr begin fun))
+ (error "Missing `end'"))
+ (t
+ (error "Unbalanced parentheses")))
+ (forward-char 2))
+
+ ;; Macro
+ ((= (following-char) ??)
+ ;; Skip over the ?
+ (forward-char 1)
+ )
+
+ ;; Type spec's
+ ((looking-at "-type\\s \\|-opaque\\s ")
+ (if stack
+ (forward-char 1)
+ (erlang-push (list 'icr token (current-column)) stack)
+ (forward-char 6)))
+ ((looking-at "-spec\\s ")
+ (if stack
+ (forward-char 1)
+ (forward-char 6)
+ (skip-chars-forward "^(\n")
+ (erlang-push (list 'spec (point) (current-column)) stack)
+ ))
+
+ ;; Type spec delimiter
+ ((looking-at "::")
+ (erlang-push (list ':: token (current-column)) stack)
+ (forward-char 2))
+
+ ;; Don't follow through in the clause below
+ ;; '|' don't need spaces around it
+ ((looking-at "|")
+ (forward-char 1))
+
+ ;; Other punctuation: Skip over it and any following punctuation
+ ((= cs ?.)
+ ;; Skip over all characters in the operand.
+ (skip-syntax-forward "."))
+
+ ;; Other char: Skip over it.
+ (t
+ (forward-char 1))))
+
+ ;; Open parenthesis
+ ((= cs ?\()
+ (erlang-push (list '\( token (current-column)) stack)
+ (forward-char 1))
+
+ ;; Close parenthesis
+ ((= cs ?\))
+ (while (memq (car (car stack)) '(|| -> :: when))
+ (erlang-pop stack))
+ (cond ((eq (car (car stack)) '\()
+ (erlang-pop stack)
+ (if (and (eq (car (car stack)) 'fun)
+ (eq (car (car (cdr stack))) '::))
+ ;; Inside fun type def ') closes fun definition
+ (erlang-pop stack)))
+ ((eq (car (car stack)) 'icr)
+ (erlang-pop stack)
+ ;; Normal catch not try-catch might have caused icr
+ ;; and then incr should be removed and is not an error.
+ (if (eq (car (car stack)) '\()
+ (erlang-pop stack)
+ (error "Missing `end'")
+ ))
+ ((eq (car (car stack)) 'begin)
+ (error "Missing `end'"))
+ (t
+ (error "Unbalanced parenthesis"))
+ )
+ (forward-char 1))
+
+ ;; Character quote: Skip it and the quoted char.
+ ((= cs ?/)
+ (forward-char 2))
+
+ ;; Character escape: Skip it and the escape sequence.
+ ((= cs ?\\)
+ (forward-char 1)
+ (skip-syntax-forward "w"))
+
+ ;; Everything else
+ (t
+ (forward-char 1)))
+ (list stack token cs in-what)))
+
+(defun erlang-calculate-stack-indent (indent-point state)
+ "From the given last position and state (stack) calculate indentation.
+Return nil if inside string, t if in a comment."
+ (let* ((stack (and state (car state)))
+ (token (nth 1 state))
+ (stack-top (and stack (car stack))))
+ (cond ((null state) ;No state
+ 0)
+ ((nth 3 state)
+ ;; Return nil or t.
+ (eq (nth 3 state) 'comment))
+ ((null stack)
+ (if (looking-at "when[^_a-zA-Z0-9]")
+ erlang-indent-guard
+ 0))
+ ((eq (car stack-top) '\()
+ ;; Element of list, tuple or part of an expression,
+ (cond ((null erlang-argument-indent)
+ ;; indent to next column.
+ (1+ (nth 2 stack-top)))
+ ((= (char-syntax (following-char)) ?\))
+ (goto-char (nth 1 stack-top))
+ (cond ((looking-at "[({]\\s *\\($\\|%\\)")
+ ;; Line ends with parenthesis.
+ (let ((previous (erlang-indent-find-preceding-expr))
+ (stack-pos (nth 2 stack-top)))
+ (if (>= previous stack-pos) stack-pos
+ (- (+ previous erlang-argument-indent) 1))))
+ (t
+ (nth 2 stack-top))))
+ (t
+ (goto-char (nth 1 stack-top))
+ (cond ((looking-at "[({]\\s *\\($\\|%\\)")
+ ;; Line ends with parenthesis.
+ (erlang-indent-parenthesis (nth 2 stack-top)))
+ (t
+ ;; Indent to the same column as the first
+ ;; argument.
+ (goto-char (1+ (nth 1 stack-top)))
+ (skip-chars-forward " \t")
+ (current-column))))))
+ ;;
+ ((eq (car stack-top) '<<)
+ ;; Element of binary (possible comprehension) expression,
+ (cond ((null erlang-argument-indent)
+ ;; indent to next column.
+ (+ 2 (nth 2 stack-top)))
+ ((looking-at "\\(>>\\)[^_a-zA-Z0-9]")
+ (nth 2 stack-top))
+ (t
+ (goto-char (nth 1 stack-top))
+ ;; Indent to the same column as the first
+ ;; argument.
+ (goto-char (+ 2 (nth 1 stack-top)))
+ (skip-chars-forward " \t")
+ (current-column))))
+
+ ((memq (car stack-top) '(icr fun spec))
+ ;; The default indentation is the column of the option
+ ;; directly following the keyword. (This does not apply to
+ ;; `case'.) Should no option be on the same line, the
+ ;; indentation is the indentation of the keyword +
+ ;; `erlang-indent-level'.
+ ;;
+ ;; `after' should be indented to the same level as the
+ ;; corresponding receive.
+ (cond ((looking-at "\\(after\\|catch\\|of\\)\\($\\|[^_a-zA-Z0-9]\\)")
+ (nth 2 stack-top))
+ ((looking-at "when[^_a-zA-Z0-9]")
+ ;; Handling one when part
+ (+ (nth 2 stack-top) erlang-indent-level erlang-indent-guard))
+ (t
+ (save-excursion
+ (goto-char (nth 1 stack-top))
+ (if (looking-at "case[^_a-zA-Z0-9]")
+ (+ (nth 2 stack-top) erlang-indent-level)
+ (skip-chars-forward "a-z")
+ (skip-chars-forward " \t")
+ (if (memq (following-char) '(?% ?\n))
+ (+ (nth 2 stack-top) erlang-indent-level)
+ (current-column))))))
+ )
+ ((and (eq (car stack-top) '||) (looking-at "\\(]\\|>>\\)[^_a-zA-Z0-9]"))
+ (nth 2 (car (cdr stack))))
+ ;; Real indentation, where operators create extra indentation etc.
+ ((memq (car stack-top) '(-> || begin try))
+ (if (looking-at "\\(of\\)[^_a-zA-Z0-9]")
+ (nth 2 stack-top)
+ (goto-char (nth 1 stack-top))
+ ;; Check if there is more code after the '->' on the
+ ;; same line. If so use this indentation as base, else
+ ;; use parent indentation + 2 * level as base.
+ (let ((off erlang-indent-level)
+ (skip 2))
+ (cond ((null (cdr stack))) ; Top level in function.
+ ((eq (car stack-top) 'begin)
+ (setq skip 5))
+ ((eq (car stack-top) 'try)
+ (setq skip 5))
+ ((eq (car stack-top) '->)
+ ;; If in fun definition use standard indent level not double
+ ;;(if (not (eq (car (car (cdr stack))) 'fun))
+ ;; Removed it made multi clause fun's look to bad
+ (setq off (* 2 erlang-indent-level)))) ;; )
+ (let ((base (erlang-indent-find-base stack indent-point off skip)))
+ ;; Special cases
+ (goto-char indent-point)
+ (cond ((looking-at "\\(end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)")
+ (if (eq (car stack-top) '->)
+ (erlang-pop stack))
+ (if stack
+ (erlang-caddr (car stack))
+ 0))
+ ((looking-at "catch\\($\\|[^_a-zA-Z0-9]\\)")
+ (if (or (eq (car stack-top) 'try)
+ (eq (car (car (cdr stack))) 'icr))
+ (progn
+ (if (eq (car stack-top) '->)
+ (erlang-pop stack))
+ (if stack
+ (erlang-caddr (car stack))
+ 0))
+ base)) ;; old catch
+ (t
+ ;; Look at last thing to see how we are to move relative
+ ;; to the base.
+ (goto-char token)
+ (cond ((looking-at "||\\|,\\|->")
+ base)
+ ((erlang-at-keyword)
+ (+ (current-column) erlang-indent-level))
+ ((or (= (char-syntax (following-char)) ?.)
+ (erlang-at-operator))
+ (+ base erlang-indent-level))
+ (t
+ (goto-char indent-point)
+ (cond ((memq (following-char) '(?\( ?{))
+ ;; Function application or record.
+ (+ (erlang-indent-find-preceding-expr)
+ erlang-argument-indent))
+ ;; Empty line, or end; treat it as the end of
+ ;; the block. (Here we have a choice: should
+ ;; the user be forced to reindent continued
+ ;; lines, or should the "end" be reindented?)
+
+ ;; Avoid treating comments a continued line.
+ ((= (following-char) ?%)
+ base)
+ ;; Continued line (e.g. line beginning
+ ;; with an operator.)
+ (t (+ base erlang-indent-level)))))))))
+ ))
+ ((eq (car stack-top) 'when)
+ (goto-char (nth 1 stack-top))
+ (if (looking-at "when\\s *\\($\\|%\\)")
+ (progn
+ (erlang-pop stack)
+ (if (and stack (memq (nth 0 (car stack)) '(icr fun)))
+ (progn
+ (goto-char (nth 1 (car stack)))
+ (+ (nth 2 (car stack)) erlang-indent-guard
+ ;; receive XYZ or receive
+ ;; XYZ
+ ;; This if thing does not seem to be needed
+ ;;(if (looking-at "[a-z]+\\s *\\($\\|%\\)")
+ ;; erlang-indent-level
+ ;; (* 2 erlang-indent-level))))
+ (* 2 erlang-indent-level)))
+ ;;erlang-indent-level))
+ (+ erlang-indent-level erlang-indent-guard)))
+ ;; "when" is followed by code, let's indent to the same
+ ;; column.
+ (forward-char 4) ; Skip "when"
+ (skip-chars-forward " \t")
+ (current-column)))
+ ;; Type and Spec indentation
+ ((eq (car stack-top) '::)
+ (cond ((null erlang-argument-indent)
+ ;; indent to next column.
+ (+ 2 (nth 2 stack-top)))
+ ((looking-at "::[^_a-zA-Z0-9]")
+ (nth 2 stack-top))
+ (t
+ (goto-char (nth 1 stack-top))
+ (cond ((looking-at "::\\s *\\($\\|%\\)")
+ ;; Line ends with ::
+ (+ (erlang-indent-find-preceding-expr 2)
+ erlang-argument-indent))
+ ;; (* 2 erlang-indent-level))
+ (t
+ ;; Indent to the same column as the first
+ ;; argument.
+ (goto-char (+ 2 (nth 1 stack-top)))
+ (skip-chars-forward " \t")
+ (current-column))))))
+ )))
+
+
+(defun erlang-indent-find-base (stack indent-point &optional offset skip)
+ "Find the base column for current stack."
+ (or skip (setq skip 2))
+ (or offset (setq offset erlang-indent-level))
+ (save-excursion
+ (let* ((stack-top (car stack)))
+ (goto-char (nth 1 stack-top))
+ (if (< skip (- (point-max) (point)))
+ (progn
+ (forward-char skip)
+ (if (looking-at "\\s *\\($\\|%\\)")
+ (progn
+ (if (memq (car stack-top) '(-> ||))
+ (erlang-pop stack))
+ ;; Take parent identation + offset,
+ ;; else just erlang-indent-level if no parent
+ (if stack
+ (+ (erlang-caddr (car stack))
+ offset)
+ erlang-indent-level))
+ (erlang-skip-blank indent-point)
+ (current-column)))
+ (+ (current-column) skip)))))
+
+
+;; Does not handle `begin' .. `end'.
+(defun erlang-indent-find-preceding-expr (&optional arg)
+ "Return the first column of the preceding expression.
+This assumes that the preceding expression is either simple
+\(i.e. an atom) or parenthesized."
+ (save-excursion
+ (or arg (setq arg 1))
+ (forward-sexp (- arg))
+ (let ((col (current-column)))
+ (skip-chars-backward " \t")
+ ;; Needed to match the colon in "'foo':'bar'".
+ (if (not (memq (preceding-char) '(?# ?:)))
+ col
+ (backward-char 1)
+ (forward-sexp -1)
+ (current-column)))))
+
+(defun erlang-indent-parenthesis (stack-position)
+ (let ((previous (erlang-indent-find-preceding-expr)))
+ (if (> previous stack-position)
+ (+ stack-position erlang-argument-indent)
+ (+ previous erlang-argument-indent))))
+
+(defun erlang-skip-blank (&optional lim)
+ "Skip over whitespace and comments until limit reached."
+ (or lim (setq lim (point-max)))
+ (let (stop)
+ (while (and (not stop) (< (point) lim))
+ (cond ((= (following-char) ?%)
+ (skip-chars-forward "^\n" lim))
+ ((= (following-char) ?\n)
+ (skip-chars-forward "\n" lim))
+ ((looking-at "\\s ")
+ (if (re-search-forward "\\S " lim 'move)
+ (forward-char -1)))
+ (t
+ (setq stop t))))
+ stop))
+
+(defun erlang-at-keyword ()
+ "Are we looking at an Erlang keyword which will increase indentation?"
+ (looking-at (concat "\\(when\\|if\\|fun\\|case\\|begin\\|query\\|"
+ "of\\|receive\\|after\\|catch\\|try\\)[^_a-zA-Z0-9]")))
+
+(defun erlang-at-operator ()
+ "Are we looking at an Erlang operator?"
+ (looking-at
+ "\\(bnot\\|div\\|mod\\|band\\|bor\\|bxor\\|bsl\\|bsr\\)[^_a-zA-Z0-9]"))
+
+(defun erlang-comment-indent ()
+ "Compute Erlang comment indentation.
+
+Used both by `indent-for-comment' and the Erlang specific indentation
+commands."
+ (cond ((looking-at "%%%") 0)
+ ((looking-at "%%")
+ (or (erlang-calculate-indent)
+ (current-indentation)))
+ (t
+ (save-excursion
+ (skip-chars-backward " \t")
+ (max (if (bolp) 0 (1+ (current-column)))
+ comment-column)))))
+
+;;; Erlang movement commands
+
+;; All commands below work as movement commands. I.e. if the point is
+;; at the end of the clause, and the command `erlang-end-of-clause' is
+;; executed, the point is moved to the end of the NEXT clause. (This
+;; mimics the behaviour of `end-of-defun'.)
+;;
+;; Personally I would like to rewrite them to be "pure", and add a set
+;; of movement functions, like `erlang-next-clause',
+;; `erlang-previous-clause', and the same for functions.
+;;
+;; The current implementation makes it hopeless to use the functions as
+;; subroutines in more complex commands. /andersl
+
+(defun erlang-beginning-of-clause (&optional arg)
+ "Move backward to previous start of clause.
+With argument, do this that many times.
+Return t unless search stops due to end of buffer."
+ (interactive "p")
+ (or arg (setq arg 1))
+ (if (< arg 0)
+ ;; Step back to the end of the previous line, unless we are at
+ ;; the beginning of the buffer. The reason for this move is
+ ;; that the regexp below includes the last character of the
+ ;; previous line.
+ (if (bobp)
+ (or (looking-at "\n")
+ (forward-char 1))
+ (forward-char -1)
+ (if (looking-at "\\`\n")
+ (forward-char 1))))
+ ;; The regexp matches a function header that isn't
+ ;; included in a string.
+ (and (re-search-forward "\\(\\`\\|\\`\n\\|[^\\]\n\\)\\(-?[a-z]\\|'\\|-\\)"
+ nil 'move (- arg))
+ (let ((beg (match-beginning 2)))
+ (and beg (goto-char beg))
+ t)))
+
+(defun erlang-end-of-clause (&optional arg)
+ "Move to the end of the current clause.
+With argument, do this that many times."
+ (interactive "p")
+ (or arg (setq arg 1))
+ (while (and (looking-at "[ \t]*[%\n]")
+ (zerop (forward-line 1))))
+ ;; Move to the next clause.
+ (erlang-beginning-of-clause (- arg))
+ (beginning-of-line);; Just to be sure...
+ (let ((continue t))
+ (while (and (not (bobp)) continue)
+ (forward-line -1)
+ (skip-chars-forward " \t")
+ (if (looking-at "[%\n]")
+ nil
+ (end-of-line)
+ (setq continue nil)))))
+
+(defun erlang-mark-clause ()
+ "Put mark at end of clause, point at beginning."
+ (interactive)
+ (push-mark (point))
+ (erlang-end-of-clause 1)
+ ;; Sets the region. In Emacs 19 and XEmacs, we want to activate
+ ;; the region.
+ (condition-case nil
+ (push-mark (point) nil t)
+ (error (push-mark (point))))
+ (erlang-beginning-of-clause 1)
+ ;; The above function deactivates the mark.
+ (if (boundp 'deactivate-mark)
+ (funcall (symbol-function 'set) 'deactivate-mark nil)))
+
+(defun erlang-beginning-of-function (&optional arg)
+ "Move backward to previous start of function.
+With positive argument, do this that many times.
+With negative argument, search forward.
+
+Return t unless search stops due to end of buffer."
+ (interactive "p")
+ (or arg (setq arg 1))
+ (cond
+ ;; Search backward
+ ((> arg 0)
+ (while (and (> arg 0)
+ (and (erlang-beginning-of-clause 1)
+ (let ((start (point))
+ (name (erlang-name-of-function))
+ (arity (erlang-get-function-arity)))
+ ;; Note: "arity" is nil for e.g. "-import", hence
+ ;; two "-import" clauses are not considered to
+ ;; be part of the same function.
+ (while (and (erlang-beginning-of-clause 1)
+ (string-equal name
+ (erlang-name-of-function))
+ arity
+ (equal arity
+ (erlang-get-function-arity)))
+ (setq start (point)))
+ (goto-char start)
+ t)))
+ (setq arg (1- arg))))
+ ;; Search forward
+ ((< arg 0)
+ (end-of-line)
+ (erlang-beginning-of-clause 1)
+ ;; Step -arg functions forward.
+ (while (and (< arg 0)
+ ;; Step one function forward, or stop if the end of
+ ;; the buffer was reached. Return t if we found the
+ ;; function.
+ (let ((name (erlang-name-of-function))
+ (arity (erlang-get-function-arity))
+ (found (erlang-beginning-of-clause -1)))
+ (while (and found
+ (string-equal name (erlang-name-of-function))
+ arity
+ (equal arity
+ (erlang-get-function-arity)))
+ (setq found (erlang-beginning-of-clause -1)))
+ found))
+ (setq arg (1+ arg)))))
+ (zerop arg))
+
+
+(defun erlang-end-of-function (&optional arg)
+ "Move forward to next end of function.
+
+With argument, do this that many times.
+With negative argument go towards the beginning of the buffer."
+ (interactive "p")
+ (or arg (setq arg 1))
+ (let ((first t))
+ ;; Forward
+ (while (and (> arg 0) (< (point) (point-max)))
+ (let ((pos (point)))
+ (while (progn
+ (if (and first
+ (progn
+ (forward-char 1)
+ (erlang-beginning-of-clause 1)))
+ nil
+ (or (bobp) (forward-char -1))
+ (erlang-beginning-of-clause -1))
+ (setq first nil)
+ (erlang-pass-over-function)
+ (skip-chars-forward " \t")
+ (if (looking-at "[%\n]")
+ (forward-line 1))
+ (<= (point) pos))))
+ (setq arg (1- arg)))
+ ;; Backward
+ (while (< arg 0)
+ (let ((pos (point)))
+ (erlang-beginning-of-clause 1)
+ (erlang-pass-over-function)
+ (forward-line 1)
+ (if (>= (point) pos)
+ (if (erlang-beginning-of-function 2)
+ (progn
+ (erlang-pass-over-function)
+ (skip-chars-forward " \t")
+ (if (looking-at "[%\n]")
+ (forward-line 1)))
+ (goto-char (point-min)))))
+ (setq arg (1+ arg)))))
+
+(eval-and-compile
+ (if (default-boundp 'beginning-of-defun-function)
+ (defalias 'erlang-mark-function 'mark-defun)
+ (defun erlang-mark-function ()
+ "Put mark at end of function, point at beginning."
+ (interactive)
+ (push-mark (point))
+ (erlang-end-of-function 1)
+ ;; Sets the region. In Emacs 19 and XEmacs, we want to activate
+ ;; the region.
+ (condition-case nil
+ (push-mark (point) nil t)
+ (error (push-mark (point))))
+ (erlang-beginning-of-function 1)
+ ;; The above function deactivates the mark.
+ (if (boundp 'deactivate-mark)
+ (funcall (symbol-function 'set) 'deactivate-mark nil)))))
+
+(defun erlang-pass-over-function ()
+ (while (progn
+ (erlang-skip-blank)
+ (and (not (looking-at "\\.\\(\\s \\|\n\\|\\s<\\)"))
+ (not (eobp))))
+ (forward-sexp 1))
+ (if (not (eobp))
+ (forward-char 1)))
+
+(defun erlang-name-of-function ()
+ (save-excursion
+ ;; Skip over attribute leader.
+ (if (looking-at "-[ \t]*")
+ (re-search-forward "-[ \t]*" nil 'move))
+ (let ((start (point)))
+ (forward-sexp 1)
+ (buffer-substring start (point)))))
+
+
+;;; Miscellaneous
+
+(defun erlang-fill-paragraph (&optional justify)
+ "Like \\[fill-paragraph], but handle Erlang comments.
+If any of the current line is a comment, fill the comment or the
+paragraph of it that point is in, preserving the comment's indentation
+and initial `%':s."
+ (interactive "P")
+ (let ((has-comment nil)
+ ;; If has-comment, the appropriate fill-prefix for the comment.
+ comment-fill-prefix)
+ ;; Figure out what kind of comment we are looking at.
+ (save-excursion
+ (beginning-of-line)
+ (cond
+ ;; Find the command prefix.
+ ((looking-at (concat "\\s *" comment-start-skip))
+ (setq has-comment t)
+ (setq comment-fill-prefix (buffer-substring (match-beginning 0)
+ (match-end 0))))
+ ;; A line with some code, followed by a comment? Remember that the
+ ;; % which starts the comment shouldn't be part of a string or
+ ;; character.
+ ((progn
+ (while (not (looking-at "%\\|$"))
+ (skip-chars-forward "^%\n\"\\\\")
+ (cond
+ ((eq (char-after (point)) ?\\) (forward-char 2))
+ ((eq (char-after (point)) ?\") (forward-sexp 1))))
+ (looking-at comment-start-skip))
+ (setq has-comment t)
+ (setq comment-fill-prefix
+ (concat (make-string (current-column) ? )
+ (buffer-substring (match-beginning 0) (match-end 0)))))))
+ (if (not has-comment)
+ (fill-paragraph justify)
+ ;; Narrow to include only the comment, and then fill the region.
+ (save-restriction
+ (narrow-to-region
+ ;; Find the first line we should include in the region to fill.
+ (save-excursion
+ (while (and (zerop (forward-line -1))
+ (looking-at "^\\s *%")))
+ ;; We may have gone to far. Go forward again.
+ (or (looking-at "^\\s *%")
+ (forward-line 1))
+ (point))
+ ;; Find the beginning of the first line past the region to fill.
+ (save-excursion
+ (while (progn (forward-line 1)
+ (looking-at "^\\s *%")))
+ (point)))
+ ;; Lines with only % on them can be paragraph boundaries.
+ (let ((paragraph-start (concat paragraph-start "\\|^[ \t%]*$"))
+ (paragraph-separate (concat paragraph-start "\\|^[ \t%]*$"))
+ (fill-prefix comment-fill-prefix))
+ (fill-paragraph justify))))))
+
+
+(defun erlang-uncomment-region (beg end)
+ "Uncomment all commented lines in the region."
+ (interactive "r")
+ (uncomment-region beg end))
+
+
+(defun erlang-generate-new-clause ()
+ "Create additional Erlang clause header.
+
+Parses the source file for the name of the current Erlang function.
+Create the header containing the name, A pair of parentheses,
+and an arrow. The space between the function name and the
+first parenthesis is preserved. The point is placed between
+the parentheses."
+ (interactive)
+ (let ((name (save-excursion
+ (and (erlang-beginning-of-clause)
+ (erlang-get-function-name t))))
+ (arrow (save-excursion
+ (and (erlang-beginning-of-clause)
+ (erlang-get-function-arrow)))))
+ (if (or (null arrow) (null name))
+ (error "Can't find name of current Erlang function"))
+ (if (and (bolp) (eolp))
+ nil
+ (end-of-line)
+ (newline))
+ (insert name)
+ (save-excursion
+ (insert ") " arrow))
+ (if erlang-new-clause-with-arguments
+ (erlang-clone-arguments))))
+
+
+(defun erlang-clone-arguments ()
+ "Insert, at the point, the argument list of the previous clause.
+
+The mark is set at the beginning of the inserted text, the point
+at the end."
+ (interactive)
+ (let ((args (save-excursion
+ (beginning-of-line)
+ (and (erlang-beginning-of-clause)
+ (erlang-get-function-arguments))))
+ (p (point)))
+ (if (null args)
+ (error "Can't clone argument list"))
+ (insert args)
+ (set-mark p)))
+
+;;; Information retrieval functions.
+
+(defun erlang-buffer-substring (beg end)
+ "Like `buffer-substring-no-properties'.
+Although, this function works on all versions of Emacs."
+ (if (fboundp 'buffer-substring-no-properties)
+ (funcall (symbol-function 'buffer-substring-no-properties) beg end)
+ (buffer-substring beg end)))
+
+
+(defun erlang-get-module ()
+ "Return the name of the module as specified by `-module'.
+
+Return nil if file contains no `-module' attribute."
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (let ((md (match-data)))
+ (unwind-protect
+ (if (re-search-forward
+ (eval-when-compile
+ (concat "^-module\\s *(\\s *\\(\\("
+ erlang-atom-regexp
+ "\\)?\\)\\s *)\\s *\\."))
+ (point-max) t)
+ (erlang-remove-quotes
+ (erlang-buffer-substring (match-beginning 1)
+ (match-end 1)))
+ nil)
+ (store-match-data md))))))
+
+
+(defun erlang-get-module-from-file-name (&optional file)
+ "Extract the module name from a file name.
+
+First, the directory part is removed. Second, the part of the file name
+matching `erlang-file-name-extension-regexp' is removed.
+
+Should the match fail, nil is returned.
+
+By modifying `erlang-file-name-extension-regexp' to match files other
+than Erlang source files, Erlang specific functions could be applied on
+non-Erlang files. Most notably; the support for Erlang modules in the
+tags system could be used by files written in other languages."
+ (or file (setq file buffer-file-name))
+ (if (null file)
+ nil
+ (setq file (file-name-nondirectory file))
+ (if (string-match erlang-file-name-extension-regexp file)
+ (substring file 0 (match-beginning 0))
+ nil)))
+
+
+;; Used by `erlang-get-export' and `erlang-get-import'.
+
+(defun erlang-get-function-arity-list ()
+ "Parse list of `function/arity' as used by `-import' and `-export'.
+
+Point must be before the opening bracket. When the
+function returns the point will be placed after the closing bracket.
+
+The function does not return an error if the list is incorrectly
+formatted.
+
+Return list of (function . arity). The order of the returned list
+corresponds to the order of the parsed Erlang list."
+ (let ((res '()))
+ (erlang-skip-blank)
+ (forward-char 1)
+ (if (not (eq (preceding-char) ?\[))
+ '() ; Not looking at an Erlang list.
+ (while ; Note: `while' has no body.
+ (progn
+ (erlang-skip-blank)
+ (and (looking-at (eval-when-compile
+ (concat erlang-atom-regexp "/\\([0-9]+\\)\\>")))
+ (progn
+ (setq res (cons
+ (cons
+ (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning 1) (match-end 1)))
+ (erlang-string-to-int
+ (erlang-buffer-substring
+ (match-beginning
+ (+ 1 erlang-atom-regexp-matches))
+ (match-end
+ (+ 1 erlang-atom-regexp-matches)))))
+ res))
+ (goto-char (match-end 0))
+ (erlang-skip-blank)
+ (forward-char 1)
+ ;; Test if there are more exported functions.
+ (eq (preceding-char) ?,))))))
+ (nreverse res)))
+
+
+;;; Note that `-export' and the open parenthesis must be written on
+;;; the same line.
+
+(defun erlang-get-export ()
+ "Return a list of `(function . arity)' as specified by `-export'."
+ (save-excursion
+ (goto-char (point-min))
+ (let ((md (match-data))
+ (res '()))
+ (unwind-protect
+ (progn
+ (while (re-search-forward "^-export\\s *(" (point-max) t)
+ (erlang-skip-blank)
+ (setq res (nconc res (erlang-get-function-arity-list))))
+ res)
+ (store-match-data md)))))
+
+
+(defun erlang-get-import ()
+ "Parse an Erlang source file for imported functions.
+
+Return an alist with module name as car part and list of conses containing
+function and arity as cdr part."
+ (save-excursion
+ (goto-char (point-min))
+ (let ((md (match-data))
+ (res '()))
+ (unwind-protect
+ (progn
+ (while (re-search-forward "^-import\\s *(" (point-max) t)
+ (erlang-skip-blank)
+ (if (looking-at erlang-atom-regexp)
+ (let ((module (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning 0)
+ (match-end 0)))))
+ (goto-char (match-end 0))
+ (erlang-skip-blank)
+ (if (eq (following-char) ?,)
+ (progn
+ (forward-char 1)
+ (erlang-skip-blank)
+ (let ((funcs (erlang-get-function-arity-list))
+ (pair (assoc module res)))
+ (if pair
+ (setcdr pair (nconc (cdr pair) funcs))
+ (setq res (cons (cons module funcs)
+ res)))))))))
+ (nreverse res))
+ (store-match-data md)))))
+
+
+(defun erlang-get-function-name (&optional arg)
+ "Return name of current function, or nil.
+
+If optional argument is non-nil, everything up to and including
+the first `(' is returned.
+
+Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
+ (save-excursion
+ (if (not (eobp)) (forward-char 1))
+ (and (erlang-beginning-of-clause)
+ (erlang-get-function-name t)))"
+ (let ((n (if arg 0 1)))
+ (and (looking-at (eval-when-compile
+ (concat "^" erlang-atom-regexp "\\s *(")))
+ (erlang-buffer-substring (match-beginning n) (match-end n)))))
+
+
+(defun erlang-get-function-arrow ()
+ "Return arrow of current function, could be \"->\" or nil.
+
+Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
+ (save-excursion
+ (if (not (eobp)) (forward-char 1))
+ (and (erlang-beginning-of-clause)
+ (erlang-get-function-arrow)))"
+ (and
+ (save-excursion
+ (re-search-forward "[^-:]*-\\|:" (point-max) t)
+ (erlang-buffer-substring (- (point) 1) (+ (point) 1)))))
+
+(defun erlang-get-function-arity ()
+ "Return the number of arguments of function at point, or nil."
+ (and (looking-at (eval-when-compile
+ (concat "^" erlang-atom-regexp "\\s *(")))
+ (save-excursion
+ (goto-char (match-end 0))
+ (condition-case nil
+ (let ((res 0)
+ (cont t))
+ (while cont
+ (cond ((eobp)
+ (setq res nil)
+ (setq cont nil))
+ ((looking-at "\\s *)")
+ (setq cont nil))
+ ((looking-at "\\s *\\($\\|%\\)")
+ (forward-line 1))
+ ((looking-at "\\s *,")
+ (setq res (+ 1 res))
+ (goto-char (match-end 0)))
+ (t
+ (when (zerop res)
+ (setq res (+ 1 res)))
+ (forward-sexp 1))))
+ res)
+ (error nil)))))
+
+(defun erlang-get-function-arguments ()
+ "Return arguments of current function, or nil."
+ (if (not (looking-at (eval-when-compile
+ (concat "^" erlang-atom-regexp "\\s *("))))
+ nil
+ (save-excursion
+ (condition-case nil
+ (let ((start (match-end 0)))
+ (goto-char (- start 1))
+ (forward-sexp)
+ (erlang-buffer-substring start (- (point) 1)))
+ (error nil)))))
+
+
+(defun erlang-get-function-under-point ()
+ "Return the module and function under the point, or nil.
+
+Should no explicit module name be present at the point, the
+list of imported functions is searched.
+
+The following could be returned:
+ (\"module\" \"function\") -- Both module and function name found.
+ (nil \"function\") -- No module name was found.
+ nil -- No function name found
+
+In the future the list may contain more elements."
+ (save-excursion
+ (let ((md (match-data))
+ (res nil))
+ (if (eq (char-syntax (following-char)) ? )
+ (skip-chars-backward " \t"))
+ (skip-chars-backward "a-zA-Z0-9_:'")
+ (cond ((looking-at (eval-when-compile
+ (concat erlang-atom-regexp ":" erlang-atom-regexp)))
+ (setq res (list
+ (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning 1) (match-end 1)))
+ (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning (1+ erlang-atom-regexp-matches))
+ (match-end (1+ erlang-atom-regexp-matches)))))))
+ ((looking-at erlang-atom-regexp)
+ (let ((fk (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning 0) (match-end 0))))
+ (mod nil)
+ (imports (erlang-get-import)))
+ (while (and imports (null mod))
+ (if (assoc fk (cdr (car imports)))
+ (setq mod (car (car imports)))
+ (setq imports (cdr imports))))
+ (setq res (list mod fk)))))
+ (store-match-data md)
+ res)))
+
+
+;; TODO: Escape single quotes inside the string without
+;; replace-regexp-in-string.
+(defun erlang-add-quotes-if-needed (str)
+ "Return STR, possibly with quotes."
+ (let ((case-fold-search nil)) ; force string matching to be case sensitive
+ (if (and (stringp str)
+ (not (string-match (eval-when-compile
+ (concat "\\`" erlang-atom-regexp "\\'")) str)))
+ (progn (if (fboundp 'replace-regexp-in-string)
+ (setq str (replace-regexp-in-string "'" "\\'" str t t )))
+ (concat "'" str "'"))
+ str)))
+
+
+(defun erlang-remove-quotes (str)
+ "Return STR without quotes, if present."
+ (let ((md (match-data)))
+ (prog1
+ (if (string-match "\\`'\\(.*\\)'\\'" str)
+ (substring str 1 -1)
+ str)
+ (store-match-data md))))
+
+
+;;; Check module name
+
+;; The function `write-file', bound to C-x C-w, calls
+;; `set-visited-file-name' which clears the hook. :-(
+;; To make sure that the hook always is present, we advise
+;; `set-visited-file-name'.
+(defun erlang-check-module-name-init ()
+ "Initialize the functionality to compare file and module names.
+
+Unless we have `before-save-hook', we redefine the function
+`set-visited-file-name' since it clears the variable
+`local-write-file-hooks'. The original function definition is
+stored in `erlang-orig-set-visited-file-name'."
+ (if (boundp 'before-save-hook)
+ ;; If we have that, `make-local-hook' is obsolete.
+ (add-hook 'before-save-hook 'erlang-check-module-name nil t)
+ (require 'advice)
+ (unless (ad-advised-definition-p 'set-visited-file-name)
+ (defadvice set-visited-file-name (after erlang-set-visited-file-name
+ activate)
+ (if (eq major-mode 'erlang-mode)
+ (add-hook 'local-write-file-hooks 'erlang-check-module-name))))
+ (add-hook 'local-write-file-hooks 'erlang-check-module-name)))
+
+
+(defun erlang-check-module-name ()
+ "If the module name doesn't match file name, ask for permission to change.
+
+The variable `erlang-check-module-name' controls the behaviour of this
+function. It it is nil, this function does nothing. If it is t, the
+source is silently changed. If it is set to the atom `ask', the user
+is prompted.
+
+This function is normally placed in the hook `local-write-file-hooks'."
+ (if erlang-check-module-name
+ (let ((mn (erlang-add-quotes-if-needed
+ (erlang-get-module)))
+ (fn (erlang-add-quotes-if-needed
+ (erlang-get-module-from-file-name (buffer-file-name)))))
+ (if (and (stringp mn) (stringp fn))
+ (or (string-equal mn fn)
+ (if (or (eq erlang-check-module-name t)
+ (y-or-n-p
+ "Module does not match file name. Modify source? "))
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (if (re-search-forward
+ (eval-when-compile
+ (concat "^-module\\s *(\\s *\\(\\("
+ erlang-atom-regexp
+ "\\)?\\)\\s *)\\s *\\."))
+ (point-max) t)
+ (progn
+ (goto-char (match-beginning 1))
+ (delete-region (match-beginning 1)
+ (match-end 1))
+ (insert fn))))))))))
+ ;; Must return nil since it is added to `local-write-file-hook'.
+ nil)
+
+
+;;; Electric functions.
+
+(defun erlang-electric-semicolon (&optional arg)
+ "Insert a semicolon character and possibly a prototype for the next line.
+
+The variable `erlang-electric-semicolon-criteria' states a criterion,
+when fulfilled a newline is inserted, the next line is indented and a
+prototype for the next line is inserted. Normally the prototype
+consists of \" ->\". Should the semicolon end the clause a new clause
+header is generated.
+
+The variable `erlang-electric-semicolon-insert-blank-lines' controls
+the number of blank lines inserted between the current line and new
+function header.
+
+Behaves just like the normal semicolon when supplied with a
+numerical arg, point is inside string or comment, or when there are
+non-whitespace characters following the point on the current line."
+ (interactive "P")
+ (self-insert-command (prefix-numeric-value arg))
+ (if (or arg
+ (and (listp erlang-electric-commands)
+ (not (memq 'erlang-electric-semicolon
+ erlang-electric-commands)))
+ (erlang-in-literal)
+ (not (looking-at "\\s *\\(%.*\\)?$"))
+ (null (erlang-test-criteria-list
+ erlang-electric-semicolon-criteria)))
+ (setq erlang-electric-newline-inhibit nil)
+ (setq erlang-electric-newline-inhibit t)
+ (undo-boundary)
+ (end-of-line)
+ (newline)
+ (if (condition-case nil
+ (progn (erlang-indent-line) t)
+ (error (if (bolp) (delete-backward-char 1))))
+ (if (not (bolp))
+ (save-excursion
+ (insert " ->"))
+ (condition-case nil
+ (progn
+ (erlang-generate-new-clause)
+ (if erlang-electric-semicolon-insert-blank-lines
+ (save-excursion
+ (beginning-of-line)
+ (newline
+ erlang-electric-semicolon-insert-blank-lines))))
+ (error (if (bolp) (delete-backward-char 1))))))))
+
+
+(defun erlang-electric-comma (&optional arg)
+ "Insert a comma character and possibly a new indented line.
+The variable `erlang-electric-comma-criteria' states a criterion,
+when fulfilled a newline is inserted and the next line is indented.
+
+Behaves just like the normal comma when supplied with a
+numerical arg, point is inside string or comment, or when there are
+non-whitespace characters following the point on the current line."
+ (interactive "P")
+
+ (self-insert-command (prefix-numeric-value arg))
+
+ (if (or arg
+ (and (listp erlang-electric-commands)
+ (not (memq 'erlang-electric-comma erlang-electric-commands)))
+ (erlang-in-literal)
+ (not (looking-at "\\s *\\(%.*\\)?$"))
+ (null (erlang-test-criteria-list
+ erlang-electric-comma-criteria)))
+ (setq erlang-electric-newline-inhibit nil)
+ (setq erlang-electric-newline-inhibit t)
+ (undo-boundary)
+ (end-of-line)
+ (newline)
+ (condition-case nil
+ (erlang-indent-line)
+ (error (if (bolp) (delete-backward-char 1))))))
+
+(defun erlang-electric-lt (&optional arg)
+ "Insert a less-than sign, and optionally mark it as an open paren."
+
+ (interactive "p")
+
+ (self-insert-command arg)
+
+ ;; Was this the second char in bit-syntax open (`<<')?
+ (unless (< (point) 2)
+ (save-excursion
+ (backward-char 2)
+ (when (and (eq (char-after (point)) ?<)
+ (not (eq (get-text-property (point) 'category)
+ 'bitsyntax-open-inner)))
+ ;; Then mark the two chars...
+ (put-text-property (point) (1+ (point))
+ 'category 'bitsyntax-open-outer)
+ (forward-char 1)
+ (put-text-property (point) (1+ (point))
+ 'category 'bitsyntax-open-inner)
+ ;;...and unmark any subsequent less-than chars.
+ (forward-char 1)
+ (while (eq (char-after (point)) ?<)
+ (remove-text-properties (point) (1+ (point))
+ '(category nil))
+ (forward-char 1))))))
+
+(defun erlang-after-bitsyntax-close ()
+ "Return t if point is immediately after a bit-syntax close parenthesis (`>>')."
+ (and (>= (point) 2)
+ (save-excursion
+ (backward-char 2)
+ (and (eq (char-after (point)) ?>)
+ (not (eq (get-text-property (point) 'category)
+ 'bitsyntax-close-outer))))))
+
+(defun erlang-after-arrow ()
+ "Return true if point is immediately after a function arrow (`->')."
+ (and (>= (point) 2)
+ (and
+ (save-excursion
+ (backward-char)
+ (eq (char-before (point)) ?-))
+ (or (not (listp erlang-electric-commands))
+ (memq 'erlang-electric-gt
+ erlang-electric-commands))
+ (not (erlang-in-literal))
+ (looking-at "\\s *\\(%.*\\)?$")
+ (erlang-test-criteria-list erlang-electric-arrow-criteria))))
+
+
+(defun erlang-electric-gt (&optional arg)
+ "Insert a greater-than sign, and optionally mark it as a close paren."
+
+ (interactive "p")
+
+ (self-insert-command arg)
+
+ (cond
+ ;; Did we just write a bit-syntax close (`>>')?
+ ((erlang-after-bitsyntax-close)
+ (save-excursion
+ ;; Then mark the two chars...
+ (backward-char 2)
+ (put-text-property (point) (1+ (point))
+ 'category 'bitsyntax-close-inner)
+ (forward-char)
+ (put-text-property (point) (1+ (point))
+ 'category 'bitsyntax-close-outer)
+ ;;...and unmark any subsequent greater-than chars.
+ (forward-char)
+ (while (eq (char-after (point)) ?>)
+ (remove-text-properties (point) (1+ (point))
+ '(category nil))
+ (forward-char))))
+
+ ;; Did we just write a function arrow (`->')?
+ ((erlang-after-arrow)
+ (let ((erlang-electric-newline-inhibit t))
+ (undo-boundary)
+ (end-of-line)
+ (newline)
+ (condition-case nil
+ (erlang-indent-line)
+ (error (if (bolp) (delete-backward-char 1))))))
+
+ ;; Then it's just a plain greater-than.
+ (t
+ nil)))
+
+
+(defun erlang-electric-arrow\ off (&optional arg)
+ "Insert a '>'-sign and possibly a new indented line.
+
+This command is only `electric' when the `>' is part of an `->' arrow.
+The variable `erlang-electric-arrow-criteria' states a sequence of
+criteria, which decides when a newline should be inserted and the next
+line indented.
+
+It behaves just like the normal greater than sign when supplied with a
+numerical arg, point is inside string or comment, or when there are
+non-whitespace characters following the point on the current line.
+
+After being split/merged into `erlang-after-arrow' and
+`erlang-electric-gt', it is now unused and disabled."
+ (interactive "P")
+ (let ((prec (preceding-char)))
+ (self-insert-command (prefix-numeric-value arg))
+ (if (or arg
+ (and (listp erlang-electric-commands)
+ (not (memq 'erlang-electric-arrow
+ erlang-electric-commands)))
+ (not (eq prec ?-))
+ (erlang-in-literal)
+ (not (looking-at "\\s *\\(%.*\\)?$"))
+ (null (erlang-test-criteria-list
+ erlang-electric-arrow-criteria)))
+ (setq erlang-electric-newline-inhibit nil)
+ (setq erlang-electric-newline-inhibit t)
+ (undo-boundary)
+ (end-of-line)
+ (newline)
+ (condition-case nil
+ (erlang-indent-line)
+ (error (if (bolp) (delete-backward-char 1)))))))
+
+
+(defun erlang-electric-newline (&optional arg)
+ "Break line at point and indent, continuing comment if within one.
+The variable `erlang-electric-newline-criteria' states a criterion,
+when fulfilled a newline is inserted and the next line is indented.
+
+Should the current line begin with a comment, and the variable
+`comment-multi-line' be non-nil, a new comment start is inserted.
+
+Should the previous command be another electric command we assume that
+the user pressed newline out of old habit, hence we will do nothing."
+ (interactive "P")
+ (cond ((and (not arg)
+ erlang-electric-newline-inhibit
+ (memq last-command erlang-electric-newline-inhibit-list))
+ ()) ; Do nothing!
+ ((or arg
+ (and (listp erlang-electric-commands)
+ (not (memq 'erlang-electric-newline
+ erlang-electric-commands)))
+ (null (erlang-test-criteria-list
+ erlang-electric-newline-criteria)))
+ (newline (prefix-numeric-value arg)))
+ (t
+ (if (and comment-multi-line
+ (save-excursion
+ (beginning-of-line)
+ (looking-at (concat "\\s *" comment-start-skip))))
+ (let ((str (buffer-substring
+ (or (match-end 1) (match-beginning 0))
+ (min (match-end 0) (point)))))
+ (newline)
+ (undo-boundary)
+ (insert str))
+ (newline)
+ (undo-boundary)
+ (indent-according-to-mode)))))
+
+
+(defun erlang-test-criteria-list (criteria)
+ "Given a list of criterion functions, test if criteria are fulfilled.
+
+Each element in the criteria list can a function returning nil, t or
+the atom `stop'. t means that the criterion is fulfilled, `stop' means
+that it isn't fulfilled and that the search should stop,
+and nil means continue searching.
+
+Should the list contain the atom t the criterion is assumed to be
+fulfilled, unless preceded by a function returning `stop', of course.
+
+Should the argument be the atom t instead of a list, the criterion is
+assumed to be trivially true.
+
+Should all functions return nil, the criteria are assumed not to be
+fulfilled.
+
+Return t if criteria fulfilled, nil otherwise."
+ (if (eq criteria t)
+ t
+ (save-excursion
+ (let ((answer nil))
+ (while (and criteria (null answer))
+ (if (eq (car criteria) t)
+ (setq answer t)
+ (setq answer (funcall (car criteria))))
+ (setq criteria (cdr criteria)))
+ (if (and answer (not (eq answer 'stop)))
+ t
+ nil)))))
+
+
+(defun erlang-in-literal (&optional lim)
+ "Test if point is in string, quoted atom or comment.
+
+Return one of the three atoms `atom', `string', and `comment'.
+Should the point be inside none of the above mentioned types of
+context, nil is returned."
+ (save-excursion
+ (let* ((lim (or lim (save-excursion
+ (erlang-beginning-of-clause)
+ (point))))
+ (state (if (fboundp 'syntax-ppss) ; post Emacs 21.3
+ (funcall (symbol-function 'syntax-ppss))
+ (parse-partial-sexp lim (point)))))
+ (cond
+ ((eq (nth 3 state) ?') 'atom)
+ ((nth 3 state) 'string)
+ ((nth 4 state) 'comment)
+ (t nil)))))
+
+
+(defun erlang-at-end-of-function-p ()
+ "Test if point is at end of an Erlang function.
+
+This function is designed to be a member of a criteria list."
+ (eq (save-excursion (erlang-skip-blank) (point))
+ (save-excursion
+ (erlang-beginning-of-function -1) (point))))
+
+
+(defun erlang-at-end-of-clause-p ()
+ "Test if point is at end of an Erlang clause.
+
+This function is designed to be a member of a criteria list."
+ (eq (save-excursion (erlang-skip-blank) (point))
+ (save-excursion
+ (erlang-beginning-of-clause -1) (point))))
+
+
+(defun erlang-stop-when-inside-argument-list ()
+ "Return `stop' if inside parenthesis list, nil otherwise.
+
+Knows about the list comprehension syntax. When the point is
+after `||', `stop' is not returned.
+
+This function is designed to be a member of a criteria list."
+ (save-excursion
+ (condition-case nil
+ (let ((orig-point (point))
+ (state nil))
+ (up-list -1)
+ (if (not (eq (following-char) ?\[))
+ 'stop
+ ;; Do not return `stop' when inside a list comprehension
+ ;; construction. (The point must be after `||').
+ (while (< (point) orig-point)
+ (setq state (erlang-partial-parse (point) orig-point state)))
+ (if (and (car state) (eq (car (car (car state))) '||))
+ nil
+ 'stop)))
+ (error
+ nil))))
+
+
+(defun erlang-stop-when-at-guard ()
+ "Return `stop' when at function guards.
+
+This function is designed to be a member of a criteria list."
+ (save-excursion
+ (beginning-of-line)
+ (if (and (looking-at (eval-when-compile
+ (concat "^" erlang-atom-regexp "\\s *(")))
+ (not (looking-at
+ (eval-when-compile
+ (concat "^" erlang-atom-regexp ".*->")))))
+ 'stop
+ nil)))
+
+
+(defun erlang-next-lines-empty-p ()
+ "Return non-nil if next lines are empty.
+
+The variable `erlang-next-lines-empty-threshold' contains the number
+of lines required to be empty.
+
+A line containing only spaces and tabs is considered empty.
+
+This function is designed to be a member of a criteria list."
+ (and erlang-next-lines-empty-threshold
+ (save-excursion
+ (let ((left erlang-next-lines-empty-threshold)
+ (cont t))
+ (while (and cont (> left 0))
+ (forward-line 1)
+ (setq cont (looking-at "\\s *$"))
+ (setq left (- left 1)))
+ cont))))
+
+
+(defun erlang-at-keyword-end-p ()
+ "Test if next readable token is the keyword end.
+
+This function is designed to be a member of a criteria list."
+ (save-excursion
+ (erlang-skip-blank)
+ (looking-at "end[^_a-zA-Z0-9]")))
+
+
+;; Erlang tags support which is aware of erlang modules.
+;;
+;; Not yet implemented under XEmacs. (Hint: The Emacs 19 etags
+;; package works under XEmacs.)
+
+(eval-when-compile
+ (if (or (featurep 'bytecomp)
+ (featurep 'byte-compile))
+ (progn
+ (require 'etags))))
+
+
+;; Variables:
+
+(defvar erlang-tags-function-alist
+ '((find-tag . erlang-find-tag)
+ (find-tag-other-window . erlang-find-tag-other-window)
+ (find-tag-regexp . erlang-find-tag-regexp)
+ (find-tag-other-frame . erlang-find-tag-other-frame))
+ "Alist of old tags commands and the replacement functions.")
+
+(defvar erlang-tags-installed nil
+ "Non-nil when the Erlang tags system is installed.")
+(defvar erlang-tags-file-list '()
+ "List of files in tag list. Used when finding tag on form `module:'.")
+(defvar erlang-tags-completion-table nil
+ "Like `tags-completion-table', this table contains `tag' and `module:tag'.")
+(defvar erlang-tags-buffer-installed-p nil
+ "Non-nil when Erlang module recognising functions installed.")
+(defvar erlang-tags-buffer-list '()
+ "Temporary list of buffers.")
+(defvar erlang-tags-orig-completion-table nil
+ "Temporary storage for `tags-completion-table'.")
+(defvar erlang-tags-orig-tag-order nil
+ "Temporary storage for `find-tag-tag-order'.")
+(defvar erlang-tags-orig-regexp-tag-order nil
+ "Temporary storage for `find-tag-regexp-tag-order'.")
+(defvar erlang-tags-orig-search-function nil
+ "Temporary storage for `find-tag-search-function'.")
+(defvar erlang-tags-orig-regexp-search-function nil
+ "Temporary storage for `find-tag-regexp-search-function'.")
+(defvar erlang-tags-orig-format-hooks nil
+ "Temporary storage for `tags-table-format-hooks'.") ;v19
+(defvar erlang-tags-orig-format-functions nil
+ "Temporary storage for `tags-table-format-functions'.") ;v > 19
+
+(defun erlang-tags-init ()
+ "Install an alternate version of tags, aware of Erlang modules.
+
+After calling this function, the tags functions are aware of
+Erlang modules. Tags can be entered on the for `module:tag' as well
+as on the old form `tag'.
+
+In the completion list, `module:tag' and `module:' shows up.
+
+Call this function from an appropriate init file, or add it to
+Erlang mode hook with the commands:
+ (add-hook 'erlang-mode-hook 'erlang-tags-init)
+ (add-hook 'erlang-shell-mode-hook 'erlang-tags-init)
+
+This function only works under Emacs 18 and Emacs 19. Currently, It
+is not implemented under XEmacs. (Hint: The Emacs 19 etags module
+works under XEmacs.)"
+ (interactive)
+ (cond ((= erlang-emacs-major-version 18)
+ (require 'tags)
+ (erlang-tags-define-keys (current-local-map))
+ (setq erlang-tags-installed t))
+ (t
+ (require 'etags)
+ ;; Test on a function available in the Emacs 19 version
+ ;; of tags but not in the XEmacs version.
+ (if (not (fboundp 'find-tag-noselect))
+ ()
+ (erlang-tags-define-keys (current-local-map))
+ (setq erlang-tags-installed t)))))
+
+
+;; Set all keys bound to `find-tag' et.al. in the global map and the
+;; menu to `erlang-find-tag' et.al. in `map'.
+;;
+;; The function `substitute-key-definition' does not work properly
+;; in all version of Emacs.
+
+(defun erlang-tags-define-keys (map)
+ "Bind tags commands to keymap MAP aware of Erlang modules."
+ (let ((alist erlang-tags-function-alist))
+ (while alist
+ (let* ((old (car (car alist)))
+ (new (cdr (car alist)))
+ (keys (append (where-is-internal old global-map))))
+ (while keys
+ (define-key map (car keys) new)
+ (setq keys (cdr keys))))
+ (setq alist (cdr alist))))
+ ;; Update the menu.
+ (erlang-menu-substitute erlang-menu-base-items erlang-tags-function-alist)
+ (erlang-menu-init))
+
+
+;; There exists a variable `find-tag-default-function'. It is not used
+;; since `complete-tag' uses it to get current word under point. In that
+;; situation we don't want the module to be prepended.
+
+(defun erlang-find-tag-default ()
+ "Return the default tag.
+Search `-import' list of imported functions.
+Single quotes are been stripped away."
+ (let ((mod-func (erlang-get-function-under-point)))
+ (cond ((null mod-func)
+ nil)
+ ((null (car mod-func))
+ (nth 1 mod-func))
+ (t
+ (concat (car mod-func) ":" (nth 1 mod-func))))))
+
+
+;; Return `t' since it is used inside `tags-loop-form'.
+;;;###autoload
+(defun erlang-find-tag (modtagname &optional next-p regexp-p)
+ "Like `find-tag'. Capable of retrieving Erlang modules.
+
+Tags can be given on the forms `tag', `module:', `module:tag'."
+ (interactive (erlang-tag-interactive "Find `module:tag' or `tag': "))
+ (switch-to-buffer (erlang-find-tag-noselect modtagname next-p regexp-p))
+ t)
+
+
+;; Code mainly from `find-tag-other-window' in `etags.el'.
+;;;###autoload
+(defun erlang-find-tag-other-window (tagname &optional next-p regexp-p)
+ "Like `find-tag-other-window' but aware of Erlang modules."
+ (interactive (erlang-tag-interactive
+ "Find `module:tag' or `tag' other window: "))
+
+ ;; This is to deal with the case where the tag is found in the
+ ;; selected window's buffer; without this, point is moved in both
+ ;; windows. To prevent this, we save the selected window's point
+ ;; before doing find-tag-noselect, and restore it afterwards.
+ (let* ((window-point (window-point (selected-window)))
+ (tagbuf (erlang-find-tag-noselect tagname next-p regexp-p))
+ (tagpoint (progn (set-buffer tagbuf) (point))))
+ (set-window-point (prog1
+ (selected-window)
+ (switch-to-buffer-other-window tagbuf)
+ ;; We have to set this new window's point; it
+ ;; might already have been displaying a
+ ;; different portion of tagbuf, in which case
+ ;; switch-to-buffer-other-window doesn't set
+ ;; the window's point from the buffer.
+ (set-window-point (selected-window) tagpoint))
+ window-point)))
+
+
+(defun erlang-find-tag-other-frame (tagname &optional next-p)
+ "Like `find-tag-other-frame' but aware of Erlang modules."
+ (interactive (erlang-tag-interactive
+ "Find `module:tag' or `tag' other frame: "))
+ (let ((pop-up-frames t))
+ (erlang-find-tag-other-window tagname next-p)))
+
+
+(defun erlang-find-tag-regexp (regexp &optional next-p other-window)
+ "Like `find-tag-regexp' but aware of Erlang modules."
+ (interactive (if (fboundp 'find-tag-regexp)
+ (erlang-tag-interactive
+ "Find `module:regexp' or `regexp': ")
+ (error "This version of Emacs can't find tags by regexps")))
+ (funcall (if other-window
+ 'erlang-find-tag-other-window
+ 'erlang-find-tag)
+ regexp next-p t))
+
+
+;; Just like C-u M-. This could be added to the menu.
+(defun erlang-find-next-tag ()
+ "Find next tag, like \\[find-tag] with prefix arg."
+ (interactive)
+ (let ((current-prefix-arg '(4)))
+ (if erlang-tags-installed
+ (call-interactively 'erlang-find-tag)
+ (call-interactively 'find-tag))))
+
+
+;; Mimics `find-tag-noselect' found in `etags.el', but uses `find-tag' to
+;; be compatible with `tags.el'.
+;;
+;; Handles three cases:
+;; * `module:' Loop over all possible file names. Stop if a file-name
+;; without extension and directory matches the module.
+;;
+;; * `module:tag'
+;; Emacs 19: Replace test functions with functions aware of
+;; Erlang modules. Tricky because the etags system wasn't
+;; built for these kind of operations...
+;;
+;; Emacs 18: We loop over `find-tag' until we find a file
+;; whose module matches the requested module. The
+;; drawback is that a lot of files could be loaded into
+;; Emacs.
+;;
+;; * `tag' Just give it to `find-tag'.
+
+(defun erlang-find-tag-noselect (modtagname &optional next-p regexp-p)
+ "Like `find-tag-noselect' but aware of Erlang modules."
+ (interactive (erlang-tag-interactive "Find `module:tag' or `tag': "))
+ (or modtagname
+ (setq modtagname (symbol-value 'last-tag)))
+ (funcall (symbol-function 'set) 'last-tag modtagname)
+ ;; `tags.el' uses this variable to record how M-, would
+ ;; know where to restart a tags command.
+ (if (boundp 'tags-loop-form)
+ (funcall (symbol-function 'set)
+ 'tags-loop-form '(erlang-find-tag nil t)))
+ (save-window-excursion
+ (cond
+ ((string-match ":$" modtagname)
+ ;; Only the module name was given. Read all files whose file name
+ ;; match.
+ (let ((modname (substring modtagname 0 (match-beginning 0)))
+ (file nil))
+ (if (not next-p)
+ (save-excursion
+ (visit-tags-table-buffer)
+ (setq erlang-tags-file-list
+ (funcall (symbol-function 'tags-table-files)))))
+ (while (null file)
+ (or erlang-tags-file-list
+ (save-excursion
+ (if (and (featurep 'etags)
+ (funcall
+ (symbol-function 'visit-tags-table-buffer) 'same)
+ (funcall
+ (symbol-function 'visit-tags-table-buffer) t))
+ (setq erlang-tags-file-list
+ (funcall (symbol-function 'tags-table-files)))
+ (error "No %stags containing %s" (if next-p "more " "")
+ modtagname))))
+ (if erlang-tags-file-list
+ (let ((this-module (erlang-get-module-from-file-name
+ (car erlang-tags-file-list))))
+ (if (and (stringp this-module)
+ (string= modname this-module))
+ (setq file (car erlang-tags-file-list)))
+ (setq erlang-tags-file-list (cdr erlang-tags-file-list)))))
+ (set-buffer (or (get-file-buffer file)
+ (find-file-noselect file)))))
+
+ ((string-match ":" modtagname)
+ (if (boundp 'find-tag-tag-order)
+ ;; Method one: Add module-recognising functions to the
+ ;; list of order functions. However, the tags system
+ ;; from Emacs 18, and derives thereof (read: XEmacs)
+ ;; hasn't got this feature.
+ (progn
+ (erlang-tags-install-module-check)
+ (unwind-protect
+ (funcall (symbol-function 'find-tag)
+ modtagname next-p regexp-p)
+ (erlang-tags-remove-module-check)))
+ ;; Method two: Call the tags system until a file matching
+ ;; the module is found. This could result in that many
+ ;; files are read. (e.g. The tag "foo:file" will take a
+ ;; while to process.)
+ (let* ((modname (substring modtagname 0 (match-beginning 0)))
+ (tagname (substring modtagname (match-end 0) nil))
+ (last-tag tagname)
+ file)
+ (while
+ (progn
+ (funcall (symbol-function 'find-tag) tagname next-p regexp-p)
+ (setq next-p t)
+ ;; Determine the module form the file name. (The
+ ;; alternative, to check `-module', would make this
+ ;; code useless for non-Erlang programs.)
+ (setq file (erlang-get-module-from-file-name buffer-file-name))
+ (not (and (stringp file)
+ (string= modname file))))))))
+ (t
+ (funcall (symbol-function 'find-tag) modtagname next-p regexp-p)))
+ (current-buffer))) ; Return the new buffer.
+
+
+;; Process interactive arguments for erlang-find-tag-*.
+;;
+;; Negative arguments work only for `etags', not `tags'. This is not
+;; a problem since negative arguments means step back into the
+;; history list, a feature not implemented in `tags'.
+
+(defun erlang-tag-interactive (prompt)
+ (condition-case nil
+ (require 'etags)
+ (error
+ (require 'tags)))
+ (if current-prefix-arg
+ (list nil (if (< (prefix-numeric-value current-prefix-arg) 0)
+ '-
+ t))
+ (let* ((default (erlang-find-tag-default))
+ (prompt (if default
+ (format "%s(default %s) " prompt default)
+ prompt))
+ (spec (if (featurep 'etags)
+ (completing-read prompt 'erlang-tags-complete-tag)
+ (read-string prompt))))
+ (list (if (equal spec "")
+ (or default (error "There is no default tag"))
+ spec)))))
+
+
+;; Search tag functions which are aware of Erlang modules. The tactic
+;; is to store new search functions into the local variables of the
+;; TAGS buffers. The variables are restored directly after the
+;; search. The situation is complicated by the fact that new TAGS
+;; files can be loaded during the search.
+;;
+
+(defun erlang-tags-install-module-check ()
+ "Install our own tag search functions."
+ ;; Make sure our functions are installed in TAGS files loaded
+ ;; into Emacs while searching.
+ (cond
+ ((>= erlang-emacs-major-version 20)
+ (setq erlang-tags-orig-format-functions
+ (symbol-value 'tags-table-format-functions))
+ (funcall (symbol-function 'set) 'tags-table-format-functions
+ (cons 'erlang-tags-recognize-tags-table
+ erlang-tags-orig-format-functions))
+ (setq erlang-tags-buffer-list '())
+ )
+ (t
+ (setq erlang-tags-orig-format-hooks
+ (symbol-value 'tags-table-format-hooks))
+ (funcall (symbol-function 'set) 'tags-table-format-hooks
+ (cons 'erlang-tags-recognize-tags-table
+ erlang-tags-orig-format-hooks))
+ (setq erlang-tags-buffer-list '())
+ ))
+
+ ;; Install our functions in the TAGS files already resident.
+ (save-excursion
+ (let ((files (symbol-value 'tags-table-computed-list)))
+ (while files
+ (if (stringp (car files))
+ (if (get-file-buffer (car files))
+ (progn
+ (set-buffer (get-file-buffer (car files)))
+ (erlang-tags-install-local))))
+ (setq files (cdr files))))))
+
+
+(defun erlang-tags-install-local ()
+ "Install our tag search functions in current buffer."
+ (if erlang-tags-buffer-installed-p
+ ()
+ ;; Mark this buffer as "installed" and record.
+ (set (make-local-variable 'erlang-tags-buffer-installed-p) t)
+ (setq erlang-tags-buffer-list
+ (cons (current-buffer) erlang-tags-buffer-list))
+
+ ;; Save the original values.
+ (set (make-local-variable 'erlang-tags-orig-tag-order)
+ (symbol-value 'find-tag-tag-order))
+ (set (make-local-variable 'erlang-tags-orig-regexp-tag-order)
+ (symbol-value 'find-tag-regexp-tag-order))
+ (set (make-local-variable 'erlang-tags-orig-search-function)
+ (symbol-value 'find-tag-search-function))
+ (set (make-local-variable 'erlang-tags-orig-regexp-search-function)
+ (symbol-value 'find-tag-regexp-search-function))
+
+ ;; Install our own functions.
+ (set (make-local-variable 'find-tag-search-function)
+ 'erlang-tags-search-forward)
+ (set (make-local-variable 'find-tag-regexp-search-function)
+ 'erlang-tags-regexp-search-forward)
+ (set (make-local-variable 'find-tag-tag-order)
+ '(erlang-tag-match-module-p))
+ (set (make-local-variable 'find-tag-regexp-tag-order)
+ '(erlang-tag-match-module-regexp-p))))
+
+
+(defun erlang-tags-remove-module-check ()
+ "Remove our own tags search functions."
+ (cond
+ ((>= erlang-emacs-major-version 20)
+ (funcall (symbol-function 'set)
+ 'tags-table-format-functions
+ erlang-tags-orig-format-functions)
+ )
+ (t
+ (funcall (symbol-function 'set)
+ 'tags-table-format-hooks
+ erlang-tags-orig-format-hooks)
+ ))
+
+ ;; Remove our functions from the TAGS files. (Note that
+ ;; `tags-table-computed-list' need not be the same list as when
+ ;; the search was started.)
+ (save-excursion
+ (let ((buffers erlang-tags-buffer-list))
+ (while buffers
+ (if (buffer-name (car buffers))
+ (progn
+ (set-buffer (car buffers))
+ (erlang-tags-remove-local)))
+ (setq buffers (cdr buffers))))))
+
+
+(defun erlang-tags-remove-local ()
+ "Remove our tag search functions from current buffer."
+ (if (null erlang-tags-buffer-installed-p)
+ ()
+ (funcall (symbol-function 'set) 'erlang-tags-buffer-installed-p nil)
+ (funcall (symbol-function 'set)
+ 'find-tag-tag-order erlang-tags-orig-tag-order)
+ (funcall (symbol-function 'set)
+ 'find-tag-regexp-tag-order erlang-tags-orig-regexp-tag-order)
+ (funcall (symbol-function 'set)
+ 'find-tag-search-function erlang-tags-orig-search-function)
+ (funcall (symbol-function 'set)
+ 'find-tag-regexp-search-function
+ erlang-tags-orig-regexp-search-function)))
+
+
+(defun erlang-tags-recognize-tags-table ()
+ "Install our functions in all loaded TAGS files.
+
+This function is added to `tags-table-format-hooks/functions' when searching
+for a tag on the form `module:tag'."
+ (if (null (funcall (symbol-function 'etags-recognize-tags-table)))
+ nil
+ (erlang-tags-install-local)
+ t))
+
+
+(defun erlang-tags-search-forward (tag &optional bound noerror count)
+ "Forward search function, aware of Erlang module prefix."
+ (if (string-match ":" tag)
+ (setq tag (substring tag (match-end 0) nil)))
+ ;; Avoid unintended recursion.
+ (if (eq erlang-tags-orig-search-function 'erlang-tags-search-forward)
+ (search-forward tag bound noerror count)
+ (funcall erlang-tags-orig-search-function tag bound noerror count)))
+
+
+(defun erlang-tags-regexp-search-forward (tag &optional bound noerror count)
+ "Forward regexp search function, aware of Erlang module prefix."
+ (if (string-match ":" tag)
+ (setq tag (substring tag (match-end 0) nil)))
+ (if (eq erlang-tags-orig-regexp-search-function
+ 'erlang-tags-regexp-search-forward)
+ (re-search-forward tag bound noerror count)
+ (funcall erlang-tags-orig-regexp-search-function
+ tag bound noerror count)))
+
+
+;; t if point is at a tag line that matches TAG, containing
+;; module information. Assumes that all other order functions
+;; are stored in `erlang-tags-orig-[regex]-tag-order'.
+
+(defun erlang-tag-match-module-p (tag)
+ (erlang-tag-match-module-common-p tag erlang-tags-orig-tag-order))
+
+(defun erlang-tag-match-module-regexp-p (tag)
+ (erlang-tag-match-module-common-p tag erlang-tags-orig-regexp-tag-order))
+
+(defun erlang-tag-match-module-common-p (tag order)
+ (let ((mod nil)
+ (found nil))
+ (if (string-match ":" tag)
+ (progn
+ (setq mod (substring tag 0 (match-beginning 0)))
+ (setq tag (substring tag (match-end 0) nil))))
+ (while (and order (not found))
+ (setq found
+ (and (not (memq (car order)
+ '(erlang-tag-match-module-p
+ erlang-tag-match-module-regexp-p)))
+ (funcall (car order) tag)))
+ (setq order (cdr order)))
+ (and found
+ (or (null mod)
+ (string= mod (erlang-get-module-from-file-name
+ (file-of-tag)))))))
+
+
+;;; Tags completion, Emacs 19 `etags' specific.
+;;;
+;;; The basic idea is to create a second completion table `erlang-tags-
+;;; completion-table' containing all normal tags plus tags on the form
+;;; `module:tag'.
+
+
+(defun erlang-complete-tag ()
+ "Perform tags completion on the text around point.
+Completes to the set of names listed in the current tags table.
+
+Should the Erlang tags system be installed this command knows
+about Erlang modules."
+ (interactive)
+ (condition-case nil
+ (require 'etags)
+ (error nil))
+ (cond ((and erlang-tags-installed
+ (fboundp 'complete-tag)) ; Emacs 19
+ (let ((orig-tags-complete-tag (symbol-function 'tags-complete-tag)))
+ (fset 'tags-complete-tag
+ (symbol-function 'erlang-tags-complete-tag))
+ (unwind-protect
+ (funcall (symbol-function 'complete-tag))
+ (fset 'tags-complete-tag orig-tags-complete-tag))))
+ ((fboundp 'complete-tag) ; Emacs 19
+ (funcall (symbol-function 'complete-tag)))
+ ((fboundp 'tag-complete-symbol) ; XEmacs
+ (funcall (symbol-function 'tag-complete-symbol)))
+ (t
+ (error "This version of Emacs can't complete tags"))))
+
+
+;; Based on `tags-complete-tag', but this one uses
+;; `erlang-tags-completion-table' instead of `tags-completion-table'.
+;;
+;; This is the entry-point called by system function `completing-read'.
+(defun erlang-tags-complete-tag (string predicate what)
+ (save-excursion
+ ;; If we need to ask for the tag table, allow that.
+ (let ((enable-recursive-minibuffers t))
+ (visit-tags-table-buffer))
+ (if (eq what t)
+ (all-completions string (erlang-tags-completion-table) predicate)
+ (try-completion string (erlang-tags-completion-table) predicate))))
+
+
+;; `tags-completion-table' calls itself recursively, make it
+;; call our own wedge instead. Note that the recursive call
+;; is very rare; it only occurs when a tags-file contains
+;; `include'-statements.
+(defun erlang-tags-completion-table ()
+ "Build completion table. Tags on the form `tag' or `module:tag'."
+ (setq erlang-tags-orig-completion-table
+ (symbol-function 'tags-completion-table))
+ (fset 'tags-completion-table
+ (symbol-function 'erlang-tags-completion-table-1))
+ (unwind-protect
+ (erlang-tags-completion-table-1)
+ (fset 'tags-completion-table
+ erlang-tags-orig-completion-table)))
+
+
+(defun erlang-tags-completion-table-1 ()
+ (make-local-variable 'erlang-tags-completion-table)
+ (or erlang-tags-completion-table
+ (let ((tags-completion-table nil)
+ (tags-completion-table-function
+ 'erlang-etags-tags-completion-table))
+ (funcall erlang-tags-orig-completion-table)
+ (setq erlang-tags-completion-table tags-completion-table))))
+
+
+;; Based on `etags-tags-completion-table'. The difference is that we
+;; add three symbols to the vector, the tag, module: and module:tag.
+;; The module is extracted from the file name of a tag. (This one
+;; only works if we are looking at an `etags' file. However, this is
+;; the only format supported by Emacs, so far.)
+(defun erlang-etags-tags-completion-table ()
+ (let ((table (make-vector 511 0))
+ (file nil))
+ (save-excursion
+ (goto-char (point-min))
+ ;; This monster regexp matches an etags tag line.
+ ;; \1 is the string to match;
+ ;; \2 is not interesting;
+ ;; \3 is the guessed tag name; XXX guess should be better eg DEFUN
+ ;; \4 is not interesting;
+ ;; \5 is the explicitly-specified tag name.
+ ;; \6 is the line to start searching at;
+ ;; \7 is the char to start searching at.
+ (while (progn
+ (while (and
+ (eq (following-char) ?\f)
+ (looking-at "\f\n\\([^,\n]*\\),.*\n"))
+ (setq file (buffer-substring
+ (match-beginning 1) (match-end 1)))
+ (goto-char (match-end 0)))
+ (re-search-forward
+ "\
+^\\(\\([^\177]+[^-a-zA-Z0-9_$\177]+\\)?\\([-a-zA-Z0-9_$?:]+\\)\
+\[^-a-zA-Z0-9_$?:\177]*\\)\177\\(\\([^\n\001]+\\)\001\\)?\
+\\([0-9]+\\)?,\\([0-9]+\\)?\n"
+ nil t))
+ (let ((tag (if (match-beginning 5)
+ ;; There is an explicit tag name.
+ (buffer-substring (match-beginning 5) (match-end 5))
+ ;; No explicit tag name. Best guess.
+ (buffer-substring (match-beginning 3) (match-end 3))))
+ (module (and file
+ (erlang-get-module-from-file-name file))))
+ (intern tag table)
+ (if (stringp module)
+ (progn
+ (intern (concat module ":" tag) table)
+ ;; Only the first one will be stored in the table.
+ (intern (concat module ":") table))))))
+ table))
+
+;;;
+;;; Prepare for other methods to run an Erlang slave process.
+;;;
+
+(defvar erlang-shell-function 'inferior-erlang
+ "Command to execute start a new Erlang shell.
+
+Change this variable to use your favorite
+Erlang compilation package.")
+
+(defvar erlang-shell-display-function 'inferior-erlang-run-or-select
+ "Command to execute to display Erlang shell.
+
+Change this variable to use your favorite
+Erlang compilation package.")
+
+(defvar erlang-compile-function 'inferior-erlang-compile
+ "Command to execute to compile current buffer.
+
+Change this variable to use your favorite
+Erlang compilation package.")
+
+(defvar erlang-compile-erlang-function "c"
+ "Erlang function to call to compile an erlang file.")
+
+(defvar erlang-compile-display-function 'inferior-erlang-run-or-select
+ "Command to execute to view last compilation.
+
+Change this variable to use your favorite
+Erlang compilation package.")
+
+(defvar erlang-next-error-function 'inferior-erlang-next-error
+ "Command to execute to go to the next error.
+
+Change this variable to use your favorite Erlang compilation
+package. Not used in Emacs 21.")
+
+
+;;;###autoload
+(defun erlang-shell ()
+ "Start a new Erlang shell.
+
+The variable `erlang-shell-function' decides which method to use,
+default is to start a new Erlang host. It is possible that, in the
+future, a new shell on an already running host will be started."
+ (interactive)
+ (call-interactively erlang-shell-function))
+
+
+;;;###autoload (autoload 'run-erlang "erlang" "Start a new Erlang shell." t)
+
+;; It is customary for Emacs packages to supply a function on this
+;; form, even though it violates the `erlang-*' name convention.
+(defalias 'run-erlang 'erlang-shell)
+
+
+(defun erlang-shell-display ()
+ "Display an Erlang shell, or start a new."
+ (interactive)
+ (call-interactively erlang-shell-display-function))
+
+
+;;;###autoload
+(defun erlang-compile ()
+ "Compile Erlang module in current buffer."
+ (interactive)
+ (call-interactively erlang-compile-function))
+
+
+(defun erlang-compile-display ()
+ "Display compilation output."
+ (interactive)
+ (call-interactively erlang-compile-display-function))
+
+
+(defun erlang-next-error ()
+ "Display next error message from the latest compilation."
+ (interactive)
+ (call-interactively erlang-next-error-function))
+
+
+
+;;;
+;;; Erlang Shell Mode -- Major mode used for Erlang shells.
+;;;
+
+;; This mode is designed to be implementation independent,
+;; e.g. it does not assume that we are running an inferior
+;; Erlang, there exists a lot of other possibilities.
+
+
+(defvar erlang-shell-buffer-name "*erlang*"
+ "The name of the Erlang link shell buffer.")
+
+(defvar erlang-shell-mode-map nil
+ "Keymap used by Erlang shells.")
+
+
+(defvar erlang-shell-mode-hook nil
+ "*User functions to run when an Erlang shell is started.
+
+This hook is used to change the behaviour of Erlang mode. It is
+normally used by the user to personalise the programming environment.
+When used in a site init file, it could be used to customise Erlang
+mode for all users on the system.
+
+The function added to this hook is run every time a new Erlang
+shell is started.
+
+See also `erlang-load-hook', a hook which is run once, when Erlang
+mode is loaded, and `erlang-mode-hook' which is run every time a new
+Erlang source file is loaded into Emacs.")
+
+
+(defvar erlang-input-ring-file-name "~/.erlang_history"
+ "*When non-nil, file name used to store Erlang shell history information.")
+
+
+(defun erlang-shell-mode ()
+ "Major mode for interacting with an Erlang shell.
+
+We assume that we already are in Comint mode.
+
+The following special commands are available:
+\\{erlang-shell-mode-map}"
+ (interactive)
+ (setq major-mode 'erlang-shell-mode)
+ (setq mode-name "Erlang Shell")
+ (erlang-mode-variables)
+ (if erlang-shell-mode-map
+ nil
+ (setq erlang-shell-mode-map (copy-keymap comint-mode-map))
+ (erlang-shell-mode-commands erlang-shell-mode-map))
+ (use-local-map erlang-shell-mode-map)
+ (unless inferior-erlang-use-cmm
+ ;; This was originally not a marker, but it needs to be, at least
+ ;; in Emacs 21, and should be backwards-compatible. Otherwise,
+ ;; would need to test whether compilation-parsing-end is a marker
+ ;; after requiring `compile'.
+ (set (make-local-variable 'compilation-parsing-end) (copy-marker 1))
+ (set (make-local-variable 'compilation-error-list) nil)
+ (set (make-local-variable 'compilation-old-error-list) nil))
+ ;; Needed when compiling directly from the Erlang shell.
+ (setq compilation-last-buffer (current-buffer))
+ (erlang-add-compilation-alist erlang-error-regexp-alist)
+ (setq comint-prompt-regexp "^[^>=]*> *")
+ (setq comint-eol-on-send t)
+ (setq comint-input-ignoredups t)
+ (setq comint-scroll-show-maximum-output t)
+ (setq comint-scroll-to-bottom-on-output t)
+ ;; In Emacs 19.30, `add-hook' has got a `local' flag, use it. If
+ ;; the call fails, just call the normal `add-hook'.
+ (condition-case nil
+ (progn
+ (add-hook 'comint-output-filter-functions
+ 'inferior-erlang-strip-delete nil t)
+ (add-hook 'comint-output-filter-functions
+ 'inferior-erlang-strip-ctrl-m nil t))
+ (error
+ (funcall (symbol-function 'make-local-hook)
+ 'comint-output-filter-functions) ; obsolete as of Emacs 21.1
+ (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-delete)
+ (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-ctrl-m)))
+ ;; Some older versions of comint don't have an input ring.
+ (if (fboundp 'comint-read-input-ring)
+ (progn
+ (setq comint-input-ring-file-name erlang-input-ring-file-name)
+ (comint-read-input-ring t)
+ (make-local-variable 'kill-buffer-hook)
+ (add-hook 'kill-buffer-hook 'comint-write-input-ring)))
+ ;; At least in Emacs 21, we need to be in `compilation-minor-mode'
+ ;; for `next-error' to work. We can avoid it clobbering the shell
+ ;; keys thus.
+ (when inferior-erlang-use-cmm
+ (compilation-minor-mode 1)
+ (set (make-local-variable 'minor-mode-overriding-map-alist)
+ `((compilation-minor-mode
+ . ,(let ((map (make-sparse-keymap)))
+ ;; It would be useful to put keymap properties on the
+ ;; error lines so that we could use RET and mouse-2
+ ;; on them directly.
+ (when (boundp 'compilation-skip-threshold) ; new compile.el
+ (define-key map [mouse-2] #'erlang-mouse-2-command)
+ (define-key map "\C-m" #'erlang-RET-command))
+ (if (boundp 'compilation-menu-map)
+ (define-key map [menu-bar compilation]
+ (cons "Errors" compilation-menu-map)))
+ map)))))
+ (run-hooks 'erlang-shell-mode-hook))
+
+
+(defun erlang-mouse-2-command (event)
+ "Command bound to `mouse-2' in inferior Erlang buffer.
+Selects Comint or Compilation mode command as appropriate."
+ (interactive "e")
+ (if (save-window-excursion
+ (save-excursion
+ (mouse-set-point event)
+ (consp (get-text-property (line-beginning-position) 'message))))
+ (call-interactively (lookup-key compilation-mode-map [mouse-2]))
+ (call-interactively (lookup-key comint-mode-map [mouse-2]))))
+
+(defun erlang-RET-command ()
+ "Command bound to `RET' in inferior Erlang buffer.
+Selects Comint or Compilation mode command as appropriate."
+ (interactive)
+ (if (consp (get-text-property (line-beginning-position) 'message))
+ (call-interactively (lookup-key compilation-mode-map "\C-m"))
+ (call-interactively (lookup-key comint-mode-map "\C-m"))))
+
+(defun erlang-shell-mode-commands (map)
+ (define-key map "\M-\t" 'erlang-complete-tag)
+ (define-key map "\C-a" 'comint-bol) ; Normally the other way around.
+ (define-key map "\C-c\C-a" 'beginning-of-line)
+ (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
+ (define-key map "\M-\C-m" 'compile-goto-error)
+ (unless inferior-erlang-use-cmm
+ (define-key map "\C-x`" 'erlang-next-error)))
+
+;;;
+;;; Inferior Erlang -- Run an Erlang shell as a subprocess.
+;;;
+
+(defvar inferior-erlang-display-buffer-any-frame nil
+ "*When nil, `inferior-erlang-display-buffer' use only selected frame.
+When t, all frames are searched. When 'raise, the frame is raised.")
+
+(defvar inferior-erlang-shell-type 'newshell
+ "The type of Erlang shell to use.
+
+When this variable is set to the atom `oldshell', the old shell is used.
+When set to `newshell' the new shell is used. Should the variable be
+nil, the default shell is used.
+
+This variable influence the setting of other variables.")
+
+(defvar inferior-erlang-machine "erl"
+ "*The name of the Erlang shell.")
+
+(defvar inferior-erlang-machine-options '()
+ "*The options used when activating the Erlang shell.
+
+This must be a list of strings.")
+
+(defvar inferior-erlang-process-name "inferior-erlang"
+ "The name of the inferior Erlang process.")
+
+(defvar inferior-erlang-buffer-name erlang-shell-buffer-name
+ "The name of the inferior Erlang buffer.")
+
+(defvar inferior-erlang-prompt-timeout 60
+ "*Number of seconds before `inferior-erlang-wait-prompt' timeouts.
+
+The time specified is waited after every output made by the inferior
+Erlang shell. When this variable is t, we assume that we always have
+a prompt. When nil, we will wait forever, or until \\[keyboard-quit].")
+
+(defvar inferior-erlang-process nil
+ "Process of last invoked inferior Erlang, or nil.")
+
+(defvar inferior-erlang-buffer nil
+ "Buffer of last invoked inferior Erlang, or nil.")
+
+;;;###autoload
+(defun inferior-erlang ()
+ "Run an inferior Erlang.
+
+This is just like running Erlang in a normal shell, except that
+an Emacs buffer is used for input and output.
+\\<comint-mode-map>
+The command line history can be accessed with \\[comint-previous-input] and \\[comint-next-input].
+The history is saved between sessions.
+
+Entry to this mode calls the functions in the variables
+`comint-mode-hook' and `erlang-shell-mode-hook' with no arguments.
+
+The following commands imitate the usual Unix interrupt and
+editing control characters:
+\\{erlang-shell-mode-map}"
+ (interactive)
+ (require 'comint)
+ (let ((opts inferior-erlang-machine-options))
+ (cond ((eq inferior-erlang-shell-type 'oldshell)
+ (setq opts (cons "-oldshell" opts)))
+ ((eq inferior-erlang-shell-type 'newshell)
+ (setq opts (append '("-newshell" "-env" "TERM" "vt100") opts))))
+ (setq inferior-erlang-buffer
+ (apply 'make-comint
+ inferior-erlang-process-name inferior-erlang-machine
+ nil opts)))
+ (setq inferior-erlang-process
+ (get-buffer-process inferior-erlang-buffer))
+ (if (> 21 erlang-emacs-major-version) ; funcalls to avoid compiler warnings
+ (funcall (symbol-function 'set-process-query-on-exit-flag)
+ inferior-erlang-process nil)
+ (funcall (symbol-function 'process-kill-without-query) inferior-erlang-process))
+ (if erlang-inferior-shell-split-window
+ (switch-to-buffer-other-window inferior-erlang-buffer)
+ (switch-to-buffer inferior-erlang-buffer))
+ (if (and (not (eq system-type 'windows-nt))
+ (eq inferior-erlang-shell-type 'newshell))
+ (setq comint-process-echoes t))
+ ;; `rename-buffer' takes only one argument in Emacs 18.
+ (condition-case nil
+ (rename-buffer inferior-erlang-buffer-name t)
+ (error (rename-buffer inferior-erlang-buffer-name)))
+ (erlang-shell-mode))
+
+
+(defun inferior-erlang-run-or-select ()
+ "Switch to an inferior Erlang buffer, possibly starting new process."
+ (interactive)
+ (if (null (inferior-erlang-running-p))
+ (inferior-erlang)
+ (inferior-erlang-display-buffer t)))
+
+
+(defun inferior-erlang-display-buffer (&optional select)
+ "Make the inferior Erlang process visible.
+The window is returned.
+
+Should `inferior-erlang-display-buffer-any-frame' be nil the buffer is
+displayed in the current frame. Should it be non-nil, and the buffer
+already is visible in any other frame, no new window will be created.
+Should it be the atom 'raise, the frame containing the window will
+be raised.
+
+Should the optional argument SELECT be non-nil, the window is
+selected. Should the window be in another frame, that frame is raised.
+
+Note, should the mouse pointer be places outside the raised frame, that
+frame will become deselected before the next command."
+ (interactive)
+ (or (inferior-erlang-running-p)
+ (error "No inferior Erlang process is running"))
+ (let ((win (inferior-erlang-window
+ inferior-erlang-display-buffer-any-frame))
+ (frames-p (fboundp 'selected-frame)))
+ (if (null win)
+ (let ((old-win (selected-window)))
+ (save-excursion
+ (switch-to-buffer-other-window inferior-erlang-buffer)
+ (setq win (selected-window)))
+ (select-window old-win))
+ (if (and window-system
+ frames-p
+ (or select
+ (eq inferior-erlang-display-buffer-any-frame 'raise))
+ (not (eq (selected-frame) (window-frame win))))
+ (raise-frame (window-frame win))))
+ (if select
+ (select-window win))
+ (sit-for 0)
+ win))
+
+
+(defun inferior-erlang-running-p ()
+ "Non-nil when an inferior Erlang is running."
+ (and inferior-erlang-process
+ (memq (process-status inferior-erlang-process) '(run open))
+ inferior-erlang-buffer
+ (buffer-name inferior-erlang-buffer)))
+
+
+(defun inferior-erlang-window (&optional all-frames)
+ "Return the window containing the inferior Erlang, or nil."
+ (and (inferior-erlang-running-p)
+ (if (and all-frames (>= erlang-emacs-major-version 19))
+ (get-buffer-window inferior-erlang-buffer t)
+ (get-buffer-window inferior-erlang-buffer))))
+
+
+(defun inferior-erlang-wait-prompt ()
+ "Wait until the inferior Erlang shell prompt appears."
+ (if (eq inferior-erlang-prompt-timeout t)
+ ()
+ (or (inferior-erlang-running-p)
+ (error "No inferior Erlang shell is running"))
+ (save-excursion
+ (set-buffer inferior-erlang-buffer)
+ (let ((msg nil))
+ (while (save-excursion
+ (goto-char (process-mark inferior-erlang-process))
+ (forward-line 0)
+ (not (looking-at comint-prompt-regexp)))
+ (if msg
+ ()
+ (setq msg t)
+ (message "Waiting for Erlang shell prompt (press C-g to abort)."))
+ (or (accept-process-output inferior-erlang-process
+ inferior-erlang-prompt-timeout)
+ (error "No Erlang shell prompt before timeout")))
+ (if msg (message ""))))))
+
+(defun inferior-erlang-send-empty-cmd-unless-already-at-prompt ()
+ "If not already at a prompt, try to send an empty cmd to get a prompt.
+The empty command resembles hitting RET. This is useful in some
+situations, for instance if a crash or error report from sasl
+has been printed after the last prompt."
+ (save-excursion
+ (set-buffer inferior-erlang-buffer)
+ (if (> (point-max) 1)
+ ;; make sure we get a prompt if buffer contains data
+ (if (save-excursion
+ (goto-char (process-mark inferior-erlang-process))
+ (forward-line 0)
+ (not (looking-at comint-prompt-regexp)))
+ (inferior-erlang-send-command "")))))
+
+(autoload 'comint-send-input "comint")
+
+(defun inferior-erlang-send-command (cmd &optional hist)
+ "Send command CMD to the inferior Erlang.
+
+The contents of the current command line (if any) will
+be placed at the next prompt.
+
+If optional second argument is non-nil the command is inserted into
+the history list.
+
+Return the position after the newly inserted command."
+ (or (inferior-erlang-running-p)
+ (error "No inferior Erlang process is running"))
+ (let ((old-buffer (current-buffer))
+ (insert-point (marker-position (process-mark inferior-erlang-process)))
+ (insert-length (if comint-process-echoes
+ 0
+ (1+ (length cmd)))))
+ (set-buffer inferior-erlang-buffer)
+ (goto-char insert-point)
+ (insert cmd)
+ ;; Strange things happened if `comint-eol-on-send' is declared
+ ;; in the `let' expression above, but setq:d here. The
+ ;; `set-buffer' statement obviously makes the buffer local
+ ;; instance of `comint-eol-on-send' shadow this one.
+ ;; I'm considering this a bug in Elisp.
+ ;;
+ ;; This was previously cautioned against in the Lisp manual. It
+ ;; has been sorted out in Emacs 21. -- fx
+ (let ((comint-eol-on-send nil)
+ (comint-input-filter (if hist comint-input-filter 'ignore)))
+ (if (and (not erlang-xemacs-p)
+ (>= emacs-major-version 22))
+ (comint-send-input nil t)
+ (comint-send-input)))
+ ;; Adjust all windows whose points are incorrect.
+ (if (null comint-process-echoes)
+ (walk-windows
+ (function
+ (lambda (window)
+ (if (and (eq (window-buffer window) inferior-erlang-buffer)
+ (= (window-point window) insert-point))
+ (set-window-point window
+ (+ insert-point insert-length)))))
+ nil t))
+ (set-buffer old-buffer)
+ (+ insert-point insert-length)))
+
+
+(defun inferior-erlang-strip-delete (&optional s)
+ "Remove `^H' (delete) and the characters it was supposed to remove."
+ (interactive)
+ (if (and (boundp 'comint-last-input-end)
+ (boundp 'comint-last-output-start))
+ (save-excursion
+ (goto-char
+ (if (interactive-p)
+ (symbol-value 'comint-last-input-end)
+ (symbol-value 'comint-last-output-start)))
+ (while (progn (skip-chars-forward "^\C-h")
+ (not (eq (point) (point-max))))
+ (delete-char 1)
+ (or (bolp)
+ (backward-delete-char 1))))))
+
+
+;; Basically `comint-strip-ctrl-m', with a few extra checks.
+(defun inferior-erlang-strip-ctrl-m (&optional string)
+ "Strip trailing `^M' characters from the current output group."
+ (interactive)
+ (if (and (boundp 'comint-last-input-end)
+ (boundp 'comint-last-output-start))
+ (let ((pmark (process-mark (get-buffer-process (current-buffer)))))
+ (save-excursion
+ (goto-char
+ (if (interactive-p)
+ (symbol-value 'comint-last-input-end)
+ (symbol-value 'comint-last-output-start)))
+ (while (re-search-forward "\r+$" pmark t)
+ (replace-match "" t t))))))
+
+
+(defun inferior-erlang-compile (arg)
+ "Compile the file in the current buffer.
+
+With prefix arg, compiles for debug.
+
+Should Erlang return `{error, nofile}' it could not load the object
+module after completing the compilation. This is due to a bug in the
+compile command `c' when using the option `outdir'.
+
+There exists two workarounds for this bug:
+
+ 1) Place the directory in the Erlang load path.
+
+ 2) Set the Emacs variable `erlang-compile-use-outdir' to nil.
+ To do so, place the following line in your `~/.emacs'-file:
+ (setq erlang-compile-use-outdir nil)"
+ (interactive "P")
+ (save-some-buffers)
+ (inferior-erlang-prepare-for-input)
+ (let* ((dir (inferior-erlang-compile-outdir))
+;;; (file (file-name-nondirectory (buffer-file-name)))
+ (noext (substring (buffer-file-name) 0 -4))
+ (opts (append (list (cons 'outdir dir))
+ (if current-prefix-arg
+ (list 'debug_info 'export_all))
+ erlang-compile-extra-opts))
+ end)
+ (save-excursion
+ (set-buffer inferior-erlang-buffer)
+ (compilation-forget-errors))
+ (setq end (inferior-erlang-send-command
+ (inferior-erlang-compute-compile-command noext opts)
+ nil))
+ (sit-for 0)
+ (inferior-erlang-wait-prompt)
+ (save-excursion
+ (set-buffer inferior-erlang-buffer)
+ (setq compilation-error-list nil)
+ (set-marker compilation-parsing-end end))
+ (setq compilation-last-buffer inferior-erlang-buffer)))
+
+(defun inferior-erlang-prepare-for-input (&optional no-display)
+ "Create an inferior erlang buffer if needed and ready it for input.
+The buffer is displayed, according to `inferior-erlang-display-buffer'
+unless the optional NO-DISPLAY is non-nil."
+ (or (inferior-erlang-running-p)
+ (save-excursion
+ (inferior-erlang)))
+ (or (inferior-erlang-running-p)
+ (error "Error starting inferior Erlang shell"))
+ (if (not no-display)
+ (inferior-erlang-display-buffer))
+ (inferior-erlang-send-empty-cmd-unless-already-at-prompt)
+ (sit-for 0)
+ (inferior-erlang-wait-prompt))
+
+(defun inferior-erlang-compile-outdir ()
+ "Return the directory to compile the current buffer into."
+ (let* ((buffer-dir (directory-file-name
+ (file-name-directory (buffer-file-name))))
+ (parent-dir (directory-file-name
+ (file-name-directory buffer-dir)))
+ (ebin-dir (concat (file-name-as-directory parent-dir) "ebin"))
+ (buffer-dir-base-name (file-name-nondirectory
+ (expand-file-name
+ (concat (file-name-as-directory buffer-dir)
+ ".")))))
+ (if (and (string= buffer-dir-base-name "src")
+ (file-directory-p ebin-dir))
+ (file-name-as-directory ebin-dir)
+ (file-name-as-directory buffer-dir))))
+
+(defun inferior-erlang-compute-compile-command (module-name opts)
+ (let* ((out-dir-opt (assoc 'outdir opts))
+ (out-dir (cdr out-dir-opt)))
+ (if erlang-compile-use-outdir
+ (format "%s(\"%s\"%s)."
+ erlang-compile-erlang-function
+ module-name
+ (inferior-erlang-format-comma-opts opts))
+ (let (;; Hopefully, noone else will ever use these...
+ (tmpvar "Tmp7236")
+ (tmpvar2 "Tmp8742"))
+ (format
+ (concat
+ "f(%s), {ok, %s} = file:get_cwd(), "
+ "file:set_cwd(\"%s\"), "
+ "%s = %s(\"%s\"%s), file:set_cwd(%s), f(%s), %s.")
+ tmpvar2 tmpvar
+ out-dir
+ tmpvar2
+ erlang-compile-erlang-function
+ module-name (inferior-erlang-format-comma-opts
+ (remq out-dir-opt opts))
+ tmpvar tmpvar tmpvar2)))))
+
+(defun inferior-erlang-format-comma-opts (opts)
+ (if (null opts)
+ ""
+ (concat ", " (inferior-erlang-format-opts opts))))
+
+(defun inferior-erlang-format-opts (opts)
+ (concat "[" (inferior-erlang-string-join (mapcar 'inferior-erlang-format-opt
+ opts)
+ ", ")
+ "]"))
+
+(defun inferior-erlang-format-opt (opt)
+ (cond ((stringp opt) (concat "\"" opt "\""))
+ ((atom opt) (format "%s" opt))
+ ((consp opt) (concat "{" (inferior-erlang-string-join
+ (mapcar 'inferior-erlang-format-opt
+ (list (car opt) (cdr opt)))
+ ", ")
+ "}"))
+ (t (error (format "Unexpected opt %s" opt)))))
+
+(defun inferior-erlang-string-join (strs sep)
+ (let ((result (or (car strs) "")))
+ (setq strs (cdr strs))
+ (while strs
+ (setq result (concat result sep (car strs)))
+ (setq strs (cdr strs)))
+ result))
+
+;; `next-error' only accepts buffers with major mode `compilation-mode'
+;; or with the minor mode `compilation-minor-mode' activated.
+;; (To activate the minor mode is out of the question, since it will
+;; ruin the inferior Erlang keymap.)
+;; This is done differently in Emacs 21.
+(defun inferior-erlang-next-error (&optional argp)
+ "Just like `next-error'.
+Capable of finding error messages in an inferior Erlang buffer."
+ (interactive "P")
+ (let ((done nil)
+ (buf (or (and (boundp 'next-error-last-buffer)
+ next-error-last-buffer)
+ (and (boundp 'compilation-last-buffer)
+ compilation-last-buffer))))
+ (if (and (bufferp buf)
+ (save-excursion
+ (set-buffer buf)
+ (and (eq major-mode 'erlang-shell-mode)
+ (setq major-mode 'compilation-mode))))
+ (unwind-protect
+ (progn
+ (setq done t)
+ (next-error argp))
+ (save-excursion
+ (set-buffer buf)
+ (setq major-mode 'erlang-shell-mode))))
+ (or done
+ (next-error argp))))
+
+
+(defun inferior-erlang-change-directory (&optional dir)
+ "Make the inferior Erlang change directory.
+The default is to go to the directory of the current buffer."
+ (interactive)
+ (or dir (setq dir (file-name-directory (buffer-file-name))))
+ (or (inferior-erlang-running-p)
+ (error "No inferior Erlang is running"))
+ (inferior-erlang-display-buffer)
+ (inferior-erlang-send-empty-cmd-unless-already-at-prompt)
+ (inferior-erlang-wait-prompt)
+ (inferior-erlang-send-command (format "cd('%s')." dir) nil))
+
+(defun erlang-align-arrows (start end)
+ "Align arrows (\"->\") in function clauses from START to END.
+When called interactively, aligns arrows after function clauses inside
+the region.
+
+With a prefix argument, aligns all arrows, not just those in function
+clauses.
+
+Example:
+
+sum(L) -> sum(L, 0).
+sum([H|T], Sum) -> sum(T, Sum + H);
+sum([], Sum) -> Sum.
+
+becomes:
+
+sum(L) -> sum(L, 0).
+sum([H|T], Sum) -> sum(T, Sum + H);
+sum([], Sum) -> Sum."
+ (interactive "r")
+ (save-excursion
+ (let (;; regexp for matching arrows. without a prefix argument,
+ ;; the regexp matches function heads. With a prefix, it
+ ;; matches any arrow.
+ (re (if current-prefix-arg
+ "^.*\\(\\)->"
+ (eval-when-compile
+ (concat "^" erlang-atom-regexp ".*\\(\\)->"))))
+ ;; part of regexp matching directly before the arrow
+ (arrow-match-pos (if current-prefix-arg
+ 1
+ (1+ erlang-atom-regexp-matches)))
+ ;; accumulator for positions where arrows are found, ordered
+ ;; by buffer position (from greatest to smallest)
+ (arrow-positions '())
+ ;; accumulator for longest distance from start of line to arrow
+ (most-indent 0)
+ ;; marker to track the end of the region we're aligning
+ (end-marker (progn (goto-char end)
+ (point-marker))))
+ ;; Pass 1: Find the arrow positions, adjust the whitespace
+ ;; before each arrow to one space, and find the greatest
+ ;; indentation level.
+ (goto-char start)
+ (while (re-search-forward re end-marker t)
+ (goto-char (match-beginning arrow-match-pos))
+ (just-one-space) ; adjust whitespace
+ (setq arrow-positions (cons (point) arrow-positions))
+ (setq most-indent (max most-indent (erlang-column-number))))
+ (set-marker end-marker nil) ; free the marker
+ ;; Pass 2: Insert extra padding so that all arrow indentation is
+ ;; equal. This is done last-to-first by buffer position, so that
+ ;; inserting spaces before one arrow doesn't change the
+ ;; positions of the next ones.
+ (mapc (lambda (arrow-pos)
+ (goto-char arrow-pos)
+ (let* ((pad (- most-indent (erlang-column-number))))
+ (when (> pad 0)
+ (insert-char ?\ pad))))
+ arrow-positions))))
+
+(defun erlang-column-number ()
+ "Return the column number of the current position in the buffer.
+Tab characters are counted by their visual width."
+ (string-width (buffer-substring (line-beginning-position) (point))))
+
+(defun erlang-current-defun ()
+ "`add-log-current-defun-function' for Erlang."
+ (save-excursion
+ (erlang-beginning-of-function)
+ (if (looking-at "[a-z0-9_]+")
+ (match-string 0))))
+
+;; Aliases for backward compatibility with older versions of Erlang Mode.
+;;
+;; Unfortuantely, older versions of Emacs doesn't have `defalias' and
+;; `make-obsolete' so we have to define our own `obsolete' function.
+
+(defun erlang-obsolete (sym newdef)
+ "Make the obsolete function SYM refer to the defined function NEWDEF.
+
+Simplified version of a combination `defalias' and `make-obsolete',
+it assumes that NEWDEF is loaded."
+ (defalias sym (symbol-function newdef))
+ (if (fboundp 'make-obsolete)
+ (make-obsolete sym newdef)))
+
+
+(erlang-obsolete 'calculate-erlang-indent 'erlang-calculate-indent)
+(erlang-obsolete 'calculate-erlang-stack-indent
+ 'erlang-calculate-stack-indent)
+(erlang-obsolete 'at-erlang-keyword 'erlang-at-keyword)
+(erlang-obsolete 'at-erlang-operator 'erlang-at-operator)
+(erlang-obsolete 'beginning-of-erlang-clause 'erlang-beginning-of-clause)
+(erlang-obsolete 'end-of-erlang-clause 'erlang-end-of-clause)
+(erlang-obsolete 'mark-erlang-clause 'erlang-mark-clause)
+(erlang-obsolete 'beginning-of-erlang-function 'erlang-beginning-of-function)
+(erlang-obsolete 'end-of-erlang-function 'erlang-end-of-function)
+(erlang-obsolete 'mark-erlang-function 'erlang-mark-function)
+(erlang-obsolete 'pass-over-erlang-clause 'erlang-pass-over-function)
+(erlang-obsolete 'name-of-erlang-function 'erlang-name-of-function)
+
+
+;; Fixme: shouldn't redefine `set-visited-file-name' anyhow -- see above.
+(defconst erlang-unload-hook
+ (list (lambda ()
+ (defalias 'set-visited-file-name
+ 'erlang-orig-set-visited-file-name)
+ (when (featurep 'advice)
+ (ad-unadvise 'Man-notify-when-ready)
+ (ad-unadvise 'set-visited-file-name)))))
+
+
+(defun erlang-string-to-int (string)
+ (if (fboundp 'string-to-number)
+ (string-to-number string)
+ (funcall (symbol-function 'string-to-int) string)))
+
+;; The end...
+
+(provide 'erlang)
+
+(run-hooks 'erlang-load-hook)
+
+;; Local variables:
+;; coding: iso-8859-1
+;; End:
+
+;;; erlang.el ends here
diff --git a/lib/tools/emacs/erlang_appwiz.el b/lib/tools/emacs/erlang_appwiz.el
new file mode 100644
index 0000000000..ecbce66f47
--- /dev/null
+++ b/lib/tools/emacs/erlang_appwiz.el
@@ -0,0 +1,1345 @@
+;;; -*- Emacs-Lisp -*-
+;;; File: erlang_appwiz.el
+;;; Author: Johan Bevermyr
+;;; Created: Tue Dec 9 13:14:24 1997
+;;; Purpose: Adds a simple application wizard to erlang.el.
+
+;; OBS! Must be loaded before the erlang.el file is loaded.
+;; Add the following to your .emacs file before erlang.el is loaded.
+;;
+;; (load "erlang_appwiz" t nil)
+;;
+;; Customisation of makefile generation:
+;;
+;; The templates for generating makefiles are stored in the
+;; variables erlang-skel-makefile-src and erlang-skel-makefile-middle.
+;;
+;; These can be modified by setting the variables before or after this
+;; file is loaded.
+;;
+;; For example, to generate OTP-style make files:
+;;
+;;
+;;(defvar erlang-skel-makefile-src
+;; '((erlang-skel-include erlang-skel-nomodule-header)
+;; "CC_ROOT := $(shell pwd | sed 's/erts.*$$//')" n
+;; "AUTOCONF := $(CC_ROOT)/erts/autoconf" n
+;; "TARGET := $(shell $(AUTOCONF)/config.guess)"
+;; "include $(CC_ROOT)/internal_tools/make/$(TARGET)/otp.mk" n
+;; n
+;; "# ----------------------------------------------------" n
+;; "# Application version " n
+;; "# ----------------------------------------------------" n
+;; "include ../vsn.mk" n
+;; "VSN=$(KERNEL_VSN)" n
+;; n
+;; "# ----------------------------------------------------" n
+;; "# Release directory specification" n
+;; "# ----------------------------------------------------" n
+;; "RELEASE_PATH= ../../../release/$(TARGET)" n
+;; "RELSYSDIR = $(RELEASE_PATH)/lib/kernel-$(VSN)" n
+;; n
+;; "# ----------------------------------------------------" n
+;; "# Target Specs" n
+;; "# ----------------------------------------------------" n
+;; n
+;; "MODULES= " appwiz-erlang-modulename n
+;; n
+;; "HRL_FILES="
+;; n
+;; INTERNAL_HRL_FILES= appwiz-erlang-modulename "_sup.hrl" n
+;; n
+;; "ERL_FILES= $(MODULES:%=%.erl)" n
+;; n
+;; "TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET)" n
+;; n
+;; "APP_FILE= " appwiz-erlang-modulename ".app" n
+;; n
+;; "APP_SRC= $(APP_FILE).src" n
+;; "APP_TARGET= ../ebin/$(APP_FILE)" n
+;; n
+;; "# ----------------------------------------------------" n
+;; "# FLAGS " n
+;; "# ----------------------------------------------------" n
+;; "ERL_FLAGS += " n
+;; "ERL_COMPILE_FLAGS += -I../include" n
+;; n
+;; "# ----------------------------------------------------" n
+;; "# Targets" n
+;; "# ----------------------------------------------------" n
+;; n
+;; "debug opt: $(TARGET_FILES)" n
+;; n
+;; "clean:" n
+;; " rm -f $(TARGET_FILES) $(GEN_FILES)" n
+;; " rm -f core" n
+;; n
+;; "docs:" n
+;; n
+;; "# ----------------------------------------------------" n
+;; "# Special Build Targets " n
+;; "# ----------------------------------------------------" n
+;; " " n
+;; "$(APP_TARGET): $(APP_SRC) " n
+;; " sed -e 's;%VSN%;$(VSN);' $(APP_SRC) > $(APP_TARGET)" n
+;; " " n
+;; "# ----------------------------------------------------" n
+;; "# Release Target " n
+;; "# ----------------------------------------------------" n
+;; "include $(CC_ROOT)/internal_tools/make/otp_release_targets.mk" n
+;; n
+;; "release_spec: opt" n
+;; " $(INSTALL_DIR) $(RELSYSDIR)/src " n
+;; " $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src " n
+;; " $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src " n
+;; " $(INSTALL_DIR) $(RELSYSDIR)/include " n
+;; " $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include " n
+;; " $(INSTALL_DIR) $(RELSYSDIR)/ebin " n
+;; " $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin " n
+;; n
+;; "release_docs_spec:" n
+;; ))
+;;
+;;
+;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Erlang application wizard
+;;
+
+(defun erlang-application-wizard (directory name)
+ "Creates all files and directories needed for an application.
+The top-level directory is placed in DIRECTORY. NAME is used when
+creating the root directory and for naming application files."
+
+ (interactive "DApplication root directory: \nsName of application: ")
+ (let ((dir nil)
+ (lastchar (substring directory (- (length directory) 1)))
+ (apptype (completing-read "Type of application: "
+ '(("gen_server" 1)
+ ("gen_event" 2)
+ ("gen_fsm" 3)
+ ("other" 4))
+ nil t "gen_server"))
+ (appname nil)
+ (apptemplate nil)
+ (apitemplate nil)
+ (extension nil))
+
+ (if (string= lastchar "/")
+ (setq dir directory)
+ (setq dir (concat directory "/")))
+
+ ;; determine type of application
+ (cond ((string= apptype "gen_server")
+ (setq extension "_server")
+ (setq appname (concat name extension))
+ (setq apptemplate 'tempo-template-erlang-generic-server)
+ (setq apitemplate 'tempo-template-erlang-large-header))
+ ((string= apptype "gen_event")
+ (setq extension "_event")
+ (setq appname (concat name extension))
+ (setq apptemplate 'tempo-template-erlang-gen-event)
+ (setq apitemplate 'tempo-template-erlang-large-header))
+ ((string= apptype "gen_fsm")
+ (setq extension "_fsm")
+ (setq appname (concat name extension))
+ (setq apptemplate 'tempo-template-erlang-gen-fsm)
+ (setq apitemplate 'tempo-template-large-header))
+ (t
+ ;; use defaults _work
+ (setq extension "_work")
+ (setq appname (concat name extension))
+ (setq apptemplate 'tempo-template-erlang-large-header)
+ (setq apitemplate 'tempo-template-erlang-large-header)))
+
+ (setq appwiz-erlang-modulename appname)
+ (setq appwiz-erlang-ext extension)
+
+ ;; create directories
+ (make-directory (concat dir name "/" "src") t)
+ (make-directory (concat dir name "/" "ebin") t)
+ (make-directory (concat dir name "/" "include") t)
+
+ ;; create directory content
+ ;;;;;;;;; .erl
+ (find-file (concat dir name "/" "src/" name ".erl"))
+ (funcall apitemplate)
+ (insert "API module for the application " name ".")
+ (save-buffer)
+
+ ;;;;;;;;; _app.erl
+ (find-file (concat dir name "/" "src/" name "_app.erl"))
+ (tempo-template-erlang-application)
+ (insert "Application callback module for the application " name ".")
+
+ (let ((quotedname (erlang-add-quotes-if-needed
+ (concat name "_sup")))
+ (start (point)))
+ (while (search-forward "'TopSupervisor':start_link" nil t)
+ (replace-match (concat quotedname ":start_link") nil t))
+ (goto-char start))
+
+ (save-buffer)
+
+ ;;;;;;;;; _sup.erl
+ (find-file (concat dir name "/" "src/" name "_sup.erl"))
+ (tempo-template-erlang-supervisor)
+ (insert "Top level supervisor for the application " name ".")
+
+
+ (let ((quotedname (erlang-add-quotes-if-needed appname))
+ (start (point)))
+ (while (search-forward "'AName'" nil t)
+ (replace-match quotedname nil t))
+ (goto-char start))
+
+ (let ((quotedname (erlang-add-quotes-if-needed appname))
+ (start (point)))
+ (goto-char 0)
+ (while (search-forward "'AMODULE'" nil t)
+ (replace-match quotedname nil t))
+ (goto-char start))
+
+ (save-buffer)
+
+ ;;;;;;;;; _sup.hrl
+ (find-file (concat dir name "/" "src/" name "_sup.hrl"))
+ (tempo-template-erlang-nomodule-header)
+ (save-buffer)
+
+ ;;;;;;;;; _(application).erl
+ (find-file (concat dir name "/" "src/" appname ".erl"))
+ (funcall apptemplate)
+ (save-buffer)
+
+ ;;;;;;;;; makefile (src)
+ (find-file (concat dir name "/" "src/makefile"))
+ (setq appwiz-erlang-modulename name)
+ (setq appwiz-erlang-ext extension)
+ (tempo-template-erlang-makefile-src)
+ (insert "Makefile for application " name ".")
+ (let ((start (point)))
+ (goto-char 0)
+ (while (search-forward "%" nil t)
+ (replace-match "#" nil t))
+ (goto-char start))
+ (save-buffer)
+
+ ;;;;;;;;; makefile (middle)
+ (find-file (concat dir name "/" "makefile"))
+ (tempo-template-erlang-makefile-middle)
+ (insert "Makefile for application " name ".")
+ (let ((start (point)))
+ (goto-char 0)
+ (while (search-forward "%" nil t)
+ (replace-match "#" nil t))
+ (goto-char start))
+ (save-buffer)
+
+ ;;;;;;;;; .app
+ (find-file (concat dir name "/" "ebin/" name ".app"))
+ (erlang-mode)
+ (tempo-template-erlang-app)
+ (insert "Application specification file for " name ".")
+ (save-buffer)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; These are setq:ed
+;;
+
+(defvar appwiz-erlang-modulename "foo")
+(defvar appwiz-erlang-ext "_work")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Skeletons.
+;; Skeletons for nomodule header and .app file added by JB.
+;;
+
+(defvar erlang-skel
+ '(("If" "if" erlang-skel-if)
+ ("Case" "case" erlang-skel-case)
+ ("Receive" "receive" erlang-skel-receive)
+ ("Receive After" "after" erlang-skel-receive-after)
+ ("Receive Loop" "loop" erlang-skel-receive-loop)
+ ("Module" "module" erlang-skel-module)
+ ("Author" "author" erlang-skel-author)
+ ("Query" "query" erlang-skel-query)
+ ()
+ ("Small Header" "small-header"
+ erlang-skel-small-header erlang-skel-header)
+ ("Normal Header" "normal-header"
+ erlang-skel-normal-header erlang-skel-header)
+ ("Large Header" "large-header"
+ erlang-skel-large-header erlang-skel-header)
+ ("No Moudle Header" "nomodule-header"
+ erlang-skel-nomodule-header erlang-skel-header)
+ ()
+ ("Small Server" "small-server"
+ erlang-skel-small-server erlang-skel-header)
+ ()
+ ("application" "application"
+ erlang-skel-application erlang-skel-header)
+ ("app" "app"
+ erlang-skel-app erlang-skel-header)
+ ("supervisor" "supervisor"
+ erlang-skel-supervisor erlang-skel-header)
+ ("supervisor_bridge" "supervisor-bridge"
+ erlang-skel-supervisor-bridge erlang-skel-header)
+ ("gen_server" "generic-server"
+ erlang-skel-generic-server erlang-skel-header)
+ ("gen_event" "gen-event"
+ erlang-skel-gen-event erlang-skel-header)
+ ("gen_fsm" "gen-fsm"
+ erlang-skel-gen-fsm erlang-skel-header))
+ "*Description of all skeletons templates.
+Both functions and menu entries will be created.
+
+Each entry in `erlang-skel' should be a list with three or four
+elements, or the empty list.
+
+The first element is the name which shows up in the menu. The second
+is the `tempo' identfier (The string \"erlang-\" will be added in
+front of it). The third is the skeleton descriptor, a variable
+containing `tempo' attributes as described in the function
+`tempo-define-template'. The optinal fourth elements denotes a
+function which should be called when the menu is selected.
+
+Functions corresponding to every template will be created. The name
+of the function will be `tempo-template-erlang-X' where `X' is the
+tempo identifier as specified in the second argument of the elements
+in this list.
+
+A list with zero elemets means that the a horisontal line should
+be placed in the menu.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Template for .app file skeleton
+;;
+
+(defvar erlang-skel-app
+ '((erlang-skel-include erlang-skel-nomodule-header)
+ "{application, "
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "," n>
+ "[{description, \"" (erlang-get-module-from-file-name) "\"}," n>
+ "{vsn, \"0.1\"}," n>
+ "{modules, ["
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "," n>
+ (erlang-add-quotes-if-needed
+ (concat (erlang-get-module-from-file-name) "_app")) "," n>
+ (erlang-add-quotes-if-needed
+ (concat (erlang-get-module-from-file-name) "_sup")) "," n>
+ (erlang-add-quotes-if-needed
+ (concat (erlang-get-module-from-file-name) appwiz-erlang-ext)) "]}," n>
+ "{registered, ["
+ (erlang-add-quotes-if-needed
+ (concat (erlang-get-module-from-file-name) appwiz-erlang-ext)) ","
+ (erlang-add-quotes-if-needed
+ (concat (erlang-get-module-from-file-name) "_sup")) "]}," n>
+ "{applications, [kernel," n>
+ "stdlib," n>
+ "sasl," n>
+ "mnesia]}," n>
+ "{env, []}," n>
+ "{mod, {"
+ (erlang-add-quotes-if-needed
+ (concat (erlang-get-module-from-file-name) "_app"))
+ ", []}}]}." n
+ )
+ "*The template of an application file
+Please see the function `tempo-define-template'.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Template for no-module header skeleton.
+;;
+
+(defvar erlang-skel-nomodule-header
+ '(o (erlang-skel-separator)
+ (erlang-skel-include erlang-skel-copyright-comment
+ erlang-skel-file-comment
+ erlang-skel-author-comment)
+ "%%% Purpose : " p n
+ (erlang-skel-include erlang-skel-created-comment)
+ (erlang-skel-separator) n)
+ "*The template of a normal header.
+Please see the function `tempo-define-template'.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; .app extension added.
+;;
+
+(defvar erlang-file-name-extension-regexp "\\.\\(erl\\|hrl\\|app\\)$"
+ "*Regexp which should match an erlang file name.
+
+This regexp is used when an Erlang module name is extracted from the
+name of an Erlang source file.
+
+The regexp should only match the section of the file name which should
+be excluded from the module name.
+
+To match all files set this variable to \"\\\\(\\\\..*\\\\|\\\\)$\".
+The matches all except the extension. This is useful if the Erlang
+tags system should interpretate tags on the form `module:tag' for
+files written in other languages than Erlang.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Wizard menu added.
+;;
+
+(defvar erlang-menu-items
+ '(("Indent"
+ (("Indent Line" erlang-indent-command)
+ ("Indent Region " erlang-indent-region
+ (if erlang-xemacs-p (mark) mark-active))
+ ("Indent Clause" erlang-indent-caluse)
+ ("Indent Function" erlang-indent-function)
+ ("Indent Buffer" erlang-indent-current-buffer)))
+ ("Edit"
+ (("Fill Comment" erlang-fill-paragraph)
+ ("Comment Region" comment-region
+ (if erlang-xemacs-p (mark) mark-active))
+ ("Uncomment Region" erlang-uncomment-region
+ (if erlang-xemacs-p (mark) mark-active))
+ nil
+ ("beginning of Function" erlang-beginning-of-function)
+ ("End of Function" erlang-end-of-function)
+ ("Mark Function" erlang-mark-function)
+ nil
+ ("beginning of Clause" erlang-beginning-of-clause)
+ ("End of Clause" erlang-end-of-clause)
+ ("Mark Clause" erlang-mark-clause)
+ nil
+ ("New Clause" erlang-generate-new-clause)
+ ("Clone Arguments" erlang-clone-arguments)))
+ ("Font Lock Mode"
+ (("Level 3" erlang-font-lock-level-3)
+ ("Level 2" erlang-font-lock-level-2)
+ ("Level 1" erlang-font-lock-level-1)
+ ("Off" erlang-font-lock-level-0)))
+ ("TAGS"
+ (("Find Tag" find-tag)
+ ("Find Next Tag" erlang-find-next-tag)
+ ;("Find Regexp" find-tag-regexp)
+ ("Complete Word" erlang-complete-tag)
+ ("Tags Apropos" tags-apropos)
+ ("Search Files" tags-search)))
+ nil
+ ("Erlang Shell" inferior-erlang-run-or-select)
+ ("Compile" erlang-compile)
+ ("Next Error" inferior-erlang-next-error)
+ nil
+ ("Version" erlang-version)
+ nil
+ ("Wizards"
+ (("Application Wizard" erlang-application-wizard))))
+ "*Description of menu used in Erlang mode.
+
+This variable must be a list. The elements are either nil representing
+a horisontal line or a list with two or three elements. The first is
+the name of the menu item, the second is the function to call, or a
+submenu, on the same same form as ITEMS. The third optional argument
+is an expression which is evaluated every time the menu is displayed.
+Should the expression evaluate to nil the menu item is ghosted.
+
+Example:
+ '((\"Func1\" function-one)
+ (\"SubItem\"
+ ((\"Yellow\" function-yellow)
+ (\"Blue\" function-blue)))
+ nil
+ (\"Region Funtion\" spook-function midnight-variable))
+
+Call the function `erlang-menu-init' after modifying this variable.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Prefixing space removed from date string
+;;
+
+(defun erlang-skel-d-mmm-yyyy ()
+ "Return the current date as a string in \"DD Mon YYYY\" form.
+The first character of DD is *not* space if the value is less than 10."
+ (let ((date (current-time-string)))
+ (format "%d %s %s"
+ (string-to-int (substring date 8 10))
+ (substring date 4 7)
+ (substring date -4))))
+
+(defvar erlang-skel-date-function 'erlang-skel-d-mmm-yyyy
+ "*Function which returns date string.
+Look in the module `time-stamp' for a battery of functions.")
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Fixed skeletons. erlang-add-quotes-if-needed introduced where needed.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Server templates.
+
+(defvar erlang-skel-small-server
+ '((erlang-skel-include erlang-skel-large-header)
+ "-export([start/0,init/1])." n n n
+ "start() ->" n> "spawn("
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name))
+ ", init, [self()])." n n
+ "init(From) ->" n>
+ "loop(From)." n n
+ "loop(From) ->" n>
+ "receive" n>
+ p "_ ->" n>
+ "loop(From)" n>
+ "end."
+ )
+ "*Template of a small server.
+Please see the function `tempo-define-template'.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Behaviour templates.
+
+(defvar erlang-skel-application
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(application)." n
+ n
+ "%% application callbacks" n
+ "-export([start/2, stop/1])." n n
+ (erlang-skel-separator)
+ "%%% Callback functions from application" n
+ (erlang-skel-separator)
+ n
+ (erlang-skel-separator 2)
+ "%% Func: start/2" n
+ "%% Returns: {ok, Pid} |" n
+ "%% {ok, Pid, State} |" n
+ "%% {error, Reason} " n
+ (erlang-skel-separator 2)
+ "start(Type, StartArgs) ->" n>
+ "case 'TopSupervisor':start_link(StartArgs) of" n>
+ "{ok, Pid} -> " n>
+ "{ok, Pid};" n>
+ "Error ->" n>
+ "Error" n>
+ "end." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: stop/1" n
+ "%% Returns: any "n
+ (erlang-skel-separator 2)
+ "stop(State) ->" n>
+ "ok." n
+ n
+ (erlang-skel-separator)
+ "%%% Internal functions" n
+ (erlang-skel-separator)
+ )
+ "*The template of an application behaviour.
+Please see the function `tempo-define-template'.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar erlang-skel-supervisor
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(supervisor)." n
+ n
+ "%% External exports" n
+ "-export([start_link/1])." n
+ n
+ "%% supervisor callbacks" n
+ "-export([init/1])." n n
+ (erlang-skel-separator)
+ "%%% API" n
+ (erlang-skel-separator)
+ "start_link(StartArgs) ->" n>
+ "supervisor:start_link({local, "
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "}, "
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name))
+ ", StartArgs)." n
+ n
+ (erlang-skel-separator)
+ "%%% Callback functions from supervisor" n
+ (erlang-skel-separator)
+ n
+ (erlang-skel-separator 2)
+ "%% Func: init/1" n
+ "%% Returns: {ok, {SupFlags, [ChildSpec]}} |" n
+ "%% ignore |" n
+ "%% {error, Reason} " n
+ (erlang-skel-separator 2)
+ "init(StartArgs) ->" n>
+ "AChild = {'AName',{'AModule',start_link,[]}," n>
+ "permanent,2000,worker,['AModule']}," n>
+ "{ok,{{one_for_all,4,3600}, [AChild]}}." n
+ n
+ (erlang-skel-separator)
+ "%%% Internal functions" n
+ (erlang-skel-separator)
+ )
+ "*The template of an supervisor behaviour.
+Please see the function `tempo-define-template'.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar erlang-skel-supervisor-bridge
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(supervisor_bridge)." n
+ n
+ "%% External exports" n
+ "-export([start_link/0])." n
+ n
+ "%% supervisor callbacks" n
+ "-export([init/1, terminate/2])." n n
+ "-record(state, {})." n
+ n
+ (erlang-skel-separator)
+ "%%% API" n
+ (erlang-skel-separator)
+ "start_link() -> " n>
+ "supervisor_bridge:start_link({local, "
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "}, "
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name))
+ ", [])." n
+ n
+ (erlang-skel-separator)
+ "%%% Callback functions from supervisor_bridge" n
+ (erlang-skel-separator)
+ n
+ (erlang-skel-separator 2)
+ "%% Func: init/1" n
+ "%% Returns: {ok, Pid, State} |" n
+ "%% ignore |" n
+ "%% {error, Reason} " n
+ (erlang-skel-separator 2)
+ "init([]) ->" n>
+ "case 'AModule':start_link() of" n>
+ "{ok, Pid} ->" n>
+ "{ok, Pid, #state{}};" n>
+ "Error ->" n>
+ "Error" n>
+ "end." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: terminate/2" n
+ "%% Purpose: Synchronized shutdown of the underlying sub system." n
+ "%% Returns: any" n
+ (erlang-skel-separator 2)
+ "terminate(Reason, State) ->" n>
+ "'AModule':stop()," n>
+ "ok." n
+ n
+ (erlang-skel-separator)
+ "%%% Internal functions" n
+ (erlang-skel-separator)
+ )
+ "*The template of an supervisor_bridge behaviour.
+Please see the function `tempo-define-template'.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar erlang-skel-generic-server
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(gen_server)." n
+ n
+ "%% External exports" n
+ "-export([start_link/0])." n
+ n
+ "%% gen_server callbacks" n
+ "-export([init/1, handle_call/3, handle_cast/2, "
+ "handle_info/2, terminate/2])." n n
+ "-record(state, {})." n
+ n
+ (erlang-skel-separator)
+ "%%% API" n
+ (erlang-skel-separator)
+ "start_link() -> " n>
+ "gen_server:start_link({local, "
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "}, "
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name))
+ ", [], [])." n
+ n
+ (erlang-skel-separator)
+ "%%% Callback functions from gen_server" n
+ (erlang-skel-separator)
+ n
+ (erlang-skel-separator 2)
+ "%% Func: init/1" n
+ "%% Returns: {ok, State} |" n
+ "%% {ok, State, Timeout} |" n
+ "%% ignore |" n
+ "%% {stop, Reason}" n
+ (erlang-skel-separator 2)
+ "init([]) ->" n>
+ "{ok, #state{}}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: handle_call/3" n
+ "%% Returns: {reply, Reply, State} |" n
+ "%% {reply, Reply, State, Timeout} |" n
+ "%% {noreply, State} |" n
+ "%% {noreply, State, Timeout} |" n
+ "%% {stop, Reason, Reply, State} | (terminate/2 is called)" n
+ "%% {stop, Reason, State} (terminate/2 is called)" n
+ (erlang-skel-separator 2)
+ "handle_call(Request, From, State) ->" n>
+ "Reply = ok," n>
+ "{reply, Reply, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: handle_cast/2" n
+ "%% Returns: {noreply, State} |" n
+ "%% {noreply, State, Timeout} |" n
+ "%% {stop, Reason, State} (terminate/2 is called)" n
+ (erlang-skel-separator 2)
+ "handle_cast(Msg, State) ->" n>
+ "{noreply, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: handle_info/2" n
+ "%% Returns: {noreply, State} |" n
+ "%% {noreply, State, Timeout} |" n
+ "%% {stop, Reason, State} (terminate/2 is called)" n
+ (erlang-skel-separator 2)
+ "handle_info(Info, State) ->" n>
+ "{noreply, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: terminate/2" n
+ "%% Purpose: Shutdown the server" n
+ "%% Returns: any (ignored by gen_server)" n
+ (erlang-skel-separator 2)
+ "terminate(Reason, State) ->" n>
+ "ok." n
+ n
+ (erlang-skel-separator)
+ "%%% Internal functions" n
+ (erlang-skel-separator)
+ )
+ "*The template of a generic server.
+Please see the function `tempo-define-template'.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar erlang-skel-gen-event
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(gen_event)." n
+ n
+ "%% External exports" n
+ "-export([start_link/0, add_handler/0])." n
+ n
+ "%% gen_event callbacks" n
+ "-export([init/1, handle_event/2, handle_call/2, "
+ "handle_info/2, terminate/2])." n n
+ "-record(state, {})." n
+ n
+ (erlang-skel-separator)
+ "%%% API" n
+ (erlang-skel-separator)
+ "start_link() ->" n>
+ "gen_event:start_link({local, "
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "}). " n
+ n
+ "add_handler() ->" n>
+ "gen_event:add_handler("
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) ", "
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name))
+ ", [])." n
+ n
+ (erlang-skel-separator)
+ "%%% Callback functions from gen_event" n
+ (erlang-skel-separator)
+ n
+ (erlang-skel-separator 2)
+ "%% Func: init/1" n
+ "%% Returns: {ok, State} |" n
+ "%% Other" n
+ (erlang-skel-separator 2)
+ "init([]) ->" n>
+ "{ok, #state{}}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: handle_event/2" n
+ "%% Returns: {ok, State} |" n
+ "%% {swap_handler, Args1, State1, Mod2, Args2} |" n
+ "%% remove_handler " n
+ (erlang-skel-separator 2)
+ "handle_event(Event, State) ->" n>
+ "{ok, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: handle_call/2" n
+ "%% Returns: {ok, Reply, State} |" n
+ "%% {swap_handler, Reply, Args1, State1, Mod2, Args2} |" n
+ "%% {remove_handler, Reply} " n
+ (erlang-skel-separator 2)
+ "handle_call(Request, State) ->" n>
+ "Reply = ok," n>
+ "{ok, Reply, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: handle_info/2" n
+ "%% Returns: {ok, State} |" n
+ "%% {swap_handler, Args1, State1, Mod2, Args2} |" n
+ "%% remove_handler " n
+ (erlang-skel-separator 2)
+ "handle_info(Info, State) ->" n>
+ "{ok, State}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: terminate/2" n
+ "%% Purpose: Shutdown the server" n
+ "%% Returns: any" n
+ (erlang-skel-separator 2)
+ "terminate(Reason, State) ->" n>
+ "ok." n
+ n
+ (erlang-skel-separator)
+ "%%% Internal functions" n
+ (erlang-skel-separator)
+ )
+ "*The template of a gen_event.
+Please see the function `tempo-define-template'.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar erlang-skel-gen-fsm
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(gen_fsm)." n
+ n
+ "%% External exports" n
+ "-export([start_link/0])." n
+ n
+ "%% gen_fsm callbacks" n
+ "-export([init/1, state_name/2, state_name/3, handle_event/3," n>
+ "handle_sync_event/4, handle_info/3, terminate/3])." n n
+ "-record(state, {})." n
+ n
+ (erlang-skel-separator)
+ "%%% API" n
+ (erlang-skel-separator)
+ "start_link() ->" n>
+ "gen_fsm:start_link({local, "
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "}, "
+ (erlang-add-quotes-if-needed (erlang-get-module-from-file-name))
+ ", [], [])." n
+ n
+ (erlang-skel-separator)
+ "%%% Callback functions from gen_fsm" n
+ (erlang-skel-separator)
+ n
+ (erlang-skel-separator 2)
+ "%% Func: init/1" n
+ "%% Returns: {ok, StateName, StateData} |" n
+ "%% {ok, StateName, StateData, Timeout} |" n
+ "%% ignore |" n
+ "%% {stop, StopReason} " n
+ (erlang-skel-separator 2)
+ "init([]) ->" n>
+ "{ok, state_name, #state{}}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: StateName/2" n
+ "%% Returns: {next_state, NextStateName, NextStateData} |" n
+ "%% {next_state, NextStateName, NextStateData, Timeout} |" n
+ "%% {stop, Reason, NewStateData} " n
+ (erlang-skel-separator 2)
+ "state_name(Event, StateData) ->" n>
+ "{nextstate, state_name, StateData}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: StateName/3" n
+ "%% Returns: {next_state, NextStateName, NextStateData} |" n
+ "%% {next_state, NextStateName, NextStateData, Timeout} |" n
+ "%% {reply, Reply, NextStateName, NextStateData} |" n
+ "%% {reply, Reply, NextStateName, NextStateData, Timeout} |" n
+ "%% {stop, Reason, NewStateData} |" n
+ "%% {stop, Reason, Reply, NewStateData} " n
+ (erlang-skel-separator 2)
+ "state_name(Event, From, StateData) ->" n>
+ "Reply = ok," n>
+ "{reply, Reply, state_name, StateData}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: handle_event/3" n
+ "%% Returns: {next_state, NextStateName, NextStateData} |" n
+ "%% {next_state, NextStateName, NextStateData, Timeout} |" n
+ "%% {stop, Reason, NewStateData} " n
+ (erlang-skel-separator 2)
+ "handle_event(Event, StateName, StateData) ->" n>
+ "{nextstate, StateName, StateData}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: handle_sync_event/4" n
+ "%% Returns: {next_state, NextStateName, NextStateData} |" n
+ "%% {next_state, NextStateName, NextStateData, Timeout} |" n
+ "%% {reply, Reply, NextStateName, NextStateData} |" n
+ "%% {reply, Reply, NextStateName, NextStateData, Timeout} |" n
+ "%% {stop, Reason, NewStateData} |" n
+ "%% {stop, Reason, Reply, NewStateData} " n
+ (erlang-skel-separator 2)
+ "handle_sync_event(Event, From, StateName, StateData) ->" n>
+ "Reply = ok," n>
+ "{reply, Reply, StateName, StateData}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: handle_info/3" n
+ "%% Returns: {next_state, NextStateName, NextStateData} |" n
+ "%% {next_state, NextStateName, NextStateData, Timeout} |" n
+ "%% {stop, Reason, NewStateData} " n
+ (erlang-skel-separator 2)
+ "handle_info(Info, StateName, StateData) ->" n>
+ "{nextstate, StateName, StateData}." n
+ n
+ (erlang-skel-separator 2)
+ "%% Func: terminate/3" n
+ "%% Purpose: Shutdown the fsm" n
+ "%% Returns: any" n
+ (erlang-skel-separator 2)
+ "terminate(Reason, StateName, StatData) ->" n>
+ "ok." n
+ n
+ (erlang-skel-separator)
+ "%%% Internal functions" n
+ (erlang-skel-separator)
+ )
+ "*The template of a gen_fsm.
+Please see the function `tempo-define-template'.")
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Original erlang-add-quotes-if-needed is broken, we install a
+;; new version.
+;;
+
+(add-hook 'erlang-load-hook 'my-erlang-load-mods)
+
+(defun fixed-erlang-add-quotes-if-needed (str)
+ "Return STR, possibly with quotes."
+ (let ((saved-case-fold-search case-fold-search)
+ (result nil))
+ (setq case-fold-search nil)
+ (setq result (if (string-match (concat "\\`" erlang-atom-regexp "\\'") str)
+ str
+ (concat "'" str "'")))
+ (setq case-fold-search saved-case-fold-search)
+ result))
+
+(defun my-erlang-load-mods ()
+ (fset 'erlang-add-quotes-if-needed
+ (symbol-function 'fixed-erlang-add-quotes-if-needed))
+ (appwiz-skel-init))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Additional skeletons which are not shown in the Erlang menu.
+;;
+
+(defvar appwiz-skel
+ '(
+; ("generic-server-no-api" erlang-skel-generic-server-no-api)
+; ("generic-server-api" erlang-skel-generic-server-api)
+; ("gen-event-no-api" erlang-skel-gen-event-no-api)
+; ("gen-event-api" erlang-skel-gen-event-api)
+; ("gen-fsm-no-api" erlang-skel-gen-fsm-no-api)
+; ("gen-fsm-api" erlang-skel-gen-fsm-api)
+ ("makefile-middle" erlang-skel-makefile-middle)
+ ("makefile-src" erlang-skel-makefile-src)))
+
+(defun appwiz-skel-init ()
+ "Generate the skeleton functions."
+ (interactive)
+ (condition-case nil
+ (require 'tempo)
+ (error t))
+ (if (featurep 'tempo)
+ (let ((skel appwiz-skel))
+ (while skel
+ (funcall (symbol-function 'tempo-define-template)
+ (concat "erlang-" (nth 0 (car skel)))
+ ;; The tempo template used contains an `include'
+ ;; function call only, hence changes to the
+ ;; variables describing the templates take effect
+ ;; immdiately.
+ (list (list 'erlang-skel-include (nth 1 (car skel))))
+ (nth 0 (car skel)))
+ (setq skel (cdr skel))))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;;
+;;
+;;(defvar erlang-skel-generic-server-no-api
+;; '((erlang-skel-include erlang-skel-large-header)
+;; "-behaviour(gen_server)." n
+;; n
+;; "%% gen_server callbacks" n
+;; "-export([init/1, handle_call/3, handle_cast/2, "
+;; "handle_info/2, terminate/2])." n n
+;; "-record(state, {})." n
+;; n
+;; (erlang-skel-separator)
+;; "%%% Callback functions from gen_server" n
+;; (erlang-skel-separator)
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: init/1" n
+;; "%% Returns: {ok, State} |" n
+;; "%% {ok, State, Timeout} |" n
+;; "%% ignore |" n
+;; "%% {stop, Reason}" n
+;; (erlang-skel-separator 2)
+;; "init([]) ->" n>
+;; "{ok, #state{}}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: handle_call/3" n
+;; "%% Returns: {reply, Reply, State} |" n
+;; "%% {reply, Reply, State, Timeout} |" n
+;; "%% {noreply, State} |" n
+;; "%% {noreply, State, Timeout} |" n
+;; "%% {stop, Reason, Reply, State} | (terminate/2 is called)" n
+;; "%% {stop, Reason, State} (terminate/2 is called)" n
+;; (erlang-skel-separator 2)
+;; "handle_call(Request, From, State) ->" n>
+;; "Reply = ok," n>
+;; "{reply, Reply, State}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: handle_cast/2" n
+;; "%% Returns: {noreply, State} |" n
+;; "%% {noreply, State, Timeout} |" n
+;; "%% {stop, Reason, State} (terminate/2 is called)" n
+;; (erlang-skel-separator 2)
+;; "handle_cast(Msg, State) ->" n>
+;; "{noreply, State}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: handle_info/2" n
+;; "%% Returns: {noreply, State} |" n
+;; "%% {noreply, State, Timeout} |" n
+;; "%% {stop, Reason, State} (terminate/2 is called)" n
+;; (erlang-skel-separator 2)
+;; "handle_info(Info, State) ->" n>
+;; "{noreply, State}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: terminate/2" n
+;; "%% Purpose: Shutdown the server" n
+;; "%% Returns: any (ignored by gen_server)" n
+;; (erlang-skel-separator 2)
+;; "terminate(Reason, State) ->" n>
+;; "ok." n
+;; n
+;; (erlang-skel-separator)
+;; "%%% Internal functions" n
+;; (erlang-skel-separator)
+;; )
+;; "*The template of a generic server.
+;;Please see the function `tempo-define-template'.")
+;;
+;;(defvar erlang-skel-generic-server-api
+;; '((erlang-skel-include erlang-skel-large-header)
+;; "%% External exports" n
+;; "-export([start_link/0])." n
+;; n
+;; (erlang-skel-separator)
+;; "%%% API" n
+;; (erlang-skel-separator)
+;; "start_link() ->" n>
+;; "gen_server:start_link({local, "
+;; (erlang-add-quotes-if-needed
+;; (concat (erlang-get-module-from-file-name) "_server")) "}, "
+;; (erlang-add-quotes-if-needed
+;; (concat (erlang-get-module-from-file-name) "_server")) ", [], [])." n
+;; n
+;; ))
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;;
+;;
+;;(defvar erlang-skel-gen-event-no-api
+;; '((erlang-skel-include erlang-skel-large-header)
+;; "-behaviour(gen_event)." n
+;; n
+;; "%% gen_event callbacks" n
+;; "-export([init/1, handle_event/2, handle_call/2, "
+;; "handle_info/2, terminate/2])." n n
+;; "-record(state, {})." n
+;; n
+;; (erlang-skel-separator)
+;; "%%% Callback functions from gen_event" n
+;; (erlang-skel-separator)
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: init/1" n
+;; "%% Returns: {ok, State} |" n
+;; "%% Other" n
+;; (erlang-skel-separator 2)
+;; "init([]) ->" n>
+;; "{ok, #state{}}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: handle_event/2" n
+;; "%% Returns: {ok, State} |" n
+;; "%% {swap_handler, Args1, State1, Mod2, Args2} |" n
+;; "%% remove_handler " n
+;; (erlang-skel-separator 2)
+;; "handle_event(Event, State) ->" n>
+;; "{ok, State}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: handle_call/2" n
+;; "%% Returns: {ok, Reply, State} |" n
+;; "%% {swap_handler, Reply, Args1, State1, Mod2, Args2} |" n
+;; "%% {remove_handler, Reply} " n
+;; (erlang-skel-separator 2)
+;; "handle_call(Request, State) ->" n>
+;; "Reply = ok," n>
+;; "{ok, Reply, State}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: handle_info/2" n
+;; "%% Returns: {ok, State} |" n
+;; "%% {swap_handler, Args1, State1, Mod2, Args2} |" n
+;; "%% remove_handler " n
+;; (erlang-skel-separator 2)
+;; "handle_info(Info, State) ->" n>
+;; "{ok, State}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: terminate/2" n
+;; "%% Purpose: Shutdown the server" n
+;; "%% Returns: any" n
+;; (erlang-skel-separator 2)
+;; "terminate(Reason, State) ->" n>
+;; "ok." n
+;; n
+;; (erlang-skel-separator)
+;; "%%% Internal functions" n
+;; (erlang-skel-separator)
+;; )
+;; "*The template of a gen_event.
+;;Please see the function `tempo-define-template'.")
+;;
+;;(defvar erlang-skel-gen-event-api
+;; '((erlang-skel-include erlang-skel-large-header)
+;; "%% External exports" n
+;; "-export([start_link/0, add_handler/0])." n
+;; n
+;; (erlang-skel-separator)
+;; "%%% API" n
+;; (erlang-skel-separator)
+;; "start_link() ->" n>
+;; "gen_event:start_link({local, "
+;; (erlang-add-quotes-if-needed
+;; (concat (erlang-get-module-from-file-name) "_event")) "}). " n
+;; n
+;; "add_handler() ->" n>
+;; "gen_event:add_handler("
+;; (erlang-add-quotes-if-needed
+;; (concat (erlang-get-module-from-file-name) "_event")) ", "
+;; (erlang-add-quotes-if-needed
+;; (concat (erlang-get-module-from-file-name) "_event")) ", [])." n
+;; n))
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;;
+;;
+;;(defvar erlang-skel-gen-fsm
+;; '((erlang-skel-include erlang-skel-large-header)
+;; "-behaviour(gen_fsm)." n
+;; n
+;; "%% gen_fsm callbacks" n
+;; "-export([init/1, state_name/2, state_name/3, handle_event/3," n>
+;; "handle_sync_event/4, handle_info/3, terminate/3])." n n
+;; "-record(state, {})." n
+;; n
+;; (erlang-skel-separator)
+;; "%%% Callback functions from gen_fsm" n
+;; (erlang-skel-separator)
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: init/1" n
+;; "%% Returns: {ok, StateName, StateData} |" n
+;; "%% {ok, StateName, StateData, Timeout} |" n
+;; "%% ignore |" n
+;; "%% {stop, StopReason} " n
+;; (erlang-skel-separator 2)
+;; "init([]) ->" n>
+;; "{ok, state_name, #state{}}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: StateName/2" n
+;; "%% Returns: {next_state, NextStateName, NextStateData} |" n
+;; "%% {next_state, NextStateName, NextStateData, Timeout} |" n
+;; "%% {stop, Reason, NewStateData} " n
+;; (erlang-skel-separator 2)
+;; "state_name(Event, StateData) ->" n>
+;; "{nextstate, state_name, StateData}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: StateName/3" n
+;; "%% Returns: {next_state, NextStateName, NextStateData} |" n
+;; "%% {next_state, NextStateName, NextStateData, Timeout} |" n
+;; "%% {reply, Reply, NextStateName, NextStateData} |" n
+;; "%% {reply, Reply, NextStateName, NextStateData, Timeout} |" n
+;; "%% {stop, Reason, NewStateData} |" n
+;; "%% {stop, Reason, Reply, NewStateData} " n
+;; (erlang-skel-separator 2)
+;; "state_name(Event, From, StateData) ->" n>
+;; "Reply = ok," n>
+;; "{reply, Reply, state_name, StateData}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: handle_event/3" n
+;; "%% Returns: {next_state, NextStateName, NextStateData} |" n
+;; "%% {next_state, NextStateName, NextStateData, Timeout} |" n
+;; "%% {stop, Reason, NewStateData} " n
+;; (erlang-skel-separator 2)
+;; "handle_event(Event, StateName, StateData) ->" n>
+;; "{nextstate, StateName, StateData}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: handle_sync_event/4" n
+;; "%% Returns: {next_state, NextStateName, NextStateData} |" n
+;; "%% {next_state, NextStateName, NextStateData, Timeout} |" n
+;; "%% {reply, Reply, NextStateName, NextStateData} |" n
+;; "%% {reply, Reply, NextStateName, NextStateData, Timeout} |" n
+;; "%% {stop, Reason, NewStateData} |" n
+;; "%% {stop, Reason, Reply, NewStateData} " n
+;; (erlang-skel-separator 2)
+;; "handle_sync_event(Event, From, StateName, StateData) ->" n>
+;; "Reply = ok," n>
+;; "{reply, Reply, StateName, StateData}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: handle_info/3" n
+;; "%% Returns: {next_state, NextStateName, NextStateData} |" n
+;; "%% {next_state, NextStateName, NextStateData, Timeout} |" n
+;; "%% {stop, Reason, NewStateData} " n
+;; (erlang-skel-separator 2)
+;; "handle_info(Info, StateName, StateData) ->" n>
+;; "{nextstate, StateName, StateData}." n
+;; n
+;; (erlang-skel-separator 2)
+;; "%% Func: terminate/3" n
+;; "%% Purpose: Shutdown the fsm" n
+;; "%% Returns: any" n
+;; (erlang-skel-separator 2)
+;; "terminate(Reason, StateName, StatData) ->" n>
+;; "ok." n
+;; n
+;; (erlang-skel-separator)
+;; "%%% Internal functions" n
+;; (erlang-skel-separator)
+;; )
+;; "*The template of a gen_fsm.
+;;Please see the function `tempo-define-template'.")
+;;
+;;(defvar erlang-skel-gen-fsm-no-api
+;; '((erlang-skel-include erlang-skel-large-header)
+;; "%% External exports" n
+;; "-export([start_link/0])." n
+;; n
+;; (erlang-skel-separator)
+;; "%%% API" n
+;; (erlang-skel-separator)
+;; "start_link() ->" n>
+;; "gen_fsm:start_link({local, "
+;; (erlang-add-quotes-if-needed
+;; (concat (erlang-get-module-from-file-name) "_fsm")) "}, "
+;; (erlang-add-quotes-if-needed
+;; (concat (erlang-get-module-from-file-name) "_fsm")) ", [], [])." n
+;; n
+;; ))
+;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; requires that the variables appwiz-erlang-modulename and
+;; appwiz-erlang-ext are defined.
+;;
+
+(defvar erlang-skel-makefile-src
+ '((erlang-skel-include erlang-skel-nomodule-header)
+ "MAKE = make" n
+ n
+ "ERL = erlc" n
+ n
+ "EBIN = ../ebin" n
+ n
+ (erlang-skel-makefile-separator)
+ n
+ (upcase appwiz-erlang-modulename) "_HEADER_FILES = "
+ appwiz-erlang-modulename "_sup.hrl" n
+ n
+ (upcase appwiz-erlang-modulename) "_SOURCE_FILES = \\" n
+ " " appwiz-erlang-modulename ".erl" " "
+ appwiz-erlang-modulename "_sup.erl \\" n
+ " " appwiz-erlang-modulename "_app.erl" " "
+ appwiz-erlang-modulename appwiz-erlang-ext ".erl" n
+ n
+ (upcase appwiz-erlang-modulename) "_OBJECT_FILES = $("
+ (upcase appwiz-erlang-modulename) "_SOURCE_FILES:.erl=.jam)" n
+ n
+ n
+ (erlang-skel-makefile-separator)
+ "#" n
+ "# Transformations " n
+ "#" n
+ n
+ ".erl.jam:" n
+ " $(ERL) $<" n
+ n
+ (erlang-skel-makefile-separator) n
+ n
+ n
+ "def : "
+ appwiz-erlang-modulename n
+ n
+ appwiz-erlang-modulename ": $("
+ (upcase appwiz-erlang-modulename) "_OBJECT_FILES)" n
+ " cp $(" (upcase appwiz-erlang-modulename) "_OBJECT_FILES) "
+ "$(EBIN)" n
+ n
+ "clean :" n
+ " /bin/rm -f $(" (upcase appwiz-erlang-modulename)
+ "_OBJECT_FILES)" n
+ n
+ "$(" (upcase appwiz-erlang-modulename) "_OBJECT_FILES): $("
+ (upcase appwiz-erlang-modulename) "_HEADER_FILES)" n
+ n
+ ".SUFFIXES : .erl .jam" n
+ n
+ ))
+
+(defvar erlang-skel-makefile-middle
+ '((erlang-skel-include erlang-skel-nomodule-header)
+ "MAKE = make" n
+ n
+ (erlang-skel-makefile-separator)
+ n
+ "def:" n
+ " (cd src ; $(MAKE))" n
+ n
+ "clean:" n
+ " (cd src ; $(MAKE) clean)" n
+ n
+ ))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun erlang-skel-makefile-separator ()
+ "Return a comment separator."
+ (concat (make-string 70 ?\#) "\n"))
diff --git a/lib/tools/emacs/internal_doc/emacs.sgml b/lib/tools/emacs/internal_doc/emacs.sgml
new file mode 100644
index 0000000000..5b28928605
--- /dev/null
+++ b/lib/tools/emacs/internal_doc/emacs.sgml
@@ -0,0 +1,3258 @@
+<!DOCTYPE CHAPTER PUBLIC "-//Stork//DTD chapter//EN">
+<!--
+ ``The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved via the world wide web at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+ Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+ AB. All Rights Reserved.''
+
+ $Id$
+-->
+<CHAPTER><HEADER>
+<TITLE> The Erlang editing mode for Emacs </TITLE>
+
+<PREPARED>Anders Lindgren
+ <RESPONSIBLE>
+ <DOCNO>
+ <APPROVED>
+ <CHECKED>
+ <DATE>1998-04-20
+ <REV>C
+ <FILE>emacs-user.sgml</HEADER>
+
+<SECTION>
+<TITLE> Introduction </TITLE>
+
+
+<p>
+If you want to get started immediately, the chapters
+"<SEEALSO MARKER="#unix_dotemacs">An Example for UNIX</SEEALSO>"
+and
+"<SEEALSO MARKER="#win_dotemacs">An Example for Windows</SEEALSO>"
+gives you examples of the configurations you need to make to use the
+Erlang Editing mode for Emacs.
+</P>
+
+
+<P>
+Emacs has been the text editor of choice for programmers in the UNIX
+community for many years. Thanks to a continuing development process,
+Emacs is the most powerful editor available. Today, Emacs runs under
+most operating systems including MS-Windows, OS/2, Macintosh, and
+several dialects of UNIX.
+</P>
+
+<P>
+Emacs has editing support for all major programming languages and
+quite a lot of minor and unknown languages are supported as well.
+</P>
+
+<P>
+Emacs is designed to be extendible. In the unlikely event that you
+would miss a feature in Emacs you can add it yourself, or you might
+find it in the large number of add-on packages that people all over
+the world have written.
+</P>
+
+<P>
+This book is the documentation to the Emacs package <C> erlang.el</C>.
+It provides support for the programming language Erlang. The package
+provides an editing mode with lots of bells and whistles, compilation
+support, and it makes it possible for the user to start Erlang shells
+that run inside Emacs.
+</P>
+
+<P>
+Emacs is written by the Free Software Foundation and is part of the
+GNU project. Emacs, including the source code and documentation, is
+released under the GNU General Public License.
+</P>
+
+<SECTION>
+
+<TITLE>Overview of this Book</TITLE>
+
+
+<P>This book can be divided into the following sections:
+
+<LIST>
+<ITEM><EM> Introduction. </EM> This part introduces Emacs, the Erlang
+editing mode, and this book. In fact, this is the section you
+currently are reading.
+
+<ITEM><EM> The editing mode. </EM> Here the editing mode is described.
+The editing mode contains a whole series of features including
+indentation, syntax highlighting, electric commands, module name
+verification, comment support including paragraph filling, skeletons,
+tags support, and much more.
+
+<ITEM><EM> Erlang shells. </EM> How to start and use an Erlang shell
+that runs inside Emacs is described in this section.
+
+<ITEM><EM> Compilation support. </EM> This package is capable of
+starting compilations of Erlang module. Should compilation errors
+occur Emacs is capable of placing the cursor on the erroneous lines.
+
+<ITEM><EM> Customization. </EM> The Erlang editing mode, like most
+Emacs packages, supports extensive customization. In this chapter we
+demonstrate how you can bind your favorite functions to the hotkeys
+on the keyboard. It also introduces the concept of "hooks", a general
+method for the user to add code that will be executed when a specific
+situation occur, for example when an Erlang file is loaded into Emacs.
+
+</LIST>
+
+<P>
+The terminology used in this book is the terminology used in the
+documentation to Emacs. The chapter "<SEEALSO
+MARKER="#notation">Notation</SEEALSO>" contains a list of commonly
+used words and their meaning in the Emacs world.
+</P>
+
+<P>
+The intended readers of this book are Emacs users. The book contains
+some examples on how to customize this package using the Emacs
+extension language Emacs Lisp. You can safely skip those sections.
+</P>
+
+</SECTION>
+</SECTION>
+
+<SECTION>
+<TITLE>Emacs</TITLE>
+
+<P>
+The first component needed to get this package up and running is, of
+course, an Emacs editor. You can use either the standard Emacs
+distribution from FSF or XEmacs, an alternative distribution. Both
+brands have their advantages and disadvantages.
+</P>
+
+<P>
+Regardless of the brand, it is recommended to use a modern version.
+If an old version is used it is possible that some of the features
+provided by the editing mode cannot be used.
+</P>
+
+<P>
+The chapter "<SEEALSO MARKER="#distributions">Emacs
+Distributions</SEEALSO>" below contains a short summary on the
+differences between the Emacs brands, as well as instructions where to
+get the distributions and how to install them.
+</P>
+
+</SECTION>
+
+<SECTION>
+<TITLE>Installing the Erlang Support Packages</TITLE>
+
+<P>
+Once Emacs has been installed, it must be informed about the presence
+of the Erlang support packages.
+</P>
+
+<P>
+If you do not know if the packages have been installed open, an Erlang
+source file. The mode line should contain the word "Erlang". You can
+check the version of the installed package by selecting the "version"
+entry in the Erlang menu in Emacs. Should no Erlang menu be present,
+or if the menu does not contain a "Version" item, you are using an old
+version.
+</P>
+
+<P>
+The packages can either be installed for all users by the system
+administrator, or each individual user can install it in their own
+Emacs setup. The chapter "<SEEALSO
+MARKER="#installation">Installation of the Erlang Editing Mode</SEEALSO>"
+ contains a description
+on how to install the packages.
+</P>
+
+</SECTION>
+
+
+<SECTION>
+<TITLE> The Editing Mode </TITLE>
+
+<P>
+The Erlang editing for Emacs provides a number of features described
+in this and the following chapters. The editing mode can work with
+either Erlang source mode or Mnesia database rules. The Erlang
+editing mode for Emacs is in Emacs terminology a <EM> Major mode </EM>.
+</P>
+
+<P>
+When Erlang mode is correctly installed, it is automatically activated
+when a file ending in <C>.erl</C> or <C>.hrl</C> is opened in Emacs.
+It is possible to activate Erlang mode for other buffers as well.
+</P>
+
+<P>
+The editing mode provides a menu containing a selection of commands
+structured into logical subgroups. The menu is designed to help new
+users get an overview of the features provided by the Erlang packages
+while still giving full power to more advanced users.
+</P>
+
+<P>
+Erlang mode has got a local key map that contains keyboard bindings
+for a number of commands. In the chapter
+"<SEEALSO MARKER="#key_bindings">Custom Key Bindings</SEEALSO>" below,
+we will demonstrate how the users can bind their favorite commands to
+the local Erlang key map.
+</P>
+
+<P>
+It is possible for the users to perform advanced customizations by
+adding their own functions to the "hook" variables provided by this
+package. This will be described in the "<SEEALSO
+MARKER="#customization">Customization</SEEALSO>" chapter below.
+</P>
+
+
+<SECTION>
+<TITLE>The Mode</TITLE>
+
+<LIST>
+<ITEM><C>M-x erlang-mode RET</C><BR>
+
+<P>
+This command activates the Erlang major mode for the current buffer.
+When this mode is active the mode line contain the word "Erlang".
+</P>
+
+</LIST>
+</SECTION>
+
+<SECTION>
+<TITLE>The Version</TITLE>
+
+<LIST>
+<ITEM><C>M-x erlang-version RET</C><BR>
+
+<P>
+This command displays the version number of the Erlang editing mode.
+Remember to always supply the version number when asking questions
+about Erlang mode.
+</P>
+
+<P>
+Should this command not be present in your setup (after Erlang mode
+has been activated) you probably have a very old version of the Erlang
+editing mode.
+</P>
+
+</LIST>
+</SECTION>
+
+<SECTION>
+<TITLE>Module Name Check</TITLE>
+
+<P>
+When a file is saved the name in the <C>-module().</C> line is checked
+against the file name. Should they mismatch Emacs can change the
+module specifier so that it matches the file name. By default, the user
+is asked before the change is performed.
+</P>
+
+
+<LIST>
+<ITEM> <EM> Variable: </EM> <C>erlang-check-module-name</C> (default <C>ask</C>)<BR>
+
+<P>
+This variable controls the behavior of the module name check system.
+When it is <C>t</C> Emacs changes the module specifier without asking
+the user, when it is bound to the atom <C>ask</C> the user is asked.
+Should it be <C>nil</C> the module name check mechanism is
+deactivated.
+</P>
+
+</LIST>
+</SECTION>
+
+<SECTION>
+<TITLE>Variables</TITLE>
+
+<P>
+There are several variables that control the behavior of the
+Erlang Editing mode.
+</P>
+
+<LIST>
+ <ITEM><EM> Variable: </EM> <C>erlang-mode-hook</C><BR>
+
+<P>
+Functions to run when the Erlang mode is activated. See chapter
+"<SEEALSO MARKER="#customization">Customization</SEEALSO>" below for
+examples.
+</P>
+
+
+ <ITEM><EM> Variable: </EM> <C>erlang-new-file-hook</C><BR>
+
+<P>
+Functions to run when a new file is created. See chapter "<SEEALSO
+MARKER="#customization">Customization</SEEALSO>" below for examples.
+</P>
+
+
+ <ITEM><EM> Variable: </EM> <C>erlang-mode-load-hook</C><BR>
+
+<P>
+Functions to run when the <C>erlang</C> package is loaded into Emacs.
+See chapter "<SEEALSO MARKER="#customization">Customization</SEEALSO>"
+below for examples.
+</P>
+
+</LIST>
+
+</SECTION>
+</SECTION>
+
+<!-- Chapter -->
+
+<SECTION>
+<TITLE>Indentation</TITLE>
+
+<P>
+The "Oxford Advanced Learners Dictionary of Current English" says the
+following about the word "indent":
+</P>
+
+<QUOTE>
+<P>
+ "start (a line of print or writing) farther from
+ the margin than the others".
+</P>
+</QUOTE>
+
+<P>
+Possibly the most important feature of an editor designed for
+programmers is the ability to indent a line of code in accordance
+with the structure of the programming language.
+</P>
+
+<P>
+The Erlang mode does, of course, provide this feature. The layout
+used is based on the common use of the language.
+</P>
+
+<P>
+It is strongly recommend to use this feature and avoid to indent lines
+in a nonstandard way. Some motivations are:
+</P>
+
+<LIST>
+
+ <ITEM> Code using the same layout is easy to read and maintain.
+
+ <ITEM> The indentation features can be used to reindent large sections of a
+file. If some lines use nonstandard indentation they will be
+reindented.
+
+ <ITEM> Since several features of Erlang mode is based on the
+standard layout they might not work correctly if a nonstandard layout
+is used. For example, the movement commands (described in chapter
+"<SEEALSO MARKER="#func_cmds">Function and clause commands</SEEALSO>"
+below) will not work unless the function headers start in the first
+column.
+
+</LIST>
+
+<SECTION>
+<TITLE>The Layout</TITLE>
+
+<P>
+The basic layout is that the clause headers start in the first column,
+and the bodies of clauses and complex expressions (e.g. "case" and
+"if") are indented more that the surrounding code. For example:
+</P>
+
+<CODE>
+remove_bugs([]) ->
+ [];
+remove_bugs([X | Xs])
+ case X of
+ bug ->
+ test(Xs);
+ _ ->
+ [X | test(Xs)]
+ end.
+</CODE>
+
+
+<LIST>
+
+<ITEM> <EM> Variable: </EM> <C>erlang-indent-level</C><BR>
+
+<P>
+The depth of the indentation is controlled by the variable
+"erlang-indent-level", see section "<SEEALSO
+MARKER="#customization">Customization</SEEALSO>" below.
+</P>
+
+</LIST>
+
+</SECTION>
+
+<SECTION>
+<TITLE>Indentation of comments</TITLE>
+
+<P>
+Lines containing comment are indented differently depending on the
+number of %-characters used:
+</P>
+
+<LIST>
+<ITEM> Lines with one %-character is indented to the right of the
+code. The column is specified by the variable <C>comment-column</C>,
+by default column 48 is used.
+
+<ITEM> Lines with two %-characters will be indented to the same depth
+as code would have been in the same situation.
+
+<ITEM> Lines with three of more %-characters are indented to the left
+margin.
+
+</LIST>
+
+<P>
+<EM> Example: </EM>
+</P>
+
+<CODE>
+%%%
+%%% Function: remove_bugs
+%%%
+
+remove_bugs([]) ->
+ [];
+remove_bugs([X | Xs])
+ case X of
+ bug -> % Oh no, a bug!
+ % Remove it.
+ test(Xs);
+ %% This element is not a bug, let's keep it.
+ _ ->
+ [X | test(Xs)]
+ end.
+</CODE>
+</SECTION>
+
+<SECTION>
+
+<TITLE>Indentation commands</TITLE>
+
+<P>The following command are directly available for indentation.</P>
+
+<LIST>
+<ITEM><C>TAB</C> (<C>erlang-indent-command</C>)<BR>
+
+<P>Indent the current line of code.</P>
+
+
+<ITEM><C>M-C-\</C> (<C>indent-region</C>)<BR>
+
+<P>Indent all lines in the region.</P>
+
+
+<ITEM><C>M-l</C> (<C>indent-for-comment</C>)<BR>
+
+<P>
+Insert a comment character to the right of the code on the line (if
+any). The comment character is placed in the column specified by the
+variable "comment-column", by default column 48 is used.
+</P>
+
+
+<ITEM><C>C-c C-q</C> (<C>erlang-indent-function</C>)<BR>
+
+<P>
+Indent the current Erlang function.
+</P>
+
+
+<ITEM><C> M-x erlang-indent-clause RET</C><BR>
+
+<P>
+Indent the current Erlang clause.</P>
+
+
+<ITEM><C>M-x erlang-indent-current-buffer RET</C><BR>
+
+<P>
+Indent the entire buffer.
+</P>
+
+</LIST>
+
+</SECTION>
+<SECTION>
+<MARKER ID="customization">
+<TITLE>Customization</TITLE>
+
+<P>
+The most common customization of the indentation system is to bind the
+return key to <C>newline-and-indent</C>. Please see the chapter
+"<SEEALSO MARKER="#key_bindings">Custom Key Bindings</SEEALSO>"
+below for an example.
+</P>
+
+<P>
+There are several Emacs variables that control the indentation system.
+</P>
+
+<LIST>
+
+<ITEM><EM> Variable: </EM> <C>erlang-indent-level</C> (default 4)<BR>
+
+<P>
+The amount of indentation for normal Erlang functions and complex
+expressions. Should, for example, the value of this variable be 2 the
+example above would be indented like:
+</P>
+
+<CODE>
+remove_bugs([]) ->
+ [];
+remove_bugs([X | Xs])
+ case X of
+ bug ->
+ test(Xs);
+ _ ->
+ [X | test(Xs)]
+ end.
+</CODE>
+
+
+<ITEM><EM> Variable: </EM> <C>erlang-indent-guard</C> (default 2)<BR>
+
+<P>The amount of indentation for Erlang guards.</P>
+
+
+<ITEM><EM> Variable: </EM> <C>erlang-argument-indent</C> (default 2)<BR>
+
+<P>The amount of indentation for function calls that span several lines.</P>
+
+<P>
+<EM> Example: </EM>
+</P>
+
+<CODE>
+foo() ->
+ a_very_long_function_name(
+ AVeryLongVariableName),
+</CODE>
+
+
+<ITEM><EM> Variable: </EM> <C>erlang-tab-always-indent</C>
+(default <C>t</C>)<BR>
+
+<P>
+When non-<C>nil</C> the <C>TAB</C> command always indents the line
+(this is the default). When <C>nil</C>, the line will be indented
+only when the point is in the beginning of any text on the line,
+otherwise it will insert a tab character into the buffer.
+</P>
+
+</LIST>
+
+</SECTION>
+</SECTION>
+
+
+<!-- CHAPTER -->
+
+<SECTION>
+
+<TITLE> General Commands </TITLE>
+
+<P>
+This chapter contains a group of commands that are not found in any
+other category. Unlike most other books we do not have a chapter named
+"Miscellaneous xxx" found at the end of most books. This chapter is
+placed near the beginning to reflect the importance and usefulness of
+the commands.
+</P>
+
+<SECTION>
+
+<TITLE>Filling comments</TITLE>
+
+<P>
+How many times have you edited a section of text in a comment only to
+wind up with a unevenly formatted paragraph? Or even worse, have you
+ever decided not to edit a comment just because the formatting would
+look bad?
+</P>
+
+<P>
+When editing normal text in text mode you can let Emacs reformat the
+text by the <C>fill-paragraph</C> command. This command will not work
+for comments since it will treat the comment characters as words.
+</P>
+
+<P>
+The Erlang editing mode provides a command that known about the Erlang
+comment structure and can be used to fill text paragraphs in comments.
+</P>
+
+
+<LIST>
+<ITEM><C>M-q</C> (<C>erlang-fill-paragraph</C>)<BR>
+
+Fill the text in an Erlang comment. This command known about the
+Erlang comment characters. The column to perform the word wrap is
+defined by the variable <C>fill-column</C>.
+
+</LIST>
+
+<P>
+<EM> Example: </EM>
+</P>
+
+<P>
+For the sake of this example, let's assume that <C>fill-column</C> is set
+to column 30. Assume that we have an Erlang comment paragraph on the
+following form:
+</P>
+
+<CODE>
+%% This is just a test to show
+%% how the Erlang fill
+%% paragraph command works.
+</CODE>
+
+<P>
+Assume that you would add the words "very simple" before the word
+"test":
+</P>
+
+<CODE>
+%% This is just a very simple test to show
+%% how the Erlang fill
+%% paragraph command works.
+</CODE>
+
+<P>
+Clearly, the text is badly formatted. Instead of formatting this
+paragraph line by line, let's try <C>erlang-fill-paragraph</C> by
+pressing <C>M-q</C>. The result is:
+</P>
+
+<CODE>
+%% This is just a very simple
+%% test to show how the Erlang
+%% fill paragraph command
+%% works.
+</CODE>
+
+<P>
+As you can see the paragraph is now evenly formatted.
+</P>
+
+</SECTION>
+
+<SECTION>
+<TITLE> Creating Comments </TITLE>
+
+<P>
+In Erlang it is possible to write comments to the right of the code.
+The indentation system described in the chapter "Indentation" above is
+able to indent lines containing only comments, and gives support for
+end-of-the-line comments.
+</P>
+
+<LIST>
+
+<ITEM><C>M-;</C> (<C>indent-for-comment</C>)<BR>
+
+This command will create, or reindent, a comment to the right of the
+code. The variable <C>comment-column</C> controls the placement of the
+comment character.
+
+</LIST>
+</SECTION>
+
+<SECTION>
+
+<TITLE> Comment Region </TITLE>
+
+<P>
+The standard command <C>comment-region</C> can be used to comment out
+all lines in a region. To uncomment the lines in a region precede
+this command with <C>C-u</C>.
+</P>
+
+</SECTION>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+<TITLE>Syntax Highlighting</TITLE>
+
+<P>
+It is possible for Emacs to use colors when displaying a buffer. By
+"syntax highlighting", we mean that syntactic components, for example
+keywords and function names, will be colored.
+</P>
+
+<P>
+The basic idea of syntax highlighting is to make the structure of a
+program clearer. For example, the highlighting will make it easier to
+spot simple bugs. Have not you ever written a variable in lower-case
+only? With syntax highlighting a variable will colored while atoms
+will be shown with the normal text color.
+</P>
+
+<P>
+The syntax highlighting can be activated from the Erlang menu. There
+are four different alternatives:
+</P>
+
+<LIST>
+
+<ITEM> Off: Normal black and white display.
+
+<ITEM> Level 1: Function headers, reserved words, comments, strings, quoted
+atoms, and character constants will be colored.
+
+<ITEM> Level 2: The above, attributes, Erlang bif:s, guards, and words
+in comments enclosed in single quotes will be colored.
+
+<ITEM> Level 3: The above, variables, records, and macros will be colored.
+(This level is also known as the Christmas tree level.)
+
+</LIST>
+
+
+<P>
+The syntax highlighting is based on the standard Emacs package
+"font-lock". It is possible to use the font-lock commands and
+variables to enable syntax highlighting. The commands in question
+are:
+</P>
+
+<LIST>
+<ITEM><C>M-x font-lock-mode RET</C><BR>
+
+<P>
+This command activates syntax highlighting for the current buffer.
+</P>
+
+
+<ITEM><C>M-x global-font-lock-mode RET</C><BR>
+
+<P>
+Activate syntax highlighting for all buffers.
+</P>
+
+</LIST>
+
+<P>
+The variable <C>font-lock-maximum-decoration</C> is used to specify
+the level of highlighting. If the variable is bound to an integer,
+that level is used; if it is bound to <C>t</C> the highest possible
+level is used. (It is possible to set different levels for different
+editing modes; please see the font-lock documentation for more
+information.)
+</P>
+
+<P>
+It is possible to change the color used. It is even possible to use
+bold, underlined, and italic fonts in combination with colors.
+However, the method to do this differs between Emacs and XEmacs; and
+between different versions of Emacs. For Emacs 19.34, the variable
+<C>font-lock-face-attributes</C> controls the colors. For version 20 of
+Emacs and XEmacs, the faces can be defined in the interactive custom
+system.
+</P>
+
+<SECTION>
+<MARKER ID="font-lock">
+<TITLE>Customization</TITLE>
+
+<P>
+Font-lock mode is activated in different ways in different versions of
+Emacs. For modern versions of GNU Emacs place the following lines in
+your <C>~/.emacs</C> file:
+</P>
+
+<CODE>
+(setq font-lock-maximum-decoration t)
+(global-font-lock-mode 1)
+</CODE>
+
+<!-- TODO: Check this -->
+<P>
+For modern versions of XEmacs the following code can be used:
+</P>
+
+<CODE>
+(setq auto-font-lock-mode 1)
+</CODE>
+
+<P>
+For older versions of Emacs and XEmacs, font-lock mode must be
+activated individually for each buffer. The following will add a
+function to the Erlang mode hook that activates font-lock mode for all
+Erlang buffers.
+</P>
+
+<CODE>
+(defun my-erlang-font-lock-hook ()
+ (font-lock-mode 1))
+
+(add-hook 'erlang-mode-hook 'my-erlang-font-lock-hook)
+</CODE>
+
+</SECTION>
+
+<SECTION>
+<TITLE>Known Problems</TITLE>
+
+<P>
+Emacs has one problem with the syntactic structure of Erlang, namely
+the <C>$</C> character. The normal Erlang use of the $ character is
+to denote the ASCII value of a character, for example:
+</P>
+
+<CODE>
+ascii_value_of_a() -> $a.
+</CODE>
+
+<P>
+In order to get the font-lock mechanism to work for the next example,
+the $ character must be marked as an "escape" character that changes
+the ordinary Emacs interpretation of the following double-quote
+character.
+</P>
+
+<CODE>
+ascii_value_of_quote() -> $".
+</CODE>
+
+
+<P>
+The problem is that Emacs will also treat the <C>$</C> character as an
+"escape" character at the end of strings and quoted atoms.
+Practically, this means that Emacs will not detect the end of the
+following string:
+</P>
+
+<CODE>
+the_id() -> "$id: $".
+</CODE>
+
+<P>
+Fortunately, there are ways around this. From Erlang's point of view
+the following two strings are equal: <C>"test$"</C> and
+<C>"test\$"</C>. The <C>\</C>-character is also marked as an Emacs "escape"
+character, hence it will change the Emacs interpretation of the
+<C>$</C>-character.
+</P>
+
+<P>
+This work-around cannot always be used. For example, when the string is
+used by an external version control program. In this situation we can
+try to avoid placing the <C>$</C>-character at the end of the string, for
+example:
+</P>
+
+<CODE>
+-vsn(" $Revision: 1.1 $ ").
+</CODE>
+
+<P>
+Should this not be possible we can try to create an artificial end of
+the string by placing an extra quote sign in the file. We do this as a
+comment:
+</P>
+
+<CODE>
+-vsn("$Revision: 1.1 $"). % "
+</CODE>
+
+
+<P>
+The comment will be ignored by Erlang since it is a comment. From
+Emacs point of view the comment character is part of the string.
+</P>
+
+<P>
+This problem is a generic problem for languages with similar syntax.
+For example, the major mode for Perl suffers from the same problem.
+</P>
+
+</SECTION>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+<TITLE>Electric Commands</TITLE>
+
+<P>
+An "electric" command is a character that in addition to just
+inserting the character performs some type of action. For example the
+";" character is typed in a situation where is ends a function clause
+a new function header is generated.
+</P>
+
+<P>
+Since some people find electric commands annoying they can be
+deactivated, see section "<SEEALSO MARKER="#unplug_elec">Unplugging
+the Electric Commands</SEEALSO>" below.
+</P>
+
+<SECTION>
+
+<TITLE>The Commands</TITLE>
+
+<LIST>
+<ITEM><C> ; </C> (<C>erlang-electric-semicolon</C>)<BR>
+
+<P>
+Insert a semicolon. When ending a function or the body of a
+case clause, and the next few lines are empty, the special action will
+be performed. For functions, a new function header will be generated
+and the point will be placed between the parentheses. (See the
+command <C>erlang-clone-arguments</C>.) For other clauses the string
+"<C> -&gt;</C>" will be inserted and the point will be placed in from of
+the arrow.
+</P>
+
+<ITEM><C> , </C> (<C>erlang-electric-comma</C>)<BR>
+
+<P>
+Insert a comma. If the point is at the end of the line
+and the next few lines are empty, a new indented line is created.
+</P>
+
+<ITEM><C> > </C> (<C>erlang-electric-arrow</C>)<BR>
+
+<P>
+Insert a <C>></C> character. If it is inserted at the end of a line
+after a <C>-</C> character so that an arrow "<C>-></C>" is being
+formed, a new indented line is created. This requires that the next
+few lines are empty.
+
+<ITEM><C> RET </C> (<C>erlang-electric-newline</C>)<BR>
+
+<P>
+The special action of this command is normally off by default. When
+bound to the return key the following line will be indented. Should
+the current line contain a comment the initial comment characters will
+be copied to the new line. For example, assume that the point is at
+the end of a line (denoted by "<C>&lt;point&gt</C>" below).
+</P>
+
+<CODE>
+ %% A comment<point>
+</CODE>
+
+<P>
+When pressing return (and <C>erlang-electric-newline</C> is active)
+the result will be:
+</P>
+
+<CODE>
+ %% A comment
+ %% <point>
+</CODE>
+
+<P>
+This command has a second feature. When issued directly after another
+electric command that created a new line this command does nothing.
+The motivation is that it is in the fingers of many programmers to hit
+the return key just when they have, for example, finished a function
+clause with the <C>;</C> character. Without this feature both the
+electric semicolon and this command would insert one line each which
+is probably not what the user wants.
+</P>
+
+</LIST>
+
+</SECTION>
+
+<SECTION>
+<TITLE> Undo </TITLE>
+
+<P>
+All electric command will set an undo marker after the initial
+character has been inserted but before the special action has been
+performed. By executing the undo command (<C>C-x u</C>) the effect of
+the special action will be undone while leaving the character.
+Execute undo a second time to remove the character itself.
+</P>
+
+</SECTION>
+
+<SECTION>
+<TITLE> Variables </TITLE>
+
+<P>
+The electric commands are controlled by a number of variables.
+</P>
+
+<LIST>
+ <ITEM><C>erlang-electric-commands</C><BR>
+
+<P>
+This variable controls if an electric command is active or not. This
+variable should contain a list of electric commands to be active. To
+activate all electric commands bind this variable to the atom
+<C>t</C>.
+</P>
+
+
+ <ITEM><C>erlang-electric-newline-inhibit</C><BR>
+
+<P>
+When non-<C>nil</C> when <C>erlang-electric-newline</C> should do
+nothing when preceded by a electric command that is member of the
+list <C>erlang-electric-newline-inhibit-list</C>.
+</P>
+
+
+ <ITEM><C>erlang-electric-newline-inhibit-list</C><BR>
+
+<P>
+A list of electric commands. The command
+<C>erlang-electric-newline</C> will do nothing when preceded by a
+command in this list, and the variable
+<C>erlang-electric-newline-inhibit</C> is non-<C>nil</C>.
+</P>
+
+ <ITEM><C>erlang-electric-X-criteria</C><BR>
+
+<P>
+There is one variable of this form for each electric command. The
+variable is used to decide if the special action of an electric
+command should be used. The variable contains a list of criteria
+functions that are called in the order they appear in the list.
+ </p>
+<p>
+If a criteria function returns the atom <C>stop</C> the special
+action is not performed.
+
+If it returns a non-<C>nil</C> value the action is taken.
+
+If it returns <C>nil</C> the next function in the list is called.
+
+Should no function in the list return
+a non-<C>nil</C> value the special action will not be executed.
+
+Should the list contain the atom <C>t</C> the special action is performed
+(unless a previous function returned the atom <C>stop</C>).
+</P>
+
+
+ <ITEM><C>erlang-next-lines-empty-threshold</C> (default 2)<BR>
+
+<P>
+Should the function <C>erlang-next-lines-empty-p</C> be part of a
+criteria list of an electric command (currently semicolon, comma, and
+arrow), this variable controls the number of blank lines required.
+</P>
+
+</LIST>
+
+</SECTION>
+
+<SECTION>
+<MARKER ID="unplug_elec">
+<TITLE> Unplugging the Electric Commands </TITLE>
+
+<P>
+To disable all electric commands set the variable
+<C>erlang-electric-commands</C> to the empty list. In short, place the
+following line in your <C>~/.emacs</C> file:
+</P>
+
+<CODE>
+(setq erlang-electric-commands '())
+</CODE>
+
+</SECTION>
+
+<SECTION>
+
+<TITLE> Customizing the Electric Commands </TITLE>
+
+<P>
+To activate all electric commands, including
+<C>erlang-electric-newline</C>, add the following line to your
+<C>~/.emacs</C> file:
+</P>
+
+<CODE>
+(setq erlang-electric-commands t)
+</CODE>
+
+</SECTION>
+</SECTION>
+
+
+<!-- CHAPTER -->
+
+<SECTION>
+<MARKER ID="func_cmds">
+<TITLE> Function and Clause Commands </TITLE>
+
+<P>
+The Erlang editing mode has a set of commands that are aware of the
+Erlang functions and function clauses. The commands can be used to
+move the point (cursor) to the end of, or to the beginning of Erlang
+functions, or to jump between functions. The region can be placed
+around a function. Function headers can be cloned (copied).
+</P>
+
+
+<SECTION>
+<TITLE> Movement Commands </TITLE>
+
+<P>
+There is a set of commands that can be used to move the point to
+the beginning or the end of an Erlang clause or function. The
+commands are also designed for movement between Erlang functions and
+clauses.
+</P>
+
+<LIST>
+
+ <ITEM><C> C-a M-a </C> (<C>erlang-beginning-of-function</C>)<BR>
+
+<P>
+Move the point to the beginning of the current or preceding Erlang
+function. With an argument skip backwards over this many Erlang
+functions. Should the argument be negative the point is moved to the
+beginning of a function below the current function.
+</P>
+
+<P>
+This function returns <C>t</C> if a function was found, <C>nil</C>
+otherwise.
+</P>
+
+
+ <ITEM><C> M-C-a </C> (<C>erlang-beginning-of-clause</C>)<BR>
+
+<P>
+As above but move point to the beginning of the current or preceding
+Erlang clause.
+</P>
+
+<P>
+This function returns <c>t</c> if a clause was found, <C>nil</C> otherwise.
+</P>
+
+ <ITEM><C> C-a M-e </C> (<C>erlang-end-of-function</C>)<BR>
+
+<P>
+Move to the end of the current or following Erlang function. With an
+argument to it that many times. Should the argument be negative move
+to the end of a function above the current functions.
+</P>
+
+
+ <ITEM><C> M-C-e </C> (<C>erlang-end-of-clause</C>)<BR>
+
+<P>
+As above but move point to the end of the current or following Erlang
+clause.
+</P>
+
+</LIST>
+
+<P>
+When one of the movement commands is executed and the point is already
+placed at the beginning or end of a function or clause, the point is
+moved to the previous/following function or clause.
+</P>
+
+<P>
+When the point is above the first or below the last function in the
+buffer, and an <c>erlang-beginning-of-</c>, or <c>erlang-end-of-</c>
+command is issued, the point is moved to the beginning or to the end
+of the buffer, respectively.
+<P>
+
+
+<SECTION>
+<TITLE> Development Tips </TITLE>
+
+<P>
+The functions described above can be used both as user commands and
+called as functions in programs written in Emacs Lisp.
+</P>
+
+<P>
+<EM> Example: </EM>
+</P>
+
+<P>
+The sequence below will move the point to the beginning of the current
+function even if the point should already be positioned at the
+beginning of the function:
+</P>
+
+<CODE>
+ (end-of-line)
+ (erlang-beginning-of-function)
+</CODE>
+
+
+<P>
+<EM> Example: </EM>
+</P>
+
+<P>
+To repeat over all the function in a buffer the following code can be
+used. It will first move the point to the beginning of the buffer,
+then it will locate the first Erlang function. Should the buffer
+contain no functions at all the call to
+<C>erlang-beginning-of-function</C> will return <C>nil</C> and hence
+the loop will never be entered.
+</P>
+
+<CODE>
+ (goto-char (point-min))
+ (erlang-end-of-function 1)
+ (let ((found-func (erlang-beginning-of-function 1)))
+ (while found-func
+ ;; Do something with this function.
+ ;; Go to the beginning of the next function.
+ (setq found-func (erlang-beginning-of-function -1))))
+</CODE>
+
+</SECTION>
+</SECTION>
+
+<SECTION>
+
+<TITLE>Region Commands</TITLE>
+
+<LIST>
+
+ <ITEM><C> C-c M-h </C> (<C>erlang-mark-function</C>)<BR>
+
+<P>
+Put the region around the current Erlang function. The point is
+placed in the beginning and the mark at the end of the function.
+</P>
+
+ <ITEM><C> M-C-h </C> (<C>erlang-mark-clause</C>)<BR>
+
+<P>
+Put the region around the current Erlang clause. The point is
+placed in the beginning and the mark at the end of the function.
+</P>
+
+</LIST>
+</SECTION>
+
+<SECTION>
+
+<TITLE>Function Header Commands</TITLE>
+
+<LIST>
+ <ITEM><C> C-c C-j </C> (<C>erlang-generate-new-clause</C>)<BR>
+
+<P>
+Create a new clause in the current Erlang function. The point is
+placed between the parentheses of the argument list.
+</P>
+
+ <ITEM><C> C-c C-y </C> (<C>erlang-clone-arguments</C>)<BR>
+
+<P>
+Copy the function arguments of the preceding Erlang clause. This
+command is useful when defining a new clause with almost the same
+argument as the preceding.
+</P>
+
+</LIST>
+
+</SECTION>
+
+<SECTION>
+<TITLE>Limitations</TITLE>
+
+<P>
+Several clauses are considered to be part of the same Erlang function
+if they have the same name. It is possible that in the future the
+arity of the function also will be checked.
+
+To avoid to perform a full parse of the entire buffer the functions
+described in the chapter only look at lines where the function starts
+in the first column. This means that the commands does not work
+properly if the source code contain non-standardized indentation.
+
+</SECTION>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+
+<TITLE>Skeletons</TITLE>
+
+<P>
+A skeleton is a piece of pre-written code that can be inserted into
+the buffer. Erlang mode comes with a set of predefined skeletons
+ranging from simple <C>if</C> expressions to stand-alone applications.
+</P>
+
+<P>
+The skeletons can be accessed either from the Erlang menu of from
+commands named <C>tempo-template-erlang-</C>X.
+</P>
+
+<P>
+The skeletons is defined using the standard Emacs package "tempo". It
+is possible to define new skeletons for your favorite erlang
+constructions.
+</P>
+
+<SECTION>
+
+<TITLE>Commands</TITLE>
+
+<LIST>
+
+ <ITEM><C> C-c M-f </C> (<C>tempo-forward-mark</C>)
+ <ITEM><C> C-c M-b </C> (<C>tempo-backward-mark</C>)
+
+<P>
+In a skeleton certain positions are marked. These two commands
+move the point between such positions.
+</P>
+
+</LIST>
+</SECTION>
+
+<SECTION>
+
+<TITLE>Predefined Skeletons</TITLE>
+
+<LIST>
+
+ <ITEM>Simple skeletons: If, Case, Receive, Receive After, Receive Loop.
+
+ <ITEM>Header elements: Module, Author.
+
+<P>
+These commands inserts lines on the form <C>-module(</C>xxx<C>).</C> and
+<C>-author('my@home').</C>. They can be used directly, but are also used
+as part of the full headers described below:
+</P>
+
+
+ <ITEM>Full Headers: Small, Medium, and Large Headers
+
+<P>
+These commands generate three variants of file headers.
+</P>
+
+</LIST>
+
+<P>
+The following skeletons will complete almost ready-to-run modules.
+
+<LIST>
+
+ <ITEM>Small Server
+
+ <ITEM>application
+
+ <ITEM>Supervisor
+
+ <ITEM>Supervisor Bridge
+
+ <ITEM>gen_server
+
+ <ITEM>gen_event
+
+ <ITEM>gen_fsm
+
+</LIST>
+</SECTION>
+
+<SECTION>
+<TITLE>Defining New Skeletons</TITLE>
+
+<P>
+It is possible to define new Erlang skeletons. The skeletons are
+defined using the standard package "tempo". The skeleton is described
+using the following variables:
+</P>
+
+<LIST>
+
+ <ITEM><C>erlang-skel-</C>X (Where X is the name of this skeleton.)<BR>
+
+<P>
+Each skeleton is described by a variable. It contains a list of Tempo
+rules. See below for two examples of skeleton definitions. See the
+Tempo Reference Manual for a complete description of tempo rules.
+</P>
+
+ <ITEM><C>erlang-skel</C><BR>
+
+<P>
+This variable describes all Erlang skeletons. It is used to define
+the skeletons and to add them to the Erlang menu. The variable is a
+list where is each entry is either the empty list, representing a
+vertical bar in the menu, or a list on the form:
+</P>
+
+<CODE>
+ (Menu-name tempo-name erlang-skel-X)
+</CODE>
+
+<P>
+The Menu-name is name to use in the menu. A named function is created
+for each skeleton, it is <C>tempo-template-erlang-</C>tempo-name.
+Finally, <C>erlang-skel-</C>X is the name of the variable describing the
+skeleton.
+</P>
+
+<P>
+The best time to change this variable is right after the Erlang mode
+has been loaded but before it has been activated. See the "Example"
+section below.
+</P>
+
+</LIST>
+
+<SECTION>
+
+<TITLE>Examples</TITLE>
+
+<P>
+Below is two example on skeletons and one example on how to add an
+entry to the <C>erlang-skel</C> variable. Please see the Tempo
+reference manual for details about the format.
+</P>
+
+
+<P>
+<EM> Example 1: </EM>
+</P>
+
+<P>
+The "If" skeleton is defined by the following variable
+(slightly rearranged for pedagogical reasons):
+</P>
+
+<CODE>
+(defvar erlang-skel-if
+ '((erlang-skel-skip-blank) ;; 1
+ o ;; 2
+ > ;; 3
+ "if" ;; 4
+ n> ;; 5
+ p ;; 6
+ " ->" ;; 7
+ n> ;; 8
+ p ;; 9
+ "ok" ;; 10
+ n> ;; 11
+ "end" ;; 12
+ p)) ;; 13
+</CODE>
+
+<P>
+Each line describes an action to perform:
+</P>
+
+<LIST>
+
+ <ITEM> 1: This is a normal function call. Here we skip over any space
+characters after the point. (If we do not they will end up after the
+skeleton.)
+
+ <ITEM> 2: This means "Open Line", i.e. split the current line at the
+point, but leave the point on the end of the first line.
+
+ <ITEM> 3: Indent Line. This indents the current line.
+
+ <ITEM> 4: Here we insert the string <C>if</C> into the buffer
+
+ <ITEM> 5, 8, 11: Newline and indent.
+
+ <ITEM> 6, 9, 13: Mark these positions as special. The point will be
+placed at the position of the first <C>p</C>. The point can later be
+moved to the other by the <C>tempo-forward-mark</C> and
+<C>tempo-backward-mark</C> described above.
+
+ <ITEM> 7, 10, 12: These insert the strings "<C> -></C>",
+"<C>ok</C>", and "<C>end</C>", respectively.
+
+</LIST>
+
+<P>
+<EM> Example 2: </EM>
+</P>
+
+<P>
+This example contains very few entries. Basically, what it does is to
+include other skeletons in the correct place.
+</P>
+
+<CODE>
+(defvar erlang-skel-small-header
+ '(o ;; 1
+ (erlang-skel-include erlang-skel-module ;; 2
+ erlang-skel-author)
+ n ;; 3
+ (erlang-skel-include erlang-skel-compile ;; 4
+ erlang-skel-export ;; 5
+ erlang-skel-vc))) ;; 6
+</CODE>
+
+<P>
+The lines performs the following actions:
+</P>
+
+<LIST>
+ <ITEM> 1: "Open Line" (see example 1 above).
+
+ <ITEM> 2: Insert the skeletons <C>erlang-skel-module</C> and
+<C>erlang-skel-compile</C> into the buffer.
+
+ <ITEM> 3: Insert one empty line.
+
+ <ITEM> 4: Insert three more skeletons.
+
+</LIST>
+
+<P>
+<EM> Example 3: </EM>
+</P>
+
+<P>
+Here we assume that we have defined a new skeleton named
+<C>erlang-skel-example</C>. The best time to add this skeleton to the
+variable <C>erlang-skel</C> is when Erlang mode has been loaded but
+before it has been activated. We define a function that adds two
+entries to <C>erlang-skel</C>, the first is <C>()</C> that represent a
+divisor in the menu, the second is the entry for the <C>Example</C>
+skeleton. We then add the function to the <C>erlang-load-hook</C>, a
+hook that is called when Erlang mode is loaded into Emacs.
+
+<CODE>
+(defun my-erlang-skel-hook ()
+ (setq erlang-skel
+ (append erlang-skel
+ '(()
+ ("Example" "example" erlang-skel-example)))))
+
+(add-hook 'erlang-load-hook 'my-erlang-skel-hook)
+</CODE>
+
+</SECTION>
+</SECTION>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+
+<TITLE>Manual Pages</TITLE>
+
+<P>
+The UNIX version of Erlang tools contain a set of manual pages that
+can be accessed by the standard UNIX command "man". The Erlang mode
+place a list of all available manual pages in the "Erlang" menu.
+</P>
+
+<P>
+Unfortunately this feature is not available in the Windows version of
+the Erlang editing mode since the Erlang tools are not delivered with
+the manual pages.
+</P>
+
+<SECTION>
+<TITLE> The Menu </TITLE>
+
+<P>
+In the Erlang menu a list of all Erlang manual pages can be found.
+The menu item "Man Pages". The sub-menu to this menu item contains a
+list of categories, normally "Man - Commands" and "Man - Modules".
+Under these is a menu containing the names of the man pages.
+Should this menu be to large it is split alphabetically into a number
+of sub-menus.
+</P>
+
+<P>
+The menu item "Man - Function" is capable of finding the man page of a
+named Erlang function. This commands understands the
+<C>module:function</C> notation. This command defaults to the name under
+the point. Should the name not contain a module name the list of
+imported modules is searched.
+</P>
+
+</SECTION>
+
+<SECTION>
+<TITLE>Customization</TITLE>
+
+<P>
+The following variables control the manual page feature.
+</P>
+
+<LIST>
+
+ <ITEM><C>erlang-man-dirs</C><BR>
+
+<P>
+This variable is a list representing the sub-menu to the "Man Pages"
+menu item in the Erlang menu. Each element is a list with three
+elements. The first is the name of the menu, e.g. "Man - Modules" or
+"Man - Local Stuff". The second is the name of a directory. The
+third is a flag that control the interpretation of the directory.
+When <C>nil</C> the directory is treated as an absolute path, when
+non-<C>nil</C> it is taken as relative to the directory named in the
+variable <C>erlang-root-dir</C>.
+</P>
+
+
+ <ITEM><C>erlang-man-max-menu-size</C><BR>
+
+<P>
+The maximum number of menu items in a manual page menu. If the number
+of manual pages would be more than this variable the menu will be
+split alphabetically into chunks each not larger than the value of
+this variable.
+</P>
+
+</LIST>
+
+</SECTION>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+<TITLE>Tags</TITLE>
+
+<P>
+Tags is a standard Emacs package used to record information about
+source files in large development projects. In addition to listing
+the files of a project, a tags file normally contains information
+about all functions and variables that are defined. By far, the most
+useful command of the tags system is its ability to find the
+definition of functions in any file in the project. However the Tags
+system is not limited to this feature, for example, it is possible to
+do a text search in all files in a project, or to perform a
+project-wide search and replace.
+</P>
+
+<SECTION>
+<TITLE>Creating a TAGS file</TITLE>
+
+<P>
+In order to use the Tags system a file named <C>TAGS</C> must be created.
+The file can be seen as a database over all functions, records, and
+macros in all files in the project. The <C>TAGS</C> file can be created
+using to different methods for Erlang. The first is the standard
+Emacs utility "etags", the second is by using the Erlang module
+<C>tags</C>.
+</P>
+
+</SECTION>
+
+<SECTION>
+<TITLE>The etags utility</TITLE>
+<!-- <TITLE>The <C>etags</C> utility</TITLE> -->
+
+<P>
+The <C>etags</C> is a program that is part of the Emacs distribution. It
+is normally executed from a command line, like a unix shell or a DOS
+box.
+</P>
+
+<P>
+The <C>etags</C> program of fairly modern versions of Emacs and XEmacs
+has native support for Erlang. To check if your version does include
+this support, issue the command <C>etags --help</C> at a the command
+line prompt. At the end of the help text there is a list of supported
+languages. Unless Erlang is a member of this list I suggest that you
+should upgrade to a newer version of Emacs.
+</P>
+
+<P>
+As seen in the help text -- unless you have not upgraded your Emacs yet
+(well, what are you waiting around here for? Off you go and upgrade!)
+-- <C>etags</C> associate the file extensions <C>.erl</C> and
+<C>.hrl</C> with Erlang.
+</P>
+
+<P>
+Basically, the <C>etags</C> utility is runed using the following form:
+</P>
+
+<CODE>
+ etags file1.erl file2.erl
+</CODE>
+
+<P>
+This will create a file named <C>TAGS</C> in the current directory.
+</P>
+
+<P>
+The <C>etags</C> utility can also read a list of files from its standard
+input by supplying a single dash in place of the file names. This
+feature is useful when a project consists of a large number of files.
+The standard UNIX command <C>find</C> can be used to generate the list of
+files, e.g:
+</P>
+
+<CODE>
+ file . -name "*.[he]rl" -print | etags -
+</CODE>
+
+<P>
+The above line will create a <C>TAGS</C> file covering all the Erlang
+source files in the current directory, and in the subdirectories
+below.
+</P>
+
+<P>
+Please see the GNU Emacs Manual and the etags man page for more info.
+</P>
+
+
+<P>
+The code implementing the Erlang support for the <C>etags</C> program has
+been donated to the Free Software Foundation by the company Anders
+Lindgren Development.
+</P>
+
+</SECTION>
+
+<SECTION>
+<TITLE>The tags Erlang module</TITLE>
+<!-- <TITLE>The <C>tags</C> Erlang module</TITLE> -->
+
+<P>
+One of the tools in the Erlang distribution is a module named
+<C>tags</C>. This tool can create a <C>TAGS</C> file from Erlang
+source files.
+</P>
+
+<P>
+The following are examples of useful functions in this module. Please
+see the reference manual on tags for details.
+</P>
+
+<LIST>
+
+ <ITEM><C>tags:file('foo.erl').</C><BR>
+
+<P>
+Create a <C>TAGS</C> file for the file "foo.erl".
+</P>
+
+ <ITEM><C>tags:subdir('src/project/', [{outfile, 'project.TAGS'}]).</C><BR>
+
+<P>
+Create a tags file containing all Erlang source files in the directory
+<C>"src/project/"</C>. The option <C>outfile</C> specify the name of
+the created <C>TAGS</C> file.
+</P>
+
+ <ITEM><C>tags:root([{outdir, 'bar'}]).</C><BR>
+
+<P>
+Create a <C>TAGS</C> file of all the Erlang files in the Erlang
+distribution. The <C>TAGS</C> file will be placed in the the directory
+<C>bar</C>.
+</P>
+
+</LIST>
+
+</SECTION>
+
+<SECTION>
+<TITLE>Additional Erlang support</TITLE>
+
+<P>
+The standard Tags system has only support for simple names. The
+naming convention <C>module:function</C> used by Erlang is not supported.
+</P>
+
+<P>
+The Erlang mode supplies an alternative set of Tags functions that is
+aware of the format <C>module:function</C>. When selecting a the
+default search string for the commands the name under the point is
+first selected. Should the name not contain a module name the
+<C>-import</C> list at the beginning of the buffer is scanned.
+</P>
+
+<SECTION>
+
+<TITLE>Limitations</TITLE>
+
+<P>
+Currently, the additional Erlang module name support is not compatible
+with the <C>etags.el</C> package that is part of XEmacs.
+</P>
+
+</SECTION>
+</SECTION>
+
+<SECTION>
+<TITLE>Useful Tags Commands</TITLE>
+
+<LIST>
+
+ <ITEM><C> M-. </C> (<C>erlang-find-tag</C>)<BR>
+
+<P>
+Find a function definition. The default value is the function name
+under the point. Should the function name lack the module specifier
+the <C>-import</C> list is searched for an appropriate candidate.
+</P>
+
+
+ <ITEM><C> C-u M-. </C> (<C>erlang-find-tag</C> with an argument)<BR>
+
+<P>
+The <C>find-tag</C> commands place the point on the first occurrence of
+a function that match the tag. This command move the point to the
+next match.
+</P>
+
+
+ <ITEM><C> C-x 4 . </C> (<C>erlang-find-tag-other-window</C>)<BR>
+
+<P>
+As above, but the new file will be shown in another window in the same
+frame.
+</P>
+
+
+ <ITEM><C> C-x 5 . </C> (<C>erlang-find-tag-other-frame</C>)<BR>
+
+<P>
+As <C>erlang-find-tag</C> but the new file will be shown in a new frame.
+</P>
+
+ <ITEM><C> M-TAB </C> (<C>erlang-complete-tag</C>)<BR>
+
+<P>
+This command is used to fill in the end of a partially written
+function name. For example, assume that the point is at the end of
+the string <C>a_long</C>, and the Tags file contain the function
+<C>a_long_function_name</C>. By executing this command the string
+<C>a_long</C> will be expanded into <C>a_long_function_name</C>.
+</P>
+
+
+ <ITEM><C> M-x tags-search RET </C><BR>
+
+<P>
+This command will search through all the files in a project for a
+string. (Actually, it search for a pattern described by a regular
+expression.)
+</P>
+
+
+ <ITEM><C> M-, </C> (<C>tags-loop-continue</C>)<BR>
+
+<P>
+Move the point to the next search match.
+</P>
+
+</LIST>
+
+</SECTION>
+</SECTION>
+
+<SECTION>
+<TITLE>IMenu</TITLE>
+
+<P>
+IMenu is a standard package of GNU Emacs. With IMenu it is possible
+to get a menu in the menu bar containing all the functions in the
+buffer. Erlang mode provides support for Erlang source files.
+</P>
+
+<!-- TODO
+<P>
+Unfortunately the IMenu package is not part of XEmacs. In the future
+Erlang mode might get support for the XEmacs package "funcmenu" that
+provides similar support for XEmacs.
+</P>
+-->
+
+<SECTION>
+<TITLE>Starting IMenu</TITLE>
+
+<LIST>
+
+ <ITEM><C> M-x imenu-add-to-menubar RET</C><BR>
+
+<P>
+This command will create the IMenu menu containing all the functions
+in the current buffer. The command will ask you for a suitable name
+for the menu.
+</P>
+
+</LIST>
+</SECTION>
+
+<SECTION>
+<TITLE>Customization</TITLE>
+
+<P>
+See chapter "<SEEALSO MARKER="#customization">Customization</SEEALSO>"
+below for a general description on how to customize the Erlang mode.
+</P>
+
+<P>
+To automatically create the IMenu menu for all Erlang buffers, place
+the lines below into the appropriate init file (e.g. ~/.emacs). The
+function <C>my-erlang-imenu-hook</C> will be called each time an
+Erlang source file is read. It will call the
+<C>imenu-add-to-menubar</C> function. The menu will be named
+"Functions".
+</P>
+
+<CODE>
+(add-hook 'erlang-mode-hook 'my-erlang-imenu-hook)
+
+(defun my-erlang-imenu-hook ()
+ (if (and window-system (fboundp 'imenu-add-to-menubar))
+ (imenu-add-to-menubar "Functions")))
+</CODE>
+
+</SECTION>
+</SECTION>
+
+<!-- ---------------------------- Inferior Erlang -->
+
+<!-- - CHAPTER -->
+
+<SECTION>
+<TITLE>Running Erlang from Emacs</TITLE>
+
+<P>
+One of the strengths of Emacs is its ability to start slave processes.
+Since Emacs is extendible it is possible let Emacs be a part of a
+large application. For example, Emacs could be used as the user
+interface for Erlang applications.
+</P>
+
+<P>
+The Erlang editing mode provides two simple, yet very useful,
+applications. The first is to start an Erlang shell and use an Emacs
+buffer for input and output. The second is a compile commands that
+makes it possible to compile Erlang modules and to locate the lines
+containing the compilation errors.
+</P>
+
+<P>
+The actual communication between Emacs and Erlang can be performed by
+different low-level techniques. The Erlang editing mode provides a
+technique called "inferior" processes. The add on package Erl'em
+supplies a technically much more advanced communication technique
+known as an Erl'em link. All the commands that are provided by the
+editing mode can use either technique. However, more advanced
+packages will probably need features only provided by the Erl'em
+package.
+</P>
+
+<SECTION>
+<TITLE>Inferior Erlang</TITLE>
+
+<P>
+The editing mode is capable of starting a so called "inferior" Erlang
+process. This is a normal subprocess that use one Emacs buffer for
+input and output. The effect is that a command line shell, or an
+Erlang shell, can be displayed inside Emacs.
+</P>
+
+<P>
+The big drawback with an inferior process is that the communication
+between Emacs and the process is limited to commands issued at the
+command line. Since this is the interface that is used by the user it
+is difficult, to say the least, to write an Emacs application that
+communicate with the inferior process. For example, the
+<C>erlang-compile</C> command described in the section "Compilation"
+below really stretch the capabilities of the inferior Erlang process.
+In fact, should the user have issued a command that would take some
+time to complete it is impossible for Emacs to perform the
+<C>erlang-compile</C> command.
+</P>
+
+</SECTION>
+
+
+<SECTION>
+<TITLE>The Erl'em Link</TITLE>
+
+<P>
+The Erl'em package established a low-level communication channel
+between Emacs and an Erlang node. This communication channel can be
+used by Emacs to issue several independent Erlang commands, to start
+Erlang processes and to open several Erlang IO streams. It is also
+possible for Erlang to call Emacs functions.
+</P>
+
+<P>
+In short the Erl'em package is designed to be the base of complex
+application that is partially implemented in Emacs and partially in
+Erlang.
+</P>
+
+<P>
+It is the hope of the author that the Erl'em link in the future will
+be used as the base for porting the user interface of the Erlang
+debugger to Emacs. If this could be possible, Emacs could be used as
+an Integrated Debugger Environment (IDE) for Erlang.
+</P>
+
+<P>
+The structure of the Erl'em link and its programming interface is
+described in the text "Erl'em Developers Manual".
+</P>
+
+</SECTION>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+<TITLE>Erlang Shell</TITLE>
+
+<P>
+It is possible to start an Erlang shell inside Emacs. The shell will
+use an Emacs buffer for input and output. Normal Emacs commands can
+be used to edit the command line and to recall lines from the command
+line history.
+</P>
+
+<P>
+The output will never be erased from the buffer so you will never risk
+letting important output fall over the top edge of the display.
+</P>
+
+<P>
+As discussed in the previous chapter there are two low-level
+methods for Emacs to communicate with Erlang. The first is by
+starting an inferior process, the second is by using an Erl'em link.
+When using inferior processes each new shell will start a new Erlang
+node. Should the Erl'em link be used it is possible to start several
+shells on the same node, a feature not normally available.
+</P>
+
+<SECTION>
+<TITLE>The shell</TITLE>
+
+<P>
+In this section we describe how to start a shell. In the next we cover
+how to use it once it has been started.
+</P>
+
+<LIST>
+ <ITEM><C> M-x erlang-shell RET </C><BR>
+
+<P>
+Start a new Erlang shell. When an inferior process is used a new
+Erlang node is started for each shell. Should the Erl'em link package
+be installed several shells can be started on the same Erlang node.
+</P>
+
+<P>
+A word of warning. The Erlang function <C>halt().</C> will kill the
+current Erlang node, including all shells running on it.
+</P>
+
+
+ <ITEM><C> M-x erlang-shell-display RET </C><BR>
+
+<P>
+Display one Erlang shell. If there are no Erlang shells active a new
+will be started.
+</P>
+
+</LIST>
+
+</SECTION>
+
+<SECTION>
+
+<TITLE>Command line history</TITLE>
+
+<P>
+The look and feel on an Erlang shell inside Emacs should be the same
+as in a normal Erlang shell. There is just one major difference, the
+cursor keys will actually move the cursor around just like in any
+normal Emacs buffer. The command line history can be accessed by the
+following commands:
+</P>
+
+<LIST>
+
+ <ITEM><C> C-up </C> or <C> M-p </C> (<C>comint-previous-input</C>)<BR>
+
+<P>
+Move to the previous line in the input history.
+</P>
+
+
+ <ITEM><C> C-down </C> or <C> M-n </C> (<C>comint-next-input</C>)<BR>
+
+<P>
+Move to the next line in the input history.
+</P>
+
+</LIST>
+
+<P>
+If the Erlang shell buffer would be killed the command line history is
+saved to a file. The command line history is automatically retrieved
+when a new Erlang shell is started.
+</P>
+
+</SECTION>
+
+<SECTION>
+
+<TITLE>The Erlang Shell Mode</TITLE>
+
+<P>
+The buffers that are used to run Erlang shells use the major mode
+<C>erlang-shell-mode</C>. This major mode is based on the standard
+mode <C>comint-mode</C>.
+</P>
+
+<LIST>
+ <ITEM><C> erlang-shell-mode </C><BR>
+
+<P>
+Enter Erlang shell mode. To operate correctly the buffer should be in
+Comint mode when this command is called.
+</P>
+
+</LIST>
+
+</SECTION>
+
+<SECTION>
+
+<TITLE>Variables</TITLE>
+
+<P>
+In this section we present the variables that control the behavior of
+the Erlang shell. See also the next section "Inferior Erlang
+Variables".
+</P>
+
+<LIST>
+
+<ITEM> <EM>Variable: </EM> <C>erlang-shell-mode-hook</C>
+(default <C>()</C>)<BR>
+
+<P>
+Function to run when this mode is activated. See chapter "<SEEALSO
+MARKER="#customization">Customization</SEEALSO>" below for examples.
+</P>
+
+
+<ITEM> <EM>Variable: </EM> <C>erlang-input-ring-file-name</C>
+(default "~/.erlang_history")<BR>
+
+<P>
+The file name used to save the command line history.
+</P>
+
+
+<ITEM> <EM>Variable: </EM> <C>erlang-shell-function</C>
+(default <C>inferior-erlang</C>)<BR>
+
+<P>
+This variable contain the low-level function to call to start an
+Erlang shell. This variable will be changed by the Erl'em
+installation.
+</P>
+
+
+<ITEM> <EM>Variable: </EM> <C>erlang-shell-display-function</C>
+(default <C>inferior-erlang-run-or-select</C>)<BR>
+
+<P>
+This variable contain the low-level function to call when the
+<C>erlang-shell-display</C> is issued. This variable will be changed by
+the Erl'em installation.
+</P>
+
+</LIST>
+
+</SECTION>
+
+<SECTION>
+<TITLE>Inferior Erlang Variables</TITLE>
+
+<P>
+The variables described in this chapter are only used when inferior
+Erlang processes are used. They do not affect the behavior of the
+shell when using an Erl'em link.
+</P>
+
+<LIST>
+
+ <ITEM> <EM>Variable: </EM>
+<C>inferior-erlang-display-buffer-any-frame</C> (default
+<C>nil</C>)<BR>
+
+<P>
+When this variable is <C>nil</C> the command
+<C>erlang-shell-display</C> will display the inferior process in the
+current frame. When <C>t</C>, it will do nothing when it already is
+visible in another frame. When it is bound to the atom <C>raise</C>
+the frame displaying the buffer will be raised.
+</P>
+
+ <ITEM> <EM>Variable: </EM> <C>inferior-erlang-shell-type</C>
+(default <C>newshell</C>)<BR>
+
+<P>
+There are two different variants of the Erlang shell, named the old
+and the new shell. The old is a simple variant that does not provide
+command line editing facilities. The new, on the other hand, provide
+full edition features. Apart from this major difference, they differ
+on some subtle points. Since Emacs itself takes care of the command
+line edition features you can switch between the two shell types if
+your shell behaves strange.
+</P>
+
+<P>
+To use the new or the old shell bind this variable to <C>newshell</C> or
+<C>oldshell</C>, respectively.
+</P>
+
+ <ITEM> <EM>Variable: </EM> <C>inferior-erlang-machine</C>
+(default <C>"erl"</C>)<BR>
+
+<P>
+The command name of the Erlang runtime system.
+</P>
+
+
+ <ITEM> <EM>Variable: </EM> <C>inferior-erlang-machine-options</C>
+(default <C>()</C>)<BR>
+
+<P>
+A list of strings containing command line options that is used when
+starting an inferior Erlang.
+</P>
+
+
+ <ITEM> <EM>Variable: </EM> <C>inferior-erlang-buffer-name</C>
+(default <C>"*erlang*"</C>)<BR>
+
+<P>
+The base name of the Erlang shell buffer. Should several Erlang shell
+buffers be used they will be named <C>*erlang*<2></C>,
+<C>*erlang*<3></C> etc.
+</P>
+
+</LIST>
+
+</SECTION>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+<TITLE>Compilation</TITLE>
+
+<P>
+The classic edit-compile-bugfix cycle for Erlang is to edit the source
+file in an editor, save it to a file and switch to an Erlang shell.
+In the shell the compilation command is given. Should the compilation
+fail you have to bring out the editor and locate the correct line.
+</P>
+
+<P>
+With the Erlang editing mode the entire edit-compile-bugfix cycle can
+be performed without leaving Emacs. Emacs can order Erlang to compile
+a file and it can parse the error messages to automatically place the
+point on the erroneous lines.
+</P>
+
+<SECTION>
+
+<TITLE>Commands</TITLE>
+
+<LIST>
+
+ <ITEM><C>C-c C-k</C> (<C>erlang-compile</C>)<BR>
+
+<P>
+This command compiles the file in the current buffer.
+</P>
+
+<P>
+The action performed by this command depend on the low-level
+communication method used. Should an inferior Erlang process be used
+Emacs tries to issue a compile command at the Erlang shell prompt.
+The compilation output will be sent to the shell buffer.
+This command will fail if it is not possible to issue a command at the
+Erlang shell prompt.
+</P>
+
+<P>
+Should an Erl'em link be used the compile command sent to Erlang will
+be independent of any active shell. The output will be sent to a
+dedicated buffer.
+</P>
+
+
+ <ITEM><C>C-x ` </C> (<C>erlang-next-error</C>)<BR>
+
+<P>
+This command will place the point on the line where the first error
+was found. Each successive use of this command will move the point to
+the next error. The buffer displaying the compilation errors will be
+updated so that the current error will be visible.
+</P>
+
+<P>
+You can reparse the compiler output from the beginning by preceding
+this command by <C> C-u </C>.
+</P>
+
+ <ITEM><C>erlang-compile-display</C><BR>
+
+<P>
+Show the output generated by the compile command.
+</P>
+
+</LIST>
+</SECTION>
+
+<SECTION>
+<TITLE>Variables</TITLE>
+
+<LIST>
+
+ <ITEM> <EM>Variable: </EM> <C>erlang-compile-use-outdir</C>
+(default <C>t</C>)<BR>
+
+<P>
+In some versions of Erlang the <C>outdir</C> options contains a bug.
+Should the directory not be present in the current Erlang load path
+the object file will not be loaded.
+</P>
+
+<P>
+Should this variable be set to <C>nil</C> the <C>erlang-compile</C>
+command will use a workaround by change current directory, compile the
+file, and change back.
+</P>
+
+
+ <ITEM> <EM>Variable: </EM> <C>erlang-compile-function</C>
+(default <C>inferior-erlang-compile</C>)<BR>
+
+<P>
+The low-level function to use to compile an Erlang module.
+</P>
+
+
+ <ITEM> <EM>Variable: </EM> <C>erlang-compile-display-function</C>
+(default <C>inferior-erlang-run-or-select</C>)<BR>
+
+<P>
+The low-level function to call when the result of a compilation should
+be shown.
+</P>
+
+
+ <ITEM> <EM>Variable: </EM> <C>erlang-next-error-function</C>
+(default <C>inferior-erlang-next-error</C>)<BR>
+
+<P>
+The low-level function to use when <C>erlang-next-error</C> is used.
+</P>
+
+</LIST>
+
+</SECTION>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+<TITLE>Customization</TITLE>
+
+<P>
+One of the strengths of Emacs is that users can fairly easy customize
+the behavior of almost every detail. The Erlang editing mode is not
+an exception to this rule.
+</P>
+
+<P>
+Normally, Emacs is customized through the user and system init files,
+<C>~/.emacs</C> and <C>site-start.el</C>, respectively. The content
+of the files are expressions written in the Emacs extension language
+Emacs Lisp. The semantics of Lisp is fairly similar Erlang's.
+However, the syntax is very different. Fortunately, most
+customizations require only very minor knowledge of the language.
+</P>
+
+<SECTION>
+
+<TITLE>Emacs Lisp</TITLE>
+
+<P>
+In this section we show the basic constructions of Emacs Lisp needed to
+perform customizations.
+</P>
+
+<P>
+In addition to placing the expressions in the init file, they can be
+evaluated while Emacs is started. One method is to use the <C> M-:
+</C> (On older versions of Emacs this is bound to <C> ESC ESC</C>)
+function that evaluates Emacs Lisp expressions in the minibuffer.
+Another method is to write the expressions in the <C> *scratch* </C> buffer,
+place the point at the end of the line and press <C>C-j</C>.
+</P>
+
+<P>
+Below is a series of example that we use to demonstrate simple Emacs
+Lisp constructions.
+</P>
+
+<LIST>
+
+
+ <ITEM> <EM>Example 1:</EM> <BR>
+
+<P>
+In this example we set the variable <C>foo</C> to the value 10 added
+to the value of the variable <C>a</C>. As we can see by this example,
+Emacs Lisp use prefix form for all function calls, including simple
+functions like <C>+</C>.
+</P>
+
+<CODE>
+(setq foo (+ 10 a))
+</CODE>
+
+
+ <ITEM> <EM>Example 2:</EM> <BR>
+
+<P>
+In this example we first define a function <C>bar</C> that sums the value
+of its four parameters. Then we evaluated an expression that first
+calls <C>bar</C> then calls the standard Emacs function <C>message</C>.
+</P>
+
+<CODE>
+(defun bar (a b c d)
+ (+ a b c d))
+
+(message "The sum becomes %d" (bar 1 2 3 4))
+</CODE>
+
+
+ <ITEM> <EM>Example 3:</EM><BR>
+
+<P>
+Among the Emacs Lisp data types we have atoms. However, in
+the following expressions we assign the variable <C>foo</C> the value of
+the variable <C>bar</C>.
+</P>
+
+<CODE>
+(setq foo bar)
+</CODE>
+
+<P>
+To assign the variable <C>foo</C> the atom <C>bar</C> we must quote
+the atom with a <C>'</C>-character. Note the syntax, we should precede the
+expression (in this case <C>bar</C>) with the quote, not surround it.
+</P>
+
+<CODE>
+(setq foo 'bar)
+</CODE>
+
+</LIST>
+
+</SECTION>
+
+
+<SECTION>
+<TITLE>Hooks</TITLE>
+
+<P>
+A hook variable is a variable that contain a list of functions to
+run. In Emacs there is a large number of hook variables, each is
+runed at a special situation. By adding functions to hooks the user
+make Emacs automatically perform anything (well, almost).
+</P>
+
+<P>
+To add a function to a hook you must use the function <C>add-hook</C>.
+To remove it use <C>remove-hook</C>.
+</P>
+
+<P>
+See chapter "The Editing Mode" above for a list of hooks defined by
+the Erlang editing mode.
+</P>
+
+<LIST>
+ <ITEM> <EM> Example: </EM> <BR>
+
+<P>
+In this example we add <C>tempo-template-erlang-large-header</C> to
+the hook <C>erlang-new-file-hook</C>. The effect is that whenever a
+new Erlang file is created a file header is immediately inserted.
+</P>
+
+<CODE>
+ (add-hook 'erlang-new-file-hook 'tempo-template-erlang-large-header)
+</CODE>
+
+<ITEM> <EM> Example: </EM> <BR>
+
+<P>
+Here we define a new function that sets a few variables when it is
+called. We then add the function to the hook <C>erlang-mode-hook</C> that
+gets called every time Erlang mode is activated.
+</P>
+
+<CODE>
+(defun my-erlang-mode-hook ()
+ (setq erlang-electric-commands t))
+
+(add-hook 'erlang-mode-hook 'my-erlang-mode-hook)
+</CODE>
+
+</LIST>
+
+</SECTION>
+
+<SECTION>
+<MARKER ID="key_bindings">
+<TITLE>Custom Key Bindings</TITLE>
+
+<P>
+It is possible to bind keys to your favorite commands. Emacs use a
+number of key-maps: the global key-map defines the default value of
+keys, local maps are used by the individual major modes, minor modes
+can have their own key map etc.
+</P>
+
+<P>
+The commands <C>global-set-key</C> and <C>local-set-key</C> defines
+keys in the global and in the current local key-map, respectively.
+</P>
+
+<P>
+If we would like to redefine a key in the Erlang editing mode we can
+do that by activating Erlang mode and call <C>local-set-key</C>. To
+automate this we must define a function that calls
+<C>local-set-key</C>. This function can then be added to the Erlang
+mode hook so that the correct local key map is active when the key is
+defined.
+</P>
+
+<P>
+<EM> Example: </EM>
+</P>
+
+<P>
+Here we bind <C> C-c C-c </C> to the command <C>erlang-compile</C>,
+the function key <C>f1</C> to <C>erlang-shell</C>, and <C> M-f1 </C>
+to <C> erlang-shell-display </C>. The calls to <C> local-set-key </C>
+will not be performed when the init file is loaded, they will be
+called first when the functions in the hook <C>erlang-mode-hook</C> is
+called, i.e. when Erlang mode is started.
+</P>
+
+<CODE>
+(defun my-erlang-keymap-hook ()
+ (local-set-key (read-kbd-macro "C-c C-c") 'erlang-compile)
+ (local-set-key (read-kbd-macro "<f1>") 'erlang-shell)
+ (local-set-key (read-kbd-macro "M-<f1>") 'erlang-shell-display))
+(add-hook 'erlang-mode-hook 'my-erlang-keymap-hook)
+</CODE>
+
+<P>
+The function <C>read-kbd-macro</C> used in the above example converts
+a string of readable keystrokes into Emacs internal representation.
+</P>
+
+<P>
+<EM> Example: </EM>
+</P>
+
+<P>
+In Erlang mode the tags commands understand the Erlang module naming
+convention. However, the normal tags commands does not. This line
+will bind <C> M-. </C> in the global map to <C>erlang-find-tag</C>.
+</P>
+
+<CODE>
+(global-set-key (read-kbd-macro "M-." 'erlang-find-tag))
+</CODE>
+
+</SECTION>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+<MARKER ID="distributions">
+<TITLE>Emacs Distributions</TITLE>
+
+<P>
+Today there are two major Emacs development streams. The first is
+GNU Emacs from Free Software Foundation and the second is XEmacs.
+Both have advantages and disadvantages, you have to decide for
+yourself which Emacs you prefer.
+</P>
+
+<SECTION>
+
+<TITLE> GNU Emacs </TITLE>
+
+<P>
+This is the standard distribution from The Free Software Foundation,
+an organization lead by the original author of Emacs, Richard
+M. Stallman.
+</P>
+
+<P>
+The source code for the latest version of Emacs can be fetched from
+<C>http://www.gnu.org</C>. A binary distribution for Window NT and 95
+can be found at
+<C>http://www.cs.washington.edu/homes/voelker/ntemacs.html</C>.
+</P>
+
+</SECTION>
+
+<SECTION>
+
+<TITLE> XEmacs </TITLE>
+
+<P>
+This is an alternative version of Emacs. Historically XEmacs is based
+on Lucid Emacs that in turn was based on an early version of Emacs 19.
+The big advantage of XEmacs is that it can handle graphics much
+better. One difference is a list of icons that contains a number of
+commonly used commands. Another is the ability to display graphical
+images in the buffer.
+</P>
+
+<P>
+The major drawback is that when a new feature turns up in GNU Emacs,
+it will often take quite a long time before it will be incorporated
+into XEmacs.
+</P>
+
+<P>
+The latest distribution can be fetched from <C>http://www.xemacs.org</C>.
+</P>
+
+</SECTION>
+
+<SECTION>
+<TITLE> Installing Emacs </TITLE>
+
+<P>
+The source distributions usually comes in a tared and gzipped format.
+Unpack this with the following command:
+</P>
+
+<CODE>
+ tar zxvf <file>.tar.gz
+</CODE>
+
+<P>
+If your tar command do not know how to handle the "z" (unpack) option
+you can unpack it separately:
+</P>
+
+<CODE>
+ gunzip <file>.tar.gz
+ tar xvf <file>.tar
+</CODE>
+
+<P>
+The program <C>gunzip</C> is part of the <C>gzip</C> package that can
+be found on the <C>http://www.gnu.org</C> site.
+</P>
+
+<P>
+Next, read the file named <C>INSTALL</C>. The build process is
+normally performed in three steps: in the first the build system
+performs a number of tests on your system, the next step is to
+actually build the Emacs executable, finally Emacs is installed.
+</P>
+
+</SECTION>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+<MARKER ID="installation">
+<TITLE> Installation of the Erlang Editing Mode</TITLE>
+
+<P>
+In the OTP installation, the Erlang editing mode is already
+installed. All that is needed is that the system administrator or the
+individual user configures their Emacs Init files to use it.
+
+<P>
+If we assume that OTP has been installed in
+<em>OTP_ROOT</em>, the editing mode can be found in
+<em>OTP_ROOT</em><c>/misc/emacs</C>.
+
+<P>
+The <C>erlang.el</C> file found in the installation directory is already
+compiled. If it needs to be recompiled, the following command line
+should create a new <C>erlang.elc</C> file:
+
+<CODE>
+ emacs -batch -q -no-site-file -f batch-byte-compile erlang.el
+</CODE>
+
+<P>
+
+<SECTION>
+<TITLE>Editing the right Emacs Init file</TITLE>
+<P>
+System administrators edit <C>site-start.el</C>, individuals edit
+their <C>.emacs</C> files.
+
+<p>
+On UNIX systems, individuals should edit/create the file <c>.emacs</c>
+in their home directories.
+
+<p>
+On Windows NT/95, individuals should also edit/create their
+<c>.emacs</c> file, but the location of the file depends on the
+configuration of the system.
+
+<p>
+<list>
+<item>
+If the <em>HOME</em> environment variable
+is set, Emacs will look for the <c>.emacs</c> file in the directory
+indicated by the <em>HOME</em> variable.
+
+
+<item>
+If <em>HOME</em> is not set,
+Emacs will look for the <c>.emacs</c> file in <c>C:\</c>.
+</list>
+</section>
+
+
+<SECTION>
+<TITLE> Extending the load path</TITLE>
+<P>
+The directory with the editing mode,
+<em>OTP_ROOT</em><c>/misc/emacs</C>, must be in the load path for Emacs.
+
+<P>
+Add the following line to the selected initialization file (replace
+<C> OTP_ROOT </C> with the name of the installation
+directory for OTP, keep the quote characters):
+</P>
+<CODE>
+ (setq load-path (cons "OTP_ROOT/misc/emacs" load-path))
+</CODE>
+
+
+<P>
+Note: When running under Windows, use <C> / </C> or <C> \\ </C> as
+separator in pathnames in the Emacs configuration files. Using a single
+<C> \ </C> in strings does not work, as it is interpreted by Emacs as
+an escape character.
+</P>
+
+
+</section>
+
+<section>
+<TITLE> Specifying the OTP installation directory</TITLE>
+
+<P>
+Some functions in the Erlang editing mode require that the OTP
+installation directory is known. The following is an example where we
+assume that they are installed in the directory <C>OTP_ROOT</C>,
+change this to reflect the location on your system.
+</P>
+
+<CODE>
+ (setq erlang-root-dir "OTP_ROOT")
+</CODE>
+
+</section>
+
+<section>
+<title>Extending the execution path</title>
+
+<p>
+To use inferior Erlang Shells, you need to do the following
+configuration. If your <em>PATH</em> environment variable already
+includes the location of the <c>erl</c> or <c>erl.exe</c> executable
+this configuration is not necessary.
+
+<p>
+You can either extend the <em>PATH</em> environment variable with the
+location of the <c>erl</c>/<c>erl.exe</c> executable. Please refer to
+instructions for setting environment variables on your particular
+platform for details.
+
+<p>
+You can also extend the execution path for Emacs as described
+below. If the executable is located in <c>OTP_ROOT/bin</c> then you
+add the following line to you Emacs Init file:
+
+<code>
+ (setq exec-path (cons "OTP_ROOT/bin" exec-path))
+
+</code>
+</section>
+
+<section>
+<TITLE>Final setup</TITLE>
+<P>
+Finally, add the following line to the init file:
+</P>
+
+<CODE>
+ (require 'erlang-start)
+</CODE>
+
+<P>
+This will inform Emacs that the Erlang editing mode is available. It
+will associate the file extensions <C> .erl </C> and <C> .hrl </C>
+with Erlang mode. Also it will make sure that files with the
+extension <C> .beam </C> will be ignored when using file name
+completion.
+</P>
+
+</SECTION>
+
+<SECTION>
+<MARKER ID="unix_dotemacs">
+<TITLE> An Example for UNIX </TITLE>
+
+<P>
+Below is a complete example of what should be added to a user's
+<c>.emacs</c> provided that OTP is installed in the directory
+<C>/usr/local/otp</C>:
+
+<CODE>
+(setq load-path (cons "/usr/local/otp/misc/emacs"
+ load-path))
+(setq erlang-root-dir "/usr/local/otp")
+(setq exec-path (cons "/usr/local/otp/bin" exec-path))
+(require 'erlang-start)
+</CODE>
+
+<P>
+Any additional user configurations can be added after this. See for
+instance section "<SEEALSO
+MARKER="#font-lock">Customization</SEEALSO>" for some useful
+customizations.
+
+
+</section>
+
+<SECTION>
+<MARKER ID="win_dotemacs">
+<TITLE> An Example for Windows </TITLE>
+
+<P>
+Below is a complete example of what should be added to a user's
+<c>.emacs</c> provided that OTP is installed in the directory
+<C>C:\Program Files\erl-4.7</C>:
+
+<CODE>
+(setq load-path (cons "C:/Program Files/erl-4.7/misc/emacs"
+ load-path))
+(setq erlang-root-dir "C:/Program Files/erl-4.7")
+(setq exec-path (cons "C:/Program Files/erl-4.7/bin" exec-path))
+(require 'erlang-start)
+</CODE>
+
+<P>
+Any additional user configurations can be added after this. See for
+instance section "<SEEALSO
+MARKER="#font-lock">Customization</SEEALSO>" for some useful
+customizations.
+
+
+
+</section>
+
+
+<SECTION>
+<TITLE> Check the Installation </TITLE>
+
+<P>
+Restart the Emacs and load or create an Erlang file (with the <C>.erl</C>
+extension). If the installation was performed correctly the mode line
+should contain the word "Erlang". Select the "Version" menu item in
+the "Erlang" menu, check that the version number matches the version in
+found in the files in <c>OTP_ROOT/misc/emacs</c>.
+</P>
+
+</SECTION>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+<MARKER ID="notation">
+<TITLE> Notation </TITLE>
+
+<P>
+In this book we use the same terminology used in the Emacs
+documentation. This chapter contain a short glossary of words and
+their meaning in the Emacs world.
+</P>
+
+<LIST>
+
+ <ITEM><EM> Buffer </EM>
+
+<P>
+A buffer is used by Emacs to handle text. When editing a file the
+content is loaded into a buffer. However buffers can contain other
+things as well. For example, a buffer can contain a list of files in
+a directory, it can contain generated help texts, or it is possible to
+start processes that use a buffer in Emacs for input and output. A
+buffer need not be visible, but if it is, it is shown in a window.
+</P>
+
+ <ITEM><EM> Emacs Lisp </EM>
+
+<P>
+Emacs is written in two languages. The Emacs core is written in C.
+The major part, as well as most add-on packages, are written in Emacs
+Lisp. This is also the language used by the init files.
+</P>
+
+ <ITEM><EM> Frame </EM>
+
+<P>
+This is what most other systems refer to as a <EM> window </EM>.
+Emacs use frame since the word window was used for another feature
+long before window systems were invented.
+</P>
+
+ <ITEM><EM> init file </EM>
+
+<P>
+Files read by Emacs at startup. The user startup file is named
+<C>~/.emacs</C>. The init files are used to customize Emacs, for
+example to add new packages like <C>erlang</C>. The language used in
+the startup files is Emacs Lisp.
+</P>
+
+ <ITEM><EM> Major mode </EM>
+
+<P>
+A major mode provides support for edit text of a particular sort. For
+example, the Erlang editing mode is a major mode. Each buffer have
+exactly one major mode active.
+</P>
+
+ <ITEM><EM> Minor mode </EM>
+
+<P>
+A minor mode provides some additional support. Each buffer can have
+several minor modes active at the same time. One example is
+<C>font-lock-mode</C> that activates syntax highlighting, another is
+<C>follow-mode</C> that make two side-by-side windows act like one
+tall window.
+</P>
+
+ <ITEM><EM> Mode line </EM>
+
+<P>
+The line at the bottom of each Emacs window that contain information
+about the buffer. E.g. the name of the buffer, the line number, and
+the name of the the current major mode.
+</P>
+
+ <ITEM><C> nil </C>
+
+<P>
+The value used in Emacs Lisp to represent false. True can be
+represented by any non-<C>nil</C> value, but it is preferred to use
+<C>t</C>.
+</P>
+
+ <ITEM><EM> Point </EM>
+<P>
+The point can be seen as the position of the cursor. More precisely,
+the point is the position between two characters while the cursor is
+drawn over the character following the point.
+</P>
+
+ <ITEM><C> t </C>
+
+<P>
+The value <C>t</C> is used by flags in Emacs Lisp to represent true.
+See also <C>nil</C>.
+</P>
+
+ <ITEM><EM> Window </EM>
+
+<P>
+An area where text is visible in Emacs. A <EM>frame</EM> (which is a
+window in non-Emacs terminology) can contain one or more windows. New
+windows can be created by splitting windows either vertically or
+horizontally.
+</P>
+
+</LIST>
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+<TITLE> Keys </TITLE>
+
+<LIST>
+
+ <ITEM><C> C- </C> The control key.
+
+ <ITEM><C> M- </C> The meta key. Normally this is the left ALT key.
+Alternatively the escape key can be used (with the difference that the
+escape key should be pressed and released while the ALT key work just
+like the control key.)
+
+ <ITEM><C> M-C- </C> Press both meta and control at the same time. (Or press the
+escape key, release it, and then press the control key.)
+
+ <ITEM><C> RET </C> The return key.
+
+</LIST>
+
+<P>
+All commands in Emacs have names. A named command can be executed by
+pressing <C> M-x</C>, typing the name of the command, and hitting <C>
+RET </C>.
+</P>
+
+</SECTION>
+
+<!-- CHAPTER -->
+
+<SECTION>
+<TITLE> Further reading </TITLE>
+
+<P>
+In this chapter I present some references to material on Emacs. They
+are divided into the two categories "Usage" and "Development". The
+first is for normal Emacs users who would like to know how to get more
+power out of their editor. The second is for people who would like
+to develop their own applications in Emacs Lisp.
+</P>
+
+<P>
+Personally, I would recommend the series of books from the Free
+Software Foundation, they are written by the people that wrote Emacs
+and they form a coherent series useful for everyone from beginners to
+experienced Emacs Lisp developers.
+</P>
+
+<SECTION>
+<TITLE> Usage </TITLE>
+
+<LIST>
+
+
+ <ITEM> Richard M. Stallman. GNU Emacs Manual. Free Software
+Foundation, 1995. <BR>
+
+<P>
+This is the Bible on using Emacs. It is written by the principle
+author of Emacs. An on-line version of this manual is part of the
+standard Emacs distribution, see the "Help->Browse Manuals" menu.
+</P>
+
+
+ <ITEM> "comp.emacs", News Group on Usenet. <BR>
+
+<P>
+General Emacs group, everything is discussed here from beginners to
+complex development issues.
+</P>
+
+
+ <ITEM> "comp.emacs.xemacs", News Group on Usenet. <BR>
+
+<P>
+This group cover XEmacs only.
+</P>
+
+
+ <ITEM> "gnu.emacs.help", News Group on Usenet. <BR>
+
+<P>
+This group is like "comp.emacs" except that the topic only should
+cover GNU Emacs, not XEmacs or any other Emacs derivate.
+</P>
+
+
+ <ITEM> "gnu.emacs.sources", News Group on Usenet. <BR>
+
+<P>
+In this group a lot of interesting Emacs packages are posted. In fact
+only source code is permitted, questions should be redirected to one of
+the other Emacs groups.
+</P>
+
+
+ <ITEM> "gnu.emacs.bugs", News Group on Usenet. <BR>
+
+<P>
+If you have found a bug in Emacs you should post it here. Do not post
+bug reports on packages that are nor part of the standard Emacs
+distribution, they should be sent to the maintainer of the package.
+</P>
+
+</LIST>
+</SECTION>
+
+
+<SECTION>
+<TITLE> Development </TITLE>
+
+<LIST>
+
+ <ITEM> Robert J. Chassell. Programming in Emacs Lisp: an Introduction.
+Free Software Foundation, 1995. <BR>
+
+<P>
+This a good introduction to Lisp in general and Emacs Lisp in
+particular. Just like the other books form FSF, this book is free and
+can be downloaded from <C> http://www.gnu.org </C>.
+</P>
+
+
+ <ITEM> Bil Lewis et.al. The GNU Emacs Lisp Reference Manual. Free Software
+Foundation, 1995. <BR>
+
+<P>
+This is the main source of information for any serious Emacs
+developer. This manual covers every aspect of Emacs Lisp. This
+manual, like Emacs itself, is free. The manuscript can be downloaded
+from <C> http://www.gnu.org </C> and can either be converted into printable
+form or be converted into a hypertext on-line manual.
+</P>
+
+
+ <ITEM> Bob Glickstein. Writing GNU Emacs Extensions. O'Reilly, 1997. <BR>
+
+<P>
+This is a good tutorial on how to write Emacs packages.
+</P>
+
+
+ <ITEM> Anders Lindgren. Erl'em Developers Manual. Ericsson, 1998. <BR>
+
+<P>
+This text covers the architecture of the Erl'em communication link and
+the application programmers interface to it.
+</P>
+
+<!-- <ITEM> David K&aring;gedal. Tempo Manual. -->
+
+<!-- TODO: the url -->
+
+<P>
+The tempo package is presented in this manual. The latest version can
+be found at <C> http://www.lysator.liu.se </C>.
+</P>
+
+</LIST>
+
+</SECTION>
+</SECTION>
+
+
+<!-- TODO -->
+<!-- Known Bugs -->
+
+<!-- Arity -->
+
+<SECTION>
+
+<TITLE> Reporting Bugs </TITLE>
+
+<P>
+Please send bug reports to the following email address:
+</P>
+
+<CODE>
+</CODE>
+
+<P>
+Please state as accurate as possible:
+</P>
+
+<LIST>
+ <ITEM> Version number of the Erlang editing mode (see the menu), Emacs,
+Erlang, and of any other relevant software.
+
+ <ITEM> What the expected result was.
+
+ <ITEM> What you did, preferably in a repeatable step-by-step form.
+
+ <ITEM> A description of the unexpected result.
+
+ <ITEM> Relevant pieces of Erlang code causing the problem.
+
+ <ITEM> Personal Emacs customizations, if any.
+</LIST>
+
+<P>
+Should the Emacs generate an error, please set the emacs variable
+<C>debug-on-error</C> to <C>t</C>. Repeat the error and enclose the
+debug information in your bug-report.
+</P>
+
+<P>
+To set the variable you can use the following command:
+</P>
+
+<CODE>
+ M-x set-variable RET debug-on-error RET t RET
+</CODE>
+
+</SECTION>
+
+</CHAPTER>
diff --git a/lib/tools/emacs/tags.3 b/lib/tools/emacs/tags.3
new file mode 100644
index 0000000000..f98069a2f3
--- /dev/null
+++ b/lib/tools/emacs/tags.3
@@ -0,0 +1,61 @@
+.TH TAGS 3 1996-05-30 "Ericsson Software Technology" "ERLANG MODULE DEFINITION"
+.SH MODULE
+tags \- Generate Emacs TAGS file from Erlang source files.
+.SH DESCRIPTION
+A TAGS file is used by Emacs to find function and variable definitions
+in any source file in a big project. This module can generate a TAGS
+file from Erlang source files. It recognises functions, records, and
+defines.
+.SH EXPORTS
+.TP 8
+.B root([Options])
+Create a TAGS file covering all files in the Erlang distribution.
+.TP 8
+.B file(File [, Options])
+Create a TAGS file for the file `File'.
+.TP 8
+.B files(FileList [, Options])
+Create a TAGS file for the files in the list `FileList'.
+.TP 8
+.B dir(Dir [, Options])
+Create a TAGS file for all files in directory `Dir'.
+.TP 8
+.B dirs(DirList [, Options])
+Create a TAGS file for all files in any directory in `DirList'.
+.TP 8
+.B subdir(Dir [, Options])
+Descend recursively down the directory `Dir' and create a TAGS file
+based on all files found.
+.TP 8
+.B subdirs(DirList [, Options])
+Descend recursively down all the directories in `DirList' and create a
+TAGS file based on all files found.
+.SH OPTIONS
+The functions above have an optional argument, \fBOptions\fR. It is a
+list which can contain the following elements:
+.TP 8
+.B {outfile, NameOfTAGSFile}
+Create a TAGS file named `NameOfTAGSFile'.
+.TP 8
+.B {outdir, NameOfDirectory}
+Create a file named TAGS in the directory `NameOfDirectory'.
+.P
+The default behaviour is to create a file named "TAGS" in the current
+directory.
+.SH SEE ALSO
+GNU Emacs Manual, chapter "Editing Programs", section "Tag Tables".
+.P
+Erlang mode V2.0 for Emacs.
+.SH AUTHOR
+.nf
+Anders Lindgren
+.fi
+
+.\" Local Variables:
+.\" mode: nroff
+.\" eval: (auto-fill-mode 1)
+.\" left-margin: 0
+.\" fill-column: 70
+.\" version-control: never
+.\" indent-tabs-mode: nil
+.\" End:
diff --git a/lib/tools/emacs/tags.erl b/lib/tools/emacs/tags.erl
new file mode 120000
index 0000000000..87be7264e9
--- /dev/null
+++ b/lib/tools/emacs/tags.erl
@@ -0,0 +1 @@
+../src/tags.erl \ No newline at end of file
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
new file mode 100644
index 0000000000..b2cc23b92b
--- /dev/null
+++ b/lib/tools/emacs/test.erl.indented
@@ -0,0 +1,536 @@
+%% -*- erlang -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+%%%-------------------------------------------------------------------
+%%% File : test.erl
+%%% Author : Dan Gudmundsson <[email protected]>
+%%% Description : Test emacs mode indention and font-locking
+%%% this file is intentionally not indented.
+%%% Copy the file and indent it and you should end up with test.erl.indented
+%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
+%%%-------------------------------------------------------------------
+
+%% Start off with syntax highlighting you have to verify this by looking here
+%% and see that the code looks alright
+
+-module(test).
+-compile(export_all).
+
+%% Module attributes should be highlighted
+
+-export([t/1]).
+-record(record1, {a,
+ b,
+ c
+ }).
+-record(record2, {
+ a,
+ b
+ }).
+
+-define(MACRO_1, macro).
+-define(MACRO_2(_), macro).
+
+-spec t(integer()) -> any().
+
+-type ann() :: Var :: integer().
+-type ann2() :: Var ::
+ 'return' | 'return_white_spaces' | 'return_comments'
+ | 'text' | ann().
+-type paren() ::
+ (ann2()).
+-type t1() :: atom().
+-type t2() :: [t1()].
+-type t3(Atom) :: integer(Atom).
+-type t4() :: t3(foobar).
+-type t5() :: {t1(), t3(foo)}.
+-type t6() :: 1 | 2 | 3 |
+ 'foo' | 'bar'.
+-type t7() :: [].
+-type t71() :: [_].
+-type t8() :: {any(),none(),pid(),port(),
+ reference(),float()}.
+-type t9() :: [1|2|3|foo|bar] |
+ list(a | b | c) | t71().
+-type t10() :: {1|2|3|foo|t9()} | {}.
+-type t11() :: 1..2.
+-type t13() :: maybe_improper_list(integer(), t11()).
+-type t14() :: [erl_scan:foo() |
+ %% Should be highlighted
+ non_neg_integer() | nonempty_list() |
+ nonempty_improper_list() | nonempty_maybe_improper_list() |
+ %% Should not be highlighted
+ nonempty_() | nonlist() |
+ erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
+ <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
+ <<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
+ <<_:34>>|<<_:34>>|<<_:34>>].
+-type t16() :: fun().
+-type t17() :: fun((...) -> paren()).
+-type t18() :: fun(() -> t17() | t16()).
+-type t19() :: fun((t18()) -> t16()) |
+ fun((nonempty_maybe_improper_list('integer', any())|
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
+ nonempty_maybe_improper_list('integer', any())|
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer()).
+-type t20() :: [t19(), ...].
+-type t21() :: tuple().
+-type t21(A) :: A.
+-type t22() :: t21(integer()).
+-type t23() :: #rec1{}.
+-type t24() :: #rec2{a :: t23(), b :: [atom()]}.
+-type t25() :: #rec3{f123 :: [t24() |
+ 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())]}.
+-type t99() ::
+ {t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
+ t15(),t20(),t21(), t22(),t25()}.
+-spec t1(FooBar :: t99()) -> t99();
+ (t2()) -> t2();
+ (t4()) -> t4() when is_subtype(t4(), t24);
+ (t23()) -> t23() when is_subtype(t23(), atom()),
+ is_subtype(t23(), t14());
+ (t24()) -> t24() when is_subtype(t24(), atom()),
+ is_subtype(t24(), t14()),
+ is_subtype(t24(), t4()).
+-spec mod:t2() -> any().
+-opaque attributes_data() ::
+ [{'column', column()} | {'line', info_line()} |
+ {'text', string()}] | {line(),column()}.
+-record(r,{
+ f1 :: attributes_data(),
+ f222 = foo:bar(34, #rec3{}, 234234234423,
+ aassdsfsdfsdf, 2234242323) ::
+ [t24() | 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())],
+ f333 :: [t24() | 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())],
+ f3 = x:y(),
+ f4 = x:z() :: t99(),
+ f17 :: 'undefined',
+ f18 :: 1 | 2 | 'undefined',
+ f19 = 3 :: integer()|undefined,
+ f5 = 3 :: undefined|integer()}).
+
+
+
+highlighting(X) % Function definitions should be highlighted
+ when is_integer(X) -> % and so should `when' and `is_integer' be
+ %% Highlighting
+ %% Various characters (we keep an `atom' after to see that highlighting ends)
+ $a,atom, % Characters should be marked
+ "string",atom, % and strings
+ 'asdasd',atom, % quote should be atoms??
+ 'VaV',atom,
+ 'aVa',atom,
+ '\'atom',atom,
+ 'atom\'',atom,
+ 'at\'om',atom,
+ '#1',atom,
+
+ $", atom, % atom should be ok
+ $', atom,
+
+ "string$", atom, "string$", atom, % currently buggy I know...
+ "string\$", atom, % workaround for bug above
+
+ "char $in string", atom,
+
+ $[, ${, $\\, atom,
+ ?MACRO_1,
+ ?MACRO_2(foo),
+
+ %% Numerical constants
+ 16#DD, % AD Should not be highlighted
+ 32#dd, % AD Should not be highlighted
+ 32#ddAB, % AD Should not be highlighted
+ 32#101, % AD Should not be highlighted
+ 32#ABTR, % AD Should not be highlighted
+
+ %% Variables
+ Variables = lists:foo(),
+ _Variables = lists:foo(), % AD
+ AppSpec = Xyz/2,
+ Module42 = Xyz(foo, bar),
+ Module:foo(),
+ _Module:foo(), % AD
+ FooÅÅ = lists:reverse([tl,hd,tl,hd]), % AD Should highlight FooÅÅ
+ _FooÅÅ = 42, % AD Should highlight _FooÅÅ
+
+ %% Bifs
+ erlang:registered(),
+ registered(),
+ hd(tl(tl(hd([a,b,c])))),
+ erlang:anything(lists),
+ %% Guards
+ is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
+ is_function(Fun), is_pid(self()),
+ not_a_guard:is_list([]),
+ %% Other Types
+
+ atom, % not (currently) hightlighted
+ 234234,
+ 234.43,
+
+ [list, are, not, higlighted],
+ {nor, is, tuple},
+ ok.
+
+%%%
+%%% Indentation
+%%%
+
+%%% Left
+
+%% Indented
+
+ % Right
+
+
+indent_basics(X, Y, Z)
+ when X > 42,
+ Z < 13;
+ Y =:= 4711 ->
+ %% comments
+ % right comments
+ case lists:filter(fun(_, AlongName,
+ B,
+ C) ->
+ true
+ end,
+ [a,v,b])
+ of
+ [] ->
+ Y = 5 * 43,
+ ok;
+ [_|_] ->
+ Y = 5 * 43,
+ ok
+ end,
+ Y,
+ %% List, tuples and binaries
+ [a,
+ b, c
+ ],
+ [ a,
+ b, c
+ ],
+
+ [
+ a,
+ b
+ ],
+ {a,
+ b,c
+ },
+ { a,
+ b,c
+ },
+
+ {
+ a,
+ b
+ },
+
+ <<1:8,
+ 2:8
+ >>,
+ <<
+ 1:8,
+ 2:8
+ >>,
+ << 1:8,
+ 2:8
+ >>,
+
+ (a,
+ b,
+ c
+ ),
+
+ ( a,
+ b,
+ c
+ ),
+
+
+ (
+ a,
+ b,
+ c
+ ),
+
+
+ ok;
+indent_basics(Xlongname,
+ #struct{a=Foo,
+ b=Bar},
+ [X|
+ Y]) ->
+ testing_next_clause,
+ ok;
+indent_basics( % AD added clause
+ X, % not sure how this should look
+ Y,
+ Z)
+ when
+ X < 42, Z > 13;
+ Y =:= 4711 ->
+ foo;
+indent_basics(X, Y, Z) when % AD added clause
+ X < 42, Z > 13; % testing when indentation
+ Y =:= 4711 ->
+ foo;
+indent_basics(X, Y, Z) % AD added clause
+ when % testing when indentation
+ X < 42, Z > 13; % unsure about this one
+ Y =:= 4711 ->
+ foo.
+
+
+indent_icr(Z) -> % icr = if case receive
+ %% If
+ if Z >= 0 ->
+ X = 43 div 4,
+ foo(X);
+ Z =< 10 ->
+ X = 43 div 4,
+ foo(X);
+ Z == 5 orelse
+ Z == 7 ->
+ X = 43 div 4,
+ foo(X);
+ true ->
+ if_works
+ end,
+ %% Case
+ case {Z, foo, bar} of
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_} when
+ Z =:= 42 -> % AD line should be indented as a when
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_}
+ when Z < 10 -> % AD when should be indented
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_}
+ when % AD when should be indented
+ Z < 10 % and the guards should follow when
+ andalso % unsure about how though
+ true ->
+ X = 43 div 4,
+ foo(X)
+ end,
+ %% begin
+ begin
+ sune,
+ X = 74234 + foo(8456) +
+ 345 div 43,
+ ok
+ end,
+
+
+ %% receive
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ Z ->
+ X = 43 div 4,
+ foo(X)
+ end,
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ Z % AD added clause
+ when Z =:= 1 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z when % AD added clause
+ Z =:= 2 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z ->
+ X = 43 div 4,
+ foo(X)
+ after infinity ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ receive
+ after 10 ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ ok.
+
+indent_fun() ->
+ %% Changed fun to one indention level
+ Var = spawn(fun(X)
+ when X == 2;
+ X > 10 ->
+ hello,
+ case Hello() of
+ true when is_atom(X) ->
+ foo;
+ false ->
+ bar
+ end;
+ (Foo) when is_atom(Foo),
+ is_integer(X) ->
+ X = 6* 45,
+ Y = true andalso
+ kalle
+ end),
+ ok.
+
+indent_try_catch() ->
+ try
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R % AD added clause
+ when R =:= 42 -> % when should be indented
+ foo(R);
+ error:R % AD added clause
+ when % when should be indented
+ R =:= 42 -> % but unsure about this (maybe 2 more)
+ foo(R);
+ error:R when % AD added clause
+ R =:= foo -> % line should be 2 indented (works)
+ foo(R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile)
+ end;
+indent_try_catch() ->
+ try foo(bar) of
+ X when true andalso
+ kalle ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2);
+ X % AD added clause
+ when false andalso % when should be 2 indented
+ bengt ->
+ gurka();
+ X when % AD added clause
+ false andalso % line should be 2 indented
+ not bengt ->
+ gurka();
+ X ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile),
+ bar(with_long_arg,
+ with_second_arg)
+ end;
+indent_try_catch() ->
+ try foo()
+ after
+ foo(),
+ bar(with_long_arg,
+ with_second_arg)
+ end.
+
+indent_catch() ->
+ D = B +
+ float(43.1),
+
+ B = catch oskar(X),
+
+ A = catch (baz +
+ bax),
+ catch foo(),
+
+ C = catch B +
+ float(43.1),
+
+ case catch (X) of
+ A ->
+ B
+ end,
+ try sune of
+ _ -> foo
+ catch _:_ -> baf
+ end.
+
+indent_binary() ->
+ X = lists:foldr(fun(M) ->
+ <<Ma/binary, " ">>
+ end, [], A),
+ A = <<X/binary, 0:8>>,
+ B.
+
+
+indent_comprehensions() ->
+ %% I don't have a good idea how we want to handle this
+ %% but they are here to show how they are indented today.
+ Result1 = [X ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ ],
+ Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ ],
+
+ Binary1 = << <<X:8>> ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ >>,
+
+ Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ >>,
+ ok.
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
new file mode 100644
index 0000000000..773998a4c6
--- /dev/null
+++ b/lib/tools/emacs/test.erl.orig
@@ -0,0 +1,536 @@
+%% -*- erlang -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+%%%-------------------------------------------------------------------
+%%% File : test.erl
+%%% Author : Dan Gudmundsson <[email protected]>
+%%% Description : Test emacs mode indention and font-locking
+%%% this file is intentionally not indented.
+%%% Copy the file and indent it and you should end up with test.erl.indented
+%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
+%%%-------------------------------------------------------------------
+
+%% Start off with syntax highlighting you have to verify this by looking here
+%% and see that the code looks alright
+
+-module(test).
+-compile(export_all).
+
+%% Module attributes should be highlighted
+
+-export([t/1]).
+-record(record1, {a,
+ b,
+ c
+}).
+-record(record2, {
+ a,
+ b
+ }).
+
+-define(MACRO_1, macro).
+-define(MACRO_2(_), macro).
+
+-spec t(integer()) -> any().
+
+-type ann() :: Var :: integer().
+-type ann2() :: Var ::
+ 'return' | 'return_white_spaces' | 'return_comments'
+ | 'text' | ann().
+-type paren() ::
+ (ann2()).
+-type t1() :: atom().
+-type t2() :: [t1()].
+-type t3(Atom) :: integer(Atom).
+-type t4() :: t3(foobar).
+-type t5() :: {t1(), t3(foo)}.
+-type t6() :: 1 | 2 | 3 |
+ 'foo' | 'bar'.
+-type t7() :: [].
+-type t71() :: [_].
+-type t8() :: {any(),none(),pid(),port(),
+ reference(),float()}.
+-type t9() :: [1|2|3|foo|bar] |
+ list(a | b | c) | t71().
+-type t10() :: {1|2|3|foo|t9()} | {}.
+-type t11() :: 1..2.
+-type t13() :: maybe_improper_list(integer(), t11()).
+-type t14() :: [erl_scan:foo() |
+ %% Should be highlighted
+ non_neg_integer() | nonempty_list() |
+ nonempty_improper_list() | nonempty_maybe_improper_list() |
+ %% Should not be highlighted
+ nonempty_() | nonlist() |
+erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
+ <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
+<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
+<<_:34>>|<<_:34>>|<<_:34>>].
+-type t16() :: fun().
+-type t17() :: fun((...) -> paren()).
+-type t18() :: fun(() -> t17() | t16()).
+-type t19() :: fun((t18()) -> t16()) |
+ fun((nonempty_maybe_improper_list('integer', any())|
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
+nonempty_maybe_improper_list('integer', any())|
+1|2|3|a|b|<<_:3,_:_*14>>|integer()).
+-type t20() :: [t19(), ...].
+-type t21() :: tuple().
+-type t21(A) :: A.
+-type t22() :: t21(integer()).
+-type t23() :: #rec1{}.
+-type t24() :: #rec2{a :: t23(), b :: [atom()]}.
+-type t25() :: #rec3{f123 :: [t24() |
+1|2|3|4|a|b|c|d|
+nonempty_maybe_improper_list(integer, any())]}.
+-type t99() ::
+{t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
+t15(),t20(),t21(), t22(),t25()}.
+-spec t1(FooBar :: t99()) -> t99();
+(t2()) -> t2();
+ (t4()) -> t4() when is_subtype(t4(), t24);
+(t23()) -> t23() when is_subtype(t23(), atom()),
+ is_subtype(t23(), t14());
+(t24()) -> t24() when is_subtype(t24(), atom()),
+ is_subtype(t24(), t14()),
+ is_subtype(t24(), t4()).
+-spec mod:t2() -> any().
+-opaque attributes_data() ::
+[{'column', column()} | {'line', info_line()} |
+ {'text', string()}] | {line(),column()}.
+-record(r,{
+ f1 :: attributes_data(),
+f222 = foo:bar(34, #rec3{}, 234234234423,
+ aassdsfsdfsdf, 2234242323) ::
+[t24() | 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())],
+f333 :: [t24() | 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())],
+f3 = x:y(),
+f4 = x:z() :: t99(),
+f17 :: 'undefined',
+f18 :: 1 | 2 | 'undefined',
+f19 = 3 :: integer()|undefined,
+f5 = 3 :: undefined|integer()}).
+
+
+
+highlighting(X) % Function definitions should be highlighted
+ when is_integer(X) -> % and so should `when' and `is_integer' be
+ %% Highlighting
+ %% Various characters (we keep an `atom' after to see that highlighting ends)
+ $a,atom, % Characters should be marked
+ "string",atom, % and strings
+ 'asdasd',atom, % quote should be atoms??
+ 'VaV',atom,
+ 'aVa',atom,
+ '\'atom',atom,
+ 'atom\'',atom,
+ 'at\'om',atom,
+ '#1',atom,
+
+ $", atom, % atom should be ok
+ $', atom,
+
+ "string$", atom, "string$", atom, % currently buggy I know...
+ "string\$", atom, % workaround for bug above
+
+ "char $in string", atom,
+
+ $[, ${, $\\, atom,
+ ?MACRO_1,
+ ?MACRO_2(foo),
+
+ %% Numerical constants
+ 16#DD, % AD Should not be highlighted
+ 32#dd, % AD Should not be highlighted
+ 32#ddAB, % AD Should not be highlighted
+ 32#101, % AD Should not be highlighted
+ 32#ABTR, % AD Should not be highlighted
+
+ %% Variables
+ Variables = lists:foo(),
+ _Variables = lists:foo(), % AD
+ AppSpec = Xyz/2,
+ Module42 = Xyz(foo, bar),
+ Module:foo(),
+ _Module:foo(), % AD
+ FooÅÅ = lists:reverse([tl,hd,tl,hd]), % AD Should highlight FooÅÅ
+ _FooÅÅ = 42, % AD Should highlight _FooÅÅ
+
+ %% Bifs
+ erlang:registered(),
+ registered(),
+ hd(tl(tl(hd([a,b,c])))),
+ erlang:anything(lists),
+ %% Guards
+ is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
+ is_function(Fun), is_pid(self()),
+ not_a_guard:is_list([]),
+ %% Other Types
+
+ atom, % not (currently) hightlighted
+ 234234,
+ 234.43,
+
+ [list, are, not, higlighted],
+ {nor, is, tuple},
+ ok.
+
+%%%
+%%% Indentation
+%%%
+
+%%% Left
+
+%% Indented
+
+% Right
+
+
+indent_basics(X, Y, Z)
+ when X > 42,
+Z < 13;
+Y =:= 4711 ->
+ %% comments
+ % right comments
+ case lists:filter(fun(_, AlongName,
+ B,
+ C) ->
+ true
+ end,
+ [a,v,b])
+ of
+ [] ->
+ Y = 5 * 43,
+ ok;
+ [_|_] ->
+ Y = 5 * 43,
+ ok
+ end,
+ Y,
+ %% List, tuples and binaries
+ [a,
+ b, c
+ ],
+ [ a,
+ b, c
+ ],
+
+ [
+ a,
+ b
+],
+ {a,
+ b,c
+ },
+ { a,
+ b,c
+ },
+
+ {
+ a,
+ b
+ },
+
+<<1:8,
+ 2:8
+ >>,
+ <<
+ 1:8,
+ 2:8
+ >>,
+ << 1:8,
+ 2:8
+ >>,
+
+ (a,
+ b,
+ c
+ ),
+
+ ( a,
+ b,
+ c
+ ),
+
+
+ (
+ a,
+ b,
+ c
+ ),
+
+
+ ok;
+indent_basics(Xlongname,
+ #struct{a=Foo,
+ b=Bar},
+ [X|
+ Y]) ->
+ testing_next_clause,
+ ok;
+indent_basics( % AD added clause
+ X, % not sure how this should look
+ Y,
+ Z)
+ when
+ X < 42, Z > 13;
+ Y =:= 4711 ->
+ foo;
+indent_basics(X, Y, Z) when % AD added clause
+ X < 42, Z > 13; % testing when indentation
+ Y =:= 4711 ->
+ foo;
+indent_basics(X, Y, Z) % AD added clause
+ when % testing when indentation
+ X < 42, Z > 13; % unsure about this one
+ Y =:= 4711 ->
+ foo.
+
+
+indent_icr(Z) -> % icr = if case receive
+ %% If
+ if Z >= 0 ->
+ X = 43 div 4,
+ foo(X);
+ Z =< 10 ->
+ X = 43 div 4,
+ foo(X);
+ Z == 5 orelse
+ Z == 7 ->
+ X = 43 div 4,
+ foo(X);
+ true ->
+ if_works
+ end,
+ %% Case
+ case {Z, foo, bar} of
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_} when
+ Z =:= 42 -> % AD line should be indented as a when
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_}
+ when Z < 10 -> % AD when should be indented
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_}
+ when % AD when should be indented
+ Z < 10 % and the guards should follow when
+ andalso % unsure about how though
+ true ->
+ X = 43 div 4,
+ foo(X)
+ end,
+ %% begin
+ begin
+ sune,
+ X = 74234 + foo(8456) +
+ 345 div 43,
+ ok
+ end,
+
+
+ %% receive
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ Z ->
+ X = 43 div 4,
+ foo(X)
+ end,
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ Z % AD added clause
+ when Z =:= 1 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z when % AD added clause
+ Z =:= 2 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z ->
+ X = 43 div 4,
+ foo(X)
+ after infinity ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ receive
+ after 10 ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ ok.
+
+indent_fun() ->
+ %% Changed fun to one indention level
+Var = spawn(fun(X)
+ when X == 2;
+ X > 10 ->
+ hello,
+ case Hello() of
+ true when is_atom(X) ->
+ foo;
+ false ->
+ bar
+ end;
+ (Foo) when is_atom(Foo),
+ is_integer(X) ->
+ X = 6* 45,
+ Y = true andalso
+ kalle
+ end),
+ ok.
+
+indent_try_catch() ->
+ try
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R % AD added clause
+ when R =:= 42 -> % when should be indented
+ foo(R);
+ error:R % AD added clause
+ when % when should be indented
+ R =:= 42 -> % but unsure about this (maybe 2 more)
+ foo(R);
+ error:R when % AD added clause
+ R =:= foo -> % line should be 2 indented (works)
+ foo(R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile)
+ end;
+indent_try_catch() ->
+ try foo(bar) of
+ X when true andalso
+ kalle ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2);
+ X % AD added clause
+ when false andalso % when should be 2 indented
+ bengt ->
+ gurka();
+ X when % AD added clause
+ false andalso % line should be 2 indented
+ not bengt ->
+ gurka();
+ X ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile),
+ bar(with_long_arg,
+ with_second_arg)
+ end;
+ indent_try_catch() ->
+ try foo()
+ after
+ foo(),
+ bar(with_long_arg,
+ with_second_arg)
+ end.
+
+indent_catch() ->
+ D = B +
+ float(43.1),
+
+ B = catch oskar(X),
+
+ A = catch (baz +
+ bax),
+ catch foo(),
+
+ C = catch B +
+ float(43.1),
+
+ case catch (X) of
+ A ->
+ B
+ end,
+ try sune of
+ _ -> foo
+ catch _:_ -> baf
+ end.
+
+indent_binary() ->
+ X = lists:foldr(fun(M) ->
+ <<Ma/binary, " ">>
+ end, [], A),
+ A = <<X/binary, 0:8>>,
+ B.
+
+
+indent_comprehensions() ->
+%% I don't have a good idea how we want to handle this
+%% but they are here to show how they are indented today.
+Result1 = [X ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ ],
+Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ ],
+
+Binary1 = << <<X:8>> ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ >>,
+
+Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ >>,
+ok.
diff --git a/lib/tools/emacs/vsn.mk b/lib/tools/emacs/vsn.mk
new file mode 100644
index 0000000000..f33ea8b519
--- /dev/null
+++ b/lib/tools/emacs/vsn.mk
@@ -0,0 +1,3 @@
+
+EMACS_VSN = 2.4.13
+