diff options
Diffstat (limited to 'lib/tools/emacs')
-rw-r--r-- | lib/tools/emacs/erlang-eunit.el | 4 | ||||
-rw-r--r-- | lib/tools/emacs/erlang-flymake.el | 4 | ||||
-rw-r--r-- | lib/tools/emacs/erlang.el | 340 |
3 files changed, 285 insertions, 63 deletions
diff --git a/lib/tools/emacs/erlang-eunit.el b/lib/tools/emacs/erlang-eunit.el index 53543d7b01..52c5783806 100644 --- a/lib/tools/emacs/erlang-eunit.el +++ b/lib/tools/emacs/erlang-eunit.el @@ -22,7 +22,7 @@ ;;; Author: Klas Johansson (eval-when-compile - (require 'cl)) + (require 'cl-lib)) (require 'erlang) (defvar erlang-eunit-src-candidate-dirs '("../src" ".") @@ -319,7 +319,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set." ;; instead of possibly several: one for each file to compile, ;; for instance for both x.erl and x_tests.erl. (save-some-buffers erlang-eunit-autosave) - (flet ((save-some-buffers (&optional any) nil)) + (cl-letf (((symbol-function 'save-some-buffers) #'ignore)) ;; Compilation of the source file is mandatory (the file must ;; exist, otherwise the procedure is aborted). Compilation of the diff --git a/lib/tools/emacs/erlang-flymake.el b/lib/tools/emacs/erlang-flymake.el index 0b7936a81f..4e4da51e7a 100644 --- a/lib/tools/emacs/erlang-flymake.el +++ b/lib/tools/emacs/erlang-flymake.el @@ -22,7 +22,7 @@ (require 'flymake) (eval-when-compile - (require 'cl)) + (require 'cl-lib)) (defvar erlang-flymake-command "erlc" @@ -68,7 +68,7 @@ check on newline and when there are no changes)." (defun erlang-flymake-init () (let* ((temp-file - (flet ((flymake-get-temp-dir () (erlang-flymake-temp-dir))) + (cl-letf (((symbol-function 'flymake-get-temp-dir) #'erlang-flymake-temp-dir)) (flymake-init-create-temp-buffer-copy 'flymake-create-temp-with-folder-structure))) (code-dir-opts diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index 0b3a2319e2..429471a0e0 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -4,8 +4,8 @@ ;; Author: Anders Lindgren ;; Keywords: erlang, languages, processes ;; Date: 2011-12-11 -;; Version: 2.8.2 -;; Package-Requires: ((emacs "24.1")) +;; Version: 2.8.3 +;; Package-Requires: ((emacs "24.3")) ;; %CopyrightBegin% ;; @@ -87,15 +87,27 @@ "The Erlang programming language." :group 'languages) -(defconst erlang-version "2.8.2" +(defconst erlang-version "2.8.3" "The version number of Erlang mode.") (defcustom erlang-root-dir nil - "The directory where the Erlang system is installed. -The name should not contain the trailing slash. + "The directory where the Erlang man pages are installed. The +name should not contain a trailing slash. Should this variable be nil, no manual pages will show up in the -Erlang mode menu." +Erlang mode menu unless man pages have been downloaded by Erlang +mode (see below). + +You can download the Erlang man pages automatically by placing +the following lines in your Emacs init file or by executing the +Emacs command `M-x erlang-man-download-ask RET' (the download URL +can be customized with the Emacs variable +erlang-man-download-url): + + (require 'erlang) + (erlang-man-download) + +" :group 'erlang :type '(restricted-sexp :match-alternatives (stringp 'nil)) :safe (lambda (val) (or (eq nil val) (stringp val)))) @@ -1058,6 +1070,7 @@ behaviour.") (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) + (define-key map "\C-c\C-d" 'erlang-man-function-no-prompt) map) "Keymap used in Erlang mode.") (defvar erlang-mode-abbrev-table nil @@ -1880,7 +1893,8 @@ the location of the manual pages." () (setq erlang-menu-man-items '(nil - ("Man - Function" erlang-man-function))) + ("Man - Function" erlang-man-function) + ("Man - Function Under Cursor" erlang-man-function-no-prompt))) (if erlang-man-dirs (setq erlang-menu-man-items (append erlang-menu-man-items @@ -1914,8 +1928,7 @@ The format is described in the documentation of `erlang-man-dirs'." (while dir-list (setq dir (cond ((nth 2 (car dir-list)) ;; Relative to `erlang-root-dir'. - (and (stringp erlang-root-dir) - (erlang-man-dir (nth 1 (car dir-list))))) + (erlang-man-dir (nth 1 (car dir-list)) t)) (t ;; Absolute (nth 1 (car dir-list))))) @@ -1933,8 +1946,93 @@ 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)) + +(defcustom erlang-man-download-url "http://erlang.org/download/otp_doc_man_22.1.tar.gz" + "The URL from which the erlang-man-download function will + download Erlang man pages ") + +(defun erlang-man-user-local-emacs-dir () + "Returns the directory where man pages that are downloaded by +the functions erlang-man-download and erlang-man-download-ask are +stored." + (concat (file-name-as-directory (locate-user-emacs-file "cache")) + (file-name-as-directory "erlang_mode_man_pages")) + ) + +(defun erlang-man-download (&optional download-url-param) + "Downloads the Erlang man pages into the +\"cache/erlang_mode_man_pages\" subdirectory under the user's +Emacs directory, if the man pages haven't been downloaded +already. The URL from which the man pages are downloaded can be +configured with the variable \"erlang-man-download-url\"" + (interactive) + (let* ((download-url (or download-url-param erlang-man-download-url)) + (downloaded-man-dir (erlang-man-user-local-emacs-dir)) + (downloaded-man-url-file (concat + (file-name-as-directory downloaded-man-dir) + "erlang_man_download_url"))) + (if (and (file-exists-p downloaded-man-url-file) + (string= download-url (with-temp-buffer + (insert-file-contents downloaded-man-url-file) + (buffer-string)))) + downloaded-man-dir + (let ((man-file (concat (file-name-as-directory downloaded-man-dir) "man.tar.gz"))) + (message "Downloading: %s to %s" download-url man-file) + (require 'url) + (mkdir downloaded-man-dir t) + (url-copy-file download-url man-file t) + ;; Write the download URL to a file so that future calls to + ;; erlang-man-download can check if the man cache should be + ;; updated + (write-region download-url nil downloaded-man-url-file) + ;; url-copy-file unpacks the zip archive (at least on my + ;; system) but this behavior is undocumented so do a tar with + ;; the z flag as well + (message "Note that %s will only be unpacked automatically if your system has the tar tool in its path" man-file) + (shell-command (format "tar -x -z -f %s -C %s" man-file downloaded-man-dir)) + (message "The error message above can be ignored if everything works fine") + (message "Unpacking man pages using the command \"%s\"" + (format "tar -x -f %s -C %s" man-file downloaded-man-dir)) + (shell-command (format "tar -x -f %s -C %s" man-file downloaded-man-dir)) + (message "Restarting erlang-mode") + (erlang-mode) + downloaded-man-dir + ) + ) + ) + ) + +(defun erlang-man-download-ask (&optional subdir) + "Downloads the Erlang man pages into the +\"cache/erlang_mode_man_pages\" subdirectory under the user's +Emacs directory, if the man pages haven't been downloaded +already. This function ask the user to confirm before downloading +and lets the user edit the download URL. The function +erlang-man-download downloads the man pages without prompting the +user." + (interactive) + (if (y-or-n-p "Could not find Erlang man pages on your system. Do you want to download them?") + (let ((download-url (read-string "URL to download man pages from: " erlang-man-download-url))) + (concat (directory-file-name (erlang-man-download download-url)) (or subdir ""))))) + + +(defun erlang-man-dir (subdir &optional no-download) + (let ((default-man-dir (if erlang-root-dir + (concat (directory-file-name (concat + (file-name-as-directory erlang-root-dir) + (file-name-as-directory "lib") + (file-name-as-directory "erlang"))) + subdir))) + (alt-man-dir (if erlang-root-dir + (concat (directory-file-name erlang-root-dir) subdir))) + (downloaded-man-dir (erlang-man-user-local-emacs-dir))) + (if (and erlang-root-dir (file-directory-p default-man-dir)) + default-man-dir + (if (and erlang-root-dir (file-directory-p alt-man-dir)) + alt-man-dir + (if (file-directory-p (concat (directory-file-name downloaded-man-dir) subdir)) + (concat (directory-file-name downloaded-man-dir) subdir) + (and (not no-download) (erlang-man-download-ask subdir))))))) ;; Should the menu be to long, let's split it into a number of ;; smaller menus. Warning, this code contains beautiful @@ -2043,31 +2141,23 @@ This function is aware of imported functions." ;; chosen to keep it since it provides a very useful functionality ;; which is not possible to achieve using a clean approach. ;; / AndersL +;; +;; The previous hack seems to have broken in Emacs 25. This is fixed +;; by trying to find the function in the man buffer a few times with a +;; delay in between (see function +;; erlang-man-repeated-search-for-function). This fix is also a hack +;; but it should be quite robust and will work even if the function +;; that erlang-man-function-display-man-page tries to hook into +;; disappears or changes. (defvar erlang-man-function-name nil "Name of function for last `erlang-man-function' call. Used for communication between `erlang-man-function' and the patch to `Man-notify-when-ready'.") -(defun erlang-man-function (&optional name) - "Find manual page for NAME, where NAME is module:function. -The entry for `function' is displayed. - -This function is aware of imported functions." - (interactive - (list (let* ((default (erlang-default-function-or-module)) - (input (read-string - (format - "Manual entry for `module:func' or `module'%s: " - (if default - (format " (default %s)" default) - ""))))) - (if (string= input "") - default - input)))) - (require 'man) - (setq name (or name - (erlang-default-function-or-module))) +(defun erlang-man-function-display-man-page (name) + "Helper function for erlang-man-function. Displays the man page + text for the Erlang function named name if it can be found." (let ((modname nil) (funcname nil)) (cond ((string-match ":" name) @@ -2078,7 +2168,7 @@ This function is aware of imported functions." (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 + ;; From Emacs 19: The man command could possibly start an ;; asynchronous process, i.e. we must hook ourselves into ;; the system to be activated when the man-process ;; terminates. @@ -2093,9 +2183,92 @@ This function is aware of imported functions." (t (erlang-man-module modname) (when funcname - (erlang-man-find-function (current-buffer) funcname)))))) + (erlang-man-repeated-search-for-function nil + funcname + modname)))))) + +(defun erlang-man-function (&optional name) + "Find manual page for NAME, where NAME is module:function. +The entry for `function' is displayed. + +This function is aware of imported functions." + (interactive + (list (let* ((default (erlang-default-function-or-module)) + (input (read-string + (format + "Manual entry for `module:func' or `module'%s: " + (if default + (format " (default %s)" default) + ""))))) + (if (string= input "") + default + input)))) + (require 'man) + (setq name (or name + (erlang-default-function-or-module))) + (erlang-man-function-display-man-page name)) +(defun erlang-man-function-no-prompt () + "Find manual page for the function under the cursor. +The man entry for `function' is displayed. This function +provides the same functionality as erlang-man-function except for +that it does not ask the user to confirm the function name before +opening the man page for the function." + (interactive) + (let ((name (erlang-default-function-or-module))) + (if name + (erlang-man-function name) + (error "No function name under the cursor")))) + +(defun erlang-man-repeated-search-for-function (man-buffer + function-name + &optional + module-name) + "This function tries to scroll MAN-BUFFER to the documentation +of function FUNCTION-NAME. The function will try again a few +times if the documentation for FUNCTION-NAME can't be found. This +is necessary as the man page is loaded asynchronously from Emacs +19 and the correct function to hook into depends on the Emacs +version. The function will automatically try to find the correct +buffer from the list of opened buffers if MAN-BUFFER is nil. The +optional parameter MODULE-NAME will make the search for the +buffer more accurate." + (let* ((time-between-attempts 0.5) + (max-wait-time 5.1) + (search-for-function + (lambda (self + time-waited + time-between-attempts + max-wait-time + man-buffer + function-name + module-name + ) + (when (and (not (erlang-man-find-function man-buffer function-name module-name)) + (<= (+ time-waited time-between-attempts) + max-wait-time)) + (message "Finding function %s..." function-name) + ; Call this function again later + (run-at-time time-between-attempts nil + self + self + (+ time-waited time-between-attempts) + time-between-attempts + max-wait-time + man-buffer + function-name + module-name) + )))) + (funcall search-for-function + search-for-function + 0.0 + time-between-attempts + max-wait-time + man-buffer + function-name + module-name))) + ;; Should the defadvice be at the top level, the package `advice' would ;; be required. Now it is only required when this functionality ;; is used. (Emacs 19 specific.) @@ -2118,25 +2291,57 @@ command is executed asynchronously." "Set point at the documentation of the function name in `erlang-man-function-name' when the man page is displayed." (if erlang-man-function-name - (erlang-man-find-function (ad-get-arg 0) erlang-man-function-name)) - (setq erlang-man-function-name nil))) - - -(defun erlang-man-find-function (buf func) - "Find manual page for function in `erlang-man-function-name' in buffer BUF." - (if func - (let ((win (get-buffer-window buf))) - (if win - (progn - (set-buffer buf) - (goto-char (point-min)) - (if (re-search-forward - (concat "^[ \t]+" func " ?(") - (point-max) t) - (progn - (forward-word -1) - (set-window-point win (point))) - (message "Could not find function `%s'" func))))))) + (erlang-man-repeated-search-for-function (ad-get-arg 0) + erlang-man-function-name) + (setq erlang-man-function-name nil)))) + + + + +(defun erlang-man-find-function (buf func &optional module-name) + "Find manual page for function `erlang-man-function-name' in buffer BUF. +The function will automatically try to find the correct buffer among the +opened buffers if BUF is nil. The optional parameter MODULE-NAME will make +the search for the buffer more accurate." + (let ((buffer (or buf + (progn + ; find buffer containing man page + (require 'cl-lib) + (car (cl-remove-if-not (lambda (buf) + (string-match + (or module-name "") + (format "%s" buf))) + (cl-remove-if-not + (lambda (buf) + (string-match + "[Mm][Aa][Nn]" + (format "%s" buf))) + (buffer-list)))))))) + (if (and func buffer) + (let ((win (get-buffer-window buffer))) + (if win + (progn + (set-buffer buffer) + (goto-char (point-min)) + (if (re-search-forward + (concat "^[ \t]*\\([a-z0-9_]*[ \t]*:\\)?[ \t]*" func "[ \t]*([A-Za-z0-9 \t:,_()]*)[ \t]*->") + (point-max) t) + (progn + (forward-word -1) + (set-window-point win (point)) + (message "Found documentation for function `%s'" func) + t) + (if (re-search-forward + (concat "^[ \t]*\\([a-z0-9_]*[ \t]*:\\)?[ \t]*" func "[ \t]*\(") + (point-max) t) + (progn + (forward-word -1) + (set-window-point win (point)) + (message "Found documentation for function `%s'" func) + t) + (progn + (message "Could not find function `%s'" func) + nil))))))))) (defvar erlang-man-file-regexp "\\(.*\\)/man[^/]*/\\([^.]+\\)\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$") @@ -2175,8 +2380,19 @@ the site init file: For example: (setq erlang-root-dir \"/usr/local/erlang\") -After installing the line, kill and restart Emacs, or restart Erlang -mode with the command `M-x erlang-mode RET'."))) +Alternatively, you can download the Erlang man pages +automatically by placing the following lines in your Emacs init +file or by executing the Emacs command `M-x +erlang-man-download-ask RET' (the download URL can be customized +with the Emacs variable erlang-man-download-url): + + (require 'erlang) + (erlang-man-download) + +After installing the line/lines in your Emacs init file or after +running the command `M-x erlang-man-download-ask RET', kill and +restart Emacs, or restart Erlang mode with the command `M-x +erlang-mode RET'."))) ;; Skeleton code: @@ -2679,8 +2895,9 @@ Value is list (stack token-start token-type in-what)." (if stack (forward-char 1) (forward-char 6) - (skip-chars-forward "^(\n") (erlang-push (list 'spec (point) (current-column)) stack) + (skip-chars-forward "^(\n") + (erlang-push (list 'spec_arg (point) (current-column)) stack) )) ;; Type spec delimiter @@ -2797,7 +3014,7 @@ Return nil if inside string, t if in a comment." (t (erlang-indent-to-first-element stack-top 2)))) - ((memq (car stack-top) '(icr fun spec)) + ((memq (car stack-top) '(icr fun spec_arg)) ;; The default indentation is the column of the option ;; directly following the keyword. (This does not apply to ;; `case'.) Should no option be on the same line, the @@ -2844,10 +3061,11 @@ 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 too bad - (setq off (+ erlang-indent-level (if (not erlang-icr-indent) - erlang-indent-level - erlang-icr-indent))))) + ;; Removed it made multi clause Named fun's look too bad + (setq off (+ erlang-indent-level + (if (not erlang-icr-indent) + erlang-indent-level + erlang-icr-indent))))) (let ((base (erlang-indent-find-base stack indent-point off skip))) ;; Special cases (goto-char indent-point) @@ -2873,6 +3091,10 @@ Return nil if inside string, t if in a comment." (erlang-caddr (car stack)) 0))) (t (erlang-indent-standard indent-point token base 'nil))))) ;; old catch + ;; Indent result types + ((eq (car (car (cdr stack))) 'spec_arg) + (setq base (+ (erlang-caddr (car (last stack))) erlang-indent-level)) + (erlang-indent-standard indent-point token base 'nil)) (t (erlang-indent-standard indent-point token base 'nil) )))) |