5450 lines
181 KiB
EmacsLisp
5450 lines
181 KiB
EmacsLisp
|
;;; erlang.el --- Major modes for editing and running Erlang
|
|||
|
|
|||
|
;; Copyright (C) 1995-1998,2000 Ericsson Telecom AB
|
|||
|
|
|||
|
;; Author: Anders Lindgren
|
|||
|
;; Version: 2.4.1
|
|||
|
;; Keywords: erlang, languages, processes
|
|||
|
;; Date: 2000-09-11
|
|||
|
|
|||
|
;; Lars Thorsýn's modifications of 2000-06-07 included.
|
|||
|
|
|||
|
;; The original version of this package was written by Robert Virding.
|
|||
|
;;
|
|||
|
;; Most skeletons has been written at Ericsson Telecom by
|
|||
|
;; magnus@erix.ericsson.se and janne@erix.ericsson.se
|
|||
|
|
|||
|
;;; 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:
|
|||
|
;; support@erlang.ericsson.se
|
|||
|
;;
|
|||
|
;; 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.4.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 ending slash.
|
|||
|
|
|||
|
Should this variable be nil, no manual pages will show up in the
|
|||
|
Erlang mode menu.")
|
|||
|
|
|||
|
(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 Erland mode menu.
|
|||
|
|
|||
|
External programs which temporary adds menu items to the Erland mode
|
|||
|
menu 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)))
|
|||
|
("Syntax Highlighting"
|
|||
|
(("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 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.")
|
|||
|
|
|||
|
(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 is runed every time Erlang mode is
|
|||
|
started. See also `erlang-load-hook', a hook which is runed 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 runed.
|
|||
|
|
|||
|
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 loation 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' packags 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)
|
|||
|
"*Command 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-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 reindent 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 idea to create the menu.")
|
|||
|
|
|||
|
(defvar erlang-man-dirs
|
|||
|
'(("Man - Commands" "/man/man1" t)
|
|||
|
("Man - Modules" "/man/man3" t)
|
|||
|
("Man - Unsupported" "/uc/man/man3" 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 20
|
|||
|
"*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.")
|
|||
|
|
|||
|
(defconst erlang-atom-regexp "\\([a-z][a-zA-Z0-9_]*\\|'[^\n']*[^\\]'\\)"
|
|||
|
"Regexp which should match an Erlang atom.
|
|||
|
|
|||
|
The regexp must be surrounded with a pair of regexp parentheses.")
|
|||
|
(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'.")
|
|||
|
|
|||
|
(defconst erlang-variable-regexp "\\([A-Z_][a-zA-Z0-9_]*\\)"
|
|||
|
"Regexp which should match an Erlang variable.
|
|||
|
|
|||
|
The regexp must be surrounded with a pair of regexp parenthesis.")
|
|||
|
(defconst erlang-variable-regexp-matches 1
|
|||
|
"Number of regexp parenthesis pairs in `erlang-variable-regexp'.
|
|||
|
|
|||
|
This is used to determine matches in complex rexeps which contains
|
|||
|
`erlang-variable-regexp'.")
|
|||
|
|
|||
|
(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 interpretate tags on the form `module:tag' for
|
|||
|
files written in other languages than Erlang.")
|
|||
|
|
|||
|
(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 erlang-emacs-major-version
|
|||
|
(if (boundp 'emacs-major-version)
|
|||
|
emacs-major-version
|
|||
|
(string-match "\\([0-9]+\\)\\.\\([0-9]+\\)" emacs-version)
|
|||
|
(string-to-int (substring emacs-version
|
|||
|
(match-beginning 1) (match-end 1))))
|
|||
|
"Major version number of Emacs.")
|
|||
|
|
|||
|
(defconst erlang-emacs-minor-version
|
|||
|
(if (boundp 'emacs-minor-version)
|
|||
|
emacs-minor-version
|
|||
|
(string-match "\\([0-9]+\\)\\.\\([0-9]+\\)" emacs-version)
|
|||
|
(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 effekt in all
|
|||
|
buffers in Erlang mode, just like under GNU Emacs.
|
|||
|
|
|||
|
Never EVER set this variable!")
|
|||
|
|
|||
|
|
|||
|
;; Tempo skeleton templates:
|
|||
|
|
|||
|
(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))
|
|||
|
"*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 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 horisontal line should
|
|||
|
be placed in the menu.")
|
|||
|
|
|||
|
;; In XEmacs `user-mail-address' returns "x@y.z (Foo Bar)" ARGH!
|
|||
|
;; What's wrong with that? RFC 822 says it's legal. [sverkerw]
|
|||
|
(defvar erlang-skel-mail-address
|
|||
|
(concat (user-login-name) "@"
|
|||
|
(or (and (boundp 'mail-host-address)
|
|||
|
(symbol-value 'mail-host-address))
|
|||
|
(system-name)))
|
|||
|
"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 `recieve' 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
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Include files" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% External exports" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-export([" n> "start/2," n>
|
|||
|
"stop/1" n
|
|||
|
" ])." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Internal exports" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-export([" n
|
|||
|
" ])." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Macros" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Records" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
"%% External functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
(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-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
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Include files" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% External exports" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-export([" n> "start_link/0" n
|
|||
|
" ])." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Internal exports" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-export([" n> "init/1" n
|
|||
|
" ])." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Macros" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-define(SERVER, ?MODULE)." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Records" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
"%% External functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Function: start_link/0" 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)
|
|||
|
"%% Server functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Func: init/1" n
|
|||
|
"%% Returns: {ok, {SupFlags, [ChildSpec]}} |" n
|
|||
|
"%% ignore |" n
|
|||
|
"%% {error, Reason} " 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
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Include files" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% External exports" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-export([" n> "start_link/0" n
|
|||
|
" ])." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Internal exports" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-export([" n> "init/1, " n> "terminate/2" n
|
|||
|
" ])." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Macros" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-define(SERVER, ?MODULE)." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Records" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-record(state, {})." n
|
|||
|
n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
"%% External functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Function: start_link/0" 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)
|
|||
|
"%% Server functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
(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-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
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Include files" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% 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, code_change/3])." n n
|
|||
|
"-record(state, {})." n
|
|||
|
n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
"%% External functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Function: start_link/0" 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)
|
|||
|
"%% Server functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Function: init/1" n
|
|||
|
"%% Description: Initiates the server" 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)
|
|||
|
"%% Function: handle_call/3" n
|
|||
|
"%% Description: Handling call messages" 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)
|
|||
|
"%% Function: handle_cast/2" n
|
|||
|
"%% Description: Handling cast messages" 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)
|
|||
|
"%% Function: handle_info/2" n
|
|||
|
"%% Description: Handling all non call/cast messages" 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)
|
|||
|
"%% Function: terminate/2" n
|
|||
|
"%% Description: 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 2)
|
|||
|
"%% Func: code_change/3" n
|
|||
|
"%% Purpose: Convert process state when code is changed" n
|
|||
|
"%% Returns: {ok, NewState}" 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
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Include files" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% 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, code_change/3])." n n
|
|||
|
"-record(state, {})." n
|
|||
|
n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
"%% External functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Function: start_link/0" n
|
|||
|
"%% Description: Starts the server" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"start_link() ->" n>
|
|||
|
"gen_event:start_link({local, ?SERVER}). " n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Function: add_handler/0" 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)
|
|||
|
"%% Server functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
(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 2)
|
|||
|
"%% Func: code_change/3" n
|
|||
|
"%% Purpose: Convert process state when code is changed" n
|
|||
|
"%% Returns: {ok, NewState}" 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
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Include files" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% 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, code_change/4])." n n
|
|||
|
"-record(state, {})." n
|
|||
|
n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
"%% External functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Function: start_link/0" n
|
|||
|
"%% Description: Starts the server" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"start_link() ->" n>
|
|||
|
"gen_fsm:start_link({local, ?SERVER}, ?MODULE, [], [])." n
|
|||
|
n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
"%% Server functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
(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>
|
|||
|
"{next_state, 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>
|
|||
|
"{next_state, 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>
|
|||
|
"{next_state, 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 2)
|
|||
|
"%% Func: code_change/4" n
|
|||
|
"%% Purpose: Convert process state when code is changed" n
|
|||
|
"%% Returns: {ok, NewState, NewStateData}" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"code_change(OldVsn, StateName, StateData, Extra) ->" n>
|
|||
|
"{ok, StateName, StateData}." 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)
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Include files" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% External exports" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-export([" n
|
|||
|
" ])." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Internal exports" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-export([" n
|
|||
|
" ])." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Macros" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Records" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
"%% External functions" 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)
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Include files" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% External exports" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-export([" n> "init/1, " n> "terminate/2," n> "code_change/3" n
|
|||
|
" ])." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Internal exports" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-export([" n
|
|||
|
" ])." n
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Macros" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Records" n
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"-record(state, {})." n
|
|||
|
n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
"%% External functions" n
|
|||
|
(erlang-skel-double-separator 2)
|
|||
|
(erlang-skel-separator 2)
|
|||
|
"%% Function: init/1" n
|
|||
|
"%% Description: Initiates the server" 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)
|
|||
|
"%% Function: terminate/2" n
|
|||
|
"%% Description: 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 2)
|
|||
|
"%% Function: code_change/3" n
|
|||
|
"%% Description: Convert process state when code is changed" n
|
|||
|
"%% Returns: {ok, NewState}" 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'.")
|
|||
|
|
|||
|
|
|||
|
|
|||
|
;; Font-lock variables
|
|||
|
|
|||
|
(defvar erlang-font-lock-modern-p
|
|||
|
(cond ((>= erlang-emacs-major-version 20) t)
|
|||
|
(erlang-xemacs-p (>= erlang-emacs-minor-version 14))
|
|||
|
((= erlang-emacs-major-version 19) (>= erlang-emacs-minor-version 29))
|
|||
|
(t nil))
|
|||
|
"Non-nil when this version of Emacs uses a modern version of Font Lock.
|
|||
|
|
|||
|
This is determinated by checking the version of Emacs used, the actual
|
|||
|
font-lock code is not loaded.")
|
|||
|
|
|||
|
|
|||
|
;; The next few variables define different Erlang font-lock patterns.
|
|||
|
;; They could be appended to form a custom font-lock appearence.
|
|||
|
;;
|
|||
|
;; The function `erlang-font-lock-set-face' could be used to change
|
|||
|
;; the face of a pattern.
|
|||
|
;;
|
|||
|
;; Note that Erlang strings and atoms are hightlighted with using
|
|||
|
;; syntactix analysis.
|
|||
|
|
|||
|
(defvar erlang-font-lock-keywords-func
|
|||
|
(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-dollar
|
|||
|
(list
|
|||
|
(list "\\(\\$\\([^\\]\\|\\\\\\([^0-7^\n]\\|[0-7]+\\|\\^[a-zA-Z]\\)\\)\\)"
|
|||
|
1 'font-lock-string-face))
|
|||
|
"Font lock keyword highlighting numbers in ascii-form (e.g. $A).")
|
|||
|
|
|||
|
(defvar erlang-font-lock-keywords-arrow
|
|||
|
(list
|
|||
|
(list "\\(->\\|:-\\)\\(\\s \\|$\\)" 2 '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)
|
|||
|
(list "\\(||\\)\\(\\s \\|$\\)" 1 'font-lock-keyword-face))
|
|||
|
"Font lock keyword highlighting list comprehension operators.")
|
|||
|
|
|||
|
(defvar erlang-font-lock-keywords-keywords
|
|||
|
(list
|
|||
|
(list (concat "\\<\\(after\\|begin\\|c\\(atch\\|ase\\)\\|end\\|fun\\|if"
|
|||
|
"\\|of\\|receive\\|when\\|andalso\\|orelse\\|query\\)\\([^a-zA-Z0-9_]\\|$\\)")
|
|||
|
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 'font-lock-function-name-face))
|
|||
|
"Font lock keyword highlighting attribues.")
|
|||
|
|
|||
|
(defvar erlang-font-lock-keywords-quotes
|
|||
|
(list
|
|||
|
(list "`\\([-+a-zA-Z0-9_:*][-+a-zA-Z0-9_:*]+\\)'"
|
|||
|
1
|
|||
|
(if erlang-font-lock-modern-p
|
|||
|
'font-lock-reference-face
|
|||
|
'font-lock-keyword-face)
|
|||
|
t))
|
|||
|
"Font lock keyword highlighting words in single quotes in comments.
|
|||
|
|
|||
|
This is not the keyword hightlighting Erlang strings and atoms, they
|
|||
|
are highlighted by syntactic analysis.")
|
|||
|
|
|||
|
;; Note: The guard `float' collides with the bif `float'.
|
|||
|
(defvar erlang-font-lock-keywords-guards
|
|||
|
(list
|
|||
|
(list
|
|||
|
(concat "\\<\\("
|
|||
|
"\\(is_\\)*\\(atom\\|function\\|binary\\|constant\\|float"
|
|||
|
"\\|integer\\|list\\|number\\|p\\(id\\|ort\\)\\|"
|
|||
|
"re\\(ference\\|cord\\)\\|tuple"
|
|||
|
"\\)\\)\\s *(")
|
|||
|
|
|||
|
1
|
|||
|
(if erlang-font-lock-modern-p
|
|||
|
'font-lock-reference-face
|
|||
|
'font-lock-keyword-face)))
|
|||
|
"Font lock keyword highlighting guards.")
|
|||
|
|
|||
|
(defvar erlang-font-lock-keywords-bifs
|
|||
|
(list
|
|||
|
(list
|
|||
|
(concat
|
|||
|
"\\<\\("
|
|||
|
"a\\(bs\\|live\\|pply\\|tom_to_list\\)\\|"
|
|||
|
"binary_to_\\(list\\|term\\)\\|"
|
|||
|
"concat_binary\\|d\\(ate\\|isconnect_node\\)\\|"
|
|||
|
"e\\(lement\\|rase\\|xit\\)\\|"
|
|||
|
"float\\(\\|_to_list\\)\\|"
|
|||
|
"g\\(arbage_collect\\|et\\(\\|_keys\\)\\|roup_leader\\)\\|"
|
|||
|
"h\\(alt\\|d\\)\\|"
|
|||
|
"i\\(nte\\(ger_to_list\\|rnal_bif\\)\\|s_alive\\)\\|"
|
|||
|
"l\\(ength\\|i\\(nk\\|st_to_\\(atom\\|binary\\|float\\|integer"
|
|||
|
"\\|pid\\|tuple\\)\\)\\)\\|"
|
|||
|
"make_ref\\|no\\(de\\(\\|_\\(link\\|unlink\\)\\|s\\)\\|talive\\)\\|"
|
|||
|
"open_port\\|"
|
|||
|
"p\\(id_to_list\\|rocess\\(_\\(flag\\|info\\)\\|es\\)\\|ut\\)\\|"
|
|||
|
"r\\(egister\\(\\|ed\\)\\|ound\\)\\|"
|
|||
|
"s\\(e\\(lf\\|telement\\)\\|ize\\|"
|
|||
|
"p\\(awn\\(\\|_link\\)\\|lit_binary\\)\\|tatistics\\)\\|"
|
|||
|
"t\\(erm_to_binary\\|hrow\\|ime\\|l\\|"
|
|||
|
"r\\(ace\\|unc\\)\\|uple_to_list\\)\\|"
|
|||
|
"un\\(link\\|register\\)\\|whereis"
|
|||
|
"\\)\\s *(")
|
|||
|
1
|
|||
|
'font-lock-keyword-face))
|
|||
|
"Font lock keyword highlighting built in functions.")
|
|||
|
|
|||
|
(defvar erlang-font-lock-keywords-macros
|
|||
|
(list
|
|||
|
(list (concat "?\\s *\\(" erlang-atom-regexp
|
|||
|
"\\|" erlang-variable-regexp "\\)\\>")
|
|||
|
1 (if erlang-font-lock-modern-p
|
|||
|
'font-lock-reference-face
|
|||
|
'font-lock-type-face))
|
|||
|
(list (concat "^-\\(define\\|ifn?def\\)\\s *(\\s *\\(" erlang-atom-regexp
|
|||
|
"\\|" erlang-variable-regexp "\\)\\>")
|
|||
|
2 (if erlang-font-lock-modern-p
|
|||
|
'font-lock-reference-face
|
|||
|
'font-lock-type-face)))
|
|||
|
"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 "\\<[0-9][0-9]?#\\([0-9a-fA_F]+\\)\\>"
|
|||
|
1 nil t)
|
|||
|
(list (concat "^-record(\\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 "\\>")
|
|||
|
1 (if erlang-font-lock-modern-p
|
|||
|
'font-lock-variable-name-face
|
|||
|
'font-lock-type-face)))
|
|||
|
"Font lock keyword highlighting Erlang variables.
|
|||
|
Must be preceded by `erlang-font-lock-keywords-macros' and `-records'
|
|||
|
to work properly.")
|
|||
|
|
|||
|
|
|||
|
(defvar erlang-font-lock-keywords-1
|
|||
|
(append erlang-font-lock-keywords-func
|
|||
|
erlang-font-lock-keywords-dollar
|
|||
|
erlang-font-lock-keywords-arrow
|
|||
|
erlang-font-lock-keywords-keywords)
|
|||
|
;; DocStringOrig: erlang-font-lock-keywords
|
|||
|
"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 `singel quotes'.
|
|||
|
`erlang-font-lock-keywords-3' - Variables, macros and records.
|
|||
|
|
|||
|
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-2
|
|||
|
(append erlang-font-lock-keywords-1
|
|||
|
erlang-font-lock-keywords-attr
|
|||
|
erlang-font-lock-keywords-quotes
|
|||
|
erlang-font-lock-keywords-guards
|
|||
|
erlang-font-lock-keywords-bifs)
|
|||
|
;; DocStringCopy: erlang-font-lock-keywords
|
|||
|
"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.
|
|||
|
|
|||
|
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-3
|
|||
|
(append erlang-font-lock-keywords-2
|
|||
|
erlang-font-lock-keywords-macros
|
|||
|
erlang-font-lock-keywords-records
|
|||
|
erlang-font-lock-keywords-vars)
|
|||
|
;; DocStringCopy: erlang-font-lock-keywords
|
|||
|
"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.
|
|||
|
|
|||
|
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 erlang-font-lock-keywords-3
|
|||
|
;; DocStringCopy: erlang-font-lock-keywords
|
|||
|
"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.
|
|||
|
|
|||
|
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-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.
|
|||
|
|
|||
|
Unfortuantely, 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)))))))
|
|||
|
|
|||
|
(or (fboundp 'regexp-opt)
|
|||
|
(defun regexp-opt (strings &optional paren)
|
|||
|
"Return a regular expression that matches any string in
|
|||
|
STRINGS. If PAREN is true, it will always enclose the regular
|
|||
|
expression in parentheses.
|
|||
|
|
|||
|
Unlike its Emacs-20 namesake, it will not optimize the generated
|
|||
|
expression."
|
|||
|
;; This stop-gap definition is taken from
|
|||
|
;; _GNU_Emacs_Lisp_Reference_Manual_, ed 2.5, for Emacs 20.3.
|
|||
|
(let ((open (if paren "\\(" ""))
|
|||
|
(close (if paren "\\)" "")))
|
|||
|
(concat open
|
|||
|
(mapconcat 'regexp-quote strings "\\|")
|
|||
|
close))))
|
|||
|
|
|||
|
(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 '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)
|
|||
|
(run-hooks 'erlang-mode-hook)
|
|||
|
(if (zerop (buffer-size))
|
|||
|
(run-hooks 'erlang-new-file-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)
|
|||
|
(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)
|
|||
|
(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)
|
|||
|
(define-key map "\177" 'backward-delete-char-untabify)
|
|||
|
(define-key map "\M-q" 'erlang-fill-paragraph)
|
|||
|
(define-key map "\M-\C-a" 'erlang-beginning-of-function)
|
|||
|
(define-key map "\M-\C-e" 'erlang-end-of-function)
|
|||
|
(define-key map "\M-\C-h" 'erlang-mark-function)
|
|||
|
(define-key map "\M-\t" 'erlang-complete-tag)
|
|||
|
(define-key map "\C-c\M-\t" 'tempo-complete-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-z" 'erlang-shell-display)
|
|||
|
(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.
|
|||
|
(mapcar #'(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))
|
|||
|
(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_]*\\)\\="))
|
|||
|
|
|||
|
|
|||
|
;; Compilation.
|
|||
|
;;
|
|||
|
;; The following code is compatible with the standard package `compilation',
|
|||
|
;; making it possible to go to errors using `erlang-next-error'.
|
|||
|
;;
|
|||
|
;; The normal `compile' command works ofcourse. 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))
|
|||
|
(t
|
|||
|
(set 'font-lock-keywords erlang-font-lock-keywords-3))))
|
|||
|
|
|||
|
;; Modern font-locks can handle the above much more elegant:
|
|||
|
(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)
|
|||
|
nil nil ((?_ . "w")) erlang-beginning-of-clause
|
|||
|
(font-lock-mark-block-function . erlang-mark-clause))))
|
|||
|
|
|||
|
|
|||
|
|
|||
|
;; Useful when definig yout 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 does only handle 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-func
|
|||
|
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
|
|||
|
"Fontify current buffer. Level ranges from 0 (off) to 3 (Christmas Tree).
|
|||
|
|
|||
|
The following fontification level exists:
|
|||
|
0 - No fontification
|
|||
|
1 - Function headers, reserved keywords, strings and comments.
|
|||
|
2 - Bifs, guards and `single quotes'.
|
|||
|
3 - Variables, macros and records.
|
|||
|
|
|||
|
To automatically activate font lock mode, place the following lines
|
|||
|
in your ~/.emacs file:
|
|||
|
|
|||
|
\(defun my-erlang-mode-hook ()
|
|||
|
(cond (window-system
|
|||
|
(font-lock-mode 1))))
|
|||
|
\(add-hook 'erlang-mode-hook 'my-erlang-mode-hook)
|
|||
|
\(setq font-lock-maximum-decoration t)"
|
|||
|
(interactive)
|
|||
|
(font-lock-mode 0))
|
|||
|
|
|||
|
|
|||
|
(defun erlang-font-lock-level-1 ()
|
|||
|
;; DocStringCopy: font-cmd
|
|||
|
"Fontify current buffer. Level ranges from 0 (off) to 3 (Christmas Tree).
|
|||
|
|
|||
|
The following fontification level exists:
|
|||
|
0 - No fontification
|
|||
|
1 - Function headers, reserved keywords, strings and comments.
|
|||
|
2 - Bifs, guards and `single quotes'.
|
|||
|
3 - Variables, macros and records.
|
|||
|
|
|||
|
To automatically activate font lock mode, place the following lines
|
|||
|
in your ~/.emacs file:
|
|||
|
|
|||
|
\(defun my-erlang-mode-hook ()
|
|||
|
(cond (window-system
|
|||
|
(font-lock-mode 1))))
|
|||
|
\(add-hook 'erlang-mode-hook 'my-erlang-mode-hook)
|
|||
|
\(setq font-lock-maximum-decoration t)"
|
|||
|
(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. Level ranges from 0 (off) to 3 (Christmas Tree).
|
|||
|
|
|||
|
The following fontification level exists:
|
|||
|
0 - No fontification
|
|||
|
1 - Function headers, reserved keywords, strings and comments.
|
|||
|
2 - Bifs, guards and `single quotes'.
|
|||
|
3 - Variables, macros and records.
|
|||
|
|
|||
|
To automatically activate font lock mode, place the following lines
|
|||
|
in your ~/.emacs file:
|
|||
|
|
|||
|
\(defun my-erlang-mode-hook ()
|
|||
|
(cond (window-system
|
|||
|
(font-lock-mode 1))))
|
|||
|
\(add-hook 'erlang-mode-hook 'my-erlang-mode-hook)
|
|||
|
\(setq font-lock-maximum-decoration t)"
|
|||
|
(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. Level ranges from 0 (off) to 3 (Christmas Tree).
|
|||
|
|
|||
|
The following fontification level exists:
|
|||
|
0 - No fontification
|
|||
|
1 - Function headers, reserved keywords, strings and comments.
|
|||
|
2 - Bifs, guards and `single quotes'.
|
|||
|
3 - Variables, macros and records.
|
|||
|
|
|||
|
To automatically activate font lock mode, place the following lines
|
|||
|
in your ~/.emacs file:
|
|||
|
|
|||
|
\(defun my-erlang-mode-hook ()
|
|||
|
(cond (window-system
|
|||
|
(font-lock-mode 1))))
|
|||
|
\(add-hook 'erlang-mode-hook 'my-erlang-mode-hook)
|
|||
|
\(setq font-lock-maximum-decoration t)"
|
|||
|
(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-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 extentions could be added to `erlang-menu-personal-items'.
|
|||
|
|
|||
|
Should any variable describing the menu configuration, this function
|
|||
|
should be called."
|
|||
|
(erlang-menu-install "Erlang" erlang-menu-items erlang-mode-map t))
|
|||
|
|
|||
|
|
|||
|
(defun erlang-menu-install (name items keymap &optional popup)
|
|||
|
"Install a menu on Emacs 19 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 horisontal
|
|||
|
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 meny is the global, the local, or an
|
|||
|
activated 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 occurence 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 occurence 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 19,
|
|||
|
;; 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 beatiful
|
|||
|
;; 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 ".*/\\([^/]+\\)\\.[^.]$" 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 ".*\\.[0-9]\\'"))
|
|||
|
|
|||
|
|
|||
|
(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 "\\b" (regexp-quote module) "\\.[^.]$"))
|
|||
|
(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
|
|||
|
;; choosed to keep it since it provides a very useful functionality
|
|||
|
;; which is not possible to achive using a clean approach.
|
|||
|
;; / AndersL
|
|||
|
|
|||
|
(defvar erlang-man-function-name nil
|
|||
|
"Name of function for last `erlang-man-function' call.
|
|||
|
Used for commnication 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
|
|||
|
;; asyncronous 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 happend since this is only called when
|
|||
|
;; running under Emacs 19.
|
|||
|
(error (error (concat "This commands needs the package `advice', "
|
|||
|
"please upgrade your Emacs."))))
|
|||
|
(require 'man)
|
|||
|
(defadvice Man-notify-when-ready
|
|||
|
(after erlang-Man-notify-when-ready activate)
|
|||
|
"Sets 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 de 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[^/]*/\\([^/]+\\)\\.[^.]$" 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)))
|
|||
|
(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)))
|
|||
|
|
|||
|
Techically, 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))))
|
|||
|
|
|||
|
(defun erlang-skel-separator (&optional percent)
|
|||
|
"Return a comment separator."
|
|||
|
(let ((percent (or percent 3)))
|
|||
|
(concat (make-string percent ?%)
|
|||
|
(make-string (- 70 percent) ?-)
|
|||
|
"\n")))
|
|||
|
|
|||
|
(defun erlang-skel-double-separator (&optional percent)
|
|||
|
"Return a comment separator."
|
|||
|
(let ((percent (or percent 3)))
|
|||
|
(concat (make-string percent ?%)
|
|||
|
(make-string (- 70 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"
|
|||
|
(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)
|
|||
|
(if (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)))
|
|||
|
(erlang-pop stack))))
|
|||
|
(cond ((looking-at
|
|||
|
"\\(if\\|case\\|receive\\|after\\)[^_a-zA-Z0-9]")
|
|||
|
;; Must push a new icr (if/case/receive) layer.
|
|||
|
(erlang-push (list 'icr token (current-column)) stack))
|
|||
|
((looking-at "\\(fun\\)[^_a-zA-Z0-9]")
|
|||
|
;; Puch a new icr layer if we are defining a `fun'
|
|||
|
;; expression, not when we are refering an existing
|
|||
|
;; function.
|
|||
|
(if (save-excursion
|
|||
|
(goto-char (match-end 1))
|
|||
|
(erlang-skip-blank to)
|
|||
|
(eq (following-char) ?\())
|
|||
|
(erlang-push (list 'icr token (current-column)) stack)))
|
|||
|
((looking-at "\\(begin\\|query\\)[^_a-zA-Z0-9]")
|
|||
|
(erlang-push (list 'begin token (current-column)) stack))
|
|||
|
((looking-at "when[^_a-zA-Z0-9][^->\.]*->")
|
|||
|
(erlang-push (list 'when 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))))
|
|||
|
|
|||
|
;; Symbol constituent, punctuation, or expression prefix?
|
|||
|
((memq cs '(?. ?_ ?'))
|
|||
|
(cond
|
|||
|
|
|||
|
;; Clause end
|
|||
|
((= (following-char) ?\;)
|
|||
|
(if (and stack (eq (car (car stack)) '->))
|
|||
|
(erlang-pop stack))
|
|||
|
(forward-char 1))
|
|||
|
|
|||
|
;; 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))
|
|||
|
|
|||
|
;; Parameter separator
|
|||
|
((looking-at ",")
|
|||
|
(forward-char 1))
|
|||
|
|
|||
|
;; 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))
|
|||
|
(error "Missing `end'"))
|
|||
|
(t
|
|||
|
(error "Unbalanced parentheses")))
|
|||
|
(forward-char 2))
|
|||
|
|
|||
|
;; Macro
|
|||
|
((= (following-char) ??)
|
|||
|
;; Skip over macro name and any following whitespace.
|
|||
|
(forward-word 1)
|
|||
|
(skip-syntax-forward "-" to)
|
|||
|
;; Macro might have an argument list. Should be handled like
|
|||
|
;; an ordinary function argument list in consecutive calls
|
|||
|
;; to erlang-partial-parse.
|
|||
|
)
|
|||
|
|
|||
|
;; 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)) '(|| ->))
|
|||
|
(erlang-pop stack))
|
|||
|
(cond ((eq (car (car stack)) '\()
|
|||
|
(erlang-pop stack))
|
|||
|
((memq (car (car stack)) '(icr 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,
|
|||
|
(if (null erlang-argument-indent)
|
|||
|
;; indent to next column.
|
|||
|
(1+ (nth 2 stack-top))
|
|||
|
(goto-char (nth 1 stack-top))
|
|||
|
(cond ((looking-at "[({]\\s *\\($\\|%\\)")
|
|||
|
;; Line ends with parenthesis.
|
|||
|
(+ (erlang-indent-find-preceding-expr)
|
|||
|
erlang-argument-indent))
|
|||
|
(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) 'icr)
|
|||
|
;; 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 indentated to the save level as the
|
|||
|
;; corresponding receive.
|
|||
|
(if (looking-at "after[^_a-zA-Z0-9]")
|
|||
|
(nth 2 stack-top)
|
|||
|
(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))))))
|
|||
|
;; Real indentation, where operators create extra indentation etc.
|
|||
|
((memq (car stack-top) '(-> || begin))
|
|||
|
(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) '->)
|
|||
|
(setq off (* 2 erlang-indent-level))))
|
|||
|
(let ((base (erlang-indent-find-base stack indent-point off skip)))
|
|||
|
;; 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?)
|
|||
|
((looking-at "\\(end\\|after\\)[^_a-zA-Z0-9]\\|$")
|
|||
|
(if (eq (car (car stack)) '->)
|
|||
|
(erlang-pop stack))
|
|||
|
(if stack
|
|||
|
(erlang-caddr (car stack))
|
|||
|
0))
|
|||
|
;; Avoid trating 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 (eq (nth 0 (car stack)) 'icr))
|
|||
|
(progn
|
|||
|
(goto-char (nth 1 (car stack)))
|
|||
|
(+ (nth 2 (car stack)) erlang-indent-guard
|
|||
|
;; receive XYZ or receive
|
|||
|
;; XYZ
|
|||
|
(if (looking-at "[a-z]+\\s *\\($\\|%\\)")
|
|||
|
erlang-indent-level
|
|||
|
(* 2 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))))))
|
|||
|
|
|||
|
|
|||
|
(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))
|
|||
|
(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)))))
|
|||
|
|
|||
|
|
|||
|
;; Does not handle `begin' .. `end'.
|
|||
|
(defun erlang-indent-find-preceding-expr ()
|
|||
|
"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
|
|||
|
(forward-sexp -1)
|
|||
|
(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-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\\)[^_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 wants 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)))))
|
|||
|
|
|||
|
(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 wants 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")
|
|||
|
(comment-region beg end -1))
|
|||
|
|
|||
|
|
|||
|
(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 (concat ") " 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 retreival 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
|
|||
|
(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 ()
|
|||
|
"Parses list of `function/arity' as used by `-import' and `-export'.
|
|||
|
|
|||
|
The point must be placed at 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 (concat erlang-atom-regexp
|
|||
|
"/\\([0-9]+\\)\\>"))
|
|||
|
(progn
|
|||
|
(setq res (cons
|
|||
|
(cons
|
|||
|
(erlang-remove-quotes
|
|||
|
(erlang-buffer-substring
|
|||
|
(match-beginning 1) (match-end 1)))
|
|||
|
(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 conjuction 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 (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.
|
|||
|
|
|||
|
The \":-\" arrow is used by mnesia queries.
|
|||
|
|
|||
|
Normally used in conjuction 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 (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 (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 retured:
|
|||
|
(\"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 (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.
|
|||
|
(defun erlang-add-quotes-if-needed (str)
|
|||
|
"Return STR, possibly with quotes."
|
|||
|
(if (and (stringp str)
|
|||
|
(not (string-match (concat "\\`" erlang-atom-regexp "\\'") str)))
|
|||
|
(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 (match-beginning 1) (match-end 1))
|
|||
|
str)
|
|||
|
(store-match-data md))))
|
|||
|
|
|||
|
|
|||
|
;;; Check module name
|
|||
|
|
|||
|
;; I don't want to use `advice' since it is not part of Emacs 18.
|
|||
|
;;
|
|||
|
;; 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 add a piece of
|
|||
|
;; code to the function `set-visited-file-name'.
|
|||
|
(defun erlang-check-module-name-init ()
|
|||
|
"Initialize the functionality to compare file and module names.
|
|||
|
|
|||
|
We redefines 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 (fboundp 'erlang-orig-set-visited-file-name)
|
|||
|
()
|
|||
|
(fset 'erlang-orig-set-visited-file-name
|
|||
|
(symbol-function 'set-visited-file-name))
|
|||
|
(defun set-visited-file-name (&rest args)
|
|||
|
"Please see the function `erlang-orig-set-visited-file-name'."
|
|||
|
(interactive "FSet visited file name: ")
|
|||
|
(apply (symbol-function 'erlang-orig-set-visited-file-name) args)
|
|||
|
(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-hook'."
|
|||
|
(if erlang-check-module-name
|
|||
|
(let ((mn (erlang-get-module))
|
|||
|
(fn (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
|
|||
|
(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 critera,
|
|||
|
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 critera,
|
|||
|
when fulfilled a newline is inserted and the next line is indeted.
|
|||
|
|
|||
|
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 ()
|
|||
|
"Returns true if point is placed 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 ()
|
|||
|
"Returns true if point is placed 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 possible 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 critera,
|
|||
|
when fulfilled a newline is inserted and the next line is indeted.
|
|||
|
|
|||
|
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 criteria functions, test if criteria is fulfilled.
|
|||
|
|
|||
|
Each element in the criteria list can a function returning nil, t or
|
|||
|
the atom `stop'. t means that the criteria is fulfilled, `stop' means
|
|||
|
that it the criteria isn't fulfilled and that the search should stop,
|
|||
|
and nil means continue searching.
|
|||
|
|
|||
|
Should the list contain the atom t the criteria 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 criteria is
|
|||
|
assumed to be trivially true.
|
|||
|
|
|||
|
Should all function return nil, the criteria is 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 (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-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
|
|||
|
;; construnction. (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 (concat "^" erlang-atom-regexp "\\s *("))
|
|||
|
(not (looking-at
|
|||
|
(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 work 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'.")
|
|||
|
|
|||
|
(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' aswell
|
|||
|
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 doesn't want the module to be prepended.
|
|||
|
|
|||
|
(defun erlang-find-tag-default ()
|
|||
|
"Return the default tag, searches `-import' list of imported functions.
|
|||
|
Single quotes has 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 retreiving 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 ()
|
|||
|
(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 filen-ames. Stop if a file-name
|
|||
|
;; without extension and directory matches the module.
|
|||
|
;;
|
|||
|
;; * `module:tag'
|
|||
|
;; Emacs 19: Replace testfunctions 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 variabels 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.
|
|||
|
;;
|
|||
|
;; This code is Emacs 19 `etags' specific.
|
|||
|
|
|||
|
(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.
|
|||
|
(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."
|
|||
|
(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' 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 uninteded 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-tag-completion-table' instead of `tag-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
|
|||
|
;; adds 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-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.")
|
|||
|
|
|||
|
|
|||
|
;;;###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.
|
|||
|
(fset '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 functioned added to this hook is runed every time a new Erlang
|
|||
|
shell is started.
|
|||
|
|
|||
|
See also `erlang-load-hook', a hook which is runed once, when Erlang
|
|||
|
mode is loaded, and `erlang-mode-hook' which is runed 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)
|
|||
|
(set (make-local-variable 'compilation-parsing-end) 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
|
|||
|
(funcall (symbol-function 'add-hook) 'comint-output-filter-functions
|
|||
|
'inferior-erlang-strip-delete nil t)
|
|||
|
(funcall (symbol-function 'add-hook) 'comint-output-filter-functions
|
|||
|
'inferior-erlang-strip-ctrl-m nil t))
|
|||
|
(error
|
|||
|
(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 doesn'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)))
|
|||
|
(run-hooks 'erlang-shell-mode-hook))
|
|||
|
|
|||
|
|
|||
|
(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 "\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 C-g.")
|
|||
|
|
|||
|
(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.
|
|||
|
|
|||
|
The command line history can be accessed with M-p and M-n.
|
|||
|
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))
|
|||
|
(process-kill-without-query inferior-erlang-process)
|
|||
|
(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 appear."
|
|||
|
(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-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 happend 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.
|
|||
|
(let ((comint-eol-on-send nil)
|
|||
|
(comint-input-filter (if hist comint-input-filter 'ignore)))
|
|||
|
(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)
|
|||
|
(eq (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 ()
|
|||
|
"Compile the file in the current buffer.
|
|||
|
|
|||
|
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)
|
|||
|
(save-some-buffers)
|
|||
|
(or (inferior-erlang-running-p)
|
|||
|
(save-excursion
|
|||
|
(inferior-erlang)))
|
|||
|
(or (inferior-erlang-running-p)
|
|||
|
(error "Error starting inferior Erlang shell."))
|
|||
|
(let ((dir (file-name-directory (buffer-file-name)))
|
|||
|
;;; (file (file-name-nondirectory (buffer-file-name)))
|
|||
|
(noext (substring (buffer-file-name) 0 -4))
|
|||
|
;; Hopefully, noone else will ever use these...
|
|||
|
(tmpvar "Tmp7236")
|
|||
|
(tmpvar2 "Tmp8742")
|
|||
|
end)
|
|||
|
(inferior-erlang-display-buffer)
|
|||
|
(inferior-erlang-wait-prompt)
|
|||
|
(setq end (inferior-erlang-send-command
|
|||
|
(if erlang-compile-use-outdir
|
|||
|
(format "c(\"%s\", [{outdir, \"%s\"}])." noext dir)
|
|||
|
(format
|
|||
|
(concat
|
|||
|
"f(%s), {ok, %s} = file:get_cwd(), "
|
|||
|
"file:set_cwd(\"%s\"), "
|
|||
|
"%s = c(\"%s\"), file:set_cwd(%s), f(%s), %s.")
|
|||
|
tmpvar2 tmpvar
|
|||
|
dir
|
|||
|
tmpvar2 noext tmpvar tmpvar tmpvar2))
|
|||
|
nil))
|
|||
|
(save-excursion
|
|||
|
(set-buffer inferior-erlang-buffer)
|
|||
|
(setq compilation-error-list nil)
|
|||
|
(setq compilation-parsing-end end))
|
|||
|
(setq compilation-last-buffer inferior-erlang-buffer)))
|
|||
|
|
|||
|
|
|||
|
;; `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.)
|
|||
|
(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 (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-wait-prompt)
|
|||
|
(inferior-erlang-send-command (format "cd('%s')." dir) nil))
|
|||
|
|
|||
|
;; 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."
|
|||
|
(fset 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)
|
|||
|
|
|||
|
|
|||
|
;; The end...
|
|||
|
|
|||
|
(provide 'erlang)
|
|||
|
|
|||
|
(run-hooks 'erlang-load-hook)
|
|||
|
|
|||
|
;;; erlang.el ends here
|