aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tools
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tools')
-rw-r--r--lib/tools/doc/src/lcnt.xml12
-rw-r--r--lib/tools/doc/src/lcnt_chapter.xml32
-rw-r--r--lib/tools/doc/src/make.xml26
-rw-r--r--lib/tools/doc/src/notes.xml37
-rw-r--r--lib/tools/emacs/erlang-edoc.el6
-rw-r--r--lib/tools/emacs/erlang-eunit.el48
-rw-r--r--lib/tools/emacs/erlang-flymake.el3
-rw-r--r--lib/tools/emacs/erlang-pkg.el4
-rw-r--r--lib/tools/emacs/erlang-start.el24
-rw-r--r--lib/tools/emacs/erlang-test.el93
-rw-r--r--lib/tools/emacs/erlang.el1365
-rw-r--r--lib/tools/emacs/erldoc.el16
-rw-r--r--lib/tools/src/cover.erl44
-rw-r--r--lib/tools/src/lcnt.erl58
-rw-r--r--lib/tools/src/make.erl83
-rw-r--r--lib/tools/src/tools.app.src5
-rw-r--r--lib/tools/src/xref_base.erl3
-rw-r--r--lib/tools/src/xref_reader.erl57
-rw-r--r--lib/tools/test/Makefile6
-rw-r--r--lib/tools/test/lcnt_SUITE.erl87
-rw-r--r--lib/tools/test/make_SUITE.erl60
-rw-r--r--lib/tools/test/make_SUITE_data/incl_src/test_incl2.erl9
-rw-r--r--lib/tools/test/make_SUITE_data/test_incl.hrl1
-rw-r--r--lib/tools/test/make_SUITE_data/test_incl1.erl9
-rw-r--r--lib/tools/test/xref_SUITE.erl13
-rw-r--r--lib/tools/vsn.mk2
26 files changed, 1216 insertions, 887 deletions
diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml
index 9c8ce148e9..31e5c241e9 100644
--- a/lib/tools/doc/src/lcnt.xml
+++ b/lib/tools/doc/src/lcnt.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2009</year>
- <year>2016</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -38,7 +38,7 @@
<modulesummary>A runtime system Lock Profiling tool.</modulesummary>
<description>
<p>The <c>lcnt</c> module is used to profile the internal ethread locks in the
- Erlang Runtime System. With <c>lcnt</c> enabled, Internal counters in the
+ Erlang Runtime System. With <c>lcnt</c> enabled, internal counters in the
runtime system are updated each time a lock is taken. The counters stores
information about the number of acquisition tries and the number of collisions
that has occurred during the acquisition tries. The counters also record the
@@ -47,17 +47,17 @@
<p>
The data produced by the lock counters will give an estimate on how well
the runtime system will behave from a parallelizable view point for the
- scenarios tested. This tool was mainly developed to help erlang runtime
+ scenarios tested. This tool was mainly developed to help Erlang runtime
developers iron out potential and generic bottlenecks.
</p>
<p>Locks in the emulator are named after what type of resource they protect and where
in the emulator they are initialized, those are lock 'classes'. Most of those
locks are also instantiated several times, and given unique identifiers, to increase
locking granularity. Typically an instantiated lock protects a disjunct set of
- the resource, i.e ets-tables, processes or ports. In other cases it protects a
- specific range of a resource, e.g. <c>pix_lock</c> which protects index to process
+ the resource, for example ets tables, processes or ports. In other cases it protects a
+ specific range of a resource, for example <c>pix_lock</c> which protects index to process
mappings, and is given a unique number within the class. A unique lock in <c>lcnt</c>
- is referenced by a name (class) and an identifier, <c>{Name, Id}</c>.
+ is referenced by a name (class) and an identifier: <c>{Name, Id}</c>.
</p>
<p>Some locks in the system are static and protects global resources, for example
<c>bif_timers</c> and the <c>run_queue</c> locks. Other locks are dynamic and not
diff --git a/lib/tools/doc/src/lcnt_chapter.xml b/lib/tools/doc/src/lcnt_chapter.xml
index 6cfdb5cf1b..c73fcb31e0 100644
--- a/lib/tools/doc/src/lcnt_chapter.xml
+++ b/lib/tools/doc/src/lcnt_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2009</year><year>2016</year>
+ <year>2009</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -42,42 +42,32 @@
completed its access to the resource and unlocked it. The <c>lcnt</c> tool measures these lock conflicts.
</p>
<p>
- Locks has an inherent cost in execution time and memory space. It takes time initialize, destroy, aquiring or releasing locks. To decrease lock contention it
+ Locks have an inherent cost in execution time and memory space. It takes time initialize, destroy, aquiring or releasing locks. To decrease lock contention it
some times necessary to use finer grained locking strategies. This will usually also increase the locking overhead and hence there is a tradeoff
between lock contention and overhead. In general, lock contention increases with the number of threads running concurrently. The <c>lcnt</c> tool does not measure locking overhead.
</p>
<section>
<title> Enabling lock-counting </title>
- <p>For investigation of locks in the emulator we use an internal tool called <c>lcnt</c> (short for lock-count). The VM needs to be compiled with this option enabled. To enable this, use:</p>
+ <p>For investigation of locks in the emulator we use an internal tool called <c>lcnt</c> (short for lock-count). The VM needs to be compiled with this option enabled.
+ To compile a lock-counting VM along with a normal VM, use:</p>
<pre>
cd $ERL_TOP
-./configure --enable-lock-counter
- </pre>
-
- <p>
- Another way to enable this alongside a normal VM is to compile it at emulator directory level, much like a debug build. To compile it this way do the following,
- </p>
- <pre>
-cd $ERL_TOP/erts/emulator
-make lcnt FLAVOR=smp
- </pre>
- <p> and then starting Erlang with,</p>
+./configure --enable-lock-counter</pre>
+ <p>Start the lock-counting VM like this:</p>
<pre>
-$ERL_TOP/bin/cerl -lcnt
- </pre>
- <p>To verify that you lock-counting enabled check that <c>[lock-counting]</c> appears in the status text when the VM is started.</p>
+$ERL_TOP/bin/erl -emu_type lcnt</pre>
+ <p>To verify that lock counting is enabled check that <c>[lock-counting]</c> appears in the status text when the VM is started.</p>
<pre>
-Erlang R13B03 (erts-5.7.4) [source] [64-bit] [smp:8:8] [rq:8] [async-threads:0] [hipe]
- [kernel-poll:false] [lock-counting]
- </pre>
+Erlang/OTP 20 [erts-9.0] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe]
+ [kernel-poll:false] [lock-counting]</pre>
</section>
<section>
<title>Getting started</title>
<p>Once you have a lock counting enabled VM the module <c>lcnt</c> can be used. The module is intended to be used from the current running nodes shell. To access remote nodes use <c>lcnt:clear(Node)</c> and <c>lcnt:collect(Node)</c>. </p>
<p>All locks are continuously monitored and its statistics updated. Use <c>lcnt:clear/0</c> to initially clear all counters before running any specific tests. This command will also reset the duration timer internally.</p>
- <p>To retrieve lock statistics information use, <c>lcnt:collect/0,1</c>. The collect operation will start a <c>lcnt</c> server if it not already started. All collected data will be built into an erlang term and uploaded to the server and a duration time will also be uploaded. This duration is the time between <c>lcnt:clear/0,1</c> and <c>lcnt:collect/0,1</c>.</p>
+ <p>To retrieve lock statistics information, use <c>lcnt:collect/0,1</c>. The collect operation will start a <c>lcnt</c> server if it not already started. All collected data will be built into an Erlang term and uploaded to the server and a duration time will also be uploaded. This duration is the time between <c>lcnt:clear/0,1</c> and <c>lcnt:collect/0,1</c>.</p>
<p>Once the data is collected to the server it can be filtered, sorted and printed in many different ways.</p>
<p>See the <seealso marker="lcnt">reference manual</seealso> for a description of each function.</p>
diff --git a/lib/tools/doc/src/make.xml b/lib/tools/doc/src/make.xml
index fddf5ebd7b..123fcd4afc 100644
--- a/lib/tools/doc/src/make.xml
+++ b/lib/tools/doc/src/make.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2016</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -43,15 +43,15 @@
<fsummary>Compile a set of modules.</fsummary>
<type>
<v>Options = [Option]</v>
- <v>&nbsp;Option = noexec | load | netload | &lt;compiler option&gt;</v>
+ <v>&nbsp;Option = noexec | load | netload | {emake, Emake} | &lt;compiler option&gt;</v>
</type>
<desc>
- <p>This function first looks in the current working directory
- for a file named <c>Emakefile</c> (see below) specifying the
- set of modules to compile and the compile options to use. If
- no such file is found, the set of modules to compile
- defaults to all modules in the current working
- directory.</p>
+ <p>This function determines the set of modules to compile and the
+ compile options to use, by first looking for the <c>emake</c> make
+ option, if not present reads the configuration from a file named
+ <c>Emakefile</c> (see below). If no such file is found, the
+ set of modules to compile defaults to all modules in the
+ current working directory.</p>
<p>Traversing the set of modules, it then recompiles every module for
which at least one of the following conditions apply:</p>
<list type="bulleted">
@@ -77,6 +77,9 @@
<item><c>netload</c> <br></br>
Net load mode. Loads all recompiled modules on all known nodes.</item>
+ <item><c>{emake, Emake}</c> <br></br>
+
+ Rather than reading the <c>Emakefile</c> specify configuration explicitly.</item>
</list>
<p>All items in <c>Options</c> that are not make options are assumed
to be compiler options and are passed as-is to
@@ -108,9 +111,10 @@
<section>
<title>Emakefile</title>
- <p><c>make:all/0,1</c> and <c>make:files/1,2</c> looks in the
- current working directory for a file named <c>Emakefile</c>. If
- it exists, <c>Emakefile</c> should contain elements like this:</p>
+ <p><c>make:all/0,1</c> and <c>make:files/1,2</c> first looks for
+ <c>{emake, Emake}</c> in options, then in the current working directory
+ for a file named <c>Emakefile</c>. If present <c>Emake</c> should
+ contain elements like this:</p>
<code type="none">
Modules.
{Modules,Options}. </code>
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index af20200d49..bdd5455354 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2016</year>
+ <year>2004</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,41 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ In some situations, <c>make:all()</c> and friends did not
+ detect changes in include files located in the current
+ directory. This is now corrected.</p>
+ <p>
+ Own Id: OTP-14339 Aux Id: ERL-395 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>The <c>make</c> module now accepts the
+ <c>{emake,Emake}</c> option.</p>
+ <p>
+ Own Id: OTP-14253</p>
+ </item>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.9.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/tools/emacs/erlang-edoc.el b/lib/tools/emacs/erlang-edoc.el
index 241590b850..d0dcc81028 100644
--- a/lib/tools/emacs/erlang-edoc.el
+++ b/lib/tools/emacs/erlang-edoc.el
@@ -169,4 +169,10 @@
(jit-lock-refontify))
(provide 'erlang-edoc)
+
+;; Local variables:
+;; coding: utf-8
+;; indent-tabs-mode: nil
+;; End:
+
;;; erlang-edoc.el ends here
diff --git a/lib/tools/emacs/erlang-eunit.el b/lib/tools/emacs/erlang-eunit.el
index 3b85e6680a..38c40927f4 100644
--- a/lib/tools/emacs/erlang-eunit.el
+++ b/lib/tools/emacs/erlang-eunit.el
@@ -68,7 +68,7 @@ buffer and vice versa"
;;;
(defun erlang-eunit-open-src-file-other-window (test-file-path)
"Open the src file which corresponds to the an EUnit test file"
- (find-file-other-window (erlang-eunit-src-filename test-file-path)))
+ (find-file-other-window (erlang-eunit-src-filename test-file-path)))
;;; Return the name and path of the EUnit test file
;;, (input may be either the source filename itself or the EUnit test filename)
@@ -154,7 +154,7 @@ buffer and vice versa"
;;; Join filenames
(defun filename-join (dir file)
(if (or (= (elt file 0) ?/)
- (= (car (last (append dir nil))) ?/))
+ (= (car (last (append dir nil))) ?/))
(concat dir file)
(concat dir "/" file)))
@@ -299,7 +299,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
;;; 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.
+ "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,
@@ -311,7 +311,7 @@ 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)))
+ (test-filename (erlang-eunit-test-filename buffer-file-name)))
;; The purpose of out-maneuvering `save-some-buffers', as is done
;; below, is to ask the question about saving buffers only once,
@@ -326,9 +326,9 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
;; 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 under-cover)
- (if (file-readable-p test-filename)
- (erlang-eunit-compile-file test-filename)
- t)
+ (if (file-readable-p test-filename)
+ (erlang-eunit-compile-file test-filename)
+ t)
(apply test-fun test-args)
(if under-cover
(save-excursion
@@ -381,16 +381,16 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
(goto-char compilation-parsing-end)
(erlang-eunit-all-list-elems-fulfill-p
(lambda (re) (let ((continue t)
- (result t))
- (while continue ; ignore warnings, stop at errors
- (if (re-search-forward re (point-max) t)
- (if (erlang-eunit-is-compilation-warning)
- t
- (setq result nil)
- (setq continue nil))
- (setq result t)
- (setq continue nil)))
- result))
+ (result t))
+ (while continue ; ignore warnings, stop at errors
+ (if (re-search-forward re (point-max) t)
+ (if (erlang-eunit-is-compilation-warning)
+ t
+ (setq result nil)
+ (setq continue nil))
+ (setq result t)
+ (setq continue nil)))
+ result))
(mapcar (lambda (e) (car e)) erlang-error-regexp-alist))))
(defun erlang-eunit-is-compilation-warning ()
@@ -402,7 +402,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
(let ((matches-p t))
(while (and list matches-p)
(if (not (funcall pred (car list)))
- (setq matches-p nil))
+ (setq matches-p nil))
(setq list (cdr list)))
matches-p))
@@ -439,15 +439,21 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
(defun erlang-eunit-ensure-keymap-for-key (key-seq)
(let ((prefix-keys (butlast (append key-seq nil)))
- (prefix-seq ""))
+ (prefix-seq ""))
(while prefix-keys
(setq prefix-seq (concat prefix-seq (make-string 1 (car prefix-keys))))
(setq prefix-keys (cdr prefix-keys))
(if (not (keymapp (lookup-key (current-local-map) prefix-seq)))
- (local-set-key prefix-seq (make-sparse-keymap))))))
+ (local-set-key prefix-seq (make-sparse-keymap))))))
(add-hook 'erlang-mode-hook 'erlang-eunit-add-key-bindings)
(provide 'erlang-eunit)
-;; erlang-eunit ends here
+
+;; Local variables:
+;; coding: utf-8
+;; indent-tabs-mode: nil
+;; End:
+
+;; erlang-eunit.el ends here
diff --git a/lib/tools/emacs/erlang-flymake.el b/lib/tools/emacs/erlang-flymake.el
index 2e447b55de..0b7936a81f 100644
--- a/lib/tools/emacs/erlang-flymake.el
+++ b/lib/tools/emacs/erlang-flymake.el
@@ -37,8 +37,7 @@
"Return a list of include directories to add to the compiler options.")
(defvar erlang-flymake-extra-opts
- (list "+warn_obsolete_guard"
- "+warn_unused_import"
+ (list "+warn_unused_import"
"+warn_shadow_vars"
"+warn_export_vars"
"+strong_validation"
diff --git a/lib/tools/emacs/erlang-pkg.el b/lib/tools/emacs/erlang-pkg.el
index 4d0aa6fcd3..02d6bebbf4 100644
--- a/lib/tools/emacs/erlang-pkg.el
+++ b/lib/tools/emacs/erlang-pkg.el
@@ -1,3 +1,3 @@
(define-package "erlang" "2.7.0"
- "Erlang major mode"
- '())
+ "Erlang major mode"
+ '((emacs "24.1")))
diff --git a/lib/tools/emacs/erlang-start.el b/lib/tools/emacs/erlang-start.el
index 160057e179..c35f280bf4 100644
--- a/lib/tools/emacs/erlang-start.el
+++ b/lib/tools/emacs/erlang-start.el
@@ -39,7 +39,7 @@
;;
;; Please state as exactly as possible:
;; - Version number of Erlang Mode (see the menu), Emacs, Erlang,
-;; and of any other relevant software.
+;; 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.
@@ -60,7 +60,7 @@
;;
(autoload 'erlang-mode "erlang" "Major mode for editing Erlang code." t)
-(autoload 'erlang-version "erlang"
+(autoload 'erlang-version "erlang"
"Return the current version of Erlang mode." t)
(autoload 'erlang-shell "erlang" "Start a new Erlang shell." t)
(autoload 'run-erlang "erlang" "Start a new Erlang shell." t)
@@ -68,7 +68,7 @@
(autoload 'erlang-compile "erlang"
"Compile Erlang module in current buffer." t)
-(autoload 'erlang-man-module "erlang"
+(autoload 'erlang-man-module "erlang"
"Find manual page for MODULE." t)
(autoload 'erlang-man-function "erlang"
"Find manual page for NAME, where NAME is module:function." t)
@@ -108,25 +108,22 @@ A function suitable for `eldoc-documentation-function'.\n\n(fn)" nil nil)
;;
;; Associate files using interpreter "escript" with Erlang mode.
-;;
+;;
;;;###autoload
(add-to-list 'interpreter-mode-alist (cons "escript" 'erlang-mode))
;;
;; Ignore files ending in ".jam", ".vee", and ".beam" when performing
-;; file completion.
+;; file completion and in dired omit mode.
;;
;;;###autoload
(let ((erl-ext '(".jam" ".vee" ".beam")))
(while erl-ext
- (let ((cie completion-ignored-extensions))
- (while (and cie (not (string-equal (car cie) (car erl-ext))))
- (setq cie (cdr cie)))
- (if (null cie)
- (setq completion-ignored-extensions
- (cons (car erl-ext) completion-ignored-extensions))))
+ (add-to-list 'completion-ignored-extensions (car erl-ext))
+ (when (boundp 'dired-omit-extensions)
+ (add-to-list 'dired-omit-extensions (car erl-ext)))
(setq erl-ext (cdr erl-ext))))
@@ -136,4 +133,9 @@ A function suitable for `eldoc-documentation-function'.\n\n(fn)" nil nil)
(provide 'erlang-start)
+;; Local variables:
+;; coding: utf-8
+;; indent-tabs-mode: nil
+;; End:
+
;; erlang-start.el ends here.
diff --git a/lib/tools/emacs/erlang-test.el b/lib/tools/emacs/erlang-test.el
index ba6190d194..efe3d515e9 100644
--- a/lib/tools/emacs/erlang-test.el
+++ b/lib/tools/emacs/erlang-test.el
@@ -2,13 +2,13 @@
;;; Unit tests for erlang.el.
-;; Author: Johan Claesson
+;; Author: Johan Claesson
;; Created: 2016-05-07
;; Keywords: erlang, languages
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 2016. All Rights Reserved.
+;; Copyright Ericsson AB 2016-2017. All Rights Reserved.
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
@@ -28,6 +28,27 @@
;;; Commentary:
;; This library require GNU Emacs 25 or later.
+;;
+;; There are two ways to run emacs unit tests.
+;;
+;; 1. Within a running emacs process. Load this file. Then to run
+;; all defined test cases:
+;;
+;; M-x ert RET t RET
+;;
+;; To run only the erlang test cases:
+;;
+;; M-x ert RET "^erlang" RET
+;;
+;;
+;; 2. In a new stand-alone emacs process. This process exits
+;; when it executed the tests. For example:
+;;
+;; emacs -Q -batch -L . -l erlang.el -l erlang-test.el \
+;; -f ert-run-tests-batch-and-exit
+;;
+;; The -L option adds a directory to the load-path. It should be the
+;; directory containing erlang.el and erlang-test.el.
;;; Code:
@@ -59,11 +80,12 @@ concatenated to form an erlang file to test on.")
tags-file-name
tags-table-list
tags-table-set-list
+ tags-add-tables
+ tags-completion-table
erlang-buffer
erlang-mode-hook
prog-mode-hook
- erlang-shell-mode-hook
- tags-add-tables)
+ erlang-shell-mode-hook)
(unwind-protect
(progn
(setq-default tags-file-name nil)
@@ -71,11 +93,14 @@ concatenated to form an erlang file to test on.")
(erlang-test-create-erlang-file erlang-file)
(erlang-test-compile-tags erlang-file tags-file)
(setq erlang-buffer (find-file-noselect erlang-file))
- (with-current-buffer erlang-buffer
- (setq-local tags-file-name tags-file))
- ;; Setting global tags-file-name is a workaround for
- ;; GNU Emacs bug#23164.
- (setq tags-file-name tags-file)
+ (if (< emacs-major-version 26)
+ (progn
+ (with-current-buffer erlang-buffer
+ (setq-local tags-file-name tags-file))
+ ;; Setting global tags-file-name is a workaround for
+ ;; GNU Emacs bug#23164.
+ (setq tags-file-name tags-file))
+ (visit-tags-table tags-file t))
(erlang-test-complete-at-point tags-file)
(erlang-test-completion-table)
(erlang-test-xref-find-definitions erlang-file erlang-buffer))
@@ -117,12 +142,20 @@ concatenated to form an erlang file to test on.")
for line = 1 then (1+ line)
do (when tagname
(switch-to-buffer erlang-buffer)
- (xref-find-definitions tagname)
- (erlang-test-verify-pos erlang-file line)
- (xref-find-definitions (concat "erlang_test:" tagname))
- (erlang-test-verify-pos erlang-file line)))
- (xref-find-definitions "erlang_test:")
- (erlang-test-verify-pos erlang-file 1))
+ (erlang-test-xref-jump tagname erlang-file line)
+ (erlang-test-xref-jump (concat "erlang_test:" tagname)
+ erlang-file line)))
+ (erlang-test-xref-jump "erlang_test:" erlang-file 1))
+
+(defun erlang-test-xref-jump (id expected-file expected-line)
+ (goto-char (point-max))
+ (insert "\n%% " id)
+ (save-buffer)
+ (if (fboundp 'xref-find-definitions)
+ (xref-find-definitions (erlang-id-to-string
+ (erlang-get-identifier-at-point)))
+ (error "xref-find-definitions not defined (too old emacs?)"))
+ (erlang-test-verify-pos expected-file expected-line))
(defun erlang-test-verify-pos (expected-file expected-line)
(should (string-equal (file-truename expected-file)
@@ -136,13 +169,13 @@ concatenated to form an erlang file to test on.")
(setq-local tags-file-name tags-file)
(insert "\nerlang_test:fun")
(erlang-complete-tag)
- (should (looking-back "erlang_test:function"))
+ (should (looking-back "erlang_test:function" (point-at-bol)))
(insert "\nfun")
(erlang-complete-tag)
- (should (looking-back "function"))
+ (should (looking-back "function" (point-at-bol)))
(insert "\nerlang_")
(erlang-complete-tag)
- (should (looking-back "erlang_test:"))))
+ (should (looking-back "erlang_test:" (point-at-bol)))))
(ert-deftest erlang-test-compile-options ()
@@ -179,6 +212,30 @@ concatenated to form an erlang file to test on.")
erlang))
+(ert-deftest erlang-test-parse-id ()
+ (cl-loop for id-string in '("fun/10"
+ "qualified-function module:fun/10"
+ "record reko"
+ "macro _SYMBOL"
+ "macro MACRO/10"
+ "module modula"
+ "macro"
+ nil)
+ for id-list in '((nil nil "fun" 10)
+ (qualified-function "module" "fun" 10)
+ (record nil "reko" nil)
+ (macro nil "_SYMBOL" nil)
+ (macro nil "MACRO" 10)
+ (module nil "modula" nil)
+ (nil nil "macro" nil)
+ nil)
+ for id-list2 = (erlang-id-to-list id-string)
+ do (should (equal id-list id-list2))
+ for id-string2 = (erlang-id-to-string id-list)
+ do (should (equal id-string id-string2))
+ collect id-list2))
+
+
(provide 'erlang-test)
;;; erlang-test.el ends here
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 39c6065ce4..438abc2d29 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -4,10 +4,12 @@
;; Author: Anders Lindgren
;; Keywords: erlang, languages, processes
;; Date: 2011-12-11
+;; Version: 2.7.0
+;; Package-Requires: ((emacs "24.1"))
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 1996-2016. All Rights Reserved.
+;; Copyright Ericsson AB 1996-2017. All Rights Reserved.
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
@@ -24,7 +26,7 @@
;; %CopyrightEnd%
;;
-;; Lars Thors�n's modifications of 2000-06-07 included.
+;; Lars Thorsén's modifications of 2000-06-07 included.
;; The original version of this package was written by Robert Virding.
;;
;;; Commentary:
@@ -85,42 +87,15 @@
(defconst erlang-version "2.7"
"The version number of Erlang mode.")
-(defvar erlang-root-dir nil
+(defcustom erlang-root-dir nil
"The directory where the Erlang system is installed.
The name should not contain the trailing slash.
Should this variable be nil, no manual pages will show up in the
-Erlang mode menu.")
-
-(eval-and-compile
- (defconst erlang-emacs-major-version
- (if (boundp 'emacs-major-version)
- emacs-major-version
- (string-match "\\([0-9]+\\)\\.\\([0-9]+\\)" emacs-version)
- (erlang-string-to-int (substring emacs-version
- (match-beginning 1) (match-end 1))))
- "Major version number of Emacs."))
-
-(eval-and-compile
- (defconst erlang-emacs-minor-version
- (if (boundp 'emacs-minor-version)
- emacs-minor-version
- (string-match "\\([0-9]+\\)\\.\\([0-9]+\\)" emacs-version)
- (erlang-string-to-int (substring emacs-version
- (match-beginning 2) (match-end 2))))
- "Minor version number of Emacs."))
-
-(defconst erlang-xemacs-p (string-match "Lucid\\|XEmacs" emacs-version)
- "Non-nil when running under XEmacs or Lucid Emacs.")
-
-(defvar erlang-xemacs-popup-menu '("Erlang Mode Commands" . nil)
- "Common popup menu for all buffers in Erlang mode.
-
-This variable is destructively modified every time the Erlang menu
-is modified. The effect is that all changes take effect in all
-buffers in Erlang mode, just like under GNU Emacs.
-
-Never EVER set this variable!")
+Erlang mode menu."
+ :group 'erlang
+ :type '(restricted-sexp :match-alternatives (stringp 'nil))
+ :safe (lambda (val) (or (eq nil val) (stringp val))))
(defvar erlang-menu-items '(erlang-menu-base-items
erlang-menu-skel-items
@@ -129,7 +104,7 @@ Never EVER set this variable!")
erlang-menu-man-items
erlang-menu-personal-items
erlang-menu-version-items)
- "*List of menu item list to combine to create Erlang mode menu.
+ "List of menu item list to combine to create Erlang mode menu.
External programs which temporarily add menu items to the Erlang mode
menu may use this variable. Please use the function `add-hook' to add
@@ -141,17 +116,14 @@ 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 Region " erlang-indent-region 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))
+ ("Comment Region" comment-region mark-active)
+ ("Uncomment Region" uncomment-region mark-active)
nil
("Beginning of Function" erlang-beginning-of-function)
("End of Function" erlang-end-of-function)
@@ -238,7 +210,7 @@ This variable is added to the list of Erlang menus stored in
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.
+ "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.
@@ -272,7 +244,7 @@ To use the example, copy the following lines to your `~/.emacs' file:
(imenu-add-to-menubar \"Imenu\")))")
(defvar erlang-load-hook nil
- "*Functions to run when Erlang mode is loaded.
+ "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.
@@ -304,17 +276,20 @@ manual pages can be retrieved (note that you must set the value of
A useful function is `tempo-template-erlang-normal-header'.
\(This function only exists when the `tempo' package is available.)")
-(defvar erlang-check-module-name 'ask
- "*Non-nil means check that module name and file name agrees when saving.
+(defcustom 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.")
+If the value of this variable is the symbol `ask', the user is
+prompted. If the value is t the source is silently changed."
+ :group 'erlang
+ :type '(choice (const :tag "Check on save" 'ask)
+ (const :tag "Don't check on save" t)))
(defvar erlang-electric-commands
'(erlang-electric-comma
erlang-electric-semicolon
erlang-electric-gt)
- "*List of activated electric commands.
+ "List of activated electric commands.
The list should contain the electric commands which should be active.
Currently, the available electric commands are:
@@ -328,8 +303,8 @@ 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.
+(defcustom 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.
@@ -339,28 +314,32 @@ 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.")
+inhibited."
+ :group 'erlang
+ :type 'boolean
+ :safe 'booleanp)
(defvar erlang-electric-newline-inhibit-list
'(erlang-electric-semicolon
erlang-electric-comma
erlang-electric-gt)
- "*Commands which can inhibit the next newline.")
+ "Commands which can inhibit the next newline.")
-(defvar erlang-electric-semicolon-insert-blank-lines nil
- "*Number of blank lines inserted before header, or nil.
+(defcustom 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.")
+inserted."
+ :group 'erlang)
(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'.
+ "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:
@@ -381,7 +360,7 @@ The test is performed by the function `erlang-test-criteria-list'.")
erlang-at-keyword-end-p
erlang-at-end-of-clause-p
erlang-at-end-of-function-p)
- "*List of functions controlling `erlang-electric-comma'.
+ "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:
@@ -399,7 +378,7 @@ The test is performed by the function `erlang-test-criteria-list'.")
'(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'.
+ "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:
@@ -415,7 +394,7 @@ The test is performed by the function `erlang-test-criteria-list'.")
(defvar erlang-electric-newline-criteria
'(t)
- "*List of functions controlling `erlang-electric-newline'.
+ "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
@@ -435,8 +414,8 @@ 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.
+(defcustom 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
@@ -457,46 +436,67 @@ 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.")
+electric command, other functions in the criteria list could."
+ :group 'erlang
+ :type '(restricted-sexp :match-alternatives (integerp 'nil))
+ :safe (lambda (val) (or (eq val nil) (integerp val))))
-(defvar erlang-new-clause-with-arguments nil
- "*Non-nil means that the arguments are cloned when a clause is generated.
+(defcustom 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.")
+`erlang-generate-new-clause' and by use of the electric semicolon."
+ :group 'erlang
+ :type 'boolean
+ :safe 'booleanp)
-(defvar erlang-compile-use-outdir t
- "*When nil, go to the directory containing source file when compiling.
+(defcustom 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.")
-(put 'erlang-indent-level 'safe-local-variable 'integerp)
-
-(defvar erlang-icr-indent nil
- "*Indentation of Erlang if/case/receive/ patterns. `nil' means
- keeping default behavior. When non-nil, indent to th column of
- if/case/receive.")
-
-(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.
+ (setq erlang-compile-use-outdir nil)"
+ :group 'erlang
+ :type 'boolean
+ :safe 'booleanp)
+
+(defcustom erlang-indent-level 4
+ "Indentation of Erlang calls/clauses within blocks."
+ :group 'erlang
+ :type 'integer
+ :safe 'integerp)
+
+(defcustom erlang-icr-indent nil
+ "Indentation of Erlang if/case/receive patterns.
+nil means keeping default behavior. When non-nil, indent to the column of
+if/case/receive."
+ :group 'erlang
+ :type 'boolean
+ :safe 'booleanp)
+
+(defcustom erlang-indent-guard 2
+ "Indentation of Erlang guards."
+ :group 'erlang
+ :type 'integer
+ :safe 'integerp)
+
+(defcustom 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,
-regardless of where in the line point is when the TAB command is used.")
+function."
+ :group 'erlang
+ :type '(restricted-sexp :match-alternatives (integerp 'nil))
+ :safe (lambda (val) (or (eq val nil) (integerp val))))
+
+(defcustom erlang-tab-always-indent t
+ "Non-nil means TAB in Erlang mode should always re-indent the current line,
+regardless of where in the line point is when the TAB command is used."
+ :group 'erlang
+ :type 'boolean
+ :safe 'booleanp)
(defvar erlang-man-inhibit (eq system-type 'windows-nt)
"Inhibit the creation of the Erlang Manual Pages menu.
@@ -509,7 +509,7 @@ there is no attempt to create the menu.")
("Man - Modules" "/man/man3" t)
("Man - Files" "/man/man4" t)
("Man - Applications" "/man/man6" t))
- "*The man directories displayed in the Erlang menu.
+ "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.
@@ -517,17 +517,17 @@ Should the flag the nil, the directory is absolute, should it be non-nil
the directory is relative to the variable `erlang-root-dir'.")
(defvar erlang-man-max-menu-size 35
- "*The maximum number of menu items in one menu allowed.")
+ "The maximum number of menu items in one menu allowed.")
(defvar erlang-man-display-function 'erlang-man-display
- "*Function used to display man page.
+ "Function used to display man page.
The function is called with one argument, the name of the file
containing the man page. Use this variable when the default
function, `erlang-man-display', does not work on your system.")
(defvar erlang-compile-extra-opts '()
- "*Additional options to the compilation command.
+ "Additional options to the compilation command.
This is an elisp list of options. Each option can be either:
- an atom
- a dotted pair
@@ -539,7 +539,7 @@ Example: '(bin_opt_info (i . \"/path1/include\") (i . \"/path2/include\"))")
(".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.
+ "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
@@ -547,22 +547,21 @@ 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.
+ "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.
+ "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)
+ (defvar erlang-regexp-modern-p t
"Non-nil when this version of Emacs uses a modern version of regexp.
Supporting \_< and \_> This is determined by checking the version of Emacs used."))
@@ -608,6 +607,24 @@ The regexp must be surrounded with a pair of regexp parentheses."))
This is used to determine matches in complex regexps which contains
`erlang-variable-regexp'."))
+(defconst erlang-module-function-regexp
+ (eval-when-compile
+ (concat erlang-atom-regexp ":" erlang-atom-regexp))
+ "Regexp matching an erlang module:function.")
+
+(defconst erlang-name-regexp
+ (concat "\\("
+ "\\(?:\\sw\\|\\s_\\)+"
+ "\\|"
+ erlang-atom-quoted-regexp
+ "\\)")
+ "Matches a name of a function, macro or record")
+
+(defconst erlang-id-regexp
+ (concat "\\(?:\\(qualified-function\\|record\\|macro\\|module\\) \\)?"
+ "\\(?:" erlang-atom-regexp ":\\)?"
+ erlang-name-regexp "?"
+ "\\(?:/\\([0-9]+\\)\\)?"))
(eval-and-compile
(defun erlang-regexp-opt (strings &optional paren)
@@ -752,6 +769,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"bitsize"
"bitstring_to_list"
"byte_size"
+ "ceil"
"check_old_code"
"check_process_code"
"date"
@@ -762,6 +780,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"erase"
"error"
"exit"
+ "floor"
"float"
"float_to_binary"
"float_to_list"
@@ -801,6 +820,8 @@ resulting regexp is surrounded by \\_< and \\_>."
"list_to_float"
"list_to_integer"
"list_to_pid"
+ "list_to_port"
+ "list_to_ref"
"list_to_tuple"
"load_module"
"make_ref"
@@ -819,12 +840,14 @@ resulting regexp is surrounded by \\_< and \\_>."
"port_command"
"port_connect"
"port_control"
+ "port_to_list"
"pre_loaded"
"process_flag"
"process_info"
"processes"
"purge_module"
"put"
+ "ref_to_list"
"register"
"registered"
"round"
@@ -905,7 +928,6 @@ resulting regexp is surrounded by \\_< and \\_>."
"get_cookie"
"get_module_info"
"get_stacktrace"
- "hash"
"has_prepared_code_on_load"
"hibernate"
"insert_element"
@@ -932,14 +954,12 @@ resulting regexp is surrounded by \\_< and \\_>."
"port_get_data"
"port_info"
"port_set_data"
- "port_to_list"
"ports"
"posixtime_to_universaltime"
"prepare_loading"
"process_display"
"raise"
"read_timer"
- "ref_to_list"
"resume_process"
"send"
"send_after"
@@ -981,7 +1001,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"Regexp which should match beginning of a clause.")
(defvar erlang-file-name-extension-regexp "\\.erl$"
- "*Regexp which should match an Erlang file name.
+ "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.
@@ -995,7 +1015,7 @@ tags system should interpret tags on the form `module:tag' for
files written in other languages than Erlang.")
(defvar erlang-inferior-shell-split-window t
- "*If non-nil, when starting an inferior shell, split windows.
+ "If non-nil, when starting an inferior shell, split windows.
If nil, the inferior shell replaces the window. This is the traditional
behaviour.")
@@ -1018,8 +1038,7 @@ behaviour.")
(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-\C-e" 'erlang-end-of-function))
(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)
@@ -1034,14 +1053,14 @@ behaviour.")
(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-u" '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.")
+ "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
@@ -1077,12 +1096,7 @@ behaviour.")
"Font lock keyword highlighting a function header.")
(defface erlang-font-lock-exported-function-name-face
- (if (featurep 'xemacs)
- (progn
- (require 'font-lock)
- `((t (:foreground ,(face-foreground 'font-lock-function-name-face))
- (:background ,(face-background 'font-lock-function-name-face)))))
- '((default (:inherit font-lock-function-name-face))))
+ '((default (:inherit font-lock-function-name-face)))
"Face used for highlighting exported functions."
:group 'erlang)
@@ -1294,11 +1308,7 @@ Example:
The difference between this and the standard Erlang Mode
syntax table is that `_' is treated as part of words by
-this syntax table.
-
-Unfortunately, XEmacs hasn't got support for a special Font
-Lock syntax table. The effect is that `apply' in the atom
-`foo_apply' will be highlighted as a bif.")
+this syntax table.")
(defvar erlang-replace-etags-tags-completion-table nil
"Internal flag used by advice `erlang-replace-tags-table'.
@@ -1308,29 +1318,6 @@ replaced by `erlang-etags-tags-completion-table'.")
;;; Avoid errors while compiling this file.
-;; `eval-when-compile' is not defined in Emacs 18. We define it as a
-;; no-op.
-(or (fboundp 'eval-when-compile)
- (defmacro eval-when-compile (&rest rest) nil))
-
-;; These umm...functions are new in Emacs 20. And, yes, until version
-;; 19.27 Emacs backquotes were this ugly.
-
-(or (fboundp 'unless)
- (defmacro unless (condition &rest body)
- "(unless CONDITION BODY...): If CONDITION is false, do BODY, else return nil."
- `((if (, condition) nil ,@body))))
-
-(or (fboundp 'when)
- (defmacro when (condition &rest body)
- "(when CONDITION BODY...): If CONDITION is true, do BODY, else return nil."
- `((if (, condition) (progn ,@body) nil))))
-
-(or (fboundp 'char-before)
- (defmacro char-before (&optional pos)
- "Return the character in the current buffer just before POS."
- `( (char-after (1- (or ,pos (point)))))))
-
;; defvar some obsolete variables, which we still support for
;; backwards compatibility reasons.
(eval-when-compile
@@ -1341,37 +1328,19 @@ replaced by `erlang-etags-tags-completion-table'.")
(defvar next-error-last-buffer))
(eval-when-compile
- (if (or (featurep 'bytecomp)
- (featurep 'byte-compile))
- (progn
- (cond ((string-match "Lucid\\|XEmacs" emacs-version)
- (put 'comment-indent-hook 'byte-obsolete-variable nil)
- ;; Do not warn for unused variables
- ;; when compiling under XEmacs.
- (setq byte-compile-warnings
- '(free-vars unresolved callargs redefine))))
- (require 'comint)
- (require 'tempo)
- (require 'compile))))
+ (require 'comint)
+ (require 'tempo)
+ (require 'compile))
(defun erlang-version ()
"Return the current version of Erlang mode."
(interactive)
- (if (erlang-interactive-p)
+ (if (called-interactively-p 'interactive)
(message "Erlang mode version %s, written by Anders Lindgren"
erlang-version))
erlang-version)
-(defun erlang-interactive-p ()
- (if (fboundp 'called-interactively-p)
- (called-interactively-p 'interactive)
- (funcall (symbol-function 'interactive-p))))
-
-(unless (fboundp 'prog-mode)
- (defun prog-mode ()
- (use-local-map (make-keymap))))
-
;;;###autoload
(define-derived-mode erlang-mode prog-mode "Erlang"
"Major mode for editing Erlang source files in Emacs.
@@ -1443,8 +1412,7 @@ Other commands:
(erlang-tags-init)
(erlang-font-lock-init)
(erlang-skel-init)
- (when (fboundp 'tempo-use-tag-list)
- (tempo-use-tag-list 'erlang-tempo-tags))
+ (tempo-use-tag-list 'erlang-tempo-tags)
(when (and (fboundp 'add-function) (fboundp 'erldoc-eldoc-function))
(or eldoc-documentation-function
(setq-local eldoc-documentation-function #'ignore))
@@ -1460,48 +1428,50 @@ Other commands:
(add-to-list 'auto-mode-alist (cons r 'erlang-mode)))
(defun erlang-syntax-table-init ()
- (if (null erlang-mode-syntax-table)
- (let ((table (make-syntax-table)))
- (modify-syntax-entry ?\n ">" table)
- (modify-syntax-entry ?\" "\"" table)
- (modify-syntax-entry ?# "." table)
- ;; (modify-syntax-entry ?$ "\\" table) ;; Creates problems with indention afterwards
- ;; (modify-syntax-entry ?$ "'" table) ;; Creates syntax highlighting and indention problems
- (modify-syntax-entry ?$ "/" table) ;; Misses the corner case "string that ends with $"
- ;; we have to live with that for now..it is the best alternative
- ;; that can be worked around with "string hat ends with \$"
- (modify-syntax-entry ?% "<" table)
- (modify-syntax-entry ?& "." table)
- (modify-syntax-entry ?\' "\"" table)
- (modify-syntax-entry ?* "." table)
- (modify-syntax-entry ?+ "." table)
- (modify-syntax-entry ?- "." table)
- (modify-syntax-entry ?/ "." table)
- (modify-syntax-entry ?: "." table)
- (modify-syntax-entry ?< "." table)
- (modify-syntax-entry ?= "." table)
- (modify-syntax-entry ?> "." table)
- (modify-syntax-entry ?\\ "\\" table)
- (modify-syntax-entry ?_ "_" table)
- (modify-syntax-entry ?| "." table)
- (modify-syntax-entry ?^ "'" table)
-
- ;; Pseudo bit-syntax: Latin1 double angle quotes as parens.
- ;;(modify-syntax-entry ?\253 "(?\273" table)
- ;;(modify-syntax-entry ?\273 ")?\253" table)
-
- (setq erlang-mode-syntax-table table)))
-
+ (erlang-ensure-syntax-table-is-initialized)
(set-syntax-table erlang-mode-syntax-table))
+(defun erlang-ensure-syntax-table-is-initialized ()
+ (unless erlang-mode-syntax-table
+ (let ((table (make-syntax-table)))
+ (modify-syntax-entry ?\n ">" table)
+ (modify-syntax-entry ?\" "\"" table)
+ (modify-syntax-entry ?# "." table)
+ ;; (modify-syntax-entry ?$ "\\" table) ;; Creates problems with indention afterwards
+ ;; (modify-syntax-entry ?$ "'" table) ;; Creates syntax highlighting and indention problems
+ (modify-syntax-entry ?$ "/" table) ;; Misses the corner case "string that ends with $"
+ ;; we have to live with that for now..it is the best alternative
+ ;; that can be worked around with "string that ends with \$"
+ (modify-syntax-entry ?% "<" table)
+ (modify-syntax-entry ?& "." table)
+ (modify-syntax-entry ?\' "\"" table)
+ (modify-syntax-entry ?* "." table)
+ (modify-syntax-entry ?+ "." table)
+ (modify-syntax-entry ?- "." table)
+ (modify-syntax-entry ?/ "." table)
+ (modify-syntax-entry ?: "." table)
+ (modify-syntax-entry ?< "." table)
+ (modify-syntax-entry ?= "." table)
+ (modify-syntax-entry ?> "." table)
+ (modify-syntax-entry ?\\ "\\" table)
+ (modify-syntax-entry ?_ "_" table)
+ (modify-syntax-entry ?| "." table)
+ (modify-syntax-entry ?^ "'" table)
+
+ ;; Pseudo bit-syntax: Latin1 double angle quotes as parens.
+ ;;(modify-syntax-entry ?\253 "(?\273" table)
+ ;;(modify-syntax-entry ?\273 ")?\253" table)
+
+ (setq erlang-mode-syntax-table table))))
+
+
(defun erlang-electric-init ()
;; Set up electric character functions to work with
;; delsel/pending-del mode. Also, set up text properties for bit
;; syntax handling.
(mapc #'(lambda (cmd)
- (put cmd 'delete-selection t) ;for delsel (Emacs)
- (put cmd 'pending-delete t)) ;for pending-del (XEmacs)
+ (put cmd 'delete-selection t)) ;for delsel (Emacs)
'(erlang-electric-semicolon
erlang-electric-comma
erlang-electric-gt))
@@ -1539,8 +1509,6 @@ Other commands:
(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)
@@ -1747,7 +1715,7 @@ menu configuration is changed."
(defun erlang-menu-install (name items keymap &optional popup)
- "Install a menu in Emacs or XEmacs based on an abstract description.
+ "Install a menu in Emacs based on an abstract description.
NAME is the name of the menu.
@@ -1758,32 +1726,15 @@ same same form as ITEMS. The third optional element is an expression
which is evaluated every time the menu is displayed. Should the
expression evaluate to nil the menu item is ghosted.
-KEYMAP is the keymap to add to menu to. (When using XEmacs, the menu
-will only be visible when this menu is the global, the local, or an
-activate minor mode keymap.)
-
-If POPUP is non-nil, the menu is bound to the XEmacs `mode-popup-menu'
-variable, i.e. it will popup when pressing the right mouse button.
+KEYMAP is the keymap to add to menu to.
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)))
+ (define-key keymap (vector 'menu-bar (intern name))
+ (erlang-menu-make-keymap name items)))
(defun erlang-menu-make-keymap (name items)
- "Build a menu for Emacs 19."
+ "Build a menu."
(let ((menumap (funcall (symbol-function 'make-sparse-keymap)
name))
(count 0)
@@ -1821,54 +1772,6 @@ Please see the variable `erlang-menu-base-items'."
(setq items (cdr items)))
(cons name menumap)))
-
-(defun erlang-menu-xemacs (name items &optional keymap)
- "Build a menu for XEmacs."
- (let ((res '())
- first second third entry)
- (while items
- ;; Replace any occurrence of atoms by their value.
- (while (and items (atom (car items)) (not (null (car items))))
- (if (and (boundp (car items))
- (listp (symbol-value (car items))))
- (setq items (append (reverse (symbol-value (car items)))
- (cdr items)))
- (setq items (cdr items))))
- (setq first (car-safe (car items)))
- (setq second (car-safe (cdr-safe (car items))))
- (setq third (car-safe (cdr-safe (cdr-safe (car items)))))
- (cond ((null first)
- (setq res (cons "------" res)))
- ((symbolp second)
- (setq res (cons (vector first second (or third t)) res)))
- ((and (consp second) (eq (car second) 'lambda))
- (setq res (cons (vector first (list 'call-interactively second)
- (or third t)) res)))
- (t
- (setq res (cons (cons first
- (cdr (erlang-menu-xemacs
- first second)))
- res))))
- (setq items (cdr items)))
- (setq res (reverse res))
- ;; When adding a menu to a minor-mode keymap under Emacs,
- ;; it disappears when the mode is disabled. The expression
- ;; generated below imitates this behaviour.
- ;; (This could be expressed much clearer using backquotes,
- ;; but I don't want to pull in every package.)
- (if keymap
- (let ((expr (list 'or
- (list 'eq keymap 'global-map)
- (list 'eq keymap (list 'current-local-map))
- (list 'symbol-value
- (list 'car-safe
- (list 'rassq
- keymap
- 'minor-mode-map-alist))))))
- (setq res (cons ':included (cons expr res)))))
- (cons name res)))
-
-
(defun erlang-menu-substitute (items alist)
"Substitute functions in menu described by ITEMS.
@@ -1959,7 +1862,9 @@ menu is left unchanged."
The variable `erlang-man-dirs' contains entries describing
the location of the manual pages."
(interactive)
- (if erlang-man-inhibit
+ (if (or erlang-man-inhibit
+ (and (boundp 'menu-bar-mode)
+ (not menu-bar-mode)))
()
(setq erlang-menu-man-items
'(nil
@@ -1998,7 +1903,7 @@ The format is described in the documentation of `erlang-man-dirs'."
(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)))))
+ (erlang-man-dir (nth 1 (car dir-list)))))
(t
;; Absolute
(nth 1 (car dir-list)))))
@@ -2016,6 +1921,8 @@ The format is described in the documentation of `erlang-man-dirs'."
'(("Man Pages"
(("Error! Why?" erlang-man-describe-error)))))))
+(defun erlang-man-dir (subdir)
+ (concat erlang-root-dir "/lib/erlang/" subdir))
;; Should the menu be to long, let's split it into a number of
;; smaller menus. Warning, this code contains beautiful
@@ -2078,7 +1985,7 @@ menus is created."
"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)))
+ (list (let* ((mod (erlang-default-module))
(input (read-string
(format "Manual entry for module%s: "
(if (or (null mod) (string= mod ""))
@@ -2087,26 +1994,36 @@ This function is aware of imported functions."
(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"))
+ (setq module (or module
+ (erlang-default-module)))
+ (when (or (null module) (string= module ""))
+ (error "No Erlang module name given"))
(let ((dir-list erlang-man-dirs)
- (pat (concat "/" (regexp-quote module) "\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$"))
+ (pat (concat "/" (regexp-quote module)
+ "\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$"))
(file nil)
file-list)
(while (and dir-list (null file))
- (setq file-list (erlang-man-get-files
- (if (nth 2 (car dir-list))
- (concat erlang-root-dir (nth 1 (car dir-list)))
- (nth 1 (car dir-list)))))
- (while (and file-list (null file))
- (if (string-match pat (car file-list))
- (setq file (car file-list)))
- (setq file-list (cdr file-list)))
- (setq dir-list (cdr dir-list)))
+ (let ((dir (if (nth 2 (car dir-list))
+ (erlang-man-dir (nth 1 (car dir-list)))
+ (nth 1 (car dir-list)))))
+ (when (file-directory-p dir)
+ (setq file-list (erlang-man-get-files dir))
+ (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))))
+ ;; Did not found the manual file. Fallback to manual-entry.
+ (manual-entry module))))
+
+(defun erlang-default-module ()
+ (let ((id (erlang-get-identifier-at-point)))
+ (if (eq (erlang-id-kind id) 'qualified-function)
+ (erlang-id-module id)
+ (erlang-id-name id))))
;; Warning, the function `erlang-man-function' is a hack!
@@ -2126,37 +2043,28 @@ 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))
+ (list (let* ((default (erlang-default-function-or-module))
(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 default
+ (format " (default %s)" default)
+ "")))))
(if (string= input "")
- (if (and mod func)
- (concat mod ":" func)
- mod)
+ default
input))))
- ;; Emacs 18 doesn't provide `man'...
- (condition-case nil
- (require 'man)
- (error nil))
+ (require 'man)
+ (setq name (or name
+ (erlang-default-function-or-module)))
(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)
+ (cond ((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"))
+ (when (or (null modname) (string= modname ""))
+ (error "No Erlang module name given"))
(cond ((fboundp 'Man-notify-when-ready)
;; Emacs 19: The man command could possibly start an
;; asynchronous process, i.e. we must hook ourselves into
@@ -2166,16 +2074,20 @@ This function is aware of imported functions."
()
(erlang-man-patch-notify)
(setq erlang-man-function-name funcname))
- (condition-case nil
+ (condition-case err
(erlang-man-module modname)
- (error (setq erlang-man-function-name nil))))
+ (error (setq erlang-man-function-name nil)
+ (signal (car err) (cdr err)))))
(t
(erlang-man-module modname)
- (if funcname
- (erlang-man-find-function
- (or (get-buffer "*Manual Entry*") ; Emacs 18
- (current-buffer)) ; XEmacs
- funcname))))))
+ (when funcname
+ (erlang-man-find-function (current-buffer) funcname))))))
+
+(defun erlang-default-function-or-module ()
+ (let ((id (erlang-get-identifier-at-point)))
+ (if (eq (erlang-id-kind id) 'qualified-function)
+ (format "%s:%s" (erlang-id-module id) (erlang-id-name id))
+ (erlang-id-name id))))
;; Should the defadvice be at the top level, the package `advice' would
@@ -2220,36 +2132,22 @@ command is executed asynchronously."
(set-window-point win (point)))
(message "Could not find function `%s'" func)))))))
+(defvar erlang-man-file-regexp
+ "\\(.*\\)/man[^/]*/\\([^.]+\\)\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$")
(defun erlang-man-display (file)
"Display FILE as a `man' file.
This is the default manual page display function.
The variables `erlang-man-display-function' contains the function
to be used."
- ;; Emacs 18 doesn't `provide' man.
- (condition-case nil
- (require 'man)
- (error nil))
+ (require 'man)
(if file
(let ((process-environment (copy-sequence process-environment)))
- (if (string-match "\\(.*\\)/man[^/]*/\\([^.]+\\)\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$" file)
+ (if (string-match erlang-man-file-regexp 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))))
+ (setenv "MANPATH" dir)
+ (manual-entry page))
(error "Can't find man page for %s\n" file)))))
@@ -2277,7 +2175,7 @@ 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.)
+;; Emacsen.
(defvar erlang-skel)
(defun erlang-skel-init ()
@@ -2288,36 +2186,33 @@ all skeletons.
The skeleton routines are based on the `tempo' package. Should this
package not be present, this function does nothing."
(interactive)
- (condition-case nil
- (require 'tempo)
- (error t))
- (if (featurep 'tempo)
- (let ((skel erlang-skel)
- (menu '()))
- (while skel
- (cond ((null (car skel))
- (setq menu (cons nil menu)))
- (t
- (funcall (symbol-function 'tempo-define-template)
- (concat "erlang-" (nth 1 (car skel)))
- ;; The tempo template used contains an `include'
- ;; function call only, hence changes to the
- ;; variables describing the templates take effect
- ;; immdiately.
- (list (list 'erlang-skel-include (nth 2 (car skel))))
- (nth 1 (car skel))
- (car (car skel))
- 'erlang-tempo-tags)
- (setq menu (cons (erlang-skel-make-menu-item
- (car skel)) menu))))
- (setq skel (cdr skel)))
- (setq erlang-menu-skel-items
- (list nil (list "Skeletons" (nreverse menu))))
- (setq erlang-menu-items
- (erlang-menu-add-above 'erlang-menu-skel-items
- 'erlang-menu-version-items
- erlang-menu-items))
- (erlang-menu-init))))
+ (require '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
+ ;; immediately.
+ (list (list 'erlang-skel-include (nth 2 (car skel))))
+ (nth 1 (car skel))
+ (car (car skel))
+ 'erlang-tempo-tags)
+ (setq menu (cons (erlang-skel-make-menu-item
+ (car skel)) menu))))
+ (setq skel (cdr skel)))
+ (setq erlang-menu-skel-items
+ (list nil (list "Skeletons" (nreverse menu))))
+ (setq erlang-menu-items
+ (erlang-menu-add-above 'erlang-menu-skel-items
+ 'erlang-menu-version-items
+ erlang-menu-items))
+ (erlang-menu-init)))
(defun erlang-skel-make-menu-item (skel)
(let ((func (intern (concat "tempo-template-erlang-" (nth 1 skel)))))
@@ -2392,7 +2287,7 @@ can contain other `tempo' attributes. Please see the function
The first character of DD is space if the value is less than 10."
(let ((date (current-time-string)))
(format "%2d %s %s"
- (erlang-string-to-int (substring date 8 10))
+ (string-to-number (substring date 8 10))
(substring date 4 7)
(substring date -4))))
@@ -2954,10 +2849,10 @@ Return nil if inside string, t if in a comment."
((eq (car stack-top) '->)
;; If in fun definition use standard indent level not double
;;(if (not (eq (car (car (cdr stack))) 'fun))
- ;; Removed it made multi clause fun's look to bad
+ ;; Removed it made multi clause fun's look too bad
(setq off (+ erlang-indent-level (if (not erlang-icr-indent)
erlang-indent-level
- erlang-icr-indent)))))
+ erlang-icr-indent)))))
(let ((base (erlang-indent-find-base stack indent-point off skip)))
;; Special cases
(goto-char indent-point)
@@ -3262,11 +3157,8 @@ With argument, do this that many times."
(interactive)
(push-mark (point))
(erlang-end-of-clause 1)
- ;; Sets the region. In Emacs 19 and XEmacs, we want to activate
- ;; the region.
- (condition-case nil
- (push-mark (point) nil t)
- (error (push-mark (point))))
+ ;; Sets the region.
+ (push-mark (point) nil t)
(erlang-beginning-of-clause 1)
;; The above function deactivates the mark.
(if (boundp 'deactivate-mark)
@@ -3374,11 +3266,8 @@ With negative argument go towards the beginning of the buffer."
(interactive)
(push-mark (point))
(erlang-end-of-function 1)
- ;; Sets the region. In Emacs 19 and XEmacs, we want to activate
- ;; the region.
- (condition-case nil
- (push-mark (point) nil t)
- (error (push-mark (point))))
+ ;; Sets the region.
+ (push-mark (point) nil t)
(erlang-beginning-of-function 1)
;; The above function deactivates the mark.
(if (boundp 'deactivate-mark)
@@ -3461,13 +3350,6 @@ and initial `%':s."
(fill-prefix comment-fill-prefix))
(fill-paragraph justify))))))
-
-(defun erlang-uncomment-region (beg end)
- "Uncomment all commented lines in the region."
- (interactive "r")
- (uncomment-region beg end))
-
-
(defun erlang-generate-new-clause ()
"Create additional Erlang clause header.
@@ -3595,7 +3477,7 @@ corresponds to the order of the parsed Erlang list."
(erlang-remove-quotes
(erlang-buffer-substring
(match-beginning 1) (match-end 1)))
- (erlang-string-to-int
+ (string-to-number
(erlang-buffer-substring
(match-beginning
(+ 1 erlang-atom-regexp-matches))
@@ -3694,34 +3576,50 @@ Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
(defun erlang-get-function-arity ()
"Return the number of arguments of function at point, or nil."
- (and (looking-at (eval-when-compile
- (concat "^" erlang-atom-regexp "\\s *(")))
- (save-excursion
- (goto-char (match-end 0))
- (condition-case nil
- (let ((res 0)
- (cont t))
- (while cont
- (cond ((eobp)
- (setq res nil)
- (setq cont nil))
- ((looking-at "\\s *)")
- (setq cont nil))
- ((looking-at "\\s *\\($\\|%\\)")
- (forward-line 1))
- ((looking-at "\\s *<<[^>]*?>>")
- (when (zerop res)
- (setq res (+ 1 res)))
- (goto-char (match-end 0)))
- ((looking-at "\\s *,")
- (setq res (+ 1 res))
- (goto-char (match-end 0)))
- (t
- (when (zerop res)
- (setq res (+ 1 res)))
- (forward-sexp 1))))
- res)
- (error nil)))))
+ (erlang-get-arity-after-regexp (concat "^" erlang-atom-regexp "\\s *(")))
+
+(defun erlang-get-argument-list-arity ()
+ "Return the number of arguments in argument list at point, or nil.
+The point should be before the opening parenthesis of the
+argument list before calling this function."
+ (erlang-get-arity-after-regexp "\\s *("))
+
+(defun erlang-get-arity-after-regexp (regexp)
+ "Return the number of arguments in argument list after REGEXP, or nil."
+ (when (looking-at regexp)
+ (save-excursion
+ (goto-char (match-end 0))
+ (erlang-get-arity))))
+
+(defun erlang-get-arity ()
+ "Return the number of arguments in argument list at point, or nil.
+The point should be after the opening parenthesis of the argument
+list before calling this function."
+ (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 *<<[^>]*?>>")
+ (when (zerop res)
+ (setq res (+ 1 res)))
+ (goto-char (match-end 0)))
+ ((looking-at "\\s *,")
+ (setq res (+ 1 res))
+ (goto-char (match-end 0)))
+ (t
+ (when (zerop res)
+ (setq res (+ 1 res)))
+ (forward-sexp 1))))
+ res)
+ (error nil)))
+
(defun erlang-get-function-name-and-arity ()
"Return the name and arity of the function at point, or nil.
@@ -3744,6 +3642,8 @@ The return value is a string of the form \"foo/1\"."
(error nil)))))
+;; Keeping erlang-get-function-under-point for backward compatibility.
+;; It is used by erldoc.el and maybe other code out there.
(defun erlang-get-function-under-point ()
"Return the module and function under the point, or nil.
@@ -3753,44 +3653,141 @@ list of imported functions is searched.
The following could be returned:
(\"module\" \"function\") -- Both module and function name found.
(nil \"function\") -- No module name was found.
- nil -- No function name found
+ nil -- No function name found.
+
+See also `erlang-get-identifier-at-point'."
+ (let* ((id (erlang-get-identifier-at-point))
+ (kind (erlang-id-kind id))
+ (module (erlang-id-module id))
+ (name (erlang-id-name id)))
+ (cond ((eq kind 'qualified-function)
+ (list module name))
+ (name
+ (list nil name)))))
+
+(defun erlang-get-identifier-at-point ()
+ "Return the erlang identifier at point, or nil.
+
+Should no explicit module name be present at the point, the
+list of imported functions is searched.
+
+When an identifier is found return a list with 4 elements:
+
+1. Kind - One of the symbols qualified-function, record, macro,
+module or nil.
+
+2. Module - Module name string or nil. In case of a
+qualified-function a search fails if no entries with correct
+module are found. For other kinds the module is just a
+preference. If no matching entries are found the search will be
+retried without regard to module.
+
+3. Name - String name of function, module, record or macro.
-In the future the list may contain more elements."
+4. Arity - Integer in case of functions and macros if the number
+of arguments could be found, otherwise nil."
(save-excursion
- (let ((md (match-data))
- (res nil))
+ (save-match-data
(if (eq (char-syntax (following-char)) ? )
(skip-chars-backward " \t"))
- (skip-chars-backward "a-zA-Z0-9_:'")
- (cond ((looking-at (eval-when-compile
- (concat erlang-atom-regexp ":" erlang-atom-regexp)))
- (setq res (list
- (erlang-remove-quotes
- (erlang-buffer-substring
- (match-beginning 1) (match-end 1)))
- (erlang-remove-quotes
- (erlang-buffer-substring
- (match-beginning (1+ erlang-atom-regexp-matches))
- (match-end (1+ erlang-atom-regexp-matches)))))))
- ((looking-at erlang-atom-regexp)
- (let ((fk (erlang-remove-quotes
- (erlang-buffer-substring
- (match-beginning 0) (match-end 0))))
- (mod nil)
- (imports (erlang-get-import)))
- (while (and imports (null mod))
- (if (assoc fk (cdr (car imports)))
- (setq mod (car (car imports)))
- (setq imports (cdr imports))))
- (cond ((eq (preceding-char) ?#)
- (setq fk (concat "-record(" fk)))
- ((eq (preceding-char) ??)
- (setq fk (concat "-define(" fk)))
- ((and (null mod) (not (member fk erlang-int-bifs)))
- (setq mod (erlang-get-module))))
- (setq res (list mod fk)))))
- (store-match-data md)
- res)))
+ (skip-chars-backward "[:word:]_:'")
+ (cond ((looking-at erlang-module-function-regexp)
+ (erlang-get-qualified-function-id-at-point))
+ ((looking-at (concat erlang-atom-regexp ":"))
+ (erlang-get-module-id-at-point))
+ ((looking-at erlang-name-regexp)
+ (erlang-get-some-other-id-at-point))))))
+
+(defun erlang-get-qualified-function-id-at-point ()
+ (let ((kind 'qualified-function)
+ (module (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning 1) (match-end 1))))
+ (name (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning (1+ erlang-atom-regexp-matches))
+ (match-end (1+ erlang-atom-regexp-matches)))))
+ (arity (progn
+ (goto-char (match-end 0))
+ (erlang-get-argument-list-arity))))
+ (list kind module name arity)))
+
+(defun erlang-get-module-id-at-point ()
+ (let ((kind 'module)
+ (module nil)
+ (name (erlang-remove-quotes
+ (erlang-buffer-substring (match-beginning 1)
+ (match-end 1))))
+ (arity nil))
+ (list kind module name arity)))
+
+(defun erlang-get-some-other-id-at-point ()
+ (let ((name (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning 0) (match-end 0))))
+ (imports (erlang-get-import))
+ kind module arity)
+ (while (and imports (null module))
+ (if (assoc name (cdr (car imports)))
+ (setq module (car (car imports)))
+ (setq imports (cdr imports))))
+ (cond ((eq (preceding-char) ?#)
+ (setq kind 'record))
+ ((eq (preceding-char) ??)
+ (setq kind 'macro))
+ ((and (null module) (not (member name erlang-int-bifs)))
+ (setq module (erlang-get-module))))
+ (setq arity (progn
+ (goto-char (match-end 0))
+ (erlang-get-argument-list-arity)))
+ (list kind module name arity)))
+
+(defmacro erlang-with-id (slots id-string &rest body)
+ (declare (indent 2))
+ (let ((id-var (make-symbol "id")))
+ `(let* ((,id-var (erlang-id-to-list ,id-string))
+ ,@(mapcar (lambda (slot)
+ (list slot
+ (list (intern (format "erlang-id-%s" slot))
+ id-var)))
+ slots))
+ ,@body)))
+
+(defun erlang-id-to-string (id)
+ (when id
+ (erlang-with-id (kind module name arity) id
+ (format "%s%s%s%s"
+ (if kind (format "%s " kind) "")
+ (if module (format "%s:" module) "")
+ name
+ (if arity (format "/%s" arity) "")))))
+
+(defun erlang-id-to-list (id)
+ (if (listp id)
+ id
+ (save-match-data
+ (erlang-ensure-syntax-table-is-initialized)
+ (with-syntax-table erlang-mode-syntax-table
+ (let (case-fold-search)
+ (when (string-match erlang-id-regexp id)
+ (list (when (match-string 1 id)
+ (intern (match-string 1 id)))
+ (match-string 2 id)
+ (match-string 3 id)
+ (when (match-string 4 id)
+ (string-to-number (match-string 4 id))))))))))
+
+(defun erlang-id-kind (id)
+ (car (erlang-id-to-list id)))
+
+(defun erlang-id-module (id)
+ (nth 1 (erlang-id-to-list id)))
+
+(defun erlang-id-name (id)
+ (nth 2 (erlang-id-to-list id)))
+
+(defun erlang-id-arity (id)
+ (nth 3 (erlang-id-to-list id)))
;; TODO: Escape single quotes inside the string without
@@ -3801,9 +3798,9 @@ In the future the list may contain more elements."
(if (and (stringp str)
(not (string-match (eval-when-compile
(concat "\\`" erlang-atom-regexp "\\'")) str)))
- (progn (if (fboundp 'replace-regexp-in-string)
- (setq str (replace-regexp-in-string "'" "\\'" str t t )))
- (concat "'" str "'"))
+ (progn
+ (setq str (replace-regexp-in-string "'" "\\'" str t t ))
+ (concat "'" str "'"))
str)))
@@ -3820,10 +3817,10 @@ In the future the list may contain more elements."
"Returns non-nil if there is an exported function in the current
buffer between point and MAX."
(block nil
- (while (and (not erlang-inhibit-exported-function-name-face)
- (erlang-match-next-function max))
- (when (erlang-last-match-exported-p)
- (return (match-data))))))
+ (while (and (not erlang-inhibit-exported-function-name-face)
+ (erlang-match-next-function max))
+ (when (erlang-last-match-exported-p)
+ (return (match-data))))))
(defun erlang-match-next-function (max)
"Searches forward in current buffer for the next erlang function,
@@ -4082,7 +4079,7 @@ non-whitespace characters following the point on the current line."
nil)))
-(defun erlang-electric-arrow\ off (&optional arg)
+(defun erlang-electric-arrow (&optional arg)
"Insert a '>'-sign and possibly a new indented line.
This command is only `electric' when the `>' is part of an `->' arrow.
@@ -4198,9 +4195,7 @@ context, nil is returned."
(let* ((lim (or lim (save-excursion
(erlang-beginning-of-clause)
(point))))
- (state (if (fboundp 'syntax-ppss) ; post Emacs 21.3
- (funcall (symbol-function 'syntax-ppss))
- (parse-partial-sexp lim (point)))))
+ (state (funcall (symbol-function 'syntax-ppss))))
(cond
((eq (nth 3 state) ?') 'atom)
((nth 3 state) 'string)
@@ -4308,16 +4303,10 @@ This function is designed to be a member of a criteria list."
(looking-at "end[^_a-zA-Z0-9]")))
-;; Erlang tags support which is aware of erlang modules.
-;;
-;; Not yet implemented under XEmacs. (Hint: The Emacs 19 etags
-;; package works under XEmacs.)
+;;; Erlang tags support which is aware of erlang modules.
(eval-when-compile
- (if (or (featurep 'bytecomp)
- (featurep 'byte-compile))
- (progn
- (require 'etags))))
+ (require 'etags))
;; Variables:
@@ -4361,28 +4350,16 @@ After calling this function, the tags functions are aware of
Erlang modules. Tags can be entered on the for `module:tag' as well
as on the old form `tag'.
-In the completion list, `module:tag' and `module:' shows up.
-
-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.)"
+In the completion list, `module:tag' and `module:' shows up."
(interactive)
- (cond ((= erlang-emacs-major-version 18)
- (require 'tags)
- (erlang-tags-define-keys (current-local-map))
- (setq erlang-tags-installed t))
- (t
- (require 'etags)
- (set (make-local-variable 'find-tag-default-function)
- 'erlang-find-tag-for-completion)
- (if (>= emacs-major-version 25)
- (add-hook 'xref-backend-functions
- #'erlang-etags--xref-backend nil t)
- ;; Test on a function available in the Emacs 19 version
- ;; of tags but not in the XEmacs version.
- (when (fboundp 'find-tag-noselect)
- (erlang-tags-define-keys (current-local-map))
- (setq erlang-tags-installed t))))))
+ (require 'etags)
+ (set (make-local-variable 'find-tag-default-function)
+ 'erlang-find-tag-for-completion)
+ (if (>= emacs-major-version 25)
+ (add-hook 'xref-backend-functions
+ #'erlang-etags--xref-backend nil t)
+ (erlang-tags-define-keys (current-local-map))
+ (setq erlang-tags-installed t)))
@@ -4407,20 +4384,6 @@ works under XEmacs.)"
(erlang-menu-substitute erlang-menu-base-items erlang-tags-function-alist)
(erlang-menu-init))
-
-(defun erlang-find-tag-default ()
- "Return the default tag.
-Search `-import' list of imported functions.
-Single quotes are been stripped away."
- (let ((mod-func (erlang-get-function-under-point)))
- (cond ((null mod-func)
- nil)
- ((null (car mod-func))
- (nth 1 mod-func))
- (t
- (concat (car mod-func) ":" (nth 1 mod-func))))))
-
-
;; Return `t' since it is used inside `tags-loop-form'.
;;;###autoload
(defun erlang-find-tag (modtagname &optional next-p regexp-p)
@@ -4533,8 +4496,7 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(while (null file)
(or erlang-tags-file-list
(save-excursion
- (if (and (featurep 'etags)
- (funcall
+ (if (and (funcall
(symbol-function 'visit-tags-table-buffer) 'same)
(funcall
(symbol-function 'visit-tags-table-buffer) t))
@@ -4553,35 +4515,12 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(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))))))))
+ (progn
+ (erlang-tags-install-module-check)
+ (unwind-protect
+ (funcall (symbol-function 'find-tag)
+ modtagname next-p regexp-p)
+ (erlang-tags-remove-module-check))))
(t
(funcall (symbol-function 'find-tag) modtagname next-p regexp-p)))
(current-buffer))) ; Return the new buffer.
@@ -4607,13 +4546,11 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(list nil (if (< (prefix-numeric-value current-prefix-arg) 0)
'-
t))
- (let* ((default (erlang-find-tag-default))
+ (let* ((default (erlang-default-function-or-module))
(prompt (if default
(format "%s(default %s) " prompt default)
prompt))
- (spec (if (featurep 'etags)
- (completing-read prompt 'erlang-tags-complete-tag)
- (read-string prompt))))
+ (spec (completing-read prompt 'erlang-tags-complete-tag)))
(list (if (equal spec "")
(or default (error "There is no default tag"))
spec)))))
@@ -4630,23 +4567,12 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
"Install our own tag search functions."
;; Make sure our functions are installed in TAGS files loaded
;; into Emacs while searching.
- (cond
- ((>= erlang-emacs-major-version 20)
- (setq erlang-tags-orig-format-functions
- (symbol-value 'tags-table-format-functions))
- (funcall (symbol-function 'set) 'tags-table-format-functions
- (cons 'erlang-tags-recognize-tags-table
- erlang-tags-orig-format-functions))
- (setq erlang-tags-buffer-list '())
- )
- (t
- (setq erlang-tags-orig-format-hooks
- (symbol-value 'tags-table-format-hooks))
- (funcall (symbol-function 'set) 'tags-table-format-hooks
- (cons 'erlang-tags-recognize-tags-table
- erlang-tags-orig-format-hooks))
- (setq erlang-tags-buffer-list '())
- ))
+ (setq erlang-tags-orig-format-functions
+ (symbol-value 'tags-table-format-functions))
+ (funcall (symbol-function 'set) 'tags-table-format-functions
+ (cons 'erlang-tags-recognize-tags-table
+ erlang-tags-orig-format-functions))
+ (setq erlang-tags-buffer-list '())
;; Install our functions in the TAGS files already resident.
(save-excursion
@@ -4708,17 +4634,9 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(defun erlang-tags-remove-module-check ()
"Remove our own tags search functions."
- (cond
- ((>= erlang-emacs-major-version 20)
- (funcall (symbol-function 'set)
- 'tags-table-format-functions
- erlang-tags-orig-format-functions)
- )
- (t
- (funcall (symbol-function 'set)
- 'tags-table-format-hooks
- erlang-tags-orig-format-hooks)
- ))
+ (funcall (symbol-function 'set)
+ 'tags-table-format-functions
+ erlang-tags-orig-format-functions)
;; Remove our functions from the TAGS files. (Note that
;; `tags-table-computed-list' need not be the same list as when
@@ -4786,21 +4704,19 @@ for a tag on the form `module:tag'."
;;; completion-table' containing all normal tags plus tags on the form
;;; `module:tag' and `module:'.
-(when (and (locate-library "etags")
- (require 'etags)
- (fboundp 'etags-tags-completion-table)
- (fboundp 'tags-lazy-completion-table)) ; Emacs 23.1+
- (if (fboundp 'advice-add)
- ;; Emacs 24.4+
+(if (fboundp 'advice-add)
+ ;; Emacs 24.4+
+ (progn
+ (require 'etags)
(advice-add 'etags-tags-completion-table :around
- #'erlang-etags-tags-completion-table-advice)
- ;; Emacs 23.1-24.3
- (defadvice etags-tags-completion-table (around
- erlang-replace-tags-table
- activate)
- (if erlang-replace-etags-tags-completion-table
- (setq ad-return-value (erlang-etags-tags-completion-table))
- ad-do-it))))
+ #'erlang-etags-tags-completion-table-advice))
+ ;; Emacs 23.1-24.3
+ (defadvice etags-tags-completion-table (around
+ erlang-replace-tags-table
+ activate)
+ (if erlang-replace-etags-tags-completion-table
+ (setq ad-return-value (erlang-etags-tags-completion-table))
+ ad-do-it)))
(defun erlang-etags-tags-completion-table-advice (oldfun)
(if erlang-replace-etags-tags-completion-table
@@ -4814,28 +4730,9 @@ 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 (fboundp 'etags-tags-completion-table)
- (fboundp 'tags-lazy-completion-table)) ; Emacs 23.1+
- (let ((erlang-replace-etags-tags-completion-table t))
- (complete-tag)))
- ((and erlang-tags-installed
- (fboundp 'complete-tag)
- (fboundp 'tags-complete-tag)) ; Emacs 19-22
- (let ((orig-tags-complete-tag (symbol-function 'tags-complete-tag)))
- (fset 'tags-complete-tag
- (symbol-function 'erlang-tags-complete-tag))
- (unwind-protect
- (complete-tag)
- (fset 'tags-complete-tag orig-tags-complete-tag))))
- ((fboundp 'complete-tag) ; Emacs 19
- (complete-tag))
- ((fboundp 'tag-complete-symbol) ; XEmacs
- (funcall (symbol-function 'tag-complete-symbol)))
- (t
- (error "This version of Emacs can't complete tags"))))
+ (require 'etags)
+ (let ((erlang-replace-etags-tags-completion-table t))
+ (complete-tag)))
(defun erlang-find-tag-for-completion ()
@@ -4959,6 +4856,14 @@ about Erlang modules."
;; It adds awareness of the module:tag syntax in a similar way that is
;; done above for the old etags commands.
+(defvar erlang-current-arity nil
+ "The arity of the function currently being searched.
+
+There is no information about arity in the TAGS file.
+Consecutive functions with same name but different arity will
+only get one entry in the TAGS file. Matching TAGS entries are
+therefore selected without regarding arity. The arity is
+considered first when it is time to jump to the definition.")
(defun erlang-etags--xref-backend () 'erlang-etags)
@@ -4968,13 +4873,15 @@ about Erlang modules."
(and (erlang-soft-require 'xref)
(erlang-soft-require 'cl-generic)
+ (erlang-soft-require 'eieio)
+ (erlang-soft-require 'etags)
;; The purpose of using eval here is to avoid compilation
- ;; warnings in emacsen without cl-defmethod.
+ ;; warnings in emacsen without cl-defmethod etc.
(eval
'(progn
(cl-defmethod xref-backend-identifier-at-point
((_backend (eql erlang-etags)))
- (erlang-find-tag-default))
+ (erlang-id-to-string (erlang-get-identifier-at-point)))
(cl-defmethod xref-backend-definitions
((_backend (eql erlang-etags)) identifier)
@@ -4987,42 +4894,99 @@ about Erlang modules."
(cl-defmethod xref-backend-identifier-completion-table
((_backend (eql erlang-etags)))
(let ((erlang-replace-etags-tags-completion-table t))
- (tags-completion-table))))))
-
-
+ (tags-completion-table)))
+
+ (defclass erlang-xref-location (xref-etags-location) ())
+
+ (defun erlang-convert-xrefs (xrefs)
+ (mapcar (lambda (xref)
+ (oset xref location (erlang-make-location
+ (oref xref location)))
+ xref)
+ xrefs))
+
+ (defun erlang-make-location (etags-location)
+ (with-slots (tag-info file) etags-location
+ (make-instance 'erlang-xref-location :tag-info tag-info
+ :file file)))
+
+ (cl-defmethod xref-location-marker ((locus erlang-xref-location))
+ (with-slots (tag-info file) locus
+ (with-current-buffer (find-file-noselect file)
+ (save-excursion
+ (or (erlang-goto-tag-location-by-arity tag-info)
+ (etags-goto-tag-location tag-info))
+ ;; Reset erlang-current-arity. We want to jump to
+ ;; correct arity in the first attempt. That is now
+ ;; done. Possible remaining jumps will be from
+ ;; entries in the *xref* buffer and then we want to
+ ;; ignore the arity. (Alternatively we could remove
+ ;; all but one xref entry per file when we know the
+ ;; arity).
+ (setq erlang-current-arity nil)
+ (point-marker)))))
+
+ (defun erlang-xref-context (xref)
+ (with-slots (tag-info) (xref-item-location xref)
+ (car tag-info))))))
+
+
+(defun erlang-goto-tag-location-by-arity (tag-info)
+ (when erlang-current-arity
+ (let* ((tag-text (car tag-info))
+ (tag-pos (cdr (cdr tag-info)))
+ (tag-line (car (cdr tag-info)))
+ (regexp (erlang-tag-info-regexp tag-text))
+ (startpos (or tag-pos
+ (when tag-line
+ (goto-char (point-min))
+ (forward-line (1- tag-line))
+ (point))
+ (point-min))))
+ (setq startpos (max (- startpos 2000)
+ (point-min)))
+ (goto-char startpos)
+ (let ((pos (or (erlang-search-by-arity regexp)
+ (unless (eq startpos (point-min))
+ (goto-char (point-min))
+ (erlang-search-by-arity regexp)))))
+ (when pos
+ (goto-char pos)
+ t)))))
+
+(defun erlang-tag-info-regexp (tag-text)
+ (concat "^"
+ (regexp-quote tag-text)
+ ;; Erlang function entries in TAGS includes the opening
+ ;; parenthesis for the argument list. Erlang macro entries
+ ;; do not. Add it here in order to end up in correct
+ ;; position for erlang-get-arity.
+ (if (string-prefix-p "-define" tag-text)
+ "\\s-*("
+ "")))
+
+(defun erlang-search-by-arity (regexp)
+ (let (pos)
+ (while (and (null pos)
+ (re-search-forward regexp nil t))
+ (when (eq erlang-current-arity (save-excursion (erlang-get-arity)))
+ (setq pos (point-at-bol))))
+ pos))
(defun erlang-xref-find-definitions (identifier &optional is-regexp)
- (let ((id-list (split-string identifier ":")))
- (cond
- ;; Handle "tag"
- ((null (cdr id-list))
- (erlang-xref-find-definitions-tag identifier is-regexp))
- ;; Handle "module:"
- ((string-equal (cadr id-list) "")
- (erlang-xref-find-definitions-module (car id-list)))
- ;; Handle "module:tag"
- (t
- (erlang-xref-find-definitions-module-tag (car id-list)
- (cadr id-list)
- is-regexp)))))
-
-(defun erlang-xref-find-definitions-tag (tag is-regexp)
- "Find all definitions of TAG and reorder them so that
-definitions in the currently visited file comes first."
- (when (fboundp 'etags--xref-find-definitions)
- (let* ((current-file (and (buffer-file-name)
- (file-truename (buffer-file-name))))
- (xrefs (etags--xref-find-definitions tag is-regexp))
- local-xrefs non-local-xrefs)
- (while xrefs
- (if (string-equal (erlang-xref-truename-file (car xrefs))
- current-file)
- (push (car xrefs) local-xrefs)
- (push (car xrefs) non-local-xrefs))
- (setq xrefs (cdr xrefs)))
- (append (reverse local-xrefs)
- (reverse non-local-xrefs)))))
+ (erlang-with-id (kind module name arity) identifier
+ (setq erlang-current-arity arity)
+ (cond ((eq kind 'module)
+ (erlang-xref-find-definitions-module name))
+ (module
+ (erlang-xref-find-definitions-module-tag module
+ name
+ (eq kind
+ 'qualified-function)
+ is-regexp))
+ (t
+ (erlang-xref-find-definitions-tag kind name is-regexp)))))
(defun erlang-xref-find-definitions-module (module)
(and (fboundp 'xref-make)
@@ -5046,17 +5010,58 @@ definitions in the currently visited file comes first."
(setq files (cdr files))))))
(nreverse xrefs))))
-(defun erlang-xref-find-definitions-module-tag (module tag is-regexp)
- "Find all definitions of TAG and filter away definitions
-outside of MODULE."
- (when (fboundp 'etags--xref-find-definitions)
- (let ((xrefs (etags--xref-find-definitions tag is-regexp))
- xrefs-in-module)
- (while xrefs
- (when (string-equal module (erlang-xref-module (car xrefs)))
- (push (car xrefs) xrefs-in-module))
- (setq xrefs (cdr xrefs)))
- xrefs-in-module)))
+
+(defun erlang-xref-find-definitions-module-tag (module
+ tag
+ is-qualified
+ is-regexp)
+ "Find definitions of TAG and filter away definitions outside of
+MODULE. If IS-QUALIFIED is nil and no definitions was found inside
+the MODULE then return any definitions found outside. If
+IS-REGEXP is non-nil then TAG is a regexp."
+ (and (fboundp 'etags--xref-find-definitions)
+ (fboundp 'erlang-convert-xrefs)
+ (let ((xrefs (erlang-convert-xrefs
+ (etags--xref-find-definitions tag is-regexp)))
+ xrefs-in-module)
+ (dolist (xref xrefs)
+ (when (string-equal module (erlang-xref-module xref))
+ (push xref xrefs-in-module)))
+ (cond (is-qualified xrefs-in-module)
+ (xrefs-in-module xrefs-in-module)
+ (t xrefs)))))
+
+(defun erlang-xref-find-definitions-tag (kind tag is-regexp)
+ "Find all definitions of TAG and reorder them so that
+definitions in the currently visited file comes first."
+ (and (fboundp 'etags--xref-find-definitions)
+ (fboundp 'erlang-convert-xrefs)
+ (let* ((current-file (and (buffer-file-name)
+ (file-truename (buffer-file-name))))
+ (regexp (erlang-etags-regexp kind tag is-regexp))
+ (xrefs (erlang-convert-xrefs
+ (etags--xref-find-definitions regexp t)))
+ local-xrefs non-local-xrefs)
+ (while xrefs
+ (let ((xref (car xrefs)))
+ (if (string-equal (erlang-xref-truename-file xref)
+ current-file)
+ (push xref local-xrefs)
+ (push xref non-local-xrefs))
+ (setq xrefs (cdr xrefs))))
+ (append (reverse local-xrefs)
+ (reverse non-local-xrefs)))))
+
+(defun erlang-etags-regexp (kind tag is-regexp)
+ (let ((tag-regexp (if is-regexp
+ tag
+ (regexp-quote tag))))
+ (cond ((eq kind 'record)
+ (concat "-record\\s-*(\\s-*" tag-regexp))
+ ((eq kind 'macro)
+ (concat "-define\\s-*(\\s-*" tag-regexp))
+ (t tag-regexp))))
+
(defun erlang-xref-module (xref)
(erlang-get-module-from-file-name (erlang-xref-file xref)))
@@ -5172,7 +5177,7 @@ future, a new shell on an already running host will be started."
(defvar erlang-shell-mode-hook nil
- "*User functions to run when an Erlang shell is started.
+ "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.
@@ -5188,7 +5193,7 @@ 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.")
+ "When non-nil, file name used to store Erlang shell history information.")
(defun erlang-shell-mode ()
@@ -5226,13 +5231,11 @@ The following special commands are available:
'inferior-erlang-strip-delete nil t)
(add-hook 'comint-output-filter-functions
'inferior-erlang-strip-ctrl-m nil t)
- ;; Some older versions of comint don't have an input ring.
- (if (fboundp 'comint-read-input-ring)
- (progn
- (setq comint-input-ring-file-name erlang-input-ring-file-name)
- (comint-read-input-ring t)
- (make-local-variable 'kill-buffer-hook)
- (add-hook 'kill-buffer-hook 'comint-write-input-ring)))
+
+ (setq comint-input-ring-file-name erlang-input-ring-file-name)
+ (comint-read-input-ring t)
+ (make-local-variable 'kill-buffer-hook)
+ (add-hook 'kill-buffer-hook 'comint-write-input-ring)
;; At least in Emacs 21, we need to be in `compilation-minor-mode'
;; for `next-error' to work. We can avoid it clobbering the shell
;; keys thus.
@@ -5288,7 +5291,7 @@ Selects Comint or Compilation mode command as appropriate."
;;;
(defvar inferior-erlang-display-buffer-any-frame nil
- "*When nil, `inferior-erlang-display-buffer' use only selected frame.
+ "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
@@ -5301,10 +5304,10 @@ 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.")
+ "The name of the Erlang shell.")
(defvar inferior-erlang-machine-options '()
- "*The options used when activating the Erlang shell.
+ "The options used when activating the Erlang shell.
This must be a list of strings.")
@@ -5315,7 +5318,7 @@ This must be a list of strings.")
"The name of the inferior Erlang buffer.")
(defvar inferior-erlang-prompt-timeout 60
- "*Number of seconds before `inferior-erlang-wait-prompt' timeouts.
+ "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
@@ -5350,10 +5353,7 @@ editing control characters:
\\{erlang-shell-mode-map}"
(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: ")))))
+ (list (read-shell-command "Erlang command: "))))
(require 'comint)
(let (cmd opts)
(if command
@@ -5381,10 +5381,8 @@ editing control characters:
(setq inferior-erlang-process
(get-buffer-process inferior-erlang-buffer))
- (if (> 21 erlang-emacs-major-version) ; funcalls to avoid compiler warnings
- (funcall (symbol-function 'set-process-query-on-exit-flag)
- inferior-erlang-process nil)
- (funcall (symbol-function 'process-kill-without-query) inferior-erlang-process))
+ (funcall (symbol-function 'set-process-query-on-exit-flag)
+ inferior-erlang-process nil)
(if erlang-inferior-shell-split-window
(switch-to-buffer-other-window inferior-erlang-buffer)
(switch-to-buffer inferior-erlang-buffer))
@@ -5452,7 +5450,7 @@ frame will become deselected before the next command."
(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))
+ (if all-frames
(get-buffer-window inferior-erlang-buffer t)
(get-buffer-window inferior-erlang-buffer))))
@@ -5524,10 +5522,7 @@ Return the position after the newly inserted command."
;; has been sorted out in Emacs 21. -- fx
(let ((comint-eol-on-send nil)
(comint-input-filter (if hist comint-input-filter 'ignore)))
- (if (and (not erlang-xemacs-p)
- (>= emacs-major-version 22))
- (comint-send-input nil t)
- (comint-send-input)))
+ (comint-send-input nil t))
;; Adjust all windows whose points are incorrect.
(if (null comint-process-echoes)
(walk-windows
@@ -5549,7 +5544,7 @@ Return the position after the newly inserted command."
(boundp 'comint-last-output-start))
(save-excursion
(goto-char
- (if (erlang-interactive-p)
+ (if (called-interactively-p 'interactive)
(symbol-value 'comint-last-input-end)
(symbol-value 'comint-last-output-start)))
(while (progn (skip-chars-forward "^\C-h")
@@ -5568,7 +5563,7 @@ Return the position after the newly inserted command."
(let ((pmark (process-mark (get-buffer-process (current-buffer)))))
(save-excursion
(goto-char
- (if (erlang-interactive-p)
+ (if (called-interactively-p 'interactive)
(symbol-value 'comint-last-input-end)
(symbol-value 'comint-last-output-start)))
(while (re-search-forward "\r+$" pmark t)
@@ -5911,8 +5906,7 @@ Tab characters are counted by their visual width."
Simplified version of a combination `defalias' and `make-obsolete',
it assumes that NEWDEF is loaded."
(defalias sym (symbol-function newdef))
- (if (fboundp 'make-obsolete)
- (make-obsolete sym newdef "long ago")))
+ (make-obsolete sym newdef "long ago"))
(erlang-obsolete 'calculate-erlang-indent 'erlang-calculate-indent)
@@ -5932,15 +5926,8 @@ it assumes that NEWDEF is loaded."
(defconst erlang-unload-hook
(list (lambda ()
- (when (featurep 'advice)
- (ad-unadvise 'Man-notify-when-ready)
- (ad-unadvise 'set-visited-file-name)))))
-
-
-(defun erlang-string-to-int (string)
- (if (fboundp 'string-to-number)
- (string-to-number string)
- (funcall (symbol-function 'string-to-int) string)))
+ (ad-unadvise 'Man-notify-when-ready)
+ (ad-unadvise 'set-visited-file-name))))
;; The end...
@@ -5949,7 +5936,7 @@ it assumes that NEWDEF is loaded."
(run-hooks 'erlang-load-hook)
;; Local variables:
-;; coding: iso-8859-1
+;; coding: utf-8
;; indent-tabs-mode: nil
;; End:
diff --git a/lib/tools/emacs/erldoc.el b/lib/tools/emacs/erldoc.el
index cb355374d9..770ab299ee 100644
--- a/lib/tools/emacs/erldoc.el
+++ b/lib/tools/emacs/erldoc.el
@@ -2,7 +2,7 @@
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 2016. All Rights Reserved.
+;; Copyright Ericsson AB 2016-2017. All Rights Reserved.
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@
;; Crawl Erlang/OTP HTML documentation and generate lookup tables.
;;
;; This package depends on `cl-lib', `pcase' and
-;; `libxml-parse-html-region'; emacs 24+ compiled with libxml2 should
-;; work. On emacs 24.1 and 24.2 do `M-x package-install RET cl-lib
+;; `libxml-parse-html-region'. Emacs 24+ compiled with libxml2 should
+;; work. On Emacs 24.1 and 24.2 do `M-x package-install RET cl-lib
;; RET' to install `cl-lib'.
;;
;; Please customise `erldoc-man-index' to point to your local OTP
@@ -407,7 +407,7 @@ up the indexing."
(defvar erldoc-user-guides nil)
(defvar erldoc-missing-user-guides
- '("compiler" "hipe" "kernel" "os_mon" "parsetools" "typer")
+ '("compiler" "hipe" "kernel" "os_mon" "parsetools")
"List of standard Erlang applications with no user guides.")
;; Search in `code:lib_dir/0' using find LIB_DIR -type f -name
@@ -417,7 +417,7 @@ up the indexing."
"runtime_tools" "sasl" "snmp"
"ssl" "test_server"
("ssh" . "SSH") ("stdlib" . "STDLIB")
- ("hipe" . "HiPE") ("typer" . "TypEr"))
+ ("hipe" . "HiPE"))
"List of applications that come with a manual.")
(defun erldoc-user-guide-chapters (user-guide)
@@ -505,4 +505,10 @@ up the indexing."
(browse-url (cdr (assoc topic (erldoc-user-guides)))))
(provide 'erldoc)
+
+;; Local variables:
+;; coding: utf-8
+;; indent-tabs-mode: nil
+;; End:
+
;;; erldoc.el ends here
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index 92c10cc306..5517882ffa 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -2053,7 +2053,7 @@ munge_expr({bin_element,Line,Value,Size,TypeSpecifierList}, Vars) ->
{MungedValue,Vars2} = munge_expr(Value, Vars),
{MungedSize,Vars3} = munge_expr(Size, Vars2),
{{bin_element,Line,MungedValue,MungedSize,TypeSpecifierList},Vars3};
-munge_expr(Form, Vars) -> % var|char|integer|float|string|atom|nil|eof|default
+munge_expr(Form, Vars) ->
{Form, Vars}.
munge_exprs([Expr|Exprs], Vars, MungedExprs) when Vars#vars.is_guard=:=true,
@@ -2414,8 +2414,8 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->
{ok, InFd} ->
case file:open(OutFile, [write,raw,delayed_write]) of
{ok, OutFd} ->
+ Enc = encoding(ErlFile),
if HTML ->
- Encoding = encoding(ErlFile),
Header =
["<!DOCTYPE HTML PUBLIC "
"\"-//W3C//DTD HTML 3.2 Final//EN\">\n"
@@ -2423,13 +2423,14 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->
"<head>\n"
"<meta http-equiv=\"Content-Type\""
" content=\"text/html; charset=",
- Encoding,"\"/>\n"
+ html_encoding(Enc),"\"/>\n"
"<title>",OutFile,"</title>\n"
"</head>"
"<body style='background-color: white;"
" color: black'>\n"
"<pre>\n"],
- ok = file:write(OutFd,Header);
+ H1Bin = unicode:characters_to_binary(Header,Enc,Enc),
+ ok = file:write(OutFd,H1Bin);
true -> ok
end,
@@ -2443,12 +2444,15 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->
string:right(integer_to_list(H), 2, $0),
string:right(integer_to_list(Mi), 2, $0),
string:right(integer_to_list(S), 2, $0)]),
- ok = file:write(OutFd,
- ["File generated from ",ErlFile," by COVER ",
+
+ H2Bin = unicode:characters_to_binary(
+ ["File generated from ",ErlFile," by COVER ",
Timestamp,"\n\n"
"**************************************"
"**************************************"
- "\n\n"]),
+ "\n\n"],
+ Enc, Enc),
+ ok = file:write(OutFd, H2Bin),
Pattern = {#bump{module=Module,line='$1',_='_'},'$2'},
MS = [{Pattern,[{is_integer,'$1'},{'>','$1',0}],[{{'$1','$2'}}]}],
@@ -2752,16 +2756,22 @@ pmap_collect(Mons,Acc) ->
end.
%%%-----------------------------------------------------------------
-%%% Read encoding from source file
+%%% Decide which encoding to use when analyzing to file.
+%%% The target file contains the file path, so if either the file name
+%%% encoding or the encoding of the source file is utf8, then we need
+%%% to use utf8.
encoding(File) ->
- Encoding =
- case epp:read_encoding(File) of
- none ->
- epp:default_encoding();
- E ->
- E
- end,
- html_encoding(Encoding).
+ case file:native_name_encoding() of
+ latin1 ->
+ case epp:read_encoding(File) of
+ none ->
+ epp:default_encoding();
+ E ->
+ E
+ end;
+ utf8 ->
+ utf8
+ end.
html_encoding(latin1) ->
"iso-8859-1";
diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl
index 23d66b084e..d881fedbd5 100644
--- a/lib/tools/src/lcnt.erl
+++ b/lib/tools/src/lcnt.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -698,19 +698,47 @@ stats2record([{{File,Line},{Tries,Colls,{S,Ns,N}}}|Stats]) ->
nt = N} | stats2record(Stats)];
stats2record([]) -> [].
+
clean_id_creation(Id) when is_pid(Id) ->
Bin = term_to_binary(Id),
- <<H:3/binary, L:16, Node:L/binary, Ids:8/binary, _Creation/binary>> = Bin,
- Bin2 = list_to_binary([H, bytes16(L), Node, Ids, 0]),
+ <<H:3/binary, Rest/binary>> = Bin,
+ <<131, PidTag, AtomTag>> = H,
+ LL = atomlen_bits(AtomTag),
+ CL = creation_bits(PidTag),
+ <<L:LL, Node:L/binary, Ids:8/binary, _Creation/binary>> = Rest,
+ Bin2 = list_to_binary([H, <<L:LL>>, Node, Ids, <<0:CL>>]),
binary_to_term(Bin2);
clean_id_creation(Id) when is_port(Id) ->
Bin = term_to_binary(Id),
- <<H:3/binary, L:16, Node:L/binary, Ids:4/binary, _Creation/binary>> = Bin,
- Bin2 = list_to_binary([H, bytes16(L), Node, Ids, 0]),
+ <<H:3/binary, Rest/binary>> = Bin,
+ <<131, PortTag, AtomTag>> = H,
+ LL = atomlen_bits(AtomTag),
+ CL = creation_bits(PortTag),
+ <<L:LL, Node:L/binary, Ids:4/binary, _Creation/binary>> = Rest,
+ Bin2 = list_to_binary([H, <<L:LL>>, Node, Ids, <<0:CL>>]),
binary_to_term(Bin2);
clean_id_creation(Id) ->
Id.
+-define(PID_EXT, $g).
+-define(NEW_PID_EXT, $X).
+-define(PORT_EXT, $f).
+-define(NEW_PORT_EXT, $Y).
+-define(ATOM_EXT, $d).
+-define(SMALL_ATOM_EXT, $s).
+-define(ATOM_UTF8_EXT, $v).
+-define(SMALL_ATOM_UTF8_EXT, $w).
+
+atomlen_bits(?ATOM_EXT) -> 16;
+atomlen_bits(?SMALL_ATOM_EXT) -> 8;
+atomlen_bits(?ATOM_UTF8_EXT) -> 16;
+atomlen_bits(?SMALL_ATOM_UTF8_EXT) -> 8.
+
+creation_bits(?PID_EXT) -> 8;
+creation_bits(?NEW_PID_EXT) -> 32;
+creation_bits(?PORT_EXT) -> 8;
+creation_bits(?NEW_PORT_EXT) -> 32.
+
%% serializer
state_default(Field) -> proplists:get_value(Field, state2list(#state{})).
@@ -936,12 +964,24 @@ strings([S|Ss], Out) -> strings(Ss, Out ++ term2string("~ts", [S])).
term2string({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> term2string("~p:~p/~p", [M,F,A]);
term2string(Term) when is_port(Term) ->
% ex #Port<6442.816>
- <<_:3/binary, L:16, Node:L/binary, Ids:32, _/binary>> = term_to_binary(Term),
- term2string("#Port<~s.~w>", [Node, Ids]);
+ case term_to_binary(Term) of
+ <<_:2/binary, ?SMALL_ATOM_UTF8_EXT, L:8, Node:L/binary, Ids:32, _/binary>> ->
+ term2string("#Port<~ts.~w>", [Node, Ids]);
+ <<_:2/binary, ?ATOM_UTF8_EXT, L:16, Node:L/binary, Ids:32, _/binary>> ->
+ term2string("#Port<~ts.~w>", [Node, Ids]);
+ <<_:2/binary, ?ATOM_EXT, L:16, Node:L/binary, Ids:32, _/binary>> ->
+ term2string("#Port<~s.~w>", [Node, Ids])
+ end;
term2string(Term) when is_pid(Term) ->
% ex <0.80.0>
- <<_:3/binary, L:16, Node:L/binary, Ids:32, Serial:32, _/binary>> = term_to_binary(Term),
- term2string("<~s.~w.~w>", [Node, Ids, Serial]);
+ case term_to_binary(Term) of
+ <<_:2/binary, ?SMALL_ATOM_UTF8_EXT, L:8, Node:L/binary, Ids:32, Serial:32, _/binary>> ->
+ term2string("<~ts.~w.~w>", [Node, Ids, Serial]);
+ <<_:2/binary, ?ATOM_UTF8_EXT, L:16, Node:L/binary, Ids:32, Serial:32, _/binary>> ->
+ term2string("<~ts.~w.~w>", [Node, Ids, Serial]);
+ <<_:2/binary, ?ATOM_EXT, L:16, Node:L/binary, Ids:32, Serial:32, _/binary>> ->
+ term2string("<~s.~w.~w>", [Node, Ids, Serial])
+ end;
term2string(Term) -> term2string("~w", [Term]).
term2string(Format, Terms) -> lists:flatten(io_lib:format(Format, Terms)).
diff --git a/lib/tools/src/make.erl b/lib/tools/src/make.erl
index 37e67cbe34..ce30156db6 100644
--- a/lib/tools/src/make.erl
+++ b/lib/tools/src/make.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@
-include_lib("kernel/include/file.hrl").
--define(MakeOpts,[noexec,load,netload,noload]).
+-define(MakeOpts,[noexec,load,netload,noload,emake]).
all_or_nothing() ->
case all() of
@@ -43,29 +43,30 @@ all() ->
all([]).
all(Options) ->
- {MakeOpts,CompileOpts} = sort_options(Options,[],[]),
- case read_emakefile('Emakefile',CompileOpts) of
- Files when is_list(Files) ->
- do_make_files(Files,MakeOpts);
- error ->
- error
- end.
+ run_emake(undefined, Options).
files(Fs) ->
files(Fs, []).
files(Fs0, Options) ->
Fs = [filename:rootname(F,".erl") || F <- Fs0],
+ run_emake(Fs, Options).
+
+run_emake(Mods, Options) ->
{MakeOpts,CompileOpts} = sort_options(Options,[],[]),
- case get_opts_from_emakefile(Fs,'Emakefile',CompileOpts) of
+ Emake = get_emake(Options),
+ case normalize_emake(Emake, Mods, CompileOpts) of
Files when is_list(Files) ->
- do_make_files(Files,MakeOpts);
- error -> error
+ do_make_files(Files,MakeOpts);
+ error ->
+ error
end.
do_make_files(Fs, Opts) ->
process(Fs, lists:member(noexec, Opts), load_opt(Opts)).
+sort_options([{emake, _}=H|T],Make,Comp) ->
+ sort_options(T,[H|Make],Comp);
sort_options([H|T],Make,Comp) ->
case lists:member(H,?MakeOpts) of
@@ -89,20 +90,35 @@ sort_options([],Make,Comp) ->
%%%
%%% These elements are converted to [{ModList,OptList},...]
%%% ModList is a list of modulenames (strings)
-read_emakefile(Emakefile,Opts) ->
- case file:consult(Emakefile) of
- {ok,Emake} ->
+
+normalize_emake(EmakeRaw, Mods, Opts) ->
+ case EmakeRaw of
+ {ok, Emake} when Mods =:= undefined ->
transform(Emake,Opts,[],[]);
- {error,enoent} ->
+ {ok, Emake} when is_list(Mods) ->
+ ModsOpts = transform(Emake,Opts,[],[]),
+ ModStrings = [coerce_2_list(M) || M <- Mods],
+ get_opts_from_emakefile(ModsOpts,ModStrings,Opts,[]);
+ {error,enoent} when Mods =:= undefined ->
%% No Emakefile found - return all modules in current
%% directory and the options given at command line
- Mods = [filename:rootname(F) || F <- filelib:wildcard("*.erl")],
+ CwdMods = [filename:rootname(F) || F <- filelib:wildcard("*.erl")],
+ [{CwdMods, Opts}];
+ {error,enoent} when is_list(Mods) ->
[{Mods, Opts}];
- {error,Other} ->
- io:format("make: Trouble reading 'Emakefile':~n~tp~n",[Other]),
+ {error, Error} ->
+ io:format("make: Trouble reading 'Emakefile':~n~tp~n",[Error]),
error
end.
+get_emake(Opts) ->
+ case proplists:get_value(emake, Opts, false) of
+ false ->
+ file:consult('Emakefile');
+ OptsEmake ->
+ {ok, OptsEmake}
+ end.
+
transform([{Mod,ModOpts}|Emake],Opts,Files,Already) ->
case expand(Mod,Already) of
[] ->
@@ -143,31 +159,19 @@ expand(Mod,Already) ->
end
end.
-%%% Reads the given Emakefile to see if there are any specific compile
+%%% Reads the given Emake to see if there are any specific compile
%%% options given for the modules.
-get_opts_from_emakefile(Mods,Emakefile,Opts) ->
- case file:consult(Emakefile) of
- {ok,Emake} ->
- Modsandopts = transform(Emake,Opts,[],[]),
- ModStrings = [coerce_2_list(M) || M <- Mods],
- get_opts_from_emakefile2(Modsandopts,ModStrings,Opts,[]);
- {error,enoent} ->
- [{Mods, Opts}];
- {error,Other} ->
- io:format("make: Trouble reading 'Emakefile':~n~tp~n",[Other]),
- error
- end.
-get_opts_from_emakefile2([{MakefileMods,O}|Rest],Mods,Opts,Result) ->
+get_opts_from_emakefile([{MakefileMods,O}|Rest],Mods,Opts,Result) ->
case members(Mods,MakefileMods,[],Mods) of
{[],_} ->
- get_opts_from_emakefile2(Rest,Mods,Opts,Result);
+ get_opts_from_emakefile(Rest,Mods,Opts,Result);
{I,RestOfMods} ->
- get_opts_from_emakefile2(Rest,RestOfMods,Opts,[{I,O}|Result])
+ get_opts_from_emakefile(Rest,RestOfMods,Opts,[{I,O}|Result])
end;
-get_opts_from_emakefile2([],[],_Opts,Result) ->
+get_opts_from_emakefile([],[],_Opts,Result) ->
Result;
-get_opts_from_emakefile2([],RestOfMods,Opts,Result) ->
+get_opts_from_emakefile([],RestOfMods,Opts,Result) ->
[{RestOfMods,Opts}|Result].
members([H|T],MakefileMods,I,Rest) ->
@@ -286,11 +290,12 @@ coerce_2_list(X) when is_atom(X) ->
coerce_2_list(X) ->
X.
-%%% If you an include file is found with a modification
+%%% If an include file is found with a modification
%%% time larger than the modification time of the object
%%% file, return true. Otherwise return false.
check_includes(File, IncludePath, ObjMTime) ->
- Path = [filename:dirname(File)|IncludePath],
+ {ok,Cwd} = file:get_cwd(),
+ Path = [Cwd,filename:dirname(File)|IncludePath],
case epp:open(File, Path, []) of
{ok, Epp} ->
check_includes2(Epp, File, ObjMTime);
diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src
index 4c7dd24006..12f0cfd2df 100644
--- a/lib/tools/src/tools.app.src
+++ b/lib/tools/src/tools.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -41,7 +41,6 @@
]
},
{runtime_dependencies, ["stdlib-3.1","runtime_tools-1.8.14",
- "kernel-3.0","inets-5.10","erts-7.0",
- "compiler-5.0"]}
+ "kernel-3.0","erts-7.0","compiler-5.0"]}
]
}.
diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl
index f298a1ce81..8d2cc07e40 100644
--- a/lib/tools/src/xref_base.erl
+++ b/lib/tools/src/xref_base.erl
@@ -809,7 +809,8 @@ abst(File, Builtins, _Mode = functions) ->
{exports,X0}, {attributes,A}]}} ->
%% R9C-
Forms0 = epp:interpret_file_attribute(Code),
- {_,_,Forms,_} = sys_pre_expand:module(Forms0, []),
+ Forms1 = erl_expand_records:module(Forms0, []),
+ Forms = erl_internal:add_predefined_functions(Forms1),
X = mfa_exports(X0, A, M),
D = deprecated(A, X, M),
xref_reader:module(M, Forms, Builtins, X, D);
diff --git a/lib/tools/src/xref_reader.erl b/lib/tools/src/xref_reader.erl
index 41b93caaeb..88f92df35a 100644
--- a/lib/tools/src/xref_reader.erl
+++ b/lib/tools/src/xref_reader.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -42,17 +42,15 @@
%% experimental; -xref(FunEdge) is recognized.
lattrs=[], % local calls, {{mfa(),mfa()},Line}
xattrs=[], % external calls, -"-
- battrs=[] % badly formed xref attributes, term().
+ battrs=[] % badly formed xref attributes, term().
}).
-include("xref.hrl").
-%% sys_pre_expand has modified the forms slightly compared to what
-%% erl_id_trans recognizes.
-
%% The versions of the abstract code are as follows:
-%% R7: abstract_v1
-%% R8: abstract_v2
+%% R7: abstract_v1
+%% R8: abstract_v2
+%% R9C: raw_abstract_v1
%% -> {ok, Module, {DefAt, CallAt, LC, XC, X, Attrs}, Unresolved}} | EXIT
%% Attrs = {ALC, AXC, Bad}
@@ -92,7 +90,12 @@ form({function, Anno, Name, Arity, Clauses}, S) ->
Line = erl_anno:line(Anno),
S2 = S1#xrefr{def_at = [{MFA,Line} | S#xrefr.def_at]},
S3 = clauses(Clauses, S2),
- S3#xrefr{function = []}.
+ S3#xrefr{function = []};
+form(_, S) ->
+ %% OTP 20. Other uninteresting forms such as {eof, _} and {warning, _}.
+ %% Exposed because sys_pre_expand is no longer run.
+ S.
+
clauses(Cls, S) ->
#xrefr{funvars = FunVars, matches = Matches} = S,
@@ -109,6 +112,8 @@ clauses([{clause, _Line, _H, G, B} | Cs], FunVars, Matches, S) ->
clauses([], _FunVars, _Matches, S) ->
S.
+attr(NotList, Ln, M, Fun, AL, AX, B, S) when not is_list(NotList) ->
+ attr([NotList], Ln, M, Fun, AL, AX, B, S);
attr([E={From, To} | As], Ln, M, Fun, AL, AX, B, S) ->
case mfa(From, M) of
{_, _, MFA} when MFA =:= Fun; [] =:= Fun ->
@@ -154,6 +159,15 @@ expr({'try',_Line,Es,Scs,Ccs,As}, S) ->
S2 = clauses(Scs, S1),
S3 = clauses(Ccs, S2),
expr(As, S3);
+expr({'fun', Line, {function,M,F,A}}, S)
+ when is_atom(M), is_atom(F), is_integer(A) ->
+ %% This is the old format for external funs, generated by a pre-R15
+ %% compiler. Exposed in OTP 20 because sys_pre_expand is no longer
+ %% run.
+ Fun = {'fun', Line, {function, {atom,Line,M},
+ {atom,Line,F},
+ {integer,Line,A}}},
+ expr(Fun, S);
expr({'fun', Line, {function, {atom,_,Mod},
{atom,_,Name},
{integer,_,Arity}}}, S) ->
@@ -168,14 +182,21 @@ expr({'fun', Line, {function, Mod, Name, _Arity}}, S) ->
%% New format in R15. M:F/A (one or more variables).
As = {var, Line, '_'},
external_call(erlang, apply, [Mod, Name, As], Line, true, S);
+%% Only abstract_v1 and abstract_v2.
expr({'fun', Line, {function, Name, Arity}, _Extra}, S) ->
%% Added in R8.
handle_call(local, S#xrefr.module, Name, Arity, Line, S);
expr({'fun', _Line, {clauses, Cs}, _Extra}, S) ->
clauses(Cs, S);
-expr({named_fun, _Line, '_', Cs, _Extra}, S) ->
+%% End abstract_v1 and abstract_v2.
+expr({'fun', Line, {function, Name, Arity}}, S) ->
+ %% Added in OTP 20.
+ handle_call(local, S#xrefr.module, Name, Arity, Line, S);
+expr({'fun', _Line, {clauses, Cs}}, S) ->
+ clauses(Cs, S);
+expr({named_fun, _Line, '_', Cs}, S) ->
clauses(Cs, S);
-expr({named_fun, _Line, Name, Cs, _Extra}, S) ->
+expr({named_fun, _Line, Name, Cs}, S) ->
S1 = S#xrefr{funvars = [Name | S#xrefr.funvars]},
clauses(Cs, S1);
expr({call, Line, {atom, _, Name}, As}, S) ->
@@ -193,7 +214,12 @@ expr({match, _Line, {var,_,Var}, {'fun', _, {clauses, Cs}, _Extra}}, S) ->
%% that are passed around by the "expansion" of list comprehension.
S1 = S#xrefr{funvars = [Var | S#xrefr.funvars]},
clauses(Cs, S1);
-expr({match, _Line, {var,_,Var}, {named_fun, _, _, _, _} = Fun}, S) ->
+expr({match, _Line, {var,_,Var}, {'fun', _, {clauses, Cs}}}, S) ->
+ %% OTP 20. Exposed because sys_pre_expand is no longer run.
+ S1 = S#xrefr{funvars = [Var | S#xrefr.funvars]},
+ clauses(Cs, S1);
+expr({match, _Line, {var,_,Var}, {named_fun, _, _, _} = Fun}, S) ->
+ %% OTP 20. Exposed because sys_pre_expand is no longer run.
S1 = S#xrefr{funvars = [Var | S#xrefr.funvars]},
expr(Fun, S1);
expr({match, _Line, {var,_,Var}, E}, S) ->
@@ -295,10 +321,17 @@ check_funarg(W, ArgsList, Line, S) ->
expr(ArgsList, S1).
funarg({'fun', _, _Clauses, _Extra}, _S) -> true;
+funarg({'fun', _, {clauses, _}}, _S) ->
+ %% OTP 20. sys_pre_expand not run.
+ true;
+funarg({'fun', _, {function, _, _}}, _S) ->
+ %% OTP 20. sys_pre_expand not run.
+ true;
funarg({'fun', _, {function,_,_,_}}, _S) ->
%% New abstract format for fun M:F/A in R15.
true;
-funarg({named_fun, _, _, _, _}, _S) ->
+funarg({named_fun, _, _, _}, _S) ->
+ %% OTP 20. sys_pre_expand not run.
true;
funarg({var, _, Var}, S) -> member(Var, S#xrefr.funvars);
funarg(_, _S) -> false.
diff --git a/lib/tools/test/Makefile b/lib/tools/test/Makefile
index 84c4e56aff..2b7b17afb3 100644
--- a/lib/tools/test/Makefile
+++ b/lib/tools/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
+# Copyright Ericsson AB 1997-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -52,8 +52,8 @@ RELSYSDIR = $(RELEASE_PATH)/tools_test
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_MAKE_FLAGS +=
-ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/percept/include
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS +=
EBIN = .
diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl
index d39a5deeab..af3ce88fdd 100644
--- a/lib/tools/test/lcnt_SUITE.erl
+++ b/lib/tools/test/lcnt_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -29,7 +29,8 @@
-export([t_load/1,
t_conflicts/1,
t_locations/1,
- t_swap_keys/1]).
+ t_swap_keys/1,
+ smoke_lcnt/1]).
init_per_testcase(_Case, Config) ->
Config.
@@ -43,7 +44,8 @@ suite() ->
{timetrap,{minutes,4}}].
all() ->
- [t_load, t_conflicts, t_locations, t_swap_keys].
+ [t_load, t_conflicts, t_locations, t_swap_keys,
+ smoke_lcnt].
%%----------------------------------------------------------------------
%% Tests
@@ -146,3 +148,82 @@ t_swap_keys_file([File|Files]) ->
ok = lcnt:conflicts(),
ok = lcnt:stop(),
t_swap_keys_file(Files).
+
+%% Simple smoke test of actual lock-counting, if running on
+%% a run-time with lock-counting enabled.
+
+smoke_lcnt(Config) ->
+ case erlang:system_info(build_type) of
+ lcnt ->
+ do_smoke_lcnt(Config);
+ _ ->
+ {skip,"Lock counting is not enabled"}
+ end.
+
+do_smoke_lcnt(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ SaveFile = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)),
+ {Time,ok} = timer:tc(fun() -> lcnt:apply(fun() -> big_bang(200) end) end),
+ io:format("~p ms\n", [Time]),
+ ok = lcnt:conflicts(),
+ ok = lcnt:save(SaveFile),
+ ok = lcnt:load(SaveFile),
+ ok = lcnt:conflicts(),
+ lcnt:stop().
+
+
+%%%
+%%% A slightly modified version of Rickard Green's Big Bang benchmark.
+%%%
+
+big_bang(N) when is_integer(N) ->
+ Procs = spawn_procs(N),
+ RMsgs = lists:map(fun (P) -> {done, P} end, Procs),
+ send_procs(Procs, {procs, Procs, self()}),
+ receive_msgs(RMsgs),
+ lists:foreach(fun (P) -> exit(P, normal) end, Procs).
+
+pinger([], [], true) ->
+ receive
+ {procs, Procs, ReportTo} ->
+ pinger(Procs, [], ReportTo)
+ end;
+pinger([], [], false) ->
+ receive {ping, From} -> From ! {pong, self()} end,
+ pinger([],[],false);
+pinger([], [], ReportTo) ->
+ ReportTo ! {done, self()},
+ pinger([],[],false);
+pinger([],[Po|Pos] = Pongers, ReportTo) ->
+ receive
+ {ping, From} ->
+ From ! {pong, self()},
+ pinger([], Pongers, ReportTo);
+ {pong, Po} ->
+ pinger([], Pos, ReportTo)
+ end;
+pinger([Pi|Pis], Pongers, ReportTo) ->
+ receive {ping, From} -> From ! {pong, self()}
+ after 0 -> ok
+ end,
+ Pi ! {ping, self()},
+ pinger(Pis, [Pi|Pongers], ReportTo).
+
+spawn_procs(N) when N =< 0 ->
+ [];
+spawn_procs(N) ->
+ [spawn_link(fun () -> pinger([], [], true) end) | spawn_procs(N-1)].
+
+send_procs([], Msg) ->
+ Msg;
+send_procs([P|Ps], Msg) ->
+ P ! Msg,
+ send_procs(Ps, Msg).
+
+receive_msgs([]) ->
+ ok;
+receive_msgs([M|Ms]) ->
+ receive
+ M ->
+ receive_msgs(Ms)
+ end.
diff --git a/lib/tools/test/make_SUITE.erl b/lib/tools/test/make_SUITE.erl
index e6284db8b8..2f6fe1c732 100644
--- a/lib/tools/test/make_SUITE.erl
+++ b/lib/tools/test/make_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,11 +19,7 @@
%%
-module(make_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, make_all/1, make_files/1]).
--export([otp_6057_init/1,
- otp_6057_a/1, otp_6057_b/1, otp_6057_c/1,
- otp_6057_end/1]).
+-compile(export_all).
-include_lib("common_test/include/ct.hrl").
@@ -40,7 +36,8 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [make_all, make_files, {group, otp_6057}].
+ [make_all, make_files, recompile_on_changed_include,
+ emake_opts, {group, otp_6057}].
groups() ->
[{otp_6057,[],[otp_6057_a, otp_6057_b,
@@ -86,6 +83,55 @@ make_files(Config) when is_list(Config) ->
ensure_no_messages(),
ok.
+recompile_on_changed_include(Config) ->
+ Current = prepare_data_dir(Config),
+
+ Files = [test_incl1,"incl_src/test_incl2"],
+ up_to_date = make:files(Files),
+ ok = ensure_exists([test_incl1,test_incl2]),
+
+ {ok, FileInfo11} = file:read_file_info("test_incl1.beam"),
+ Date11 = FileInfo11#file_info.mtime,
+ {ok, FileInfo21} = file:read_file_info("test_incl2.beam"),
+ Date21 = FileInfo21#file_info.mtime,
+ timer:sleep(2000),
+
+ %% Touch the include file
+ {ok,Bin} = file:read_file("test_incl.hrl"),
+ ok = file:delete("test_incl.hrl"),
+ ok = file:write_file("test_incl.hrl",Bin),
+
+ up_to_date = make:files(Files),
+
+ {ok, FileInfo12} = file:read_file_info("test_incl1.beam"),
+ case FileInfo12#file_info.mtime of
+ Date11 -> ct:fail({"file not recompiled", "test_incl1.beam"});
+ _Date12 -> ok
+ end,
+ {ok, FileInfo22} = file:read_file_info("test_incl2.beam"),
+ case FileInfo22#file_info.mtime of
+ Date21 -> ct:fail({"file not recompiled", "test_incl2.beam"});
+ _Date22 -> ok
+ end,
+
+ file:set_cwd(Current),
+ ensure_no_messages(),
+ ok.
+
+emake_opts(Config) when is_list(Config) ->
+ Current = prepare_data_dir(Config),
+
+ %% prove that emake is used in opts instead of local Emakefile
+ Opts = [{emake, [test8, test9]}],
+ error = make:all(Opts),
+ error = make:files([test9], Opts),
+ "test8.beam" = ensure_exists([test8]),
+ "test9.beam" = ensure_exists([test9]),
+ "test5.S" = ensure_exists(["test5"],".S"),
+
+ file:set_cwd(Current),
+ ensure_no_messages(),
+ ok.
%% Moves to the data directory of this suite, clean it from any object
%% files (*.jam for a JAM emulator). Returns the previous directory.
diff --git a/lib/tools/test/make_SUITE_data/incl_src/test_incl2.erl b/lib/tools/test/make_SUITE_data/incl_src/test_incl2.erl
new file mode 100644
index 0000000000..d0db98c19a
--- /dev/null
+++ b/lib/tools/test/make_SUITE_data/incl_src/test_incl2.erl
@@ -0,0 +1,9 @@
+-module(test_incl2).
+-compile(export_all).
+-include("test_incl.hrl").
+
+f1() ->
+ ?d.
+
+f2() ->
+ true.
diff --git a/lib/tools/test/make_SUITE_data/test_incl.hrl b/lib/tools/test/make_SUITE_data/test_incl.hrl
new file mode 100644
index 0000000000..3a1bcfadd0
--- /dev/null
+++ b/lib/tools/test/make_SUITE_data/test_incl.hrl
@@ -0,0 +1 @@
+-define(d,"defined").
diff --git a/lib/tools/test/make_SUITE_data/test_incl1.erl b/lib/tools/test/make_SUITE_data/test_incl1.erl
new file mode 100644
index 0000000000..4a1dd0e73d
--- /dev/null
+++ b/lib/tools/test/make_SUITE_data/test_incl1.erl
@@ -0,0 +1,9 @@
+-module(test_incl1).
+-compile(export_all).
+-include("test_incl.hrl").
+
+f1() ->
+ ?d.
+
+f2() ->
+ true.
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index 01dbac6ecb..057449d4a2 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -89,11 +89,11 @@ init_per_suite(Conf) when is_list(Conf) ->
DataDir = ?datadir,
PrivDir = ?privdir,
CopyDir = fname(PrivDir, "datacopy"),
+ ok = file:make_dir(CopyDir),
TarFile = fname(PrivDir, "datacopy.tgz"),
- {ok, Tar} = erl_tar:open(TarFile, [write, compressed]),
- ok = erl_tar:add(Tar, DataDir, CopyDir, [compressed]),
- ok = erl_tar:close(Tar),
- ok = erl_tar:extract(TarFile, [compressed]),
+ ok = file:set_cwd(DataDir),
+ ok = erl_tar:create(TarFile, ["."], [compressed]),
+ ok = erl_tar:extract(TarFile, [compressed, {cwd,CopyDir}]),
ok = file:delete(TarFile),
[{copy_dir, CopyDir}|Conf].
@@ -1222,6 +1222,9 @@ read2(Conf) when is_list(Conf) ->
f() ->
%% Duplicated unresolved calls are ignored:
(f())(foo,bar),(f())(foo,bar). % POS1
+
+ %% Warning forms must be ignored.
+ -warning(must_not_crash).
">>,
ok = file:write_file(File, Test),
{ok, read2} = compile:file(File, [debug_info,{outdir,Dir}]),
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index f60da27c44..8aa7814e1d 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.9.1
+TOOLS_VSN = 2.10