| ;; $Id: erlang.el,v 1.21 2004/08/03 20:38:43 mlogan Exp $ |
| ;; erlang.el --- Major modes for editing and running Erlang |
| |
| ;; Copyright (C) 1995-1998,2000 Ericsson Telecom AB |
| |
| ;; Author: Anders Lindgren |
| ;; Version: 2.4 |
| ;; 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" |
| "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) |
| ("General Tcp Receive" "fs_gen_tcp_recv" |
| erlang-skel-fs-gen-tcp-recv erlang-skel-header) |
| ("Test Suite" "test-suite" |
| erlang-skel-test-suite erlang-skel-header) |
| ("Tcp Gateway" "fs_tcp_gateway" |
| erlang-skel-fs-tcp-gateway erlang-skel-header) |
| ("Edoc function" "fs_edoc_header" |
| erlang-skel-fs-edoc-header erlang-skel-header) |
| ("DB Initialize" "fs_db_init" |
| erlang-skel-fs-db-init erlang-skel-header) |
| ("gen_leader" "generic-leader" |
| erlang-skel-generic-leader 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 "%%% $Id: erlang.el,v 1.21 2004/08/03 20:38:43 mlogan Exp $" n (erlang-skel-separator) |
| (erlang-skel-include erlang-skel-copyright-comment |
| erlang-skel-file-comment |
| erlang-skel-author-comment) |
| "%%%" n |
| "%%% @doc " p n |
| "%%% @end" 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) |
| (erlang-skel-separator 2) |
| "%% Include files" n |
| (erlang-skel-separator 2) n |
| (erlang-skel-separator 2) |
| "%% External exports" n |
| (erlang-skel-separator 2) |
| "-export([start_link/0])." n n |
| (erlang-skel-separator 2) |
| "%% Internal exports" n |
| (erlang-skel-separator 2) |
| "-export([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) |
| n |
| (erlang-skel-separator 2) |
| "%% @doc The starting point." n |
| "%% @spec start_link() -> {ok, Pid}" n |
| "%% @end" n |
| (erlang-skel-separator 2) |
| "start_link() ->" n> |
| "proc_lib:start_link(?MODULE, init, [self()])." n n |
| (erlang-skel-double-separator 2) |
| "%% Server functions" n |
| (erlang-skel-double-separator 2) |
| (erlang-skel-separator 2) |
| "%% Description: Initializes this server." n |
| "%% Variables:" n |
| "%% From - The pid of the parent process." n |
| (erlang-skel-separator 2) |
| "init(From) ->" n> |
| "register(?SERVER, self())," n> |
| "proc_lib:init_ack(From, {ok, self()})," n> |
| "loop(From)." n n |
| (erlang-skel-double-separator 2) |
| "%% Internal functions" n |
| (erlang-skel-double-separator 2) |
| "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> |
| "shutdown/0," n> |
| "stop/1" 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) |
| "%% @doc The starting point for an erlang application." n |
| "%% @spec start(Type, StartArgs) -> {ok, Pid} | {ok, Pid, State} | {error, Reason}" n |
| "%% @end" n |
| (erlang-skel-separator 2) |
| "start(Type, StartArgs) ->" n> |
| "case "(erlang-get-module-from-file-name)"_sup:start_link(StartArgs) of" n> |
| "{ok, Pid} -> " n> |
| "{ok, Pid};" n> |
| "Error ->" n> |
| "Error" n> |
| "end." n |
| n |
| (erlang-skel-separator 2) |
| "%% @doc Called to shudown the "(erlang-get-module-from-file-name)" application." n |
| "%% @spec shutdown() -> ok "n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "shutdown() ->" n> |
| "application:stop("(erlang-get-module-from-file-name)")." n |
| n |
| (erlang-skel-double-separator 2) |
| "%% Internal functions" n |
| (erlang-skel-double-separator 2) |
| n |
| (erlang-skel-separator 2) |
| "%% Called upon the termintion of an application." n |
| (erlang-skel-separator 2) |
| "stop(State) ->" n> |
| "ok." n |
| n |
| ) |
| "*The template of an application behaviour. |
| Please see the function `tempo-define-template'.") |
| |
| ;; Martins work |
| |
| ;; test server test suite template |
| (defvar erlang-skel-test-suite |
| '((erlang-skel-include erlang-skel-copyright-comment |
| erlang-skel-file-comment |
| erlang-skel-author-comment) |
| "%%%" n |
| "%%% @doc " p n |
| "%%% <p>A test spec consists of the test server required functions as detailed" n |
| "%%% by the comments for the functions themselves further down in this" n |
| "%%% file. The rest of a test suite consists of user defined Case functions" n |
| "%%% as referenced by the suite clause of the all/1 function and "n |
| "%%% described below.</p>"n |
| "%%%"n |
| "%%% <strong>" |
| "%%% Case(doc) -> [Decription]" n |
| "%%% Case(suite) -> [] | TestSpec | {skip, Comment}" n |
| "%%% Case(Config) -> {skip, Comment} | {comment, Comment} | Ok" n |
| "%%% </strong><pre>"n |
| "%%%"n |
| "%%% Variables:" n |
| "%%% Description - Short description of the test case TestSpec" n |
| "%%% Comment - This comment will be printed on the HTML result page " n |
| "%%%"n |
| "%%% Types:" n |
| "%%% Description = string()" n |
| "%%% TestSpec = list()" n |
| "%%% Ok = term()" n |
| "%%% Comment = string()" n |
| "%%% Config = term()" n |
| "%%% </pre>" n |
| "%%% <p>The documentation clause (argument doc) can be used for" n |
| "%%% automatic generation of test documentation or test descriptions.</p>" n |
| "%%%"n |
| "%%% <p>The specification clause (argument spec) shall return an empty" n |
| "%%% list, the test specification for the test case or {skip,Comment}." n |
| "%%% The syntax of a test specification is described in the reference " n |
| "%%% manual for the Test Server application.</p>" n |
| "%%%"n |
| "%%% <p>Note that the specification clause always is executed on "n |
| "%%% the controller host.</p>"n |
| "%%%"n |
| "%%% <p>The execution clause (argument Config) is only called if "n |
| "%%% the specification clause returns an empty list. The execution "n |
| "%%% clause is the real test case. Here you must call the functions "n |
| "%%% you want to test, and do whatever you need to check the result. "n |
| "%%% If someting fails, make sure the process crashes or call "n |
| "%%% test_server:fail/0/1 (which also will cause the process to crash).</p>"n |
| "%%%"n |
| "%%% <p>You can return {skip,Comment} if you decide not to run the "n |
| "%%% test case after all, e.g. if it is not applicable on this platform.</p>"n |
| "%%%"n |
| "%%% <p>You can return {comment,Comment} if you wish to print some "n |
| "%%% information in the 'Comment' field on the HTML result page.</p>"n |
| "%%%"n |
| "%%% <p>If the execution clause returns anything else, it is considered "n |
| "%%% a success. </p>"n |
| "%%% @end" n |
| "%%%" n |
| (erlang-skel-include erlang-skel-created-comment) |
| (erlang-skel-separator) |
| (erlang-skel-include erlang-skel-small-header) |
| (erlang-skel-separator 2) |
| "%% Include files" n |
| (erlang-skel-separator 2) |
| n |
| (erlang-skel-separator 2) |
| "%% Macro Definitions" n |
| (erlang-skel-separator 2) |
| n |
| (erlang-skel-separator 2) |
| "%% Test Suite Exports" n |
| (erlang-skel-separator 2) |
| "-export(["n>"all/1,"n>"init_per_testcase/2,"n>"fin_per_testcase/2"n>"])." n |
| n |
| (erlang-skel-separator 2) |
| "%% External Exports - test cases must be exported." n |
| (erlang-skel-separator 2) |
| "-export(["n>"])."n |
| n |
| (erlang-skel-double-separator 2) |
| "%% Test Server Functions" n |
| (erlang-skel-double-separator 2) |
| n |
| (erlang-skel-separator 2) |
| "%% @doc This function returns the test specification for the test suite module."n |
| "%% <pre>"n |
| "%% Types:"n |
| "%% TestSpec = list()"n |
| "%% Comment = string()"n |
| "%%"n |
| "%% </pre>"n |
| "%% @spec all(suite) -> TestSpec | {skip, Comment}"n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "all(doc) -> [];"n |
| "all(suite) -> []."n n |
| n |
| (erlang-skel-separator 2) |
| "%% @doc This function is called before each test case."n |
| "%% <pre>"n |
| "%% Types:"n |
| "%% Case = atom()"n |
| "%% Config = NewConfig = term()"n |
| "%%"n |
| "%% </pre>"n |
| "%% @spec init_per_testcase(Case, Config) -> NewConfig"n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "init_per_testcase(Case, Config) -> []."n n |
| n |
| (erlang-skel-separator 2) |
| "%% @doc This function is called after each test case."n |
| "%% <pre>"n |
| "%% Types:"n |
| "%% Case = atom()"n |
| "%% Config = term()"n |
| "%%"n |
| "%% </pre>"n |
| "%% @spec fin_per_testcase(Case, Config) -> void()"n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "fin_per_testcase(Case, Config) -> ok."n n |
| n |
| (erlang-skel-double-separator 2) |
| "%% Individual Test Case Functions" n |
| (erlang-skel-double-separator 2) |
| n |
| (erlang-skel-separator 2) |
| "%% @doc ." n |
| "%% @end" n |
| (erlang-skel-separator 2) |
| "Case(doc) -> [];"n |
| "Case(suite) -> [];"n |
| "Case(Config) when list(Config) -> ok."n 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'.") |
| |
| |
| ;; fs_tcp_gateway template |
| (defvar erlang-skel-fs-tcp-gateway |
| '((erlang-skel-include erlang-skel-large-header) |
| "%% TODO Implement this behaviour" n |
| "%% -behaviour(tcp_gateway)." 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/1, init/0, sync_request/5, async_request/5, 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) |
| "%% @doc Starts the gateway server."n |
| "%% <pre>"n |
| "%%"n |
| "%% Expects:"n |
| "%% Port - The port number to bind to."n |
| "%%"n |
| "%% Types:"n |
| "%% Port = integer()"n |
| "%%"n |
| "%% </pre>"n |
| "%%"n |
| "%% @spec start_link(Port) -> {ok, pid()}"n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "start_link(Port) ->" n> |
| "CallBackModule = ?MODULE,"n> |
| " fs_tcp_gateway:start_link(CallBackModule, Port)."n |
| n |
| (erlang-skel-double-separator 2) |
| "%% Callbacks" n |
| (erlang-skel-double-separator 2) |
| (erlang-skel-separator 2) |
| "%% @doc Initialization stage for each new connection to the gateway server."n |
| "%% <pre>"n |
| "%%"n |
| "%% Types:"n |
| "%% State = term()"n |
| "%%"n |
| "%% </pre>"n |
| "%%"n |
| "%% @spec init() -> {ok, State}"n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "init() ->"n> |
| "{ok, #state{}}."n |
| n |
| (erlang-skel-separator 2) |
| "%% @doc Called when the server receives a syncronous request from a client."n |
| "%% <pre>"n |
| "%%"n |
| "%% Expects:"n |
| "%% ID - The request ID number"n |
| "%% M - module name"n |
| "%% F - function name"n |
| "%% A - List of arguments"n |
| "%% Reply - A message sent back over the tcp stream to the client"n |
| "%%"n |
| "%% Types:"n |
| "%% ID = M = F = Reply = string()"n |
| "%% A = [string()]"n |
| "%% State = term()"n |
| "%% Reply = string()"n |
| "%%"n |
| "%% </pre>"n |
| "%%"n |
| "%% @spec sync_request(ID, M, F, A, State) ->"n |
| "%% {reply, Reply, State} | {stop, Reply, Reason, State} | {noreply, State}"n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "sync_request(ID, M, F, A, State) ->"n> |
| "{reply, \"sync ok\", State}."n |
| n |
| (erlang-skel-separator 2) |
| "%% @doc Called when the server receives a asyncronous request from a client."n |
| "%% <pre>"n |
| "%%"n |
| "%% Expects:"n |
| "%% ID - The request ID number"n |
| "%% M - module name"n |
| "%% F - function name"n |
| "%% A - List of arguments"n |
| "%% Reply - A message sent back over the tcp stream to the client"n |
| "%%"n |
| "%% Types:"n |
| "%% ID = M = F = string()"n |
| "%% A = [string()]"n |
| "%% State = term()"n |
| "%% Reply = string()"n |
| "%%"n |
| "%% </pre>"n |
| "%%"n |
| "%% @spec async_request(ID, M, F, A, State) ->"n |
| "%% {stop, Reason, State} | {noreply, State}"n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "async_request(ID, M, F, A, State) ->"n> |
| "{noreply, State}."n |
| n |
| (erlang-skel-separator 2) |
| "%% @doc Called upon the shutdown of a connection."n |
| "%% @spec terminate(Reason, State) -> void()"n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "terminate(Reason, State) ->"n> |
| "ok."n |
| n |
| (erlang-skel-double-separator 2) |
| "%% Internal functions" n |
| (erlang-skel-double-separator 2) |
| ) |
| "*The template of a fs_tcp_gateway behaviour. |
| Please see the function `tempo-define-template'.") |
| |
| |
| ;; gen_tcp_recv template |
| (defvar erlang-skel-fs-gen-tcp-recv |
| '((erlang-skel-include erlang-skel-large-header) |
| "%% TODO Implement this behaviour" n |
| "%% -behaviour(gen_tcp_recv)." n |
| (erlang-skel-separator 2) |
| "%% External exports" n |
| (erlang-skel-separator 2) |
| "-export([" n> "start_link/0" n> "])." n n |
| (erlang-skel-separator 2) |
| "%% Server Callbacks" n |
| (erlang-skel-separator 2) |
| "-export([" n> "init/1," n> "handle_packet/3," n> "handle_call/4" n> |
| "handle_info/3" n> "terminate/2" n> "])." n n |
| (erlang-skel-separator 2) |
| "%% Include files" n |
| (erlang-skel-separator 2) |
| n |
| (erlang-skel-separator 2) |
| "%% API - External Exports" n |
| (erlang-skel-separator 2) |
| "-export([" n> |
| "start_link/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) n |
| (erlang-skel-separator 2) |
| "%% @doc Starts the fs_gen_tcp_recv server." n |
| "%% @spec start_link() -> {ok, Pid}" n |
| "%% @end" n |
| (erlang-skel-separator 2) |
| "start_link() ->" n> |
| "fs_gen_tcp_recv:start_link(CallbackModule, ?TCP_PORT, [], [], [])." n n n |
| (erlang-skel-double-separator 2) |
| "%% Server Functions" n |
| (erlang-skel-double-separator 2) n |
| (erlang-skel-separator 2) |
| "%% Initializes the state for a gen_tcp_recv server." n |
| "%% Returns: {ok, State}" n |
| "%% State = term()" n |
| (erlang-skel-separator 2) |
| "init([]) ->" n> |
| "{ok, []}." n n n |
| (erlang-skel-separator 2) |
| "%% Receives packets from the socket. Also receives timout messages." n |
| "%% Types:" n |
| "%% Socket = socket()" n |
| "%% Packet = binary() | string(). Can be altered in TCPOptions" n |
| "%% Reply = binary() | string(). Can be altered in TCPOptions" n |
| "%% State = NewState = term()" n |
| "%% Timeout = integer() in miliseconds." n |
| "%% Returns: " n |
| "%% {noreply, NewState} {noreply, NewState, Timeout}" n |
| "%% {reply, Reply, NewState} {reply, Reply, NewState, Timeout}" n |
| "%% {stop, Reason, NewState}" n |
| (erlang-skel-separator 2) |
| "handle_packet(Socket, Packet, State) ->" n> |
| "{noreply, State}." n n n |
| (erlang-skel-separator 2) |
| "%% Receives sync calls from a client." n |
| "%% Variables:" n |
| "%% Reply - A message sent back to the caller." n |
| "%% " n |
| "%% Types:" n |
| "%% Socket = socket()" n |
| "%% Msg = Reply = term()" n |
| "%% State = NewState = term()" n |
| "%% Timeout = integer() in miliseconds." n |
| "%% Returns: " n |
| "%% {noreply, NewState} {noreply, NewState, Timeout}" n |
| "%% {reply, Reply, NewState} {reply, Reply, NewState, Timeout}" n |
| "%% {stop, Reason, NewState} | {stop, Reason, Reply, NewState}" n |
| (erlang-skel-separator 2) |
| "handle_call(Socket, From, Msg, State) ->" n> |
| "{reply, Reply, State}." n n n |
| (erlang-skel-separator 2) |
| "%% Receives messages from other processes and timeouts." n |
| "%% Types:" n |
| "%% Socket = socket()" n |
| "%% Msg = term() | timeout" n |
| "%% State = NewState = term()" n |
| "%% Timeout = integer() in miliseconds." n |
| "%% Returns: " n |
| "%% {noreply, NewState} {noreply, NewState, Timeout}" n |
| "%% {stop, Reason, NewState}" n |
| (erlang-skel-separator 2) |
| "handle_info(Socket, Msg, State) ->" n> |
| "{noreply, State}." n n n |
| (erlang-skel-separator 2) |
| "%% Called after a socket closes or {stop, Reason, NewState}" n |
| "%% Types:" n |
| "%% Socket = socket()" n |
| "%% State = NewState = term()" n |
| "%% Returns: term()" n |
| (erlang-skel-separator 2) |
| "terminate(Socket, State) ->" n> |
| "ok." n n n |
| (erlang-skel-double-separator 2) |
| "%%% Internal Functions" n |
| (erlang-skel-double-separator 2) |
| ) |
| "*The template of a fs_gen_tcp_recv behaviour. |
| Please see the function `tempo-define-template'.") |
| |
| ;; fs_edoc_header template |
| (defvar erlang-skel-fs-edoc-header |
| '((erlang-skel-separator 2) |
| "%% @doc" n |
| "%% <pre>" n |
| "%% Variables:" n |
| "%% Types:" n |
| "%% </pre> term()" n |
| "%% @spec" n |
| "%% @end" n |
| (erlang-skel-separator 2) |
| ) |
| "*The template of an edoc function header. |
| Please see the function `tempo-define-template'.") |
| |
| ;; fs_db_init template |
| (defvar erlang-skel-fs-db-init |
| '((erlang-skel-include erlang-skel-large-header) |
| "%% TODO Implement this behaviour" n |
| "%% -behaviour(db_init)." 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/1, start_link/3, init/1, local_init/0, remote_init/0" 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-separator 2) |
| "%% @doc Starts the server. " n> |
| "%% <pre>" n> |
| "%% Variables:" n> |
| "%% CallBackModule - The module that exhibits the db init behaviour." n> |
| "%% Args - A list of arguments delivered to the CallBackModule:init/1 function." n> |
| "%% Options - A list of options for fs_db_init." n> |
| "%%" n> |
| "%% The options are as follows:" n> |
| "%% {schema_type, Type}" n> |
| "%%" n> |
| "%% Types:" n> |
| "%% Args = list()" n> |
| "%% Options = list()" n> |
| "%% Type = ram_copies | disc_copies | disc_only_copies" n> |
| "%%" n> |
| "%% </pre>" n> |
| "%% @spec start_link(CallBackModule, Args, Options) -> {ok, pid()} | {error, Reason}" n> |
| "%% @end" n> |
| "(erlang-skel-separator 2)" |
| "start_link(CallBackModule, Args, Options) ->" n> |
| "proc_lib:start_link(?MODULE, db_init, [self(), CallBackModule, Args, Options])." n> |
| n |
| "%% @spec start_link(CallBackModule) -> {ok, pid()} | {error, Reason}" n> |
| "%% @equiv start_link(CallBackModule, [], [])" n> |
| "start_link(CallBackModule) ->" n> |
| "start_link(CallBackModule, [], [])." n> |
| n |
| (erlang-skel-double-separator 2) |
| "%% Callbacks" n |
| (erlang-skel-double-separator 2) |
| (erlang-skel-separator 2) |
| "%% @doc" n |
| "%% Returns a list of nodes that db_init should try to replicate with." n |
| "%%" n |
| "%% <pre>" n |
| "%% Types:" n |
| "%% DBNodes = [node()]" n |
| "%% </pre>" n |
| "%%" n |
| "%% @spec init(Args) -> {ok, DBNodes} | no_init" n |
| "%% @end" n |
| (erlang-skel-separator 2) |
| "init(Args) ->" n |
| " {ok, DBNodesToReplicateFrom = []}." n |
| (erlang-skel-separator 2) |
| "%% @doc" n |
| "%% Created a schema and seeds the local database." n |
| "%% Returns a list of records with their initial data." n |
| "%% These are to be the tables and initial values for the database." n |
| "%%" n |
| "%% <pre>" n |
| "%% Reclist = [record()]" n |
| "%% Reason = atom()" n |
| "%% </pre>" n |
| "%%" n |
| "%% @spec local_init() -> {ok, RecList} | {error, Reason}" n |
| "%% @end" n |
| (erlang-skel-separator 2) |
| "local_init() ->" n |
| " {ok, []}." n |
| (erlang-skel-separator 2) |
| "%% @doc Pushes the schema and the table definitions to the node" n |
| "%% specified by the variable node." n |
| "%% NOTE: Do not include the schema in this list." n |
| "%%" n |
| "%% <pre>" n |
| "%% Types " n |
| "%% Node = node()" n |
| "%% TableList = [{Table, Type}]" n |
| "%% Table = atom()" n |
| "%% Type = ram_copies, disc_copies" n |
| "%% Reason = atom()" n |
| "%% </pre>" n |
| "%%" n |
| "%% @spec remote_init() -> {ok, TableList} | {error, Reason}" n |
| "%% @end" n |
| (erlang-skel-separator 2) |
| "remote_init() ->" n |
| " {ok, [{ini, ram_copies}]}." n |
| n |
| ) |
| "*The template of a fs_db_init 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/1" 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) |
| "%% @doc Starts the supervisor." n |
| "%% @spec start_link(StartArgs) -> {ok, pid()} | Error" n |
| "%% @end" n |
| (erlang-skel-separator 2) |
| "start_link(StartArgs) ->" 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> |
| "RestartStrategy = one_for_one," n> |
| "MaxRestarts = 1000," n> |
| "MaxTimeBetRestarts = 3600," n> |
| n> |
| "SupFlags = {RestartStrategy, MaxRestarts, MaxTimeBetRestarts}," n> |
| n> |
| "ChildSpecs =" n> |
| "[" n> |
| "{AppName," n> |
| "{AppName, start_link, []}," n> |
| "permanent," n> |
| "1000," n> |
| "worker," n> |
| "[AppName]}" n> |
| "]," n> |
| "{ok,{SupFlags, ChildSpecs}}." 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 |
| (erlang-skel-separator 2) |
| "-export([" n>"start_link/0,"n>"stop/0"n>"])." n |
| n |
| (erlang-skel-separator 2) |
| "%% gen_server callbacks" n |
| (erlang-skel-separator 2) |
| "-export([init/1, handle_call/3, handle_cast/2, " |
| "handle_info/2, terminate/2, code_change/3])." n n |
| (erlang-skel-separator 2) |
| "%% record definitions" n |
| (erlang-skel-separator 2) |
| "-record(state, {})." n n |
| (erlang-skel-separator 2) |
| "%% macro definitions" n |
| (erlang-skel-separator 2) |
| "-define(SERVER, ?MODULE)." n |
| n |
| (erlang-skel-double-separator 2) |
| "%% External functions" n |
| (erlang-skel-double-separator 2) |
| (erlang-skel-separator 2) |
| "%% @doc Starts the server." n |
| "%% @spec start_link() -> {ok, pid()} | {error, Reason}" n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "start_link() ->" n> |
| "gen_server:start_link({local, ?SERVER}, ?MODULE, [], [])." n |
| n |
| (erlang-skel-separator 2) |
| "%% @doc Stops the server." n |
| "%% @spec stop() -> ok" n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "stop() ->" n> |
| "gen_server:cast(?SERVER, stop)." 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(stop, State) ->" n> |
| "{stop, normal, State};" n |
| "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-double-separator 2) |
| "%%% Internal functions" n |
| (erlang-skel-double-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 |
| (erlang-skel-separator 2) |
| "-export([start_link/0, add_handler/0])." n |
| n |
| (erlang-skel-separator 2) |
| "%% gen_event callbacks" n |
| (erlang-skel-separator 2) |
| "-export([init/1, handle_event/2, handle_call/2, " |
| "handle_info/2, terminate/2, code_change/3])." n n |
| (erlang-skel-separator 2) |
| "%% Record Definitions" n |
| (erlang-skel-separator 2) |
| "-record(state, {})." n n |
| (erlang-skel-separator 2) |
| "%% Macro Definitions" n |
| (erlang-skel-separator 2) |
| "-define(SERVER, ?MODULE)." n |
| n |
| (erlang-skel-double-separator 2) |
| "%% External functions" n |
| (erlang-skel-double-separator 2) |
| (erlang-skel-separator 2) |
| "%% @doc Starts the server" n |
| "%% @spec start_link() -> {ok, Pid} | {error, {already_started, Pid}}" n |
| "%% @end" n |
| (erlang-skel-separator 2) |
| "start_link() ->" n> |
| "gen_event:start_link({local, ?SERVER}). " n |
| n |
| (erlang-skel-separator 2) |
| "%% @doc Adds an event handler" n |
| "%% @spec add_handler() -> ok | {'EXIT', Reason}" n |
| "%% @end" 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) |
| "%% @doc" n |
| "%% @spec " n |
| "%% @end " 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-generic-leader |
| '((erlang-skel-include erlang-skel-large-header) |
| "-behaviour(fs_gen_leader)." 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>"stop/0"n>"])." n |
| n |
| (erlang-skel-separator 2) |
| "%% gen_leader callbacks" n |
| (erlang-skel-separator 2) |
| "-export([init/1," n> |
| "elected/2," n> |
| "surrendered/3," n> |
| "handle_DOWN/3," n> |
| "handle_leader_call/4," n> |
| "handle_leader_cast/3," n> |
| "from_leader/3," n> |
| "handle_call/3," n> |
| "handle_cast/2," n> |
| "handle_info/2," n> |
| "terminate/2," n> |
| "code_change/4" n> |
| "])." n> |
| (erlang-skel-separator 2) |
| "%% record definitions" n |
| (erlang-skel-separator 2) |
| "-record(state, {})." n n |
| (erlang-skel-separator 2) |
| "%% macro definitions" n |
| (erlang-skel-separator 2) |
| "-define(SERVER, ?MODULE)." n |
| n |
| (erlang-skel-double-separator 2) |
| "%% External functions" n |
| (erlang-skel-double-separator 2) |
| (erlang-skel-separator 2) |
| "%% @doc Starts the gen_leader." n |
| "%% @spec start_link() -> {ok, pid()} | {error, Reason}" n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "start_link() ->" n> |
| "Candidates = [node()|nodes()]," n> |
| "Workers = []," n> |
| "gen_leader:start_link({local, ?SERVER}, Candidates, Workers, ?MODULE, [], [])." n |
| n |
| (erlang-skel-separator 2) |
| "%% @doc Stops the gen_leader." n |
| "%% @spec stop() -> ok" n |
| "%% @end"n |
| (erlang-skel-separator 2) |
| "stop() ->" n> |
| "gen_leader:cast(?SERVER, stop)." 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) |
| "%% Called when we become the leader." n> |
| "%%" n> |
| "%% elected(State::state(), E::election()) -> {ok, Broadcast, NState}" n> |
| (erlang-skel-separator 2) |
| "elected(State, E) ->" n> |
| "BroadcastToAllCandidates = [],"n> |
| "{ok, BroadcastToAllCandidates , State}." n |
| n |
| |
| (erlang-skel-separator 2) |
| "%% Called by each candidate when it recognizes another instance as leader." n> |
| "%% Strictly speaking, this function is called when the candidate " n> |
| "%% acknowledges a leader and receives a Synch message in return." n> |
| "%%" n> |
| "%% surrendered(State::state(), Synch::broadcast(), E::election()) -> {ok, NState}" n> |
| (erlang-skel-separator 2) |
| "surrendered(State, BCastFromLeaderElectedCall, E) ->" n> |
| "{ok, State#state{leader_node = LeaderNode}}."n |
| n |
| |
| (erlang-skel-separator 2) |
| "%% Called by the leader when it detects loss of a candidate node." n> |
| "%% If the function returns a broadcast() object, this will" n> |
| "%% be sent to all candidates, and they will receive it in the function" n> |
| "%% link from_leader/3. from_leader/3" n> |
| "%%" n> |
| "%% handle_DOWN(Node::node(), State::state(), E::election()) -> {ok, NState} | {ok, Broadcast, NState}" n> |
| (erlang-skel-separator 2) |
| "handle_DOWN(Node, State, _E) ->" n> |
| "{ok, State}." n> |
| |
| (erlang-skel-separator 2) |
| "%% handle_leader_call(Msg::term(), From::callerRef(), State::state(), E::election()) -> " n> |
| "%% {reply, Reply, NState} |" n> |
| "%% {reply, Reply, Broadcast, NState} |" n> |
| "%% {noreply, state()} |" n> |
| "%% {stop, Reason, Reply, NState} |" n> |
| "%% commonReply()" n> |
| "%%" n> |
| "%% Called by the leader in response to a gen_leader:leader_call/2. leader_call()." n> |
| (erlang-skel-separator 2) |
| "handle_leader_call(Msg, _From, State, _E) ->" n> |
| "{reply, Reply = [], State}." n |
| n |
| |
| (erlang-skel-separator 2) |
| "%% Called by the leader in response to a gen_leader:leader_cast/2 leader_cast()" n> |
| "%% handle_leader_cast(Msg::term(), State::term(), E::election()) -> commonReply()" n> |
| "%%" n> |
| "%% BUG: This has not yet been implemented." n> |
| (erlang-skel-separator 2) |
| "handle_leader_cast(_Msg, State, _E) ->" n> |
| "{ok, State}." n |
| n |
| |
| (erlang-skel-separator 2) |
| "%% Called by each candidate in response to a message from the leader. " n> |
| "%% In this particular module, the leader passes an update function to be " n> |
| "%% applied to the candidate's state. " n> |
| "%% " n> |
| "%% from_leader(Msg::term(), State::state(), E::election()) -> {ok, NState} " n> |
| (erlang-skel-separator 2) |
| "from_leader(MSG, State, _E) -> " n> |
| "{ok, State}. " n |
| 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(stop, State) ->" n> |
| "{stop, normal, State};" n |
| "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, _E, _Extra) ->" n> |
| "{ok, State}." n |
| n |
| |
| n |
| (erlang-skel-separator 2) |
| "%%% Internal functions" n |
| (erlang-skel-separator 2) |
| ) |
| "*The template of a generic leader. |
| 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\\|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 |
| |
| ;; XXX: |
| ; (concat "\\<" |
| ; (regexp-opt '("atom" "binary" "constant" "float" "integer" "list" |
| ; "number" "pid" "port" "reference" "record" "tuple") |
| ; t) |
| ; "\\>") |
| |
| (concat "\\<\\(atom\\|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-comment-start-regexp . "%") |
| (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, alwys 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)) |
| |
| ;; 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. Skip it too. |
| (when (= (following-char) ?\() |
| (forward-list 1))) |
| |
| ;; Other punctuation: Skip over it and any following punctuation |
| ((= cs ?.) |
| ;; Skip over all characters in the operand. |
| (skip-syntax-forward ".")) |
| |
| ;; Other char: Skip over it. |
| (t |
| (forward-char 1)))) |
| |
| ;; Open parenthesis |
| ((= cs ?\() |
| (erlang-push (list '\( token (current-column)) stack) |
| (forward-char 1)) |
| |
| ;; Close parenthesis |
| ((= cs ?\)) |
| (while (memq (car (car stack)) '(|| ->)) |
| (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 (looking-at (concat "^" erlang-atom-regexp "\\s *\\((\\)")) |
| (condition-case () |
| (save-excursion |
| (goto-char (match-beginning (+ 1 erlang-atom-regexp-matches))) |
| (forward-sexp 1) |
| (erlang-skip-blank) |
| (and (looking-at "->\\|:-") |
| (erlang-buffer-substring |
| (match-beginning 0) (match-end 0))))))) |
| |
| |
| (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 *,") |
| (goto-char (match-end 0))) |
| (t |
| (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 |