aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tools/emacs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tools/emacs')
-rw-r--r--lib/tools/emacs/Makefile4
-rw-r--r--lib/tools/emacs/README9
-rw-r--r--lib/tools/emacs/erlang-eunit.el368
-rw-r--r--lib/tools/emacs/erlang-flymake.el102
-rw-r--r--lib/tools/emacs/erlang-start.el5
-rw-r--r--lib/tools/emacs/erlang.el395
-rw-r--r--lib/tools/emacs/test.erl.indented99
-rw-r--r--lib/tools/emacs/test.erl.orig103
8 files changed, 858 insertions, 227 deletions
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
index 7249263992..8533488463 100644
--- a/lib/tools/emacs/Makefile
+++ b/lib/tools/emacs/Makefile
@@ -37,8 +37,12 @@ MAN_FILES= \
tags.3
EMACS_FILES= \
+ erlang-skels \
+ erlang-skels-old \
+ erlang_appwiz \
erlang-start \
erlang-eunit \
+ erlang-flymake \
erlang
README_FILES= README
diff --git a/lib/tools/emacs/README b/lib/tools/emacs/README
index ca068d04c4..cc107dcd41 100644
--- a/lib/tools/emacs/README
+++ b/lib/tools/emacs/README
@@ -42,7 +42,14 @@ Files\erl-<Ver>:
(setq erlang-root-dir "C:/Program Files/erl<Ver>")
(setq exec-path (cons "C:/Program Files/erl<Ver>/bin" exec-path))
(require 'erlang-start)
-
+Miscellaneous addons
+--------------------
+
+In order to check erlang source code on the fly, add the following
+line to your .emacs file (after erlang-start, see above). See
+erlang-flymake.el for more information on how to customize the syntax
+check.
+ (require 'erlang-flymake)
diff --git a/lib/tools/emacs/erlang-eunit.el b/lib/tools/emacs/erlang-eunit.el
index 05528aee6d..f2c0db67dd 100644
--- a/lib/tools/emacs/erlang-eunit.el
+++ b/lib/tools/emacs/erlang-eunit.el
@@ -1,27 +1,44 @@
;;
;; %CopyrightBegin%
-;;
-;; Copyright Ericsson AB 2009. All Rights Reserved.
-;;
+;;
+;; Copyright Ericsson AB 2009-2010. All Rights Reserved.
+;;
;; The contents of this file are subject to the Erlang Public License,
;; Version 1.1, (the "License"); you may not use this file except in
;; compliance with the License. You should have received a copy of the
;; Erlang Public License along with this software. If not, it can be
;; retrieved online at http://www.erlang.org/.
-;;
+;;
;; Software distributed under the License is distributed on an "AS IS"
;; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
;; the License for the specific language governing rights and limitations
;; under the License.
-;;
+;;
;; %CopyrightEnd%
;;;
;;; Purpose: Provide EUnit utilities.
;;;
;;; Author: Klas Johansson
-(defvar erlang-eunit-separate-src-and-test-directories t
- "*Whether or not to keep source and EUnit test files in separate directories")
+(eval-when-compile
+ (require 'cl))
+
+(defvar erlang-eunit-src-candidate-dirs '("../src" ".")
+ "*Name of directories which to search for source files matching
+an EUnit test file. The first directory in the list will be used,
+if there is no match.")
+
+(defvar erlang-eunit-test-candidate-dirs '("../test" ".")
+ "*Name of directories which to search for EUnit test files matching
+a source file. The first directory in the list will be used,
+if there is no match.")
+
+(defvar erlang-eunit-autosave nil
+ "*Set to non-nil to automtically save unsaved buffers before running tests.
+This is useful, reducing the save-compile-load-test cycle to one keychord.")
+
+(defvar erlang-eunit-recent-info '((mode . nil) (module . nil) (test . nil) (cover . nil))
+ "Info about the most recent running of an EUnit test representation.")
;;;
;;; Switch between src/EUnit test buffers
@@ -41,7 +58,6 @@ buffer and vice versa"
"Open the EUnit test file which corresponds to a src file"
(find-file-other-window (erlang-eunit-test-filename src-file-path)))
-
;;;
;;; Open the src file which corresponds to the an EUnit test file
;;;
@@ -52,37 +68,55 @@ buffer and vice versa"
;;; Return the name and path of the EUnit test file
;;, (input may be either the source filename itself or the EUnit test filename)
(defun erlang-eunit-test-filename (file-path)
- (erlang-eunit-rewrite-filename file-path "test" "_tests"))
+ (if (erlang-eunit-test-file-p file-path)
+ file-path
+ (erlang-eunit-rewrite-filename file-path erlang-eunit-test-candidate-dirs)))
;;; Return the name and path of the source file
;;, (input may be either the source filename itself or the EUnit test filename)
(defun erlang-eunit-src-filename (file-path)
- (erlang-eunit-rewrite-filename file-path "src" ""))
+ (if (erlang-eunit-src-file-p file-path)
+ file-path
+ (erlang-eunit-rewrite-filename file-path erlang-eunit-src-candidate-dirs)))
;;; Rewrite a filename from the src or test filename to the other
-(defun erlang-eunit-rewrite-filename (orig-file-path dest-dirname dest-suffix)
- (let* ((root-dir-name (erlang-eunit-file-root-dir-name orig-file-path))
- (src-module-name (erlang-eunit-source-module-name orig-file-path))
- (dest-base-name (concat src-module-name dest-suffix ".erl"))
- (dest-dir-name-1 (file-name-directory orig-file-path))
- (dest-dir-name-2 (filename-join root-dir-name dest-dirname))
- (dest-file-name-1 (filename-join dest-dir-name-1 dest-base-name))
- (dest-file-name-2 (filename-join dest-dir-name-2 dest-base-name)))
- ;; This function tries to be a bit intelligent:
- ;; * if there already is a test (or source) file in the same
- ;; directory as a source (or test) file, it'll be picked
- ;; * if there already is a test (or source) file in a separate
- ;; test (or src) directory, it'll be picked
- ;; * otherwise it'll resort to whatever alternative (same or
- ;; separate directories) that the user has chosen
- (cond ((file-readable-p dest-file-name-1)
- dest-file-name-1)
- ((file-readable-p dest-file-name-2)
- dest-file-name-2)
- (erlang-eunit-separate-src-and-test-directories
- dest-file-name-2)
- (t
- dest-file-name-1))))
+(defun erlang-eunit-rewrite-filename (orig-file-path candidate-dirs)
+ (or (erlang-eunit-locate-buddy orig-file-path candidate-dirs)
+ (erlang-eunit-buddy-file-path orig-file-path (car candidate-dirs))))
+
+;;; Search for a file's buddy file (a source file's EUnit test file,
+;;; or an EUnit test file's source file) in a list of candidate
+;;; directories.
+(defun erlang-eunit-locate-buddy (orig-file-path candidate-dirs)
+ (when candidate-dirs
+ (let ((buddy-file-path (erlang-eunit-buddy-file-path
+ orig-file-path
+ (car candidate-dirs))))
+ (if (file-readable-p buddy-file-path)
+ buddy-file-path
+ (erlang-eunit-locate-buddy orig-file-path (cdr candidate-dirs))))))
+
+(defun erlang-eunit-buddy-file-path (orig-file-path buddy-dir-name)
+ (let* ((orig-dir-name (file-name-directory orig-file-path))
+ (buddy-dir-name (file-truename
+ (filename-join orig-dir-name buddy-dir-name)))
+ (buddy-base-name (erlang-eunit-buddy-basename orig-file-path)))
+ (filename-join buddy-dir-name buddy-base-name)))
+
+;;; Return the basename of the buddy file:
+;;; /tmp/foo/src/x.erl --> x_tests.erl
+;;; /tmp/foo/test/x_tests.erl --> x.erl
+(defun erlang-eunit-buddy-basename (file-path)
+ (let ((src-module-name (erlang-eunit-source-module-name file-path)))
+ (cond
+ ((erlang-eunit-src-file-p file-path)
+ (concat src-module-name "_tests.erl"))
+ ((erlang-eunit-test-file-p file-path)
+ (concat src-module-name ".erl")))))
+
+;;; Checks whether a file is a source file or not
+(defun erlang-eunit-src-file-p (file-path)
+ (not (erlang-eunit-test-file-p file-path)))
;;; Checks whether a file is a EUnit test file or not
(defun erlang-eunit-test-file-p (file-path)
@@ -93,23 +127,17 @@ buffer and vice versa"
;;; /tmp/foo/test/x_tests.erl --> x
(defun erlang-eunit-source-module-name (file-path)
(interactive)
- (let* ((file-name (file-name-nondirectory file-path))
- (base-name (file-name-sans-extension file-name)))
- (if (string-match "^\\(.+\\)_tests$" base-name)
- (substring base-name (match-beginning 1) (match-end 1))
- base-name)))
-
-;;; Return the directory name which is common to both src and test
-;;; /tmp/foo/src/x.erl --> /tmp/foo
-;;; /tmp/foo/test/x_tests.erl --> /tmp/foo
-(defun erlang-eunit-file-root-dir-name (file-path)
- (erlang-eunit-dir-parent-dirname (file-name-directory file-path)))
-
-;;; Return the parent directory name of a directory
-;;; /tmp/foo/ --> /tmp
-;;; /tmp/foo --> /tmp
-(defun erlang-eunit-dir-parent-dirname (dir-name)
- (file-name-directory (directory-file-name dir-name)))
+ (let ((module-name (erlang-eunit-module-name file-path)))
+ (if (string-match "^\\(.+\\)_tests$" module-name)
+ (substring module-name (match-beginning 1) (match-end 1))
+ module-name)))
+
+;;; Return the module name of the file
+;;; /tmp/foo/src/x.erl --> x
+;;; /tmp/foo/test/x_tests.erl --> x_tests
+(defun erlang-eunit-module-name (file-path)
+ (interactive)
+ (file-name-sans-extension (file-name-nondirectory file-path)))
;;; Older emacsen don't have string-match-p.
(defun erlang-eunit-string-match-p (regexp string &optional start)
@@ -125,25 +153,158 @@ buffer and vice versa"
(concat dir file)
(concat dir "/" file)))
+;;; Get info about the most recent running of EUnit
+(defun erlang-eunit-recent (key)
+ (cdr (assq key erlang-eunit-recent-info)))
+
+;;; Record info about the most recent running of EUnit
+;;; Known modes are 'module-mode and 'test-mode
+(defun erlang-eunit-record-recent (mode module test)
+ (setcdr (assq 'mode erlang-eunit-recent-info) mode)
+ (setcdr (assq 'module erlang-eunit-recent-info) module)
+ (setcdr (assq 'test erlang-eunit-recent-info) test))
+
+;;; Record whether the most recent running of EUnit included cover
+;;; compilation
+(defun erlang-eunit-record-recent-compile (under-cover)
+ (setcdr (assq 'cover erlang-eunit-recent-info) under-cover))
+
+;;; Determine options for EUnit.
+(defun erlang-eunit-opts ()
+ (if current-prefix-arg ", [verbose]" ""))
+
+;;; Determine current test function
+(defun erlang-eunit-current-test ()
+ (save-excursion
+ (erlang-end-of-function 1)
+ (erlang-beginning-of-function 1)
+ (erlang-name-of-function)))
+
+(defun erlang-eunit-simple-test-p (test-name)
+ (if (erlang-eunit-string-match-p "^\\(.+\\)_test$" test-name) t nil))
+
+(defun erlang-eunit-test-generator-p (test-name)
+ (if (erlang-eunit-string-match-p "^\\(.+\\)_test_$" test-name) t nil))
+
+;;; Run one EUnit test
+(defun erlang-eunit-run-test (module-name test-name)
+ (let ((command
+ (cond ((erlang-eunit-simple-test-p test-name)
+ (format "eunit:test({%s, %s}%s)."
+ module-name test-name (erlang-eunit-opts)))
+ ((erlang-eunit-test-generator-p test-name)
+ (format "eunit:test({generator, %s, %s}%s)."
+ module-name test-name (erlang-eunit-opts)))
+ (t (format "%% WARNING: '%s' is not a test function" test-name)))))
+ (erlang-eunit-record-recent 'test-mode module-name test-name)
+ (erlang-eunit-inferior-erlang-send-command command)))
+
;;; Run EUnit tests for the current module
-(defun erlang-eunit-run-tests ()
- "Run the EUnit test suite for the current module.
+(defun erlang-eunit-run-module-tests (module-name)
+ (let ((command (format "eunit:test(%s%s)." module-name (erlang-eunit-opts))))
+ (erlang-eunit-record-recent 'module-mode module-name nil)
+ (erlang-eunit-inferior-erlang-send-command command)))
-With prefix arg, runs tests with the verbose flag set."
+(defun erlang-eunit-compile-and-run-recent ()
+ "Compile the source and test files and repeat the most recent EUnit test run.
+
+With prefix arg, compiles for debug and runs tests with the verbose flag set."
(interactive)
- (let* ((module-name (erlang-add-quotes-if-needed
- (erlang-eunit-source-module-name buffer-file-name)))
- (opts (if current-prefix-arg ", [verbose]" ""))
- (command (format "eunit:test(%s%s)." module-name opts)))
- (erlang-eunit-inferior-erlang-send-command command)))
+ (case (erlang-eunit-recent 'mode)
+ ('test-mode
+ (erlang-eunit-compile-and-test
+ 'erlang-eunit-run-test (list (erlang-eunit-recent 'module)
+ (erlang-eunit-recent 'test))))
+ ('module-mode
+ (erlang-eunit-compile-and-test
+ 'erlang-eunit-run-module-tests (list (erlang-eunit-recent 'module))
+ (erlang-eunit-recent 'cover)))
+ (t (error "EUnit has not yet been run. Please run a test first."))))
+
+(defun erlang-eunit-cover-compile ()
+ "Cover compile current module."
+ (interactive)
+ (let* ((erlang-compile-extra-opts
+ (append (list 'debug_info) erlang-compile-extra-opts))
+ (module-name
+ (erlang-add-quotes-if-needed
+ (erlang-eunit-module-name buffer-file-name)))
+ (compile-command
+ (format "cover:compile_beam(%s)." module-name)))
+ (erlang-compile)
+ (if (erlang-eunit-last-compilation-successful-p)
+ (erlang-eunit-inferior-erlang-send-command compile-command))))
+
+(defun erlang-eunit-analyze-coverage ()
+ "Analyze the data collected by cover tool for the module in the
+current buffer.
+
+Assumes that the module has been cover compiled prior to this
+call. This function will do two things: print the number of
+covered and uncovered functions in the erlang shell and display a
+new buffer called *<module name> coverage* which shows the source
+code along with the coverage analysis results."
+ (interactive)
+ (let* ((module-name (erlang-add-quotes-if-needed
+ (erlang-eunit-module-name buffer-file-name)))
+ (tmp-filename (make-temp-file "cover"))
+ (analyze-command (format "cover:analyze_to_file(%s, \"%s\"). "
+ module-name tmp-filename))
+ (buf-name (format "*%s coverage*" module-name)))
+ (erlang-eunit-inferior-erlang-send-command analyze-command)
+ ;; The purpose of the following snippet is to get the result of the
+ ;; analysis from a file into a new buffer (or an old, if one with
+ ;; the specified name already exists). Also we want the erlang-mode
+ ;; *and* view-mode to be enabled.
+ (save-excursion
+ (let ((buf (get-buffer-create (format "*%s coverage*" module-name))))
+ (set-buffer buf)
+ (setq buffer-read-only nil)
+ (insert-file-contents tmp-filename nil nil nil t)
+ (if (= (buffer-size) 0)
+ (kill-buffer buf)
+ ;; FIXME: this would be a good place to enable (emacs-mode)
+ ;; to get some nice syntax highlighting in the
+ ;; coverage report, but it doesn't play well with
+ ;; flymake. Leave it off for now.
+ (view-buffer buf))))
+ (delete-file tmp-filename)))
+
+(defun erlang-eunit-compile-and-run-current-test ()
+ "Compile the source and test files and run the current EUnit test.
-;;; Compile source and EUnit test file and finally run EUnit tests for
-;;; the current module
-(defun erlang-eunit-compile-and-run-tests ()
- "Compile the source and test files and run the EUnit test suite.
+With prefix arg, compiles for debug and runs tests with the verbose flag set."
+ (interactive)
+ (let ((module-name (erlang-add-quotes-if-needed
+ (erlang-eunit-module-name buffer-file-name)))
+ (test-name (erlang-eunit-current-test)))
+ (erlang-eunit-compile-and-test
+ 'erlang-eunit-run-test (list module-name test-name))))
+
+(defun erlang-eunit-compile-and-run-module-tests ()
+ "Compile the source and test files and run all EUnit tests in the module.
With prefix arg, compiles for debug and runs tests with the verbose flag set."
(interactive)
+ (let ((module-name (erlang-add-quotes-if-needed
+ (erlang-eunit-source-module-name buffer-file-name))))
+ (erlang-eunit-compile-and-test
+ 'erlang-eunit-run-module-tests (list module-name))))
+
+;;; Compile source and EUnit test file and finally run EUnit tests for
+;;; the current module
+(defun erlang-eunit-compile-and-test (test-fun test-args &optional under-cover)
+ "Compile the source and test files and run the EUnit test suite.
+
+If under-cover is set to t, the module under test is compile for
+code coverage analysis. If under-cover is left out or not set,
+coverage analysis is disabled. The result of the code coverage
+is both printed to the erlang shell (the number of covered vs
+uncovered functions in a module) and written to a buffer called
+*<module> coverage* (which shows the source code for the module
+and the number of times each line is covered).
+With prefix arg, compiles for debug and runs tests with the verbose flag set."
+ (erlang-eunit-record-recent-compile under-cover)
(let ((src-filename (erlang-eunit-src-filename buffer-file-name))
(test-filename (erlang-eunit-test-filename buffer-file-name)))
@@ -151,7 +312,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
;; below, is to ask the question about saving buffers only once,
;; instead of possibly several: one for each file to compile,
;; for instance for both x.erl and x_tests.erl.
- (save-some-buffers)
+ (save-some-buffers erlang-eunit-autosave)
(flet ((save-some-buffers (&optional any) nil))
;; Compilation of the source file is mandatory (the file must
@@ -159,23 +320,56 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
;; test file on the other hand, is optional, since eunit tests may
;; be placed in the source file instead. Any compilation error
;; will prevent the subsequent steps to be run (hence the `and')
- (and (erlang-eunit-compile-file src-filename)
+ (and (erlang-eunit-compile-file src-filename under-cover)
(if (file-readable-p test-filename)
(erlang-eunit-compile-file test-filename)
t)
- (erlang-eunit-run-tests)))))
+ (apply test-fun test-args)
+ (if under-cover
+ (save-excursion
+ (set-buffer (find-file-noselect src-filename))
+ (erlang-eunit-analyze-coverage)))))))
+
+(defun erlang-eunit-compile-and-run-module-tests-under-cover ()
+ "Compile the source and test files and run the EUnit test suite and measure
+code coverage.
-(defun erlang-eunit-compile-file (file-path)
+With prefix arg, compiles for debug and runs tests with the verbose flag set."
+ (interactive)
+ (let ((module-name (erlang-add-quotes-if-needed
+ (erlang-eunit-source-module-name buffer-file-name))))
+ (erlang-eunit-compile-and-test
+ 'erlang-eunit-run-module-tests (list module-name) t)))
+
+(defun erlang-eunit-compile-file (file-path &optional under-cover)
(if (file-readable-p file-path)
(save-excursion
- (set-buffer (find-file-noselect file-path))
- (erlang-compile)
- (erlang-eunit-last-compilation-successful-p))
+ (set-buffer (find-file-noselect file-path))
+ ;; In order to run a code coverage analysis on a
+ ;; module, we have two options:
+ ;;
+ ;; * either compile the module with cover:compile instead of the
+ ;; regular compiler
+ ;;
+ ;; * or first compile the module with the regular compiler (but
+ ;; *with* debug_info) and then compile it for coverage
+ ;; analysis using cover:compile_beam.
+ ;;
+ ;; We could accomplish the first by changing the
+ ;; erlang-compile-erlang-function to cover:compile, but there's
+ ;; a risk that that's used for other purposes. Therefore, a
+ ;; safer alternative (although with more steps) is to add
+ ;; debug_info to the list of compiler options and go for the
+ ;; second alternative.
+ (if under-cover
+ (erlang-eunit-cover-compile)
+ (erlang-compile))
+ (erlang-eunit-last-compilation-successful-p))
(let ((msg (format "Could not read %s" file-path)))
- (erlang-eunit-inferior-erlang-send-command
+ (erlang-eunit-inferior-erlang-send-command
(format "%% WARNING: %s" msg))
(error msg))))
-
+
(defun erlang-eunit-last-compilation-successful-p ()
(save-excursion
(set-buffer inferior-erlang-buffer)
@@ -184,7 +378,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
(lambda (re) (let ((continue t)
(result t))
(while continue ; ignore warnings, stop at errors
- (if (re-search-forward re (point-max) t)
+ (if (re-search-forward re (point-max) t)
(if (erlang-eunit-is-compilation-warning)
t
(setq result nil)
@@ -195,7 +389,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
(mapcar (lambda (e) (car e)) erlang-error-regexp-alist))))
(defun erlang-eunit-is-compilation-warning ()
- (erlang-eunit-string-match-p
+ (erlang-eunit-string-match-p
"[0-9]+: Warning:"
(buffer-substring (line-beginning-position) (line-end-position))))
@@ -221,22 +415,22 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
;;; Key bindings
;;;====================================================================
-(defvar erlang-eunit-toggle-src-and-test-file-other-window-key "\C-c\C-et"
- "*Key to which the `erlang-eunit-toggle-src-and-test-file-other-window'
-function will be bound.")
-(defvar erlang-eunit-compile-and-run-tests-key "\C-c\C-ek"
- "*Key to which the `erlang-eunit-compile-and-run-tests'
-function will be bound.")
+(defconst erlang-eunit-key-bindings
+ '(("\C-c\C-et" erlang-eunit-toggle-src-and-test-file-other-window)
+ ("\C-c\C-ek" erlang-eunit-compile-and-run-module-tests)
+ ("\C-c\C-ej" erlang-eunit-compile-and-run-current-test)
+ ("\C-c\C-el" erlang-eunit-compile-and-run-recent)
+ ("\C-c\C-ec" erlang-eunit-compile-and-run-module-tests-under-cover)
+ ("\C-c\C-ev" erlang-eunit-cover-compile)
+ ("\C-c\C-ea" erlang-eunit-analyze-coverage)))
(defun erlang-eunit-add-key-bindings ()
- (erlang-eunit-ensure-keymap-for-key
- erlang-eunit-toggle-src-and-test-file-other-window-key)
- (local-set-key erlang-eunit-toggle-src-and-test-file-other-window-key
- 'erlang-eunit-toggle-src-and-test-file-other-window)
- (erlang-eunit-ensure-keymap-for-key
- erlang-eunit-compile-and-run-tests-key)
- (local-set-key erlang-eunit-compile-and-run-tests-key
- 'erlang-eunit-compile-and-run-tests))
+ (dolist (binding erlang-eunit-key-bindings)
+ (erlang-eunit-bind-key (car binding) (cadr binding))))
+
+(defun erlang-eunit-bind-key (key function)
+ (erlang-eunit-ensure-keymap-for-key key)
+ (local-set-key key function))
(defun erlang-eunit-ensure-keymap-for-key (key-seq)
(let ((prefix-keys (butlast (append key-seq nil)))
diff --git a/lib/tools/emacs/erlang-flymake.el b/lib/tools/emacs/erlang-flymake.el
new file mode 100644
index 0000000000..bc368e9454
--- /dev/null
+++ b/lib/tools/emacs/erlang-flymake.el
@@ -0,0 +1,102 @@
+;; erlang-flymake.el
+;;
+;; Syntax check erlang source code on the fly (integrates with flymake).
+;;
+;; Start using flymake with erlang by putting the following somewhere
+;; in your .emacs file:
+;;
+;; (require 'erlang-flymake)
+;;
+;; Flymake is rather eager and does its syntax checks frequently by
+;; default and if you are bothered by this, you might want to put the
+;; following in your .emacs as well:
+;;
+;; (erlang-flymake-only-on-save)
+;;
+;; There are a couple of variables which control the compilation options:
+;; * erlang-flymake-get-code-path-dirs-function
+;; * erlang-flymake-get-include-dirs-function
+;; * erlang-flymake-extra-opts
+;;
+;; This code is inspired by http://www.emacswiki.org/emacs/FlymakeErlang.
+
+(require 'flymake)
+(eval-when-compile
+ (require 'cl))
+
+(defvar erlang-flymake-command
+ "erlc"
+ "The command that will be used to perform the syntax check")
+
+(defvar erlang-flymake-get-code-path-dirs-function
+ 'erlang-flymake-get-code-path-dirs
+ "Return a list of ebin directories to add to the code path.")
+
+(defvar erlang-flymake-get-include-dirs-function
+ 'erlang-flymake-get-include-dirs
+ "Return a list of include directories to add to the compiler options.")
+
+(defvar erlang-flymake-extra-opts
+ (list "+warn_obsolete_guard"
+ "+warn_unused_import"
+ "+warn_shadow_vars"
+ "+warn_export_vars"
+ "+strong_validation"
+ "+report")
+ "A list of options that will be passed to the compiler")
+
+(defun erlang-flymake-only-on-save ()
+ "Trigger flymake only when the buffer is saved (disables syntax
+check on newline and when there are no changes)."
+ (interactive)
+ ;; There doesn't seem to be a way of disabling this; set to the
+ ;; largest int available as a workaround (most-positive-fixnum
+ ;; equates to 8.5 years on my machine, so it ought to be enough ;-) )
+ (setq flymake-no-changes-timeout most-positive-fixnum)
+ (setq flymake-start-syntax-check-on-newline nil))
+
+
+(defun erlang-flymake-get-code-path-dirs ()
+ (list (concat (erlang-flymake-get-app-dir) "ebin")))
+
+(defun erlang-flymake-get-include-dirs ()
+ (list (concat (erlang-flymake-get-app-dir) "include")))
+
+(defun erlang-flymake-get-app-dir ()
+ (let ((src-path (file-name-directory (buffer-file-name))))
+ (file-name-directory (directory-file-name src-path))))
+
+(defun erlang-flymake-init ()
+ (let* ((temp-file
+ (flet ((flymake-get-temp-dir () (erlang-flymake-temp-dir)))
+ (flymake-init-create-temp-buffer-copy
+ 'flymake-create-temp-with-folder-structure)))
+ (code-dir-opts
+ (erlang-flymake-flatten
+ (mapcar (lambda (dir) (list "-pa" dir))
+ (funcall erlang-flymake-get-code-path-dirs-function))))
+ (inc-dir-opts
+ (erlang-flymake-flatten
+ (mapcar (lambda (dir) (list "-I" dir))
+ (funcall erlang-flymake-get-include-dirs-function))))
+ (compile-opts
+ (append inc-dir-opts
+ code-dir-opts
+ erlang-flymake-extra-opts)))
+ (list erlang-flymake-command (append compile-opts (list temp-file)))))
+
+(defun erlang-flymake-temp-dir ()
+ ;; Squeeze the user's name in there in order to make sure that files
+ ;; for two users who are working on the same computer (like a linux
+ ;; box) don't collide
+ (format "%s/flymake-%s" temporary-file-directory user-login-name))
+
+(defun erlang-flymake-flatten (list)
+ (apply #'append list))
+
+(add-to-list 'flymake-allowed-file-name-masks
+ '("\\.erl\\'" erlang-flymake-init))
+(add-hook 'erlang-mode-hook 'flymake-mode)
+
+(provide 'erlang-flymake)
+;; erlang-flymake ends here
diff --git a/lib/tools/emacs/erlang-start.el b/lib/tools/emacs/erlang-start.el
index 542e81f24c..bbcea3e46a 100644
--- a/lib/tools/emacs/erlang-start.el
+++ b/lib/tools/emacs/erlang-start.el
@@ -90,6 +90,11 @@
(or (assoc (car b) auto-mode-alist)
(setq auto-mode-alist (cons b auto-mode-alist))))
+;;
+;; Associate files using interpreter "escript" with Erlang mode.
+;;
+
+(add-to-list 'interpreter-mode-alist (cons "escript" 'erlang-mode))
;;
;; Ignore files ending in ".jam", ".vee", and ".beam" when performing
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index da586ee09a..bc7a190fb4 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -1,7 +1,7 @@
;; erlang.el --- Major modes for editing and running Erlang
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 1996-2010. All Rights Reserved.
+;; Copyright Ericsson AB 1996-2011. All Rights Reserved.
;;
;; The contents of this file are subject to the Erlang Public License,
;; Version 1.1, (the "License"); you may not use this file except in
@@ -386,7 +386,8 @@ 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-stop-when-in-type-spec
+ 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 `>'
@@ -466,14 +467,17 @@ To activate the workaround, place the following in your `~/.emacs' file:
(defvar erlang-indent-level 4
"*Indentation of Erlang calls/clauses within blocks.")
+(put 'erlang-indent-level 'safe-local-variable 'integerp)
(defvar erlang-indent-guard 2
"*Indentation of Erlang guards.")
+(put 'erlang-indent-guard 'safe-local-variable 'integerp)
(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.")
+(put 'erlang-argument-indent 'safe-local-variable '(lambda (val) (or (null val) (integerp val))))
(defvar erlang-tab-always-indent t
"*Non-nil means TAB in Erlang mode should always re-indent the current line,
@@ -519,6 +523,32 @@ This is an elisp list of options. Each option can be either:
- a string
Example: '(bin_opt_info (i . \"/path1/include\") (i . \"/path2/include\"))")
+(defvar erlang-compile-command-function-alist
+ '((".erl\\'" . inferior-erlang-compute-erl-compile-command)
+ (".xrl\\'" . inferior-erlang-compute-leex-compile-command)
+ (".yrl\\'" . inferior-erlang-compute-yecc-compile-command)
+ ("." . inferior-erlang-compute-erl-compile-command))
+ "*Alist of filename patterns vs corresponding compilation functions.
+Each element looks like (REGEXP . FUNCTION). Compiling a file whose name
+matches REGEXP specifies FUNCTION to use to compute the compilation
+command. The FUNCTION will be called with two arguments: module name and
+default compilation options, like output directory. The FUNCTION
+is expected to return a string.")
+
+(defvar erlang-leex-compile-opts '()
+ "*Options to pass to leex when compiling xrl files.
+This is an elisp list of options. Each option can be either:
+- an atom
+- a dotted pair
+- a string")
+
+(defvar erlang-yecc-compile-opts '()
+ "*Options to pass to yecc when compiling yrl files.
+This is an elisp list of options. Each option can be either:
+- an atom
+- a dotted pair
+- a string")
+
(eval-and-compile
(defvar erlang-regexp-modern-p
(if (> erlang-emacs-major-version 21) t nil)
@@ -659,24 +689,30 @@ resulting regexp is surrounded by \\_< and \\_>."
(eval-and-compile
(defconst erlang-guards-regexp (erlang-regexp-opt erlang-guards 'symbols)))
-
(eval-and-compile
(defvar erlang-predefined-types
'("any"
"arity"
+ "boolean"
"byte"
"char"
"cons"
"deep_string"
+ "iolist"
"maybe_improper_list"
+ "module"
"mfa"
"nil"
+ "neg_integer"
"none"
"non_neg_integer"
"nonempty_list"
"nonempty_improper_list"
"nonempty_maybe_improper_list"
+ "no_return"
+ "pos_integer"
"string"
+ "term"
"timeout")
"Erlang type specs types"))
@@ -885,15 +921,54 @@ files written in other languages than Erlang.")
If nil, the inferior shell replaces the window. This is the traditional
behaviour.")
-(defvar erlang-mode-map nil
+(defconst inferior-erlang-use-cmm (boundp 'minor-mode-overriding-map-alist)
+ "Non-nil means use `compilation-minor-mode' in Erlang shell.")
+
+(defvar erlang-mode-map
+ (let ((map (make-sparse-keymap)))
+ (unless (boundp 'indent-line-function)
+ (define-key map "\t" 'erlang-indent-command))
+ (define-key map ";" 'erlang-electric-semicolon)
+ (define-key map "," 'erlang-electric-comma)
+ (define-key map "<" 'erlang-electric-lt)
+ (define-key map ">" 'erlang-electric-gt)
+ (define-key map "\C-m" 'erlang-electric-newline)
+ (if (not (boundp 'delete-key-deletes-forward))
+ (define-key map "\177" 'backward-delete-char-untabify)
+ (define-key map [(backspace)] 'backward-delete-char-untabify))
+ ;;(unless (boundp 'fill-paragraph-function)
+ (define-key map "\M-q" 'erlang-fill-paragraph)
+ (unless (boundp 'beginning-of-defun-function)
+ (define-key map "\M-\C-a" 'erlang-beginning-of-function)
+ (define-key map "\M-\C-e" 'erlang-end-of-function)
+ (define-key map '(meta control h) 'erlang-mark-function)) ; Xemacs
+ (define-key map "\M-\t" 'erlang-complete-tag)
+ (define-key map "\C-c\M-\t" 'tempo-complete-tag)
+ (define-key map "\M-+" 'erlang-find-next-tag)
+ (define-key map "\C-c\M-a" 'erlang-beginning-of-clause)
+ (define-key map "\C-c\M-b" 'tempo-backward-mark)
+ (define-key map "\C-c\M-e" 'erlang-end-of-clause)
+ (define-key map "\C-c\M-f" 'tempo-forward-mark)
+ (define-key map "\C-c\M-h" 'erlang-mark-clause)
+ (define-key map "\C-c\C-c" 'comment-region)
+ (define-key map "\C-c\C-j" 'erlang-generate-new-clause)
+ (define-key map "\C-c\C-k" 'erlang-compile)
+ (define-key map "\C-c\C-l" 'erlang-compile-display)
+ (define-key map "\C-c\C-s" 'erlang-show-syntactic-information)
+ (define-key map "\C-c\C-q" 'erlang-indent-function)
+ (define-key map "\C-c\C-u" 'erlang-uncomment-region)
+ (define-key map "\C-c\C-y" 'erlang-clone-arguments)
+ (define-key map "\C-c\C-a" 'erlang-align-arrows)
+ (define-key map "\C-c\C-z" 'erlang-shell-display)
+ (unless inferior-erlang-use-cmm
+ (define-key map "\C-x`" 'erlang-next-error))
+ map)
"*Keymap used in Erlang mode.")
(defvar erlang-mode-abbrev-table nil
"Abbrev table in use in Erlang-mode buffers.")
(defvar erlang-mode-syntax-table nil
"Syntax table in use in Erlang-mode buffers.")
-(defconst inferior-erlang-use-cmm (boundp 'minor-mode-overriding-map-alist)
- "Non-nil means use `compilation-minor-mode' in Erlang shell.")
(defvar erlang-skel-file "erlang-skels"
@@ -988,7 +1063,7 @@ behaviour.")
(list (concat "^\\(-" erlang-atom-regexp "\\)\\(\\s-\\|\\.\\|(\\)")
1 (if (boundp 'font-lock-preprocessor-face)
'font-lock-preprocessor-face
- 'font-lock-function-name-face)))
+ 'font-lock-constant-face)))
"Font lock keyword highlighting attributes.")
(defvar erlang-font-lock-keywords-quotes
@@ -1019,10 +1094,12 @@ are highlighted by syntactic analysis.")
(list
(list (concat "?\\s-*\\(" erlang-atom-regexp
"\\|" erlang-variable-regexp "\\)")
- 1 'font-lock-type-face)
+ 1 'font-lock-constant-face)
(list (concat "^\\(-\\(?:define\\|ifn?def\\)\\)\\s-*(\\s-*\\(" erlang-atom-regexp
"\\|" erlang-variable-regexp "\\)")
- (list 1 'font-lock-preprocessor-face t)
+ (if (boundp 'font-lock-preprocessor-face)
+ (list 1 'font-lock-preprocessor-face t)
+ (list 1 'font-lock-constant-face t))
(list 3 'font-lock-type-face t t))
(list "^-e\\(lse\\|ndif\\)\\>" 0 'font-lock-preprocessor-face t))
"Font lock keyword highlighting macros.
@@ -1148,7 +1225,7 @@ Lock syntax table. The effect is that `apply' in the atom
`( (char-after (1- (or ,pos (point)))))))
;; defvar some obsolete variables, which we still support for
-;; backwardscompatibility reasons.
+;; backwards compatibility reasons.
(eval-when-compile
(defvar comment-indent-hook)
(defvar dabbrev-case-fold-search)
@@ -1245,7 +1322,7 @@ Other commands:
(setq major-mode 'erlang-mode)
(setq mode-name "Erlang")
(erlang-syntax-table-init)
- (erlang-keymap-init)
+ (use-local-map erlang-mode-map)
(erlang-electric-init)
(erlang-menu-init)
(erlang-mode-variables)
@@ -1300,53 +1377,6 @@ Other commands:
(set-syntax-table erlang-mode-syntax-table))
-(defun erlang-keymap-init ()
- (if erlang-mode-map
- nil
- (setq erlang-mode-map (make-sparse-keymap))
- (erlang-mode-commands erlang-mode-map))
- (use-local-map erlang-mode-map))
-
-
-(defun erlang-mode-commands (map)
- (unless (boundp 'indent-line-function)
- (define-key map "\t" 'erlang-indent-command))
- (define-key map ";" 'erlang-electric-semicolon)
- (define-key map "," 'erlang-electric-comma)
- (define-key map "<" 'erlang-electric-lt)
- (define-key map ">" 'erlang-electric-gt)
- (define-key map "\C-m" 'erlang-electric-newline)
- (if (not (boundp 'delete-key-deletes-forward))
- (define-key map "\177" 'backward-delete-char-untabify)
- (define-key map [(backspace)] 'backward-delete-char-untabify))
- ;;(unless (boundp 'fill-paragraph-function)
- (define-key map "\M-q" 'erlang-fill-paragraph)
- (unless (boundp 'beginning-of-defun-function)
- (define-key map "\M-\C-a" 'erlang-beginning-of-function)
- (define-key map "\M-\C-e" 'erlang-end-of-function)
- (define-key map '(meta control h) 'erlang-mark-function)) ; Xemacs
- (define-key map "\M-\t" 'erlang-complete-tag)
- (define-key map "\C-c\M-\t" 'tempo-complete-tag)
- (define-key map "\M-+" 'erlang-find-next-tag)
- (define-key map "\C-c\M-a" 'erlang-beginning-of-clause)
- (define-key map "\C-c\M-b" 'tempo-backward-mark)
- (define-key map "\C-c\M-e" 'erlang-end-of-clause)
- (define-key map "\C-c\M-f" 'tempo-forward-mark)
- (define-key map "\C-c\M-h" 'erlang-mark-clause)
- (define-key map "\C-c\C-c" 'comment-region)
- (define-key map "\C-c\C-j" 'erlang-generate-new-clause)
- (define-key map "\C-c\C-k" 'erlang-compile)
- (define-key map "\C-c\C-l" 'erlang-compile-display)
- (define-key map "\C-c\C-s" 'erlang-show-syntactic-information)
- (define-key map "\C-c\C-q" 'erlang-indent-function)
- (define-key map "\C-c\C-u" 'erlang-uncomment-region)
- (define-key map "\C-c\C-y" 'erlang-clone-arguments)
- (define-key map "\C-c\C-a" 'erlang-align-arrows)
- (define-key map "\C-c\C-z" 'erlang-shell-display)
- (unless inferior-erlang-use-cmm
- (define-key map "\C-x`" 'erlang-next-error)))
-
-
(defun erlang-electric-init ()
;; Set up electric character functions to work with
;; delsel/pending-del mode. Also, set up text properties for bit
@@ -1400,7 +1430,7 @@ Other commands:
(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)
+ 'erlang-get-function-name-and-arity)
(set (make-local-variable 'tempo-match-finder)
"[^-a-zA-Z0-9_]\\([-a-zA-Z0-9_]*\\)\\=")
(set (make-local-variable 'beginning-of-defun-function)
@@ -1481,7 +1511,23 @@ Other commands:
erlang-font-lock-keywords-3
erlang-font-lock-keywords-4)
nil nil ((?_ . "w")) erlang-beginning-of-clause
- (font-lock-mark-block-function . erlang-mark-clause))))
+ (font-lock-mark-block-function . erlang-mark-clause)
+ (font-lock-syntactic-keywords
+ ;; A dollar sign right before the double quote that ends a
+ ;; string is not a character escape.
+ ;;
+ ;; And a "string" has with a double quote not escaped by a
+ ;; dollar sign, any number of non-backslash non-newline
+ ;; characters or escaped backslashes, a dollar sign
+ ;; (otherwise we wouldn't care) and a double quote. This
+ ;; doesn't match multi-line strings, but this is probably
+ ;; the best we can get, since while font-locking we don't
+ ;; know whether matching started inside a string: limiting
+ ;; search to a single line keeps things sane.
+ . (("\\(?:^\\|[^$]\\)\"\\(?:[^\"\n]\\|\\\\\"\\)*\\(\\$\\)\"" 1 "w")
+ ;; And the dollar sign in $\" escapes two characters, not
+ ;; just one.
+ ("\\(\\$\\)\\\\\\\"" 1 "'"))))))
@@ -2490,9 +2536,10 @@ Value is list (stack token-start token-type in-what)."
((looking-at "\\(of\\)[^_a-zA-Z0-9]")
;; Must handle separately, try X of -> catch
(if (and stack (eq (car (car stack)) 'try))
- (let ((try-column (nth 2 (car stack))))
+ (let ((try-column (nth 2 (car stack)))
+ (try-pos (nth 1 (car stack))))
(erlang-pop stack)
- (erlang-push (list 'icr token try-column) stack))))
+ (erlang-push (list 'icr try-pos try-column) stack))))
((looking-at "\\(fun\\)[^_a-zA-Z0-9]")
;; Push a new layer if we are defining a `fun'
@@ -2653,7 +2700,8 @@ Value is list (stack token-start token-type in-what)."
(cond ((eq (car (car stack)) '\()
(erlang-pop stack)
(if (and (eq (car (car stack)) 'fun)
- (eq (car (car (cdr stack))) '::))
+ (or (eq (car (car (last stack))) 'spec)
+ (eq (car (car (cdr stack))) '::))) ;; -type()
;; Inside fun type def ') closes fun definition
(erlang-pop stack)))
((eq (car (car stack)) 'icr)
@@ -2752,7 +2800,7 @@ Return nil if inside string, t if in a comment."
;;
;; `after' should be indented to the same level as the
;; corresponding receive.
- (cond ((looking-at "\\(after\\|catch\\|of\\)\\($\\|[^_a-zA-Z0-9]\\)")
+ (cond ((looking-at "\\(after\\|of\\)\\($\\|[^_a-zA-Z0-9]\\)")
(nth 2 stack-top))
((looking-at "when[^_a-zA-Z0-9]")
;; Handling one when part
@@ -2771,7 +2819,7 @@ Return nil if inside string, t if in a comment."
((and (eq (car stack-top) '||) (looking-at "\\(]\\|>>\\)[^_a-zA-Z0-9]"))
(nth 2 (car (cdr stack))))
;; Real indentation, where operators create extra indentation etc.
- ((memq (car stack-top) '(-> || begin try))
+ ((memq (car stack-top) '(-> || try begin))
(if (looking-at "\\(of\\)[^_a-zA-Z0-9]")
(nth 2 stack-top)
(goto-char (nth 1 stack-top))
@@ -2800,19 +2848,24 @@ Return nil if inside string, t if in a comment."
(erlang-caddr (car stack))
0))
((looking-at "catch\\($\\|[^_a-zA-Z0-9]\\)")
- (if (or (eq (car stack-top) 'try)
- (eq (car (car (cdr stack))) 'icr))
- (progn
- (if (eq (car stack-top) '->)
- (erlang-pop stack))
- (if stack
- (erlang-caddr (car stack))
- 0))
- base)) ;; old catch
+ ;; Are we in a try
+ (let ((start (if (eq (car stack-top) '->)
+ (car (cdr stack))
+ stack-top)))
+ (if (null start) nil
+ (goto-char (nth 1 start)))
+ (cond ((looking-at "try\\($\\|[^_a-zA-Z0-9]\\)")
+ (progn
+ (if (eq (car stack-top) '->)
+ (erlang-pop stack))
+ (if stack
+ (erlang-caddr (car stack))
+ 0)))
+ (t (erlang-indent-standard indent-point token base 'nil))))) ;; old catch
(t
(erlang-indent-standard indent-point token base 'nil)
))))
- ))
+ ))
((eq (car stack-top) 'when)
(goto-char (nth 1 stack-top))
(if (looking-at "when\\s *\\($\\|%\\)")
@@ -2838,27 +2891,32 @@ Return nil if inside string, t if in a comment."
(current-column)))
;; Type and Spec indentation
((eq (car stack-top) '::)
- (cond ((null erlang-argument-indent)
- ;; indent to next column.
- (+ 2 (nth 2 stack-top)))
- ((looking-at "::[^_a-zA-Z0-9]")
- (nth 2 stack-top))
- (t
- (let ((start-alternativ (if (looking-at "|") 2 0)))
- (goto-char (nth 1 stack-top))
- (- (cond ((looking-at "::\\s *\\($\\|%\\)")
- ;; Line ends with ::
- (if (eq (car (car (last stack))) 'spec)
+ (if (looking-at "}")
+ ;; Closing record definition with types
+ ;; pop stack and recurse
+ (erlang-calculate-stack-indent indent-point
+ (cons (erlang-pop stack) (cdr state)))
+ (cond ((null erlang-argument-indent)
+ ;; indent to next column.
+ (+ 2 (nth 2 stack-top)))
+ ((looking-at "::[^_a-zA-Z0-9]")
+ (nth 2 stack-top))
+ (t
+ (let ((start-alternativ (if (looking-at "|") 2 0)))
+ (goto-char (nth 1 stack-top))
+ (- (cond ((looking-at "::\\s *\\($\\|%\\)")
+ ;; Line ends with ::
+ (if (eq (car (car (last stack))) 'spec)
(+ (erlang-indent-find-preceding-expr 1)
erlang-argument-indent)
- (+ (erlang-indent-find-preceding-expr 2)
- erlang-argument-indent)))
- (t
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (+ 2 (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column))) start-alternativ)))))
+ (+ (erlang-indent-find-preceding-expr 2)
+ erlang-argument-indent)))
+ (t
+ ;; Indent to the same column as the first
+ ;; argument.
+ (goto-char (+ 2 (nth 1 stack-top)))
+ (skip-chars-forward " \t")
+ (current-column))) start-alternativ))))))
)))
(defun erlang-indent-standard (indent-point token base inside-parenthesis)
@@ -2874,8 +2932,8 @@ Return nil if inside string, t if in a comment."
(+ base erlang-indent-level))
(t
(goto-char indent-point)
- (cond ((memq (following-char) '(?\( ?{))
- ;; Function application or record.
+ (cond ((memq (following-char) '(?\( ))
+ ;; Function application.
(+ (erlang-indent-find-preceding-expr)
erlang-argument-indent))
;; Empty line, or end; treat it as the end of
@@ -2930,10 +2988,16 @@ This assumes that the preceding expression is either simple
(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)))))
+ col
+ ;; Special hack to handle: (note line break)
+ ;; [#myrecord{
+ ;; foo = foo}]
+ (or
+ (ignore-errors
+ (backward-char 1)
+ (forward-sexp -1)
+ (current-column))
+ col)))))
(defun erlang-indent-parenthesis (stack-position)
(let ((previous (erlang-indent-find-preceding-expr)))
@@ -3472,8 +3536,8 @@ Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
(erlang-get-function-arrow)))"
(and
(save-excursion
- (re-search-forward "[^-:]*-\\|:" (point-max) t)
- (erlang-buffer-substring (- (point) 1) (+ (point) 1)))))
+ (re-search-forward "->" (point-max) t)
+ (erlang-buffer-substring (- (point) 2) (+ (point) 1)))))
(defun erlang-get-function-arity ()
"Return the number of arguments of function at point, or nil."
@@ -3502,6 +3566,13 @@ Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
res)
(error nil)))))
+(defun erlang-get-function-name-and-arity ()
+ "Return the name and arity of the function at point, or nil.
+The return value is a string of the form \"foo/1\"."
+ (let ((name (erlang-get-function-name))
+ (arity (erlang-get-function-arity)))
+ (and name arity (format "%s/%d" name arity))))
+
(defun erlang-get-function-arguments ()
"Return arguments of current function, or nil."
(if (not (looking-at (eval-when-compile
@@ -3677,6 +3748,7 @@ non-whitespace characters following the point on the current line."
(setq erlang-electric-newline-inhibit nil)
(setq erlang-electric-newline-inhibit t)
(undo-boundary)
+ (erlang-indent-line)
(end-of-line)
(newline)
(if (condition-case nil
@@ -4000,6 +4072,16 @@ This function is designed to be a member of a criteria list."
nil)))
+(defun erlang-stop-when-in-type-spec ()
+ "Return `stop' when in a type spec line.
+
+This function is designed to be a member of a criteria list."
+ (save-excursion
+ (beginning-of-line)
+ (when (save-match-data (looking-at "-\\(spec\\|type\\)"))
+ 'stop)))
+
+
(defun erlang-next-lines-empty-p ()
"Return non-nil if next lines are empty.
@@ -4897,9 +4979,14 @@ a prompt. When nil, we will wait forever, or until \\[keyboard-quit].")
(defvar inferior-erlang-buffer nil
"Buffer of last invoked inferior Erlang, or nil.")
+;; Enable uniquifying Erlang shell buffers based on directory name.
+(eval-after-load "uniquify"
+ '(add-to-list 'uniquify-list-buffers-directory-modes 'erlang-shell-mode))
+
;;;###autoload
-(defun inferior-erlang ()
+(defun inferior-erlang (&optional command)
"Run an inferior Erlang.
+With prefix command, prompt for command to start Erlang with.
This is just like running Erlang in a normal shell, except that
an Emacs buffer is used for input and output.
@@ -4913,17 +5000,37 @@ Entry to this mode calls the functions in the variables
The following commands imitate the usual Unix interrupt and
editing control characters:
\\{erlang-shell-mode-map}"
- (interactive)
+ (interactive
+ (when current-prefix-arg
+ (list (if (fboundp 'read-shell-command)
+ ;; `read-shell-command' is a new function in Emacs 23.
+ (read-shell-command "Erlang command: ")
+ (read-string "Erlang command: ")))))
(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)))
+ (let (cmd opts)
+ (if command
+ (setq cmd "sh"
+ opts (list "-c" command))
+ (setq cmd inferior-erlang-machine
+ 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)))))
+
+ ;; Using create-file-buffer and list-buffers-directory in this way
+ ;; makes uniquify give each buffer a unique name based on the
+ ;; directory.
+ (let ((fake-file-name (expand-file-name inferior-erlang-buffer-name default-directory)))
+ (setq inferior-erlang-buffer (create-file-buffer fake-file-name))
+ (apply 'make-comint-in-buffer
+ inferior-erlang-process-name
+ inferior-erlang-buffer
+ cmd
+ nil opts)
+ (with-current-buffer inferior-erlang-buffer
+ (setq list-buffers-directory fake-file-name))))
+
(setq inferior-erlang-process
(get-buffer-process inferior-erlang-buffer))
(if (> 21 erlang-emacs-major-version) ; funcalls to avoid compiler warnings
@@ -4936,10 +5043,6 @@ editing control characters:
(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))
@@ -5199,6 +5302,22 @@ unless the optional NO-DISPLAY is non-nil."
(file-name-as-directory buffer-dir))))
(defun inferior-erlang-compute-compile-command (module-name opts)
+ (let ((ccfn erlang-compile-command-function-alist)
+ (res (inferior-erlang-compute-erl-compile-command module-name opts))
+ ccfn-entry
+ done)
+ (if (not (null (buffer-file-name)))
+ (while (and (not done) (not (null ccfn)))
+ (setq ccfn-entry (car ccfn))
+ (setq ccfn (cdr ccfn))
+ (if (string-match (car ccfn-entry) (buffer-file-name))
+ (let ((c-fn (cdr ccfn-entry)))
+ (setq done t)
+ (if (not (null c-fn))
+ (setq result (funcall c-fn module-name opts)))))))
+ result))
+
+(defun inferior-erlang-compute-erl-compile-command (module-name opts)
(let* ((out-dir-opt (assoc 'outdir opts))
(out-dir (cdr out-dir-opt)))
(if erlang-compile-use-outdir
@@ -5222,6 +5341,48 @@ unless the optional NO-DISPLAY is non-nil."
(remq out-dir-opt opts))
tmpvar tmpvar tmpvar2)))))
+(defun inferior-erlang-compute-leex-compile-command (module-name opts)
+ (let ((file-name (buffer-file-name))
+ (erl-compile-expr (inferior-erlang-remove-any-trailing-dot
+ (inferior-erlang-compute-erl-compile-command
+ module-name opts))))
+ (format (concat "f(LErr1__), f(LErr2__), "
+ "case case leex:file(\"%s\", [%s]) of"
+ " ok -> ok;"
+ " {ok,_} -> ok;"
+ " {ok,_,_} -> ok;"
+ " LErr1__ -> LErr1__ "
+ "end of"
+ " ok -> %s;"
+ " LErr2__ -> LErr2__ "
+ "end.")
+ file-name
+ (inferior-erlang-format-comma-opts erlang-leex-compile-opts)
+ erl-compile-expr)))
+
+(defun inferior-erlang-compute-yecc-compile-command (module-name opts)
+ (let ((file-name (buffer-file-name))
+ (erl-compile-expr (inferior-erlang-remove-any-trailing-dot
+ (inferior-erlang-compute-erl-compile-command
+ module-name opts))))
+ (format (concat "f(YErr1__), f(YErr2__), "
+ "case case yecc:file(\"%s\", [%s]) of"
+ " {ok,_} -> ok;"
+ " {ok,_,_} -> ok;"
+ " YErr1__ -> YErr1__ "
+ "end of"
+ " ok -> %s;"
+ " YErr2__ -> YErr2__ "
+ "end.")
+ file-name
+ (inferior-erlang-format-comma-opts erlang-yecc-compile-opts)
+ erl-compile-expr)))
+
+(defun inferior-erlang-remove-any-trailing-dot (str)
+ (if (string= (substring str -1) ".")
+ (substring str 0 (1- (length str)))
+ str))
+
(defun inferior-erlang-format-comma-opts (opts)
(if (null opts)
""
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
index 1ccced9177..2948ccf1b5 100644
--- a/lib/tools/emacs/test.erl.indented
+++ b/lib/tools/emacs/test.erl.indented
@@ -1,20 +1,20 @@
%% -*- erlang -*-
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%%-------------------------------------------------------------------
@@ -93,11 +93,27 @@
-type t13() :: maybe_improper_list(integer(), t11()).
-type t14() :: [erl_scan:foo() |
%% Should be highlighted
- non_neg_integer() | nonempty_list() |
+ term() |
+ bool() |
+ byte() |
+ char() |
+ non_neg_integer() | nonempty_list() |
+ pos_integer() |
+ neg_integer() |
+ number() |
+ list() |
nonempty_improper_list() | nonempty_maybe_improper_list() |
+ maybe_improper_list() | string() | iolist() | byte() |
+ module() |
+ mfa() |
+ node() |
+ timeout() |
+ no_return() |
%% Should not be highlighted
nonempty_() | nonlist() |
- erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+ erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+
+
-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
<<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
@@ -146,6 +162,8 @@
| {'del_member', name(), pid()},
#state{}) -> {'noreply', #state{}}.
+-spec all(fun((T) -> boolean()), List :: [T]) ->
+ boolean() when is_subtype(T, term()). % (*)
-spec get_closest_pid(term()) ->
Return :: pid()
@@ -170,6 +188,9 @@
f19 = 3 :: integer()|undefined,
f5 = 3 :: undefined|integer()}).
+-record(state, {
+ sequence_number = 1 :: integer()
+ }).
highlighting(X) % Function definitions should be highlighted
@@ -349,6 +370,14 @@ indent_basics(X, Y, Z) % AD added clause
foo.
+
+indent_nested() ->
+ [
+ {foo, 2, "string"},
+ {bar, 3, "another string"}
+ ].
+
+
indent_icr(Z) -> % icr = if case receive
%% If
if Z >= 0 ->
@@ -483,7 +512,9 @@ indent_try_catch() ->
file:close(Xfile)
end;
indent_try_catch() ->
- try foo(bar) of
+ try
+ foo(bar)
+ of
X when true andalso
kalle ->
io:format(stdout, "Parsing file ~s, ",
@@ -541,14 +572,57 @@ indent_catch() ->
C = catch B +
float(43.1),
- case catch (X) of
+ case catch foo(X) of
+ A ->
+ B
+ end,
+
+ case
+ catch foo(X)
+ of
A ->
B
end,
+
+ case
+ foo(X)
+ of
+ A ->
+ catch B,
+ X
+ end,
+
try sune of
_ -> foo
catch _:_ -> baf
- end.
+ end,
+
+ try
+ sune
+ of
+ _ ->
+ X = 5,
+ (catch foo(X)),
+ X + 10
+ catch _:_ -> baf
+ end,
+
+ try
+ (catch sune)
+ of
+ _ ->
+ catch foo() %% BUGBUG can't handle catch inside try without parentheses
+ catch _:_ ->
+ baf
+ end,
+
+ try
+ (catch exit())
+ catch
+ _ ->
+ catch baf()
+ end,
+ ok.
indent_binary() ->
X = lists:foldr(fun(M) ->
@@ -578,3 +652,8 @@ indent_comprehensions() ->
true = (X rem 2)
>>,
ok.
+
+%% This causes an error in earlier erlang-mode versions.
+foo() ->
+ [#foo{
+ foo = foo}].
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
index 9b4203120b..1221c5655e 100644
--- a/lib/tools/emacs/test.erl.orig
+++ b/lib/tools/emacs/test.erl.orig
@@ -1,20 +1,20 @@
%% -*- erlang -*-
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%%-------------------------------------------------------------------
@@ -93,11 +93,27 @@
-type t13() :: maybe_improper_list(integer(), t11()).
-type t14() :: [erl_scan:foo() |
%% Should be highlighted
- non_neg_integer() | nonempty_list() |
+ term() |
+ bool() |
+ byte() |
+ char() |
+ non_neg_integer() | nonempty_list() |
+ pos_integer() |
+ neg_integer() |
+ number() |
+ list() |
nonempty_improper_list() | nonempty_maybe_improper_list() |
+ maybe_improper_list() | string() | iolist() | byte() |
+ module() |
+ mfa() |
+ node() |
+ timeout() |
+ no_return() |
%% Should not be highlighted
nonempty_() | nonlist() |
-erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+ erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+
+
-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
<<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
@@ -146,6 +162,8 @@ t15(),t20(),t21(), t22(),t25()}.
| {'del_member', name(), pid()},
#state{}) -> {'noreply', #state{}}.
+-spec all(fun((T) -> boolean()), List :: [T]) ->
+ boolean() when is_subtype(T, term()). % (*)
-spec get_closest_pid(term()) ->
Return :: pid()
@@ -170,6 +188,9 @@ f18 :: 1 | 2 | 'undefined',
f19 = 3 :: integer()|undefined,
f5 = 3 :: undefined|integer()}).
+-record(state, {
+ sequence_number = 1 :: integer()
+ }).
highlighting(X) % Function definitions should be highlighted
@@ -349,6 +370,14 @@ indent_basics(X, Y, Z) % AD added clause
foo.
+
+indent_nested() ->
+ [
+ {foo, 2, "string"},
+ {bar, 3, "another string"}
+ ].
+
+
indent_icr(Z) -> % icr = if case receive
%% If
if Z >= 0 ->
@@ -483,7 +512,9 @@ indent_try_catch() ->
file:close(Xfile)
end;
indent_try_catch() ->
- try foo(bar) of
+ try
+ foo(bar)
+ of
X when true andalso
kalle ->
io:format(stdout, "Parsing file ~s, ",
@@ -541,14 +572,57 @@ indent_catch() ->
C = catch B +
float(43.1),
- case catch (X) of
+ case catch foo(X) of
A ->
B
end,
+
+ case
+ catch foo(X)
+ of
+ A ->
+ B
+ end,
+
+ case
+ foo(X)
+ of
+ A ->
+ catch B,
+ X
+ end,
+
try sune of
- _ -> foo
- catch _:_ -> baf
- end.
+ _ -> foo
+ catch _:_ -> baf
+ end,
+
+ try
+sune
+ of
+ _ ->
+ X = 5,
+ (catch foo(X)),
+ X + 10
+ catch _:_ -> baf
+ end,
+
+ try
+ (catch sune)
+ of
+ _ ->
+ catch foo() %% BUGBUG can't handle catch inside try without parentheses
+ catch _:_ ->
+ baf
+ end,
+
+ try
+(catch exit())
+ catch
+_ ->
+ catch baf()
+ end,
+ ok.
indent_binary() ->
X = lists:foldr(fun(M) ->
@@ -578,3 +652,8 @@ Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
true = (X rem 2)
>>,
ok.
+
+%% This causes an error in earlier erlang-mode versions.
+foo() ->
+[#foo{
+foo = foo}].