diff --git a/emacs.d/elpa/ac-js2-20140906.442/ac-js2-autoloads.el b/emacs.d/elpa/ac-js2-20140906.442/ac-js2-autoloads.el new file mode 100644 index 0000000..03058bd --- /dev/null +++ b/emacs.d/elpa/ac-js2-20140906.442/ac-js2-autoloads.el @@ -0,0 +1,50 @@ +;;; ac-js2-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "ac-js2" "ac-js2.el" (21824 55291 0 0)) +;;; Generated autoloads from ac-js2.el + +(autoload 'ac-js2-expand-function "ac-js2" "\ +Expand the function definition left of point. +Expansion will only occur for candidates whose documentation +string contain a function prototype. + +\(fn)" t nil) + +(autoload 'ac-js2-completion-function "ac-js2" "\ +Function for `completions-at-point'. + +\(fn)" nil nil) + +(autoload 'ac-js2-company "ac-js2" "\ + + +\(fn COMMAND &optional ARG &rest IGNORED)" t nil) + +(autoload 'ac-js2-jump-to-definition "ac-js2" "\ +Jump to the definition of an object's property, variable or function. +Navigation to a property definend in an Object literal isn't +implemented. + +\(fn)" t nil) + +(autoload 'ac-js2-mode "ac-js2" "\ +A minor mode that provides auto-completion and navigation for Js2-mode. + +\(fn &optional ARG)" t nil) + +;;;*** + +;;;### (autoloads nil nil ("ac-js2-pkg.el" "ac-js2-tests.el") (21824 +;;;;;; 55291 874082 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; ac-js2-autoloads.el ends here diff --git a/emacs.d/elpa/ac-js2-20140906.442/ac-js2-pkg.el b/emacs.d/elpa/ac-js2-20140906.442/ac-js2-pkg.el new file mode 100644 index 0000000..0a7cacd --- /dev/null +++ b/emacs.d/elpa/ac-js2-20140906.442/ac-js2-pkg.el @@ -0,0 +1,7 @@ +(define-package "ac-js2" "20140906.442" "Auto-complete source for Js2-mode, with navigation" + '((js2-mode "20090723") + (skewer-mode "1.4")) + :url "https://github.com/ScottyB/ac-js2") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs.d/elpa/ac-js2-20140906.442/ac-js2-tests.el b/emacs.d/elpa/ac-js2-20140906.442/ac-js2-tests.el new file mode 100644 index 0000000..05514e3 --- /dev/null +++ b/emacs.d/elpa/ac-js2-20140906.442/ac-js2-tests.el @@ -0,0 +1,76 @@ +;;; Tests for ac-js2 + +(require 'ert) +(require 'skewer-mode) +(require 'js2-mode) +(require 'ac-js2) + +;;; Must have a skewer client connected before running the tests +;; Need to call httpd-stop from main Emacs if running tests in batch mode +(unless skewer-clients + (run-skewer)) + +(ert-deftest ac-js2-candidates-test () + "Test the major function that returns candidates for all frontends." + (let (property + property-dot + func-call + var) + (with-temp-buffer + (insert " + var temp = function(param1, param2) { + var localParam = 15; + return param1 + param2; + }; + + var look; + +temp.aFun = function(lolParam) {}; +temp.anotherFunction = function() { return {about: 3};}") + (setq ac-js2-evaluate-calls t) + (setq ac-js2-external-libraries nil) + + (js2-mode) + (ac-js2-mode t) + (js2-parse) + + (insert "tem") + (ac-js2-candidates) + (setq var ac-js2-skewer-candidates) + (delete-char -3) + + (insert "temp.") + (js2-parse) + (ac-js2-candidates) + (setq property-dot ac-js2-skewer-candidates) + (delete-char -5) + + (insert "temp.aF") + (js2-parse) + (ac-js2-candidates) + (setq property ac-js2-skewer-candidates)) + + (should (assoc 'anotherFunction property-dot)) + (print property) + (should (assoc 'aFun property)) + (should (assoc 'temp var)))) + +(defmacro completion-frontend-test (test-name completion-function) + "Utility for testing completion front ends. +TODO: cover more cases" + `(ert-deftest ,test-name () + (let (var) + (with-temp-buffer + (insert "var testComplete = function(param1, param2) {};") + + (js2-mode) + (ac-js2-mode t) + (js2-parse) + + (insert "testComplet") + (funcall ',completion-function) + (setq var (thing-at-point 'word))) + (should (string= var "testComplete"))))) + +(completion-frontend-test auto-complete-test auto-complete) +(completion-frontend-test completion-at-point-test completion-at-point) diff --git a/emacs.d/elpa/ac-js2-20140906.442/ac-js2.el b/emacs.d/elpa/ac-js2-20140906.442/ac-js2.el new file mode 100644 index 0000000..1b010e2 --- /dev/null +++ b/emacs.d/elpa/ac-js2-20140906.442/ac-js2.el @@ -0,0 +1,605 @@ +;;; ac-js2.el --- Auto-complete source for Js2-mode, with navigation + +;; Copyright (C) 2013 Scott Barnett + +;; Author: Scott Barnett +;; URL: https://github.com/ScottyB/ac-js2 +;; Version: 1.0 +;; Package-Requires: ((js2-mode "20090723")(skewer-mode "1.4")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; An attempt to get context sensitive Javascript completion in Emacs. +;; Basic completions are obtained by parsing Javascript code with +;; Js2-mode's parser. +;; +;; Installation +;; +;; Easiest way to get ac-js2 is to install it from MELPA. You may need +;; this snippet +;; +;; `(add-to-list 'package-archives +;; '("melpa" . "http://melpa.milkbox.net/packages/") t)' +;; +;; if you don't have it already to fetch packages from MELPA. +;; +;; Enable ac-js2 in js2-mode as follows: +;; +;; (add-hook 'js2-mode-hook 'ac-js2-mode) +;; +;; Ac-js2 does not require auto-complete mode but I suggest you grab +;; it anyway as ac-js2 is designed to work with a completion frontend. +;; Support for Company mode is on its way. +;; +;; For more comprehensive completions you can opt to evaluate the code +;; for candidates. A browser needs to be connected to Emacs for the +;; evaluation completions to work. Put this in your init.el file. +;; +;; `(setq ac-js2-evaluate-calls t)' +;; +;; To add completions for external libraries add something like this: +;; +;; (add-to-list 'ac-js2-external-libraries "path/to/lib/library.js") +;; +;; Then connect a browser to Emacs by calling `(run-skewer)'. You may +;; need to save the buffer for completions to start. +;; +;; If auto-complete mode is installed on your system then completions +;; should start showing up otherwise use `completion-at-point'. +;; +;; Note: library completions will only work if `ac-js2-evaluate-calls' +;; is set and a browser is connected to Emacs. +;; +;; Bonus: M-. is bound to `ac-js2-jump-to-definition' which will jump +;; to Javascript definitions found in the same buffer. Given the +;; following proprety reference: +;; +;; foo.bar.baz(); +;; +;; placing the cursor on `foo', `bar' or `baz' and executing M-. will +;; take you straight to their respective definitions. Use M-, to jump +;; back to where you were. Also works for object literals. +;; +;; Recently added `ac-js2-expand-function' that will expand a function's +;; parameters bound to `C-c C-c`. Expansion will only work if the cursor +;; is after the function. +;; +;; If you have any issues or suggestions please create an issue on Github: +;; https://github.com/ScottyB/ac-js2 + +;;; History: + +;; Version 1.0 +;; * Navigation within current buffer +;; * Completion and docstring for objects via Skewer +;; * External library support +;; * Basic completions of objects in current buffer + +;;; Code: + +(require 'js2-mode) +(require 'skewer-mode) +(require 'cl-lib) +(require 'etags) + +(defgroup ac-js2 nil + "Auto-completion for js2-mode." + :group 'completion + :prefix "ac-js2-") + +;;; Configuration variables + +(defcustom ac-js2-add-ecma-262-externs t + "If non-nil add `js2-ecma-262-externs' to completion candidates.") + +(defcustom ac-js2-add-browser-externs t + "If non-nil add `js2-browser-externs' to completion candidates.") + +(defcustom ac-js2-add-keywords t + "If non-nil add `js2-keywords' to completion candidates.") + +(defcustom ac-js2-add-prototype-completions t + "When non-nil traverse the prototype chain adding to completion candidates.") + +(defcustom ac-js2-external-libraries '() + "List of absolute paths to external Javascript libraries.") + +(defcustom ac-js2-evaluate-calls nil + "Warning. When true function calls will be evaluated in the browser. +This may cause undesired side effects however it will + provide better completions. Use at your own risk.") + +(defcustom ac-js2-force-reparse t + "Force Js2-mode to reparse buffer before fetching completion candidates.") + +;;; Internal variables + +(defvar ac-js2-keywords '() + "Cached string version of `js2-keywords'.") + +(defvar ac-js2-candidates '()) + +;; Types of skewer completion methods available +(defconst ac-js2-method-eval 0) +(defconst ac-js2-method-global 1 + "Return candidates for the global object. +Only keys of the object are returned as the other properties come + from js2-mode's externs.") + +(defvar ac-js2-data-root (file-name-directory load-file-name) + "Location of data files needed for `ac-js2-on-skewer-load'.") + +;;; Skewer integration + +(defvar ac-js2-skewer-candidates '() + "Cadidates obtained from skewering.") + +(defun ac-js2-on-skewer-load () + "Inject skewer addon and evaluate external libraries in browser." + (insert-file-contents (expand-file-name "skewer-addon.js" ac-js2-data-root)) + (and ac-js2-evaluate-calls + (mapcar (lambda (library) + (with-temp-buffer + (insert-file-contents (expand-file-name library)) + (skewer-eval (buffer-string) + nil + :type "complete"))) ac-js2-external-libraries))) + +(defun ac-js2-skewer-completion-candidates () + "Get completions returned from skewer." + (mapcar (lambda (candidate) (symbol-name (car candidate))) ac-js2-skewer-candidates)) + +(defun ac-js2-skewer-document-candidates (name) + "Return document string for NAME from skewer." + (let ((doc (cdr (assoc-string name ac-js2-skewer-candidates)))) + (or (ac-js2-format-function doc) doc))) + +(defun ac-js2-get-object-properties (name) + "Find properties of NAME for completion." + (ac-js2-skewer-eval-wrapper name `((prototypes . ,ac-js2-add-prototype-completions)))) + +(defun ac-js2-skewer-result-callback (result) + "Process the RESULT passed from the browser." + (let ((value (cdr (assoc 'value result)))) + (if (and (skewer-success-p result) value) + (setq ac-js2-skewer-candidates (append value nil))))) + +(defun ac-js2-skewer-eval-wrapper (str &optional extras) + "Wrap `skewer-eval-synchronously' to check if a skewer-client is avilable. +STR is the text to send to the browser for evaluation. Extra +parameters can be passed to the browser using EXTRAS. EXTRAS must +be of the form (param-string . value) where param-string is the +reference and value is the value that can be retrieved from the +request object in Javacript." + (setq ac-js2-skewer-candidates nil) + (if skewer-clients + (if (or ac-js2-evaluate-calls + (not (ac-js2-has-function-calls str))) + (ac-js2-skewer-result-callback + (skewer-eval-synchronously str + :type "complete" + :extra extras))) + (setq skewer-queue nil))) + +;; Generate candidates +(defun ac-js2-candidates () + "Main function called to gather candidates for auto-completion." + (if ac-js2-force-reparse (js2-reparse)) + (let ((node (js2-node-parent (js2-node-at-point (1- (point))))) + beg + (prop-get-regex "[a-zA-Z)]\\.") + name) + (setq ac-js2-candidates nil) + (cond + ((looking-back "\\.") + ;; TODO: Need to come up with a better way to extract object than this regex!! + (save-excursion + (setq beg (and (skip-chars-backward "[a-zA-Z_$][0-9a-zA-Z_$#\"())]+\\.") (point)))) + (setq name (buffer-substring-no-properties beg (1- (point)))) + (ac-js2-get-object-properties name) + (setq node (ac-js2-initialized-node (if (string-match prop-get-regex name) + (reverse (split-string name prop-get-regex)) name))) + (if (js2-object-node-p node) + (setq ac-js2-candidates + (mapcar (lambda (elem) + (ac-js2-format-node (js2-node-string (js2-object-prop-node-left elem)) + elem)) + (js2-object-node-elems node)))) + (append (mapcar 'first ac-js2-candidates) + (ac-js2-skewer-completion-candidates))) + ((js2-prop-get-node-p node) + (setq node (js2-prop-get-node-left node)) + (setq name (js2-node-string node)) + (ac-js2-get-object-properties name) + (ac-js2-skewer-completion-candidates)) + (t + (ac-js2-skewer-eval-wrapper "" `((method . ,ac-js2-method-global))) + (append (ac-js2-skewer-completion-candidates) + (ac-js2-add-extra-completions + (mapcar 'first (ac-js2-get-names-in-scope)))))))) + +(defun ac-js2-document (name) + "Show documentation for NAME from local buffer if present +otherwise use documentation obtained from skewer." + (let* ((docs (cdr (assoc name ac-js2-candidates))) + (doc (if (listp docs) (first docs) docs))) + (if doc doc (ac-js2-skewer-document-candidates name)))) + +;; Auto-complete settings + +(defun ac-js2-ac-candidates () + "Completion candidates for auto-complete mode." + (ac-js2-candidates)) + +(defun ac-js2-ac-document (name) + "Documentation to be shown for auto-complete mode." + (ac-js2-document name)) + +(defun ac-js2-ac-prefix() + (or (ac-prefix-default) (ac-prefix-c-dot))) + +(defun ac-js2-save () + "Called on `before-save-hook' to evaluate buffer." + (interactive) + (when (string= major-mode "js2-mode") + (ac-js2-skewer-eval-wrapper (buffer-string))) + t) + +;;;###autoload +(defun ac-js2-expand-function() + "Expand the function definition left of point. +Expansion will only occur for candidates whose documentation +string contain a function prototype." + (interactive) + (let* ((word (progn + (if (featurep 'auto-complete) (ac-complete)) + (substring-no-properties (or (thing-at-point 'word) "")))) + (candidate (ac-js2-ac-document word))) + (if (and (looking-back word) (stringp candidate)) + (when (string-match "^function" candidate) + (cond ((featurep 'yasnippet) + (yas-expand-snippet + (concat "(" + (replace-regexp-in-string "\\([a-zA-Z0-9]+\\)" + (lambda (txt) (concat "${" txt "}")) + (second (split-string candidate "[()]"))) + ")$0")))))))) + +(defun ac-js2-setup-auto-complete-mode () + "Setup ac-js2 to be used with auto-complete-mode." + (add-to-list 'ac-sources 'ac-source-js2) + (auto-complete-mode) + (ac-define-source "js2" + '((candidates . ac-js2-ac-candidates) + (document . ac-js2-ac-document) + (prefix . ac-js2-ac-prefix) + (requires . -1)))) + +;;; Completion at point function + +;;;###autoload +(defun ac-js2-completion-function () + "Function for `completions-at-point'." + (save-excursion + (let ((bounds (if (looking-back "\\.") + (cons (point) (point)) + (bounds-of-thing-at-point 'word)))) + (list (car bounds) (cdr bounds) (ac-js2-candidates))))) + +;;; Company + +;;;###autoload +(defun ac-js2-company (command &optional arg &rest ignored) + (interactive (list 'interactive)) + (if (not (featurep 'company)) + (message "Company is not installed") + (case command + (interactive (company-begin-backend 'ac-js2-company)) + (prefix (when ac-js2-mode + (or (company-grab-symbol) + 'stop))) + (candidates (all-completions arg (ac-js2-candidates))) + (duplicates t) + (meta (let ((doc (ac-js2-document arg))) + (when doc + (with-temp-buffer + (insert doc) + (js-mode) + (font-lock-ensure) + (buffer-string)))))))) + +;;; Helper functions + +(defun ac-js2-build-prop-name-list (prop-node) + "Build a list of names from a PROP-NODE." + (let* (names + left + left-node) + (unless (js2-prop-get-node-p prop-node) + (error "Node is not a property prop-node")) + (while (js2-prop-get-node-p prop-node) + (push (js2-name-node-name (js2-prop-get-node-right prop-node)) names) + (setq left-node (js2-prop-get-node-left prop-node)) + (when (js2-name-node-p left-node) + (setq left (js2-name-node-name left-node))) + (setq prop-node (js2-node-parent prop-node))) + (append names `(,left)))) + +(defun ac-js2-prop-names-left (name-node) + "Create a list of all of the names in the property NAME-NODE. +NAME-NODE must have a js2-prop-get-node as parent. Only adds +properties to the left of point. This is so individual jump +points can be found for each property in the chain." + (let* (name + (parent (js2-node-parent name-node)) + left + names) + (unless (or (js2-prop-get-node-p parent) (js2-name-node-p name-node)) + (error "Not a name node or doesn't have a prop-get-node as parent")) + (setq name (js2-name-node-name name-node) + left (js2-prop-get-node-left parent)) + (if (and (js2-name-node-p left) + (string= name (js2-name-node-name left))) + (setq names name) + (js2-visit-ast + parent + (lambda (node endp) + (unless endp + (if (js2-name-node-p node) + (push (js2-name-node-name node) names) + t)))) + names))) + +(defun ac-js2-has-function-calls (string) + "Check if the Javascript code in STRING has a Js2-call-node." + (with-temp-buffer + (insert string) + (let* ((ast (js2-parse))) + (catch 'call-node + (js2-visit-ast-root + ast + (lambda (node end-p) + (unless end-p + (if (js2-call-node-p node) + (throw 'call-node t) + t)))))))) + +(defun ac-js2-add-extra-completions (completions) + "Add extra candidates to COMPLETIONS." + (append completions + (if ac-js2-add-keywords (or ac-js2-keywords (setq ac-js2-keywords (mapcar 'symbol-name js2-keywords)))) + (if ac-js2-add-ecma-262-externs js2-ecma-262-externs) + (if ac-js2-add-browser-externs js2-browser-externs))) + +(defun ac-js2-root-or-node () + "Return the current node or js2-ast-root node." + (let ((node (js2-node-at-point))) + (if (js2-ast-root-p node) + node + (js2-node-get-enclosing-scope node)))) + +(defun ac-js2-get-names-in-scope () + "Fetches all symbols in scope and formats them for completion." + (let* ((scope (ac-js2-root-or-node)) + result) + (while scope + (setq result (append result + (loop for item in (js2-scope-symbol-table scope) + if (not (assoc (car item) result)) + collect item))) + (setq scope (js2-scope-parent-scope scope))) + (setq ac-js2-candidates + (mapcar #'(lambda (x) + (let* ((name (symbol-name (car x))) + (init (ac-js2-initialized-node name))) + (ac-js2-format-node name init))) + result)))) + +(defun ac-js2-initialized-node (name) + "Return initial value assigned to NAME. +NAME may be either a variable, a function or a variable that +holds a function. NAME may also be a list of names that make up a +object property. Returns nil if no initial value can be found." + (let* ((node (if (listp name) (ac-js2-find-property name) + (ac-js2-name-declaration name))) + (parent (if node (js2-node-parent node))) + (init (cond + ((js2-function-node-p parent) + parent) + ((js2-function-node-p node) + node) + ((js2-var-init-node-p parent) + (js2-var-init-node-initializer parent)) + ((js2-assign-node-p parent) + (js2-assign-node-right parent)) + (t + nil)))) + init)) + +(defun ac-js2-name-declaration (name) + "Return the declaration node for node named NAME." + (let* ((node (ac-js2-root-or-node)) + (scope-def (js2-get-defining-scope node name)) + (scope (if scope-def (js2-scope-get-symbol scope-def name) nil)) + (symbol (if scope (js2-symbol-ast-node scope) nil))) + (if (not symbol) + (ac-js2-get-function-node name scope-def) + symbol))) + +;;; Completion candidate formatting + +(defun ac-js2-format-node (name node) + "Format NAME and NODE for completion. +Returned format is a list where the first element is the NAME of +the node (shown in completion candidate list) and the last +element is the text to show as documentation." + (let ((node (if (js2-object-prop-node-p node) (js2-object-prop-node-right node) node)) + (name-format (replace-regexp-in-string "\"" "" name)) + (doc (if (and (js2-function-node-p node) + (cl-find name (js2-function-node-params node) + :test '(lambda (name param) (string= name (js2-name-node-name param))))) + "Function parameter" + (ac-js2-format-node-doc node)))) + `(,name-format . ,doc))) + +(defun ac-js2-format-object-node-doc (obj-node) + "Format OBJ-NODE to display as documentation." + (let (elems) + (unless (js2-object-node-p obj-node) + (error "Node is not an object node")) + (setq elems (js2-object-node-elems obj-node)) + (if (not elems) + "{}" + (mapconcat #'(lambda (x) (ac-js2-format-js2-object-prop-doc x)) elems "\n")))) + +(defun ac-js2-format-node-doc (node) + "Format NODE for displaying in a document string." + (let* ((node-above (and node (js2-node-at-point + (save-excursion + (goto-char (js2-node-abs-pos node)) + (forward-line -1) + (point))))) + (comment (if (js2-comment-node-p node-above) + (ac-js2-format-comment (js2-node-string node-above)))) + (doc (cond + ((js2-function-node-p node) + (ac-js2-format-function node)) + ((js2-object-node-p node) + (ac-js2-format-object-node-doc node)) + ((js2-object-prop-node-p node) + (ac-js2-format-node-doc (js2-object-prop-node-right node))) + (t + (if (js2-node-p node) (js2-node-string node) ""))))) + (if comment (concat comment "\n" doc) doc))) + +(defun ac-js2-format-js2-object-prop-doc (obj-prop) + "Format an OBJ-PROP for displaying as a document string." + (unless (js2-object-prop-node-p obj-prop) + (error "Node is not an object property node")) + (let* ((left (js2-object-prop-node-left obj-prop)) + (right (js2-object-prop-node-right obj-prop))) + (concat (js2-node-string left) " : " + (ac-js2-format-node-doc right)))) + +(defun ac-js2-format-function (func) + "Formats a function for a document string. +FUNC can be either a function node or a string starting with +'function'. Returns nil if neither." + (let ((str (or (and (js2-function-node-p func) (js2-node-string func)) + (and (stringp func) (eq 0 (string-match "function" func)) func)))) + (if str (substring str 0 (1+ (string-match ")" str)))))) + +(defun ac-js2-format-comment (comment) + "Prepare a COMMENT node for displaying in a popup." + (let* ((node-string (if (js2-comment-node-p comment) + (js2-node-string comment) + comment)) + (string (replace-regexp-in-string "[ \t]$" "" + (replace-regexp-in-string "^[ \t\n*/*]+" "" node-string)))) + string)) + +;;; Navigation commands for js2-mode + +(defun ac-js2-find-property (list-names) + "Find the property definition that consists of LIST-NAMES. +Supports navigation to 'foo.bar = 3' and 'foo = {bar: 3}'." + (catch 'prop-found + (js2-visit-ast-root + js2-mode-ast + (lambda (node endp) + (let ((parent (js2-node-parent node))) + (unless endp + (if (or (and (js2-prop-get-node-p node) + (not (or (js2-elem-get-node-p parent) (js2-call-node-p parent))) + (equal list-names (ac-js2-build-prop-name-list node))) + (and (js2-name-node-p node) + (js2-object-prop-node-p parent) + (string= (js2-name-node-name node) + (first list-names)))) + (throw 'prop-found node)) + t)))))) + +(defun ac-js2-get-function-node (name scope) + "Return node of function named NAME in SCOPE." + (catch 'function-found + (js2-visit-ast + scope + (lambda (node end-p) + (when (and (not end-p) + (string= name (ac-js2-get-function-name node))) + (throw 'function-found node)) + t)) + nil)) + +;;;###autoload +(defun ac-js2-jump-to-definition () + "Jump to the definition of an object's property, variable or function. +Navigation to a property definend in an Object literal isn't +implemented." + (interactive) + (ring-insert find-tag-marker-ring (point-marker)) + (let* ((node (js2-node-at-point)) + (parent (js2-node-parent node)) + (prop-names (if (js2-prop-get-node-p parent) + (ac-js2-prop-names-left node))) + (name (if (and (js2-name-node-p node) + (not (js2-object-prop-node-p parent))) + (js2-name-node-name node) + (error "Node is not a supported jump node"))) + (node-init (if (and prop-names (listp prop-names)) + (ac-js2-find-property prop-names) + (ac-js2-name-declaration name)))) + (unless node-init + (pop-tag-mark) + (error "No jump location found")) + (goto-char (js2-node-abs-pos node-init)))) + +(defun ac-js2-get-function-name (fn-node) + "Return the name of the function FN-NODE. +Value may be either function name or the variable name that holds +the function." + (let ((parent (js2-node-parent fn-node))) + (if (js2-function-node-p fn-node) + (or (js2-function-name fn-node) + (if (js2-var-init-node-p parent) + (js2-name-node-name (js2-var-init-node-target parent))))))) + +(defvar ac-js2-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "M-.") 'ac-js2-jump-to-definition) + (define-key map (kbd "M-,") 'pop-tag-mark) + (define-key map (kbd "C-c C-c") 'ac-js2-expand-function) + map) + "Keymap for `ac-js2-mode'.") + +;;; Minor mode + +;;;###autoload +(define-minor-mode ac-js2-mode + "A minor mode that provides auto-completion and navigation for Js2-mode." + :keymap ac-js2-mode-map + (if (featurep 'auto-complete) + (ac-js2-setup-auto-complete-mode)) + (set (make-local-variable 'completion-at-point-functions) + (cons 'ac-js2-completion-function completion-at-point-functions)) + (ac-js2-skewer-eval-wrapper (buffer-string)) + (add-hook 'before-save-hook 'ac-js2-save nil t) + (add-hook 'skewer-js-hook 'ac-js2-on-skewer-load)) + + +(provide 'ac-js2) + +;;; ac-js2.el ends here diff --git a/emacs.d/elpa/ac-js2-20140906.442/skewer-addon.js b/emacs.d/elpa/ac-js2-20140906.442/skewer-addon.js new file mode 100644 index 0000000..8e2b5a1 --- /dev/null +++ b/emacs.d/elpa/ac-js2-20140906.442/skewer-addon.js @@ -0,0 +1,116 @@ +/** + * @fileOverview Completion request handler for skewer.js + * @requires skewer + * @version 1.0 + */ + +/** + * Handles a completion request from Emacs. + * @param request The request object sent by Emacs + * @returns The completions and init values to be returned to Emacs + */ +skewer.fn.complete = function(request) { + var result = { + type : request.type, + id : request.id, + strict : request.strict, + status : "success" + }, + + /** + * Methods for generating candidates + */ + METHOD = { + EVAL : 0, + GLOBAL : 1 + }, + + /** + * Add the properties from object to extendObject. Properties + * may be from the prototype but we still want to add them. + */ + extend = function(extendObject, object) { + for(var key in object) { + extendObject[key] = object[key]; + } + }, + + globalCompletion = function() { + var global = Function('return this')(), + keys = Object.keys(global); + candidates = buildCandidates(global, keys); + }, + + evalCompletion = function(evalObject) { + var obj = (eval, eval)(evalObject); + if (typeof obj === "object") { + candidates = buildCandidates(obj) || {}; + while (request.prototypes && (obj = Object.getPrototypeOf(obj)) !== null) { + extend(candidates, buildCandidates(obj)); + } + } else if (typeof obj === "function"){ + candidates = buildCandidates(obj) || {}; + extend(candidates, buildCandidates(Object.getPrototypeOf(obj))); + if (request.prototypes) { + var protoObject = Object.getPrototypeOf(obj.prototype); + if (protoObject !== null) { + extend(candidates, buildCandidates(protoObject)); + } else { + extend(candidates, buildCandidates(obj.prototype)); + } + } + } + }, + + /** + * Completion candidates sent back to Emacs. Keys are + * completion candidates the values are the inital items or + * function interfaces. + */ + candidates = {}, + + /** + * Build the candiates to return to Emacs. + * @param obj The object to get candidates from + * @param items The selected keys from obj to create candidates for + * @return object containing completion candidates and documentation strings + */ + buildCandidates = function(obj, items) { + var keys = items || Object.getOwnPropertyNames(obj), values = {}; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (key === "callee" || key === "caller" || key === "arguments") continue; + if (Object.prototype.toString.call(obj[key]) === "[object Function]") { + values[key] = obj[key].toString(); + } else if (typeof obj[key] === "object"){ + values[key] = "[object Object]"; + } else if (typeof obj[key] === "number") { + if (!(obj instanceof Array)) { + values[key] = obj[key].toString(); + } + } else if (typeof obj[key] === "string") { + values[key] = obj[key].toString(); + } else if(obj[key] === true) { + values[key] = "true"; + } else if (obj[key] === false) { + values[key] = "false"; + } else { + values[key] = ""; + } + } + return values; + }; + try { + switch (request.method) { + case METHOD.GLOBAL: + globalCompletion(); + break; + default: + evalCompletion(request.eval); + } + result.value = candidates; + } catch (error){ + skewer.errorResult(error, result, request); + } + return result; +}; diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-autoloads.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-autoloads.el new file mode 100644 index 0000000..b68599b --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-autoloads.el @@ -0,0 +1,59 @@ +;;; alchemist-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "alchemist" "alchemist.el" (21721 25170 0 0)) +;;; Generated autoloads from alchemist.el + +(autoload 'alchemist-version "alchemist" "\ +Display Alchemist's version. + +\(fn &optional SHOW-VERSION)" t nil) + +(autoload 'alchemist-mode "alchemist" "\ +Toggle alchemist mode. + +Key bindings: +\\{alchemist-mode-map} + +\(fn &optional ARG)" t nil) + +;;;*** + +;;;### (autoloads nil "alchemist-iex" "alchemist-iex.el" (21721 25170 +;;;;;; 0 0)) +;;; Generated autoloads from alchemist-iex.el + +(defalias 'run-elixir 'alchemist-iex-run) + +(autoload 'alchemist-iex-run "alchemist-iex" "\ +Start an IEx process. +Show the IEx buffer if an IEx process is already run. + +\(fn &optional ARG)" t nil) + +(autoload 'alchemist-iex-project-run "alchemist-iex" "\ +Start an IEx process with mix 'iex -S mix' in the +context of an Elixir project. +Show the IEx buffer if an IEx process is already run. + +\(fn)" t nil) + +;;;*** + +;;;### (autoloads nil nil ("alchemist-buffer.el" "alchemist-company.el" +;;;;;; "alchemist-compile.el" "alchemist-complete.el" "alchemist-eval.el" +;;;;;; "alchemist-execute.el" "alchemist-goto.el" "alchemist-help.el" +;;;;;; "alchemist-hooks.el" "alchemist-message.el" "alchemist-mix.el" +;;;;;; "alchemist-pkg.el" "alchemist-project.el" "alchemist-utils.el") +;;;;;; (21721 25170 129388 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; alchemist-autoloads.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-buffer.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-buffer.el new file mode 100644 index 0000000..8460b56 --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-buffer.el @@ -0,0 +1,146 @@ +;;; alchemist-buffer.el --- Custom compilation mode for Alchemist + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; Custom compilation mode for Alchemist + +;;; Code: + +(require 'compile) +(require 'ansi-color) + +;; Variables + +(defgroup alchemist-buffer nil + "Custom compilation mode for Alchemist." + :prefix "alchemist-buffer-" + :group 'alchemist) + +(defcustom alchemist-buffer-status-modeline t + "if t, the face of local `mode-name' variable will change with compilation status. + +For example, when `alchemist-mix-test' failes, the `mode-name' will be +formated with the `alchemist-buffer--failed-face' face, to symbolize failing tests." + :type 'boolean + :group 'alchemist-buffer) + +(defvar alchemist-buffer--mode-name-face 'mode-line) + +(defvar alchemist-buffer--buffer-name nil + "Used to store compilation name so recompilation works as expected.") +(make-variable-buffer-local 'alchemist-buffer--buffer-name) + +(defvar alchemist-buffer--error-link-options + '(elixir "^\\([-A-Za-z0-9./_]+\\):\\([0-9]+\\)\\(: warning\\)?$" 1 2 nil (3) 1) + "File link matcher for `compilation-error-regexp-alist-alist' (matches path/to/file:line).") + +;; Faces + +(defface alchemist-buffer--success-face + '((t (:inherit font-lock-variable-name-face :bold t :background "darkgreen" :foreground "#e0ff00"))) + "Face for successful compilation run." + :group 'alchemist-buffer) + +(defface alchemist-buffer--failed-face + '((t (:inherit font-lock-variable-name-face :bold t :background "red" :foreground "white"))) + "Face for failed compilation run." + :group 'alchemist-buffer) + +(defface alchemist-buffer--running-face + '((t (:inherit font-lock-variable-name-face :bold nil :background "gray" :foreground "black"))) + "Face for running compilation." + :group 'alchemist-buffer) + +(defun alchemist-buffer--kill-any-orphan-proc () + "Ensure any dangling buffer process is killed." + (let ((orphan-proc (get-buffer-process (buffer-name)))) + (when orphan-proc + (kill-process orphan-proc)))) + +;; Private functions + +(defvar alchemist-buffer--save-buffers-predicate + (lambda () + (not (string= (substring (buffer-name) 0 1) "*")))) + +(defun alchemist-buffer--remove-dispensable-output () + (delete-matching-lines "\\(-*- mode:\\|Compiled \\|elixir-compilation;\\|Elixir started\\|^$\\)" (point-min) (point-max)) + (remove-hook 'compilation-filter-hook 'alchemist-buffer--remove-dispensable-output t)) + +(defun alchemist-buffer--remove-dispensable-output-after-finish (buffer msg) + (delete-matching-lines "\\(Excluding tags\\|Including tags\\|Elixir exited\\|Elixir finished\\)" (point-min) (point-max))) + +(defun alchemist-buffer--handle-compilation () + (ansi-color-apply-on-region (point-min) (point-max))) + +(defun alchemist-buffer--set-modeline-color (buffer status) + (setq alchemist-buffer--mode-name-face + (if (string-prefix-p "finished" status) + 'alchemist-buffer--success-face + 'alchemist-buffer--failed-face)) + + (remove-hook 'compilation-finish-functions 'alchemist-buffer--set-modeline-color)) + +;; Public functions + +(defun alchemist-buffer-initialize-modeline () + "Initialize the mode-line face." + (setq mode-name + '(:eval (propertize "Elixir" 'face alchemist-buffer--mode-name-face)))) + +(defun alchemist-buffer-reset-modeline () + "Reset the current mode-line face to default." + (setq mode-name "Elixir")) + +(define-compilation-mode alchemist-buffer-mode "Elixir" + "Elixir compilation mode." + (progn + (font-lock-add-keywords nil + '(("^Finished in .*$" . font-lock-string-face))) + ;; Set any bound buffer name buffer-locally + (setq alchemist-buffer--buffer-name alchemist-buffer--buffer-name) + (set (make-local-variable 'kill-buffer-hook) + 'alchemist-buffer--kill-any-orphan-proc))) + +(defun alchemist-buffer-run (cmdlist buffer-name) + "Run CMDLIST in `alchemist-buffer-mode'. +Returns the compilation buffer. +Argument BUFFER-NAME for the compilation." + (save-some-buffers (not compilation-ask-about-save) alchemist-buffer--save-buffers-predicate) + (let* ((alchemist-buffer--buffer-name buffer-name) + (compilation-filter-start (point-min))) + (with-current-buffer + (compilation-start (mapconcat 'concat cmdlist " ") + 'alchemist-buffer-mode + (lambda (b) alchemist-buffer--buffer-name)) + (setq-local compilation-error-regexp-alist-alist + (cons alchemist-buffer--error-link-options compilation-error-regexp-alist-alist)) + (setq-local compilation-error-regexp-alist (cons 'elixir compilation-error-regexp-alist)) + (add-hook 'compilation-filter-hook 'alchemist-buffer--handle-compilation nil t) + (add-hook 'compilation-filter-hook 'alchemist-buffer--remove-dispensable-output nil t) + (add-to-list 'compilation-finish-functions 'alchemist-buffer--remove-dispensable-output-after-finish) + (when alchemist-buffer-status-modeline + (add-hook 'compilation-finish-functions 'alchemist-buffer--set-modeline-color nil t))))) + +(provide 'alchemist-buffer) + +;;; alchemist-buffer.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-company.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-company.el new file mode 100644 index 0000000..2848aca --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-company.el @@ -0,0 +1,87 @@ +;;; alchemist-company.el --- Elixir company-mode backend -*- lexical-binding: t -*- + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; Elixir company-mode backend. + +;;; Code: + +(require 'company) + +(defgroup alchemist-company nil + "Elixir company-mode backend." + :prefix "alchemist-company-" + :group 'alchemist) + +;; Variables + +(defcustom alchemist-company-show-annotation t + "Show an annotation inline with the candidate." + :type 'boolean + :group 'alchemist-company) + +(defun alchemist-company--show-documentation () + (interactive) + (company--electric-do + (let* ((selected (nth company-selection company-candidates)) + (candidate (format "%s%s" selected (alchemist-company--annotation selected)))) + (alchemist-help--execute-without-complete candidate)))) +(put 'alchemist-company--show-documentation 'company-keep t) + +(defun alchemist-company--open-definition () + (interactive) + (company--electric-do + (let* ((selected (nth company-selection company-candidates))) + (alchemist-goto--open-definition selected)))) +(put 'alchemist-company--open-definition 'company-keep t) + +(defun alchemist-company--keybindings () + (define-key company-active-map (kbd "C-d") 'alchemist-company--show-documentation) + (define-key company-active-map (kbd "M-.") 'alchemist-company--open-definition)) + +(add-hook 'company-mode-hook 'alchemist-company--keybindings) + +(defun alchemist-company--annotation (candidate) + (get-text-property 0 'meta candidate)) + +(defun alchemist-company (command &optional arg &rest ignored) + "`company-mode' completion back-end for Elixir." + (interactive (list 'interactive)) + (when alchemist-company-show-annotation + (set 'company-tooltip-align-annotations t)) + (case command + (interactive (company-begin-backend 'alchemist-company)) + (init (when (or (eq major-mode 'elixir-mode) + (string= mode-name "Alchemist-IEx")))) + (prefix (and (or (eq major-mode 'elixir-mode) + (string= mode-name "Alchemist-IEx")) + (alchemist-help--exp-at-point))) + (candidates (cons :async + (lambda (cb) (alchemist-complete-candidates arg cb)))) + (annotation (when alchemist-company-show-annotation + (alchemist-company--annotation arg))))) + +(add-to-list 'company-backends 'alchemist-company) + +(provide 'alchemist-company) + +;;; alchemist-company.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-compile.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-compile.el new file mode 100644 index 0000000..bcc0690 --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-compile.el @@ -0,0 +1,73 @@ +;;; alchemist-compile.el --- Elixir compilation functionality + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; Elixir compilation functionality. + +;;; Code: + +(defgroup alchemist-compile nil + "Elixir compilation functionality." + :prefix "alchemist-compile-" + :group 'alchemist) + +;; Variables + +(defcustom alchemist-compile-command "elixirc" + "The shell command for elixirc." + :type 'string + :group 'alchemist-compile) + +(defvar alchemist-compile-buffer-name "*elixirc*" + "Name of the elixir output buffer.") + +;; Private functions + +(defun alchemist-compile--file (filename) + (when (not (file-exists-p filename)) + (error "The given file doesn't exists")) + (alchemist-compile (list alchemist-compile-command (expand-file-name filename)))) + +(defun alchemist-compile--read-command (command) + (read-shell-command "elixirc command: " (concat command " "))) + +;; Public functions + +(defun alchemist-compile-this-buffer () + "Compile the current buffer with elixirc." + (interactive) + (alchemist-compile--file buffer-file-name)) + +(defun alchemist-compile-file (filename) + "Compile the given FILENAME." + (interactive "Felixirc: ") + (alchemist-compile--file (expand-file-name filename))) + +(defun alchemist-compile (cmdlist) + "Compile CMDLIST with elixirc." + (interactive (list (alchemist-compile--read-command alchemist-compile-command))) + (alchemist-buffer-run (alchemist-utils--build-runner-cmdlist cmdlist) + alchemist-compile-buffer-name)) + +(provide 'alchemist-compile) + +;;; alchemist-compile.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-complete.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-complete.el new file mode 100644 index 0000000..fcaed9e --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-complete.el @@ -0,0 +1,186 @@ +;;; alchemist-complete.el --- Complete functionality for Elixir source code -*- lexical-binding: t -*- + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; Complete functionality for Elixir and Erlang source code. + +;;; Code: + +(defgroup alchemist-complete nil + "Complete functionality for Elixir source code." + :prefix "alchemist-complete-" + :group 'alchemist) + +(defvar alchemist-complete-debug-mode t) + +(defun alchemist-complete-debug-mode () + "Enables the debug mode for completion if `alchemist-complete-debug-mode' +is `nil', otherwise it disable it." + (interactive) + (setq alchemist-complete-debug-mode (not alchemist-complete-debug-mode)) + (let ((state (if alchemist-complete-debug-mode + "ENABLED" + "DISABLED"))) + (message "Alchemist complete debug mode is: %s" state))) + +(defun alchemist-complete--concat-prefix-with-functions (prefix functions &optional add-prefix) + (let* ((prefix (mapconcat 'concat (butlast (split-string prefix "\\.") 1) ".")) + (candidates (mapcar (lambda (c) (concat prefix "." c)) (cdr functions)))) + (if add-prefix + (push prefix candidates) + candidates))) + +(defun alchemist-complete--add-prefix-to-function (prefix function) + (let* ((prefix (mapconcat 'concat (butlast (split-string prefix "\\.") 1) ".")) + (candidate (concat prefix "." function))) + candidate)) + +(defun alchemist-complete--build-candidates (a-list) + (let* ((search-term (car a-list)) + (candidates (mapcar (lambda (f) + (let* ((candidate f) + (meta (if (string-match-p "^.+/" f) + (replace-regexp-in-string "^.+/" "/" f) + ""))) + (cond + ((and (string-match-p "^:" search-term) + (not (string-match-p "\\.$" search-term))) + (propertize (concat ":" candidate))) + ((string-match-p "\\." search-term) + (propertize (alchemist-complete--add-prefix-to-function search-term + (replace-regexp-in-string "/[0-9]$" "" candidate)) 'meta meta)) + (t (propertize (replace-regexp-in-string "/[0-9]$" "" candidate) 'meta meta))))) + (cdr a-list)))) + candidates)) + +(defun alchemist-complete--build-help-candidates (a-list) + (let* ((search-term (car a-list)) + (candidates (cond ((> (alchemist-utils--count-char-in-str "\\." search-term) 1) + (let ((search (if (string-match-p "\\.[a-z0-9_\?!]+$" search-term) + (list (replace-regexp-in-string "\\.[a-z0-9_\?!]+$" "" search-term)) + (list (replace-regexp-in-string "\\.$" "" search-term)))) + (candidates (mapcar (lambda (c) + (if (string-match-p "\\.[a-z0-9_\?!]+$" search-term) + (concat (replace-regexp-in-string "\\.[a-z0-9_\?!]+$" "." search-term) c) + (concat search-term c))) + (cdr a-list)))) + (append search candidates))) + ((string-match-p "\\.$" search-term) + (alchemist-complete--concat-prefix-with-functions search-term a-list t)) + ((string-match-p "\\.[a-z0-9_\?!]+$" search-term) + (alchemist-complete--concat-prefix-with-functions search-term a-list)) + (t + a-list)))) + (delete-dups candidates))) + +(defun alchemist-complete--output-to-list (output) + (let* ((output (replace-regexp-in-string "^cmp:" "" output)) + (output (split-string output)) + (output (delete nil output))) + output) + ) + +(defun alchemist-complete--clear-buffer (buffer) + "Clears the BUFFER from not used lines." + (with-current-buffer buffer + (delete-non-matching-lines "^cmp:" (point-min) (point-max)))) + +(defun alchemist-complete--elixir-complete-code (exp) + (format " +defmodule Alchemist do + def expand(exp) do + {status, result, list } = IEx.Autocomplete.expand(Enum.reverse(exp)) + + case { status, result, list } do + { :no, _, _ } -> '' + { :yes, [], _ } -> List.insert_at(list, 0, exp) + { :yes, _, _ } -> expand(exp ++ result) + end + end +end + +Alchemist.expand('%s') |> Enum.map fn (f) -> IO.puts('cmp:' ++ f) end +" exp)) + +(defun alchemist-complete--command (exp) + (let* ((elixir-code (alchemist-complete--elixir-complete-code exp)) + (compile-option (if (and (alchemist-project-p) + (alchemist-project--load-compile-when-needed-setting)) + "" + "--no-compile")) + (command (if (alchemist-project-p) + (format "%s %s -e \"%s\"" alchemist-help-mix-run-command compile-option elixir-code) + (format "%s -e \"%s\"" alchemist-execute-command elixir-code))) + ) + (when (alchemist-project-p) + (alchemist-project--establish-root-directory)) + command)) + +(defun alchemist-complete--sentinel (proc callback &optional format-function) + (set-process-sentinel proc (lambda (process signal) + (cond ((equal signal "finished\n") + (alchemist-complete--clear-buffer (process-buffer process)) + (let* ((candidates (alchemist-complete--output-to-list + (alchemist--utils-clear-ansi-sequences + (alchemist-utils--get-buffer-content (process-buffer process))))) + (candidates (if format-function + (funcall format-function candidates) + candidates))) + (funcall callback candidates))) + (t + (when alchemist-complete-debug-mode + (alchemist-complete--debug-message (alchemist-utils--get-buffer-content (process-buffer process)))) + (funcall callback '()))) + (alchemist-utils--erase-buffer (process-buffer process))))) + +(defun alchemist-complete--debug-message (content) + (alchemist-message (format "== ALCHEMIST COMPLETION FAILED ==\n== OUTPUT BEGIN:\n%s== OUTPUT END:" + content))) + +(defun alchemist-complete--completing-prompt (initial completing-collection) + (let* ((completing-collection (alchemist-complete--build-help-candidates completing-collection))) + (cond ((equal (length completing-collection) 1) + (car completing-collection)) + (completing-collection + (completing-read + "Elixir help: " + completing-collection + nil + nil + (replace-regexp-in-string "\\.$" "" initial))) + (t initial)))) + +(defun alchemist-complete (exp callback) + (let* ((buffer (get-buffer-create "alchemist-complete-buffer")) + (command (alchemist-complete--command exp)) + (proc (start-process-shell-command "alchemist-complete-proc" buffer command))) + (alchemist-complete--sentinel proc callback))) + +(defun alchemist-complete-candidates (exp callback) + (let* ((buffer (get-buffer-create "alchemist-complete-buffer")) + (command (alchemist-complete--command exp)) + (proc (start-process-shell-command "alchemist-complete-proc" buffer command))) + (alchemist-complete--sentinel proc callback #'alchemist-complete--build-candidates))) + +(provide 'alchemist-complete) + +;;; alchemist-complete.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-eval.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-eval.el new file mode 100644 index 0000000..0921b5c --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-eval.el @@ -0,0 +1,175 @@ +;;; alchemist-eval.el --- Elixir code inline evaluation functionality + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; Elixir code inline evaluation functionality + +;;; Code: + +(defgroup alchemist-eval nil + "Elixir code inline evaluation functionality." + :prefix "alchemist-eval-" + :group 'alchemist) + +(defun alchemist-eval--insert (string) + (let ((lines (split-string string "\n"))) + (if (> (length lines) 1) + (progn + (save-excursion + (end-of-line) + (mapc (lambda (s) + (newline) + (insert (format "# => %s" s))) + lines))) + (save-excursion + (end-of-line) + (insert (format " # => %s" string)))))) + +(defun alchemist-eval--evaluate-code (string) + (let ((tmp-file ".alchemist-eval.exs") + (old-directory default-directory)) + (when (alchemist-project-p) + (alchemist-project--establish-root-directory)) + (with-temp-file tmp-file + (insert string)) + (let ((output (shell-command-to-string + (alchemist-eval--build-code-evaluation-command tmp-file)))) + (delete-file tmp-file) + (cd old-directory) + (alchemist-utils--remove-newline-at-end output)))) + +(defun alchemist-eval--evaluate-code-as-quoted (string) + (let ((tmp-file ".alchemist-eval.exs") + (old-directory default-directory)) + (when (alchemist-project-p) + (alchemist-project--establish-root-directory)) + (with-temp-file tmp-file + (insert string)) + (let ((output (shell-command-to-string + (alchemist-eval--build-code-evaluation-as-quoted-command tmp-file)))) + (delete-file tmp-file) + (cd old-directory) + (alchemist-utils--remove-newline-at-end output)))) + +(defun alchemist-eval--build-code-evaluation-command (file) + (format "%s -e 'IO.inspect(elem(Code.eval_string(File.read!(\"%s\")), 0))'" + (alchemist-eval--runner) + file)) + +(defun alchemist-eval--build-code-evaluation-as-quoted-command (file) + (format "%s -e 'IO.puts inspect(elem(Code.string_to_quoted(File.read!(\"%s\")), 1), pretty: true)'" + (alchemist-eval--runner) + file)) + +(defun alchemist-eval--runner () + (if (alchemist-project-p) + (format "%s run --no-compile" alchemist-mix-command) + alchemist-execute-command)) + +(defun alchemist-eval-current-line () + "Evaluate the Elixir code on the current line." + (interactive) + (let ((current-line (thing-at-point 'line))) + (message (alchemist-eval--evaluate-code current-line)))) + +(defun alchemist-eval-print-current-line () + "Evaluate the Elixir code on the current line and insert the result." + (interactive) + (let ((current-line (thing-at-point 'line))) + (alchemist-eval--insert (alchemist-eval--evaluate-code current-line)))) + +(defun alchemist-eval-quoted-current-line () + "Get the Elixir code representation of the expression on the current line." + (interactive) + (let ((current-line (thing-at-point 'line))) + (message (alchemist-eval--evaluate-code-as-quoted current-line)))) + +(defun alchemist-eval-print-quoted-current-line () + "Get the Elixir code representation of the expression on the current line and insert the result." + (interactive) + (let ((current-line (thing-at-point 'line))) + (alchemist-eval--insert (alchemist-eval--evaluate-code-as-quoted current-line)))) + +(defun alchemist-eval-region (beg end) + "Evaluate the Elixir code on marked region." + (interactive (list (point) (mark))) + (unless (and beg end) + (error "The mark is not set now, so there is no region")) + (let ((string (buffer-substring-no-properties beg end))) + (message (alchemist-eval--evaluate-code string)))) + +(defun alchemist-eval-print-region (beg end) + "Evaluate the Elixir code on marked region and insert the result." + (interactive (list (point) (mark))) + (unless (and beg end) + (error "The mark is not set now, so there is no region")) + (let ((string (buffer-substring-no-properties beg end))) + (when (> end beg) + (exchange-point-and-mark)) + (alchemist-eval--insert (alchemist-eval--evaluate-code string)))) + +(defun alchemist-eval-quoted-region (beg end) + "Get the Elixir code representation of the expression on marked region." + (interactive (list (point) (mark))) + (unless (and beg end) + (error "The mark is not set now, so there is no region")) + (let ((string (buffer-substring-no-properties beg end))) + (message (alchemist-eval--evaluate-code-as-quoted string)))) + +(defun alchemist-eval-print-quoted-region (beg end) + "Get the Elixir code representation of the expression on marked region and insert the result." + (interactive (list (point) (mark))) + (unless (and beg end) + (error "The mark is not set now, so there is no region")) + (let ((string (buffer-substring-no-properties beg end))) + (when (> end beg) + (exchange-point-and-mark)) + (alchemist-eval--insert (alchemist-eval--evaluate-code-as-quoted string)))) + +(defun alchemist-eval-buffer () + "Evaluate the Elixir code in the current buffer." + (interactive) + (let ((string (buffer-substring-no-properties (point-min) (point-max)))) + (message (alchemist-eval--evaluate-code string)))) + +(defun alchemist-eval-print-buffer () + "Evaluate the Elixir code in the current buffer and insert the result." + (interactive) + (let ((string (buffer-substring-no-properties (point-min) (point-max)))) + (end-of-buffer) + (alchemist-eval--insert (alchemist-eval--evaluate-code string)))) + +(defun alchemist-eval-quoted-buffer () + "Get the Elixir code representation of the expression in the current buffer." + (interactive) + (let ((string (buffer-substring-no-properties (point-min) (point-max)))) + (message (alchemist-eval--evaluate-code-as-quoted string)))) + +(defun alchemist-eval-print-quoted-buffer () + "Get the Elixir code representation of the expression in the current buffer and insert result." + (interactive) + (let ((string (buffer-substring-no-properties (point-min) (point-max)))) + (alchemist-eval--insert (alchemist-eval--evaluate-code-as-quoted string)))) + +(provide 'alchemist-eval) + +;;; alchemist-eval.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-execute.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-execute.el new file mode 100644 index 0000000..6481695 --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-execute.el @@ -0,0 +1,73 @@ +;;; alchemist-execute.el --- Elixir's script execution integration + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; Elixir's script execution integration + +;;; Code: + +(defgroup alchemist-execute nil + "Elixir's script execution integration." + :prefix "alchemist-execute-" + :group 'alchemist) + +;; Variables + +(defcustom alchemist-execute-command "elixir" + "The shell command for elixir." + :type 'string + :group 'alchemist-execute) + +(defvar alchemist-execute-buffer-name "*elixir*" + "Name of the elixir output buffer.") + +;; Private functions + +(defun alchemist-execute--file (filename) + (when (not (file-exists-p filename)) + (error "The given file doesn't exists")) + (alchemist-execute (list alchemist-execute-command (expand-file-name filename)))) + +(defun alchemist-execute--read-command (command) + (read-shell-command "elixir command: " (concat command " "))) + +;; Public functions + +(defun alchemist-execute-this-buffer () + "Run the current buffer through elixir." + (interactive) + (alchemist-execute--file buffer-file-name)) + +(defun alchemist-execute-file (filename) + "Run elixir with the given FILENAME." + (interactive "Felixir: ") + (alchemist-execute--file (expand-file-name filename))) + +(defun alchemist-execute (cmdlist) + "Run a elixir with CMDLIST." + (interactive (list (alchemist-execute--read-command alchemist-execute-command))) + (alchemist-buffer-run (alchemist-utils--build-runner-cmdlist cmdlist) + alchemist-execute-buffer-name)) + +(provide 'alchemist-execute) + +;;; alchemist-execute.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-goto.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-goto.el new file mode 100644 index 0000000..7a8c2b9 --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-goto.el @@ -0,0 +1,231 @@ +;;; alchemist-goto.el --- Functionality to jump modules and function definitions + +;; Copyright © 2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; Functionality to jump modules and function definitions + +;;; Code: + +(defgroup alchemist-goto nil + "Functionality to jump modules and function definitions." + :prefix "alchemist-goto-" + :group 'alchemist) + +(defcustom alchemist-goto-erlang-source-dir "" + "Path to the erlang source code." + :type 'string + :group 'alchemist-goto) + +(defcustom alchemist-goto-elixir-source-dir "" + "Path to the elixir source code." + :type 'string + :group 'alchemist-goto) + +(defun alchemist-goto--extract-module (code) + "Extract module from CODE." + (let* ((parts (split-string code "\\.")) + (function (car (last parts))) + (case-fold-search nil)) + (when (string-match-p "^[a-z]+" function) + (delete function parts)) + (unless (string-match-p "^[a-z]+" (car parts)) + (mapconcat 'concat parts ".")))) + +(defun alchemist-goto--extract-function (code) + "Extract function from CODE." + (let* ((parts (split-string code "\\.")) + (function (car (last parts))) + (case-fold-search nil)) + (when (and function + (string-match-p "^[a-z]+" function)) + function))) + +(defun alchemist-goto--build-elixir-ex-core-file (file) + (when (string-match "\\/\\(lib\\/.+\\/lib\\)\\/.+\.ex$" file) + (let* ((file (substring-no-properties file (match-beginning 1))) + (source-directory (expand-file-name alchemist-goto-elixir-source-dir))) + (concat source-directory file)))) + +(defun alchemist-goto--build-elixir-erl-core-file (file) + (when (string-match "\\/\\(lib\\/.+\\/src\\)\\/.+\.erl$" file) + (let* ((file (substring-no-properties file (match-beginning 1))) + (source-directory (expand-file-name alchemist-goto-elixir-source-dir))) + (concat source-directory file)))) + +(defun alchemist-goto--build-erlang-core-file (file) + (when (string-match "\\/\\(lib\\/.+\\/src\\)\\/.+\.erl$" file) + (let* ((file (substring-no-properties file (match-beginning 1))) + (source-directory (expand-file-name alchemist-goto-erlang-source-dir))) + (concat source-directory file)))) + +(defun alchemist-goto--elixir-file-p (file) + (string-match-p "\\.ex\\(s\\)?$" file)) + +(defun alchemist-goto--erlang-file-p (file) + (string-match-p "\\.erl$" file)) + +(defun alchemist-goto--get-full-path-of-alias (module) + (let* ((aliases (mapcar (lambda (m) + (when (string= module (car (cdr m))) + (car m))) (alchemist-goto--alises-of-current-buffer))) + (aliases (delete nil aliases))) + (if aliases + (car aliases) + module))) + +(defun alchemist-goto--open-definition (expr) + (let* ((module (alchemist-goto--extract-module expr)) + (module (alchemist-goto--get-full-path-of-alias module)) + (module (if module module "AlchemistGoto")) + (function (alchemist-goto--extract-function expr)) + (function (if function function "\"\"")) + (file (alchemist-goto--get-module-source module function))) + (ring-insert find-tag-marker-ring (point-marker)) + (cond ((equal file nil) + (message "Don't know how to find: %s" expr)) + ((file-exists-p file) + (alchemist-goto--open-file file module function)) + ((alchemist-goto--elixir-file-p file) + (let* ((elixir-source-file (alchemist-goto--build-elixir-ex-core-file file))) + (if (file-exists-p elixir-source-file) + (alchemist-goto--open-file elixir-source-file module function) + (message "Don't know how to find: %s" expr)))) + ((alchemist-goto--erlang-file-p file) + (let* ((elixir-source-file (alchemist-goto--build-elixir-erl-core-file file)) + (erlang-source-file (alchemist-goto--build-erlang-core-file file))) + (cond ((file-exists-p elixir-source-file) + (alchemist-goto--open-file elixir-source-file module function)) + ((file-exists-p erlang-source-file) + (alchemist-goto--open-file erlang-source-file module function)) + (t + (message "Don't know how to find: %s" expr))))) + (t + (pop-tag-mark) + (message "Don't know how to find: %s" expr))))) + +(defun alchemist-goto--open-file (file module function) + (let* ((buf (find-file-noselect file))) + (switch-to-buffer buf) + (beginning-of-buffer) + (cond ((alchemist-goto--elixir-file-p file) + (alchemist-goto--jump-to-elixir-source module function)) + ((alchemist-goto--erlang-file-p file) + (alchemist-goto--jump-to-erlang-source module function))))) + +(defun alchemist-goto--jump-to-elixir-source (module function) + (let ((function (replace-regexp-in-string "\?" "\\?" function))) + (when (re-search-forward (format "^\s+\\(defp?\s+%s\(\\|defmacrop?\s+%s\(\\)" function function function) nil t) + (goto-char (match-beginning 0))) + (when (re-search-forward (format "\\(defmodule\\|defimpl\\|defprotocol\\)\s+%s\s+do" module) nil t) + (goto-char (match-beginning 0))))) + +(defun alchemist-goto--jump-to-erlang-source (module function) + (when (re-search-forward (format "\\(^%s\(\\)" function) nil t) + (goto-char (match-beginning 0))) + (when (re-search-forward (format "\\(^-module\(%s\)\\)" (substring module 1)) nil t) + (goto-char (match-beginning 0)))) + +(defun alchemist-goto--clear-output (output) + (let* ((output (replace-regexp-in-string "source-file-path:" "" output)) + (output (replace-regexp-in-string "\n" "" output)) + (output (alchemist--utils-clear-ansi-sequences output)) + (output (if (string= output "") nil output))) + output)) + +(defun alchemist-goto--debug-message (output) + (alchemist-message (format "== ALCHEMIST GOTO FAILED ==\n== OUTPUT BEGIN:\n%s== OUTPUT END:" + output))) + +(defun alchemist-goto--report-errors (output) + (when (and (not (string-match-p "source-file-path:" output)) + (not (string= (alchemist--utils-clear-ansi-sequences + (replace-regexp-in-string "\n" "" output)) ""))) + (when alchemist-complete-debug-mode + (alchemist-goto--debug-message output)))) + +(defun alchemist-goto--runner () + (if (alchemist-project-p) + (format "%s run --no-compile" alchemist-mix-command) + alchemist-execute-command)) + +(defun alchemist-goto--get-module-source (module function) + (let* ((default-directory (if (alchemist-project-p) + (alchemist-project-root) + default-directory)) + (source-file (shell-command-to-string (format "%s -e '%s'" + (alchemist-goto--runner) + (alchemist-goto--get-module-source-code module function))))) + (alchemist-goto--report-errors source-file) + (alchemist-goto--clear-output source-file))) + +(defun alchemist-goto--get-module-source-code (module function) + (format " +defmodule Source do + def find(module, function) do + cond do + Code.ensure_loaded?(module) -> + IO.puts source(module) + List.keymember?(Kernel.module_info[:exports], function, 0) -> + IO.puts source(Kernel) + true -> + IO.puts \"\" + end + end + + defp source(module) do + source = module.module_info(:compile)[:source] + + case source do + nil -> nil + source -> \"source-file-path:\" <> List.to_string(source) + end + end +end + +Source.find(%s, :%s)" module function)) + +(defun alchemist-goto--alises-of-current-buffer () + (let* ((aliases '())) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "^\s+alias\s+\\([-_A-Za-z0-9,\.\?!\]+\\)\\(\s*,\s*as:\s*\\)?\\([-_A-Za-z0-9,\.\?!\]+\\)?\n" nil t) + (let* ((alias (match-string 1)) + (as (if (match-string 3) (match-string 3) nil)) + (as (if as as (car (last (split-string alias "\\.")))))) + (setq aliases (append aliases (list (list alias as))))))) + aliases)) + +(defun alchemist-goto-definition-at-point () + "Jump to the elixir expression definition at point." + (interactive) + (let (p1 p2) + (skip-chars-backward "-_A-Za-z0-9.?!:") + (setq p1 (point)) + (skip-chars-forward "-_A-Za-z0-9.?!:") + (setq p2 (point)) + (alchemist-goto--open-definition (buffer-substring-no-properties p1 p2)))) + +(defalias 'alchemist-goto-jump-back 'pop-tag-mark) + +(provide 'alchemist-goto) + +;;; alchemist-goto.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-help.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-help.el new file mode 100644 index 0000000..10c7d9e --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-help.el @@ -0,0 +1,264 @@ +;;; alchemist-help.el --- Functionality for Elixir documentation lookup -*- lexical-binding: t -*- + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; Functionality for Elixir documentation lookup. + +;;; Code: + +(defgroup alchemist-help nil + "Functionality for Elixir documentation lookup." + :prefix "alchemist-help-" + :group 'alchemist) + +;; Variables + +(defcustom alchemist-help-ansi-color-docs t + "If t, `alchemist-help' will present ansi colored documentation." + :type 'boolean + :group 'alchemist-help) + +(defcustom alchemist-help-buffer-name "*elixir help*" + "Name of the Elixir help buffer." + :type 'string + :group 'alchemist-help) + +(defvar alchemist-help-mix-run-command "mix run" + "The shell command for 'mix run'.") + +(defvar alchemist-help-search-history '() + "Storage for the search history.") + +(defvar alchemist-help-current-search-text '() + "Stores the current search.") + +;; Faces + +(defface alchemist-help--key-face + '((t (:inherit font-lock-variable-name-face :bold t :foreground "red"))) + "Fontface for the letter keys in the summary." + :group 'alchemist-help) + +(defun alchemist-help--load-ansi-color-setting () + (let ((config (gethash "ansi-color-docs" (alchemist-project-config)))) + (if config + (intern config) + alchemist-help-ansi-color-docs))) + +(defun alchemist-help--exp-at-point () + "Return the expression under the cursor." + (let (p1 p2) + (save-excursion + (skip-chars-backward "-_A-Za-z0-9.?!:") + (setq p1 (point)) + (skip-chars-forward "-_A-Za-z0-9.?!:") + (setq p2 (point)) + (buffer-substring-no-properties p1 p2)))) + +(defun alchemist-help--start-help-process (exp callback) + (let* ((buffer (get-buffer-create "alchemist-help-buffer")) + (command (alchemist-help--eval-string-command (alchemist-help--build-code-for-search exp))) + (proc (start-process-shell-command "alchemist-help-proc" buffer command))) + (set-process-sentinel proc (lambda (process signal) + (when (equal signal "finished\n") + (funcall callback (alchemist-utils--get-buffer-content (process-buffer process)))) + (alchemist-utils--erase-buffer (process-buffer process)))))) + +(defun alchemist-help--execute (search) + (let ((last-directory default-directory) + (last-buffer (current-buffer))) + (alchemist-complete search (lambda (candidates) + (if candidates + (let* ((search (alchemist-complete--completing-prompt search candidates))) + (setq alchemist-help-current-search-text search) + (alchemist-help--start-help-process search (lambda (output) + (alchemist-help--initialize-buffer output) + (with-current-buffer last-buffer + (cd last-directory))))) + (message "No documentation found for '%s'" search)))))) + +(defun alchemist-help--execute-without-complete (search) + (setq alchemist-help-current-search-text search) + (let ((last-directory default-directory) + (last-buffer (current-buffer))) + (alchemist-help--start-help-process search (lambda (output) + (alchemist-help--initialize-buffer output) + (with-current-buffer last-buffer + (cd last-directory)))))) + +(defun alchemist-help--build-code-for-search (string) + (format "import IEx.Helpers + +Application.put_env(:iex, :colors, [enabled: %s]) + +h(%s)" (if (alchemist-help--load-ansi-color-setting) "true" "false") string)) + +(defun alchemist-help--eval-string-command (string) + (when (alchemist-project-p) + (alchemist-project--establish-root-directory)) + (let* ((compile-option (if (and (alchemist-project-p) + (alchemist-project--load-compile-when-needed-setting)) + "" + "--no-compile")) + (command (if (alchemist-project-p) + (format "%s %s -e \"%s\"" alchemist-help-mix-run-command compile-option string) + (format "%s -e \"%s\"" alchemist-execute-command string)))) + command)) + +(defun alchemist-help--bad-search-output-p (string) + (let ((match (or (string-match-p "No documentation for " string) + (string-match-p "Invalid arguments for h helper" string) + (string-match-p "** (TokenMissingError)" string) + (string-match-p "** (SyntaxError)" string) + (string-match-p "** (FunctionClauseError)" string) + (string-match-p "** (CompileError)" string) + (string-match-p "Could not load module" string)))) + (if match + t + nil))) + +(defun alchemist-help--initialize-buffer (content) + (pop-to-buffer alchemist-help-buffer-name) + (setq buffer-undo-list nil) + (let ((inhibit-read-only t) + (buffer-undo-list t)) + (cond ((alchemist-help--bad-search-output-p content) + (message (propertize + (format "No documentation for [ %s ] found." alchemist-help-current-search-text) + 'face 'alchemist-help--key-face))) + (t + (erase-buffer) + (insert content) + (unless (memq 'alchemist-help-current-search-text alchemist-help-search-history) + (add-to-list 'alchemist-help-search-history alchemist-help-current-search-text)))) + (delete-matching-lines "do not show this result in output" (point-min) (point-max)) + (delete-matching-lines "^Compiled lib\\/" (point-min) (point-max)) + (ansi-color-apply-on-region (point-min) (point-max)) + (toggle-read-only 1) + (alchemist-help-minor-mode 1))) + +(defun alchemist-help-minor-mode-key-binding-summary () + (interactive) + (message + (concat "[" (propertize "q" 'face 'alchemist-help--key-face) + "]-quit [" + (propertize "e" 'face 'alchemist-help--key-face) + "]-search-at-point [" + (propertize "m" 'face 'alchemist-help--key-face) + "]-search-marked-region [" + (propertize "s" 'face 'alchemist-help--key-face) + "]-search [" + (propertize "h" 'face 'alchemist-help--key-face) + "]-history [" + (propertize "?" 'face 'alchemist-help--key-face) + "]-keys"))) + +(defun alchemist-help-search-at-point () + "Search through `alchemist-help' with the expression under the cursor." + (interactive) + (alchemist-help--execute (alchemist-help--exp-at-point))) + +(defun alchemist-help-search-marked-region (begin end) + "Run `alchemist-help' with the marked region. +Argument BEGIN where the mark starts. +Argument END where the mark ends." + (interactive "r") + (let ((region (filter-buffer-substring begin end))) + (alchemist-help--execute region))) + +(defun alchemist-help--elixir-modules-to-list (str) + (let* ((modules (split-string str)) + (modules (mapcar (lambda (m) + (when (string-match-p "Elixir\\." m) + (replace-regexp-in-string "Elixir\\." "" m))) modules)) + (modules (delete nil modules)) + (modules (cl-sort modules 'string-lessp :key 'downcase)) + (modules (delete-dups modules))) + modules) + ) + +(defun alchemist-help--get-modules () + (let* ((elixir-code " +defmodule AlchemistModule do + def get_modules do + modules = Enum.map(:code.all_loaded, fn({m, _}) -> Atom.to_string(m) end) + + if :code.get_mode() === :interactive do + modules ++ get_modules_from_applications() + else + modules + end + end + + defp get_modules_from_applications do + for {app, _, _} <- :application.loaded_applications, + {_, modules} = :application.get_key(app, :modules), + module <- modules, + has_doc = Code.get_docs(module, :moduledoc), elem(has_doc, 1) do + Atom.to_string(module) + end + end +end + +AlchemistModule.get_modules |> Enum.map &IO.puts/1 +") + (command (if (alchemist-project-p) + (format "%s -e \"%s\"" alchemist-help-mix-run-command elixir-code) + (format "%s -e \"%s\"" alchemist-execute-command elixir-code)))) + (when (alchemist-project-p) + + (alchemist-project--establish-root-directory)) + (alchemist-help--elixir-modules-to-list (shell-command-to-string command)))) + +(define-minor-mode alchemist-help-minor-mode + "Minor mode for displaying elixir help." + :group 'alchemist-help + :keymap '(("q" . quit-window) + ("e" . alchemist-help-search-at-point) + ("m" . alchemist-help-search-marked-region) + ("s" . alchemist-help) + ("h" . alchemist-help-history) + ("?" . alchemist-help-minor-mode-key-binding-summary))) + +(defun alchemist-help (search) + "Load Elixir documentation for SEARCH." + (interactive + (list (completing-read + "Elixir help: " + (alchemist-help--get-modules) + nil + nil + nil))) + (alchemist-help--execute (if (string-match-p "\\.$" search) + search + (concat search ".")))) + +(defun alchemist-help-history (search) + "Load Elixir from the documentation history for SEARCH." + (interactive + (list + (completing-read "Elixir help history: " alchemist-help-search-history nil nil ""))) + (alchemist-help--execute-without-complete search)) + +(provide 'alchemist-help) + +;;; alchemist-help.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-hooks.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-hooks.el new file mode 100644 index 0000000..3d45bf1 --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-hooks.el @@ -0,0 +1,50 @@ +;;; alchemist-hooks.el --- Hooks functionality + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini + +;; This file is not part of GNU Emacs. + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Hooks functionality + +;;; Code: + +(defgroup alchemist-hooks nil + "Hooks" + :prefix "alchemist-hooks-" + :group 'alchemist) + +(defcustom alchemist-hooks-test-on-save nil + "If t, run `alchemist-mix-test' on save." + :type 'boolean + :group 'alchemist-hooks) + +(defun alchemist-hooks--test-on-save () + (when (and alchemist-hooks-test-on-save + (alchemist-utils--elixir-project-root)) + (alchemist-mix-test))) + +(eval-after-load 'elixir-mode + '(progn + (add-hook 'after-save-hook 'alchemist-hooks--test-on-save nil nil))) + +(provide 'alchemist-hooks) + +;;; alchemist-hooks.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-iex.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-iex.el new file mode 100644 index 0000000..bee1c83 --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-iex.el @@ -0,0 +1,171 @@ +;;; alchemist-help.el --- Interaction with an Elixir IEx process + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; Interaction with an Elixir IEx process + +;;; Code: + +(require 'comint) + +(defgroup alchemist-iex nil + "Interaction with an Elixir IEx process." + :prefix "alchemist-iex-" + :group 'alchemist) + +(defcustom alchemist-iex-program-name "iex" + "The shell command for iex." + :type 'string + :group 'alchemist-iex) + +(defvar alchemist-iex-buffer nil + "The buffer in which the Elixir IEx process is running.") + +(defvar alchemist-iex-mode-hook nil + "Hook for customizing `alchemist-iex-mode'.") + +(define-derived-mode alchemist-iex-mode comint-mode "Alchemist-IEx" + "Major mode for interacting with an Elixir IEx process." + nil "Alchemist-IEx" + (set (make-local-variable 'comint-prompt-regexp) + "^iex(\\([0-9]+\\|[a-zA-Z_@]+\\))> ") + (set (make-local-variable 'comint-input-autoexpand) nil)) + +(defun alchemist-iex-command (arg) + (split-string-and-unquote + (if (null arg) alchemist-iex-program-name + (read-string "Command to run Elixir IEx: " (concat alchemist-iex-program-name arg))))) + +(defun alchemist-iex-start-process (command) + "Start an IEX process. +With universal prefix \\[universal-argument], prompts for a COMMAND, +otherwise uses `alchemist-iex-program-name'. +It runs the hook `alchemist-iex-mode-hook' after starting the process and +setting up the IEx buffer." + (interactive (list (alchemist-iex-command current-prefix-arg))) + (setq alchemist-iex-buffer + (apply 'make-comint "Alchemist-IEx" (car command) nil (cdr command))) + (with-current-buffer alchemist-iex-buffer + (alchemist-iex-mode) + (run-hooks 'alchemist-iex-mode-hook))) + +(defun alchemist-iex-process (&optional arg) + (or (if (buffer-live-p alchemist-iex-buffer) + (get-buffer-process alchemist-iex-buffer)) + (progn + (let ((current-prefix-arg arg)) + (call-interactively 'alchemist-iex-start-process)) + (alchemist-iex-process arg)))) + +(defun alchemist-iex--remove-newlines (string) + (replace-regexp-in-string "\n" " " string)) + +(defun alchemist-iex-send-last-sexp () + "Send the previous sexp to the inferior IEx process." + (interactive) + (alchemist-iex-send-region (save-excursion (backward-sexp) (point)) (point))) + +(defun alchemist-iex-send-current-line () + "Sends the current line to the IEx process." + (interactive) + (let ((str (thing-at-point 'line))) + (alchemist-iex--send-command (alchemist-iex-process) str))) + +(defun alchemist-iex-send-current-line-and-go () + "Sends the current line to the inferior IEx process +and jump to the buffer." + (interactive) + (call-interactively 'alchemist-iex-send-current-line) + (pop-to-buffer (process-buffer (alchemist-iex-process)))) + +(defun alchemist-iex-send-region-and-go () + "Sends the marked region to the inferior IEx process +and jump to the buffer." + (interactive) + (call-interactively 'alchemist-iex-send-region) + (pop-to-buffer (process-buffer (alchemist-iex-process)))) + +(defun alchemist-iex-send-region (beg end) + "Sends the marked region to the IEx process." + (interactive (list (point) (mark))) + (unless (and beg end) + (error "The mark is not set now, so there is no region")) + (let* ((region (buffer-substring-no-properties beg end))) + (alchemist-iex--send-command (alchemist-iex-process) region))) + +(defun alchemist-iex-compile-this-buffer () + "Compiles the current buffer in the IEx process." + (interactive) + (let ((str (format "c(\"%s\")" (buffer-file-name)))) + (alchemist-iex--send-command (alchemist-iex-process) str))) + +(defun alchemist-iex-recompile-this-buffer () + "Recompiles and reloads the current buffer in the IEx process." + (interactive) + (let ((str (format "r(\"%s\")" (buffer-file-name)))) + (alchemist-iex--send-command (alchemist-iex-process) str))) + +(defun alchemist-iex--send-command (proc str) + (let ((str-no-newline (concat (alchemist-iex--remove-newlines str) "\n")) + (str (concat str "\n"))) + (with-current-buffer (process-buffer proc) + (goto-char (process-mark proc)) + (insert-before-markers str) + (move-marker comint-last-input-end (point)) + (comint-send-string proc str-no-newline)))) + +(defun alchemist-iex-clear-buffer () + "Clear the current iex process buffer." + (interactive) + (let ((comint-buffer-maximum-size 0)) + (comint-truncate-buffer))) + +;;;###autoload +(defalias 'run-elixir 'alchemist-iex-run) +(defalias 'inferior-elixir 'alchemist-iex-run) + +;;;###autoload +(defun alchemist-iex-run (&optional arg) + "Start an IEx process. +Show the IEx buffer if an IEx process is already run." + (interactive "P") + (let ((proc (alchemist-iex-process arg))) + (pop-to-buffer (process-buffer proc)))) + +;;;###autoload +(defun alchemist-iex-project-run () + "Start an IEx process with mix 'iex -S mix' in the +context of an Elixir project. +Show the IEx buffer if an IEx process is already run." + (interactive) + (let ((old-directory default-directory)) + (if (alchemist-project-p) + (progn + (alchemist-project--establish-root-directory) + (let ((proc (alchemist-iex-process " -S mix"))) + (cd old-directory) + (pop-to-buffer (process-buffer proc)))) + (message "No mix.exs file available. Please use `alchemist-iex-run' instead.")))) + +(provide 'alchemist-iex) + +;;; alchemist-iex.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-message.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-message.el new file mode 100644 index 0000000..177d0b8 --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-message.el @@ -0,0 +1,60 @@ +;;; alchemist-message.el --- Internal message functionality + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; Internal message functionality. + +;;; Code: + +(defgroup alchemist-message nil + "Internal message functionality." + :prefix "alchemist-message-" + :group 'alchemist) + +;; Variables + +(defvar alchemist-message--buffer-name "*alchemist message*") + +(defun alchemist-message (message) + (alchemist-message--initialize-buffer message)) + +(defun alchemist-message--initialize-buffer (message) + (display-buffer (get-buffer-create alchemist-message--buffer-name)) + (with-current-buffer alchemist-message--buffer-name + (let ((inhibit-read-only t) + (buffer-undo-list t)) + (erase-buffer) + (insert message) + (goto-char (point-min)) + (ansi-color-apply-on-region (point-min) (point-max)) + (read-only-mode) + (alchemist-message-mode 1)))) + +(define-minor-mode alchemist-message-mode + "Minor mode for displaying alchemist messages" + :group 'alchemist-message + :lighter " alchemist-msg" + :keymap '(("q" . quit-window))) + +(provide 'alchemist-message) + +;;; alchemist-message.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-mix.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-mix.el new file mode 100644 index 0000000..eed16dd --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-mix.el @@ -0,0 +1,174 @@ +;;; alchemist-mix.el --- Emacs integration for Elixir's mix + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; Emacs integration for Elixir's mix + +;;; Code: + +(defgroup alchemist-mix nil + "Emacs integration for Elixir's mix." + :prefix "alchemist-mix-" + :group 'alchemist) + +;; Variables + +(defcustom alchemist-mix-command "mix" + "The shell command for mix." + :type 'string + :group 'alchemist-mix) + +(defvar alchemist-mix-buffer-name "*mix*" + "Name of the mix output buffer.") + +(defvar alchemist-mix--deps-commands + '("deps" "deps.clean" "deps.compile" "deps.get" "deps.unlock" "deps.unlock") + "List of all deps.* available commands.") + +(defvar alchemist-mix--local-commands + '("local" "local.install" "local.rebar" "local.uninstall") + "List of all local.* available commands.") + +(defvar alchemist-mix--local-install-option-types '("path" "url") + "List of local.install option types.") + +(defun alchemist-mix--completing-read (prompt cmdlist) + (completing-read prompt cmdlist nil t nil nil (car cmdlist))) + +(defun alchemist-mix-display-mix-buffer () + "Display the mix buffer when exists." + (interactive) + (when (get-buffer alchemist-mix-buffer-name) + (display-buffer alchemist-mix-buffer-name))) + +(defun alchemist-mix-new (name) + "Create a new elixir project named by NAME." + (interactive "Gmix new: ") + (alchemist-mix-execute (list "new" (expand-file-name name)))) + +(defun alchemist-mix-test () + "Run the whole elixir test suite." + (interactive) + (alchemist-mix-execute (list "test"))) + +(defun alchemist-mix-test-this-buffer () + "Run the current buffer through mix test." + (interactive) + (alchemist-mix--test-file buffer-file-name)) + +(defun alchemist-mix-test-file (filename) + "Run `alchemist-mix--test-file' with the FILENAME." + (interactive "Fmix test: ") + (alchemist-mix--test-file (expand-file-name filename))) + +(defun alchemist-mix--test-file (filename) + "Run a specific FILENAME as argument for the mix command test." + (when (not (file-exists-p filename)) + (error "The given file doesn't exists")) + (alchemist-mix-execute (list "test" (expand-file-name filename)))) + +(defun alchemist-mix-test-at-point () + "Run the test at point." + (interactive) + (let* ((line (line-number-at-pos (point))) + (file-and-line (format "%s:%s" buffer-file-name line))) + (alchemist-mix-execute (list "test" file-and-line)))) + +(defun alchemist-mix-compile (command) + "Compile the whole elixir project." + (interactive "Mmix compile: ") + (alchemist-mix-execute (list "compile" command))) + +(defun alchemist-mix-run (command) + "Runs the given file or expression in the context of the application." + (interactive "Mmix run: ") + (alchemist-mix-execute (list "run" command))) + +(defun alchemist-mix-deps-with-prompt (command) + "Prompt for mix deps commands." + (interactive + (list (alchemist-mix--completing-read "mix deps: " alchemist-mix--deps-commands))) + (alchemist-mix-execute (list command))) + +(defun alchemist-mix--commands () + (let ((mix-cmd-list (shell-command-to-string (format "%s help" alchemist-mix-command)))) + (mapcar (lambda (s) + (cdr (split-string (car (split-string s "#"))))) + (cdr (split-string mix-cmd-list "\n"))))) + +(defun alchemist-mix (command) + "Prompt for mix commands." + (interactive + (list (alchemist-mix--completing-read "mix: " (alchemist-mix--commands)))) + (let ((command (read-string "mix " (concat command " ")))) + (alchemist-mix-execute (list command)))) + +(defun alchemist-mix-local-with-prompt (command) + "Prompt for mix local commands." + (interactive + (list (alchemist-mix--completing-read "mix local: " alchemist-mix--local-commands))) + (if (string= command "local.install") + (call-interactively 'alchemist-mix-local-install) + (alchemist-mix-execute (list command)))) + +(defun alchemist-mix-local-install (path-or-url) + "Prompt for mix local.install PATH-OR-URL." + (interactive + (list (completing-read "mix local.install FORMAT: " + alchemist-mix--local-install-option-types + nil t nil nil (car alchemist-mix--local-install-option-types)))) + (if (string= path-or-url (car alchemist-mix--local-install-option-types)) + (call-interactively 'alchemist-mix-local-install-with-path) + (call-interactively 'alchemist-mix-local-install-with-url))) + +(defun alchemist-mix-local-install-with-path (path) + "Runs local.install and prompt for a PATH as argument." + (interactive "fmix local.install PATH: ") + (alchemist-mix-execute (list "local.install" path))) + +(defun alchemist-mix-local-install-with-url (url) + "Runs local.install and prompt for a URL as argument." + (interactive "Mmix local.install URL: ") + (alchemist-mix-execute (list "local.install" url))) + +(defun alchemist-mix-hex-search (command) + "Display packages matching the given search query." + (interactive "Mmix hex.search: ") + (alchemist-mix-execute (list "hex.search" command))) + +(defun alchemist-mix-help (command) + "Show help output for a specific mix command." + (interactive "Mmix help: ") + (alchemist-mix-execute (list "help" command))) + +(defun alchemist-mix-execute (cmdlist) + "Run a mix command." + (interactive "Mmix: ") + (let ((old-directory default-directory)) + (alchemist-project--establish-root-directory) + (alchemist-buffer-run (alchemist-utils--build-runner-cmdlist (list alchemist-mix-command cmdlist)) + alchemist-mix-buffer-name) + (cd old-directory))) + +(provide 'alchemist-mix) + +;;; alchemist-mix.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-pkg.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-pkg.el new file mode 100644 index 0000000..4497230 --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-pkg.el @@ -0,0 +1,7 @@ +(define-package "alchemist" "20150201.2244" "Elixir tooling integration into Emacs" + '((emacs "24")) + :url "http://www.github.com/tonini/alchemist.el" :keywords + '("languages" "mix" "elixir" "elixirc" "hex")) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-project.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-project.el new file mode 100644 index 0000000..93aa108 --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-project.el @@ -0,0 +1,200 @@ +;;; alchemist-project.el --- API to identify Elixir mix projects. + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;; API to identify Elixir mix projects. + +;;; Code: + +(require 'cl) +(require 'json) + +(defgroup alchemist-project nil + "API to identify Elixir mix projects." + :prefix "alchemist-help-" + :group 'alchemist) + +(defcustom alchemist-project-config-filename ".alchemist" + "Name of the file which holds the Elixir project setup." + :type 'string + :group 'alchemist) + +(defcustom alchemist-project-compile-when-needed nil + "When `t', it compiles the Elixir project codebase when needed. + +For example: +If documentation lookup or completion for code is made, it first tries to +compile the current Elixir project codebase. This makes sure that the +documentation and completion is always up to date with the codebase. + +Please be aware that when the compilation fails, no documentation or +completion will be work. +" + :type 'string + :group 'alchemist) + +(defun alchemist-project-toggle-compile-when-needed () + "" + (interactive) + (if alchemist-project-compile-when-needed + (setq alchemist-project-compile-when-needed nil) + (setq alchemist-project-compile-when-needed t)) + (if alchemist-project-compile-when-needed + (message "Compilation of project when needed is enabled") + (message "Compilation of project when needed is disabled"))) + +(defun alchemist-project--load-compile-when-needed-setting () + (let ((config (gethash "compile-when-needed" (alchemist-project-config)))) + (if config + (intern config) + alchemist-project-compile-when-needed))) + +(defun alchemist-project--config-filepath () + "Return the path to the config file." + (format "%s/%s" + (alchemist-project-root) + alchemist-project-config-filename)) + +(defun alchemist-project--config-exists-p () + "Check if project config file exists." + (file-exists-p (alchemist-project--config-filepath))) + +(defun alchemist-project-config () + "Return the current Elixir project configs." + (let* ((json-object-type 'hash-table) + (config (if (alchemist-project--config-exists-p) + (json-read-from-string + (with-temp-buffer + (insert-file-contents (alchemist-project--config-filepath)) + (buffer-string))) + (make-hash-table :test 'equal)))) + config)) + +(defvar alchemist-project-root-indicators + '("mix.exs") + "list of file-/directory-names which indicate a root of a elixir project") + +(defvar alchemist-project-deps-indicators + '(".hex") + "list of file-/directory-names which indicate a root of a elixir project") + +(defun alchemist-project-p () + "Returns whether alchemist has access to a elixir project root or not" + (stringp (alchemist-project-root))) + +(defun alchemist-project-parent-directory (a-directory) + "Returns the directory of which a-directory is a child" + (file-name-directory (directory-file-name a-directory))) + +(defun alchemist-project-root-directory-p (a-directory) + "Returns t if a-directory is the root" + (equal a-directory (alchemist-project-parent-directory a-directory))) + +(defun alchemist-project-root (&optional directory) + "Finds the root directory of the project by walking the directory tree until it finds a project root indicator." + (let* ((directory (file-name-as-directory (or directory (expand-file-name default-directory)))) + (present-files (directory-files directory))) + (cond ((alchemist-project-root-directory-p directory) nil) + ((> (length (intersection present-files alchemist-project-deps-indicators :test 'string=)) 0) + (alchemist-project-root (file-name-directory (directory-file-name directory)))) + ((> (length (intersection present-files alchemist-project-root-indicators :test 'string=)) 0) directory) + (t (alchemist-project-root (file-name-directory (directory-file-name directory))))))) + +(defun alchemist-project--establish-root-directory () + "Set the default-directory to the Elixir project root." + (let ((project-root (alchemist-project-root))) + (when project-root + (setq default-directory project-root)))) + +(defun alchemist-project-open-tests-for-current-file () + "Opens the appropriate test file for the current buffer file +in a new window." + (interactive) + (let* ((filename (file-relative-name (buffer-file-name) (alchemist-project-root))) + (filename (replace-regexp-in-string "^lib/" "test/" filename)) + (filename (replace-regexp-in-string "\.ex$" "_test\.exs" filename)) + (filename (format "%s/%s" (alchemist-project-root) filename))) + (if (file-exists-p filename) + (find-file-other-window filename) + (if (y-or-n-p "No test file found; create one now?") + (alchemist-project--create-test-for-current-file + filename (current-buffer)) + (message "No test file found."))))) + +(defun alchemist-project--create-test-for-current-file (filename buffer) + "Creates and populates a test module, FILENAME, for the code in BUFFER. +The module name given to the test module is determined from the name of the +first module defined in BUFFER." + (let* ((directory-name (file-name-directory filename)) + (module-name (alchemist-project--grok-module-name buffer)) + (test-module-name (concat module-name "Test"))) + (unless (file-exists-p directory-name) + (make-directory (file-name-directory filename) t)) + (alchemist-project--insert-test-boilerplate + (find-file-other-window filename) test-module-name))) + +(defun alchemist-project--grok-module-name (buffer) + "Determines the name of the first module defined in BUFFER." + (save-excursion + (set-buffer buffer) + (goto-line 1) + (re-search-forward "defmodule\\s-\\(.+?\\)\\s-?,?\\s-do") + (match-string 1))) + +(defun alchemist-project--insert-test-boilerplate (buffer module) + "Inserts ExUnit boilerplate for MODULE in BUFFER. +Point is left in a convenient location." + (set-buffer buffer) + (insert (concat "defmodule " module " do\n" + " use ExUnit.Case\n" + "\n" + "end\n")) + (goto-char (point-min)) + (beginning-of-line 3)) + +(defun alchemist-project-find-test () + "Open project test directory and list all test files." + (interactive) + (when (alchemist-project-p) + (find-file (alchemist-project--open-directory-files "test")))) + +(defun alchemist-project--open-directory-files (directory) + (let ((directory (concat (replace-regexp-in-string "\/?$" "" (concat (alchemist-project-root) directory) "/")))) + (message directory) + (concat directory "/" (completing-read (concat directory ": ") + (mapcar (lambda (path) + (replace-regexp-in-string (concat "^" (regexp-quote directory) "/") "" path)) + (split-string + (shell-command-to-string + (concat + "find \"" directory + "\" -type f | grep \"_test\.exs\" | grep -v \"/.git/\" | grep -v \"/.yardoc/\"")))))))) + +(defun alchemist-project-name () + "Return the name of the current Elixir project." + (if (alchemist-project-p) + (car (cdr (reverse (split-string (alchemist-project-root) "/")))) + "")) + +(provide 'alchemist-project) + +;;; alchemist-project.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist-utils.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist-utils.el new file mode 100644 index 0000000..74fbdba --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist-utils.el @@ -0,0 +1,84 @@ +;;; alchemist-utils.el --- + +;; Copyright © 2014-2015 Samuel Tonini + +;; Author: Samuel Tonini . + +;;; Commentary: + +;;; Code: + +(require 'ansi-color) + +(defvar alchemist-utils--elixir-project-root-indicator + "mix.exs" + "The file which indicate an elixir project root.") + +(defun alchemist-utils--elixir-project-root () + "Finds the root directory of the project. +It walks the directory tree until it finds a elixir project root indicator." + (let* ((file (file-name-as-directory (expand-file-name default-directory)))) + (locate-dominating-file file alchemist-utils--elixir-project-root-indicator))) + +(defun alchemist-utils--flatten (alist) + (cond ((null alist) nil) + ((atom alist) (list alist)) + (t (append (alchemist-utils--flatten (car alist)) + (alchemist-utils--flatten (cdr alist)))))) + +(defun alchemist-utils--build-runner-cmdlist (command) + "Build the commands list for the runner." + (remove "" (alchemist-utils--flatten + (list (if (stringp command) + (split-string command) + command))))) + +(defun alchemist-utils--clear-search-text (search-text) + (let* ((search-text (replace-regexp-in-string "\\.$" "" search-text)) + (search-text (replace-regexp-in-string "^\\.$" "" search-text)) + (search-text (replace-regexp-in-string ",$" "" search-text)) + (search-text (replace-regexp-in-string "^,$" "" search-text))) + search-text)) + +(defun alchemist-utils--erase-buffer (buffer) + "Use `erase-buffer' inside BUFFER." + (with-current-buffer buffer + (erase-buffer))) + +(defun alchemist-utils--get-buffer-content (buffer) + "Return the content of BUFFER." + (with-current-buffer buffer + (buffer-substring (point-min) (point-max)))) + +(defun alchemist--utils-clear-ansi-sequences (string) + "Clear STRING from all ansi escape sequences." + (ansi-color-filter-apply string)) + +(defun alchemist-utils--remove-newline-at-end (string) + (replace-regexp-in-string "\n$" "" string)) + +(defun alchemist-utils--count-char-in-str (regexp str) + (loop with start = 0 + for count from 0 + while (string-match regexp str start) + do (setq start (match-end 0)) + finally return count)) + +(provide 'alchemist-utils) + +;;; alchemist-utils.el ends here diff --git a/emacs.d/elpa/alchemist-20150201.2244/alchemist.el b/emacs.d/elpa/alchemist-20150201.2244/alchemist.el new file mode 100644 index 0000000..4c488b9 --- /dev/null +++ b/emacs.d/elpa/alchemist-20150201.2244/alchemist.el @@ -0,0 +1,132 @@ +;;; alchemist.el --- Elixir tooling integration into Emacs + +;; Copyright © 2014-2015 Samuel Tonini +;; +;; Author: Samuel Tonini + +;; URL: http://www.github.com/tonini/alchemist.el +;; Version: 0.14.0-cvs +;; Package-Requires: ((emacs "24")) +;; Keywords: languages, mix, elixir, elixirc, hex + +;; This file is not part of GNU Emacs. + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Alchemist integrate Elixir's tooling into Emacs + +;;; Code: + +(defgroup alchemist nil + "Elixir Tooling Integration Into Emacs." + :prefix "alchemist-" + :group 'applications + :link '(url-link :tag "Github" "https://github.com/tonini/alchemist.el") + :link '(emacs-commentary-link :tag "Commentary" "alchemist")) + +(require 'alchemist-utils) +(require 'alchemist-project) +(require 'alchemist-buffer) +(require 'alchemist-compile) +(require 'alchemist-execute) +(require 'alchemist-mix) +(require 'alchemist-hooks) +(require 'alchemist-help) +(require 'alchemist-complete) +(require 'alchemist-message) +(require 'alchemist-iex) +(require 'alchemist-eval) +(require 'alchemist-goto) + +(eval-after-load 'company + '(progn + (require 'alchemist-company))) + +(defun alchemist-mode-hook () + "Hook which enables `alchemist-mode'" + (alchemist-mode 1)) + +(defvar alchemist--version "0.14.0-cvs") + +;;;###autoload +(defun alchemist-version (&optional show-version) + "Display Alchemist's version." + (interactive) + (message "Alchemist %s" (replace-regexp-in-string "-cvs" "snapshot" alchemist--version))) + +(defvar alchemist-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c a t") 'alchemist-mix-test) + (define-key map (kbd "C-c a m t f") 'alchemist-mix-test-file) + (define-key map (kbd "C-c a m t b") 'alchemist-mix-test-this-buffer) + (define-key map (kbd "C-c a m t .") 'alchemist-mix-test-at-point) + (define-key map (kbd "C-c a c c") 'alchemist-compile) + (define-key map (kbd "C-c a c f") 'alchemist-compile-file) + (define-key map (kbd "C-c a c b") 'alchemist-compile-this-buffer) + (define-key map (kbd "C-c a e e") 'alchemist-execute) + (define-key map (kbd "C-c a e f") 'alchemist-execute-file) + (define-key map (kbd "C-c a e b") 'alchemist-execute-this-buffer) + (define-key map (kbd "C-c a h h") 'alchemist-help) + (define-key map (kbd "C-c a h e") 'alchemist-help-search-at-point) + (define-key map (kbd "C-c a h m") 'alchemist-help-search-marked-region) + (define-key map (kbd "C-c a p f") 'alchemist-project-find-test) + (define-key map (kbd "C-c a p t") 'alchemist-project-open-tests-for-current-file) + (define-key map (kbd "C-c a i i") 'alchemist-iex-run) + (define-key map (kbd "C-c a i p") 'alchemist-iex-project-run) + (define-key map (kbd "C-c a i l") 'alchemist-iex-send-current-line) + (define-key map (kbd "C-c a i c") 'alchemist-iex-send-current-line-and-go) + (define-key map (kbd "C-c a i r") 'alchemist-iex-send-region) + (define-key map (kbd "C-c a i m") 'alchemist-iex-send-region-and-go) + (define-key map (kbd "C-c a i b") 'alchemist-iex-compile-this-buffer) + (define-key map (kbd "C-c a v l") 'alchemist-eval-current-line) + (define-key map (kbd "C-c a v k") 'alchemist-eval-print-current-line) + (define-key map (kbd "C-c a v j") 'alchemist-eval-quoted-current-line) + (define-key map (kbd "C-c a v h") 'alchemist-eval-print-quoted-current-line) + (define-key map (kbd "C-c a v o") 'alchemist-eval-region) + (define-key map (kbd "C-c a v i") 'alchemist-eval-print-region) + (define-key map (kbd "C-c a v u") 'alchemist-eval-quoted-region) + (define-key map (kbd "C-c a v y") 'alchemist-eval-print-quoted-region) + (define-key map (kbd "C-c a v q") 'alchemist-eval-buffer) + (define-key map (kbd "C-c a v w") 'alchemist-eval-print-buffer) + (define-key map (kbd "C-c a v e") 'alchemist-eval-quoted-buffer) + (define-key map (kbd "C-c a v r") 'alchemist-eval-print-quoted-buffer) + (define-key map (kbd "M-.") 'alchemist-goto-definition-at-point) + (define-key map (kbd "M-,") 'alchemist-goto-jump-back) + map) + "The keymap used when `alchemist-mode' is active.") + +;;;###autoload +(define-minor-mode alchemist-mode + "Toggle alchemist mode. + +Key bindings: +\\{alchemist-mode-map}" + nil + ;; The indicator for the mode line. + " alchemist" + :group 'alchemist + :global nil + :keymap 'alchemist-mode-map + (cond (alchemist-mode + (alchemist-buffer-initialize-modeline)) + (t + (alchemist-buffer-reset-modeline)))) + +(add-hook 'elixir-mode-hook 'alchemist-mode-hook) + +(provide 'alchemist) + +;;; alchemist.el ends here diff --git a/emacs.d/elpa/bison-mode-20141119.43/bison-mode-autoloads.el b/emacs.d/elpa/bison-mode-20141119.43/bison-mode-autoloads.el new file mode 100644 index 0000000..042d4d0 --- /dev/null +++ b/emacs.d/elpa/bison-mode-20141119.43/bison-mode-autoloads.el @@ -0,0 +1,28 @@ +;;; bison-mode-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "bison-mode" "bison-mode.el" (21739 37356 0 +;;;;;; 0)) +;;; Generated autoloads from bison-mode.el + +(add-to-list 'auto-mode-alist '("\\.y\\'" . bison-mode)) + +(add-to-list 'auto-mode-alist '("\\.l\\'" . bison-mode)) + +(add-to-list 'auto-mode-alist '("\\.jison\\'" . jison-mode)) + +(autoload 'bison-mode "bison-mode" "\ +Major mode for editing bison/yacc files. + +\(fn)" t nil) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; bison-mode-autoloads.el ends here diff --git a/emacs.d/elpa/bison-mode-20141119.43/bison-mode-pkg.el b/emacs.d/elpa/bison-mode-20141119.43/bison-mode-pkg.el new file mode 100644 index 0000000..b867662 --- /dev/null +++ b/emacs.d/elpa/bison-mode-20141119.43/bison-mode-pkg.el @@ -0,0 +1 @@ +(define-package "bison-mode" "20141119.43" "Major mode for editing bison, yacc and lex files." 'nil :keywords '("bison-mode" "yacc-mode")) diff --git a/emacs.d/elpa/bison-mode-20141119.43/bison-mode.el b/emacs.d/elpa/bison-mode-20141119.43/bison-mode.el new file mode 100644 index 0000000..664c0fe --- /dev/null +++ b/emacs.d/elpa/bison-mode-20141119.43/bison-mode.el @@ -0,0 +1,908 @@ +;;; bison-mode.el --- Major mode for editing bison, yacc and lex files. + +;; Copyright (C) 1998 Eric Beuscher +;; +;; Author: Eric Beuscher +;; Created: 2 Feb 1998 +;; Version: 20141119.43 +;; X-Original-Version: 0.2 +;; Keywords: bison-mode, yacc-mode + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;;; Commentary: + +;;;; I wrote this since I saw one mode for yacc files out there roaming the +;;;; world. I was daunted by the fact the it was written in 1990, and Emacs +;;;; has evolved so much since then (this I assume based on its evolution since +;;;; i started using it). So I figured if i wanted one, I should make it +;;;; myself. Please excuse idiosyncrasies, as this was my first major mode +;;;; of this kind. The indentation code may be a bit weird, I am not sure, +;;;; it was my first go at doing Emacs indentation, so I look at how other +;;;; modes did it, but then basically did what I thought was right + +;;;; I hope this is useful to other hackers, and happy Bison/Yacc hacking +;;;; If you have ideas/suggestions/problems with this code, I can be reached at +;;;; beuscher@eecs.tulane.edu + +;;;; Eric --- Sat Mar 7 1:40:20 CDT 1998 + +;;;; Bison Sections: +;;;; there are five sections to a bison file (if you include the area above the +;;;; C declarations section. most everything in this file either does +;;;; actions based on which section you are deemed to be in, or based on an +;;;; assumption that the function will only be called from certain sections. +;;;; the function `bison--section-p' is the section parser + +;;;; Indentation: +;;;; indentations are done based on the section of code you are in. there is +;;;; a procedure `bison--within-braced-c-expression-p' that checks for being in +;;;; C code. if you are within c-code, indentations should occur based on +;;;; how you have your C indentation set up. i am pretty sure this is the +;;;; case. +;;;; there are four variables, which control bison indentation within either +;;;; the bison declarations section or the bison grammar section +;;;; `bison-rule-separator-column' +;;;; `bison-rule-separator-column' +;;;; `bison-decl-type-column' +;;;; `bison-decl-token-column' + +;;;; flaw: indentation works on a per-line basis, unless within braced C sexp, +;;;; i should fix this someday +;;;; and to make matters worse, i never took out c-indent-region, so that is +;;;; still the state of the `indent-region-function' variable + +;;;; Electricity: +;;;; by default, there are electric -colon, -pipe, -open-brace, -close-brace, +;;;; -semicolon, -percent, -less-than, -greater-than +;;;; the indentation caused by these work closely with the 4 indentation +;;;; variables mentioned above. +;;;; any of these can be turned off individually by setting the appropriate +;;;; `bison-electric-...' variable. or all of them can be turned off by +;;;; setting `bison-all-electricity-off' + +;;;; todo: should make available a way to use C-electricity if in C sexps + +;;; Code: + +(require 'cc-mode) + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.y\\'" . bison-mode)) +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.l\\'" . bison-mode)) +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.jison\\'" . jison-mode)) + +;; *************** internal vars *************** + +(defvar bison--declarers '("%union" "%token" "%type" + "%left" "%right" "%nonassoc") + "commands which can declare a token or state type") + +(defvar bison--word-constituent-re "\\(\\sw\\|_\\)") +(defvar bison--production-re + (concat "^" bison--word-constituent-re "+:")) + +(defvar bison--pre-c-decls-section 0 + "section before c-declarations-section, if that section exists") +(defvar bison--c-decls-section 1 + "section denoted by %{ and $} for c-declarations at the top of a bison file") +(defvar bison--bison-decls-section 2 + "section before the rules section") +(defvar bison--grammar-rules-section 3 + "section delimited by %%'s where productions and rules are enumerated") +(defvar bison--c-code-section 4 + "section after the second %% where c-code can be placed") + +(defvar bison--c-decls-section-opener "%{") +(defvar bison--c-decls-section-closer "%}") +(defvar bison--grammar-rules-section-delimeter "%%") + + +;; *************** user-definable vars *************** + +(defvar bison-rule-separator-column 8 + "column for rule and production separators \"|\" and \";\"") +(defvar bison-rule-enumeration-column 16 + "column for beginning enumeration of a production's rules") +(defvar bison-decl-type-column 8 + "columnn in which tokens' and states' types should be when declared") +(defvar bison-decl-token-column 24 + "column in which tokens and states are listed when declared, +as with %token, %type, ...") + + +(defvar bison-all-electricity-off nil + "non-nil means all electric keys will be disabled, +nil means that a bison-electric-* key will be on or off based on the individual +key's electric variable") + +;;; i know lisp has the dual name spaces, but i find it more aesthetically +;;; pleasing to not take advantage of that +(defvar bison-electric-colon-v t + "non-nil means use an electric colon") +(defvar bison-electric-pipe-v t + "non-nil means use an electric pipe") +(defvar bison-electric-open-brace-v t + "non-nil means use an electric open-brace") +(defvar bison-electric-close-brace-v t + "non-nil means use an electric close-brace") +(defvar bison-electric-semicolon-v t + "non-nil means use an electric semicolon") +(defvar bison-electric-percent-v t + "non-nil means use an electric percent") +(defvar bison-electric-less-than-v t + "non-nil means use an electric less-than") +(defvar bison-electric-greater-than-v t + "non-nil means use an electric greater-than") + + +(defconst bison-font-lock-keywords + (append + (list + (cons (concat "^\\(" (regexp-opt bison--declarers) "\\)") + '(1 font-lock-keyword-face)) + ) + c-font-lock-keywords) + "Default expressions to highlight in Bison mode") + +;; *************** utilities *************** + +(defun just-no-space () + "Delete all spaces and tabs around point, leaving no spaces." + (interactive "*") + (skip-chars-backward " \t") + (delete-region (point) (progn (skip-chars-forward " \t") (point))) + t) + +(defun previous-white-space-p () + "return t if there is whitespace between the beginning of the line and the +current (point)" + (save-excursion + (let ((current-point (point))) + (beginning-of-line) + (if (re-search-forward "\\s " current-point t) + t + nil)))) + +(defun previous-non-ws-p () + "return t if there are non-whitespace characters between beginning of line +and \(point\)" + (save-excursion + (let ((current-point (point))) + (beginning-of-line) + (re-search-forward "[^ \t]" current-point t) + ))) + +(defun following-non-ws-p () + "return t if there are non-whitespace characters on the line" + (save-excursion + (let ((current-point (point))) + (end-of-line) + (re-search-backward "[^ \t]+" current-point t) + ))) + +(defun line-of-whitespace-p () + "return t if the line consists of nothiing but whitespace, nil otherwise" + (save-excursion + (let ((eol (progn (end-of-line) (point)))) + (beginning-of-line) ;; should already be there anyway + (not (re-search-forward "[^ \t\n]" eol t))))) + +;; *************** bison-mode *************** + +;;;###autoload +(define-derived-mode bison-mode c-mode "Bison" + "Major mode for editing bison/yacc files." + + ;; try to set the indentation correctly + (setq c-basic-offset 4) + + (c-set-offset 'knr-argdecl-intro 0) + + ;; remove auto and hungry anything + (c-toggle-auto-hungry-state -1) + (c-toggle-auto-newline -1) + (c-toggle-hungry-state -1) + + (use-local-map bison-mode-map) + + (define-key bison-mode-map ":" 'bison-electric-colon) + (define-key bison-mode-map "|" 'bison-electric-pipe) + (define-key bison-mode-map "{" 'bison-electric-open-brace) + (define-key bison-mode-map "}" 'bison-electric-close-brace) + (define-key bison-mode-map ";" 'bison-electric-semicolon) + (define-key bison-mode-map "%" 'bison-electric-percent) + (define-key bison-mode-map "<" 'bison-electric-less-than) + (define-key bison-mode-map ">" 'bison-electric-greater-than) + + (define-key bison-mode-map [tab] 'bison-indent-line) + + (make-local-variable 'indent-line-function) + (setq indent-line-function 'bison-indent-new-line) + (make-local-variable 'comment-start) + (make-local-variable 'comment-end) + (setq comment-start "/*" + comment-end "*/") + (make-local-variable 'font-lock-keywords) + (setq font-lock-keywords nil) + (set (make-local-variable 'font-lock-defaults) '(bison-font-lock-keywords))) + + +;; *************** section parsers *************** + +(defun bison--section-p () + "Return the section that user is currently in" + (save-excursion + (let ((bound (point))) + (goto-char (point-min)) + (bison--section-p-helper bound)))) + +(defun bison--section-p-helper (bound) + (if (re-search-forward + (concat "^" bison--c-decls-section-opener) + bound t) + (if (re-search-forward + (concat "^" bison--c-decls-section-closer) + bound t) + (if (re-search-forward + (concat "^" bison--grammar-rules-section-delimeter) + bound t) + (if (re-search-forward + (concat "^" bison--grammar-rules-section-delimeter) + bound t) + bison--c-code-section + bison--grammar-rules-section) + bison--bison-decls-section) + bison--c-decls-section) + (if (re-search-forward + (concat "^" bison--grammar-rules-section-delimeter) + bound t) + (if (re-search-forward + (concat "^" bison--grammar-rules-section-delimeter) + bound t) + bison--c-code-section + bison--grammar-rules-section) + (if (re-search-forward + (concat "^" bison--c-decls-section-opener) + nil t) + bison--pre-c-decls-section + (if (re-search-forward + (concat "^" bison--grammar-rules-section-delimeter) + nil t) + bison--bison-decls-section + bison--pre-c-decls-section))))) + + +;; *************** syntax parsers *************** + +(defun bison--production-p () + "return t if the \(point\) rests immediately after a production" + (save-excursion + (let ((current-point (point))) + (beginning-of-line) + (let ((position (re-search-forward + bison--production-re current-point t))) + (and position + (not (previous-white-space-p)) + (= position current-point)))))) + +(defun bison--find-production-opener () + "return and goto the point of the nearest production opener above \(point\)" + (re-search-backward bison--production-re nil t)) + + +(defun bison--find-next-production () + "return the position of the beginning of the next production, +or nil if there isnt one" + (save-excursion + (if (re-search-forward bison--production-re nil t) + (progn + (beginning-of-line) + (point)) + nil))) + +(defun bison--find-grammar-end () + "return the position of the end of the grammar rules (assuming we are within +the grammar rules section), or nil if there isnt one" + (save-excursion + (if (re-search-forward + (concat "^" bison--grammar-rules-section-delimeter) + nil t) + (progn + (beginning-of-line) + (point)) + nil))) + +(defun bison--find-grammar-begin () + "return the position of the beginning of the grammar rules (assuming we are +within the grammar rules section), or nil if there isnt one" + (save-excursion + (if (re-search-backward + (concat "^" bison--grammar-rules-section-delimeter) + nil t) + (point) + nil))) + +(defun bison--within-started-production-p () + "is used by bison-electric-* functions to determine actions +return t if within a production, nil if not + +a point is within a production if there is some non whitespace text before +either the beginnings of another production or the end of the grammar rules" + (save-excursion + (let ((bound (cond ((bison--find-next-production)) + ((bison--find-grammar-end)) + (t nil)))) + (if bound + (let ((sval (re-search-forward + (concat "\\(\\s \\|" ;; whitespace or + ;; comments + (regexp-quote comment-start) + "\\(.\\|\n\\)*" ;; comment body + (regexp-quote comment-end) + "\\)+") ;; end or + bound t))) + (if sval + (not (= sval bound)) + nil)) + nil)))) + +(defun bison--within-some-sexp-p (starter ender) + "return t if the \(point\) is within the sexp marked by the re's STARTER and +ENDER" + (save-excursion + (let ((current-point (point))) + (if (re-search-backward starter nil t) ;; find nearest starter + ;; look for ender, if found, then not within sexp + (progn + (goto-char (match-end 0)) + (not (re-search-forward ender current-point t))))))) + +(defun bison--within-c-comment-p () + "return t if the point is within a c comment delimited by \"/*\" \"*/\"" + (bison--within-some-sexp-p (regexp-quote comment-start) + (regexp-quote comment-end))) + + +(defun bison--within-string-p (&optional point) + " +start from the beginning of the buffer and toggle state as un-escaped \"'s are +found." + (let ((point (or point (point))) + (in-p nil)) + (save-excursion + (goto-char (point-min)) + + (while (re-search-forward "[^\\]\"" point t) + (setq in-p (not in-p))) + + in-p))) + +;;; bison--within-braced-c-expression-p +;;; new and improved, no more recursion, does not break when literal strings +;;; contain un-matched braces +(defun bison--within-braced-c-expression-p (section) + "return t if the point is within an sexp delimited by braces \({,}\) +" + (save-excursion + (bison--within-braced-c-expression-p-h section (point)))) + +(defun bison--within-braced-c-expression-p-h (section low-pt) + " +Notes: +save excursion is done higher up, so i dont concern myself here. +" + (cond ((= section bison--pre-c-decls-section) nil) + ((= section bison--c-decls-section) + (let ((opener (save-excursion (search-backward "%{")))) + (bison--within-braced-c-expression-p-h-h opener low-pt))) + ((= section bison--bison-decls-section) + (let ((opener (save-excursion + (or (search-backward "%}" nil t) + (point-min))))) + (bison--within-braced-c-expression-p-h-h opener low-pt))) + ((= section bison--grammar-rules-section) + (let ((opener (save-excursion (bison--find-production-opener)))) + (if opener + (bison--within-braced-c-expression-p-h-h opener low-pt) + nil))) + ((= section bison--c-code-section) + t))) + +(defun bison--within-braced-c-expression-p-h-h (high-pt low-pt) + " +Notes: +HIGH-PT goes toward (point-min), LOW-PT goes toward (point-max) +save excursion is done higher up, so i dont concern myself here. +" + (let ((pt (point))) + (let ((success nil) (count 1) (done nil)) + ;; loop until open brace found, that is not in comment or string literal + (while (and (not done) + (re-search-backward "[^%]{" high-pt t count)) ;find nearest + ;starter + (goto-char (match-end 0)) + (if (or (bison--within-c-comment-p) + (bison--within-string-p)) + + (setq count (+ count 1)) + (progn + (setq success t) + (setq done t)))) + + (if success + (let ((end-pt + (condition-case nil + (progn + (backward-char) + (forward-sexp) + (point)) + (error nil)))) + (if end-pt + (if (> end-pt low-pt) + t ; then in braced-c-exp + nil) + t)) ; if no sexp close brace, then w/in + nil)))) + + +(defun bison--bison-decl-opener-p (bol eol) + "return t if the current line is a bison declaration starter +\(i.e. has a %type, %token, %right, ...\)" + (save-excursion + (goto-char bol) + (re-search-forward + (concat "^" (regexp-opt (copy-sequence bison--declarers))) eol t))) + +(defun bison--production-opener-p (bol eol) + "return t if the current line is a line that introduces a new production" + (save-excursion + (goto-char bol) + (re-search-forward bison--production-re eol t))) + +(defun bison--find-bison-semicolon () + "return the position of next semicolon not within braces, nil otherwise" + (save-excursion + (if (search-forward ";" nil t) + (if (not (bison--within-braced-c-expression-p (bison--section-p))) + (point) + (bison--find-bison-semicolon)) + nil))) + +(defun bison--within-production-body-p (section) + "return t if the \(point\) is within the body of a production + +this procedure will fail if it is in a production header" + (save-excursion + (if (= section bison--grammar-rules-section) + (let ((current-point (point))) + (if (re-search-backward bison--production-re nil t) + t + nil)) + nil))) + +(defun bison--production-alternative-p (bol eol section) + "return t if the current line contains a \"|\" used to designate a rule +alternative" + (save-excursion + (goto-char bol) + (if (search-forward "|" eol t) + (not (bison--within-braced-c-expression-p section)) + nil))) + + +;; *************** indent functions *************** + +(defun bison--handle-indent-c-sexp (section indent-column bol) + (let* ((o-brace (re-search-backward "[^%]{" bol t)) + ) + (if o-brace + (if (save-excursion + (goto-char o-brace) + (bison--within-braced-c-expression-p section)) + (c-indent-line) + (if (= (current-indentation) o-brace) ;; if o-brace is first char + (if (not (= o-brace indent-column)) ;; but not in right spot + (progn + (back-to-indentation) + (just-no-space) + (indent-to-column indent-column)) + ;; else all is good + ) + ;; else, non-ws before o-brace, leave it alone + )) + (c-indent-line)))) + +(defun bison-indent-new-line (&optional c-sexp) + "Indent a fresh line of bison code + +assumes indenting a new line, i.e. at column 0 +" + (interactive) + + (let* ((section (bison--section-p)) + (c-sexp (or c-sexp (bison--within-braced-c-expression-p section))) + ) + (cond + (c-sexp + (cond + ((= section bison--grammar-rules-section) + (c-indent-line + (save-excursion + (forward-line -1) + (let ((bol (save-excursion (beginning-of-line) (point))) + (eol (save-excursion (end-of-line) (point)))) + (if (bison--production-opener-p bol eol) + (list + (cons 'defun-block-intro + (progn + (re-search-forward bison--production-re) ; SIGERR + (- (re-search-forward "[^ \t]") ; SIGERR + 1)))) + nil))))) + (t (c-indent-line)))) + ((= section bison--pre-c-decls-section) + (c-indent-line)) + ((= section bison--bison-decls-section) + (indent-to-column bison-decl-token-column)) + ((= section bison--grammar-rules-section) + (indent-to-column + (save-excursion + (let* ((bound (or (save-excursion (bison--find-production-opener)) + (bison--find-grammar-begin))) + (prev-semi (search-backward ";" bound t)) + ) + (if prev-semi + (if (bison--within-braced-c-expression-p section) ; CRACK + bison-rule-enumeration-column + 0) + (if (save-excursion (bison--find-production-opener)) + bison-rule-enumeration-column + 0)))))) + ((= section bison--c-code-section)) ;;leave-alone + ))) + +(defun bison-indent-line () + "Indent a line of bison code." + (interactive) + + (let* ((pos (- (point-max) (point))) + (reset-pt (function (lambda () + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos)))))) + (bol (save-excursion (beginning-of-line) (point))) + (eol (save-excursion (end-of-line) (point))) + ) + (let* ((section (bison--section-p)) + (c-sexp (bison--within-braced-c-expression-p section)) + (ws-line (line-of-whitespace-p)) + ) + (cond + ;; if you are a line of whitespace, let indent-new-line take care of it + (ws-line + (bison-indent-new-line c-sexp)) + + ((= section bison--pre-c-decls-section) + ;; leave things alone + ) + + ((= section bison--c-decls-section) + (if c-sexp + (bison--handle-indent-c-sexp section 0 bol) + (if (not (= (current-indentation) 0)) + (progn + (back-to-indentation) + (just-no-space) + (funcall reset-pt))))) + + ((= section bison--bison-decls-section) + (let ((opener (bison--bison-decl-opener-p bol eol))) + (cond + (opener + (goto-char opener) + (skip-chars-forward " \t" eol) + (if (looking-at "{") + (save-excursion + (if (following-non-ws-p) + (progn + (forward-char 1) + (just-no-space) + (newline) + (bison-indent-new-line t)))) + (let ((complete-type t)) + (if (looking-at "<") + (progn + (setq complete-type nil) + (if (not (= (current-column) bison-decl-type-column)) + (progn + (just-no-space) + (indent-to-column bison-decl-type-column)) + (and (re-search-forward + (concat "<" bison--word-constituent-re "+>") + eol t) + (setq complete-type t))))) + (and complete-type + (skip-chars-forward " \t" eol) + (looking-at + (concat "\\(" bison--word-constituent-re "\\|'\\)")) + (if (not (= (current-column) bison-decl-token-column)) + (progn + (just-no-space) + (indent-to-column bison-decl-token-column)))))) + (funcall reset-pt)) + (c-sexp + (bison--handle-indent-c-sexp section 0 bol)) + (t + (back-to-indentation) + ;; only tab in names, leave comments alone + (cond (;; put word-constiuents in bison-decl-token-column + (looking-at bison--word-constituent-re) + (if (not (= (current-column) bison-decl-token-column)) + (progn + (just-no-space) + (indent-to-column bison-decl-token-column)))) + ;; put/keep close-brace in the 0 column + ((looking-at "}") + (if (not (= (current-column) 0)) + (just-no-space))) + ;; leave comments alone + ((looking-at (regexp-quote comment-start)) nil) + ;; else do nothing + ) + (funcall reset-pt))))) + ((= section bison--grammar-rules-section) + (cond + ((bison--production-opener-p bol eol) + (beginning-of-line) + (re-search-forward bison--production-re);; SIGERR + (if (following-non-ws-p) + (if (> (current-column) bison-rule-enumeration-column) + (progn + (just-no-space) + (newline) + (indent-to-column bison-rule-enumeration-column)) + (save-excursion + (re-search-forward bison--word-constituent-re);; SIGERR + (let ((col (current-column))) + (cond ((> col (+ 1 bison-rule-enumeration-column)) + (forward-char -1) + (just-no-space) + (indent-to-column bison-rule-enumeration-column)) + ((< col (+ 1 bison-rule-enumeration-column)) + (forward-char -1) + (indent-to-column + bison-rule-enumeration-column))))))) + (funcall reset-pt)) + ((bison--production-alternative-p bol eol section) + (back-to-indentation);; should put point on "|" + (if (not (= (current-column) bison-rule-separator-column)) + (progn + (just-no-space) + (indent-to-column bison-rule-separator-column))) + (forward-char 1) + (if (following-non-ws-p) + (save-excursion + (re-search-forward bison--word-constituent-re);; SIGERR + (let ((col (current-column))) + (cond ((> col (+ 1 bison-rule-enumeration-column)) + (forward-char -1) + (just-no-space) + (indent-to-column bison-rule-enumeration-column)) + ((< col (+ 1 bison-rule-enumeration-column)) + (forward-char -1) + (indent-to-column + bison-rule-enumeration-column)))))) + (funcall reset-pt)) + (c-sexp + (bison--handle-indent-c-sexp + section bison-rule-enumeration-column bol) + (funcall reset-pt)) + ((bison--within-production-body-p section) + (back-to-indentation) + (if (not (= (current-column) bison-rule-enumeration-column)) + (progn + (just-no-space) + (indent-to-column + bison-rule-enumeration-column))) + (funcall reset-pt)) + (t + (let ((cur-ind (current-indentation))) + (if (eq (save-excursion (search-backward "}" bol t)) + cur-ind) + (if (not (= cur-ind bison-rule-enumeration-column)) + (progn + (back-to-indentation) + (just-no-space) + (indent-to-column bison-rule-enumeration-column) + (funcall reset-pt))) + ;; else leave alone + ))))) + ((= section bison--c-code-section) + (c-indent-line)) + )))) + +;; *************** electric-functions *************** + +(defun bison-electric-colon (arg) + "If the colon <:> delineates a production, + then insert a semicolon on the next line in the BISON-RULE-SEPARATOR-COLUMN, + put the cursor in the BISON-RULE-ENUMERATION-COLUMN for the beginning + of the rule + else just run self-insert-command +A colon delineates a production by the fact that it is immediately preceded by +a word(alphanumerics or '_''s), and there is no previous white space. +" + (interactive "P") + + (self-insert-command (prefix-numeric-value arg)) + (if (and bison-electric-colon-v + (not bison-all-electricity-off)) + (if (and (= bison--grammar-rules-section (bison--section-p)) + (bison--production-p) + (not (bison--within-started-production-p))) + (progn + (save-excursion ; put in a closing semicolon + (newline) + (indent-to-column bison-rule-separator-column) + (insert ";")) + (save-excursion ; remove opening whitespace + (if (re-search-backward + "\\s " + (save-excursion (beginning-of-line) (point)) + t) + (just-no-space))) + (if (not (< (current-column) bison-rule-enumeration-column)) + (newline)) + (indent-to-column bison-rule-enumeration-column))))) + +(defun bison-electric-pipe (arg) + "If the pipe <|> is used as a rule separator within a production, + then move it into BISON-RULE-SEPARATOR-COLUMN + indent to BISON-RULE-ENUMERATION-COLUMN on the same line + else just run self-insert-command +" + (interactive "P") + + (if (and bison-electric-pipe-v + (not bison-all-electricity-off) + (= bison--grammar-rules-section (bison--section-p)) + (line-of-whitespace-p) + ) + (progn + (beginning-of-line) + (just-no-space) + (indent-to-column bison-rule-separator-column) + (self-insert-command (prefix-numeric-value arg)) + (indent-to-column bison-rule-enumeration-column) + ) + (self-insert-command (prefix-numeric-value arg)))) + +(defun bison-electric-open-brace (arg) + "used for the opening brace of a C action definition for production rules, +if there is only whitespace before \(point\), then put open-brace in +bison-rule-enumeration-column" + (interactive "P") + + (if (and bison-electric-open-brace-v + (not bison-all-electricity-off)) + (let ((section (bison--section-p))) + (cond ((and (= section bison--grammar-rules-section) + (not (bison--within-braced-c-expression-p section)) + (not (previous-non-ws-p))) + (if (not (= (current-column) bison-rule-enumeration-column)) + (progn + (just-no-space) + (indent-to-column bison-rule-enumeration-column)))) + ((and (= section bison--bison-decls-section) + (not (bison--within-braced-c-expression-p section)) + (not (previous-non-ws-p))) + (if (not (= (current-column) 0)) + (progn + (just-no-space) + (indent-to-column 0))))))) + + (self-insert-command (prefix-numeric-value arg))) + + +(defun bison-electric-close-brace (arg) + "If the close-brace \"}\" is used as the c-declarations section closer +in \"%}\", then make sure the \"%}\" indents to the beginning of the line" + (interactive "P") + + (self-insert-command (prefix-numeric-value arg)) + + (if (and bison-electric-close-brace-v + (not bison-all-electricity-off)) + (cond ((search-backward "%}" (- (point) 2) t) + (if (= (bison--section-p) bison--c-decls-section) + (progn + (just-no-space) + (forward-char 2)) ; for "%}" + (forward-char 1))) + ))) + +(defun bison-electric-semicolon (arg) + "if the semicolon is used to end a production, then place it in +bison-rule-separator-column + +a semicolon is deemed to be used for ending a production if it is not found +within braces + +this is just self-insert-command as i have yet to write the actual +bison-electric-semicolon function yet +" + (interactive "P") + + (self-insert-command (prefix-numeric-value arg))) + +(defun bison-electric-percent (arg) + "If the percent is a declarer in the bison declaration's section, +then put it in the 0 column." + (interactive "P") + + (if (and bison-electric-percent-v + (not bison-all-electricity-off)) + (let ((section (bison--section-p))) + (if (and (= section bison--bison-decls-section) + (not (bison--within-braced-c-expression-p section)) + (not (previous-non-ws-p)) + (not (= (current-column) 0))) + (just-no-space)))) + + (self-insert-command (prefix-numeric-value arg))) + +(defun bison-electric-less-than (arg) + "If the less-than is a type declarer opener for tokens in the bison +declaration section, then put it in the bison-decl-type-column column." + (interactive "P") + + (if (and bison-electric-less-than-v + (not bison-all-electricity-off)) + (if (and (= (bison--section-p) bison--bison-decls-section) + (bison--bison-decl-opener-p + (save-excursion (beginning-of-line) (point)) + (point))) + (progn + (just-no-space) + (indent-to-column bison-decl-type-column)))) + + (self-insert-command (prefix-numeric-value arg))) + +(defun bison-electric-greater-than (arg) + "If the greater-than is a type declarer closer for tokens in the bison +declaration section, then indent to bison-decl-token-column." + (interactive "P") + + (self-insert-command (prefix-numeric-value arg)) + + (if (and bison-electric-greater-than-v + (not bison-all-electricity-off)) + (let ((current-pt (point)) + (bol (save-excursion (beginning-of-line) (point)))) + (if (and (= (bison--section-p) bison--bison-decls-section) + (bison--bison-decl-opener-p bol (point))) + (if (search-backward "<" bol t) + (if (re-search-forward + (concat "<" bison--word-constituent-re "+>") + current-pt t) + (if (not (following-non-ws-p)) + (progn + (just-no-space) + (indent-to-column bison-decl-token-column))))))))) + +(define-derived-mode jison-mode bison-mode + "Major mode for editing jison files.") + +(provide 'bison-mode) +(provide 'jison-mode) +;;; bison-mode.el ends here diff --git a/emacs.d/elpa/elixir-mode-20150103.439/elixir-deprecated.el b/emacs.d/elpa/elixir-mode-20150103.439/elixir-deprecated.el new file mode 100644 index 0000000..1767ff2 --- /dev/null +++ b/emacs.d/elpa/elixir-mode-20150103.439/elixir-deprecated.el @@ -0,0 +1,63 @@ +;;; elixir-deprecated.el --- Functionality to display deprecated messages + +;; Copyright 2011-2014 secondplanet +;; 2013-2014 Matt DeBoard, Samuel Tonini, Andreas Fuchs +;; Authors: Humza Yaqoob, +;; Andreas Fuchs , +;; Matt DeBoard +;; Samuel Tonini + +;; This file is not part of GNU Emacs. + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Functionality to display deprecated messages + +;;; Code: + +(defface elixir-deprecated--warning-face + '((t (:inherit font-lock-variable-name-face :bold t :foreground "red"))) + "" + :group 'elixir-deprecated) + +(defface elixir-deprecated--function-face + '((t (:inherit font-lock-variable-name-face :bold t :foreground "green"))) + "" + :group 'elixir-deprecated) + +(defun elixir-deprecated--warning (function-name message) + (let ((buffer (get-buffer "*Warnings*"))) + (when buffer + (kill-buffer buffer)) + (display-warning :deprecated + (concat "\n\n" + (propertize "DEPRECATION WARNING: " + 'face 'elixir-deprecated--warning-face) + (propertize (format "[ %s ]\n\n" function-name) + 'face 'elixir-deprecated--function-face) + message)) :warning)) + +;; DEPRECATED MESSAGES FOR RELEASE 3.0.0 + +(defvar elixir-deprecated--alchemist-message "This function will be removed in version 3.0.0.\n +Please use the package *alchemist.el* for compilation functionality.\n +Alchemist: http://www.github.com/tonini/alchemist.el") + +(defun elixir-deprecated-use-alchemist (function-name) + (elixir-deprecated--warning function-name + elixir-deprecated--alchemist-message)) + +(provide 'elixir-deprecated) diff --git a/emacs.d/elpa/elixir-mode-20150103.439/elixir-mode-autoloads.el b/emacs.d/elpa/elixir-mode-20150103.439/elixir-mode-autoloads.el new file mode 100644 index 0000000..19965df --- /dev/null +++ b/emacs.d/elpa/elixir-mode-20150103.439/elixir-mode-autoloads.el @@ -0,0 +1,76 @@ +;;; elixir-mode-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "elixir-mode" "elixir-mode.el" (21721 24856 +;;;;;; 0 0)) +;;; Generated autoloads from elixir-mode.el + +(autoload 'elixir-mode-iex "elixir-mode" "\ +Elixir mode interactive REPL. +Optional argument ARGS-P . + +\(fn &optional ARGS-P)" t nil) + +(autoload 'elixir-mode-open-modegithub "elixir-mode" "\ +Elixir mode open GitHub page. + +\(fn)" t nil) + +(autoload 'elixir-mode-open-elixir-home "elixir-mode" "\ +Elixir mode go to language home. + +\(fn)" t nil) + +(autoload 'elixir-mode-open-docs-master "elixir-mode" "\ +Elixir mode go to master documentation. + +\(fn)" t nil) + +(autoload 'elixir-mode-open-docs-stable "elixir-mode" "\ +Elixir mode go to stable documentation. + +\(fn)" t nil) + +(autoload 'elixir-mode-version "elixir-mode" "\ +Get the Elixir-Mode version as string. + +If called interactively or if SHOW-VERSION is non-nil, show the +version in the echo area and the messages buffer. + +The returned string includes both, the version from package.el +and the library version, if both a present and different. + +If the version number could not be determined, signal an error, +if called interactively, or if SHOW-VERSION is non-nil, otherwise +just return nil. + +\(fn &optional SHOW-VERSION)" t nil) + +(autoload 'elixir-mode "elixir-mode" "\ +Major mode for editing Elixir code. + +\\{elixir-mode-map} + +\(fn)" t nil) + +(add-to-list 'auto-mode-alist '("\\.elixir\\'" . elixir-mode)) + +(add-to-list 'auto-mode-alist '("\\.ex\\'" . elixir-mode)) + +(add-to-list 'auto-mode-alist '("\\.exs\\'" . elixir-mode)) + +;;;*** + +;;;### (autoloads nil nil ("elixir-deprecated.el" "elixir-mode-pkg.el" +;;;;;; "elixir-smie.el") (21721 24856 873809 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; elixir-mode-autoloads.el ends here diff --git a/emacs.d/elpa/elixir-mode-20150103.439/elixir-mode-pkg.el b/emacs.d/elpa/elixir-mode-20150103.439/elixir-mode-pkg.el new file mode 100644 index 0000000..419b178 --- /dev/null +++ b/emacs.d/elpa/elixir-mode-20150103.439/elixir-mode-pkg.el @@ -0,0 +1,5 @@ +(define-package "elixir-mode" "20150103.439" "Major mode for editing Elixir files" 'nil :url "https://github.com/elixir-lang/emacs-elixir" :keywords + '("languages" "elixir")) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs.d/elpa/elixir-mode-20150103.439/elixir-mode.el b/emacs.d/elpa/elixir-mode-20150103.439/elixir-mode.el new file mode 100644 index 0000000..8e81fa6 --- /dev/null +++ b/emacs.d/elpa/elixir-mode-20150103.439/elixir-mode.el @@ -0,0 +1,608 @@ +;;; elixir-mode.el --- Major mode for editing Elixir files + +;; Copyright 2011-2015 secondplanet +;; 2013-2015 Samuel Tonini, Matt DeBoard, Andreas Fuchs +;; Authors: Humza Yaqoob, +;; Andreas Fuchs , +;; Matt DeBoard +;; Samuel Tonini + +;; URL: https://github.com/elixir-lang/emacs-elixir +;; Created: Mon Nov 7 2011 +;; Keywords: languages elixir +;; Version: 2.3.0-cvs + +;; This file is not a part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +;;; Commentary: + +;; Provides font-locking, indentation and navigation support +;; for the Elixir programming language. + +;;; Code: + +(require 'comint) ; for interactive REPL +(require 'easymenu) ; for menubar features + +(require 'elixir-smie) ; syntax and indentation support +(require 'elixir-deprecated) ; deprecated messages + +(defgroup elixir-mode nil + "Provides font-locking, indentation and navigation support +for the Elixir programming language." + :prefix "elixir-mode-" + :group 'applications + :link '(url-link :tag "Github" "https://github.com/elixir-lang/emacs-elixir") + :link '(emacs-commentary-link :tag "Commentary" "elixir-mode")) + +(defvar elixir-mqode--website-url + "http://elixir-lang.org") + +(defvar elixir-mode-hook nil) + +(defvar elixir-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c ,r") 'elixir-mode-eval-on-region) + (define-key map (kbd "C-c ,c") 'elixir-mode-eval-on-current-line) + (define-key map (kbd "C-c ,b") 'elixir-mode-eval-on-current-buffer) + (define-key map (kbd "C-c ,a") 'elixir-mode-string-to-quoted-on-region) + (define-key map (kbd "C-c ,l") 'elixir-mode-string-to-quoted-on-current-line) + map) + "Keymap used in `elixir-mode'.") + +(defvar elixir-imenu-generic-expression + '(("Modules" "^\\s-*defmodule[ \n\t]+\\([A-Z][A-Za-z0-9._]+\\)\\s-+do.*$" 1) + ("Public Functions" "^\\s-*def[ \n\t]+\\([a-z0-9_]+\\)\\(([^)]*)\\)*[ \t\n]+do.*" 1) + ("Private Functions" "^\\s-*defp[ \n\t]+\\([a-z0-9_]+\\)\\(([^)]*)\\)*[ \t\n]+do.*" 1) + ("Public Macros" "^\\s-*defmacro[ \n\t]+\\([a-z0-9_]+\\)\\(([^)]*)\\)*[ \t\n]+do.*" 1) + ("Private Macros" "^\\s-*defmacrop[ \n\t]+\\([a-z0-9_]+\\)\\(([^)]*)\\)*[ \t\n]+do.*" 1) + ("Delegates" "^\\s-*defdelegate[ \n\t]+\\([a-z0-9_]+\\)\\(([^)]*)\\)*[ \t\n]+do.*" 1) + ("Overridables" "^\\s-*defoverridable[ \n\t]+\\([a-z0-9_]+\\)\\(([^)]*)\\)*[ \t\n]+do.*" 1) + ("Tests" "^\\s-*test[ \t\n]+\"?\\(:?[a-z0-9_@+() \t-]+\\)\"?[ \t\n]+do.*" 1)) + "Imenu pattern for `elixir-mode'.") + +(defgroup elixir nil + "Elixir major mode." + :group 'languages) + +(defcustom elixir-compiler-command "elixirc" + "Elixir mode command to compile code. Must be in your path." + :type 'string + :group 'elixir) + +(defcustom elixir-mode-command "elixir" + "The command for elixir." + :type 'string + :group 'elixir) + +(defcustom elixir-iex-command "iex" + "Elixir mode command for interactive REPL. Must be in your path." + :type 'string + :group 'elixir) + +(defcustom elixir-mode-cygwin-paths t + "Elixir mode use Cygwin style paths on Windows operating systems." + :type 'boolean + :group 'elixir) + +(defcustom elixir-mode-cygwin-prefix "/cygdrive/C" + "Elixir mode Cygwin prefix." + :type 'string + :group 'elixir) + +(defvar elixir-mode--eval-filename "elixir-mode-tmp-eval-file.exs") + +(defvar elixir-quoted--buffer-name "*elixir-quoted*") + +(defvar elixir-basic-offset 2) +(defvar elixir-key-label-offset 0) +(defvar elixir-match-label-offset 2) + +(defvar elixir-operator-face 'elixir-operator-face) +(defface elixir-operator-face + '((((class color) (min-colors 88) (background light)) + :foreground "darkred") + (((class color) (background dark)) + (:foreground "lemonchiffon1")) + (t nil)) + "For use with operators." + :group 'font-lock-faces) + +(defvar elixir-negation-face 'elixir-negation-face) +(defface elixir-negation-face + '((((class color) (min-colors 88) (background light)) + :foreground "#ff4500") + (((class color) (background dark)) + (:foreground "#ff4500")) + (t nil)) + "For use with standalone \"?\" to indicate code point." + :group 'font-lock-faces) + +(defvar elixir-attribute-face 'elixir-attribute-face) +(defface elixir-attribute-face + '((((class color) (min-colors 88) (background light)) + :foreground "MediumPurple4") + (((class color) (background dark)) + (:foreground "thistle")) + (t nil)) + "For use with module attribute tokens." + :group 'font-lock-faces) + +(defvar elixir-atom-face 'elixir-atom-face) +(defface elixir-atom-face + '((((class color) (min-colors 88) (background light)) + :foreground "RoyalBlue4") + (((class color) (background dark)) + (:foreground "light sky blue")) + (t nil)) + "For use with atoms & map keys." + :group 'font-lock-faces) + +(defun elixir-syntax-propertize-interpolation () + (let* ((beg (match-beginning 0)) + (context (save-excursion (save-match-data (syntax-ppss beg))))) + (put-text-property beg (1+ beg) 'elixir-interpolation + (cons (nth 3 context) (match-data))))) + +(defun elixir-syntax-propertize-function (start end) + (let ((case-fold-search nil)) + (goto-char start) + (remove-text-properties start end '(elixir-interpolation)) + (funcall + (syntax-propertize-rules + ((rx (group "#{" (0+ (not (any "}"))) "}")) + (0 (ignore (elixir-syntax-propertize-interpolation))))) + start end))) + +(defun elixir-match-interpolation (limit) + (let ((pos (next-single-char-property-change (point) 'elixir-interpolation + nil limit))) + (when (and pos (> pos (point))) + (goto-char pos) + (let ((value (get-text-property pos 'elixir-interpolation))) + (if (eq (car value) ?\") + (progn + (set-match-data (cdr value)) + t) + (elixir-match-interpolation limit)))))) + +(eval-when-compile + (defconst elixir-rx-constituents + `( + (atoms . ,(rx ":" + (or + (one-or-more (any "a-z" "A-Z" "_" "\"" "'")) + (and "\"" (one-or-more (not (any "\""))) "\"") + (and "'" (one-or-more (not (any "'"))) "'")))) + (builtin . ,(rx symbol-start + (or "case" "cond" "for" "if" "unless" "try" "receive" + "raise" "quote" "unquote" "unquote_splicing" "throw" + "super") + symbol-end)) + (builtin-declaration . ,(rx symbol-start + (or "def" "defp" "defmodule" "defprotocol" + "defmacro" "defmacrop" "defdelegate" + "defexception" "defstruct" "defimpl" + "defcallback") + symbol-end)) + (builtin-modules . ,(rx symbol-start + (or "Agent" "Application" "Atom" "Base" + "Behaviour" "Bitwise" "Builtin" "Code" "Dict" + "EEx" "Elixir" "Enum" "ExUnit" "Exception" + "File" "File.Stat" "File.Stream" "Float" + "Function" "GenEvent" "GenServer" "GenTCP" + "HashDict" "HashSet" "IO" "IO.ANSI" + "IO.Stream" "Inspect.Algebra" "Inspect.Opts" + "Integer" "Kernel" "Kernel.ParallelCompiler" + "Kernel.ParallelRequire" "Kernel.SpecialForms" + "Kernel.Typespec" "Keyword" "List" "Macro" + "Macro.Env" "Map" "Math" "Module" "Node" + "OptionParser" "OrdDict" "Path" "Port" + "Process" "Protocol" "Range" "Record" "Regex" + "Set" "Stream" "String" "StringIO" + "Supervisor" "Supervisor.Spec" "System" "Task" + "Task.Supervisor" "Tuple" "URI" + "UnboundMethod" "Version") + symbol-end)) + (builtin-namespace . ,(rx symbol-start + (or "import" "require" "use" "alias") + symbol-end)) + ;; Set aside code point syntax for `elixir-negation-face'. + (code-point . ,(rx symbol-start + "?" + anything + symbol-end)) + (function-declaration . ,(rx symbol-start + (or "def" "defp") + symbol-end)) + ;; Match `@doc' or `@moduledoc' syntax, with or without triple quotes. + (heredocs . ,(rx symbol-start + (or "@doc" "@moduledoc" "~s") + symbol-end)) + ;; The first character of an identifier must be a letter or an underscore. + ;; After that, they may contain any alphanumeric character + underscore. + ;; Additionally, the final character may be either `?' or `!'. + (identifiers . ,(rx (one-or-more (any "A-Z" "a-z" "_")) + (zero-or-more (any "A-Z" "a-z" "0-9" "_")) + (optional (or "?" "!")))) + (keyword . ,(rx symbol-start + (or "fn" "do" "end" "after" "else" "rescue" "catch") + symbol-end)) + (keyword-operator . ,(rx symbol-start + (or "not" "and" "or" "when" "in") + symbol-end)) + ;; Module and submodule names start with upper case letter. This + ;; can then be followed by any combination of alphanumeric chars. + ;; In turn, this can be followed by a `.' which begins the notation of + ;; a submodule, which follows the same naming pattern of the module. + ;; Finally, like other identifiers, it can be terminated with either `?' + ;; or `!'. + (module-names . ,(rx symbol-start + (one-or-more (any "A-Z")) + (zero-or-more (any "A-Z" "a-z" "_" "0-9")) + (zero-or-more + (and "." + (one-or-more (any "A-Z" "_")) + (zero-or-more (any "A-Z" "a-z" "_" "0-9")))) + (optional (or "!" "?")) + symbol-end)) + (operators1 . ,(rx symbol-start + (or "<" ">" "+" "-" "*" "/" "!" "^" "&") + symbol-end)) + (operators2 . ,(rx symbol-start + (or + "==" "!=" "<=" ">=" "&&" "||" "<>" "++" "--" "|>" "=~" + "->" "<-" "|" "." "=") + symbol-end)) + (operators3 . ,(rx symbol-start + (or "<<<" ">>>" "|||" "&&&" "^^^" "~~~" "===" "!==") + symbol-end)) + (pseudo-var . ,(rx symbol-start + (or "_" "__MODULE__" "__DIR__" "__ENV__" "__CALLER__" + "__block__" "__aliases__") + symbol-end)) + (punctuation . ,(rx symbol-start + (or "\\" "<<" ">>" "=>" "(" ")" ":" ";" "" "[" "]") + symbol-end)) + (sigils . ,(rx "~" (or "B" "C" "R" "S" "b" "c" "r" "s" "w"))))) + + (defmacro elixir-rx (&rest sexps) + (let ((rx-constituents (append elixir-rx-constituents rx-constituents))) + (cond ((null sexps) + (error "No regexp")) + ((cdr sexps) + (rx-to-string `(and ,@sexps) t)) + (t + (rx-to-string (car sexps) t)))))) + +(defconst elixir-font-lock-keywords + `( + ;; String interpolation + (elixir-match-interpolation 0 font-lock-variable-name-face t) + + ;; Module-defining & namespace builtins + (,(elixir-rx (or builtin-declaration builtin-namespace) + space + (group module-names)) + 1 font-lock-type-face) + + ;; Module attributes + (,(elixir-rx (group (or heredocs + (and "@" (1+ identifiers))))) + 1 elixir-attribute-face) + + ;; Keywords + (,(elixir-rx (group (or builtin builtin-declaration builtin-namespace + keyword keyword-operator))) + 1 font-lock-keyword-face) + + ;; Function names, i.e. `def foo do'. + (,(elixir-rx (group function-declaration) + space + (group identifiers)) + 2 font-lock-function-name-face) + + ;; Variable definitions + (,(elixir-rx (group identifiers) + (one-or-more space) + "=" + (or (one-or-more space) + (one-or-more "\n"))) + 1 font-lock-variable-name-face) + + ;; Sigils + (,(elixir-rx (group sigils)) + 1 font-lock-builtin-face) + + ;; Regex patterns. Elixir has support for eight different regex delimiters. + ;; This isn't a very DRY approach here but it gets the job done. + (,(elixir-rx "~r" + (and "/" (group (one-or-more (not (any "/")))) "/")) + 1 font-lock-string-face) + (,(elixir-rx "~r" + (and "[" (group (one-or-more (not (any "]")))) "]")) + 1 font-lock-string-face) + (,(elixir-rx "~r" + (and "{" (group (one-or-more (not (any "}")))) "}")) + 1 font-lock-string-face) + (,(elixir-rx "~r" + (and "(" (group (one-or-more (not (any ")")))) ")")) + 1 font-lock-string-face) + (,(elixir-rx "~r" + (and "|" (group (one-or-more (not (any "|")))) "|")) + 1 font-lock-string-face) + (,(elixir-rx "~r" + (and "\"" (group (one-or-more (not (any "\"")))) "\"")) + 1 font-lock-string-face) + (,(elixir-rx "~r" + (and "'" (group (one-or-more (not (any "'")))) "'")) + 1 font-lock-string-face) + (,(elixir-rx "~r" + (and "<" (group (one-or-more (not (any ">")))) ">")) + 1 font-lock-string-face) + + ;; Atoms and singleton-like words like true/false/nil. + (,(elixir-rx (group atoms)) + 1 elixir-atom-face) + + ;; Map keys + (,(elixir-rx (group (and (one-or-more identifiers) ":"))) + 1 elixir-atom-face) + + ;; Built-in modules and pseudovariables + (,(elixir-rx (group (or builtin-modules pseudo-var))) + 1 font-lock-constant-face) + + ;; Code points + (,(elixir-rx (group code-point)) + 1 elixir-negation-face))) + +(defun elixir-mode-cygwin-path (expanded-file-name) + "Elixir mode get Cygwin absolute path name. +Argument EXPANDED-FILE-NAME ." + (replace-regexp-in-string "^[a-zA-Z]:" elixir-mode-cygwin-prefix expanded-file-name t)) + +(defun elixir-mode-universal-path (file-name) + "Elixir mode multi-OS path handler. +Argument FILE-NAME ." + (let ((full-file-name (expand-file-name file-name))) + (if (and (equal system-type 'windows-nt) + elixir-mode-cygwin-paths) + (elixir-mode-cygwin-path full-file-name) + full-file-name))) + +(defun elixir-mode-command-compile (file-name) + "Elixir mode command to compile a file. +Argument FILE-NAME ." + (let ((full-file-name (elixir-mode-universal-path file-name))) + (mapconcat 'identity (append (list elixir-compiler-command) (list full-file-name)) " "))) + +(defun elixir-mode-compiled-file-name (&optional filename) + "Elixir mode compiled FILENAME." + (concat (file-name-sans-extension (or filename (buffer-file-name))) ".beam")) + +(defun elixir-mode-compile-file () + "Elixir mode compile and save current file." + (interactive) + (elixir-deprecated-use-alchemist "elixir-mode-compile-file") + (let ((compiler-output (shell-command-to-string (elixir-mode-command-compile (buffer-file-name))))) + (when (string= compiler-output "") + (message "Compiled and saved as %s" (elixir-mode-compiled-file-name))))) + +(defun elixir-quoted--initialize-buffer (quoted) + (pop-to-buffer elixir-quoted--buffer-name) + (setq buffer-undo-list nil) ; Get rid of undo information from + ; previous expansions + (let ((inhibit-read-only t) + (buffer-undo-list t)) ; Ignore undo information + (erase-buffer) + (insert quoted) + (goto-char (point-min)) + (elixir-mode) + (elixir-quoted-minor-mode 1))) + +;;;###autoload +(defun elixir-mode-iex (&optional args-p) + "Elixir mode interactive REPL. +Optional argument ARGS-P ." + (interactive "P") + (let ((switches (if (equal args-p nil) + '() + (split-string (read-string "Additional args: "))))) + (unless (comint-check-proc "*IEX*") + (set-buffer + (apply 'make-comint "IEX" + elixir-iex-command nil switches)))) + (pop-to-buffer "*IEX*") + (elixir-deprecated-use-alchemist "elixir-mode-iex")) + +;;;###autoload +(defun elixir-mode-open-modegithub () + "Elixir mode open GitHub page." + (interactive) + (browse-url "https://github.com/elixir-lang/emacs-elixir")) + +;;;###autoload +(defun elixir-mode-open-elixir-home () + "Elixir mode go to language home." + (interactive) + (browse-url elixir-mode--website-url)) + +;;;###autoload +(defun elixir-mode-open-docs-master () + "Elixir mode go to master documentation." + (interactive) + (browse-url (concat elixir-mode--website-url "/docs/master/elixir"))) + +;;;###autoload +(defun elixir-mode-open-docs-stable () + "Elixir mode go to stable documentation." + (interactive) + (browse-url (concat elixir-mode--website-url "/docs/stable/elixir"))) + +;;;###autoload +(defun elixir-mode-version (&optional show-version) + "Get the Elixir-Mode version as string. + +If called interactively or if SHOW-VERSION is non-nil, show the +version in the echo area and the messages buffer. + +The returned string includes both, the version from package.el +and the library version, if both a present and different. + +If the version number could not be determined, signal an error, +if called interactively, or if SHOW-VERSION is non-nil, otherwise +just return nil." + (interactive (list t)) + (let ((version (pkg-info-version-info 'elixir-mode))) + (when show-version + (message "Elixir-Mode version: %s" version)) + version)) + +(defun elixir-mode--code-eval-string-command (file) + (format "%s -e 'IO.puts inspect(elem(Code.eval_string(File.read!(\"%s\")), 0))'" + elixir-mode-command + file)) + +(defun elixir-mode--code-string-to-quoted-command (file) + (format "%s -e 'IO.puts inspect(elem(Code.string_to_quoted(File.read!(\"%s\")), 1), pretty: true)'" + elixir-mode-command + file)) + +(defun elixir-mode--execute-elixir-with-code-eval-string (string) + (with-temp-file elixir-mode--eval-filename + (insert string)) + (let ((output (shell-command-to-string (elixir-mode--code-eval-string-command elixir-mode--eval-filename)))) + (delete-file elixir-mode--eval-filename) + output)) + +(defun elixir-mode--execute-elixir-with-code-string-to-quoted (string) + (with-temp-file elixir-mode--eval-filename + (insert string)) + (let ((output (shell-command-to-string (elixir-mode--code-string-to-quoted-command elixir-mode--eval-filename)))) + (delete-file elixir-mode--eval-filename) + output)) + +(defun elixir-mode--eval-string (string) + (let ((output (elixir-mode--execute-elixir-with-code-eval-string string))) + (message output))) + +(defun elixir-mode--string-to-quoted (string) + (let* ((output (elixir-mode--execute-elixir-with-code-string-to-quoted string))) + (elixir-quoted--initialize-buffer output))) + +(defun elixir-mode-eval-on-region (beg end) + "Evaluate the Elixir code on the marked region. +Argument BEG Start of the region. +Argument END End of the region." + (interactive (list (point) (mark))) + (elixir-deprecated-use-alchemist "elixir-mode-eval-on-region") + (unless (and beg end) + (error "The mark is not set now, so there is no region")) + (let* ((region (buffer-substring-no-properties beg end))) + (elixir-mode--eval-string region))) + +(defun elixir-mode-eval-on-current-line () + "Evaluate the Elixir code on the current line." + (interactive) + (elixir-deprecated-use-alchemist "elixir-mode-eval-on-current-line") + (let ((current-line (thing-at-point 'line))) + (elixir-mode--eval-string current-line))) + +(defun elixir-mode-eval-on-current-buffer () + "Evaluate the Elixir code on the current buffer." + (interactive) + (elixir-deprecated-use-alchemist "elixir-mode-eval-on-current-buffer") + (let ((current-buffer (buffer-substring-no-properties (point-max) (point-min)))) + (elixir-mode--eval-string current-buffer))) + +(defun elixir-mode-string-to-quoted-on-region (beg end) + "Get the representation of the expression on the marked region. +Argument BEG Start of the region. +Argument END End of the region." + (interactive (list (point) (mark))) + (elixir-deprecated-use-alchemist "elixir-mode-string-to-quoted-on-region") + (unless (and beg end) + (error "The mark is not set now, so there is no region")) + (let ((region (buffer-substring-no-properties beg end))) + (elixir-mode--string-to-quoted region))) + +(defun elixir-mode-string-to-quoted-on-current-line () + "Get the representation of the expression on the current line." + (interactive) + (elixir-deprecated-use-alchemist "elixir-mode-string-to-quoted-on-current-line") + (let ((current-line (thing-at-point 'line))) + (elixir-mode--string-to-quoted current-line))) + +(easy-menu-define elixir-mode-menu elixir-mode-map + "Elixir mode menu." + '("Elixir" + ["Indent line" smie-indent-line] + ["Compile file" elixir-mode-compile-file] + ["IEX" elixir-mode-iex] + "---" + ["elixir-mode on GitHub" elixir-mode-open-modegithub] + ["Elixir homepage" elixir-mode-open-elixirhome] + ["About" elixir-mode-version] + )) + +;;;###autoload +(define-derived-mode elixir-mode prog-mode "Elixir" + "Major mode for editing Elixir code. + +\\{elixir-mode-map}" + (set (make-local-variable 'font-lock-defaults) + '(elixir-font-lock-keywords)) + (set (make-local-variable 'comment-start) "# ") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-start-skip) "#+ *") + (set (make-local-variable 'comment-use-syntax) t) + (set (make-local-variable 'tab-width) elixir-basic-offset) + (set (make-local-variable 'syntax-propertize-function) + #'elixir-syntax-propertize-function) + (set (make-local-variable 'imenu-generic-expression) + elixir-imenu-generic-expression) + (smie-setup elixir-smie-grammar 'verbose-elixir-smie-rules + :forward-token 'elixir-smie-forward-token + :backward-token 'elixir-smie-backward-token)) + +(define-minor-mode elixir-cos-mode + "Elixir mode toggle compile on save." + :group 'elixir-cos :lighter " CoS" + (cond + (elixir-cos-mode + (add-hook 'after-save-hook 'elixir-mode-compile-file nil t)) + (t + (remove-hook 'after-save-hook 'elixir-mode-compile-file t)))) + +(define-minor-mode elixir-quoted-minor-mode + "Minor mode for displaying elixir quoted expressions" + :group 'elixir-quoted :lighter " quoted" + :keymap '(("q" . quit-window)) + (setq buffer-read-only t)) + +;; Invoke elixir-mode when appropriate + +;;;###autoload +(progn + (add-to-list 'auto-mode-alist '("\\.elixir\\'" . elixir-mode)) + (add-to-list 'auto-mode-alist '("\\.ex\\'" . elixir-mode)) + (add-to-list 'auto-mode-alist '("\\.exs\\'" . elixir-mode))) + +(provide 'elixir-mode) + +;;; elixir-mode.el ends here diff --git a/emacs.d/elpa/elixir-mode-20150103.439/elixir-smie.el b/emacs.d/elpa/elixir-mode-20150103.439/elixir-smie.el new file mode 100644 index 0000000..e697161 --- /dev/null +++ b/emacs.d/elpa/elixir-mode-20150103.439/elixir-smie.el @@ -0,0 +1,348 @@ +;;; elixir-smie.el --- Structural syntax support for elixir-mode + +;; Copyright 2011-2015 secondplanet +;; 2013-2015 Samuel Tonini, Matt DeBoard, Andreas Fuchs + +;; This file is not a part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +;;; Commentary: + +;; Structural syntax support for elixir-mode + +;;; Code: + +(require 'smie) + +;; HACK: Patch for Emacs 24.3 smie that fix +;; https://github.com/elixir-lang/emacs-elixir/issues/107. +;; +;; defadvice is used to change the behavior only for elixir-mode. +;; Definition of advice is a definition of corresponding function +;; in Emacs 24.4. +(when (and (= 24 emacs-major-version) + (= 3 emacs-minor-version)) + (defadvice smie-rule-parent (around elixir-mode-patch activate) + (if (not (eq major-mode 'elixir-mode)) + (progn ad-do-it) + (setq ad-return-value + (save-excursion + (goto-char (cadr (smie-indent--parent))) + (cons 'column + (+ (or offset 0) + (smie-indent-virtual))))))) + + (defadvice smie-indent-comment (around elixir-mode-patch activate) + (if (not (eq major-mode 'elixir-mode)) + (progn ad-do-it) + (setq ad-return-value + (and (smie-indent--bolp) + (let ((pos (point))) + (save-excursion + (beginning-of-line) + (and (re-search-forward comment-start-skip (line-end-position) t) + (eq pos (or (match-end 1) (match-beginning 0)))))) + (save-excursion + (forward-comment (point-max)) + (skip-chars-forward " \t\r\n") + (unless + (save-excursion + (let ((next (funcall smie-forward-token-function))) + (or (if (zerop (length next)) + (or (eobp) (eq (car (syntax-after (point))) 5))) + (rassoc next smie-closer-alist)))) + (smie-indent-calculate)))))))) + +;; FIXME: This is me being lazy. CL is a compile-time dep only. +;; (But for now, there is no real file-compilation plot, so let's +;; scrape by with the runtime dep.) +(require 'cl) + +(defvar elixir-smie-verbose-p nil + "Emit context information about the current syntax state.") + +(defvar elixir-mode-syntax-table + (let ((table (make-syntax-table))) + + ;; Note that ?_ might be better as class "_", but either seems to + ;; work: + (modify-syntax-entry ?_ "_" table) + (modify-syntax-entry ?? "w" table) + (modify-syntax-entry ?~ "w" table) + (modify-syntax-entry ?! "_" table) + (modify-syntax-entry ?' "\"'" table) + (modify-syntax-entry ?\" "\"\"" table) + (modify-syntax-entry ?# "<" table) + (modify-syntax-entry ?\n ">" table) + (modify-syntax-entry ?\( "()" table) + (modify-syntax-entry ?\) ")(" table) + (modify-syntax-entry ?\{ "(}" table) + (modify-syntax-entry ?\} "){" table) + (modify-syntax-entry ?\[ "(]" table) + (modify-syntax-entry ?\] ")[" table) + (modify-syntax-entry ?: "_" table) + (modify-syntax-entry ?@ "_" table) + table) + "Elixir mode syntax table.") + +(defconst elixir-smie-grammar + (smie-prec2->grammar + (smie-merge-prec2s + (smie-bnf->prec2 + '((id) + (statements (statement) + (statement ";" statements)) + (statement ("def" non-block-expr "do" statements "end") + (non-block-expr "fn" match-statements "end") + (non-block-expr "do" statements "end") + ("if" non-block-expr "do" statements "else" statements "end") + ("if" non-block-expr "do" statements "end") + ("if" non-block-expr "COMMA" "do:" non-block-expr) + ("if" non-block-expr "COMMA" + "do:" non-block-expr "COMMA" + "else:" non-block-expr) + ("try" "do" statements "after" statements "end") + ("try" "do" statements "catch" match-statements "end") + ("try" "do" statements "rescue" match-statements "end") + ("try" "do" statements "end") + ("case" non-block-expr "do" match-statements "end")) + (non-block-expr (non-block-expr "OP" non-block-expr) + (non-block-expr "COMMA" non-block-expr) + ("(" non-block-expr ")") + ("{" non-block-expr "}") + ("[" non-block-expr "]") + ("STRING")) + (match-statements (match-statement "MATCH-STATEMENT-DELIMITER" + match-statements) + (match-statement)) + (match-statement (non-block-expr "->" statements))) + '((assoc "if" "do:" "else:") + (assoc "COMMA") + (left "OP"))) + + (smie-precs->prec2 + '((left "||") + (left "&&") + (nonassoc "=~" "===" "!==" "==" "!=" "<=" ">=" "<" ">") + (left "+" "-" "<<<" ">>>" "^^^" "~~~" "&&&" "|||") + (left "*" "/")))))) + +(defvar elixir-smie--operator-regexp + (rx (or "<<<" ">>>" "^^^" "~~~" "&&&" "|||" "===" "!==" "==" "!=" "<=" + "=" ">=" "<" ">" "&&" "||" "<>" "++" "--" "//" "/>" "=~" "|>"))) + +(defvar elixir-smie--binary-sequence-regexp + (rx (or "<<" ">>"))) + +(defvar elixir-smie--block-operator-regexp + (rx "->" (0+ nonl))) + +(defvar elixir-smie--spaces-til-eol-regexp + (rx (and (1+ space) eol)) + "Regex representing one or more whitespace characters concluding with eol.") + +(defvar elixir-smie--comment-regexp + (rx (and (0+ space) "#" (0+ not-newline))) + "Regex matching comments.") + +(defvar elixir-smie-indent-basic 2) + +(defmacro elixir-smie-debug (message &rest format-args) + `(progn + (when elixir-smie-verbose-p + (message (format ,message ,@format-args))) + nil)) + +(defun elixir-smie--implicit-semi-p () + (not (or (memq (char-before) '(?\{ ?\[)) + (looking-back elixir-smie--operator-regexp (- (point) 3) t)))) + +(defun elixir-smie--semi-ends-match () + "Return non-nil if the current line concludes a match block." + (when (not (eobp)) + (save-excursion + ;; Warning: Recursion. + ;; This is easy though. + + ;; 1. If we're at a blank line, move forward a character. This takes us to + ;; the next line. + ;; 2. If we're not at the end of the buffer, call this function again. + ;; (Otherwise, return nil.) + + ;; The point here is that we want to treat blank lines as a single semi- + ;; colon when it comes to detecting the end of match statements. This could + ;; also be handled by a `while' expression or some other looping mechanism. + (cl-flet ((self-call () + (if (< (point) (point-max)) + (elixir-smie--semi-ends-match) + nil))) + (cond + ((and (eolp) (bolp)) + (forward-char) + (self-call)) + ((looking-at elixir-smie--spaces-til-eol-regexp) + (move-beginning-of-line 2) + (self-call)) + ;; And if we're NOT on a blank line, move to the end of the line, and see + ;; if we're looking back at a block operator. + (t (move-end-of-line 1) + (looking-back elixir-smie--block-operator-regexp))))))) + +(defun elixir-smie--same-line-as-parent (parent-pos child-pos) + "Return non-nil if `child-pos' is on same line as `parent-pos'." + (= (line-number-at-pos parent-pos) (line-number-at-pos child-pos))) + +(defun elixir-smie-forward-token () + (cond + ;; If there is nothing but whitespace between the last token and eol, emit + ;; a semicolon. + ((looking-at elixir-smie--spaces-til-eol-regexp) + (goto-char (match-end 0)) + ";") + ((and (or (looking-at elixir-smie--comment-regexp) + (looking-at "[\n#]")) + (elixir-smie--implicit-semi-p)) + (when (not (save-excursion + (forward-comment 1) + (eobp))) + (if (eolp) (forward-char 1) (forward-comment 1))) + ;; Note: `elixir-smie--semi-ends-match' will be called when the point is at + ;; the beginning of a new line. Keep that in mind. + (if (elixir-smie--semi-ends-match) + "MATCH-STATEMENT-DELIMITER" + ";")) + ((looking-at elixir-smie--block-operator-regexp) + (goto-char (match-end 0)) + "->") + ((looking-at elixir-smie--operator-regexp) + (goto-char (match-end 0)) + "OP") + (t (smie-default-forward-token)))) + +(defun elixir-smie-backward-token () + (let ((pos (point))) + (forward-comment (- (point))) + (cond + ((and (> pos (line-end-position)) + (elixir-smie--implicit-semi-p)) + (if (elixir-smie--semi-ends-match) + "MATCH-STATEMENT-DELIMITER" + ";")) + ((looking-back elixir-smie--block-operator-regexp (- (point) 3) t) + (goto-char (match-beginning 0)) + "->") + ((looking-back elixir-smie--binary-sequence-regexp (- (point) 3) t) + (goto-char (match-beginning 0)) + "OP") + ((looking-back elixir-smie--operator-regexp (- (point) 3) t) + (goto-char (match-beginning 0)) + "OP") + (t (smie-default-backward-token))))) + +(defun verbose-elixir-smie-rules (kind token) + (let ((value (elixir-smie-rules kind token))) + (elixir-smie-debug "%s '%s'; sibling-p:%s parent:%s prev-is-OP:%s hanging:%s == %s" kind token + (ignore-errors (smie-rule-sibling-p)) + (ignore-errors smie--parent) + (ignore-errors (smie-rule-prev-p "OP")) + (ignore-errors (smie-rule-hanging-p)) + value) + value)) + +(defun elixir-smie-rules (kind token) + (pcase (cons kind token) + (`(:before . "OP") + (when (and (not (smie-rule-hanging-p)) + (smie-rule-prev-p "OP")) + -2)) + (`(:after . "OP") + (cond + ((smie-rule-sibling-p) nil) + ((smie-rule-hanging-p) (smie-rule-parent elixir-smie-indent-basic)) + (t (smie-rule-parent)))) + (`(:before . "MATCH-STATEMENT-DELIMITER") + (cond + ((and (not (smie-rule-sibling-p)) + (smie-rule-hanging-p)) + (smie-rule-parent elixir-smie-indent-basic)))) + (`(:before . "fn") + (smie-rule-parent)) + (`(:before . "end") + (smie-rule-parent)) + ;; Closing paren on the other line + (`(:before . "(") + (smie-rule-parent)) + (`(:before . "[") + (cond + ((smie-rule-hanging-p) + (smie-rule-parent)))) + (`(:after . "[") + (cond + ((smie-rule-hanging-p) + (smie-rule-parent elixir-smie-indent-basic)))) + (`(:before . "->") + (cond + ((smie-rule-hanging-p) + (smie-rule-parent elixir-smie-indent-basic)))) + (`(:after . "->") + (cond + ;; This first condition is kind of complicated so I'll try to make this + ;; comment as clear as possible. + + ;; "If `->' is the last thing on the line, and its parent token + ;; is `fn' ..." + ((and (smie-rule-hanging-p) + (smie-rule-parent-p "fn")) + ;; "... and if: + + ;; 1. `smie--parent' is non-nil + ;; 2. the `->' token in question is on the same line as its parent (if + ;; the logic has gotten this far, its parent will be `fn') + + ;; ... then indent the line after the `->' aligned with the + ;; parent, offset by `elixir-smie-indent-basic'." + (if (and smie--parent (elixir-smie--same-line-as-parent + (nth 1 smie--parent) + (point))) + (smie-rule-parent elixir-smie-indent-basic) + elixir-smie-indent-basic)) + ;; Otherwise, if just indent by two. + ((smie-rule-hanging-p) + (cond + ((smie-rule-parent-p "after" "catch" "do" "rescue" "try") + elixir-smie-indent-basic) + (t + (smie-rule-parent elixir-smie-indent-basic)))))) + (`(:before . ";") + (cond + ((smie-rule-parent-p "after" "catch" "def" "defmodule" "defp" "do" "else" + "fn" "if" "rescue" "try" "unless") + (smie-rule-parent elixir-smie-indent-basic)))) + (`(:after . ";") + (cond + ((smie-rule-parent-p "def") + (smie-rule-parent)) + ((smie-rule-parent-p "if") + (smie-rule-parent)) + ((and (smie-rule-parent-p "(") + (save-excursion + (goto-char (cadr smie--parent)) + (smie-rule-hanging-p))) + (smie-rule-parent elixir-smie-indent-basic)))))) + +(provide 'elixir-smie) + +;;; elixir-smie.el ends here diff --git a/emacs.d/elpa/elixir-mode-readme.txt b/emacs.d/elpa/elixir-mode-readme.txt new file mode 100644 index 0000000..9ba2bf8 --- /dev/null +++ b/emacs.d/elpa/elixir-mode-readme.txt @@ -0,0 +1,2 @@ + Provides font-locking, indentation and navigation support + for the Elixir programming language. diff --git a/emacs.d/elpa/elpy-1.7.1/LICENSE b/emacs.d/elpa/elpy-1.7.1/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/emacs.d/elpa/elpy-1.7.1/README.rst b/emacs.d/elpa/elpy-1.7.1/README.rst new file mode 100644 index 0000000..29c1e1b --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/README.rst @@ -0,0 +1,65 @@ +======================================= +Elpy, the Emacs Lisp Python Environment +======================================= + +Elpy is an Emacs package to bring powerful Python editing to Emacs. It +combines a number of other packages, both written in Emacs Lisp as +well as Python. + +.. image:: https://secure.travis-ci.org/jorgenschaefer/elpy.png?branch=master + :target: http://travis-ci.org/jorgenschaefer/elpy?branch=master + +.. image:: https://coveralls.io/repos/jorgenschaefer/elpy/badge.png?branch=master + :target: https://coveralls.io/r/jorgenschaefer/elpy?branch=master + +Documentation +============= + +Elpy is fully documented at readthedocs.org: + +http://elpy.readthedocs.org/en/latest/index.html + +Quick Installation +================== + +First, install the required Python packages::: + + # Either of these + pip install rope + pip install jedi + # flake8 for code checks + pip install flake8 + # and importmagic for automatic imports + pip install importmagic + + +Evaluate this in your ``*scratch*`` buffer: + +.. code-block:: lisp + + (require 'package) + (add-to-list 'package-archives + '("elpy" . "http://jorgenschaefer.github.io/packages/")) + + +Then run ``M-x package-refresh-contents`` to load the contents of the +new repository, and ``M-x package-install RET elpy RET`` to install +elpy. + +Finally, add the following to your ``.emacs``: + +.. code-block:: lisp + + (package-initialize) + (elpy-enable) + +Done. + +License +======= + +This project is free software: You can redistribute it and/or modify +it under the terms of the `GNU General Public License`__, either +version 3 of the License, or (at your option) any later version. + +.. __: LICENSE diff --git a/emacs.d/elpa/elpy-1.7.1/elpy-autoloads.el b/emacs.d/elpa/elpy-1.7.1/elpy-autoloads.el new file mode 100644 index 0000000..2713e20 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy-autoloads.el @@ -0,0 +1,54 @@ +;;; elpy-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "elpy" "elpy.el" (21786 45609 0 0)) +;;; Generated autoloads from elpy.el + +(autoload 'elpy-enable "elpy" "\ +Enable Elpy in all future Python buffers. + +\(fn &optional IGNORED)" t nil) + +(autoload 'elpy-mode "elpy" "\ +Minor mode in Python buffers for the Emacs Lisp Python Environment. + +This mode fully supports virtualenvs. Once you switch a +virtualenv using \\[pyvenv-workon], you can use +\\[elpy-rpc-restart] to make the elpy Python process use your +virtualenv. + +See https://github.com/jorgenschaefer/elpy/wiki/Keybindings for a +more structured list. + +\\{elpy-mode-map} + +\(fn &optional ARG)" t nil) + +(autoload 'elpy-config "elpy" "\ +Configure Elpy. + +This function will pop up a configuration buffer, which is mostly +a customize buffer, but has some more options. + +\(fn)" t nil) + +(autoload 'elpy-version "elpy" "\ +Display the version of Elpy. + +\(fn)" t nil) + +;;;*** + +;;;### (autoloads nil nil ("elpy-pkg.el" "elpy-refactor.el") (21786 +;;;;;; 45609 742650 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; elpy-autoloads.el ends here diff --git a/emacs.d/elpa/elpy-1.7.1/elpy-pkg.el b/emacs.d/elpa/elpy-1.7.1/elpy-pkg.el new file mode 100644 index 0000000..fc35ef9 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy-pkg.el @@ -0,0 +1,8 @@ +(define-package "elpy" "1.7.1" + "Emacs Python Development Environment" + '((company "0.8.2") + (find-file-in-project "3.3") + (highlight-indentation "0.5.0") + (pyvenv "1.3") + (yasnippet "0.8.0")) + ) diff --git a/emacs.d/elpa/elpy-1.7.1/elpy-refactor.el b/emacs.d/elpa/elpy-1.7.1/elpy-refactor.el new file mode 100644 index 0000000..de3b2c2 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy-refactor.el @@ -0,0 +1,285 @@ +;;; elpy-refactor.el --- Refactoring mode for Elpy + +;; Copyright (C) 2013 Jorgen Schaefer + +;; Author: Jorgen Schaefer +;; URL: https://github.com/jorgenschaefer/elpy + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 3 +;; of the License, or (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This file provides an interface, including a major mode, to use +;; refactoring options provided by the Rope library. + +;;; Code: + +;; We require elpy, but elpy loads us, so we shouldn't load it back. +;; (require 'elpy) + +(defvar elpy-refactor-changes nil + "Changes that will be commited on \\[elpy-refactor-commit].") +(make-variable-buffer-local 'elpy-refactor-current-changes) + +(defvar elpy-refactor-window-configuration nil + "The old window configuration. Will be restored after commit.") +(make-variable-buffer-local 'elpy-refactor-window-configuration) + +(make-obsolete + 'elpy-refactor + "Refactoring has been unstable and flakey, support will be dropped in the future." + "elpy 1.5.0") +(defun elpy-refactor () + "Run the Elpy refactoring interface for Python code." + (interactive) + (save-some-buffers) + (let* ((selection (elpy-refactor-select + (elpy-refactor-rpc-get-options))) + (method (car selection)) + (args (cdr selection))) + (when method + (elpy-refactor-create-change-buffer + (elpy-refactor-rpc-get-changes method args))))) + +(defun elpy-refactor-select (options) + "Show the user the refactoring options and let her choose one. + +Depending on the chosen option, ask the user for further +arguments and build the argument. + +Return a cons cell of the name of the option and the arg list +created." + (let ((buf (get-buffer-create "*Elpy Refactor*")) + (pos (vector (1- (point)) + (ignore-errors + (1- (region-beginning))) + (ignore-errors + (1- (region-end))))) + (inhibit-read-only t) + (options (sort options + (lambda (a b) + (let ((cata (cdr (assq 'category a))) + (catb (cdr (assq 'category b)))) + (if (equal cata catb) + (string< (cdr (assq 'description a)) + (cdr (assq 'description b))) + (string< cata catb)))))) + (key ?a) + last-category + option-alist) + (with-current-buffer buf + (erase-buffer) + (dolist (option options) + (let ((category (cdr (assq 'category option))) + (description (cdr (assq 'description option))) + (name (cdr (assq 'name option))) + (args (cdr (assq 'args option)))) + (when (not (equal category last-category)) + (when last-category + (insert "\n")) + (insert (propertize category 'face 'bold) "\n") + (setq last-category category)) + (insert " (" key ") " description "\n") + (setq option-alist (cons (list key name args) + option-alist)) + (setq key (1+ key)))) + (let ((window-conf (current-window-configuration))) + (unwind-protect + (progn + (with-selected-window (display-buffer buf) + (goto-char (point-min))) + (fit-window-to-buffer (get-buffer-window buf)) + (let* ((key (read-key "Refactoring action? ")) + (entry (cdr (assoc key option-alist)))) + (kill-buffer buf) + (cons (car entry) ; name + (elpy-refactor-build-arguments (cadr entry) + pos)))) + (set-window-configuration window-conf)))))) + +(defun elpy-refactor-build-arguments (args pos) + "Translate an argument list specification to an argument list. + +POS is a vector of three elements, the current offset, the offset +of the beginning of the region, and the offset of the end of the +region. + +ARGS is a list of triples, each triple containing the name of an +argument (ignored), the type of the argument, and a possible +prompt string. + +Available types: + + offset - The offset in the buffer, (1- (point)) + start_offset - Offset of the beginning of the region + end_offset - Offset of the end of the region + string - A free-form string + filename - A non-existing file name + directory - An existing directory name + boolean - A boolean question" + (mapcar (lambda (arg) + (let ((type (cadr arg)) + (prompt (caddr arg))) + (cond + ((equal type "offset") + (aref pos 0)) + ((equal type "start_offset") + (aref pos 1)) + ((equal type "end_offset") + (aref pos 2)) + ((equal type "string") + (read-from-minibuffer prompt)) + ((equal type "filename") + (expand-file-name + (read-file-name prompt))) + ((equal type "directory") + (expand-file-name + (read-directory-name prompt))) + ((equal type "boolean") + (y-or-n-p prompt))))) + args)) + +(defun elpy-refactor-create-change-buffer (changes) + "Show the user a buffer of changes. + +The user can review the changes and confirm them with +\\[elpy-refactor-commit]." + (when (not changes) + (error "No changes for this refactoring action.")) + (with-current-buffer (get-buffer-create "*Elpy Refactor*") + (elpy-refactor-mode) + (setq elpy-refactor-changes changes + elpy-refactor-window-configuration (current-window-configuration)) + (let ((inhibit-read-only t)) + (erase-buffer) + (elpy-refactor-insert-changes changes)) + (select-window (display-buffer (current-buffer))) + (goto-char (point-min)))) + +(defun elpy-refactor-insert-changes (changes) + "Format and display the changes described in CHANGES." + (insert (propertize "Use C-c C-c to apply the following changes." + 'face 'bold) + "\n\n") + (dolist (change changes) + (let ((action (cdr (assq 'action change)))) + (cond + ((equal action "change") + (insert (cdr (assq 'diff change)) + "\n")) + ((equal action "create") + (let ((type (cdr (assq 'type change)))) + (if (equal type "file") + (insert "+++ " (cdr (assq 'file change)) "\n" + "Create file " (cdr (assq 'file change)) "\n" + "\n") + (insert "+++ " (cdr (assq 'path change)) "\n" + "Create directory " (cdr (assq 'path change)) "\n" + "\n")))) + ((equal action "move") + (insert "--- " (cdr (assq 'source change)) "\n" + "+++ " (cdr (assq 'destination change)) "\n" + "Rename " (cdr (assq 'type change)) "\n" + "\n")) + ((equal action "delete") + (let ((type (cdr (assq 'type change)))) + (if (equal type "file") + (insert "--- " (cdr (assq 'file change)) "\n" + "Delete file " (cdr (assq 'file change)) "\n" + "\n") + (insert "--- " (cdr (assq 'path change)) "\n" + "Delete directory " (cdr (assq 'path change)) "\n" + "\n")))))))) + +(defvar elpy-refactor-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c C-c") 'elpy-refactor-commit) + (define-key map (kbd "q") 'bury-buffer) + (define-key map (kbd "h") 'describe-mode) + (define-key map (kbd "?") 'describe-mode) + map) + "The key map for `elpy-refactor-mode'.") + +(define-derived-mode elpy-refactor-mode diff-mode "Elpy Refactor" + "Mode to display refactoring actions and ask confirmation from the user. + +\\{elpy-refactor-mode-map}" + :group 'elpy + (view-mode 1)) + +(defun elpy-refactor-commit () + "Commit the changes in the current buffer." + (interactive) + (when (not elpy-refactor-changes) + (error "No changes to commit.")) + ;; Restore the window configuration as the first thing so that + ;; changes below are visible to the user. Especially the point + ;; change in possible buffer changes. + (set-window-configuration elpy-refactor-window-configuration) + (dolist (change elpy-refactor-changes) + (let ((action (cdr (assq 'action change)))) + (cond + ((equal action "change") + (with-current-buffer (find-file-noselect (cdr (assq 'file change))) + ;; This would break for save-excursion as the buffer is + ;; truncated, so all markets now point to position 1. + (let ((old-point (point))) + (undo-boundary) + (erase-buffer) + (insert (cdr (assq 'contents change))) + (undo-boundary) + (goto-char old-point)))) + ((equal action "create") + (if (equal (cdr (assq 'type change)) + "file") + (find-file-noselect (cdr (assq 'file change))) + (make-directory (cdr (assq 'path change))))) + ((equal action "move") + (let* ((source (cdr (assq 'source change))) + (dest (cdr (assq 'destination change))) + (buf (get-file-buffer source))) + (when buf + (with-current-buffer buf + (setq buffer-file-name dest) + (rename-buffer (file-name-nondirectory dest) t))) + (rename-file source dest))) + ((equal action "delete") + (if (equal (cdr (assq 'type change)) "file") + (let ((name (cdr (assq 'file change)))) + (when (y-or-n-p (format "Really delete %s? " name)) + (delete-file name t))) + (let ((name (cdr (assq 'directory change)))) + (when (y-or-n-p (format "Really delete %s? " name)) + (delete-directory name nil t)))))))) + (kill-buffer (current-buffer))) + +(defun elpy-refactor-rpc-get-options () + "Get a list of refactoring options from the Elpy RPC." + (if (use-region-p) + (elpy-rpc "get_refactor_options" + (list (buffer-file-name) + (1- (region-beginning)) + (1- (region-end)))) + (elpy-rpc "get_refactor_options" + (list (buffer-file-name) + (1- (point)))))) + +(defun elpy-refactor-rpc-get-changes (method args) + "Get a list of changes from the Elpy RPC after applying METHOD with ARGS." + (elpy-rpc "refactor" + (list (buffer-file-name) + method args))) + +(provide 'elpy-refactor) +;;; elpy-refactor.el ends here diff --git a/emacs.d/elpa/elpy-1.7.1/elpy.el b/emacs.d/elpa/elpy-1.7.1/elpy.el new file mode 100644 index 0000000..3e1101e --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy.el @@ -0,0 +1,3501 @@ +;;; elpy.el --- Emacs Python Development Environment -*- lexical-binding: t -*- + +;; Copyright (C) 2012-2014 Jorgen Schaefer + +;; Author: Jorgen Schaefer +;; URL: https://github.com/jorgenschaefer/elpy +;; Version: 1.7.1 +;; Keywords: Python, IDE, Languages, Tools +;; Package-Requires: ((company "0.8.2") (find-file-in-project "3.3") (highlight-indentation "0.5.0") (pyvenv "1.3") (yasnippet "0.8.0")) + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 3 +;; of the License, or (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; The Emacs Lisp Python Environment in Emacs + +;; Elpy is an Emacs package to bring powerful Python editing to Emacs. +;; It combines a number of existing Emacs packages, and uses one of a +;; selection of Python packages for code introspection. + +;; To use, you need to install not only this package, but a few Python +;; packages as well. See the installation instructions on the wiki. + +;; Documentation is available there as well. + +;; https://github.com/jorgenschaefer/elpy/wiki + +;;; Writing Elpy Modules: + +;; A module is a function which is called with one or more arguments. +;; This first argument is the command specifier symbol, which can be +;; one of the following: + +;; global-init: +;; - Called once, when Elpy is enabled using `elpy-enable'. + +;; global-stop: +;; - Called once, when Elpy is disabled using `elpy-disable'. + +;; buffer-init: +;; - Called in a buffer when elpy-mode is enabled. + +;; buffer-stop: +;; - Called in a buffer when elpy-mode is disabled. + +;;; Writing test runners: + +;; A test runner is a function that receives four arguments, described +;; in the docstring of `elpy-test-at-point'. If only the first +;; argument is given, the test runner should find tests under this +;; directory and run them. If the others are given, the test runner +;; should run the specified test only, or as few as it can. + +;; Test runners should use an interactive spec of (interactive +;; (elpy-test-at-point)) so they can be called directly by the user. +;; For their main work, they can simply call `elpy-test-run'. See the +;; `elpy-test-discover-runner' for an example. + +;;; Code: + +(require 'cus-edit) +(require 'etags) +(require 'files-x) +(require 'grep) +(require 'ido) +(require 'json) +(require 'python) + +(require 'elpy-refactor) +(require 'pyvenv) + +(defconst elpy-version "1.7.1" + "The version of the Elpy lisp code.") + +;;;;;;;;;;;;;;;;;;;;;; +;;; User customization + +(defgroup elpy nil + "The Emacs Lisp Python Environment." + :prefix "elpy-" + :group 'languages) + +(defcustom elpy-interactive-python-command "python" + "Command to use for the interactive shell. + +Customize this option to use a different interactive shell. If +the value starts with \"ipython\", it will set up python.el so +that it deals with ipytohon's particular prompt and features. + +From your .emacs, you can use `elpy-use-ipython' and +`elpy-use-cpython' instead." + :type '(choice (const :tag "Standard Python (python)" "python") + (const :tag "Standard Python 2 (python2)" "python2") + (const :tag "Standard Python 3 (python3)" "python3") + (const :tag "IPython" "ipython") + (string :tag "Other")) + :set (lambda (var val) + (set-default var val) + (if (string-match "ipython" val) + (elpy-use-ipython val) + (elpy-use-cpython val))) + ;; Don't use the default function because the default values are + ;; correct, and `elpy-use-cpython' is not available yet. + :initialize #'set-default + :safe (lambda (val) + (member val '("python" "python2" "python3" "ipython"))) + :group 'elpy) + +(defcustom elpy-mode-hook nil + "Hook run when `elpy-mode' is enabled. + +This can be used to enable minor modes for Python development." + :type 'hook + :options '(subword-mode hl-line-mode) + :group 'elpy) + +(defcustom elpy-modules '(elpy-module-sane-defaults + elpy-module-company + elpy-module-eldoc + elpy-module-flymake + elpy-module-highlight-indentation + elpy-module-pyvenv + elpy-module-yasnippet) + "Which Elpy modules to use. + +Elpy can use a number of modules for additional features, which +can be inidividually enabled or disabled." + :type '(set (const :tag "Inline code completion (company-mode)" + elpy-module-company) + (const :tag "Show function signatures (ElDoc)" + elpy-module-eldoc) + (const :tag "Highlight syntax errors (Flymake)" + elpy-module-flymake) + (const :tag "Show the virtualenv in the mode line (pyvenv)" + elpy-module-pyvenv) + (const :tag "Display indentation markers (highlight-indentation)" + elpy-module-highlight-indentation) + (const :tag "Expand code snippets (YASnippet)" + elpy-module-yasnippet) + (const :tag "Configure some sane defaults for Emacs" + elpy-module-sane-defaults)) + :group 'elpy) + +(defcustom elpy-project-ignored-directories ' (".bzr" "CVS" ".git" ".hg" ".svn" + ".tox" "build" "dist" + ".cask") + "Directories ignored by functions working on the whole project." + :type '(repeat string) + :safe (lambda (val) + (cl-every #'stringp val)) + :group 'elpy) + +(defcustom elpy-project-root nil + "The root of the project the current buffer is in. + +There is normally no use in setting this variable directly, as +Elpy tries to detect the project root automatically. See +`elpy-project-root-finder-functions' for a way of influencing +this. + +Setting this variable globally will override Elpy's automatic +project detection facilities entirely. + +Alternatively, you can set this in file- or directory-local +variables using \\[add-file-local-variable] or +\\[add-dir-local-variable]. + +Do not use this variable in Emacs Lisp programs. Instead, call +the `elpy-project-root' function. It will do the right thing." + :type 'directory + :safe 'file-directory-p + :group 'elpy) +(make-variable-buffer-local 'elpy-project-root) + +(defcustom elpy-project-root-finder-functions + '(elpy-project-find-projectile-root + elpy-project-find-python-root + elpy-project-find-git-root + elpy-project-find-hg-root + elpy-project-find-svn-root) + "List of functions to ask for the current project root. + +These will be checked in turn. The first directory found is used." + :type '(set (const :tag "Projectile project root" + elpy-project-find-projectile-root) + (const :tag "Python project (setup.py, setup.cfg)" + elpy-project-find-python-root) + (const :tag "Git repository root (.git)" + elpy-project-find-git-root) + (const :tag "Mercurial project root (.hg)" + elpy-project-find-hg-root) + (const :tag "Subversion project root (.svn)" + elpy-project-find-svn-root)) + :group 'elpy) + +(defcustom elpy-rpc-backend nil + "Your preferred backend. + +Elpy can use different backends for code introspection. These +need to be installed separately using pip or other mechanisms to +make them available to Python. If you prefer not to do this, you +can use the native backend, which is very limited but does not +have any external requirements." + :type '(choice (const :tag "Rope" "rope") + (const :tag "Jedi" "jedi") + (const :tag "Automatic" nil)) + :safe (lambda (val) + (member val '("rope" "jedi" "native" nil))) + :group 'elpy) + +(defcustom elpy-rpc-large-buffer-size 4096 + "Size for a source buffer up to which it will be sent directly. + +The Elpy RPC protocol uses JSON as the serialization format. +Large buffers take a long time to encode, so Elpy can transmit +them via temporary files. If a buffer is larger than this value, +it is sent via a temporary file." + :type 'integer + :safe #'integerp + :group 'elpy) + +(defcustom elpy-rpc-python-command (if (eq window-system 'w32) + "pythonw" + "python") + "The Python interpreter for the RPC backend. + +This should be the same interpreter the project will be run with, +and not an interactive shell like ipython." + :type '(choice (const :tag "python" "python") + (const :tag "python2" "python2") + (const :tag "python3" "python3") + (const :tag "pythonw (Python on Windows)" "pythonw") + (string :tag "Other")) + :safe (lambda (val) + (member val '("python" "python2" "python3" "pythonw"))) + :group 'elpy) + +(defcustom elpy-rpc-pythonpath (file-name-directory (locate-library "elpy")) + "A directory to add to the PYTHONPATH for the RPC process. + +This should be a directory where the elpy module can be found. If +this is nil, it's assumed elpy can be found in the standard path. +Usually, there is no need to change this." + :type 'directory + :safe #'file-directory-p + :group 'elpy) + +(defcustom elpy-rpc-timeout 1 + "Number of seconds to wait for a response when blocking. + +When Elpy blocks Emacs to wait for a response from the RPC +process, it will assume it won't come or wait too long after this +many seconds. On a slow computer, or if you have a large project, +you might want to increase this. + +A setting of nil means to block indefinitely." + :type '(choice (const :tag "Block indefinitely" nil) + integer) + :safe (lambda (val) + (or (integerp val) + (null val))) + :group 'elpy) + +(defcustom elpy-rpc-error-timeout 30 + "Minimum number of seconds between error popups. + +When Elpy encounters an error in the backend, it will display a +lengthy description of the problem for a bug report. This hangs +Emacs for a moment, and can be rather annoying if it happens +repeatedly while editing a source file. + +If this variabl is non-nil, Elpy will not display the error +message again within this amount of seconds." + :type 'integer + :group 'elpy) + +(defcustom elpy-eldoc-show-current-function t + "If true, show the current function if no calltip is available. + +When Elpy can not find the calltip of the function call at point, +it can show the name of the function or class and method being +edited instead. Setting this variable to nil disables this feature." + :type 'boolean + :group 'elpy) + +(defcustom elpy-test-runner 'elpy-test-discover-runner + "The test runner to use to run tests." + :type '(choice (const :tag "Unittest Discover" elpy-test-discover-runner) + (const :tag "Django Discover" elpy-test-django-runner) + (const :tag "Nose" elpy-test-nose-runner) + (const :tag "py.test" elpy-test-pytest-runner) + (const :tag "Twisted Trial" elpy-test-trial-runner)) + :safe 'elpy-test-runner-p + :group 'elpy) + +(defcustom elpy-test-discover-runner-command '("python" "-m" "unittest") + "The command to use for `elpy-test-discover-runner'." + :type '(repeat string) + :group 'elpy) + +(defcustom elpy-test-django-runner-command '("django-admin.py" "test" + "--noinput") + "The command to use for `elpy-test-django-runner'." + :type '(repeat string) + :group 'elpy) + +(defcustom elpy-test-nose-runner-command '("nosetests") + "The command to use for `elpy-test-django-runner'." + :type '(repeat string) + :group 'elpy) + +(defcustom elpy-test-trial-runner-command '("trial") + "The command to use for `elpy-test-django-runner'." + :type '(repeat string) + :group 'elpy) + +(defcustom elpy-test-pytest-runner-command '("py.test") + "The command to use for `elpy-test-django-runner'." + :type '(repeat string) + :group 'elpy) + +;;;;;;;;;;;;; +;;; Elpy Mode + +(defvar elpy-mode-map + (let ((map (make-sparse-keymap))) + ;; Alphabetical order to make it easier to find free C-c C-X + ;; bindings in the future. Heh. + + ;; (define-key map (kbd "") 'python-indent-dedent-line-backspace) + ;; (define-key map (kbd "") 'python-indent-dedent-line) + + ;; (define-key map (kbd "C-M-x") 'python-shell-send-defun) + ;; (define-key map (kbd "C-c <") 'python-indent-shift-left) + ;; (define-key map (kbd "C-c >") 'python-indent-shift-right) + (define-key map (kbd "C-c C-c") 'elpy-shell-send-region-or-buffer) + (define-key map (kbd "C-c C-z") 'elpy-shell-switch-to-shell) + (define-key map (kbd "C-c C-d") 'elpy-doc) + (define-key map (kbd "C-c C-e") 'elpy-multiedit-python-symbol-at-point) + (define-key map (kbd "C-c C-f") 'elpy-find-file) + (define-key map (kbd "C-c RET") 'elpy-importmagic-add-import) + (define-key map (kbd "C-c ") 'elpy-importmagic-fixup) + (define-key map (kbd "C-c C-n") 'elpy-flymake-next-error) + (define-key map (kbd "C-c C-o") 'elpy-occur-definitions) + (define-key map (kbd "C-c C-p") 'elpy-flymake-previous-error) + (define-key map (kbd "C-c C-r") 'elpy-refactor) + (define-key map (kbd "C-c C-s") 'elpy-rgrep-symbol) + (define-key map (kbd "C-c C-t") 'elpy-test) + (define-key map (kbd "C-c C-v") 'elpy-check) + ;; (define-key map (kbd "C-c C-z") 'python-shell-switch-to-shell) + + (define-key map (kbd "") 'elpy-open-and-indent-line-below) + (define-key map (kbd "") 'elpy-open-and-indent-line-above) + + (define-key map (kbd "") 'elpy-nav-forward-block) + (define-key map (kbd "") 'elpy-nav-backward-block) + (define-key map (kbd "") 'elpy-nav-backward-indent) + (define-key map (kbd "") 'elpy-nav-forward-indent) + + (define-key map (kbd "") 'elpy-nav-move-iblock-down) + (define-key map (kbd "") 'elpy-nav-move-iblock-up) + (define-key map (kbd "") 'elpy-nav-move-iblock-left) + (define-key map (kbd "") 'elpy-nav-move-iblock-right) + + (define-key map (kbd "M-.") 'elpy-goto-definition) + (define-key map (kbd "M-TAB") 'elpy-company-backend) + + map) + "Key map for the Emacs Lisp Python Environment.") + +(easy-menu-define elpy-menu elpy-mode-map + "Elpy Mode Menu" + '("Elpy" + ["Documentation" elpy-doc + :help "Get documentation for symbol at point"] + ["Run Tests" elpy-test + :help "Run test at point, or all tests in the project"] + ["Go to Definition" elpy-goto-definition + :help "Go to the definition of the symbol at point"] + ["Go to previous definition" pop-tag-mark + :active (not (ring-empty-p find-tag-marker-ring)) + :help "Return to the position"] + ["Complete" elpy-company-backend + :keys "M-TAB" + :help "Complete at point"] + ["Refactor" elpy-refactor + :help "Refactor options"] + "---" + ("Interactive Python" + ["Switch to Python Shell" elpy-shell-switch-to-shell + :help "Start and switch to the interactive Python"] + ["Send Region or Buffer" elpy-shell-send-region-or-buffer + :label (if (use-region-p) + "Send Region to Python" + "Send Buffer to Python") + :help "Send the current region or the whole buffer to Python"] + ["Send Definition" python-shell-send-defun + :help "Send current definition to Python"]) + ("Project" + ["Find File" elpy-find-file + :help "Interactively find a file in the current project"] + ["Find Symbol" elpy-rgrep-symbol + :help "Find occurrences of a symbol in the current project"] + ["Set Project Root" elpy-set-project-root + :help "Change the current project root"] + ["Set Project Variable" elpy-set-project-variable + :help "Configure a project-specific option"]) + ("Syntax Check" + ["Check Syntax" elpy-check + :help "Check the syntax of the current file"] + ["Next Error" elpy-flymake-next-error + :help "Go to the next inline error, if any"] + ["Previous Error" elpy-flymake-previous-error + :help "Go to the previous inline error, if any"]) + ("Indentation Blocks" + ["Dedent" elpy-nav-move-iblock-left + :help "Dedent current block or region" + :suffix (if (use-region-p) "Region" "Block")] + ["Indent" elpy-nav-move-iblock-right + :help "Indent current block or region" + :suffix (if (use-region-p) "Region" "Block")] + ["Up" elpy-nav-move-iblock-up + :help "Move current block or region up" + :suffix (if (use-region-p) "Region" "Block")] + ["Down" elpy-nav-move-iblock-down + :help "Move current block or region down" + :suffix (if (use-region-p) "Region" "Block")]) + "---" + ["News" elpy-news t] + ["Configure" elpy-config t])) + +;;;###autoload +(defun elpy-enable (&optional ignored) + "Enable Elpy in all future Python buffers." + (interactive) + (when (< emacs-major-version 24) + (error "Elpy requires Emacs 24 or newer")) + (when ignored + (warn "The argument to `elpy-enable' is deprecated, customize `elpy-modules' instead")) + (let ((filename (find-lisp-object-file-name 'python-mode + 'symbol-function))) + (when (and filename + (string-match "/python-mode\\.el\\'" + filename)) + (error (concat "You are using python-mode.el. " + "Elpy only works with python.el from " + "Emacs 24 and above")))) + (elpy-modules-global-init) + (add-hook 'python-mode-hook 'elpy-mode)) + +(defun elpy-disable () + "Disable Elpy in all future Python buffers." + (interactive) + (remove-hook 'python-mode-hook 'elpy-mode) + (elpy-modules-global-stop)) + +;;;###autoload +(define-minor-mode elpy-mode + "Minor mode in Python buffers for the Emacs Lisp Python Environment. + +This mode fully supports virtualenvs. Once you switch a +virtualenv using \\[pyvenv-workon], you can use +\\[elpy-rpc-restart] to make the elpy Python process use your +virtualenv. + +See https://github.com/jorgenschaefer/elpy/wiki/Keybindings for a +more structured list. + +\\{elpy-mode-map}" + :lighter " Elpy" + (when (not (eq major-mode 'python-mode)) + (error "Elpy only works with `python-mode'")) + (cond + (elpy-mode + (elpy-modules-buffer-init)) + ((not elpy-mode) + (elpy-modules-buffer-stop)))) + +;;;;;;;;;;;;; +;;; Elpy News + +(defun elpy-news () + "Display Elpy's release notes." + (interactive) + (with-current-buffer (get-buffer-create "*Elpy News*") + (let ((inhibit-read-only t)) + (erase-buffer) + (insert-file (concat (file-name-directory (locate-library "elpy")) + "NEWS.rst")) + (help-mode)) + (pop-to-buffer (current-buffer)))) + +;;;;;;;;;;;;;;; +;;; Elpy Config + +(defvar elpy-config--related-custom-groups + '(("Elpy" elpy "elpy-") + ("Python" python "python-") + ("Virtual Environments (Pyvenv)" pyvenv "pyvenv-") + ("Completion (Company)" company "company-") + ("Call Signatures (ElDoc)" eldoc "eldoc-") + ("Inline Errors (Flymake)" flymake "flymake-") + ("Snippets (YASnippet)" yasnippet "yas-") + ("Directory Grep (rgrep)" grep "grep-") + ("Search as You Type (ido)" ido "ido-") + ;; ffip does not use defcustom + ;; highlight-indent does not use defcustom, either. Its sole face + ;; is defined in basic-faces. + )) + +(defvar elpy-config--get-config "import json +import sys + +try: + import xmlrpclib +except ImportError: + import xmlrpc.client as xmlrpclib + +from distutils.version import LooseVersion + + +def latest(package, version=None): + try: + pypi = xmlrpclib.ServerProxy('https://pypi.python.org/pypi') + latest = pypi.package_releases(package)[0] + if version is None or LooseVersion(version) < LooseVersion(latest): + return latest + else: + return None + except: + return None + + +config = {} +config['python_version'] = ('{major}.{minor}.{micro}' + .format(major=sys.version_info[0], + minor=sys.version_info[1], + micro=sys.version_info[2])) + +try: + import elpy + config['elpy_version'] = elpy.__version__ +except: + config['elpy_version'] = None + +try: + import jedi + if isinstance(jedi.__version__, tuple): + config['jedi_version'] = '.'.join(str(x) for x in jedi.__version__) + else: + config['jedi_version'] = jedi.__version__ + config['jedi_latest'] = latest('jedi', config['jedi_version']) +except: + config['jedi_version'] = None + config['jedi_latest'] = latest('jedi') + +try: + import rope + config['rope_version'] = rope.VERSION + if sys.version_info[0] <= 2: + config['rope_latest'] = latest('rope', config['rope_version']) + else: + config['rope_latest'] = latest('rope_py3k', config['rope_version']) +except: + config['rope_version'] = None + config['rope_latest'] = latest('rope') + +try: + import importmagic + config['importmagic_version'] = importmagic.__version__ + config['importmagic_latest'] = latest('importmagic', config['importmagic_version']) +except: + config['importmagic_version'] = None + config['importmagic_latest'] = latest('importmagic') + +json.dump(config, sys.stdout) +") + +(defun elpy-config-error (&optional fmt &rest args) + "Note a configuration problem. + +This will show a message in the minibuffer that tells the user to +use \\[elpy-config]." + (let ((msg (if fmt + (apply #'format fmt args) + "Elpy is not properly configured"))) + (error "%s; use M-x elpy-config to configure it" msg))) + +;;;###autoload +(defun elpy-config () + "Configure Elpy. + +This function will pop up a configuration buffer, which is mostly +a customize buffer, but has some more options." + (interactive) + (let ((buf (custom-get-fresh-buffer "*Elpy Config*")) + (config (elpy-config--get-config)) + (custom-search-field nil)) + (with-current-buffer buf + (elpy-insert--header "Elpy Configuration") + + (elpy-config--insert-configuration-problems config) + + (elpy-insert--header "Options") + + (let ((custom-buffer-style 'tree)) + (Custom-mode) + (elpy-config--insert-help) + (dolist (cust elpy-config--related-custom-groups) + (widget-create 'custom-group + :custom-last t + :custom-state 'hidden + :tag (car cust) + :value (cadr cust))) + (widget-setup) + (goto-char (point-min)))) + (pop-to-buffer-same-window buf))) + +;;;###autoload +(defun elpy-version () + "Display the version of Elpy." + (interactive) + (message "Elpy %s (use M-x elpy-config for details)" elpy-version)) + +(defun elpy-config--insert-help () + (let ((start (point))) + ;; Help display from `customize-browse' + (widget-insert (format "\ +%s buttons; type RET or click mouse-1 +on a button to invoke its action. +Invoke [+] to expand a group, and [-] to collapse an expanded group.\n" + (if custom-raised-buttons + "`Raised' text indicates" + "Square brackets indicate"))) + (if custom-browse-only-groups + (widget-insert "\ +Invoke the [Group] button below to edit that item in another window.\n\n") + (widget-insert "Invoke the ") + (widget-create 'item + :format "%t" + :tag "[Group]" + :tag-glyph "folder") + (widget-insert ", ") + (widget-create 'item + :format "%t" + :tag "[Face]" + :tag-glyph "face") + (widget-insert ", and ") + (widget-create 'item + :format "%t" + :tag "[Option]" + :tag-glyph "option") + (widget-insert " buttons below to edit that +item in another window.\n\n") + + (fill-region start (point))))) + +(defun elpy-config--insert-configuration-problems (&optional config) + "Insert help text and widgets for configuration problems." + (when (not config) + (setq config (elpy-config--get-config))) + (let* ((python-version (gethash "python_version" config)) + (rope-pypi-package (if (and python-version + (string-match "^3\\." python-version)) + "rope_py3k" + "rope"))) + + (elpy-config--insert-configuration-table config) + (insert "\n") + + ;; Python not found + (when (not (gethash "python_rpc_executable" config)) + (elpy-insert--para + "Elpy can not find the configured Python interpreter. Please make " + "sure that the variable `elpy-rpc-python-command' points to a " + "command in your PATH. You can change the variable below.\n\n")) + + ;; No virtual env + (when (and (gethash "python_rpc_executable" config) + (not (gethash "virtual_env" config))) + (elpy-insert--para + "You have not activated a virtual env. While Elpy supports this, " + "it is often a good idea to work inside a virtual env. You can use " + "M-x pyvenv-activate or M-x pyvenv-workon to activate a virtual " + "env.\n\n")) + + ;; No virtual env, but ~/.local/bin not in PATH + (when (and (not (memq system-type '(ms-dos windows-nt))) + (gethash "python_rpc_executable" config) + (not pyvenv-virtual-env) + (not (or (member (expand-file-name "~/.local/bin") + exec-path) + (member (expand-file-name "~/.local/bin/") + exec-path)))) + (elpy-insert--para + "The directory ~/.local/bin/ is not in your PATH, even though you " + "do not have an active virtualenv. Installing Python packages " + "locally will create executables in that directory, so Emacs " + "won't find them. If you are missing some commands, do add this " + "directory to your PATH.\n\n")) + + ;; Python found, but can't find the elpy module + (when (and (gethash "python_rpc_executable" config) + (not (gethash "elpy_version" config))) + (elpy-insert--para + "The Python interpreter could not find the elpy module. " + "Make sure the module is installed" + (if (gethash "virtual_env" config) + " in the current virtualenv.\n" + ".\n")) + (insert "\n") + (widget-create 'elpy-insert--pip-button :package "elpy") + (insert "\n\n")) + + ;; Bad backend version + (when (and (gethash "elpy_version" config) + (not (equal (gethash "elpy_version" config) + elpy-version))) + (let ((elpy-python-version (gethash "elpy_version" config))) + (elpy-insert--para + "The Elpy backend is version " elpy-python-version " while " + "the Emacs package is " elpy-version ". This is incompatible. " + (if (version< elpy-python-version elpy-version) + "Please upgrade the Python module." + "Please upgrade the Emacs Lisp package.") + "\n"))) + + ;; Otherwise unparseable output. + (when (gethash "error_output" config) + (elpy-insert--para + "There was an unexpected problem starting the RPC process. Please " + "check the following output to see if this makes sense to you. " + "To me, it doesn't.\n") + (insert "\n" + (gethash "error_output" config) "\n" + "\n")) + + ;; Requested backend unavailable + (when (and (gethash "python_rpc_executable" config) + (or (and (equal elpy-rpc-backend "rope") + (not (gethash "rope_version" config))) + (and (equal elpy-rpc-backend "jedi") + (not (gethash "jedi_version" config))))) + (elpy-insert--para + "You requested Elpy to use the backend " elpy-rpc-backend ", " + "but the Python interpreter could not load that module. Make " + "sure the module is installed, or change the value of " + "`elpy-rpc-backend' below to one of the available backends.\n") + (insert "\n") + (widget-create 'elpy-insert--pip-button + :package (if (equal elpy-rpc-backend "rope") + rope-pypi-package + "jedi")) + (insert "\n\n")) + + ;; No backend available. + (when (and (gethash "python_rpc_executable" config) + (and (not elpy-rpc-backend) + (not (gethash "rope_version" config)) + (not (gethash "jedi_version" config)))) + (elpy-insert--para + "There is no backend available. Please install either Rope or Jedi.\n") + (insert "\n") + (widget-create 'elpy-insert--pip-button :package rope-pypi-package) + (insert "\n") + (widget-create 'elpy-insert--pip-button :package "jedi") + (insert "\n\n")) + + ;; Newer version of Rope available + (when (and (gethash "rope_version" config) + (gethash "rope_latest" config)) + (elpy-insert--para + "There is a newer version of Rope available.\n") + (insert "\n") + (widget-create 'elpy-insert--pip-button + :package rope-pypi-package :upgrade t) + (insert "\n\n")) + + ;; Newer version of Jedi available + (when (and (gethash "jedi_version" config) + (gethash "jedi_latest" config)) + (elpy-insert--para + "There is a newer version of Jedi available.\n") + (insert "\n") + (widget-create 'elpy-insert--pip-button + :package "jedi" :upgrade t) + (insert "\n\n")) + + ;; No importmagic available + (when (not (gethash "importmagic_version" config)) + (elpy-insert--para + "The importmagic package is not available. Commands using this will " + "not work.\n") + (insert "\n") + (widget-create 'elpy-insert--pip-button + :package "importmagic") + (insert "\n\n")) + + ;; Newer version of importmagic available + (when (and (gethash "importmagic_version" config) + (gethash "importmagic_latest" config)) + (elpy-insert--para + "There is a newer version of the importmagic package available.\n") + (insert "\n") + (widget-create 'elpy-insert--pip-button + :package "importmagic" :upgrade t) + (insert "\n\n")) + + ;; flake8, the default syntax checker, not found + (when (not (executable-find "flake8")) + (elpy-insert--para + "The configured syntax checker could not be found. Elpy uses this " + "program to provide syntax checks of your programs, so you might " + "want to install one. Elpy by default uses flake8.\n") + (insert "\n") + (widget-create 'elpy-insert--pip-button :package "flake8") + (insert "\n\n")) + + )) + +(defun elpy-config--get-config () + "Return the configuration from `elpy-rpc-python-command'. + +This returns a hash table with the following keys (all strings): + +emacs_version +python_rpc +python_rpc_executable +python_interactive +python_interactive_executable +python_version (RPC) +elpy_version +jedi_version +rope_version +virtual_env +virtual_env_short" + (with-temp-buffer + (let ((config (make-hash-table :test #'equal))) + (puthash "emacs_version" emacs-version config) + (puthash "python_rpc" elpy-rpc-python-command config) + (puthash "python_rpc_executable" + (executable-find elpy-rpc-python-command) + config) + (let ((interactive-python (if (boundp 'python-python-command) + python-python-command + python-shell-interpreter))) + (puthash "python_interactive" + interactive-python + config) + (puthash "python_interactive_executable" + (executable-find interactive-python) + config)) + (let ((venv (getenv "VIRTUAL_ENV"))) + (puthash "virtual_env" venv config) + (if venv + (puthash "virtual_env_short" (file-name-nondirectory venv) config) + (puthash "virtual_env_short" nil config))) + (let ((return-value (ignore-errors + (let ((process-environment + (elpy-rpc--environment)) + (default-directory "/")) + (call-process elpy-rpc-python-command + nil + (current-buffer) + nil + "-c" + elpy-config--get-config))))) + (when return-value + (let ((data (ignore-errors + (let ((json-array-type 'list)) + (goto-char (point-min)) + (json-read))))) + (if (not data) + (puthash "error_output" (buffer-string) config) + (dolist (pair data) + (puthash (symbol-name (car pair)) (cdr pair) config)))))) + config))) + +(defun elpy-config--insert-configuration-table (&optional config) + "Insert a table describing the current Elpy config." + (when (not config) + (setq config (elpy-config--get-config))) + (let ((emacs-version (gethash "emacs_version" config)) + (python-version (gethash "python_version" config)) + (python-rpc (gethash "python_rpc" config)) + (python-rpc-executable (gethash "python_rpc_executable" config)) + (python-interactive (gethash "python_interactive" config)) + (python-interactive-executable (gethash "python_interactive_executable" + config)) + (elpy-python-version (gethash "elpy_version" config)) + (jedi-version (gethash "jedi_version" config)) + (jedi-latest (gethash "jedi_latest" config)) + (rope-version (gethash "rope_version" config)) + (rope-latest (gethash "rope_latest" config)) + (importmagic-version (gethash "importmagic_version" config)) + (importmagic-latest (gethash "importmagic_latest" config)) + (virtual-env (gethash "virtual_env" config)) + (virtual-env-short (gethash "virtual_env_short" config)) + table maxwidth) + (setq table + `(("Virtualenv" . ,(if (gethash "virtual_env" config) + (format "%s (%s)" + virtual-env-short + virtual-env) + "None")) + ("RPC Python" . ,(cond + (python-version + (format "%s (%s)" + python-version + python-rpc-executable)) + (python-rpc-executable + python-rpc-executable) + (python-rpc + (format "%s (not found)" python-rpc)) + (t + (format "Not configured")))) + ("Interactive Python" . ,(cond + (python-interactive-executable + (format "%s (%s)" + python-interactive + python-interactive-executable)) + (python-interactive + (format "%s (not found)" + python-interactive)) + (t + "Not configured"))) + ("Emacs" . ,emacs-version) + ("Elpy" . ,(cond + ((and elpy-python-version elpy-version + (equal elpy-python-version elpy-version)) + elpy-version) + (elpy-python-version + (format "%s (Python), %s (Emacs Lisp)" + elpy-python-version + elpy-version)) + (t + (format "Not found (Python), %s (Emacs Lisp)" + elpy-version)))) + ("Jedi" . ,(elpy-config--package-link "jedi" + jedi-version + jedi-latest)) + ("Rope" . ,(elpy-config--package-link "rope" + rope-version + rope-latest)) + ("Importmagic" . ,(elpy-config--package-link "importmagic" + importmagic-version + importmagic-latest)) + ("Syntax checker" . ,(let ((syntax-checker + (executable-find + python-check-command))) + (if syntax-checker + (format "%s (%s)" + (file-name-nondirectory + syntax-checker) + syntax-checker) + (format "Not found (%s)" + python-check-command)))))) + (setq maxwidth 0) + (dolist (row table) + (when (> (length (car row)) + maxwidth) + (setq maxwidth (length (car row))))) + (dolist (row table) + (insert (car row) + (make-string (- maxwidth (length (car row))) + ?.) + ": " + (cdr row) + "\n")))) + +(defun elpy-config--package-link (name version latest) + "Return a string detailing a Python package. + +NAME is the PyPI name of the package. VERSION is the currently +installed version. LATEST is the latest-available version on +PyPI, or nil if that's VERSION." + (cond + ((and (not version) (not latest)) + "Not found") + ((not latest) + version) + ((not version) + (format "Not found (%s available)" latest)) + (t + (format "%s (%s available)" version latest)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Elpy Formatted Insertion + +(defmacro elpy-insert--popup (buffer-name &rest body) + "Pop up a help buffer named BUFFER-NAME and execute BODY in it." + (declare (indent 1)) + `(with-help-window ,buffer-name + (with-current-buffer standard-output + ,@body))) + +(defun elpy-insert--para (&rest messages) + "Insert a bunch of text and then fill it." + (let ((start (point))) + (mapc (lambda (obj) + (if (stringp obj) + (insert obj) + (insert (format "%s" obj)))) + messages) + (fill-region start (point)))) + +(defun elpy-insert--header (&rest text) + "Insert TEXT has a header for a buffer." + (insert (propertize (mapconcat #'(lambda (x) x) + text + "") + 'face 'header-line) + "\n" + "\n")) + +(define-widget 'elpy-insert--pip-button 'item + "A button that runs pip (or an alternative)." + :button-prefix "[" + :button-suffix "]" + :format "%[run%] %v" + :value-create 'elpy-insert--pip-button-value-create + :action 'elpy-insert--pip-button-action) + +(defun elpy-insert--pip-button-value-create (widget) + "The :value-create option for the pip button widget." + (let* ((python-package (widget-get widget :package)) + (do-upgrade (widget-get widget :upgrade)) + (upgrade-option (if do-upgrade + "--upgrade " + "")) + (do-user-install (not (or (getenv "VIRTUAL_ENV") + pyvenv-virtual-env))) + (user-option (if do-user-install + "--user " + "")) + + (command (cond + ((executable-find "pip") + (format "pip install %s%s%s" + user-option upgrade-option python-package)) + ((executable-find "easy_install") + (format "easy_install %s%s" + user-option python-package)) + (t + (error "Neither easy_install nor pip found"))))) + (widget-put widget :command command) + (insert command))) + +(defun elpy-insert--pip-button-action (widget &optional event) + "The :action option for the pip button widget." + (async-shell-command (widget-get widget :command))) + +;;;;;;;;;;;; +;;; Projects + +(defvar elpy-project--variable-name-history nil + "The history for `elpy-project--read-project-variable'") + +(defun elpy-project-root () + "Return the root of the current buffer's project. + +This can very well be nil if the current file is not part of a +project. + +See `elpy-project-root-finder-functions' for a way to configure +how the project root is found. You can also set the variable +`elpy-project-root' in, for example, .dir-locals.el to override +this." + (when (not elpy-project-root) + (setq elpy-project-root + (run-hook-with-args-until-success + 'elpy-project-root-finder-functions))) + elpy-project-root) + +(defun elpy-set-project-root (new-root) + "Set the Elpy project root to NEW-ROOT." + (interactive "DNew project root: ") + (setq elpy-project-root new-root)) + +(defun elpy-project-find-python-root () + "Return the current Python project root, if any. + +This is marked with setup.py or setup.cfg." + (or (locate-dominating-file default-directory "setup.py") + (locate-dominating-file default-directory "setup.cfg"))) + +(defun elpy-project-find-git-root () + "Return the current git repository root, if any." + (locate-dominating-file default-directory ".git")) + +(defun elpy-project-find-hg-root () + "Return the current git repository root, if any." + (locate-dominating-file default-directory ".hg")) + +(defun elpy-project-find-svn-root () + "Return the current git repository root, if any." + (locate-dominating-file default-directory + (lambda (dir) + (and (file-directory-p (format "%s/.svn" dir)) + (not (file-directory-p (format "%s/../.svn" + dir))))))) + +(defun elpy-project-find-projectile-root () + "Return the current project root according to projectile." + ;; `ignore-errors' both to avoid an unbound function error as well + ;; as ignore projectile saying there is no project root here. + (ignore-errors + (projectile-project-root))) + +(defun elpy-library-root () + "Return the root of the Python package chain of the current buffer. + +That is, if you have /foo/package/module.py, it will return /foo, +so that import package.module will pick up module.py." + (locate-dominating-file default-directory + (lambda (dir) + (not (file-exists-p + (format "%s/__init__.py" + dir)))))) + +(defun elpy-project--read-project-variable (prompt) + "Prompt the user for a variable name to set project-wide." + (let* ((prefixes (mapcar (lambda (cust) + (nth 2 cust)) + elpy-config--related-custom-groups)) + (var-regex (format "^%s" (regexp-opt prefixes)))) + (intern + (completing-read + prompt + obarray + (lambda (sym) + (and (get sym 'safe-local-variable) + (string-match var-regex (symbol-name sym)) + (get sym 'custom-type))) + :require-match + nil + 'elpy-project--variable-name-history)))) + +(defun elpy-project--read-variable-value (prompt variable) + "Read the value for VARIABLE from the user." + (let ((custom-type (get variable 'custom-type))) + (if custom-type + (widget-prompt-value (if (listp custom-type) + custom-type + (list custom-type)) + prompt + (if (boundp variable) + (funcall + (or (get variable 'custom-get) + 'symbol-value) + variable)) + (not (boundp variable))) + (eval-minibuffer prompt)))) + +(defun elpy-set-project-variable (variable value) + "Set or remove a variable in the project-wide .dir-locals.el. + +With prefix argument, remove the variable." + (interactive + (let* ((variable (elpy-project--read-project-variable + (if current-prefix-arg + "Remove project variable: " + "Set project variable: "))) + (value (if current-prefix-arg + nil + (elpy-project--read-variable-value (format "Value for %s: " + variable) + variable)))) + (list variable value))) + (with-current-buffer (find-file-noselect (format "%s/%s" + (elpy-project-root) + dir-locals-file)) + (modify-dir-local-variable nil + variable + value + (if current-prefix-arg + 'delete + 'add-or-replace)))) + +;;;;;;;;;;;;;;;;;;;;;;;; +;;; Search Project Files + +(defun elpy-rgrep-symbol (regexp) + "Search for REGEXP in the current project. + +REGEXP defaults to the symbol at point, or the current region if +active. + +With a prefix argument, always prompt for a string to search +for." + (interactive + (list + (cond + (current-prefix-arg + (read-from-minibuffer "Search in project for regexp: ")) + ((use-region-p) + (buffer-substring-no-properties (region-beginning) + (region-end))) + (t + (let ((symbol (thing-at-point 'symbol))) + (if symbol + (format "\\<%s\\>" symbol) + (read-from-minibuffer "Search in project for regexp: "))))))) + (grep-compute-defaults) + (let ((grep-find-ignored-directories (append elpy-project-ignored-directories + grep-find-ignored-directories))) + (rgrep regexp + "*.py" + (or (elpy-project-root) + default-directory))) + (with-current-buffer next-error-last-buffer + (let ((inhibit-read-only t)) + (save-excursion + (goto-char (point-min)) + (when (re-search-forward "^find .*" nil t) + (replace-match (format "Searching for '%s'\n" + (regexp-quote regexp)))))))) + +;;;;;;;;;;;;;;;;;;;;;; +;;; Find Project Files + +(defun elpy-find-file (&optional dwim) + "Efficiently find a file in the current project. + +With prefix argument, tries to guess what kind of file the user +wants to open. + +On an import line, it opens the file of that module. + +Otherwise, it opens a test file associated with the current file, +if one exists. A test file is named test_.py if the current +file is .py, and is either in the same directors or a +\"test\" or \"tests\" subdirectory." + (interactive "P") + (cond + ((and dwim + (buffer-file-name) + (save-excursion + (goto-char (line-beginning-position)) + (or (looking-at "^ *import +\\([[:alnum:]._]+\\)") + (looking-at "^ *from +\\([[:alnum:]._]+\\) +import +\\([[:alnum:]._]+\\)")))) + (let* ((module (if (match-string 2) + (format "%s.%s" (match-string 1) (match-string 2)) + (match-string 1))) + (path (elpy-find--resolve-module module))) + (if path + (find-file path) + (elpy-find-file nil)))) + ((and dwim + (buffer-file-name)) + (let ((test-file (elpy-find--test-file))) + (if test-file + (find-file test-file) + (elpy-find-file nil)))) + (t + (let ((ffip-prune-patterns elpy-project-ignored-directories) + (ffip-project-root (elpy-project-root)) + ;; Set up ido to use vertical file lists. + (ido-decorations '("\n" "" "\n" "\n..." + "[" "]" " [No match]" " [Matched]" + " [Not readable]" " [Too big]" + " [Confirm]")) + (ido-setup-hook (cons (lambda () + (define-key ido-completion-map (kbd "") + 'ido-next-match) + (define-key ido-completion-map (kbd "") + 'ido-prev-match)) + ido-setup-hook))) + (find-file-in-project))))) + +(defun elpy-find--test-file () + "Return the test file for the current file, if any. + +If this is a test file, return the non-test file. + +A test file is named test_.py if the current file is +.py, and is either in the same directors or a \"test\" or +\"tests\" subdirectory." + (catch 'return + (let (full-name directory file) + (setq full-name (buffer-file-name)) + (when (not full-name) + (throw 'return nil)) + (setq full-name (expand-file-name full-name) + directory (file-name-directory full-name) + file (file-name-nondirectory full-name)) + (if (string-match "^test_" file) + (let ((file (substring file 5))) + (dolist (implementation (list (format "%s/%s" directory file) + (format "%s/../%s" directory file))) + (when (file-exists-p implementation) + (throw 'return implementation)))) + (dolist (test (list (format "%s/test_%s" directory file) + (format "%s/test/test_%s" directory file) + (format "%s/tests/test_%s" directory file) + (format "%s/../test/test_%s" directory file) + (format "%s/../tests/test_%s" directory file))) + (when (file-exists-p test) + (throw 'return test))))))) + +(defun elpy-find--module-path (module) + "Return a directory path for MODULE. + +The resulting path is not guaranteed to exist. This simply +resolves leading periods relative to the current directory and +replaces periods in the middle of the string with slashes. + +Only works with absolute imports. Stop using implicit relative +imports. They're a bad idea." + (let* ((relative-depth (when(string-match "^\\.+" module) + (length (match-string 0 module)))) + (base-directory (if relative-depth + (format "%s/%s" + (buffer-file-name) + (mapconcat (lambda (_) + "../") + (make-vector relative-depth + nil) + "")) + (elpy-library-root))) + (file-name (replace-regexp-in-string + "\\." + "/" + (if relative-depth + (substring module relative-depth) + module)))) + (expand-file-name (format "%s/%s" base-directory file-name)))) + +(defun elpy-find--resolve-module (module) + "Resolve MODULE relative to the current file and project. + +Returns a full path name for that module." + (catch 'return + (let ((path (elpy-find--module-path module))) + (while (string-prefix-p (expand-file-name (elpy-library-root)) + path) + (dolist (name (list (format "%s.py" path) + (format "%s/__init__.py" path))) + (when (file-exists-p name) + (throw 'return name))) + (if (string-match "/$" path) + (setq path (substring path 0 -1)) + (setq path (file-name-directory path))))) + nil)) + +;;;;;;;;;;;;;;;;;;;;; +;;; Interactive Shell + +(defun elpy-use-ipython (&optional ipython) + "Set defaults to use IPython instead of the standard interpreter. + +With prefix arg, prompt for the command to use." + (interactive (list (when current-prefix-arg + (read-file-name "IPython command: ")))) + (when (not ipython) + (setq ipython "ipython")) + (cond + ;; Emacs 24 until 24.3 + ((boundp 'python-python-command) + (setq python-python-command ipython)) + ;; Emacs 24.3 + ((and (version<= "24.3" emacs-version) + (not (boundp 'python-shell-interpreter-interactive-arg))) + ;; This is from the python.el commentary. + ;; Settings for IPython 0.11: + (setq python-shell-interpreter ipython + python-shell-interpreter-args "" + python-shell-prompt-regexp "In \\[[0-9]+\\]: " + python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: " + python-shell-completion-setup-code + "from IPython.core.completerlib import module_completion" + python-shell-completion-module-string-code + "';'.join(module_completion('''%s'''))\n" + python-shell-completion-string-code + "';'.join(get_ipython().Completer.all_completions('''%s'''))\n")) + ;; Emacs 24.4 + ((boundp 'python-shell-interpreter-interactive-arg) + (setq python-shell-interpreter ipython + python-shell-interpreter-args "-i")) + (t + (error "I don't know how to set ipython settings for this Emacs")))) + +(defun elpy-use-cpython (&optional cpython) + "Set defaults to use the standard interpreter instead of IPython. + +With prefix arg, prompt for the command to use." + (interactive (list (when current-prefix-arg + (read-file-name "Python command: ")))) + (when (not cpython) + (setq cpython "python")) + (cond + ;; Emacs 24 until 24.3 + ((boundp 'python-python-command) + (setq python-python-command cpython)) + ;; Emacs 24.3 and onwards. + ((and (version<= "24.3" emacs-version) + (not (boundp 'python-shell-interpreter-interactive-arg))) + (setq python-shell-interpreter cpython + python-shell-interpreter-args "-i" + python-shell-prompt-regexp ">>> " + python-shell-prompt-output-regexp "" + python-shell-completion-setup-code +"try: + import readline +except ImportError: + def __COMPLETER_all_completions(text): [] +else: + import rlcompleter + readline.set_completer(rlcompleter.Completer().complete) + def __COMPLETER_all_completions(text): + import sys + completions = [] + try: + i = 0 + while True: + res = readline.get_completer()(text, i) + if not res: break + i += 1 + completions.append(res) + except NameError: + pass + return completions" + python-shell-completion-module-string-code "" + python-shell-completion-string-code + "';'.join(__COMPLETER_all_completions('''%s'''))\n")) + ;; Emacs 24.4 + ((boundp 'python-shell-interpreter-interactive-arg) + (setq python-shell-interpreter cpython + python-shell-interpreter-args "-i")) + (t + (error "I don't know how to set ipython settings for this Emacs")))) + +(defun elpy-shell-send-region-or-buffer (&optional arg) + "Send the active region or the buffer to the Python shell. + +If there is an active region, send that. Otherwise, send the +whole buffer. + +In Emacs 24.3 and later, without prefix argument, this will +escape the Python idiom of if __name__ == '__main__' to be false +to avoid accidental execution of code. With prefix argument, this +code is executed." + (interactive "P") + ;; Ensure process exists + (elpy-shell-get-or-create-process) + (let ((if-main-regex "^if +__name__ +== +[\"']__main__[\"'] *:") + (has-if-main nil)) + (if (use-region-p) + (let ((region (elpy-shell--region-without-indentation + (region-beginning) (region-end)))) + (setq has-if-main (string-match if-main-regex region)) + (python-shell-send-string region)) + (save-excursion + (goto-char (point-min)) + (setq has-if-main (re-search-forward if-main-regex nil t))) + (python-shell-send-buffer arg)) + (display-buffer (process-buffer (elpy-shell-get-or-create-process)) + nil + 'visible) + (when has-if-main + (message (concat "Removed if __main__ == '__main__' construct, " + "use a prefix argument to evaluate."))))) + +(defun elpy-shell-switch-to-shell () + "Switch to inferior Python process buffer." + (interactive) + (pop-to-buffer (process-buffer (elpy-shell-get-or-create-process)))) + +(defun elpy-shell-get-or-create-process () + "Get or create an inferior Python process for current buffer and return it." + (let* ((bufname (format "*%s*" (python-shell-get-process-name nil))) + (proc (get-buffer-process bufname))) + (if proc + proc + (run-python (python-shell-parse-command)) + (get-buffer-process bufname)))) + +(defun elpy-shell--region-without-indentation (beg end) + "Return the current region as a string, but without indentation." + (if (= beg end) + "" + (let ((region (buffer-substring beg end)) + (indent-level nil)) + (with-temp-buffer + (insert region) + (goto-char (point-min)) + (while (< (point) (point-max)) + (cond + ((and (not indent-level) + (not (looking-at "[ \t]*$"))) + (setq indent-level (current-indentation))) + ((and indent-level + (not (looking-at "[ \t]*$")) + (< (current-indentation) + indent-level)) + (error "Can't adjust indentation, consecutive lines indented less than starting line"))) + (forward-line)) + (indent-rigidly (point-min) + (point-max) + (- indent-level)) + (buffer-string))))) + +;;;;;;;;;;;;;;;;; +;;; Syntax Checks + +(defun elpy-check (&optional whole-project-p) + "Run `python-check-command' on the current buffer's file, + +or the project root if WHOLE-PROJECT-P is non-nil (interactively, +with a prefix argument)." + (interactive "P") + (when (not (buffer-file-name)) + (error "Can't check a buffer without a file.")) + (save-some-buffers (not compilation-ask-about-save) nil) + (let ((process-environment (python-shell-calculate-process-environment)) + (exec-path (python-shell-calculate-exec-path)) + (file-name-or-directory (expand-file-name + (if whole-project-p + (or (elpy-project-root) + (buffer-file-name)) + (buffer-file-name)))) + (extra-args (if whole-project-p + (concat " --exclude=" + (mapconcat #'identity + elpy-project-ignored-directories + ",")) + ""))) + (compilation-start (concat python-check-command + " " + (shell-quote-argument file-name-or-directory) + extra-args) + nil + (lambda (mode-name) + "*Python Check*")))) + +;;;;;;;;;;;;;; +;;; Navigation + +(defun elpy-goto-definition () + "Go to the definition of the symbol at point, if found." + (interactive) + (let ((location (elpy-rpc-get-definition))) + (if location + (elpy-goto-location (car location) (cadr location)) + (error "No definition found")))) + +(defun elpy-goto-location (filename offset) + "Show FILENAME at OFFSET to the user." + (ring-insert find-tag-marker-ring (point-marker)) + (let ((buffer (find-file filename))) + (with-current-buffer buffer + (with-selected-window (get-buffer-window buffer) + (goto-char (1+ offset)))))) + +(defun elpy-nav-forward-block () + "Move to the next line indented like point. + +This will skip over lines and statements with different +indentation levels." + (interactive) + (let ((indent (current-column)) + (start (point))) + (when (/= (% indent python-indent-offset) + 0) + (setq indent (* (1+ (/ indent python-indent-offset)) + python-indent-offset))) + (python-nav-forward-statement) + (while (and (< indent (current-indentation)) + (not (eobp))) + (python-nav-forward-statement)) + (when (< (current-indentation) + indent) + (goto-char start)))) + +(defun elpy-nav-backward-block () + "Move to the previous line indented like point. + +This will skip over lines and statements with different +indentation levels." + (interactive) + (let ((indent (current-column)) + (start (point))) + (when (/= (% indent python-indent-offset) + 0) + (setq indent (* (1+ (/ indent python-indent-offset)) + python-indent-offset))) + (python-nav-backward-statement) + (while (and (< indent (current-indentation)) + (not (bobp))) + (python-nav-backward-statement)) + (when (< (current-indentation) + indent) + (goto-char start)))) + +(defun elpy-nav-forward-indent () + "Move forward to the next indent level, or over the next word." + (interactive) + (if (< (current-column) (current-indentation)) + (let* ((current (current-column)) + (next (* (1+ (/ current python-indent-offset)) + python-indent-offset))) + (goto-char (+ (point-at-bol) + next))) + (let ((eol (point-at-eol))) + (forward-word) + (when (> (point) eol) + (goto-char (point-at-bol)))))) + +(defun elpy-nav-backward-indent () + "Move backward to the previous indent level, or over the previous word." + (interactive) + (if (and (<= (current-column) (current-indentation)) + (/= (current-column) 0)) + (let* ((current (current-column)) + (next (* (1- (/ current python-indent-offset)) + python-indent-offset))) + (goto-char (+ (point-at-bol) + next))) + (backward-word))) + +(defun elpy-nav--iblock (direction skip) + "Move point forward, skipping lines indented more than the current one. + +DIRECTION should be 1 or -1 for forward or backward. + +SKIP should be #'> to skip lines with larger indentation or #'< +to skip lines with smaller indentation." + (let ((start-indentation (current-indentation))) + (python-nav-forward-statement direction) + (while (and (not (eobp)) + (not (bobp)) + (or (looking-at "^\\s-*$") + (funcall skip + (current-indentation) + start-indentation))) + (python-nav-forward-statement direction)))) + +(defun elpy-nav-move-iblock-down (&optional beg end) + "Move the current indentation block below the next one. + +With an active region, move that instead of the current block. + +An indentation block is a block indented further than the current +one." + (interactive "r") + (let ((use-region (use-region-p)) + (startm (make-marker)) + (starti nil) + (midm (make-marker)) + (midi nil) + (endm (make-marker)) + (deactivate-mark nil)) + (save-excursion + (when use-region + (goto-char beg)) + (set-marker startm (line-beginning-position)) + (setq starti (current-indentation)) + (if use-region + (progn + (goto-char end) + (when (> (current-column) + 0) + (forward-line 1))) + (elpy-nav--iblock 1 #'>)) + (set-marker midm (line-beginning-position)) + (setq midi (current-indentation)) + (elpy-nav--iblock 1 #'>) + (goto-char (line-beginning-position)) + (when (<= (current-indentation) + starti) + (when (/= (skip-chars-backward "[:space:]\n") 0) + (forward-line 1))) + (when (and (= midm (point)) + (/= (point) + (line-end-position)) + (= (line-end-position) + (point-max))) + (goto-char (point-max)) + (insert "\n")) + (set-marker endm (line-beginning-position))) + (when (and (/= startm midm) + (/= midm endm) + (/= startm endm) + (= starti midi)) + (goto-char endm) + (insert (buffer-substring startm midm)) + (when use-region + (set-mark (point))) + (delete-region startm midm) + (goto-char endm) + (back-to-indentation)))) + +(defun elpy-nav-move-iblock-up (&optional beg end) + "Move the current indentation block below the next one. + +With an active region, move that instead of the current block. + +An indentation block is a block indented further than the current +one." + (interactive "r") + (let ((use-region (use-region-p)) + (startm (make-marker)) + (starti nil) + (midm (make-marker)) + (midi nil) + (endm (make-marker)) + (deactivate-mark nil)) + (save-excursion + (when use-region + (goto-char beg)) + (set-marker startm (line-beginning-position)) + (setq starti (current-indentation)) + (if use-region + (progn + (goto-char end) + (when (> (current-column) + 0) + (forward-line 1))) + (elpy-nav--iblock 1 #'>) + (cond + ((and (save-excursion + (goto-char (line-end-position)) + (and (> (current-column) 0) + (= (point-max) (point))))) + (goto-char (line-end-position)) + (insert "\n")) + ((< (current-indentation) + starti) + (when (/= (skip-chars-backward "[:space:]\n") 0) + (forward-line 1))))) + (set-marker midm (line-beginning-position)) + (goto-char startm) + (elpy-nav--iblock -1 #'>) + (goto-char (line-beginning-position)) + (set-marker endm (line-beginning-position)) + (setq midi (current-indentation))) + (when (and (/= startm midm) + (/= midm endm) + (/= startm endm) + (= starti midi)) + (goto-char endm) + (insert (buffer-substring startm midm)) + (when use-region + (set-mark (point))) + (delete-region startm midm) + (goto-char endm) + (back-to-indentation)))) + +(defun elpy-nav-move-iblock-left () + "Dedent the current indentation block, or the active region." + (interactive) + (let (beg end) + (if (use-region-p) + (setq beg (region-beginning) + end (region-end)) + (save-excursion + (setq beg (line-beginning-position)) + (elpy-nav--iblock 1 #'>) + (setq end (line-beginning-position)))) + (python-indent-shift-left beg end))) + +(defun elpy-nav-move-iblock-right () + "Indent the current indentation block, or the active region." + (interactive) + (let (beg end) + (if (use-region-p) + (setq beg (region-beginning) + end (region-end)) + (save-excursion + (setq beg (line-beginning-position)) + (elpy-nav--iblock 1 #'>) + (setq end (line-beginning-position)))) + (python-indent-shift-right beg end))) + +(defun elpy-open-and-indent-line-below () + "Open a line below the current one, move there, and indent." + (interactive) + (move-end-of-line 1) + (newline-and-indent)) + +(defun elpy-open-and-indent-line-above () + "Open a line above the current one, move there, and indent." + (interactive) + (move-beginning-of-line 1) + (save-excursion + (insert "\n")) + (indent-according-to-mode)) + +;;;;;;;;;;;;;;;; +;;; Test running + +(defvar elpy-set-test-runner-history nil + "History variable for `elpy-set-test-runner'.") + +(defun elpy-test (&optional test-whole-project) + "Run tests on the current test, or the whole project. + +If there is a test at point, run that test. If not, or if a +prefix is given, run all tests in the current project." + (interactive "P") + (let ((current-test (elpy-test-at-point))) + (if test-whole-project + ;; With prefix arg, test the whole project. + (funcall elpy-test-runner + (car current-test) + nil nil nil) + ;; Else, run only this test + (apply elpy-test-runner current-test)))) + +(defun elpy-set-test-runner (test-runner) + "Tell Elpy to use TEST-RUNNER to run tests. + +See `elpy-test' for how to invoke it." + (interactive + (list + (let* ((runners (mapcar (lambda (value) + (cons (nth 2 value) + (nth 3 value))) + (cdr (get 'elpy-test-runner 'custom-type)))) + (current (cdr (assq elpy-test-runner + (mapcar (lambda (cell) + (cons (cdr cell) (car cell))) + runners)))) + (choice (completing-read (if current + (format "Test runner (currently %s): " + current) + "Test runner: ") + runners + nil t nil 'elpy-set-test-runner-history))) + (if (equal choice "") + elpy-test-runner + (cdr (assoc choice runners)))))) + (setq elpy-test-runner test-runner)) + +(defun elpy-test-at-point () + "Return a list specifying the test at point, if any. + +This is used as the interactive + +This list has four elements. + +- Top level directory: + All test files should be importable from here. +- Test file: + The current file name. +- Test module: + The module name, relative to the top level directory. +- Test name: + The full name of the current test within the module, for + example TestClass.test_method + +If there is no test at point, test name is nil. +If the current buffer is not visiting a file, only the top level +directory is not nil." + (if (not buffer-file-name) + (progn + (save-some-buffers) + (list (elpy-library-root) nil nil nil)) + (let* ((top (elpy-library-root)) + (file buffer-file-name) + (module (elpy-test--module-name-for-file top file)) + (test (python-info-current-defun))) + (if (and file (string-match "/test[^/]*$" file)) + (progn + (save-buffer) + (list top file module test)) + (save-some-buffers) + (list top nil nil nil))))) + +(defun elpy-test--module-name-for-file (top-level module-file) + "Return the module name relative to TOP-LEVEL for MODULE-FILE. + +For example, for a top level of /project/root/ and a module file +of /project/root/package/module.py, this would return +\"package.module\"." + (let* ((relative-name (file-relative-name module-file top-level)) + (no-extension (replace-regexp-in-string "\\.py\\'" "" relative-name)) + (no-init (replace-regexp-in-string "/__init__\\'" "" no-extension)) + (dotted (replace-regexp-in-string "/" "." no-init))) + (if (string-match "^\\." dotted) + (concat "." (replace-regexp-in-string (regexp-quote "...") "." dotted)) + dotted))) + +(defun elpy-test-runner-p (obj) + "Return t iff OBJ is a test runner. + +This uses the `elpy-test-runner-p' symbol property." + (get obj 'elpy-test-runner-p)) + +(defun elpy-test-run (working-directory command &rest args) + "Run COMMAND with ARGS in WORKING-DIRECTORY as a test command." + (let ((default-directory working-directory)) + (compile (mapconcat #'shell-quote-argument + (cons command args) + " ")))) + +(defun elpy-test-discover-runner (top file module test) + "Test the project using the python unittest discover runner. + +This requires Python 2.7 or later." + (interactive (elpy-test-at-point)) + (let ((test (cond + (test (format "%s.%s" module test)) + (module module) + (t "discover")))) + (apply #'elpy-test-run + top + (append elpy-test-discover-runner-command + (list test))))) +(put 'elpy-test-discover-runner 'elpy-test-runner-p t) + +(defun elpy-test-django-runner (top file module test) + "Test the project using the Django discover runner. + +This requires Django 1.6 or the django-discover-runner package." + (interactive (elpy-test-at-point)) + (if module + (apply #'elpy-test-run + top + (append elpy-test-django-runner-command + (list (if test + (format "%s.%s" module test) + module)))) + (apply #'elpy-test-run + top + elpy-test-django-runner-command))) +(put 'elpy-test-django-runner 'elpy-test-runner-p t) + +(defun elpy-test-nose-runner (top file module test) + "Test the project using the nose test runner. + +This requires the nose package to be installed." + (interactive (elpy-test-at-point)) + (if module + (apply #'elpy-test-run + top + (append elpy-test-nose-runner-command + (list (if test + (format "%s:%s" module test) + module)))) + (apply #'elpy-test-run + top + elpy-test-nose-runner-command))) +(put 'elpy-test-nose-runner 'elpy-test-runner-p t) + +(defun elpy-test-trial-runner (top file module test) + "Test the project using Twisted's Trial test runner. + +This requires the twisted-core package to be installed." + (interactive (elpy-test-at-point)) + (if module + (apply #'elpy-test-run + top + (append elpy-test-trial-runner-command + (list (if test + (format "%s.%s" module test) + module)))) + (apply #'elpy-test-run top elpy-test-trial-runner-command))) +(put 'elpy-test-trial-runner 'elpy-test-runner-p t) + +(defun elpy-test-pytest-runner (top file module test) + "Test the project using the py.test test runner. + +This requires the pytest package to be installed." + (interactive (elpy-test-at-point)) + (cond + (test + (let ((test-list (split-string test "\\."))) + (apply #'elpy-test-run + top + (append elpy-test-pytest-runner-command + (list (mapconcat #'identity + (cons file test-list) + "::")))))) + (module + (apply #'elpy-test-run top (append elpy-test-pytest-runner-command + (list file)))) + (t + (apply #'elpy-test-run top elpy-test-pytest-runner-command)))) +(put 'elpy-test-pytest-runner 'elpy-test-runner-p t) + +;;;;;;;;;;;;;;;;; +;;; Documentation + +(defvar elpy-doc-history nil + "History for the `elpy-doc' command.") + +(defun elpy-doc () + "Show documentation for the symbol at point. + +If there is no documentation for the symbol at point, or if a +prefix argument is given, prompt for a symbol from the user." + (interactive) + (let ((symbol-at-point nil) + (doc nil)) + (when (not current-prefix-arg) + (setq doc (elpy-rpc-get-docstring)) + (when (not doc) + (save-excursion + (python-nav-backward-up-list) + (setq doc (elpy-rpc-get-docstring)))) + (when (not doc) + (setq doc (elpy-rpc-get-pydoc-documentation + (elpy-doc--symbol-at-point)))) + (when (not doc) + (save-excursion + (python-nav-backward-up-list) + (setq doc (elpy-rpc-get-pydoc-documentation + (elpy-doc--symbol-at-point)))))) + (when (not doc) + (setq doc (elpy-rpc-get-pydoc-documentation + (elpy-doc--read-identifier-from-minibuffer + (elpy-doc--symbol-at-point))))) + (if doc + (elpy-doc--show doc) + (error "No documentation found.")))) + +(defun elpy-doc--read-identifier-from-minibuffer (initial) + "Read a pydoc-able identifier from the minibuffer." + (completing-read "Pydoc for: " + (completion-table-dynamic #'elpy-rpc-get-pydoc-completions) + nil nil initial 'elpy-doc-history)) + +(defun elpy-doc--show (documentation) + "Show DOCUMENTATION to the user, replacing ^H with bold." + (with-help-window "*Python Doc*" + (with-current-buffer "*Python Doc*" + (erase-buffer) + (insert documentation) + (goto-char (point-min)) + (while (re-search-forward "\\(.\\)\\1" nil t) + (replace-match (propertize (match-string 1) + 'face 'bold) + t t))))) + +(defun elpy-doc--symbol-at-point () + "Return the Python symbol at point, including dotted paths." + (with-syntax-table python-dotty-syntax-table + (let ((symbol (symbol-at-point))) + (if symbol + (symbol-name symbol) + nil)))) + +;;;;;;;;;;;;;; +;;; Import manipulation + +(defun elpy-importmagic--replace-block (spec) + "Replace an imports block. SPEC is (startline endline newblock)." + (let ((start-line (nth 0 spec)) + (end-line (nth 1 spec)) + (new-block (nth 2 spec))) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (forward-line start-line) + (let ((beg (point)) + (end (progn (forward-line (- end-line start-line)) (point)))) + ;; Avoid deleting and re-inserting when the blocks are equal. + (unless (string-equal (buffer-substring beg end) new-block) + (delete-region beg end) + (insert new-block))))))) + +(defun elpy-importmagic--add-import-read-args (&optional object prompt) + (let* ((default-object (save-excursion + (let ((bounds (with-syntax-table python-dotty-syntax-table + (bounds-of-thing-at-point 'symbol)))) + (if bounds (buffer-substring (car bounds) (cdr bounds)) "")))) + (object-to-import (or object (read-string "Object to import: " default-object))) + (possible-imports (elpy-rpc "get_import_symbols" (list buffer-file-name + (elpy-rpc--buffer-contents) + object-to-import))) + (statement-prompt (or prompt "New import statement: "))) + (cond + ;; An elpy warning (i.e. index not ready) is returned as a string. + ((stringp possible-imports) + (list "")) + ;; If there is no candidate, we exit immediately. + ((null possible-imports) + (message "No import candidate found") + (list "")) + ;; We have some candidates, let the user choose one. + (t + (let ((first-choice (car possible-imports)) + (user-choice (completing-read statement-prompt possible-imports))) + (list (if (equal user-choice "") first-choice user-choice))))))) + +(defun elpy-importmagic-add-import (statement) + (interactive (elpy-importmagic--add-import-read-args)) + (unless (equal statement "") + (let* ((res (elpy-rpc "add_import" (list buffer-file-name + (elpy-rpc--buffer-contents) + statement)))) + (elpy-importmagic--replace-block res)))) + +(defun elpy-importmagic-fixup () + "Query for new imports of unresolved symbols, and remove unreferenced imports. + +Also sort the imports in the import statement blocks." + (interactive) + ;; get all unresolved names, and interactively add imports for them + (let* ((res (elpy-rpc "get_unresolved_symbols" (list buffer-file-name + (elpy-rpc--buffer-contents))))) + (unless (stringp res) + (if (null res) (message "No imports to add.")) + (dolist (object res) + (let* ((prompt (format "How to import \"%s\": " object)) + (choice (elpy-importmagic--add-import-read-args object prompt))) + (elpy-importmagic-add-import (car choice)))))) + ;; now get a new import statement block (this also sorts) + (let* ((res (elpy-rpc "remove_unreferenced_imports" (list buffer-file-name + (elpy-rpc--buffer-contents))))) + (unless (stringp res) + (elpy-importmagic--replace-block res)))) + +;;;;;;;;;;;;;; +;;; Multi-Edit + +(defvar elpy-multiedit-overlays nil + "List of overlays currently being edited.") + +(defun elpy-multiedit-add-overlay (beg end) + "Add an editable overlay between BEG and END. + +A modification in any of these overlays will modify all other +overlays, too." + (interactive "r") + (when (elpy-multiedit--overlays-in-p beg end) + (error "Overlapping multiedit overlays are not allowed")) + (let ((ov (make-overlay beg end nil nil :rear-advance))) + (overlay-put ov 'elpy-multiedit t) + (overlay-put ov 'face 'highlight) + (overlay-put ov 'modification-hooks '(elpy-multiedit--overlay-changed)) + (overlay-put ov 'insert-in-front-hooks '(elpy-multiedit--overlay-changed)) + (overlay-put ov 'insert-behind-hooks '(elpy-multiedit--overlay-changed)) + (push ov elpy-multiedit-overlays))) + +(defun elpy-multiedit--overlays-in-p (beg end) + "Return t iff there are multiedit overlays between beg and end." + (catch 'return + (dolist (ov (overlays-in beg end)) + (when (overlay-get ov 'elpy-multiedit) + (throw 'return t))) + nil)) + +(defun elpy-multiedit-stop () + "Stop editing multiple places at once." + (interactive) + (dolist (ov elpy-multiedit-overlays) + (delete-overlay ov)) + (setq elpy-multiedit-overlays nil)) + +(defun elpy-multiedit--overlay-changed (ov after-change beg end + &optional pre-change-length) + "Called for each overlay that changes. + +This updates all other overlays." + (when (and after-change + (not undo-in-progress) + (overlay-buffer ov)) + (let ((text (buffer-substring (overlay-start ov) + (overlay-end ov))) + (inhibit-modification-hooks t)) + (dolist (other-ov elpy-multiedit-overlays) + (when (and (not (equal other-ov ov)) + (buffer-live-p (overlay-buffer other-ov))) + (with-current-buffer (overlay-buffer other-ov) + (save-excursion + (goto-char (overlay-start other-ov)) + (insert text) + (delete-region (point) (overlay-end other-ov))))))))) + +(defun elpy-multiedit () + "Edit all occurences of the symbol at point, or the active region. + +If multiedit is active, stop it." + (interactive) + (if elpy-multiedit-overlays + (elpy-multiedit-stop) + (let ((regex (if (use-region-p) + (regexp-quote (buffer-substring (region-beginning) + (region-end))) + (format "\\_<%s\\_>" (regexp-quote + (symbol-name + (symbol-at-point)))))) + (case-fold-search nil)) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward regex nil t) + (elpy-multiedit-add-overlay (match-beginning 0) + (match-end 0))))))) + +(defun elpy-multiedit-python-symbol-at-point (&optional use-symbol-p) + "Edit all usages of the the Python symbol at point. + +With prefix arg, edit all syntactic usages of the symbol at +point. This might include unrelated symbols that just share the +name." + (interactive "P") + (if (or elpy-multiedit-overlays + use-symbol-p + (use-region-p)) + ;; If we are already doing a multiedit, or are explicitly told + ;; to use the symbol at point, or if we are on an active region, + ;; call the multiedit function that does just that already. + (call-interactively 'elpy-multiedit) + ;; Otherwise, fetch usages from backend. + (save-some-buffers) + (let ((usages (condition-case err + (elpy-rpc-get-usages) + ;; This is quite the stunt, but elisp parses JSON + ;; null as nil, which is indistinguishable from + ;; the empty list, we stick to the error. + (error + (if (and (eq (car err) 'error) + (stringp (cadr err)) + (string-match "not implemented" (cadr err))) + 'not-supported + (error (cadr err))))))) + (cond + ((eq usages 'not-supported) + (call-interactively 'elpy-multiedit) + (message (concat "Using syntactic editing " + "as current backend does not support get_usages."))) + ((null usages) + (call-interactively 'elpy-multiedit) + (if elpy-multiedit-overlays + (message (concat "Using syntactic editing as no usages of the " + "symbol at point were found by the backend.")) + (message "No occurrences of the symbol at point found"))) + (t + (elpy-multiedit--usages usages)))))) + +(defun elpy-multiedit--usages (usages) + "Mark the usages in USAGES for editing." + (let ((name nil) + (locations (make-hash-table :test #'equal))) + (dolist (usage usages) + (let* ((filename (cdr (assq 'filename usage))) + (this-name (cdr (assq 'name usage))) + (offset (cdr (assq 'offset usage)))) + (setq name this-name) + (with-current-buffer (if filename + (find-file-noselect filename) + (current-buffer)) + (elpy-multiedit-add-overlay (+ offset 1) + (+ offset 1 (length this-name))) + (save-excursion + (goto-char (+ offset 1)) + (puthash filename + (cons (list offset + (buffer-substring (line-beginning-position) + (line-end-position)) + (- (point) + (line-beginning-position)) + (- (+ (point) (length this-name)) + (line-beginning-position))) + (gethash filename locations)) + locations))))) + (if (<= (hash-table-count locations) + 1) + (message "Editing %s usages of '%s' in this buffer" + (length usages) name) + (with-current-buffer (get-buffer-create "*Elpy Edit Usages*") + (let ((inhibit-read-only t) + (filenames nil)) + (erase-buffer) + (elpy-insert--para + "The symbol '" name "' was found in multiple files. Editing " + "all locations:\n\n") + (maphash (lambda (key value) + (when (not (member key filenames)) + (setq filenames (cons key filenames)))) + locations) + (dolist (filename (sort filenames #'string<)) + (elpy-insert--header filename) + (dolist (location (sort (gethash filename locations) + (lambda (loc1 loc2) + (< (car loc1) + (car loc2))))) + (let ((line (nth 1 location)) + (start (+ (line-beginning-position) + (nth 2 location))) + (end (+ (line-end-position) + (nth 3 location)))) + ;; Insert the \n first, else we extend the overlay. + (insert line "\n") + (elpy-multiedit-add-overlay start end))) + (insert "\n")) + (goto-char (point-min)) + (display-buffer (current-buffer) + nil + 'visible)))))) + +;;;;;;;;;;;;;;;;;;;;; +;;; Occur Definitions + +(defun elpy-occur-definitions () + "Display an occur buffer of all definitions in the current buffer. + +Also, switch to that buffer." + (interactive) + (let ((list-matching-lines-face nil)) + (occur "^ *\\(def\\|class\\) ")) + (let ((window (get-buffer-window "*Occur*"))) + (if window + (select-window window) + (switch-to-buffer "*Occur*")))) + +;;;;;;;;;;;;;;;;;;; +;;; Promise objects + +(defvar elpy-promise-marker (make-symbol "*elpy-promise*") + "An uninterned symbol marking an Elpy promise object.") + +(defun elpy-promise (success &optional error) + "Return a new promise. + +A promise is an object with a success and error callback. If the +promise is resolved using `elpy-promise-resolve', its success +callback is called with the given value. The current buffer is +restored, too. + +If the promise is rejected using `elpy-promise-reject', its error +callback is called. For this function, the current buffer is not +necessarily restored, as it is also called when the buffer does +not exist anymore." + (vector elpy-promise-marker ; 0 id + success ; 1 success-callback + error ; 2 error-callback + (current-buffer) ; 3 current-buffer + nil ; 4 run + )) + +(defun elpy-promise-p (obj) + "Return non-nil if the argument is a promise object." + (and (vectorp obj) + (= (length obj) 5) + (eq (aref obj 0) elpy-promise-marker))) + +(defsubst elpy-promise-success-callback (promise) + "Return the success callback for PROMISE." + (aref promise 1)) + +(defsubst elpy-promise-error-callback (promise) + "Return the error callback for PROMISE." + (aref promise 2)) + +(defsubst elpy-promise-buffer (promise) + "Return the buffer for PROMISE." + (aref promise 3)) + +(defsubst elpy-promise-resolved-p (promise) + "Return non-nil if the PROMISE has been resolved or rejected." + (aref promise 4)) + +(defsubst elpy-promise-set-resolved (promise) + "Mark PROMISE as having been resolved." + (aset promise 4 t)) + +(defun elpy-promise-resolve (promise value) + "Resolve PROMISE with VALUE." + (when (not (elpy-promise-resolved-p promise)) + (unwind-protect + (let ((success-callback (elpy-promise-success-callback promise))) + (when success-callback + (condition-case err + (with-current-buffer (elpy-promise-buffer promise) + (funcall success-callback value)) + (error + (elpy-promise-reject promise err))))) + (elpy-promise-set-resolved promise)))) + +(defun elpy-promise-reject (promise reason) + "Reject PROMISE because of REASON." + (when (not (elpy-promise-resolved-p promise)) + (unwind-protect + (let ((error-callback (elpy-promise-error-callback promise))) + (when error-callback + (if (buffer-live-p (elpy-promise-buffer promise)) + (with-current-buffer (elpy-promise-buffer promise) + (funcall error-callback reason)) + (with-temp-buffer + (funcall error-callback reason))))) + (elpy-promise-set-resolved promise)))) + +(defun elpy-promise-wait (promise &optional timeout) + "Wait for PROMISE to be resolved, for up to TIMEOUT seconds. + +This will accept process output while waiting. + +This will wait for the current Elpy RPC process specifically, as +Emacs currently has a bug where it can wait for the entire time +of the timeout, even if output arrives. + +See http://debbugs.gnu.org/cgi/bugreport.cgi?bug=17647" + (let ((end-time (when timeout + (time-add (current-time) + (seconds-to-time timeout)))) + (process (get-buffer-process (elpy-rpc--get-rpc-buffer)))) + (while (and (not (elpy-promise-resolved-p promise)) + (or (not end-time) + (time-less-p (current-time) + end-time))) + (accept-process-output process timeout)))) + +;;;;;;; +;;; RPC + +;; elpy-rpc is a simple JSON-based RPC protocol. It's mostly JSON-RPC +;; 1.0, except we do not implement the full protocol as we do not need +;; all the features. Emacs starts a Python subprocess which runs a +;; special module. The module reads JSON-RPC requests and responds +;; with JSON-RPC responses. + +(defvar elpy-rpc--call-id 0 + "Call id of the last elpy-rpc call. + +Used to associate responses to callbacks.") +(make-variable-buffer-local 'elpy-rpc--call-id) + +(defvar elpy-rpc--buffer-p nil + "True iff the current buffer is an elpy-rpc buffer.") +(make-variable-buffer-local 'elpy-rpc--buffer-p) + +(defvar elpy-rpc--buffer nil + "The elpy-rpc buffer associated with this buffer.") +(make-variable-buffer-local 'elpy-rpc--buffer) + +(defvar elpy-rpc--backend-library-root nil + "The project root used by this backend.") +(make-variable-buffer-local 'elpy-rpc--backend-library-root) + +(defvar elpy-rpc--backend-python-command nil + "The Python interpreter used by this backend.") +(make-variable-buffer-local 'elpy-rpc--backend-python-command) + +(defvar elpy-rpc--backend-callbacks nil + "The callbacks registered for calls to the current backend. + +This maps call IDs to functions.") +(make-variable-buffer-local 'elpy-rpc--backend-callbacks) + +(defvar elpy-rpc--last-error-popup nil + "The last time an error popup happened.") + +(defun elpy-rpc (method params &optional success error) + "Call METHOD with PARAMS in the backend. + +If SUCCESS and optionally ERROR is given, return immediately and +call those when a result is available. Otherwise, wait for a +result and return that." + (when (not error) + (setq error #'elpy-rpc--default-error-callback)) + (if success + (elpy-rpc--call method params success error) + (elpy-rpc--call-blocking method params))) + +(defun elpy-rpc--call-blocking (method-name params) + "Call METHOD-NAME with PARAMS in the current RPC backend. + +Returns the result, blocking until this arrived." + (let* ((result-arrived nil) + (error-occured nil) + (result-value nil) + (error-object nil) + (promise (elpy-rpc--call method-name params + (lambda (result) + (setq result-value result + result-arrived t)) + (lambda (err) + (setq error-object err + error-occured t))))) + (elpy-promise-wait promise elpy-rpc-timeout) + (cond + (error-occured + (elpy-rpc--default-error-callback error-object)) + (result-arrived + result-value) + (t + (error "Timeout during RPC call %s from backend" + method-name))))) + +(defun elpy-rpc--call (method-name params success error) + "Call METHOD-NAME with PARAMS in the current RPC backend. + +When a result is available, SUCCESS will be called with that +value as its sole argument. If an error occurs, ERROR will be +called with the error list. + +Returns a PROMISE object." + (let ((promise (elpy-promise success error))) + (with-current-buffer (elpy-rpc--get-rpc-buffer) + (setq elpy-rpc--call-id (1+ elpy-rpc--call-id)) + (elpy-rpc--register-callback elpy-rpc--call-id promise) + (process-send-string + (get-buffer-process (current-buffer)) + (concat (json-encode `((id . ,elpy-rpc--call-id) + (method . ,method-name) + (params . ,params))) + "\n"))) + promise)) + +(defun elpy-rpc--register-callback (call-id promise) + "Register for PROMISE to be called when CALL-ID returns. + +Must be called in an elpy-rpc buffer." + (when (not elpy-rpc--buffer-p) + (error "Must be called in RPC buffer")) + (when (not elpy-rpc--backend-callbacks) + (setq elpy-rpc--backend-callbacks (make-hash-table :test #'equal))) + (puthash call-id promise elpy-rpc--backend-callbacks)) + +(defun elpy-rpc--get-rpc-buffer () + "Return the RPC buffer associated with the current buffer, +creating one if necessary." + (when (not (elpy-rpc--process-buffer-p elpy-rpc--buffer)) + (setq elpy-rpc--buffer + (or (elpy-rpc--find-buffer (elpy-library-root) + elpy-rpc-python-command) + (elpy-rpc--open (elpy-library-root) + elpy-rpc-python-command)))) + elpy-rpc--buffer) + +(defun elpy-rpc--process-buffer-p (buffer) + "Return non-nil when BUFFER is a live elpy-rpc process buffer. + +If BUFFER is a buffer for an elpy-rpc process, but the process +died, this will kill the process and buffer." + (cond + ((or (not buffer) + (not (buffer-live-p buffer))) + nil) + ((not (buffer-local-value 'elpy-rpc--buffer-p buffer)) + nil) + ((and (get-buffer-process buffer) + (process-live-p (get-buffer-process buffer))) + t) + (t + (ignore-errors + (kill-process (get-buffer-process buffer))) + (ignore-errors + (kill-buffer buffer)) + nil))) + +(defun elpy-rpc--find-buffer (library-root python-command) + "Return an existing RPC buffer for this project root and command." + (catch 'return + (let ((full-python-command (executable-find python-command))) + (dolist (buf (buffer-list)) + (when (and (elpy-rpc--process-buffer-p buf) + (equal (buffer-local-value 'elpy-rpc--backend-library-root + buf) + library-root) + (equal (buffer-local-value 'elpy-rpc--backend-python-command + buf) + full-python-command)) + (throw 'return buf)))) + nil)) + +(defun elpy-rpc--open (library-root python-command) + "Start a new RPC process and return the associated buffer." + ;; Prevent configuration errors + (when (and elpy-rpc-backend + (not (stringp elpy-rpc-backend))) + (error "`elpy-rpc-backend' should be nil or a string.")) + (let* ((full-python-command (executable-find python-command)) + (name (format " *elpy-rpc [project:%s python:%s]*" + library-root + full-python-command)) + (new-elpy-rpc-buffer (generate-new-buffer name)) + (proc nil)) + (with-current-buffer new-elpy-rpc-buffer + (setq elpy-rpc--buffer-p t + elpy-rpc--buffer (current-buffer) + elpy-rpc--backend-library-root library-root + elpy-rpc--backend-python-command full-python-command + default-directory "/" + proc (condition-case err + (let ((process-connection-type nil) + (process-environment (elpy-rpc--environment))) + (start-process name + (current-buffer) + full-python-command + "-W" "ignore" + "-m" "elpy.__main__")) + (error + (elpy-config-error + "Elpy can't start Python (%s: %s)" + (car err) (cadr err))))) + (set-process-query-on-exit-flag proc nil) + (set-process-sentinel proc #'elpy-rpc--sentinel) + (set-process-filter proc #'elpy-rpc--filter) + (elpy-rpc-init elpy-rpc-backend library-root + (lambda (result) + (let ((backend (cdr (assq 'backend result)))) + (when (and elpy-rpc-backend + (not (equal backend elpy-rpc-backend))) + (elpy-config-error + "Can't set backend %s, using %s instead" + elpy-rpc-backend backend)))))) + new-elpy-rpc-buffer)) + +(defun elpy-rpc--sentinel (process event) + "The sentinel for the RPC process. + +As process sentinels are only ever called when the process +terminates, this will call the error handler of all registered +RPC calls with the event." + (let ((buffer (process-buffer process)) + (err (list 'process-sentinel (substring event 0 -1)))) + (when (and buffer + (buffer-live-p buffer)) + (with-current-buffer buffer + (when elpy-rpc--backend-callbacks + (maphash (lambda (call-id promise) + (ignore-errors + (elpy-promise-reject promise err))) + elpy-rpc--backend-callbacks) + (setq elpy-rpc--backend-callbacks nil)))))) + +(defun elpy-rpc--filter (process output) + "The filter for the RPC process." + (let ((buffer (process-buffer process))) + (when (and buffer + (buffer-live-p buffer)) + (with-current-buffer buffer + (goto-char (point-max)) + (insert output) + (while (progn + (goto-char (point-min)) + (search-forward "\n" nil t)) + (let ((line-end (point)) + (json nil) + (did-read-json nil)) + (goto-char (point-min)) + (condition-case err + (setq json (let ((json-array-type 'list)) + (json-read)) + line-end (1+ (point)) + did-read-json t) + (error + (goto-char (point-min)))) + (cond + (did-read-json + (delete-region (point-min) line-end) + (elpy-rpc--handle-json json)) + ((looking-at "elpy-rpc ready\n") + (replace-match "") + (elpy-rpc--check-backend-version "1.1")) + ((looking-at "elpy-rpc ready (\\([^ ]*\\))\n") + (let ((rpc-version (match-string 1))) + (replace-match "") + (elpy-rpc--check-backend-version rpc-version))) + ((looking-at ".*No module named elpy\n") + (replace-match "") + (elpy-config-error "Elpy module not found")) + (t + (let ((line (buffer-substring (point-min) + line-end))) + (delete-region (point-min) line-end) + (elpy-rpc--handle-unexpected-line line)))))))))) + +(defun elpy-rpc--check-backend-version (rpc-version) + "Check that we are using the right version." + (when (not (equal rpc-version elpy-version)) + (elpy-insert--popup "*Elpy Version Mismatch*" + (elpy-insert--header "Elpy Version Mismatch") + (elpy-insert--para + "You are not using the same version of Elpy in Emacs Lisp" + "compared to Python. This can cause random problems. Please" + "do make sure to use compatible versions.\n") + (insert + "\n" + "Elpy Emacs Lisp version: " elpy-version "\n" + "Elpy Python version....: " rpc-version "\n")))) + +(defun elpy-rpc--handle-unexpected-line (line) + "Handle an unexpected line from the backend. + +This is usually an error or backtrace." + (let ((buf (get-buffer "*Elpy Output*"))) + (when (not buf) + (elpy-insert--popup "*Elpy Output*" + (elpy-insert--header "Output from Backend") + (elpy-insert--para + "There was some unexpected output from the Elpy backend. " + "This is usually some module that does not use correct logging, " + "but might indicate a configuration problem.\n\n") + (elpy-insert--header "Output") + (setq buf (current-buffer)))) + (with-current-buffer buf + (goto-char (point-max)) + (let ((inhibit-read-only t)) + (insert line))))) + +(defun elpy-rpc--handle-json (json) + "Handle a single JSON object from the RPC backend." + (let ((call-id (cdr (assq 'id json))) + (error-object (cdr (assq 'error json))) + (result (cdr (assq 'result json)))) + (let ((promise (gethash call-id elpy-rpc--backend-callbacks))) + (when (not promise) + (error "Received a response for unknown call-id %s" call-id)) + (remhash call-id elpy-rpc--backend-callbacks) + (if error-object + (elpy-promise-reject promise error-object) + (elpy-promise-resolve promise result))))) + +(defun elpy-rpc--default-error-callback (error-object) + "Display an error from the RPC backend." + ;; We actually might get an (error "foo") thing here. + (if (and (consp error-object) + (not (consp (car error-object)))) + (signal (car error-object) (cdr error-object)) + (let ((message (cdr (assq 'message error-object))) + (code (cdr (assq 'code error-object))) + (data (cdr (assq 'data error-object)))) + (cond + ((not (numberp code)) + (error "Bad response from RPC: %S" error-object)) + ((< code 300) + (message "Elpy warning: %s" message)) + ((< code 500) + (error "Elpy error: %s" message)) + ((and elpy-rpc-error-timeout + elpy-rpc--last-error-popup + (<= (float-time) + (+ elpy-rpc--last-error-popup + elpy-rpc-error-timeout))) + (message "Elpy error popup ignored, see `elpy-rpc-error-timeout': %s" + message)) + (t + (let ((config (elpy-config--get-config))) + (elpy-insert--popup "*Elpy Error*" + (elpy-insert--header "Elpy Error") + (elpy-insert--para + "The backend encountered an unexpected error. This indicates " + "a bug in Elpy. Please open a bug report with the data below " + "in the Elpy bug tracker:") + (insert "\n" + "\n") + (insert-button + "https://github.com/jorgenschaefer/elpy/issues/new" + 'action (lambda (button) + (browse-url (button-get button 'url))) + 'url "https://github.com/jorgenschaefer/elpy/issues/new") + (insert "\n" + "\n" + "```\n") + (elpy-insert--header "Error Message") + (insert message "\n\n") + (elpy-insert--header "Configuration") + (elpy-config--insert-configuration-table config) + (let ((traceback (cdr (assq 'traceback data)))) + (when traceback + (insert "\n") + (elpy-insert--header "Traceback") + (insert traceback))) + (let ((jedi-info (cdr (assq 'jedi_debug_info data)))) + (when jedi-info + (insert "\n") + (elpy-insert--header "Jedi Debug Information") + (pcase (cdr (assq 'debug_info jedi-info)) + (`nil (insert "Jedi did not emit any debug info.\n")) + (infos + (dolist (outstr infos) + (insert outstr "\n")))) + (insert "\n" + "```\n" + "\n" + "Reproduction:\n" + "\n") + (let ((method (cdr (assq 'method jedi-info))) + (source (cdr (assq 'source jedi-info))) + (script-args (cdr (assq 'script_args jedi-info)))) + (insert "```Python\n") + (insert "import jedi\n" + "\n" + "source = '''\\\n" + source + "'''\n" + "\n" + "script = jedi.Script(" script-args ")\n" + "script." method "()\n")))) + (let ((rope-info (cdr (assq 'rope_debug_info data)))) + (when rope-info + (insert "\n") + (elpy-insert--header "Rope Debug Information") + (insert "```\n" + "\n" + "Reproduction:\n" + "\n") + (let ((project-root (cdr (assq 'project_root rope-info))) + (filename (cdr (assq 'filename rope-info))) + (source (cdr (assq 'source rope-info))) + (function-name (cdr (assq 'function_name rope-info))) + (function-args (cdr (assq 'function_args rope-info)))) + (insert "```Python\n") + (insert "\n" + "source = '''\n" + source + "'''\n" + "\n") + (insert "project = rope.base.project.Project(\n" + (format " %S,\n" project-root) + " ropefolder=None\n" + ")\n") + (insert "resource = rope.base.libutils.path_to_resource(\n" + " project,\n" + (format " %S,\n" filename) + " 'file'\n" + ")\n") + (insert (format "%s(\n %s\n)\n" + function-name function-args))))) + (when (not (= 0 (current-column))) + (insert "\n")) + (insert "```")) + (setq elpy-rpc--last-error-popup (float-time)))))))) + +(defun elpy-rpc--environment () + "Return a `process-environment' for the RPC process. + +This includes `elpy-rpc-pythonpath' in the PYTHONPATH, if set." + (if (or (not elpy-rpc-pythonpath) + (not (file-exists-p (expand-file-name "elpy/__init__.py" + elpy-rpc-pythonpath)))) + process-environment + (let* ((old-pythonpath (getenv "PYTHONPATH")) + (new-pythonpath (if old-pythonpath + (concat elpy-rpc-pythonpath + path-separator + old-pythonpath) + elpy-rpc-pythonpath))) + (cons (concat "PYTHONPATH=" new-pythonpath) + process-environment)))) + +(defun elpy-rpc--buffer-contents () + "Return the contents of the current buffer. + +This returns either a string, or a file object for the RPC +protocol if the buffer is larger than +`elpy-rpc-large-buffer-size'." + (if (< (buffer-size) elpy-rpc-large-buffer-size) + (buffer-string) + (let ((file-name (make-temp-file "elpy-rpc-")) + (coding-system-for-write 'utf-8)) + (write-region nil nil file-name nil :nomessage) + `((filename . ,file-name) + (delete_after_use . t))))) + +;; RPC API functions + +(defun elpy-rpc-restart () + "Restart all RPC processes." + (interactive) + (dolist (buffer (buffer-list)) + (when (elpy-rpc--process-buffer-p buffer) + (ignore-errors + (kill-process (get-buffer-process buffer))) + (ignore-errors + (kill-buffer buffer))))) + +(defun elpy-rpc-init (backend library-root &optional success error) + "Initialize the backend. + +This has to be called as the first method, else Elpy won't be +able to respond to other calls." + (elpy-rpc "init" + ;; This uses a vector because otherwise, json-encode in + ;; older Emacsen gets seriously confused, especially when + ;; backend is nil. + (vector `((backend . ,backend) + (project_root . ,(expand-file-name library-root)))) + success error)) + +(defun elpy-rpc-get-calltip (&optional success error) + "Call the get_calltip API function. + +Returns a calltip string for the function call at point." + (elpy-rpc "get_calltip" + (list buffer-file-name + (elpy-rpc--buffer-contents) + (- (point) + (point-min))) + success error)) + +(defun elpy-rpc-get-completions (&optional success error) + "Call the get_completions API function. + +Returns a list of possible completions for the Python symbol at +point." + (elpy-rpc "get_completions" + (list buffer-file-name + (elpy-rpc--buffer-contents) + (- (point) + (point-min))) + success error)) + +(defun elpy-rpc-get-completion-docstring (completion &optional success error) + "Call the get_completion_docstring API function. + +Returns a doc string or nil" + (elpy-rpc "get_completion_docstring" (list completion) success error)) + +(defun elpy-rpc-get-completion-location (completion &optional success error) + "Call the get_completion_location API function. + +Returns a list of file name and line number, or nil" + (elpy-rpc "get_completion_location" (list completion) success error)) + +(defun elpy-rpc-get-definition (&optional success error) + "Call the find_definition API function. + +Returns nil or a list of (filename, point)." + (elpy-rpc "get_definition" + (list buffer-file-name + (elpy-rpc--buffer-contents) + (- (point) + (point-min))) + success error)) + +(defun elpy-rpc-get-docstring (&optional success error) + "Call the get_docstring API function. + +Returns a possible multi-line docstring for the symbol at point." + (elpy-rpc "get_docstring" + (list buffer-file-name + (elpy-rpc--buffer-contents) + (- (point) + (point-min))) + success error)) + +(defun elpy-rpc-get-pydoc-completions (prefix &optional success error) + "Return a list of modules available in pydoc starting with PREFIX." + (elpy-rpc "get_pydoc_completions" (list prefix) + success error)) + +(defun elpy-rpc-get-pydoc-documentation (symbol &optional success error) + "Get the Pydoc documentation for SYMBOL. + +Returns a possible multi-line docstring." + (elpy-rpc "get_pydoc_documentation" (list symbol) + success error)) + +(defun elpy-rpc-get-usages (&optional success error) + (elpy-rpc "get_usages" + (list buffer-file-name + (elpy-rpc--buffer-contents) + (- (point) + (point-min))) + success error)) + +;;;;;;;;;;; +;;; Modules + +(defvar elpy-modules-initialized-p nil + "Boolean, set to true if modules were run with `global-init'.") + +(defun elpy-modules-run (command &rest args) + "Run COMMAND with ARGS for all modules in `elpy-modules'." + (dolist (module elpy-modules) + (apply module command args))) + +(defun elpy-modules-global-init () + "Run the global-init method of Elpy modules. + +Make sure this only happens once." + (when (not elpy-modules-initialized-p) + (elpy-modules-run 'global-init) + (setq elpy-modules-initialized-p t))) + +(defun elpy-modules-global-stop () + "Run the global-stop method of Elpy modules. + +Make sure this only happens once per global-init call." + (when elpy-modules-initialized-p + (elpy-modules-run 'global-stop) + (setq elpy-modules-initialized-p nil))) + +(defun elpy-modules-buffer-init () + "Run the buffer-init method of Elpy modules. + +Make sure global-init is called first." + (elpy-modules-global-init) + (elpy-modules-run 'buffer-init)) + +(defun elpy-modules-buffer-stop () + "Run the buffer-stop method of Elpy modules." + (elpy-modules-run 'buffer-stop)) + +(defun elpy-modules-remove-modeline-lighter (mode-name) + "Remove the lighter for MODE-NAME. + +It's not necessary to see (Python Elpy yas company ElDoc) all the +time. Honestly." + (interactive) + (cond + ((eq mode-name 'eldoc-minor-mode) + (setq eldoc-minor-mode-string nil)) + (t + (let ((cell (assq mode-name minor-mode-alist))) + (when cell + (setcdr cell + (list ""))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Module: Sane Defaults + +(defun elpy-module-sane-defaults (command &rest args) + (pcase command + (`buffer-init + ;; Set `forward-sexp-function' to nil in python-mode. See + ;; http://debbugs.gnu.org/db/13/13642.html + (set (make-local-variable 'forward-sexp-function) nil) + ;; PEP8 recommends two spaces in front of inline comments. + (set (make-local-variable 'comment-inline-offset) 2)) + (`buffer-stop + (kill-local-variable 'forward-sexp-function) + (kill-local-variable 'comment-inline-offset)))) + +;;;;;;;;;;;;;;;;;;; +;;; Module: Company + +(defun elpy-module-company (command &rest args) + "Module to support company-mode completions." + (pcase command + (`global-init + (require 'company) + (elpy-modules-remove-modeline-lighter 'company-mode) + (define-key company-active-map (kbd "C-d") + 'company-show-doc-buffer)) + (`buffer-init + ;; We want immediate completions from company. + (set (make-local-variable 'company-idle-delay) + 0) + ;; And annotations should be right-aligned. + (set (make-local-variable 'company-tooltip-align-annotations) + t) + ;; Also, dabbrev in comments and strings is nice. + (set (make-local-variable 'company-dabbrev-code-everywhere) + t) + ;; Add our own backend and remove a bunch of backends that + ;; interfere in Python mode. + (set (make-local-variable 'company-backends) + (cons 'elpy-company-backend + (delq 'company-semantic + (delq 'company-ropemacs + (delq 'company-capf + (mapcar #'identity company-backends)))))) + (company-mode 1)) + (`buffer-stop + (company-mode -1) + (kill-local-variable 'company-idle-delay) + (kill-local-variable 'company-tooltip-align-annotations) + (kill-local-variable 'company-backends)) + )) + +(defvar elpy-company--cache nil + "Buffer-local cache for candidate information.") +(make-variable-buffer-local 'elpy-company--cache) + +(defun elpy-company--cache-clear () + "Clear and initialize the cache." + (if elpy-company--cache + (clrhash elpy-company--cache) + (setq elpy-company--cache + (make-hash-table :test #'equal)))) + +(defun elpy-company--cache-annotation (name) + "Return the cached annotation for NAME." + (when elpy-company--cache + (cdr (assq 'annotation (gethash name elpy-company--cache))))) + +(defun elpy-company--cache-meta (name) + "Return the cached annotation for NAME." + (when elpy-company--cache + (cdr (assq 'meta (gethash name elpy-company--cache))))) + +(defun elpy-company--cache-name (name) + "Return the cached name for NAME. + +Confused yet? We pass \"our\" name, that is, prefix + suffix, +here, and return the \"name\" as used by the backend." + (when elpy-company--cache + (cdr (assq 'name (gethash name elpy-company--cache))))) + +(defun elpy-company--cache-completions (prefix result) + "Store RESULT in the candidate cache and return candidates." + (elpy-company--cache-clear) + (mapcar (lambda (completion) + (let* ((suffix (cdr (assq 'suffix completion))) + (name (concat prefix suffix))) + (puthash name completion elpy-company--cache) + name)) + result)) + +(defun elpy-company-backend (command &optional arg &rest ignored) + "A company-mode backend for Elpy." + (interactive (list 'interactive)) + (pcase command + (`interactive + (company-begin-backend 'elpy-company-backend)) + ;; init => Called once per buffer + ;; prefix => return the prefix at point + (`prefix + (when (and elpy-mode + (not (company-in-string-or-comment))) + (company-grab-symbol-cons "\\." 1))) + ;; candidates => return candidates for this prefix + (`candidates + (cons :async + (lambda (callback) + (elpy-rpc-get-completions + (lambda (result) + (elpy-company--cache-clear) + (funcall + callback + (cond + ;; The backend returned something + (result + (elpy-company--cache-completions arg result)) + ;; Nothing from the backend, try dabbrev-code. + ((> (length arg) company-minimum-prefix-length) + (elpy--sort-and-strip-duplicates + (company-dabbrev-code 'candidates arg))) + ;; Well, ok, let's go meh. + (t + nil)))))))) + ;; sorted => t if the list is already sorted + (`sorted + t) + ;; duplicates => t if there could be duplicates + (`duplicates + nil) + ;; no-cache => t if company shouldn't cache results + ;; meta => short docstring for minibuffer + (`meta + (let ((meta (elpy-company--cache-meta arg))) + (when (and meta + (string-match "\\`\\(.*\n.*\\)\n.*" meta)) + (setq meta (match-string 1 meta))) + meta)) + ;; annotation => short docstring for completion buffer + (`annotation + (elpy-company--cache-annotation arg)) + ;; doc-buffer => put doc buffer in `company-doc-buffer' + (`doc-buffer + (let* ((name (elpy-company--cache-name arg)) + (doc (elpy-rpc-get-completion-docstring name))) + (when doc + (company-doc-buffer doc)))) + ;; require-match => Never require a match, even if the user + ;; started to interact with company. See `company-require-match'. + (`require-match + 'never) + ;; location => (buffer . point) or (file . + ;; line-number) + (`location + (let* ((name (elpy-company--cache-name arg)) + (loc (elpy-rpc-get-completion-location name))) + (when loc + (cons (car loc) + (cadr loc))))) + ;; match => for non-prefix based backends + ;; post-completion => after insertion, for snippets + )) + +(defun elpy--sort-and-strip-duplicates (seq) + "Sort SEQ and remove any duplicates." + (sort (delete-dups seq) + (lambda (a b) + (string< a b)))) + +;;;;;;;;;;;;;;;;; +;;; Module: ElDoc + +(defun elpy-module-eldoc (command &rest args) + "Module to support ElDoc for Python files." + (pcase command + (`global-init + (require 'eldoc) + (setq eldoc-minor-mode-string nil)) + (`buffer-init + (set (make-local-variable 'eldoc-documentation-function) + 'elpy-eldoc-documentation) + (eldoc-mode 1)) + (`buffer-stop + (eldoc-mode -1) + (kill-local-variable 'eldoc-documentation-function)))) + +(defun elpy-eldoc-documentation () + "Return some interesting information for the code at point. + +This will return flymake errors for the line at point if there +are any. If not, this will do an asynchronous call to the RPC +backend to get a call tip, and display that using +`eldoc-message'. If the backend has no call tip, this will +display the current class and method instead." + (let ((flymake-error (elpy-flymake-error-at-point))) + (if flymake-error + flymake-error + (elpy-rpc-get-calltip + (lambda (calltip) + (eldoc-message + (cond + ((not calltip) + (when elpy-eldoc-show-current-function + (let ((current-defun (python-info-current-defun))) + (when current-defun + (format "In: %s()" current-defun))))) + ((stringp calltip) + calltip) + (t + (let ((name (cdr (assq 'name calltip))) + (index (cdr (assq 'index calltip))) + (params (cdr (assq 'params calltip)))) + (when index + (setf (nth index params) + (propertize (nth index params) + 'face + 'eldoc-highlight-function-argument))) + (format "%s(%s)" + name + (mapconcat #'identity params ", ")) + )))))) + ;; Return the last message until we're done + eldoc-last-message))) + +;;;;;;;;;;;;;;;;;;; +;;; Module: Flymake + +(defun elpy-module-flymake (command &rest args) + "Enable Flymake support for Python." + (pcase command + (`global-init + (require 'flymake) + (elpy-modules-remove-modeline-lighter 'flymake-mode) + ;; Flymake support using flake8, including warning faces. + (when (and (executable-find "flake8") + (equal python-check-command + (elpy-flymake--standard-value 'python-check-command))) + (setq python-check-command "flake8")) + + ;; Add our initializer function + (add-to-list 'flymake-allowed-file-name-masks + '("\\.py\\'" elpy-flymake-python-init))) + (`buffer-init + ;; `flymake-no-changes-timeout': The original value of 0.5 is too + ;; short for Python code, as that will result in the current line + ;; to be highlighted most of the time, and that's annoying. This + ;; value might be on the long side, but at least it does not, in + ;; general, interfere with normal interaction. + (set (make-local-variable 'flymake-no-changes-timeout) + 60) + + ;; `flymake-start-syntax-check-on-newline': This should be nil for + ;; Python, as;; most lines with a colon at the end will mean the + ;; next line is always highlighted as error, which is not helpful + ;; and mostly annoying. + (set (make-local-variable 'flymake-start-syntax-check-on-newline) + nil) + + ;; Enable warning faces for flake8 output. + ;; COMPAT: Obsolete variable as of 24.4 + (if (boundp 'flymake-warning-predicate) + (set (make-local-variable 'flymake-warning-predicate) "^W[0-9]") + (set (make-local-variable 'flymake-warning-re) "^W[0-9]")) + + (flymake-mode 1)) + (`buffer-stop + (flymake-mode -1) + (kill-local-variable 'flymake-no-changes-timeout) + (kill-local-variable 'flymake-start-syntax-check-on-newline) + ;; COMPAT: Obsolete variable as of 24.4 + (if (boundp 'flymake-warning-predicate) + (kill-local-variable 'flymake-warning-predicate) + (kill-local-variable 'flymake-warning-re))))) + +(defun elpy-flymake-python-init () + ;; Make sure it's not a remote buffer as flymake would not work + (when (not (file-remote-p buffer-file-name)) + (let* ((temp-file (flymake-init-create-temp-buffer-copy + 'flymake-create-temp-inplace))) + (list python-check-command + (list temp-file) + ;; Run flake8 from / to avoid import problems (#169) + "/")))) + +(defun elpy-flymake-next-error () + "Move forward to the next Flymake error and show a +description." + (interactive) + (flymake-goto-next-error) + (elpy-flymake-show-error)) + +(defun elpy-flymake-previous-error () + "Move backward to the previous Flymake error and show a +description." + (interactive) + (flymake-goto-prev-error) + (elpy-flymake-show-error)) + +(defun elpy-flymake-show-error () + "Show the flymake error message at point." + (interactive) + (message "%s" (elpy-flymake-error-at-point))) + +(defun elpy-flymake-error-at-point () + "Return the flymake error at point, or nil if there is none." + (when (boundp 'flymake-err-info) + (let* ((lineno (line-number-at-pos)) + (err-info (car (flymake-find-err-info flymake-err-info + lineno)))) + (when err-info + (mapconcat #'flymake-ler-text + err-info + ", "))))) + +(defun elpy-flymake--standard-value (var) + "Return the standard value of the given variable." + (let ((sv (get var 'standard-value))) + (when (consp sv) + (ignore-errors + (eval (car sv)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Module: Highlight Indentation + +(defun elpy-module-highlight-indentation (command &rest args) + "Module to highlight indentation in Python files." + (pcase command + (`global-init + (require 'highlight-indentation)) + (`buffer-init + (highlight-indentation-mode 1)) + (`buffer-stop + (highlight-indentation-mode -1)))) + +;;;;;;;;;;;;;;;;;; +;;; Module: pyvenv + +(defun elpy-module-pyvenv (command &rest args) + "Module to display the current virtualenv in the mode line." + (pcase command + (`global-init + (pyvenv-mode 1)) + (`global-stop + (pyvenv-mode -1)))) + +;;;;;;;;;;;;;;;;;;;;; +;;; Module: Yasnippet + +(defun elpy-module-yasnippet (command &rest args) + "Module to enable YASnippet snippets." + (pcase command + (`global-init + (require 'yasnippet) + (elpy-modules-remove-modeline-lighter 'yas-minor-mode) + + ;; We provide some YASnippet snippets. Add them. + + ;; yas-snippet-dirs can be a string for a single directory. Make + ;; sure it's a list in that case so we can add our own entry. + (when (not (listp yas-snippet-dirs)) + (setq yas-snippet-dirs (list yas-snippet-dirs))) + (add-to-list 'yas-snippet-dirs + (concat (file-name-directory (locate-library "elpy")) + "snippets/") + t) + + ;; Now load yasnippets. + (yas-reload-all)) + (`global-stop + (setq yas-snippet-dirs + (delete (concat (file-name-directory (locate-library "elpy")) + "snippets/") + yas-snippet-dirs))) + (`buffer-init + (yas-minor-mode 1)) + (`buffer-stop + (yas-minor-mode -1)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Backwards compatibility + +;; Functions for Emacs 24 before 24.3 +(when (not (fboundp 'python-info-current-defun)) + (defalias 'python-info-current-defun 'python-current-defun)) + +(when (not (fboundp 'python-nav-forward-statement)) + (defun python-nav-forward-statement (&rest ignored) + "Function added in Emacs 24.3" + (error "Enhanced Python navigation only available in Emacs 24.3+"))) + +(when (not (fboundp 'python-nav-backward-up-list)) + (defun python-nav-backward-up-list () + "Compatibility function for older Emacsen" + (ignore-errors + (backward-up-list)))) + +(when (not (fboundp 'python-shell-calculate-exec-path)) + (defun python-shell-calculate-exec-path () + "Compatibility function for older Emacsen." + exec-path)) + +(when (not (fboundp 'python-shell-calculate-process-environment)) + (defun python-shell-calculate-process-environment () + "Compatibility function for older Emacsen." + process-environment)) + +(when (not (fboundp 'python-shell-get-process-name)) + (defun python-shell-get-process-name (dedicated) + "Compatibility function for older Emacsen." + "Python")) + +(when (not (fboundp 'python-shell-parse-command)) + (defun python-shell-parse-command () + "Compatibility function for older Emacsen." + python-python-command)) + +(when (not (fboundp 'python-shell-send-buffer)) + (defun python-shell-send-buffer (&optional arg) + (python-send-buffer))) + +(when (not (fboundp 'python-shell-send-string)) + (defalias 'python-shell-send-string 'python-send-string)) + +;; Emacs 24.2 made `locate-dominating-file' accept a predicate instead +;; of a string. Simply overwrite the current one, it's +;; backwards-compatible. The code below is taken from Emacs 24.3. +(when (or (< emacs-major-version 24) + (and (= emacs-major-version 24) + (<= emacs-minor-version 2))) + (defun locate-dominating-file (file name) + "Look up the directory hierarchy from FILE for a directory containing NAME. +Stop at the first parent directory containing a file NAME, +and return the directory. Return nil if not found. +Instead of a string, NAME can also be a predicate taking one argument +\(a directory) and returning a non-nil value if that directory is the one for +which we're looking." + ;; We used to use the above locate-dominating-files code, but the + ;; directory-files call is very costly, so we're much better off doing + ;; multiple calls using the code in here. + ;; + ;; Represent /home/luser/foo as ~/foo so that we don't try to look for + ;; `name' in /home or in /. + (setq file (abbreviate-file-name file)) + (let ((root nil) + ;; `user' is not initialized outside the loop because + ;; `file' may not exist, so we may have to walk up part of the + ;; hierarchy before we find the "initial UID". Note: currently unused + ;; (user nil) + try) + (while (not (or root + (null file) + ;; FIXME: Disabled this heuristic because it is sometimes + ;; inappropriate. + ;; As a heuristic, we stop looking up the hierarchy of + ;; directories as soon as we find a directory belonging + ;; to another user. This should save us from looking in + ;; things like /net and /afs. This assumes that all the + ;; files inside a project belong to the same user. + ;; (let ((prev-user user)) + ;; (setq user (nth 2 (file-attributes file))) + ;; (and prev-user (not (equal user prev-user)))) + (string-match locate-dominating-stop-dir-regexp file))) + (setq try (if (stringp name) + (file-exists-p (expand-file-name name file)) + (funcall name file))) + (cond (try (setq root file)) + ((equal file (setq file (file-name-directory + (directory-file-name file)))) + (setq file nil)))) + (if root (file-name-as-directory root)))) + ) + +;; highlight-indentation 0.5 does not use modes yet +(when (not (fboundp 'highlight-indentation-mode)) + (defun highlight-indentation-mode (on-or-off) + (cond + ((and (> on-or-off 0) + (not highlight-indent-active)) + (highlight-indentation)) + ((and (<= on-or-off 0) + highlight-indent-active) + (highlight-indentation))))) + +(provide 'elpy) +;;; elpy.el ends here diff --git a/emacs.d/elpa/elpy-1.7.1/elpy/__init__.py b/emacs.d/elpa/elpy-1.7.1/elpy/__init__.py new file mode 100644 index 0000000..11f88d6 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy/__init__.py @@ -0,0 +1,41 @@ +# Elpy, the Emacs Lisp Python Environment + +# Copyright (C) 2013 Jorgen Schaefer + +# Author: Jorgen Schaefer +# URL: http://github.com/jorgenschaefer/elpy + +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +"""The Emacs Lisp Python Environment. + +Elpy is a mode for Emacs to support writing Python code. This package +provides the backend within Python to support auto-completion, +documentation extraction, and navigation. + +Emacs will start the protocol by running the module itself, like so: + + python -m elpy + +This will emit a greeting string on a single line, and then wait for +the protocol to start. Details of the protocol can be found in +elpy.rpc. + +This package is unlikely to be useful on its own. + +""" + +__author__ = "Jorgen Schaefer" +__version__ = "1.7.1" +__license__ = "GPL" diff --git a/emacs.d/elpa/elpy-1.7.1/elpy/__main__.py b/emacs.d/elpa/elpy-1.7.1/elpy/__main__.py new file mode 100644 index 0000000..eac1b11 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy/__main__.py @@ -0,0 +1,26 @@ +"""Main interface to the RPC server. + +You should be able to just run the following to use this module: + +python -m elpy + +The first line should be "elpy-rpc ready". If it isn't, something +broke. + +""" + +import sys + +import elpy +from elpy.server import ElpyRPCServer + +if __name__ == '__main__': + # Workaround for libraries writing to stderr while they should + # not. See #458. Can be removed once #461 is implemented. + import os + sys.stderr = open(os.devnull) + # End workaround + sys.stdout.write('elpy-rpc ready ({0})\n' + .format(elpy.__version__)) + sys.stdout.flush() + ElpyRPCServer().serve_forever() diff --git a/emacs.d/elpa/elpy-1.7.1/elpy/compat.py b/emacs.d/elpa/elpy-1.7.1/elpy/compat.py new file mode 100644 index 0000000..ebe91a2 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy/compat.py @@ -0,0 +1,33 @@ +"""Python 2/3 compatibility definitions. + +These are used by the rest of Elpy to keep compatibility definitions +in one place. + +""" + +import sys + + +if sys.version_info >= (3, 0): + PYTHON3 = True + + from io import StringIO + + def ensure_not_unicode(obj): + return obj +else: + PYTHON3 = False + + from StringIO import StringIO # noqa + + def ensure_not_unicode(obj): + """Return obj. If it's a unicode string, convert it to str first. + + Pydoc functions simply don't find anything for unicode + strings. No idea why. + + """ + if isinstance(obj, unicode): + return obj.encode("utf-8") + else: + return obj diff --git a/emacs.d/elpa/elpy-1.7.1/elpy/impmagic.py b/emacs.d/elpa/elpy-1.7.1/elpy/impmagic.py new file mode 100644 index 0000000..5bc8fb7 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy/impmagic.py @@ -0,0 +1,82 @@ +"""Glue for the "importmagic" library. + +""" + +import os +import sys +import threading + + +try: + import importmagic.index + import importmagic.symbols + import importmagic.importer +except ImportError: # pragma: no cover + importmagic = None + + +class ImportMagic(object): + + def __init__(self): + self.is_enabled = bool(importmagic) + # fail_message is reported to the user when symbol_index + # is (still) None + self.fail_message = "symbol index is not yet ready" + self.project_root = None + self.symbol_index = None + self._thread = None + + def _build_symbol_index(self, project_root, custom_path, blacklist_re): + try: + index = importmagic.index.SymbolIndex(blacklist_re=blacklist_re) + if os.environ.get('ELPY_TEST'): + # test suite support: do not index the whole PYTHONPATH, it + # takes much too long + index.build_index([]) + elif custom_path: + index.build_index(custom_path) + else: + index.build_index([project_root] + sys.path) + except Exception as e: + self.fail_message = "symbol index failed to build: %s" % e + else: + self.symbol_index = index + + def build_index(self, project_root, custom_path=None, blacklist_re=None): + self.project_root = None + self._thread = threading.Thread(target=self._build_symbol_index, + args=(project_root, custom_path, + blacklist_re)) + self._thread.setDaemon(True) + self._thread.start() + + def get_import_symbols(self, symbol): + scores = self.symbol_index.symbol_scores(symbol) + return ["from %s import %s" % (mod, var) if var else "import %s" % mod + for (_, mod, var) in scores] + + def add_import(self, source, statement): + imports = importmagic.importer.Imports(self.symbol_index, source) + if statement.startswith('import '): + imports.add_import(statement[7:]) + else: + sep = statement.find(' import ') + if sep > -1: + imports.add_import_from(statement[5:sep], statement[sep+8:]) + start_line, end_line, import_block = imports.get_update() + return start_line, end_line, import_block + + def get_unresolved_symbols(self, source): + scope = importmagic.symbols.Scope.from_source(source) + unres, unref = scope.find_unresolved_and_unreferenced_symbols() + return list(unres) + + def remove_unreferenced_imports(self, source): + scope = importmagic.symbols.Scope.from_source(source) + unres, unref = scope.find_unresolved_and_unreferenced_symbols() + # Note: we do not supply "unres" to the call below, since we do + # not want to add imports without querying the user from which + # module symbols should be imported. + start_line, end_line, import_block = importmagic.importer.get_update( + source, self.symbol_index, set(), unref) + return start_line, end_line, import_block diff --git a/emacs.d/elpa/elpy-1.7.1/elpy/jedibackend.py b/emacs.d/elpa/elpy-1.7.1/elpy/jedibackend.py new file mode 100644 index 0000000..2c65151 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy/jedibackend.py @@ -0,0 +1,320 @@ +"""Elpy backend using the Jedi library. + +This backend uses the Jedi library: + +https://github.com/davidhalter/jedi + +""" + +import sys +import traceback + +import jedi + +from elpy import rpc + + +class JediBackend(object): + """The Jedi backend class. + + Implements the RPC calls we can pass on to Jedi. + + Documentation: http://jedi.jedidjah.ch/en/latest/docs/plugin-api.html + + """ + name = "jedi" + + def __init__(self, project_root): + self.project_root = project_root + self.completions = {} + sys.path.append(project_root) + + def rpc_get_completions(self, filename, source, offset): + line, column = pos_to_linecol(source, offset) + proposals = run_with_debug(jedi, 'completions', + source=source, line=line, column=column, + path=filename, encoding='utf-8') + if proposals is None: + return [] + self.completions = dict((proposal.name, proposal) + for proposal in proposals) + return [{'name': proposal.name, + 'suffix': proposal.complete, + 'annotation': proposal.type, + 'meta': proposal.description} + for proposal in proposals] + + def rpc_get_completion_docstring(self, completion): + proposal = self.completions.get(completion) + if proposal is None: + return None + else: + return proposal.docstring(fast=False) + + def rpc_get_completion_location(self, completion): + proposal = self.completions.get(completion) + if proposal is None: + return None + else: + return (proposal.module_path, proposal.line) + + def rpc_get_docstring(self, filename, source, offset): + line, column = pos_to_linecol(source, offset) + try: + locations = run_with_debug(jedi, 'goto_definitions', + source=source, line=line, column=column, + path=filename, encoding='utf-8', + re_raise=jedi.NotFoundError) + except jedi.NotFoundError: + return None + if locations: + return locations[-1].docstring() + else: + return None + + def rpc_get_definition(self, filename, source, offset): + line, column = pos_to_linecol(source, offset) + try: + locations = run_with_debug(jedi, 'goto_definitions', + source=source, line=line, column=column, + path=filename, encoding='utf-8', + re_raise=jedi.NotFoundError) + except jedi.NotFoundError: + return None + # goto_definitions() can return silly stuff like __builtin__ + # for int variables, so we fall back on goto() in those + # cases. See issue #76. + if ( + locations and + locations[0].module_path is None + ): + locations = run_with_debug(jedi, 'goto_assignments', + source=source, line=line, + column=column, + path=filename, encoding='utf-8') + if not locations: + return None + else: + loc = locations[-1] + try: + if loc.module_path: + if loc.module_path == filename: + offset = linecol_to_pos(source, + loc.line, + loc.column) + else: + with open(loc.module_path) as f: + offset = linecol_to_pos(f.read(), + loc.line, + loc.column) + except IOError: + return None + return (loc.module_path, offset) + + def rpc_get_calltip(self, filename, source, offset): + line, column = pos_to_linecol(source, offset) + calls = run_with_debug(jedi, 'call_signatures', + source=source, line=line, column=column, + path=filename, encoding='utf-8') + if calls: + call = calls[0] + else: + call = None + if not call: + return None + return {"name": call.name, + "index": call.index, + "params": [param.description for param in call.params]} + + def rpc_get_usages(self, filename, source, offset): + """Return the uses of the symbol at offset. + + Returns a list of occurrences of the symbol, as dicts with the + fields name, filename, and offset. + + """ + line, column = pos_to_linecol(source, offset) + try: + uses = run_with_debug(jedi, 'usages', + source=source, line=line, column=column, + path=filename, encoding='utf-8', + re_raise=(jedi.NotFoundError,)) + except jedi.NotFoundError: + return [] + + if uses is None: + return None + result = [] + for use in uses: + if use.module_path == filename: + offset = linecol_to_pos(source, use.line, use.column) + elif use.module_path is not None: + with open(use.module_path) as f: + text = f.read() + offset = linecol_to_pos(text, use.line, use.column) + + result.append({"name": use.name, + "filename": use.module_path, + "offset": offset}) + + return result + + +# From the Jedi documentation: +# +# line is the current line you want to perform actions on (starting +# with line #1 as the first line). column represents the current +# column/indent of the cursor (starting with zero). source_path +# should be the path of your file in the file system. + +def pos_to_linecol(text, pos): + """Return a tuple of line and column for offset pos in text. + + Lines are one-based, columns zero-based. + + This is how Jedi wants it. Don't ask me why. + + """ + line_start = text.rfind("\n", 0, pos) + 1 + line = text.count("\n", 0, line_start) + 1 + col = pos - line_start + return line, col + + +def linecol_to_pos(text, line, col): + """Return the offset of this line and column in text. + + Lines are one-based, columns zero-based. + + This is how Jedi wants it. Don't ask me why. + + """ + nth_newline_offset = 0 + for i in range(line - 1): + new_offset = text.find("\n", nth_newline_offset) + if new_offset < 0: + raise ValueError("Text does not have {0} lines." + .format(line)) + nth_newline_offset = new_offset + 1 + offset = nth_newline_offset + col + if offset > len(text): + raise ValueError("Line {0} column {1} is not within the text" + .format(line, col)) + return offset + + +def run_with_debug(jedi, name, *args, **kwargs): + re_raise = kwargs.pop('re_raise', ()) + # Remove form feed characters, they confuse Jedi (jedi#424) + if 'source' in kwargs: + kwargs['source'] = kwargs['source'].replace("\f", " ") + try: + script = jedi.Script(*args, **kwargs) + return getattr(script, name)() + except Exception as e: + if isinstance(e, re_raise): + raise + # Bug jedi#417 + if isinstance(e, TypeError) and str(e) == 'no dicts allowed': + return None + # Bug jedi#427 + if isinstance(e, UnicodeDecodeError): + return None + # Bug jedi#429 + if isinstance(e, IndexError): + return None + # Bug jedi#431 + if isinstance(e, AttributeError) and str(e).endswith("'end_pos'"): + return None + # Bug in Python 2.6, see #275 + if isinstance(e, OSError) and e.errno == 13: + return None + # Bug jedi#466 + if ( + isinstance(e, SyntaxError) and + "EOL while scanning string literal" in str(e) + ): + return None + # Bug jedi#482 + if isinstance(e, UnicodeEncodeError): + return None + # Bug jedi#485 + if ( + isinstance(e, ValueError) and + "invalid \\x escape" in str(e) + ): + return None + # Bug jedi#485 in Python 3 + if ( + isinstance(e, SyntaxError) and + "truncated \\xXX escape" in str(e) + ): + return None + # Bug jedi#465 + if ( + isinstance(e, SyntaxError) and + "encoding declaration in Unicode string" in str(e) + ): + return None + # Bug #337 / jedi#471 + if ( + isinstance(e, ImportError) and + "No module named" in str(e) + ): + return None + # Bug #365 / jedi#486 - fixed in Jedi 0.8.2 + if ( + isinstance(e, UnboundLocalError) and + "local variable 'path' referenced before assignment" in str(e) + ): + return None + # Bug #366 / jedi#491 + if ( + isinstance(e, ValueError) and + "__loader__ is None" in str(e) + ): + return None + # Bug #353 + if ( + isinstance(e, OSError) and + "No such file or directory" in str(e) + ): + return None + + from jedi import debug + + debug_info = [] + + def _debug(level, str_out): + if level == debug.NOTICE: + prefix = "[N]" + elif level == debug.WARNING: + prefix = "[W]" + else: + prefix = "[?]" + debug_info.append(u"{0} {1}".format(prefix, str_out)) + + jedi.set_debug_function(_debug, speed=False) + try: + script = jedi.Script(*args, **kwargs) + return getattr(script, name)() + except Exception as e: + source = kwargs.get('source') + sc_args = [] + sc_args.extend(repr(arg) for arg in args) + sc_args.extend("{0}={1}".format(k, "source" if k == "source" + else repr(v)) + for (k, v) in kwargs.items()) + + data = { + "traceback": traceback.format_exc(), + "jedi_debug_info": {'script_args': ", ".join(sc_args), + 'source': source, + 'method': name, + 'debug_info': debug_info} + } + raise rpc.Fault(message=str(e), + code=500, + data=data) + finally: + jedi.set_debug_function(None) diff --git a/emacs.d/elpa/elpy-1.7.1/elpy/pydocutils.py b/emacs.d/elpa/elpy-1.7.1/elpy/pydocutils.py new file mode 100644 index 0000000..259bd1c --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy/pydocutils.py @@ -0,0 +1,91 @@ +import sys +import types + +from pydoc import safeimport, resolve, ErrorDuringImport +from pkgutil import iter_modules + +from elpy import compat + +# Types we want to recurse into (nodes). +CONTAINER_TYPES = (type, types.ModuleType) +# Types of attributes we can get documentation for (leaves). +PYDOC_TYPES = (type, + types.FunctionType, + types.BuiltinFunctionType, + types.BuiltinMethodType, + types.MethodType, + types.ModuleType) +if not compat.PYTHON3: # pragma: nocover + # Python 2 old style classes + CONTAINER_TYPES = tuple(list(CONTAINER_TYPES) + [types.ClassType]) + PYDOC_TYPES = tuple(list(PYDOC_TYPES) + [types.ClassType]) + + +def get_pydoc_completions(modulename): + """Get possible completions for modulename for pydoc. + + Returns a list of possible values to be passed to pydoc. + + """ + modulename = compat.ensure_not_unicode(modulename) + modulename = modulename.rstrip(".") + if modulename == "": + return sorted(get_modules()) + candidates = get_completions(modulename) + if candidates: + return sorted(candidates) + needle = modulename + if "." in needle: + modulename, part = needle.rsplit(".", 1) + candidates = get_completions(modulename) + else: + candidates = get_modules() + return sorted(candidate for candidate in candidates + if candidate.startswith(needle)) + + +def get_completions(modulename): + modules = set("{0}.{1}".format(modulename, module) + for module in get_modules(modulename)) + + try: + module, name = resolve(modulename) + except ImportError: + return modules + if isinstance(module, CONTAINER_TYPES): + modules.update("{0}.{1}".format(modulename, name) + for name in dir(module) + if not name.startswith("_") and + isinstance(getattr(module, name), + PYDOC_TYPES)) + return modules + + +def get_modules(modulename=None): + """Return a list of modules and packages under modulename. + + If modulename is not given, return a list of all top level modules + and packages. + + """ + modulename = compat.ensure_not_unicode(modulename) + if not modulename: + try: + return ([modname for (importer, modname, ispkg) + in iter_modules() + if not modname.startswith("_")] + + list(sys.builtin_module_names)) + except OSError: + # Bug in Python 2.6, see #275 + return list(sys.builtin_module_names) + try: + module = safeimport(modulename) + except ErrorDuringImport: + return [] + if module is None: + return [] + if hasattr(module, "__path__"): + return [modname for (importer, modname, ispkg) + in iter_modules(module.__path__) + if not modname.startswith("_")] + return [] diff --git a/emacs.d/elpa/elpy-1.7.1/elpy/refactor.py b/emacs.d/elpa/elpy-1.7.1/elpy/refactor.py new file mode 100644 index 0000000..4044631 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy/refactor.py @@ -0,0 +1,366 @@ +"""Refactoring methods for elpy. + +This interfaces directly with rope, regardless of the backend used, +because the other backends don't really offer refactoring choices. +Once Jedi is similarly featureful as Rope we can try and offer both. + + +# Too complex: + +- Restructure: Interesting, but too complex, and needs deep Rope + knowledge to do well. + +- ChangeSignature: Slightly less complex interface, but still to + complex, requiring a large effort for the benefit. + + +# Too useless: + +I could not get these to work in any useful fashion. I might be doing +something wrong. + +- ExtractVariable does not replace the code extracted with the + variable, making it a glorified copy&paste function. Emacs can do + better than this interface by itself. + +- EncapsulateField: Getter/setter methods are outdated, this should be + using properties. + +- IntroduceFactory: Inserts a trivial method to the current class. + Cute. + +- IntroduceParameter: Introduces a parameter correctly, but does not + replace the old code with the parameter. So it just edits the + argument list and adds a shiny default. + +- LocalToField: Seems to just add "self." in front of all occurrences + of a variable in the local scope. + +- MethodObject: This turns the current method into a callable + class/object. Not sure what that would be good for. + + +# Can't even get to work: + +- ImportOrganizer expand_star_imports, handle_long_imports, + relatives_to_absolutes: Seem not to do anything. + +- create_move: I was not able to figure out what it would like to see + as its attrib argument. + +""" + +from elpy.rpc import Fault + +try: + from rope.base.exceptions import RefactoringError + from rope.base.project import Project + from rope.base.libutils import path_to_resource + from rope.base import change as rope_change + from rope.base import worder + from rope.refactor.importutils import ImportOrganizer + from rope.refactor.topackage import ModuleToPackage + from rope.refactor.rename import Rename + from rope.refactor.move import create_move + from rope.refactor.inline import create_inline + from rope.refactor.extract import ExtractMethod + from rope.refactor.usefunction import UseFunction + ROPE_AVAILABLE = True +except ImportError: + ROPE_AVAILABLE = False + + +def options(description, **kwargs): + """Decorator to set some options on a method.""" + def set_notes(function): + function.refactor_notes = {'name': function.__name__, + 'category': "Miscellaneous", + 'description': description, + 'doc': getattr(function, '__doc__', + ''), + 'args': []} + function.refactor_notes.update(kwargs) + return function + return set_notes + + +class Refactor(object): + """The main refactoring interface. + + Once initialized, the first call should be to get_refactor_options + to get a list of refactoring options at a given position. The + returned value will also list any additional options required. + + Once you picked one, you can call get_changes to get the actual + refactoring changes. + + """ + def __init__(self, project_root, filename): + self.project_root = project_root + if ROPE_AVAILABLE: + self.project = Project(project_root, ropefolder=None) + self.resource = path_to_resource(self.project, filename) + else: + self.project = None + self.resource = FakeResource(filename) + + def get_refactor_options(self, start, end=None): + """Return a list of options for refactoring at the given position. + + If `end` is also given, refactoring on a region is assumed. + + Each option is a dictionary of key/value pairs. The value of + the key 'name' is the one to be used for get_changes. + + The key 'args' contains a list of additional arguments + required for get_changes. + + """ + result = [] + for symbol in dir(self): + if not symbol.startswith("refactor_"): + continue + method = getattr(self, symbol) + if not method.refactor_notes.get('available', True): + continue + category = method.refactor_notes['category'] + if end is not None and category != 'Region': + continue + if end is None and category == 'Region': + continue + is_on_symbol = self._is_on_symbol(start) + if not is_on_symbol and category in ('Symbol', 'Method'): + continue + requires_import = method.refactor_notes.get('only_on_imports', + False) + if requires_import and not self._is_on_import_statement(start): + continue + result.append(method.refactor_notes) + return result + + def _is_on_import_statement(self, offset): + "Does this offset point to an import statement?" + data = self.resource.read() + bol = data.rfind("\n", 0, offset) + 1 + eol = data.find("\n", 0, bol) + if eol == -1: + eol = len(data) + line = data[bol:eol] + line = line.strip() + if line.startswith("import ") or line.startswith("from "): + return True + else: + return False + + def _is_on_symbol(self, offset): + "Is this offset on a symbol?" + if not ROPE_AVAILABLE: + return False + data = self.resource.read() + if offset >= len(data): + return False + if data[offset] != '_' and not data[offset].isalnum(): + return False + word = worder.get_name_at(self.resource, offset) + if word: + return True + else: + return False + + def get_changes(self, name, *args): + """Return a list of changes for the named refactoring action. + + Changes are dictionaries describing a single action to be + taken for the refactoring to be successful. + + A change has an action and possibly a type. In the description + below, the action is before the slash and the type after it. + + change: Change file contents + - file: The path to the file to change + - contents: The new contents for the file + - Diff: A unified diff showing the changes introduced + + create/file: Create a new file + - file: The file to create + + create/directory: Create a new directory + - path: The directory to create + + move/file: Rename a file + - source: The path to the source file + - destination: The path to the destination file name + + move/directory: Rename a directory + - source: The path to the source directory + - destination: The path to the destination directory name + + delete/file: Delete a file + - file: The file to delete + + delete/directory: Delete a directory + - path: The directory to delete + + """ + if not name.startswith("refactor_"): + raise ValueError("Bad refactoring name {0}".format(name)) + method = getattr(self, name) + if not method.refactor_notes.get('available', True): + raise RuntimeError("Method not available") + return method(*args) + + @options("Convert from x import y to import x.y as y", category="Imports", + args=[("offset", "offset", None)], + only_on_imports=True, + available=ROPE_AVAILABLE) + def refactor_froms_to_imports(self, offset): + """Converting imports of the form "from ..." to "import ...".""" + refactor = ImportOrganizer(self.project) + changes = refactor.froms_to_imports(self.resource, offset) + return translate_changes(changes) + + @options("Reorganize and clean up", category="Imports", + available=ROPE_AVAILABLE) + def refactor_organize_imports(self): + """Clean up and organize imports.""" + refactor = ImportOrganizer(self.project) + changes = refactor.organize_imports(self.resource) + return translate_changes(changes) + + @options("Convert the current module into a package", category="Module", + available=ROPE_AVAILABLE) + def refactor_module_to_package(self): + """Convert the current module into a package.""" + refactor = ModuleToPackage(self.project, self.resource) + changes = refactor.get_changes() + return translate_changes(changes) + + @options("Rename symbol at point", category="Symbol", + args=[("offset", "offset", None), + ("new_name", "string", "Rename to: "), + ("in_hierarchy", "boolean", + "Rename in super-/subclasses as well? "), + ("docs", "boolean", + "Replace occurences in docs and strings? ") + ], + available=ROPE_AVAILABLE) + def refactor_rename_at_point(self, offset, new_name, in_hierarchy, docs): + """Rename the symbol at point.""" + try: + refactor = Rename(self.project, self.resource, offset) + except RefactoringError as e: + raise Fault(str(e), code=400) + changes = refactor.get_changes(new_name, in_hierarchy=in_hierarchy, + docs=docs) + return translate_changes(changes) + + @options("Rename current module", category="Module", + args=[("new_name", "string", "Rename to: ")], + available=ROPE_AVAILABLE) + def refactor_rename_current_module(self, new_name): + """Rename the current module.""" + refactor = Rename(self.project, self.resource, None) + changes = refactor.get_changes(new_name) + return translate_changes(changes) + + @options("Move the current module to a different package", + category="Module", + args=[("new_name", "directory", "Destination package: ")], + available=ROPE_AVAILABLE) + def refactor_move_module(self, new_name): + """Move the current module.""" + refactor = create_move(self.project, self.resource) + resource = path_to_resource(self.project, new_name) + changes = refactor.get_changes(resource) + return translate_changes(changes) + + @options("Inline function call at point", category="Symbol", + args=[("offset", "offset", None), + ("only_this", "boolean", "Only this occurrence? ")], + available=ROPE_AVAILABLE) + def refactor_create_inline(self, offset, only_this): + """Inline the function call at point.""" + refactor = create_inline(self.project, self.resource, offset) + if only_this: + changes = refactor.get_changes(remove=False, only_current=True) + else: + changes = refactor.get_changes(remove=True, only_current=False) + return translate_changes(changes) + + @options("Extract current region as a method", category="Region", + args=[("start", "start_offset", None), + ("end", "end_offset", None), + ("name", "string", "Method name: "), + ("make_global", "boolean", "Create global method? ")], + available=ROPE_AVAILABLE) + def refactor_extract_method(self, start, end, name, + make_global): + """Extract region as a method.""" + refactor = ExtractMethod(self.project, self.resource, start, end) + changes = refactor.get_changes(name, similar=True, global_=make_global) + return translate_changes(changes) + + @options("Use the function at point wherever possible", category="Method", + args=[("offset", "offset", None)], + available=ROPE_AVAILABLE) + def refactor_use_function(self, offset): + """Use the function at point wherever possible.""" + refactor = UseFunction(self.project, self.resource, offset) + changes = refactor.get_changes() + return translate_changes(changes) + + +def translate_changes(initial_change): + """Translate rope.base.change.Change instances to dictionaries. + + See Refactor.get_changes for an explanation of the resulting + dictionary. + + """ + agenda = [initial_change] + result = [] + while agenda: + change = agenda.pop(0) + if isinstance(change, rope_change.ChangeSet): + agenda.extend(change.changes) + elif isinstance(change, rope_change.ChangeContents): + result.append({'action': 'change', + 'file': change.resource.real_path, + 'contents': change.new_contents, + 'diff': change.get_description()}) + elif isinstance(change, rope_change.CreateFile): + result.append({'action': 'create', + 'type': 'file', + 'file': change.resource.real_path}) + elif isinstance(change, rope_change.CreateFolder): + result.append({'action': 'create', + 'type': 'directory', + 'path': change.resource.real_path}) + elif isinstance(change, rope_change.MoveResource): + result.append({'action': 'move', + 'type': ('directory' + if change.new_resource.is_folder() + else 'file'), + 'source': change.resource.real_path, + 'destination': change.new_resource.real_path}) + elif isinstance(change, rope_change.RemoveResource): + if change.resource.is_folder(): + result.append({'action': 'delete', + 'type': 'directory', + 'path': change.resource.real_path}) + else: + result.append({'action': 'delete', + 'type': 'file', + 'file': change.resource.real_path}) + return result + + +class FakeResource(object): + """A fake resource in case Rope is absence.""" + + def __init__(self, filename): + self.real_path = filename + + def read(self): + with open(self.real_path) as f: + return f.read() diff --git a/emacs.d/elpa/elpy-1.7.1/elpy/ropebackend.py b/emacs.d/elpa/elpy-1.7.1/elpy/ropebackend.py new file mode 100644 index 0000000..8328efd --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy/ropebackend.py @@ -0,0 +1,322 @@ +"""Elpy backend using the Rope library. + +This backend uses the Rope library: + +http://rope.sourceforge.net/ + +""" + +import os +import time +import traceback + +import rope.contrib.codeassist +import rope.base.project +import rope.base.libutils +import rope.base.exceptions +import rope.contrib.findit + +from elpy import rpc +import elpy.pydocutils + +VALIDATE_EVERY_SECONDS = 5 +MAXFIXES = 5 + + +class RopeBackend(object): + """The Rope backend class. + + Implements the RPC calls we can pass on to Rope. Also subclasses + the native backend to provide methods Rope does not provide, if + any. + + """ + name = "rope" + + def __init__(self, project_root): + super(RopeBackend, self).__init__() + self.last_validation = 0 + if not os.path.exists(project_root): + project_root = "" + self.project_root = project_root + self.completions = {} + prefs = dict(ignored_resources=['*.pyc', '*~', '.ropeproject', + '.hg', '.svn', '_svn', '.git'], + python_files=['*.py'], + save_objectdb=False, + compress_objectdb=False, + automatic_soa=True, + soa_followed_calls=0, + perform_doa=True, + validate_objectdb=True, + max_history_items=32, + save_history=False, + compress_history=False, + indent_size=4, + extension_modules=[], + import_dynload_stdmods=True, + ignore_syntax_errors=False, + ignore_bad_imports=False) + self.project = rope.base.project.Project(self.project_root, + ropefolder=None, + **prefs) + + def get_resource(self, filename): + if filename is not None and os.path.exists(filename): + return rope.base.libutils.path_to_resource(self.project, + filename, + 'file') + else: + return None + + def validate(self): + """Validate the stored project. + + This should be called before every use of Rope. It will + revalidate the project, but do some call throttling. + + """ + now = time.time() + if now > self.last_validation + VALIDATE_EVERY_SECONDS: + try: + self.project.validate() + except rope.base.exceptions.ResourceNotFoundError: + pass + self.last_validation = now + + def call_rope(self, rope_function, filename, source, offset, + **kwargs): + self.validate() + resource = self.get_resource(filename) + try: + return rope_function(self.project, + source, offset, + resource, + maxfixes=MAXFIXES, + **kwargs) + except (rope.base.exceptions.BadIdentifierError, + rope.base.exceptions.ModuleSyntaxError, + rope.base.exceptions.ResourceNotFoundError, + IndentationError, + LookupError, + AttributeError): + return None + except Exception as e: + data = { + "traceback": traceback.format_exc(), + "rope_debug_info": { + "project_root": self.project_root, + "filename": filename, + "source": source, + "function_name": (rope_function.__module__ + + "." + + rope_function.__name__), + "function_args": ", ".join([ + "project", "source", str(offset), "resource", + "maxfixes={0}".format(MAXFIXES) + ] + [ + u"{}={}".format(k, v) + for (k, v) in kwargs.items() + ]) + } + } + raise rpc.Fault( + code=500, + message=str(e), + data=data + ) + + def rpc_get_completions(self, filename, source, offset): + proposals = self.call_rope( + rope.contrib.codeassist.code_assist, + filename, source, offset + ) + if proposals is None: + return [] + try: + starting_offset = rope.contrib.codeassist.starting_offset(source, + offset) + except (rope.base.exceptions.BadIdentifierError, + rope.base.exceptions.ModuleSyntaxError, + IndentationError, + LookupError, + AttributeError): + return [] + prefixlen = offset - starting_offset + self.completions = dict((proposal.name, proposal) + for proposal in proposals) + try: + return [{'name': proposal.name, + 'suffix': proposal.name[prefixlen:], + 'annotation': proposal.type, + 'meta': str(proposal)} + for proposal in proposals] + except rope.base.exceptions.ModuleSyntaxError: + # Bug#406 + return [] + + def rpc_get_completion_docstring(self, completion): + proposal = self.completions.get(completion) + if proposal is None: + return None + else: + return proposal.get_doc() + + def rpc_get_completion_location(self, completion): + proposal = self.completions.get(completion) + if proposal is None: + return None + else: + if not proposal.pyname: + return None + module, lineno = proposal.pyname.get_definition_location() + if module is None: + return None + resource = module.get_module().get_resource() + return (resource.real_path, lineno) + + def rpc_get_definition(self, filename, source, offset): + location = self.call_rope( + rope.contrib.findit.find_definition, + filename, source, offset + ) + if location is None: + return None + else: + return (location.resource.real_path, location.offset) + + def rpc_get_calltip(self, filename, source, offset): + offset = find_called_name_offset(source, offset) + if 0 < offset < len(source) and source[offset] == ')': + offset -= 1 + + calltip = self.call_rope( + rope.contrib.codeassist.get_calltip, + filename, source, offset, + remove_self=True + ) + if calltip is None: + return None + + calltip = calltip.replace(".__init__(", "(") + calltip = calltip.replace("(self)", "()") + calltip = calltip.replace("(self, ", "(") + # "elpy.tests.support.source_and_offset(source)" + # => + # "support.source_and_offset(source)" + try: + openpos = calltip.index("(") + period2 = calltip.rindex(".", 0, openpos) + period1 = calltip.rindex(".", 0, period2) + calltip = calltip[period1 + 1:] + except ValueError: + pass + return calltip + + def rpc_get_docstring(self, filename, source, offset): + return self.call_rope( + rope.contrib.codeassist.get_doc, + filename, source, offset + ) + + +def find_called_name_offset(source, orig_offset): + """Return the offset of a calling function. + + This only approximates movement. + + """ + offset = min(orig_offset, len(source) - 1) + paren_count = 0 + while True: + if offset <= 1: + return orig_offset + elif source[offset] == '(': + if paren_count == 0: + return offset - 1 + else: + paren_count -= 1 + elif source[offset] == ')': + paren_count += 1 + offset -= 1 + + +################################################################## +# A recurring problem in Rope for Elpy is that it searches the whole +# project root for Python files. If the user edits a file in their +# home directory, this can easily read a whole lot of files, making +# Rope practically useless. We change the file finding algorithm here +# to only recurse into directories with an __init__.py file in them. +def find_source_folders(self, folder): + for resource in folder.get_folders(): + if self._is_package(resource): + return [folder] + result = [] + for resource in folder.get_files(): + if resource.name.endswith('.py'): + result.append(folder) + break + for resource in folder.get_folders(): + if self._is_package(resource): + result.append(resource) + return result + +import rope.base.pycore +rope.base.pycore.PyCore._find_source_folders = find_source_folders + + +def get_files(self): + if self.files is None: + self.files = get_python_project_files(self.project) + return self.files + +rope.base.project._FileListCacher.get_files = get_files + + +def get_python_project_files(project): + for dirname, subdirs, files in os.walk(project.root.real_path): + for filename in files: + yield rope.base.libutils.path_to_resource( + project, os.path.join(dirname, filename), 'file') + subdirs[:] = [subdir for subdir in subdirs + if os.path.exists(os.path.join(dirname, subdir, + "__init__.py"))] + + +################################################################## +# Monkey patching a method in rope because it doesn't complete import +# statements. + +orig_code_completions = (rope.contrib.codeassist. + _PythonCodeAssist._code_completions) + + +def code_completions(self): + proposals = get_import_completions(self) + if proposals: + return proposals + else: + return orig_code_completions(self) + + +def get_import_completions(self): + if not self.word_finder.is_import_statement(self.offset): + return [] + modulename = self.word_finder.get_primary_at(self.offset) + # Rope can handle modules in packages + if "." in modulename: + return [] + return dict((name, FakeProposal(name)) + for name in elpy.pydocutils.get_modules() + if name.startswith(modulename)) + + +class FakeProposal(object): + def __init__(self, name): + self.name = name + self.type = "mock" + + def get_doc(self): + return None + +rope.contrib.codeassist._PythonCodeAssist._code_completions = code_completions diff --git a/emacs.d/elpa/elpy-1.7.1/elpy/rpc.py b/emacs.d/elpa/elpy-1.7.1/elpy/rpc.py new file mode 100644 index 0000000..07b2ce9 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy/rpc.py @@ -0,0 +1,151 @@ +"""A simple JSON-RPC-like server. + +The server will read and write lines of JSON-encoded method calls and +responses. + +See the documentation of the JSONRPCServer class for further details. + +""" + +import json +import sys +import traceback + + +class JSONRPCServer(object): + """Simple JSON-RPC-like server. + + This class will read single-line JSON expressions from stdin, + decode them, and pass them to a handler. Return values from the + handler will be JSON-encoded and written to stdout. + + To implement a handler, you need to subclass this class and add + methods starting with "rpc_". Methods then will be found. + + Method calls should be encoded like this: + + {"id": 23, "method": "method_name", "params": ["foo", "bar"]} + + This will call self.rpc_method("foo", "bar"). + + Responses will be encoded like this: + + {"id": 23, "result": "foo"} + + Errors will be encoded like this: + + {"id": 23, "error": "Simple error message"} + + See http://www.jsonrpc.org/ for the inspiration of the protocol. + + """ + + def __init__(self, stdin=None, stdout=None): + """Return a new JSON-RPC server object. + + It will read lines of JSON data from stdin, and write the + responses to stdout. + + """ + if stdin is None: + self.stdin = sys.stdin + else: + self.stdin = stdin + if stdout is None: + self.stdout = sys.stdout + else: + self.stdout = stdout + + def read_json(self): + """Read a single line and decode it as JSON. + + Can raise an EOFError() when the input source was closed. + + """ + line = self.stdin.readline() + if line == '': + raise EOFError() + return json.loads(line) + + def write_json(self, **kwargs): + """Write an JSON object on a single line. + + The keyword arguments are interpreted as a single JSON object. + It's not possible with this method to write non-objects. + + """ + self.stdout.write(json.dumps(kwargs) + "\n") + self.stdout.flush() + + def handle_request(self): + """Handle a single JSON-RPC request. + + Read a request, call the appropriate handler method, and + return the encoded result. Errors in the handler method are + caught and encoded as error objects. Errors in the decoding + phase are not caught, as we can not respond with an error + response to them. + + """ + request = self.read_json() + if 'method' not in request: + raise ValueError("Received a bad request: {0}" + .format(request)) + method_name = request['method'] + request_id = request.get('id', None) + params = request.get('params') or [] + try: + method = getattr(self, "rpc_" + method_name, None) + if method is not None: + result = method(*params) + else: + result = self.handle(method_name, params) + if request_id is not None: + self.write_json(result=result, + id=request_id) + except Fault as fault: + error = {"message": fault.message, + "code": fault.code} + if fault.data is not None: + error["data"] = fault.data + self.write_json(error=error, id=request_id) + except Exception as e: + error = {"message": str(e), + "code": 500, + "data": {"traceback": traceback.format_exc()}} + self.write_json(error=error, id=request_id) + + def handle(self, method_name, args): + """Handle the call to method_name. + + You should overwrite this method in a subclass. + """ + raise Fault("Unknown method {0}".format(method_name)) + + def serve_forever(self): + """Serve requests forever. + + Errors are not caught, so this is a slight misnomer. + + """ + while True: + try: + self.handle_request() + except (KeyboardInterrupt, EOFError, SystemExit): + break + + +class Fault(Exception): + """RPC Fault instances. + + code defines the severity of the warning. + + 2xx: Normal behavior lead to end of operation, i.e. a warning + 4xx: An expected error occurred + 5xx: An unexpected error occurred (usually includes a traceback) + """ + def __init__(self, message, code=500, data=None): + super(Fault, self).__init__(message) + self.message = message + self.code = code + self.data = data diff --git a/emacs.d/elpa/elpy-1.7.1/elpy/server.py b/emacs.d/elpa/elpy-1.7.1/elpy/server.py new file mode 100644 index 0000000..909cd0e --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/elpy/server.py @@ -0,0 +1,275 @@ +"""Method implementations for the Elpy JSON-RPC server. + +This file implements the methods exported by the JSON-RPC server. It +handles backend selection and passes methods on to the selected +backend. + +""" +import io +import os +import pydoc + +from elpy.pydocutils import get_pydoc_completions +from elpy.rpc import JSONRPCServer, Fault +from elpy.impmagic import ImportMagic + + +try: + from elpy import jedibackend +except ImportError: # pragma: no cover + jedibackend = None + +try: + from elpy import ropebackend +except ImportError: # pragma: no cover + ropebackend = None + + +class ElpyRPCServer(JSONRPCServer): + """The RPC server for elpy. + + See the rpc_* methods for exported method documentation. + + """ + def __init__(self): + super(ElpyRPCServer, self).__init__() + self.backend = None + self.import_magic = ImportMagic() + self.project_root = None + + def _call_backend(self, method, default, *args, **kwargs): + """Call the backend method with args. + + If there is currently no backend, return default.""" + meth = getattr(self.backend, method, None) + if meth is None: + return default + else: + return meth(*args, **kwargs) + + def rpc_echo(self, *args): + """Return the arguments. + + This is a simple test method to see if the protocol is + working. + + """ + return args + + def rpc_init(self, options): + self.project_root = options["project_root"] + + if self.import_magic.is_enabled: + self.import_magic.build_index(self.project_root) + + if ropebackend and options["backend"] == "rope": + self.backend = ropebackend.RopeBackend(self.project_root) + elif jedibackend and options["backend"] == "jedi": + self.backend = jedibackend.JediBackend(self.project_root) + elif ropebackend: + self.backend = ropebackend.RopeBackend(self.project_root) + elif jedibackend: + self.backend = jedibackend.JediBackend(self.project_root) + else: + self.backend = None + + return { + 'backend': (self.backend.name if self.backend is not None + else None) + } + + def rpc_get_calltip(self, filename, source, offset): + """Get the calltip for the function at the offset. + + """ + return self._call_backend("rpc_get_calltip", None, filename, + get_source(source), offset) + + def rpc_get_completions(self, filename, source, offset): + """Get a list of completion candidates for the symbol at offset. + + """ + results = self._call_backend("rpc_get_completions", [], filename, + get_source(source), offset) + # Uniquify by name + results = list(dict((res['name'], res) for res in results) + .values()) + results.sort(key=lambda cand: _pysymbol_key(cand["name"])) + return results + + def rpc_get_completion_docstring(self, completion): + """Return documentation for a previously returned completion. + + """ + return self._call_backend("rpc_get_completion_docstring", + None, completion) + + def rpc_get_completion_location(self, completion): + """Return the location for a previously returned completion. + + This returns a list of [file name, line number]. + + """ + return self._call_backend("rpc_get_completion_location", None, + completion) + + def rpc_get_definition(self, filename, source, offset): + """Get the location of the definition for the symbol at the offset. + + """ + return self._call_backend("rpc_get_definition", None, filename, + get_source(source), offset) + + def rpc_get_docstring(self, filename, source, offset): + """Get the docstring for the symbol at the offset. + + """ + return self._call_backend("rpc_get_docstring", None, filename, + get_source(source), offset) + + def rpc_get_pydoc_completions(self, name=None): + """Return a list of possible strings to pass to pydoc. + + If name is given, the strings are under name. If not, top + level modules are returned. + + """ + return get_pydoc_completions(name) + + def rpc_get_pydoc_documentation(self, symbol): + """Get the Pydoc documentation for the given symbol. + + Uses pydoc and can return a string with backspace characters + for bold highlighting. + + """ + try: + docstring = pydoc.render_doc(str(symbol), + "Elpy Pydoc Documentation for %s", + False) + except (ImportError, pydoc.ErrorDuringImport): + return None + else: + if isinstance(docstring, bytes): + docstring = docstring.decode("utf-8", "replace") + return docstring + + def rpc_get_refactor_options(self, filename, start, end=None): + """Return a list of possible refactoring options. + + This list will be filtered depending on whether it's + applicable at the point START and possibly the region between + START and END. + + """ + try: + from elpy import refactor + except: + raise ImportError("Rope not installed, refactorings unavailable") + ref = refactor.Refactor(self.project_root, filename) + return ref.get_refactor_options(start, end) + + def rpc_refactor(self, filename, method, args): + """Return a list of changes from the refactoring action. + + A change is a dictionary describing the change. See + elpy.refactor.translate_changes for a description. + + """ + try: + from elpy import refactor + except: + raise ImportError("Rope not installed, refactorings unavailable") + if args is None: + args = () + ref = refactor.Refactor(self.project_root, filename) + return ref.get_changes(method, *args) + + def rpc_get_usages(self, filename, source, offset): + """Get usages for the symbol at point. + + """ + source = get_source(source) + if hasattr(self.backend, "rpc_get_usages"): + return self.backend.rpc_get_usages(filename, source, offset) + else: + raise Fault("get_usages not implemented by current backend", + code=400) + + def _ensure_import_magic(self): # pragma: no cover + if not self.import_magic.is_enabled: + raise Fault("fixup_imports not enabled; install importmagic module", + code=400) + if not self.import_magic.symbol_index: + raise Fault(self.import_magic.fail_message, code=200) # XXX code? + + def rpc_get_import_symbols(self, filename, source, symbol): + """Return a list of modules from which the given symbol can be imported. + + """ + self._ensure_import_magic() + return self.import_magic.get_import_symbols(symbol) + + def rpc_add_import(self, filename, source, statement): + """Add an import statement to the module. + + """ + self._ensure_import_magic() + source = get_source(source) + return self.import_magic.add_import(source, statement) + + def rpc_get_unresolved_symbols(self, filename, source): + """Return a list of unreferenced symbols in the module. + + """ + self._ensure_import_magic() + source = get_source(source) + return self.import_magic.get_unresolved_symbols(source) + + def rpc_remove_unreferenced_imports(self, filename, source): + """Remove unused import statements. + + """ + self._ensure_import_magic() + source = get_source(source) + return self.import_magic.remove_unreferenced_imports(source) + + +def get_source(fileobj): + """Translate fileobj into file contents. + + fileobj is either a string or a dict. If it's a string, that's the + file contents. If it's a string, then the filename key contains + the name of the file whose contents we are to use. + + If the dict contains a true value for the key delete_after_use, + the file should be deleted once read. + + """ + if not isinstance(fileobj, dict): + return fileobj + else: + try: + with io.open(fileobj["filename"], encoding="utf-8") as f: + return f.read() + finally: + if fileobj.get('delete_after_use'): + try: + os.remove(fileobj["filename"]) + except: # pragma: no cover + pass + + +def _pysymbol_key(name): + """Return a sortable key index for name. + + Sorting is case-insensitive, with the first underscore counting as + worse than any character, but subsequent underscores do not. This + means that dunder symbols (like __init__) are sorted after symbols + that start with an alphabetic character, but before those that + start with only a single underscore. + + """ + if name.startswith("_"): + name = "~" + name[1:] + return name.lower() diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/.yas-setup.el b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/.yas-setup.el new file mode 100644 index 0000000..c528323 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/.yas-setup.el @@ -0,0 +1,62 @@ +(defun elpy-snippet-split-args (arg-string) + "Split a python argument string into ((name, default)..) tuples" + (mapcar (lambda (x) + (split-string x "[[:blank:]]*=[[:blank:]]*" t)) + (split-string arg-string "[[:blank:]]*,[[:blank:]]*" t))) + +(defun elpy-snippet-current-method-and-args () + "Return information on the current definition." + (let ((current-defun (python-info-current-defun)) + (current-arglist + (save-excursion + (python-nav-beginning-of-defun) + (when (re-search-forward "(" nil t) + (let* ((start (point)) + (end (progn + (forward-char -1) + (forward-sexp) + (- (point) 1)))) + (elpy-snippet-split-args + (buffer-substring-no-properties start end)))))) + class method args) + (when (not current-arglist) + (setq current-arglist '(("self")))) + (if (and current-defun + (string-match "^\\(.*\\)\\.\\(.*\\)$" current-defun)) + (setq class (match-string 1 current-defun) + method (match-string 2 current-defun)) + (setq class "Class" + method "method")) + (setq args (mapcar #'car current-arglist)) + (list class method args))) + +(defun elpy-snippet-init-assignments (arg-string) + "Return the typical __init__ assignments for arguments." + (let ((indentation (make-string (save-excursion + (goto-char start-point) + (current-indentation)) + ?\s))) + (mapconcat (lambda (arg) + (if (string-match "^\\*" (car arg)) + "" + (format "self.%s = %s\n%s" + (car arg) + (car arg) + indentation))) + (elpy-snippet-split-args arg-string) + ""))) + +(defun elpy-snippet-super-form () + "Return (Class, first-arg).method" + (let* ((defun-info (elpy-snippet-current-method-and-args)) + (class (nth 0 defun-info)) + (method (nth 1 defun-info)) + (args (nth 2 defun-info)) + (first-arg (nth 0 args))) + (format "(%s, %s).%s" class first-arg method))) + +(defun elpy-snippet-super-arguments () + "Return the argument list for the current method." + (mapconcat (lambda (x) x) + (cdr (nth 2 (elpy-snippet-current-method-and-args))) + ", ")) diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__abs__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__abs__ new file mode 100644 index 0000000..12b1585 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__abs__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __abs__ +# key: __abs__ +# group: Special methods +# -- +def __abs__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__add__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__add__ new file mode 100644 index 0000000..a70f20f --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__add__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __add__ +# key: __add__ +# group: Special methods +# -- +def __add__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__and__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__and__ new file mode 100644 index 0000000..56961d3 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__and__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __and__ +# key: __and__ +# group: Special methods +# -- +def __and__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__bool__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__bool__ new file mode 100644 index 0000000..ba4ffe8 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__bool__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __bool__ +# key: __bool__ +# group: Special methods +# -- +def __bool__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__call__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__call__ new file mode 100644 index 0000000..dea8463 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__call__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __call__ +# key: __call__ +# group: Special methods +# -- +def __call__(self, ${1:*args}): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__cmp__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__cmp__ new file mode 100644 index 0000000..c266621 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__cmp__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __cmp__ +# key: __cmp__ +# group: Special methods +# -- +def __cmp__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__coerce__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__coerce__ new file mode 100644 index 0000000..297138c --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__coerce__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __coerce__ +# key: __coerce__ +# group: Special methods +# -- +def __coerce__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__complex__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__complex__ new file mode 100644 index 0000000..108e47e --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__complex__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __complex__ +# key: __complex__ +# group: Special methods +# -- +def __complex__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__contains__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__contains__ new file mode 100644 index 0000000..5376c2d --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__contains__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __contains__ +# key: __contains__ +# group: Special methods +# -- +def __contains__(self, item): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__del__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__del__ new file mode 100644 index 0000000..a3a1a04 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__del__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __del__ +# key: __del__ +# group: Special methods +# -- +def __del__(self): + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__delattr__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__delattr__ new file mode 100644 index 0000000..fe41a47 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__delattr__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __delattr__ +# key: __delattr__ +# group: Special methods +# -- +def __delattr__(self, name): + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__delete__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__delete__ new file mode 100644 index 0000000..7b216b4 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__delete__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __delete__ +# key: __delete__ +# group: Special methods +# -- +def __delete__(self, instance): + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__delitem__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__delitem__ new file mode 100644 index 0000000..fd79a02 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__delitem__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __delitem__ +# key: __delitem__ +# group: Special methods +# -- +def __delitem__(self, key): + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__div__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__div__ new file mode 100644 index 0000000..8da4ea9 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__div__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __div__ +# key: __div__ +# group: Special methods +# -- +def __div__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__divmod__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__divmod__ new file mode 100644 index 0000000..5863274 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__divmod__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __divmod__ +# key: __divmod__ +# group: Special methods +# -- +def __divmod__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__enter__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__enter__ new file mode 100644 index 0000000..0ad587f --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__enter__ @@ -0,0 +1,9 @@ +# -*- mode: snippet -*- +# name: __enter__ +# key: __enter__ +# group: Special methods +# -- +def __enter__(self): + $0 + + return self \ No newline at end of file diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__eq__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__eq__ new file mode 100644 index 0000000..0f772ff --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__eq__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __eq__ +# key: __eq__ +# group: Special methods +# -- +def __eq__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__exit__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__exit__ new file mode 100644 index 0000000..e194524 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__exit__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __exit__ +# key: __exit__ +# group: Special methods +# -- +def __exit__(self, exc_type, exc_value, traceback): + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__float__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__float__ new file mode 100644 index 0000000..6f1ab4a --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__float__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __float__ +# key: __float__ +# group: Special methods +# -- +def __float__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__floordiv__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__floordiv__ new file mode 100644 index 0000000..117f174 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__floordiv__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __floordiv__ +# key: __floordiv__ +# group: Special methods +# -- +def __floordiv__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ge__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ge__ new file mode 100644 index 0000000..a7f5c49 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ge__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __ge__ +# key: __ge__ +# group: Special methods +# -- +def __ge__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__get__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__get__ new file mode 100644 index 0000000..9d4a9fa --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__get__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __get__ +# key: __get__ +# group: Special methods +# -- +def __get__(self, instance, owner): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__getattr__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__getattr__ new file mode 100644 index 0000000..07e9afc --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__getattr__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __getattr__ +# key: __getattr__ +# group: Special methods +# -- +def __getattr__(self, name): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__getattribute__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__getattribute__ new file mode 100644 index 0000000..e6bfedd --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__getattribute__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __getattribute__ +# key: __getattribute__ +# group: Special methods +# -- +def __getattribute__(self, name): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__getitem__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__getitem__ new file mode 100644 index 0000000..6d73dc7 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__getitem__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __getitem__ +# key: __getitem__ +# group: Special methods +# -- +def __getitem__(self, key): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__gt__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__gt__ new file mode 100644 index 0000000..0fb0eb3 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__gt__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __gt__ +# key: __gt__ +# group: Special methods +# -- +def __gt__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__hash__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__hash__ new file mode 100644 index 0000000..a117104 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__hash__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __hash__ +# key: __hash__ +# group: Special methods +# -- +def __hash__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__hex__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__hex__ new file mode 100644 index 0000000..62df1d7 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__hex__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __hex__ +# key: __hex__ +# group: Special methods +# -- +def __hex__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__iadd__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__iadd__ new file mode 100644 index 0000000..fe1f244 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__iadd__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __iadd__ +# key: __iadd__ +# group: Special methods +# -- +def __iadd__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__iand__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__iand__ new file mode 100644 index 0000000..6b027e9 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__iand__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __iand__ +# key: __iand__ +# group: Special methods +# -- +def __iand__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__idiv__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__idiv__ new file mode 100644 index 0000000..fb32a86 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__idiv__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __idiv__ +# key: __idiv__ +# group: Special methods +# -- +def __idiv__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ifloordiv__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ifloordiv__ new file mode 100644 index 0000000..8d20072 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ifloordiv__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __ifloordiv__ +# key: __ifloordiv__ +# group: Special methods +# -- +def __ifloordiv__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ilshift__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ilshift__ new file mode 100644 index 0000000..e29895f --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ilshift__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __ilshift__ +# key: __ilshift__ +# group: Special methods +# -- +def __ilshift__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__imod__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__imod__ new file mode 100644 index 0000000..4d74b13 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__imod__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __imod__ +# key: __imod__ +# group: Special methods +# -- +def __imod__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__imul__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__imul__ new file mode 100644 index 0000000..ee84829 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__imul__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __imul__ +# key: __imul__ +# group: Special methods +# -- +def __imul__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__index__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__index__ new file mode 100644 index 0000000..7337f76 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__index__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __index__ +# key: __index__ +# group: Special methods +# -- +def __index__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__init__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__init__ new file mode 100644 index 0000000..e1f6cde --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__init__ @@ -0,0 +1,10 @@ +# -*- mode: snippet -*- +# name: __init__ with assignment +# key: __init__ +# group: Special methods +# -- +def __init__(self${1:, args}): + """$2 + + """ + ${1:$(elpy-snippet-init-assignments yas-text)} diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__instancecheck__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__instancecheck__ new file mode 100644 index 0000000..4b567aa --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__instancecheck__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __instancecheck__ +# key: __instancecheck__ +# group: Special methods +# -- +def __instancecheck__(self, instance): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__int__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__int__ new file mode 100644 index 0000000..fa136df --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__int__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __int__ +# key: __int__ +# group: Special methods +# -- +def __int__(self): + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__invert__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__invert__ new file mode 100644 index 0000000..bbf1df4 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__invert__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __invert__ +# key: __invert__ +# group: Special methods +# -- +def __invert__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ior__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ior__ new file mode 100644 index 0000000..045a0d5 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ior__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __ior__ +# key: __ior__ +# group: Special methods +# -- +def __ior__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ipow__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ipow__ new file mode 100644 index 0000000..ec62f49 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ipow__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __ipow__ +# key: __ipow__ +# group: Special methods +# -- +def __ipow__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__irshift__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__irshift__ new file mode 100644 index 0000000..193f790 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__irshift__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __irshift__ +# key: __irshift__ +# group: Special methods +# -- +def __irshift__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__isub__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__isub__ new file mode 100644 index 0000000..70fd574 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__isub__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __isub__ +# key: __isub__ +# group: Special methods +# -- +def __isub__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__iter__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__iter__ new file mode 100644 index 0000000..a7002e0 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__iter__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __iter__ +# key: __iter__ +# group: Special methods +# -- +def __iter__(self): + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__itruediv__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__itruediv__ new file mode 100644 index 0000000..14caec6 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__itruediv__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __itruediv__ +# key: __itruediv__ +# group: Special methods +# -- +def __itruediv__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ixor__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ixor__ new file mode 100644 index 0000000..ffba3ed --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ixor__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __ixor__ +# key: __ixor__ +# group: Special methods +# -- +def __ixor__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__le__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__le__ new file mode 100644 index 0000000..f08aebe --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__le__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __le__ +# key: __le__ +# group: Special methods +# -- +def __le__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__len__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__len__ new file mode 100644 index 0000000..7d15f93 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__len__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __len__ +# key: __len__ +# group: Special methods +# -- +def __len__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__long__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__long__ new file mode 100644 index 0000000..16c3cd2 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__long__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __long__ +# key: __long__ +# group: Special methods +# -- +def __long__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__lshift__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__lshift__ new file mode 100644 index 0000000..493a21f --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__lshift__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __lshift__ +# key: __lshift__ +# group: Special methods +# -- +def __lshift__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__lt__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__lt__ new file mode 100644 index 0000000..a47a248 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__lt__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __lt__ +# key: __lt__ +# group: Special methods +# -- +def __lt__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__mod__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__mod__ new file mode 100644 index 0000000..94a5ff5 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__mod__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __mod__ +# key: __mod__ +# group: Special methods +# -- +def __mod__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__mul__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__mul__ new file mode 100644 index 0000000..f4a3d7a --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__mul__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __mul__ +# key: __mul__ +# group: Special methods +# -- +def __mul__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ne__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ne__ new file mode 100644 index 0000000..684f967 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ne__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __ne__ +# key: __ne__ +# group: Special methods +# -- +def __ne__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__neg__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__neg__ new file mode 100644 index 0000000..b430dcd --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__neg__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __neg__ +# key: __neg__ +# group: Special methods +# -- +def __neg__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__new__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__new__ new file mode 100644 index 0000000..aac14dc --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__new__ @@ -0,0 +1,10 @@ +# -*- mode: snippet -*- +# name: __new__ +# key: __new__ +# group: Special methods +# -- +def __new__(cls${1:, args}): + """$2 + + """ + ${1:$(elpy-snippet-init-assignments yas-text)} diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__nonzero__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__nonzero__ new file mode 100644 index 0000000..cef110e --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__nonzero__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __nonzero__ +# key: __nonzero__ +# group: Special methods +# -- +def __nonzero__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__oct__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__oct__ new file mode 100644 index 0000000..7a46b56 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__oct__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __oct__ +# key: __oct__ +# group: Special methods +# -- +def __oct__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__or__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__or__ new file mode 100644 index 0000000..6c14ea7 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__or__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __or__ +# key: __or__ +# group: Special methods +# -- +def __or__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__pos__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__pos__ new file mode 100644 index 0000000..ac429b8 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__pos__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __pos__ +# key: __pos__ +# group: Special methods +# -- +def __pos__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__pow__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__pow__ new file mode 100644 index 0000000..c799102 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__pow__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __pow__ +# key: __pow__ +# group: Special methods +# -- +def __pow__(self, other, modulo=None): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__radd__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__radd__ new file mode 100644 index 0000000..761d63d --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__radd__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __radd__ +# key: __radd__ +# group: Special methods +# -- +def __radd__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rand__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rand__ new file mode 100644 index 0000000..be85670 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rand__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rand__ +# key: __rand__ +# group: Special methods +# -- +def __rand__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rdivmod__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rdivmod__ new file mode 100644 index 0000000..2c06a87 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rdivmod__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rdivmod__ +# key: __rdivmod__ +# group: Special methods +# -- +def __rdivmod__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__repr__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__repr__ new file mode 100644 index 0000000..45ae2f9 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__repr__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __repr__ +# key: __repr__ +# group: Special methods +# -- +def __repr__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__reversed__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__reversed__ new file mode 100644 index 0000000..8f29b7b --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__reversed__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __reversed__ +# key: __reversed__ +# group: Special methods +# -- +def __reversed__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rfloordiv__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rfloordiv__ new file mode 100644 index 0000000..317aeef --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rfloordiv__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rfloordiv__ +# key: __rfloordiv__ +# group: Special methods +# -- +def __rfloordiv__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rlshift__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rlshift__ new file mode 100644 index 0000000..5522f85 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rlshift__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rlshift__ +# key: __rlshift__ +# group: Special methods +# -- +def __rlshift__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rmod__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rmod__ new file mode 100644 index 0000000..562c07f --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rmod__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rmod__ +# key: __rmod__ +# group: Special methods +# -- +def __rmod__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rmul__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rmul__ new file mode 100644 index 0000000..aae0b45 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rmul__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rmul__ +# key: __rmul__ +# group: Special methods +# -- +def __rmul__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ror__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ror__ new file mode 100644 index 0000000..0789549 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__ror__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __ror__ +# key: __ror__ +# group: Special methods +# -- +def __ror__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rpow__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rpow__ new file mode 100644 index 0000000..7253b2e --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rpow__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rpow__ +# key: __rpow__ +# group: Special methods +# -- +def __rpow__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rrshift__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rrshift__ new file mode 100644 index 0000000..5d62907 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rrshift__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rrshift__ +# key: __rrshift__ +# group: Special methods +# -- +def __rrshift__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rshift__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rshift__ new file mode 100644 index 0000000..1ec6af2 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rshift__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rshift__ +# key: __rshift__ +# group: Special methods +# -- +def __rshift__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rsub__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rsub__ new file mode 100644 index 0000000..d58d7e8 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rsub__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rsub__ +# key: __rsub__ +# group: Special methods +# -- +def __rsub__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rtruediv__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rtruediv__ new file mode 100644 index 0000000..993d117 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rtruediv__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rtruediv__ +# key: __rtruediv__ +# group: Special methods +# -- +def __rtruediv__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rxor__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rxor__ new file mode 100644 index 0000000..4d1cc09 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__rxor__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __rxor__ +# key: __rxor__ +# group: Special methods +# -- +def __rxor__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__set__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__set__ new file mode 100644 index 0000000..bf87f57 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__set__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __set__ +# key: __set__ +# group: Special methods +# -- +def __set__(self, instance, value): + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__setattr__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__setattr__ new file mode 100644 index 0000000..3482e8e --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__setattr__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __setattr__ +# key: __setattr__ +# group: Special methods +# -- +def __setattr__(self, name, value): + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__setitem__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__setitem__ new file mode 100644 index 0000000..d229fa1 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__setitem__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __setitem__ +# key: __setitem__ +# group: Special methods +# -- +def __setitem__(self, key, value): + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__slots__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__slots__ new file mode 100644 index 0000000..c35d488 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__slots__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __slots__ +# key: __slots__ +# group: Class attributes +# -- +__slots__ = ($1) +$0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__str__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__str__ new file mode 100644 index 0000000..3b39088 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__str__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __str__ +# key: __str__ +# group: Special methods +# -- +def __str__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__sub__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__sub__ new file mode 100644 index 0000000..a183459 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__sub__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __sub__ +# key: __sub__ +# group: Special methods +# -- +def __sub__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__subclasscheck__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__subclasscheck__ new file mode 100644 index 0000000..3918b4c --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__subclasscheck__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __subclasscheck__ +# key: __subclasscheck__ +# group: Special methods +# -- +def __subclasscheck__(self, instance): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__truediv__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__truediv__ new file mode 100644 index 0000000..c76e055 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__truediv__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __truediv__ +# key: __truediv__ +# group: Special methods +# -- +def __truediv__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__unicode__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__unicode__ new file mode 100644 index 0000000..62b82f6 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__unicode__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __unicode__ +# key: __unicode__ +# group: Special methods +# -- +def __unicode__(self): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__xor__ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__xor__ new file mode 100644 index 0000000..3c7e694 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/__xor__ @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __xor__ +# key: __xor__ +# group: Special methods +# -- +def __xor__(self, other): + return $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/ase b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/ase new file mode 100644 index 0000000..82389db --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/ase @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: Assert Equal +# key: ase +# group: Testing +# -- +self.assertEqual(${1:expected}, ${2:actual}) diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/asne b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/asne new file mode 100644 index 0000000..7e766b6 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/asne @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: Assert Not Equal +# key: asne +# group: Testing +# -- +self.assertNotEqual(${1:expected}, ${2:actual}) diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/asr b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/asr new file mode 100644 index 0000000..8aafd05 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/asr @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: Assert Raises +# key: asr +# group: Testing +# -- +with self.assertRaises(${1:Exception}): + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/class b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/class new file mode 100644 index 0000000..b5c180d --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/class @@ -0,0 +1,13 @@ +# -*- mode: snippet -*- +# name: class(parent): ... +# key: class +# group: Definitions +# -- +class ${1:ClassName}(${2:object}): + """${3:Documentation for $1} + + """ + def __init__(self${4:, args}): + super($1, self).__init__($5) + ${4:$(elpy-snippet-init-assignments yas-text)} + $0 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/defs b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/defs new file mode 100644 index 0000000..974ef40 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/defs @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: def method(self, ...): +# key: defs +# group: Definitions +# -- +def ${1:methodname}(self, ${2:arg}): + ${3:pass} diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/enc b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/enc new file mode 100644 index 0000000..54aeebc --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/enc @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: # coding: utf-8 +# key: enc +# group: Header +# -- +# coding: utf-8 diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/env b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/env new file mode 100644 index 0000000..d4fb804 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/env @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: #!/usr/bin/env python +# key: env +# group: Header +# -- +#!/usr/bin/env python diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/from b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/from new file mode 100644 index 0000000..0a706eb --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/from @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: from MOD import SYM +# key: from +# group: Header +# -- +from ${1:module} import ${2:symbol} +$0 \ No newline at end of file diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/pdb b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/pdb new file mode 100644 index 0000000..ed673a3 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/pdb @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: pdb.set_trace() +# key: pdb +# group: Debug +# -- +import pdb; pdb.set_trace() diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/py3 b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/py3 new file mode 100644 index 0000000..ae8eb7b --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/py3 @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: from __future__ import ... +# key: py3 +# group: Python 3 +# -- +from __future__ import division, absolute_import +from __future__ import print_function, unicode_literals diff --git a/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/super b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/super new file mode 100644 index 0000000..3b4e0d9 --- /dev/null +++ b/emacs.d/elpa/elpy-1.7.1/snippets/python-mode/super @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: super() +# key: super +# group: Definitions +# -- +super`(elpy-snippet-super-form)`(${1:`(elpy-snippet-super-arguments)`}) +$0 \ No newline at end of file diff --git a/emacs.d/elpa/erlang-20141104.17/erlang-autoloads.el b/emacs.d/elpa/erlang-20141104.17/erlang-autoloads.el new file mode 100644 index 0000000..17b510c --- /dev/null +++ b/emacs.d/elpa/erlang-20141104.17/erlang-autoloads.el @@ -0,0 +1,144 @@ +;;; erlang-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "erlang" "erlang.el" (21726 22636 0 0)) +;;; Generated autoloads from erlang.el + +(autoload 'erlang-mode "erlang" "\ +Major mode for editing Erlang source files in Emacs. +It knows about syntax and comment, it can indent code, it is capable +of fontifying the source file, the TAGS commands are aware of Erlang +modules, and the Erlang man pages can be accessed. + +Should this module, \"erlang.el\", be installed properly, Erlang mode +is activated whenever an Erlang source or header file is loaded into +Emacs. To indicate this, the mode line should contain the word +\"Erlang\". + +The main feature of Erlang mode is indentation, press TAB and the +current line will be indented correctly. + +Comments starting with only one `%' are indented to the column stored +in the variable `comment-column'. Comments starting with two `%':s +are indented with the same indentation as code. Comments starting +with at least three `%':s are indented to the first column. + +However, Erlang mode contains much more, this is a list of the most +useful commands: + TAB - Indent the line. + C-c C-q - Indent current function. + M-; - Create a comment at the end of the line. + M-q - Fill a comment, i.e. wrap lines so that they (hopefully) + will look better. + M-a - Goto the beginning of an Erlang clause. + M-C-a - Ditto for function. + M-e - Goto the end of an Erlang clause. + M-C-e - Ditto for function. + M-h - Mark current Erlang clause. + M-C-h - Ditto for function. + C-c C-z - Start, or switch to, an inferior Erlang shell. + C-c C-k - Compile current file. + C-x ` - Next error. + , - Electric comma. + ; - Electric semicolon. + +Erlang mode check the name of the file against the module name when +saving, whenever a mismatch occurs Erlang mode offers to modify the +source. + +The variable `erlang-electric-commands' controls the electric +commands. To deactivate all of them, set it to nil. + +There exists a large number of commands and variables in the Erlang +module. Please press `M-x apropos RET erlang RET' to see a complete +list. Press `C-h f name-of-function RET' and `C-h v name-of-variable +RET'to see the full description of functions and variables, +respectively. + +On entry to this mode the contents of the hook `erlang-mode-hook' is +executed. + +Please see the beginning of the file `erlang.el' for more information +and examples of hooks. + +Other commands: +\\{erlang-mode-map} + +\(fn)" t nil) + +(dolist (r '("\\.erl$" "\\.app\\.src$" "\\.escript" "\\.hrl$" "\\.xrl$" "\\.yrl" "/ebin/.+\\.app")) (add-to-list 'auto-mode-alist (cons r 'erlang-mode))) + +(autoload 'erlang-find-tag "erlang" "\ +Like `find-tag'. Capable of retrieving Erlang modules. + +Tags can be given on the forms `tag', `module:', `module:tag'. + +\(fn MODTAGNAME &optional NEXT-P REGEXP-P)" t nil) + +(autoload 'erlang-find-tag-other-window "erlang" "\ +Like `find-tag-other-window' but aware of Erlang modules. + +\(fn TAGNAME &optional NEXT-P REGEXP-P)" t nil) + +(autoload 'erlang-shell "erlang" "\ +Start a new Erlang shell. + +The variable `erlang-shell-function' decides which method to use, +default is to start a new Erlang host. It is possible that, in the +future, a new shell on an already running host will be started. + +\(fn)" t nil) + (autoload 'run-erlang "erlang" "Start a new Erlang shell." t) + +(autoload 'erlang-compile "erlang" "\ +Compile Erlang module in current buffer. + +\(fn)" t nil) + +(autoload 'inferior-erlang "erlang" "\ +Run an inferior Erlang. +With prefix command, prompt for command to start Erlang with. + +This is just like running Erlang in a normal shell, except that +an Emacs buffer is used for input and output. +\\ +The command line history can be accessed with \\[comint-previous-input] and \\[comint-next-input]. +The history is saved between sessions. + +Entry to this mode calls the functions in the variables +`comint-mode-hook' and `erlang-shell-mode-hook' with no arguments. + +The following commands imitate the usual Unix interrupt and +editing control characters: +\\{erlang-shell-mode-map} + +\(fn &optional COMMAND)" t nil) + +;;;*** + +;;;### (autoloads nil "erlang-start" "erlang-start.el" (21726 22636 +;;;;;; 0 0)) +;;; Generated autoloads from erlang-start.el + +(let ((a '("\\.erl\\'" . erlang-mode)) (b '("\\.hrl\\'" . erlang-mode))) (or (assoc (car a) auto-mode-alist) (setq auto-mode-alist (cons a auto-mode-alist))) (or (assoc (car b) auto-mode-alist) (setq auto-mode-alist (cons b auto-mode-alist)))) + +(add-to-list 'interpreter-mode-alist (cons "escript" 'erlang-mode)) + +(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)))) (setq erl-ext (cdr erl-ext)))) + +;;;*** + +;;;### (autoloads nil nil ("erlang-eunit.el" "erlang-flymake.el" +;;;;;; "erlang-pkg.el" "erlang-skels-old.el" "erlang-skels.el" "erlang_appwiz.el") +;;;;;; (21726 22636 81178 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; erlang-autoloads.el ends here diff --git a/emacs.d/elpa/erlang-20141104.17/erlang-eunit.el b/emacs.d/elpa/erlang-20141104.17/erlang-eunit.el new file mode 100644 index 0000000..0adeff1 --- /dev/null +++ b/emacs.d/elpa/erlang-20141104.17/erlang-eunit.el @@ -0,0 +1,452 @@ +;; +;; %CopyrightBegin% +;; +;; Copyright Ericsson AB 2009-2010. All Rights Reserved. +;; +;; The contents of this file are subject to the Erlang Public License, +;; Version 1.1, (the "License"); you may not use this file except in +;; compliance with the License. You should have received a copy of the +;; Erlang Public License along with this software. If not, it can be +;; retrieved online at http://www.erlang.org/. +;; +;; Software distributed under the License is distributed on an "AS IS" +;; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +;; the License for the specific language governing rights and limitations +;; under the License. +;; +;; %CopyrightEnd% +;;; +;;; Purpose: Provide EUnit utilities. +;;; +;;; Author: Klas Johansson + +(eval-when-compile + (require 'cl)) + +(defvar erlang-eunit-src-candidate-dirs '("../src" ".") + "*Name of directories which to search for source files matching +an EUnit test file. The first directory in the list will be used, +if there is no match.") + +(defvar erlang-eunit-test-candidate-dirs '("../test" ".") + "*Name of directories which to search for EUnit test files matching +a source file. The first directory in the list will be used, +if there is no match.") + +(defvar erlang-eunit-autosave nil + "*Set to non-nil to automtically save unsaved buffers before running tests. +This is useful, reducing the save-compile-load-test cycle to one keychord.") + +(defvar erlang-eunit-recent-info '((mode . nil) (module . nil) (test . nil) (cover . nil)) + "Info about the most recent running of an EUnit test representation.") + +(defvar erlang-error-regexp-alist + '(("^\\([^:( \t\n]+\\)[:(][ \t]*\\([0-9]+\\)[:) \t]" . (1 2))) + "*Patterns for matching Erlang errors.") + +;;; +;;; Switch between src/EUnit test buffers +;;; +(defun erlang-eunit-toggle-src-and-test-file-other-window () + "Switch to the src file if the EUnit test file is the current +buffer and vice versa" + (interactive) + (if (erlang-eunit-test-file-p buffer-file-name) + (erlang-eunit-open-src-file-other-window buffer-file-name) + (erlang-eunit-open-test-file-other-window buffer-file-name))) + +;;; +;;; Open the EUnit test file which corresponds to a src file +;;; +(defun erlang-eunit-open-test-file-other-window (src-file-path) + "Open the EUnit test file which corresponds to a src file" + (find-file-other-window (erlang-eunit-test-filename src-file-path))) + +;;; +;;; Open the src file which corresponds to the an EUnit test file +;;; +(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))) + +;;; Return the name and path of the EUnit test file +;;, (input may be either the source filename itself or the EUnit test filename) +(defun erlang-eunit-test-filename (file-path) + (if (erlang-eunit-test-file-p file-path) + file-path + (erlang-eunit-rewrite-filename file-path erlang-eunit-test-candidate-dirs))) + +;;; Return the name and path of the source file +;;, (input may be either the source filename itself or the EUnit test filename) +(defun erlang-eunit-src-filename (file-path) + (if (erlang-eunit-src-file-p file-path) + file-path + (erlang-eunit-rewrite-filename file-path erlang-eunit-src-candidate-dirs))) + +;;; Rewrite a filename from the src or test filename to the other +(defun erlang-eunit-rewrite-filename (orig-file-path candidate-dirs) + (or (erlang-eunit-locate-buddy orig-file-path candidate-dirs) + (erlang-eunit-buddy-file-path orig-file-path (car candidate-dirs)))) + +;;; Search for a file's buddy file (a source file's EUnit test file, +;;; or an EUnit test file's source file) in a list of candidate +;;; directories. +(defun erlang-eunit-locate-buddy (orig-file-path candidate-dirs) + (when candidate-dirs + (let ((buddy-file-path (erlang-eunit-buddy-file-path + orig-file-path + (car candidate-dirs)))) + (if (file-readable-p buddy-file-path) + buddy-file-path + (erlang-eunit-locate-buddy orig-file-path (cdr candidate-dirs)))))) + +(defun erlang-eunit-buddy-file-path (orig-file-path buddy-dir-name) + (let* ((orig-dir-name (file-name-directory orig-file-path)) + (buddy-dir-name (file-truename + (filename-join orig-dir-name buddy-dir-name))) + (buddy-base-name (erlang-eunit-buddy-basename orig-file-path))) + (filename-join buddy-dir-name buddy-base-name))) + +;;; Return the basename of the buddy file: +;;; /tmp/foo/src/x.erl --> x_tests.erl +;;; /tmp/foo/test/x_tests.erl --> x.erl +(defun erlang-eunit-buddy-basename (file-path) + (let ((src-module-name (erlang-eunit-source-module-name file-path))) + (cond + ((erlang-eunit-src-file-p file-path) + (concat src-module-name "_tests.erl")) + ((erlang-eunit-test-file-p file-path) + (concat src-module-name ".erl"))))) + +;;; Checks whether a file is a source file or not +(defun erlang-eunit-src-file-p (file-path) + (not (erlang-eunit-test-file-p file-path))) + +;;; Checks whether a file is a EUnit test file or not +(defun erlang-eunit-test-file-p (file-path) + (erlang-eunit-string-match-p "^\\(.+\\)_tests.erl$" file-path)) + +;;; Return the module name of the source file +;;; /tmp/foo/src/x.erl --> x +;;; /tmp/foo/test/x_tests.erl --> x +(defun erlang-eunit-source-module-name (file-path) + (interactive) + (let ((module-name (erlang-eunit-module-name file-path))) + (if (string-match "^\\(.+\\)_tests$" module-name) + (substring module-name (match-beginning 1) (match-end 1)) + module-name))) + +;;; Return the module name of the file +;;; /tmp/foo/src/x.erl --> x +;;; /tmp/foo/test/x_tests.erl --> x_tests +(defun erlang-eunit-module-name (file-path) + (interactive) + (file-name-sans-extension (file-name-nondirectory file-path))) + +;;; Older emacsen don't have string-match-p. +(defun erlang-eunit-string-match-p (regexp string &optional start) + (if (fboundp 'string-match-p) ;; appeared in emacs 23 + (string-match-p regexp string start) + (save-match-data ;; fallback for earlier versions of emacs + (string-match regexp string start)))) + +;;; Join filenames +(defun filename-join (dir file) + (if (or (= (elt file 0) ?/) + (= (car (last (append dir nil))) ?/)) + (concat dir file) + (concat dir "/" file))) + +;;; Get info about the most recent running of EUnit +(defun erlang-eunit-recent (key) + (cdr (assq key erlang-eunit-recent-info))) + +;;; Record info about the most recent running of EUnit +;;; Known modes are 'module-mode and 'test-mode +(defun erlang-eunit-record-recent (mode module test) + (setcdr (assq 'mode erlang-eunit-recent-info) mode) + (setcdr (assq 'module erlang-eunit-recent-info) module) + (setcdr (assq 'test erlang-eunit-recent-info) test)) + +;;; Record whether the most recent running of EUnit included cover +;;; compilation +(defun erlang-eunit-record-recent-compile (under-cover) + (setcdr (assq 'cover erlang-eunit-recent-info) under-cover)) + +;;; Determine options for EUnit. +(defun erlang-eunit-opts () + (if current-prefix-arg ", [verbose]" "")) + +;;; Determine current test function +(defun erlang-eunit-current-test () + (save-excursion + (erlang-end-of-function 1) + (erlang-beginning-of-function 1) + (erlang-name-of-function))) + +(defun erlang-eunit-simple-test-p (test-name) + (if (erlang-eunit-string-match-p "^\\(.+\\)_test$" test-name) t nil)) + +(defun erlang-eunit-test-generator-p (test-name) + (if (erlang-eunit-string-match-p "^\\(.+\\)_test_$" test-name) t nil)) + +;;; Run one EUnit test +(defun erlang-eunit-run-test (module-name test-name) + (let ((command + (cond ((erlang-eunit-simple-test-p test-name) + (format "eunit:test({%s, %s}%s)." + module-name test-name (erlang-eunit-opts))) + ((erlang-eunit-test-generator-p test-name) + (format "eunit:test({generator, %s, %s}%s)." + module-name test-name (erlang-eunit-opts))) + (t (format "%% WARNING: '%s' is not a test function" test-name))))) + (erlang-eunit-record-recent 'test-mode module-name test-name) + (erlang-eunit-inferior-erlang-send-command command))) + +;;; Run EUnit tests for the current module +(defun erlang-eunit-run-module-tests (module-name) + (let ((command (format "eunit:test(%s%s)." module-name (erlang-eunit-opts)))) + (erlang-eunit-record-recent 'module-mode module-name nil) + (erlang-eunit-inferior-erlang-send-command command))) + +(defun erlang-eunit-compile-and-run-recent () + "Compile the source and test files and repeat the most recent EUnit test run. + +With prefix arg, compiles for debug and runs tests with the verbose flag set." + (interactive) + (case (erlang-eunit-recent 'mode) + ('test-mode + (erlang-eunit-compile-and-test + 'erlang-eunit-run-test (list (erlang-eunit-recent 'module) + (erlang-eunit-recent 'test)))) + ('module-mode + (erlang-eunit-compile-and-test + 'erlang-eunit-run-module-tests (list (erlang-eunit-recent 'module)) + (erlang-eunit-recent 'cover))) + (t (error "EUnit has not yet been run. Please run a test first.")))) + +(defun erlang-eunit-cover-compile () + "Cover compile current module." + (interactive) + (let* ((erlang-compile-extra-opts + (append (list 'debug_info) erlang-compile-extra-opts)) + (module-name + (erlang-add-quotes-if-needed + (erlang-eunit-module-name buffer-file-name))) + (compile-command + (format "cover:compile_beam(%s)." module-name))) + (erlang-compile) + (if (erlang-eunit-last-compilation-successful-p) + (erlang-eunit-inferior-erlang-send-command compile-command)))) + +(defun erlang-eunit-analyze-coverage () + "Analyze the data collected by cover tool for the module in the +current buffer. + +Assumes that the module has been cover compiled prior to this +call. This function will do two things: print the number of +covered and uncovered functions in the erlang shell and display a +new buffer called * coverage* which shows the source +code along with the coverage analysis results." + (interactive) + (let* ((module-name (erlang-add-quotes-if-needed + (erlang-eunit-module-name buffer-file-name))) + (tmp-filename (make-temp-file "cover")) + (analyze-command (format "cover:analyze_to_file(%s, \"%s\"). " + module-name tmp-filename)) + (buf-name (format "*%s coverage*" module-name))) + (erlang-eunit-inferior-erlang-send-command analyze-command) + ;; The purpose of the following snippet is to get the result of the + ;; analysis from a file into a new buffer (or an old, if one with + ;; the specified name already exists). Also we want the erlang-mode + ;; *and* view-mode to be enabled. + (save-excursion + (let ((buf (get-buffer-create (format "*%s coverage*" module-name)))) + (set-buffer buf) + (setq buffer-read-only nil) + (insert-file-contents tmp-filename nil nil nil t) + (if (= (buffer-size) 0) + (kill-buffer buf) + ;; FIXME: this would be a good place to enable (emacs-mode) + ;; to get some nice syntax highlighting in the + ;; coverage report, but it doesn't play well with + ;; flymake. Leave it off for now. + (view-buffer buf)))) + (delete-file tmp-filename))) + +(defun erlang-eunit-compile-and-run-current-test () + "Compile the source and test files and run the current EUnit test. + +With prefix arg, compiles for debug and runs tests with the verbose flag set." + (interactive) + (let ((module-name (erlang-add-quotes-if-needed + (erlang-eunit-module-name buffer-file-name))) + (test-name (erlang-eunit-current-test))) + (erlang-eunit-compile-and-test + 'erlang-eunit-run-test (list module-name test-name)))) + +(defun erlang-eunit-compile-and-run-module-tests () + "Compile the source and test files and run all EUnit tests in the module. + +With prefix arg, compiles for debug and runs tests with the verbose flag set." + (interactive) + (let ((module-name (erlang-add-quotes-if-needed + (erlang-eunit-source-module-name buffer-file-name)))) + (erlang-eunit-compile-and-test + 'erlang-eunit-run-module-tests (list module-name)))) + +;;; Compile source and EUnit test file and finally run EUnit tests for +;;; the current module +(defun erlang-eunit-compile-and-test (test-fun test-args &optional under-cover) + "Compile the source and test files and run the EUnit test suite. + +If under-cover is set to t, the module under test is compile for +code coverage analysis. If under-cover is left out or not set, +coverage analysis is disabled. The result of the code coverage +is both printed to the erlang shell (the number of covered vs +uncovered functions in a module) and written to a buffer called +* coverage* (which shows the source code for the module +and the number of times each line is covered). +With prefix arg, compiles for debug and runs tests with the verbose flag set." + (erlang-eunit-record-recent-compile under-cover) + (let ((src-filename (erlang-eunit-src-filename buffer-file-name)) + (test-filename (erlang-eunit-test-filename buffer-file-name))) + + ;; The purpose of out-maneuvering `save-some-buffers', as is done + ;; below, is to ask the question about saving buffers only once, + ;; instead of possibly several: one for each file to compile, + ;; for instance for both x.erl and x_tests.erl. + (save-some-buffers erlang-eunit-autosave) + (flet ((save-some-buffers (&optional any) nil)) + + ;; Compilation of the source file is mandatory (the file must + ;; exist, otherwise the procedure is aborted). Compilation of the + ;; test file on the other hand, is optional, since eunit tests may + ;; be placed in the source file instead. Any compilation error + ;; will prevent the subsequent steps to be run (hence the `and') + (and (erlang-eunit-compile-file src-filename under-cover) + (if (file-readable-p test-filename) + (erlang-eunit-compile-file test-filename) + t) + (apply test-fun test-args) + (if under-cover + (save-excursion + (set-buffer (find-file-noselect src-filename)) + (erlang-eunit-analyze-coverage))))))) + +(defun erlang-eunit-compile-and-run-module-tests-under-cover () + "Compile the source and test files and run the EUnit test suite and measure +code coverage. + +With prefix arg, compiles for debug and runs tests with the verbose flag set." + (interactive) + (let ((module-name (erlang-add-quotes-if-needed + (erlang-eunit-source-module-name buffer-file-name)))) + (erlang-eunit-compile-and-test + 'erlang-eunit-run-module-tests (list module-name) t))) + +(defun erlang-eunit-compile-file (file-path &optional under-cover) + (if (file-readable-p file-path) + (save-excursion + (set-buffer (find-file-noselect file-path)) + ;; In order to run a code coverage analysis on a + ;; module, we have two options: + ;; + ;; * either compile the module with cover:compile instead of the + ;; regular compiler + ;; + ;; * or first compile the module with the regular compiler (but + ;; *with* debug_info) and then compile it for coverage + ;; analysis using cover:compile_beam. + ;; + ;; We could accomplish the first by changing the + ;; erlang-compile-erlang-function to cover:compile, but there's + ;; a risk that that's used for other purposes. Therefore, a + ;; safer alternative (although with more steps) is to add + ;; debug_info to the list of compiler options and go for the + ;; second alternative. + (if under-cover + (erlang-eunit-cover-compile) + (erlang-compile)) + (erlang-eunit-last-compilation-successful-p)) + (let ((msg (format "Could not read %s" file-path))) + (erlang-eunit-inferior-erlang-send-command + (format "%% WARNING: %s" msg)) + (error msg)))) + +(defun erlang-eunit-last-compilation-successful-p () + (save-excursion + (set-buffer inferior-erlang-buffer) + (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)) + (mapcar (lambda (e) (car e)) erlang-error-regexp-alist)))) + +(defun erlang-eunit-is-compilation-warning () + (erlang-eunit-string-match-p + "[0-9]+: Warning:" + (buffer-substring (line-beginning-position) (line-end-position)))) + +(defun erlang-eunit-all-list-elems-fulfill-p (pred list) + (let ((matches-p t)) + (while (and list matches-p) + (if (not (funcall pred (car list))) + (setq matches-p nil)) + (setq list (cdr list))) + matches-p)) + +;;; Evaluate a command in an erlang buffer +(defun erlang-eunit-inferior-erlang-send-command (command) + "Evaluate a command in an erlang buffer." + (interactive "P") + (inferior-erlang-prepare-for-input) + (inferior-erlang-send-command command) + (sit-for 0) ;; redisplay + (inferior-erlang-wait-prompt)) + + +;;;==================================================================== +;;; Key bindings +;;;==================================================================== + +(defconst erlang-eunit-key-bindings + '(("\C-c\C-et" erlang-eunit-toggle-src-and-test-file-other-window) + ("\C-c\C-ek" erlang-eunit-compile-and-run-module-tests) + ("\C-c\C-ej" erlang-eunit-compile-and-run-current-test) + ("\C-c\C-el" erlang-eunit-compile-and-run-recent) + ("\C-c\C-ec" erlang-eunit-compile-and-run-module-tests-under-cover) + ("\C-c\C-ev" erlang-eunit-cover-compile) + ("\C-c\C-ea" erlang-eunit-analyze-coverage))) + +(defun erlang-eunit-add-key-bindings () + (dolist (binding erlang-eunit-key-bindings) + (erlang-eunit-bind-key (car binding) (cadr binding)))) + +(defun erlang-eunit-bind-key (key function) + (erlang-eunit-ensure-keymap-for-key key) + (local-set-key key function)) + +(defun erlang-eunit-ensure-keymap-for-key (key-seq) + (let ((prefix-keys (butlast (append key-seq nil))) + (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)))))) + +(add-hook 'erlang-mode-hook 'erlang-eunit-add-key-bindings) + + +(provide 'erlang-eunit) +;; erlang-eunit ends here diff --git a/emacs.d/elpa/erlang-20141104.17/erlang-flymake.el b/emacs.d/elpa/erlang-20141104.17/erlang-flymake.el new file mode 100644 index 0000000..2e447b5 --- /dev/null +++ b/emacs.d/elpa/erlang-20141104.17/erlang-flymake.el @@ -0,0 +1,103 @@ +;; erlang-flymake.el +;; +;; Syntax check erlang source code on the fly (integrates with flymake). +;; +;; Start using flymake with erlang by putting the following somewhere +;; in your .emacs file: +;; +;; (require 'erlang-flymake) +;; +;; Flymake is rather eager and does its syntax checks frequently by +;; default and if you are bothered by this, you might want to put the +;; following in your .emacs as well: +;; +;; (erlang-flymake-only-on-save) +;; +;; There are a couple of variables which control the compilation options: +;; * erlang-flymake-get-code-path-dirs-function +;; * erlang-flymake-get-include-dirs-function +;; * erlang-flymake-extra-opts +;; +;; This code is inspired by http://www.emacswiki.org/emacs/FlymakeErlang. + +(require 'flymake) +(eval-when-compile + (require 'cl)) + +(defvar erlang-flymake-command + "erlc" + "The command that will be used to perform the syntax check") + +(defvar erlang-flymake-get-code-path-dirs-function + 'erlang-flymake-get-code-path-dirs + "Return a list of ebin directories to add to the code path.") + +(defvar erlang-flymake-get-include-dirs-function + 'erlang-flymake-get-include-dirs + "Return a list of include directories to add to the compiler options.") + +(defvar erlang-flymake-extra-opts + (list "+warn_obsolete_guard" + "+warn_unused_import" + "+warn_shadow_vars" + "+warn_export_vars" + "+strong_validation" + "+report") + "A list of options that will be passed to the compiler") + +(defun erlang-flymake-only-on-save () + "Trigger flymake only when the buffer is saved (disables syntax +check on newline and when there are no changes)." + (interactive) + ;; There doesn't seem to be a way of disabling this; set to the + ;; largest int available as a workaround (most-positive-fixnum + ;; equates to 8.5 years on my machine, so it ought to be enough ;-) ) + (setq flymake-no-changes-timeout most-positive-fixnum) + (setq flymake-start-syntax-check-on-newline nil)) + + +(defun erlang-flymake-get-code-path-dirs () + (list (concat (erlang-flymake-get-app-dir) "ebin"))) + +(defun erlang-flymake-get-include-dirs () + (list (concat (erlang-flymake-get-app-dir) "include") + (concat (erlang-flymake-get-app-dir) "deps"))) + +(defun erlang-flymake-get-app-dir () + (let ((src-path (file-name-directory (buffer-file-name)))) + (file-name-directory (directory-file-name src-path)))) + +(defun erlang-flymake-init () + (let* ((temp-file + (flet ((flymake-get-temp-dir () (erlang-flymake-temp-dir))) + (flymake-init-create-temp-buffer-copy + 'flymake-create-temp-with-folder-structure))) + (code-dir-opts + (erlang-flymake-flatten + (mapcar (lambda (dir) (list "-pa" dir)) + (funcall erlang-flymake-get-code-path-dirs-function)))) + (inc-dir-opts + (erlang-flymake-flatten + (mapcar (lambda (dir) (list "-I" dir)) + (funcall erlang-flymake-get-include-dirs-function)))) + (compile-opts + (append inc-dir-opts + code-dir-opts + erlang-flymake-extra-opts))) + (list erlang-flymake-command (append compile-opts (list temp-file))))) + +(defun erlang-flymake-temp-dir () + ;; Squeeze the user's name in there in order to make sure that files + ;; for two users who are working on the same computer (like a linux + ;; box) don't collide + (format "%s/flymake-%s" temporary-file-directory user-login-name)) + +(defun erlang-flymake-flatten (list) + (apply #'append list)) + +(add-to-list 'flymake-allowed-file-name-masks + '("\\.erl\\'" erlang-flymake-init)) +(add-hook 'erlang-mode-hook 'flymake-mode) + +(provide 'erlang-flymake) +;; erlang-flymake ends here diff --git a/emacs.d/elpa/erlang-20141104.17/erlang-pkg.el b/emacs.d/elpa/erlang-20141104.17/erlang-pkg.el new file mode 100644 index 0000000..d0248c9 --- /dev/null +++ b/emacs.d/elpa/erlang-20141104.17/erlang-pkg.el @@ -0,0 +1,4 @@ +(define-package "erlang" "20141104.17" "Erlang major mode" 'nil) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs.d/elpa/erlang-20141104.17/erlang-skels-old.el b/emacs.d/elpa/erlang-20141104.17/erlang-skels-old.el new file mode 100644 index 0000000..b88d7bc --- /dev/null +++ b/emacs.d/elpa/erlang-20141104.17/erlang-skels-old.el @@ -0,0 +1,1267 @@ +;; +;; %CopyrightBegin% +;; +;; Copyright Ericsson AB 2010. All Rights Reserved. +;; +;; The contents of this file are subject to the Erlang Public License, +;; Version 1.1, (the "License"); you may not use this file except in +;; compliance with the License. You should have received a copy of the +;; Erlang Public License along with this software. If not, it can be +;; retrieved online at http://www.erlang.org/. +;; +;; Software distributed under the License is distributed on an "AS IS" +;; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +;; the License for the specific language governing rights and limitations +;; under the License. +;; +;; %CopyrightEnd% +;;; +;;; Purpose: Provide Erlang code skeletons. +;;; See 'erlang-skel-file' variable. + +(defvar erlang-tempo-tags nil + "Tempo tags for erlang mode") + +(defvar erlang-skel + '(("If" "if" erlang-skel-if) + ("Case" "case" erlang-skel-case) + ("Receive" "receive" erlang-skel-receive) + ("Receive After" "after" erlang-skel-receive-after) + ("Receive Loop" "loop" erlang-skel-receive-loop) + ("Module" "module" erlang-skel-module) + ("Author" "author" erlang-skel-author) + () + ("Small Header" "small-header" + erlang-skel-small-header erlang-skel-header) + ("Normal Header" "normal-header" + erlang-skel-normal-header erlang-skel-header) + ("Large Header" "large-header" + erlang-skel-large-header erlang-skel-header) + () + ("Small Server" "small-server" + erlang-skel-small-server erlang-skel-header) + () + ("Application" "application" + erlang-skel-application erlang-skel-header) + ("Supervisor" "supervisor" + erlang-skel-supervisor erlang-skel-header) + ("supervisor_bridge" "supervisor-bridge" + erlang-skel-supervisor-bridge erlang-skel-header) + ("gen_server" "generic-server" + erlang-skel-generic-server erlang-skel-header) + ("gen_event" "gen-event" + erlang-skel-gen-event erlang-skel-header) + ("gen_fsm" "gen-fsm" + erlang-skel-gen-fsm erlang-skel-header) + ("Library module" "gen-lib" + erlang-skel-lib erlang-skel-header) + ("Corba callback" "gen-corba-cb" + erlang-skel-corba-callback erlang-skel-header) + ("Small Common Test suite" "ct-test-suite-s" + erlang-skel-ct-test-suite-s erlang-skel-header) + ("Large Common Test suite" "ct-test-suite-l" + erlang-skel-ct-test-suite-l erlang-skel-header) + ("Erlang TS test suite" "ts-test-suite" + erlang-skel-ts-test-suite erlang-skel-header) + ) + "*Description of all skeleton templates. +Both functions and menu entries will be created. + +Each entry in `erlang-skel' should be a list with three or four +elements, or the empty list. + +The first element is the name which shows up in the menu. The second +is the `tempo' identifier (The string \"erlang-\" will be added in +front of it). The third is the skeleton descriptor, a variable +containing `tempo' attributes as described in the function +`tempo-define-template'. The optional fourth elements denotes a +function which should be called when the menu is selected. + +Functions corresponding to every template will be created. The name +of the function will be `tempo-template-erlang-X' where `X' is the +tempo identifier as specified in the second argument of the elements +in this list. + +A list with zero elements means that the a horizontal line should +be placed in the menu.") + +;; In XEmacs `user-mail-address' returns "x@y.z (Foo Bar)" ARGH! +;; What's wrong with that? RFC 822 says it's legal. [sverkerw] +;; This needs to use the customized value. If that's not sane, things like +;; add-log will lose anyhow. Avoid it if there _is_ a paren. +(defvar erlang-skel-mail-address + (if (or (not user-mail-address) (string-match "(" user-mail-address)) + (concat (user-login-name) "@" + (or (and (boundp 'mail-host-address) + mail-host-address) + (system-name))) + user-mail-address) + "Mail address of the user.") + +;; Expression templates: +(defvar erlang-skel-case + '((erlang-skel-skip-blank) o > + "case " p " of" n> p "_ ->" n> p "ok" n> "end" p) + "*The skeleton of a `case' expression. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-if + '((erlang-skel-skip-blank) o > + "if" n> p " ->" n> p "ok" n> "end" p) + "The skeleton of an `if' expression. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-receive + '((erlang-skel-skip-blank) o > + "receive" n> p "_ ->" n> p "ok" n> "end" p) + "*The skeleton of a `receive' expression. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-receive-after + '((erlang-skel-skip-blank) o > + "receive" n> p "_ ->" n> p "ok" n> "after " p "T ->" n> + p "ok" n> "end" p) + "*The skeleton of a `receive' expression with an `after' clause. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-receive-loop + '(& o "loop(" p ") ->" n> "receive" n> p "_ ->" n> + "loop(" p ")" n> "end.") + "*The skeleton of a simple `receive' loop. +Please see the function `tempo-define-template'.") + + +;; Attribute templates + +(defvar erlang-skel-module + '(& "-module(" + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) + ")." n) + "*The skeleton of a `module' attribute. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-author + '(& "-author('" erlang-skel-mail-address "')." n) + "*The skeleton of a `author' attribute. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-vc nil + "*The skeleton template to generate a version control attribute. +The default is to insert nothing. Example of usage: + + (setq erlang-skel-vc '(& \"-rcs(\\\"$\Id: $ \\\").\") n) + +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-export + '(& "-export([" n> "])." n) + "*The skeleton of an `export' attribute. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-import + '(& "%%-import(Module, [Function/Arity, ...])." n) + "*The skeleton of an `import' attribute. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-compile nil + ;; '(& "%%-compile(export_all)." n) + "*The skeleton of a `compile' attribute. +Please see the function `tempo-define-template'.") + + +;; Comment templates. + +(defvar erlang-skel-date-function 'erlang-skel-dd-mmm-yyyy + "*Function which returns date string. +Look in the module `time-stamp' for a battery of functions.") + +(defvar erlang-skel-copyright-comment '() + "*The template for a copyright line in the header, normally empty. +This variable should be bound to a `tempo' template, for example: + '(& \"%%% Copyright (C) 2000, Yoyodyne, Inc.\" n) + +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-created-comment + '(& "%%% Created : " (funcall erlang-skel-date-function) " by " + (user-full-name) " <" erlang-skel-mail-address ">" n) + "*The template for the \"Created:\" comment line.") + +(defvar erlang-skel-author-comment + '(& "%%% Author : " (user-full-name) " <" erlang-skel-mail-address ">" n) + "*The template for creating the \"Author:\" line in the header. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-file-comment + '(& "%%% File : " (file-name-nondirectory buffer-file-name) n) +"*The template for creating the \"Module:\" line in the header. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-small-header + '(o (erlang-skel-include erlang-skel-module) + ;; erlang-skel-author) + n + (erlang-skel-include erlang-skel-compile + ;; erlang-skel-export + erlang-skel-vc)) + "*The template of a small header without any comments. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-normal-header + '(o (erlang-skel-include erlang-skel-copyright-comment + erlang-skel-file-comment + erlang-skel-author-comment) + "%%% Description : " p n + (erlang-skel-include erlang-skel-created-comment) n + (erlang-skel-include erlang-skel-small-header) n) + "*The template of a normal header. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-large-header + '(o (erlang-skel-separator) + (erlang-skel-include erlang-skel-copyright-comment + erlang-skel-file-comment + erlang-skel-author-comment) + "%%% Description : " p n + "%%%" n + (erlang-skel-include erlang-skel-created-comment) + (erlang-skel-separator) + (erlang-skel-include erlang-skel-small-header) ) + "*The template of a large header. +Please see the function `tempo-define-template'.") + + +;; Server templates. + +(defvar erlang-skel-small-server + '((erlang-skel-include erlang-skel-large-header) + "-export([start/0,init/1])." n n n + "start() ->" n> "spawn(" (erlang-get-module-from-file-name) + ", init, [self()])." n n + "init(From) ->" n> + "loop(From)." n n + "loop(From) ->" n> + "receive" n> + p "_ ->" n> + "loop(From)" n> + "end." + ) + "*Template of a small server. +Please see the function `tempo-define-template'.") + +;; Behaviour templates. + +(defvar erlang-skel-application + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(application)." n n + "%% Application callbacks" n + "-export([start/2, stop/1])." n n + (erlang-skel-double-separator 2) + "%% Application callbacks" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Function: start(Type, StartArgs) -> {ok, Pid} |" n + "%% {ok, Pid, State} |" n + "%% {error, Reason}" n + "%% Description: This function is called whenever an application " n + "%% is started using application:start/1,2, and should start the processes" n + "%% of the application. If the application is structured according to the" n + "%% OTP design principles as a supervision tree, this means starting the" n + "%% top supervisor of the tree." n + (erlang-skel-separator 2) + "start(_Type, StartArgs) ->" n> + "case 'TopSupervisor':start_link(StartArgs) of" n> + "{ok, Pid} -> " n> + "{ok, Pid};" n> + "Error ->" n> + "Error" n> + "end." n + n + (erlang-skel-separator 2) + "%% Function: stop(State) -> void()" n + "%% Description: This function is called whenever an application" n + "%% has stopped. It is intended to be the opposite of Module:start/2 and" n + "%% should do any necessary cleaning up. The return value is ignored. "n + (erlang-skel-separator 2) + "stop(_State) ->" n> + "ok." n + n + (erlang-skel-double-separator 2) + "%% Internal functions" n + (erlang-skel-double-separator 2) + ) + "*The template of an application behaviour. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-supervisor + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(supervisor)." n n + + "%% API" n + "-export([start_link/0])." n n + + "%% Supervisor callbacks" n + "-export([init/1])." n n + + "-define(SERVER, ?MODULE)." n n + + (erlang-skel-double-separator 2) + "%% API functions" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}" n + "%% Description: Starts the supervisor" n + (erlang-skel-separator 2) + "start_link() ->" n> + "supervisor:start_link({local, ?SERVER}, ?MODULE, [])." n + n + (erlang-skel-double-separator 2) + "%% Supervisor callbacks" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Func: init(Args) -> {ok, {SupFlags, [ChildSpec]}} |" n + "%% ignore |" n + "%% {error, Reason}" n + "%% Description: Whenever a supervisor is started using "n + "%% supervisor:start_link/[2,3], this function is called by the new process "n + "%% to find out about restart strategy, maximum restart frequency and child "n + "%% specifications." n + (erlang-skel-separator 2) + "init([]) ->" n> + "AChild = {'AName',{'AModule',start_link,[]}," n> + "permanent,2000,worker,['AModule']}," n> + "{ok,{{one_for_all,0,1}, [AChild]}}." n + n + (erlang-skel-double-separator 2) + "%% Internal functions" n + (erlang-skel-double-separator 2) + ) + "*The template of an supervisor behaviour. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-supervisor-bridge + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(supervisor_bridge)." n n + + "%% API" n + "-export([start_link/0])." n n + + "%% supervisor_bridge callbacks" n + "-export([init/1, terminate/2])." n n + + "-define(SERVER, ?MODULE)." n n + + "-record(state, {})." n n + + (erlang-skel-double-separator 2) + "%% API" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}" n + "%% Description: Starts the supervisor bridge" n + (erlang-skel-separator 2) + "start_link() ->" n> + "supervisor_bridge:start_link({local, ?SERVER}, ?MODULE, [])." n + n + (erlang-skel-double-separator 2) + "%% supervisor_bridge callbacks" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Funcion: init(Args) -> {ok, Pid, State} |" n + "%% ignore |" n + "%% {error, Reason} " n + "%% Description:Creates a supervisor_bridge process, linked to the calling" n + "%% process, which calls Module:init/1 to start the subsystem. To ensure a" n + "%% synchronized start-up procedure, this function does not return until" n + "%% Module:init/1 has returned. " n + (erlang-skel-separator 2) + "init([]) ->" n> + "case 'AModule':start_link() of" n> + "{ok, Pid} ->" n> + "{ok, Pid, #state{}};" n> + "Error ->" n> + "Error" n> + "end." n + n + (erlang-skel-separator 2) + "%% Func: terminate(Reason, State) -> void()" n + "%% Description:This function is called by the supervisor_bridge when it is"n + "%% about to terminate. It should be the opposite of Module:init/1 and stop"n + "%% the subsystem and do any necessary cleaning up.The return value is ignored." + (erlang-skel-separator 2) + "terminate(Reason, State) ->" n> + "'AModule':stop()," n> + "ok." n + n + (erlang-skel-double-separator 2) + "%% Internal functions" n + (erlang-skel-double-separator 2) + ) + "*The template of an supervisor_bridge behaviour. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-generic-server + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(gen_server)." n n + + "%% API" n + "-export([start_link/0])." n n + + "%% gen_server callbacks" n + "-export([init/1, handle_call/3, handle_cast/2, " + "handle_info/2," n> + "terminate/2, code_change/3])." n n + + "-record(state, {})." n n + + (erlang-skel-double-separator 2) + "%% API" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}" n + "%% Description: Starts the server" n + (erlang-skel-separator 2) + "start_link() ->" n> + "gen_server:start_link({local, ?SERVER}, ?MODULE, [], [])." n + n + (erlang-skel-double-separator 2) + "%% gen_server callbacks" n + (erlang-skel-double-separator 2) + n + (erlang-skel-separator 2) + "%% Function: init(Args) -> {ok, State} |" n + "%% {ok, State, Timeout} |" n + "%% ignore |" n + "%% {stop, Reason}" n + "%% Description: Initializes the server" n + (erlang-skel-separator 2) + "init([]) ->" n> + "{ok, #state{}}." n + n + (erlang-skel-separator 2) + "%% Function: " + "%% handle_call(Request, From, State) -> {reply, Reply, State} |" n + "%% {reply, Reply, State, Timeout} |" n + "%% {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, Reply, State} |" n + "%% {stop, Reason, State}" n + "%% Description: Handling call messages" n + (erlang-skel-separator 2) + "handle_call(_Request, _From, State) ->" n> + "Reply = ok," n> + "{reply, Reply, State}." n + n + (erlang-skel-separator 2) + "%% Function: handle_cast(Msg, State) -> {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, State}" n + "%% Description: Handling cast messages" n + + (erlang-skel-separator 2) + "handle_cast(_Msg, State) ->" n> + "{noreply, State}." n + n + (erlang-skel-separator 2) + "%% Function: handle_info(Info, State) -> {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, State}" n + "%% Description: Handling all non call/cast messages" n + (erlang-skel-separator 2) + "handle_info(_Info, State) ->" n> + "{noreply, State}." n + n + (erlang-skel-separator 2) + "%% Function: terminate(Reason, State) -> void()" n + "%% Description: This function is called by a gen_server when it is about to"n + "%% terminate. It should be the opposite of Module:init/1 and do any necessary"n + "%% cleaning up. When it returns, the gen_server terminates with Reason." n + "%% The return value is ignored." n + + (erlang-skel-separator 2) + "terminate(_Reason, _State) ->" n> + "ok." n + n + (erlang-skel-separator 2) + "%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}" n + "%% Description: Convert process state when code is changed" n + (erlang-skel-separator 2) + "code_change(_OldVsn, State, _Extra) ->" n> + "{ok, State}." n + n + (erlang-skel-separator 2) + "%%% Internal functions" n + (erlang-skel-separator 2) + ) + "*The template of a generic server. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-gen-event + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(gen_event)." n + + "%% API" n + "-export([start_link/0, add_handler/0])." n n + + "%% gen_event callbacks" n + "-export([init/1, handle_event/2, handle_call/2, " n> + "handle_info/2, terminate/2, code_change/3])." n n + + "-record(state, {})." n n + + (erlang-skel-double-separator 2) + "%% gen_event callbacks" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Function: start_link() -> {ok,Pid} | {error,Error} " n + "%% Description: Creates an event manager." n + (erlang-skel-separator 2) + "start_link() ->" n> + "gen_event:start_link({local, ?SERVER}). " n + n + (erlang-skel-separator 2) + "%% Function: add_handler() -> ok | {'EXIT',Reason} | term()" n + "%% Description: Adds an event handler" n + (erlang-skel-separator 2) + "add_handler() ->" n> + "gen_event:add_handler(?SERVER, ?MODULE, [])." n + n + (erlang-skel-double-separator 2) + "%% gen_event callbacks" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Function: init(Args) -> {ok, State}" n + "%% Description: Whenever a new event handler is added to an event manager,"n + "%% this function is called to initialize the event handler." n + (erlang-skel-separator 2) + "init([]) ->" n> + "{ok, #state{}}." n + n + (erlang-skel-separator 2) + "%% Function: "n + "%% handle_event(Event, State) -> {ok, State} |" n + "%% {swap_handler, Args1, State1, Mod2, Args2} |"n + "%% remove_handler" n + "%% Description:Whenever an event manager receives an event sent using"n + "%% gen_event:notify/2 or gen_event:sync_notify/2, this function is called for"n + "%% each installed event handler to handle the event. "n + (erlang-skel-separator 2) + "handle_event(_Event, State) ->" n> + "{ok, State}." n + n + (erlang-skel-separator 2) + "%% Function: " n + "%% handle_call(Request, State) -> {ok, Reply, State} |" n + "%% {swap_handler, Reply, Args1, State1, "n + "%% Mod2, Args2} |" n + "%% {remove_handler, Reply}" n + "%% Description: Whenever an event manager receives a request sent using"n + "%% gen_event:call/3,4, this function is called for the specified event "n + "%% handler to handle the request."n + (erlang-skel-separator 2) + "handle_call(_Request, State) ->" n> + "Reply = ok," n> + "{ok, Reply, State}." n + n + (erlang-skel-separator 2) + "%% Function: " n + "%% handle_info(Info, State) -> {ok, State} |" n + "%% {swap_handler, Args1, State1, Mod2, Args2} |" n + "%% remove_handler" n + "%% Description: This function is called for each installed event handler when"n + "%% an event manager receives any other message than an event or a synchronous"n + "%% request (or a system message)."n + (erlang-skel-separator 2) + "handle_info(_Info, State) ->" n> + "{ok, State}." n + n + (erlang-skel-separator 2) + "%% Function: terminate(Reason, State) -> void()" n + "%% Description:Whenever an event handler is deleted from an event manager,"n + "%% this function is called. It should be the opposite of Module:init/1 and "n + "%% do any necessary cleaning up. " n + (erlang-skel-separator 2) + "terminate(_Reason, _State) ->" n> + "ok." n + n + (erlang-skel-separator 2) + "%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState} " n + "%% Description: Convert process state when code is changed" n + (erlang-skel-separator 2) + "code_change(_OldVsn, State, _Extra) ->" n> + "{ok, State}." n + n + (erlang-skel-separator 2) + "%%% Internal functions" n + (erlang-skel-separator 2) + ) + "*The template of a gen_event. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-gen-fsm + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(gen_fsm)." n n + + "%% API" n + "-export([start_link/0])." n n + + "%% gen_fsm callbacks" n + "-export([init/1, state_name/2, state_name/3, handle_event/3," n> + "handle_sync_event/4, handle_info/3, terminate/3, code_change/4])." n n + + "-record(state, {})." n n + + (erlang-skel-double-separator 2) + "%% API" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Function: start_link() -> ok,Pid} | ignore | {error,Error}" n + "%% Description:Creates a gen_fsm process which calls Module:init/1 to"n + "%% initialize. To ensure a synchronized start-up procedure, this function" n + "%% does not return until Module:init/1 has returned. " n + (erlang-skel-separator 2) + "start_link() ->" n> + "gen_fsm:start_link({local, ?SERVER}, ?MODULE, [], [])." n + n + (erlang-skel-double-separator 2) + "%% gen_fsm callbacks" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Function: init(Args) -> {ok, StateName, State} |" n + "%% {ok, StateName, State, Timeout} |" n + "%% ignore |" n + "%% {stop, StopReason} " n + "%% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or"n + "%% gen_fsm:start_link/3,4, this function is called by the new process to "n + "%% initialize. " n + (erlang-skel-separator 2) + "init([]) ->" n> + "{ok, state_name, #state{}}." n + n + (erlang-skel-separator 2) + "%% Function: "n + "%% state_name(Event, State) -> {next_state, NextStateName, NextState}|" n + "%% {next_state, NextStateName, " n + "%% NextState, Timeout} |" n + "%% {stop, Reason, NewState}" n + "%% Description:There should be one instance of this function for each possible"n + "%% state name. Whenever a gen_fsm receives an event sent using" n + "%% gen_fsm:send_event/2, the instance of this function with the same name as"n + "%% the current state name StateName is called to handle the event. It is also "n + "%% called if a timeout occurs. " n + (erlang-skel-separator 2) + "state_name(_Event, State) ->" n> + "{next_state, state_name, State}." n + n + (erlang-skel-separator 2) + "%% Function:" n + "%% state_name(Event, From, State) -> {next_state, NextStateName, NextState} |"n + "%% {next_state, NextStateName, " n + "%% NextState, Timeout} |" n + "%% {reply, Reply, NextStateName, NextState}|"n + "%% {reply, Reply, NextStateName, " n + "%% NextState, Timeout} |" n + "%% {stop, Reason, NewState}|" n + "%% {stop, Reason, Reply, NewState}" n + "%% Description: There should be one instance of this function for each" n + "%% possible state name. Whenever a gen_fsm receives an event sent using" n + "%% gen_fsm:sync_send_event/2,3, the instance of this function with the same"n + "%% name as the current state name StateName is called to handle the event." n + (erlang-skel-separator 2) + "state_name(_Event, _From, State) ->" n> + "Reply = ok," n> + "{reply, Reply, state_name, State}." n + n + (erlang-skel-separator 2) + "%% Function: " n + "%% handle_event(Event, StateName, State) -> {next_state, NextStateName, "n + "%% NextState} |" n + "%% {next_state, NextStateName, "n + "%% NextState, Timeout} |" n + "%% {stop, Reason, NewState}" n + "%% Description: Whenever a gen_fsm receives an event sent using"n + "%% gen_fsm:send_all_state_event/2, this function is called to handle"n + "%% the event." n + (erlang-skel-separator 2) + "handle_event(_Event, StateName, State) ->" n> + "{next_state, StateName, State}." n + n + (erlang-skel-separator 2) + "%% Function: " n + "%% handle_sync_event(Event, From, StateName, "n + "%% State) -> {next_state, NextStateName, NextState} |" n + "%% {next_state, NextStateName, NextState, " n + "%% Timeout} |" n + "%% {reply, Reply, NextStateName, NextState}|" n + "%% {reply, Reply, NextStateName, NextState, " n + "%% Timeout} |" n + "%% {stop, Reason, NewState} |" n + "%% {stop, Reason, Reply, NewState}" n + "%% Description: Whenever a gen_fsm receives an event sent using"n + "%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle"n + "%% the event."n + (erlang-skel-separator 2) + "handle_sync_event(Event, From, StateName, State) ->" n> + "Reply = ok," n> + "{reply, Reply, StateName, State}." n + n + (erlang-skel-separator 2) + "%% Function: " n + "%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}|" n + "%% {next_state, NextStateName, NextState, "n + "%% Timeout} |" n + "%% {stop, Reason, NewState}" n + "%% Description: This function is called by a gen_fsm when it receives any"n + "%% other message than a synchronous or asynchronous event"n + "%% (or a system message)." n + (erlang-skel-separator 2) + "handle_info(_Info, StateName, State) ->" n> + "{next_state, StateName, State}." n + n + (erlang-skel-separator 2) + "%% Function: terminate(Reason, StateName, State) -> void()" n + "%% Description:This function is called by a gen_fsm when it is about"n + "%% to terminate. It should be the opposite of Module:init/1 and do any"n + "%% necessary cleaning up. When it returns, the gen_fsm terminates with"n + "%% Reason. The return value is ignored." n + (erlang-skel-separator 2) + "terminate(_Reason, _StateName, _State) ->" n> + "ok." n + n + (erlang-skel-separator 2) + "%% Function:" n + "%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}" n + "%% Description: Convert process state when code is changed" n + (erlang-skel-separator 2) + "code_change(_OldVsn, StateName, State, _Extra) ->" n> + "{ok, StateName, State}." n + n + (erlang-skel-separator 2) + "%%% Internal functions" n + (erlang-skel-separator 2) + ) + "*The template of a gen_fsm. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-lib + '((erlang-skel-include erlang-skel-large-header) + + "%% API" n + "-export([])." n n + + (erlang-skel-double-separator 2) + "%% API" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Function: " n + "%% Description:" n + (erlang-skel-separator 2) + n + (erlang-skel-double-separator 2) + "%% Internal functions" n + (erlang-skel-double-separator 2) + ) + "*The template of a library module. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-corba-callback + '((erlang-skel-include erlang-skel-large-header) + "%% Include files" n n + + "%% API" n + "-export([])." n n + + "%% Corba callbacks" n + "-export([init/1, terminate/2, code_change/3])." n n + + "-record(state, {})." n n + + (erlang-skel-double-separator 2) + "%% Corba callbacks" n + (erlang-skel-double-separator 2) + (erlang-skel-separator 2) + "%% Function: init(Args) -> {ok, State} |" n + "%% {ok, State, Timeout} |" n + "%% ignore |" n + "%% {stop, Reason}" n + "%% Description: Initializes the server" n + (erlang-skel-separator 2) + "init([]) ->" n> + "{ok, #state{}}." n + n + (erlang-skel-separator 2) + "%% Function: terminate(Reason, State) -> void()" n + "%% Description: Shutdown the server" n + (erlang-skel-separator 2) + "terminate(_Reason, _State) ->" n> + "ok." n + n + (erlang-skel-separator 2) + "%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState} " n + "%% Description: Convert process state when code is changed" n + (erlang-skel-separator 2) + "code_change(_OldVsn, State, _Extra) ->" n> + "{ok, State}." n + n + (erlang-skel-double-separator 2) + "%% Internal functions" n + (erlang-skel-double-separator 2) + ) + "*The template of a library module. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-ts-test-suite + '((erlang-skel-include erlang-skel-large-header) + "%% Note: This directive should only be used in test suites." n + "-compile(export_all)." n n + + "-include_lib(\"test_server/include/test_server.hrl\")." n n + + (erlang-skel-separator 2) + "%% TEST SERVER CALLBACK FUNCTIONS" n + (erlang-skel-separator 2) + n + (erlang-skel-separator 2) + "%% Function: init_per_suite(Config0) -> Config1 | {skip,Reason}" n + "%%" n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Reason = term()" n + "%% The reason for skipping the suite." n + "%%" n + "%% Description: Initialization before the suite." n + "%%" n + "%% Note: This function is free to add any key/value pairs to the Config" n + "%% variable, but should NOT alter/remove any existing entries." n + (erlang-skel-separator 2) + "init_per_suite(Config) ->" n > + "Config." n n + + (erlang-skel-separator 2) + "%% Function: end_per_suite(Config) -> void()" n + "%%" n + "%% Config = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%%" n + "%% Description: Cleanup after the suite." n + (erlang-skel-separator 2) + "end_per_suite(_Config) ->" n > + "ok." n n + + (erlang-skel-separator 2) + "%% Function: init_per_testcase(TestCase, Config0) -> Config1 |" n + "%% {skip,Reason}" n + "%% TestCase = atom()" n + "%% Name of the test case that is about to run." n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Reason = term()" n + "%% The reason for skipping the test case." n + "%%" n + "%% Description: Initialization before each test case." n + "%%" n + "%% Note: This function is free to add any key/value pairs to the Config" n + "%% variable, but should NOT alter/remove any existing entries." n + (erlang-skel-separator 2) + "init_per_testcase(_TestCase, Config) ->" n > + "Config." n n + + (erlang-skel-separator 2) + "%% Function: end_per_testcase(TestCase, Config) -> void()" n + "%%" n + "%% TestCase = atom()" n + "%% Name of the test case that is finished." n + "%% Config = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%%" n + "%% Description: Cleanup after each test case." n + (erlang-skel-separator 2) + "end_per_testcase(_TestCase, _Config) ->" n > + "ok."n n + + (erlang-skel-separator 2) + "%% Function: all(Clause) -> Descr | Spec | {skip,Reason}" n + "%%" n + "%% Clause = doc | suite" n + "%% Indicates expected return value." n + "%% Descr = [string()] | []" n + "%% String that describes the test suite." n + "%% Spec = [TestCase]" n + "%% A test specification." n + "%% TestCase = ConfCase | atom()" n + "%% Configuration case, or the name of a test case function." n + "%% ConfCase = {conf,Init,Spec,End} |" n + "%% {conf,Properties,Init,Spec,End}" n + "%% Init = End = {Mod,Func} | Func" n + "%% Initialization and cleanup function." n + "%% Mod = Func = atom()" n + "%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]" n + "%% Execution properties of the test cases (may be combined)." n + "%% Shuffle = shuffle | {shuffle,Seed}" n + "%% To get cases executed in random order." n + "%% Seed = {integer(),integer(),integer()}" n + "%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |" n + "%% repeat_until_any_ok | repeat_until_any_fail" n + "%% To get execution of cases repeated." n + "%% N = integer() | forever" n + "%% Reason = term()" n + "%% The reason for skipping the test suite." n + "%%" n + "%% Description: Returns a description of the test suite when" n + "%% Clause == doc, and a test specification (list" n + "%% of the conf and test cases in the suite) when" n + "%% Clause == suite." n + (erlang-skel-separator 2) + "all(doc) -> " n > + "[\"Describe the main purpose of this suite\"];" n n + "all(suite) -> " n > + "[a_test_case]." n n + n + (erlang-skel-separator 2) + "%% TEST CASES" n + (erlang-skel-separator 2) + n + (erlang-skel-separator 2) + "%% Function: TestCase(Arg) -> Descr | Spec | ok | exit() | {skip,Reason}" n + "%%" n + "%% Arg = doc | suite | Config" n + "%% Indicates expected behaviour and return value." n + "%% Config = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Descr = [string()] | []" n + "%% String that describes the test case." n + "%% Spec = [tuple()] | []" n + "%% A test specification, see all/1." n + "%% Reason = term()" n + "%% The reason for skipping the test case." n + "%%" n + "%% Description: Test case function. Returns a description of the test" n + "%% case (doc), then returns a test specification (suite)," n + "%% or performs the actual test (Config)." n + (erlang-skel-separator 2) + "a_test_case(doc) -> " n > + "[\"Describe the main purpose of this test case\"];" n n + "a_test_case(suite) -> " n > + "[];" n n + "a_test_case(Config) when is_list(Config) -> " n > + "ok." n + ) + "*The template of a library module. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-ct-test-suite-l + '((erlang-skel-include erlang-skel-large-header) + "%% Note: This directive should only be used in test suites." n + "-compile(export_all)." n n + + "-include_lib(\"common_test/include/ct.hrl\")." n n + + (erlang-skel-separator 2) + "%% COMMON TEST CALLBACK FUNCTIONS" n + (erlang-skel-separator 2) + n + (erlang-skel-separator 2) + "%% Function: suite() -> Info" n + "%%" n + "%% Info = [tuple()]" n + "%% List of key/value pairs." n + "%%" n + "%% Description: Returns list of tuples to set default properties" n + "%% for the suite." n + "%%" n + "%% Note: The suite/0 function is only meant to be used to return" n + "%% default data values, not perform any other operations." n + (erlang-skel-separator 2) + "suite() ->" n > + "[{timetrap,{minutes,10}}]." n n + + (erlang-skel-separator 2) + "%% Function: init_per_suite(Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + "%%" n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Reason = term()" n + "%% The reason for skipping the suite." n + "%%" n + "%% Description: Initialization before the suite." n + "%%" n + "%% Note: This function is free to add any key/value pairs to the Config" n + "%% variable, but should NOT alter/remove any existing entries." n + (erlang-skel-separator 2) + "init_per_suite(Config) ->" n > + "Config." n n + + (erlang-skel-separator 2) + "%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}" n + "%%" n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%%" n + "%% Description: Cleanup after the suite." n + (erlang-skel-separator 2) + "end_per_suite(_Config) ->" n > + "ok." n n + + (erlang-skel-separator 2) + "%% Function: init_per_group(GroupName, Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + "%%" n + "%% GroupName = atom()" n + "%% Name of the test case group that is about to run." n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding configuration data for the group." n + "%% Reason = term()" n + "%% The reason for skipping all test cases and subgroups in the group." n + "%%" n + "%% Description: Initialization before each test case group." n + (erlang-skel-separator 2) + "init_per_group(_GroupName, Config) ->" n > + "Config." n n + + (erlang-skel-separator 2) + "%% Function: end_per_group(GroupName, Config0) ->" n + "%% void() | {save_config,Config1}" n + "%%" n + "%% GroupName = atom()" n + "%% Name of the test case group that is finished." n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding configuration data for the group." n + "%%" n + "%% Description: Cleanup after each test case group." n + (erlang-skel-separator 2) + "end_per_group(_GroupName, _Config) ->" n > + "ok." n n + + (erlang-skel-separator 2) + "%% Function: init_per_testcase(TestCase, Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + "%%" n + "%% TestCase = atom()" n + "%% Name of the test case that is about to run." n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Reason = term()" n + "%% The reason for skipping the test case." n + "%%" n + "%% Description: Initialization before each test case." n + "%%" n + "%% Note: This function is free to add any key/value pairs to the Config" n + "%% variable, but should NOT alter/remove any existing entries." n + (erlang-skel-separator 2) + "init_per_testcase(_TestCase, Config) ->" n > + "Config." n n + + (erlang-skel-separator 2) + "%% Function: end_per_testcase(TestCase, Config0) ->" n + "%% void() | {save_config,Config1} | {fail,Reason}" n + "%%" n + "%% TestCase = atom()" n + "%% Name of the test case that is finished." n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Reason = term()" n + "%% The reason for failing the test case." n + "%%" n + "%% Description: Cleanup after each test case." n + (erlang-skel-separator 2) + "end_per_testcase(_TestCase, _Config) ->" n > + "ok." n n + + (erlang-skel-separator 2) + "%% Function: groups() -> [Group]" n + "%%" n + "%% Group = {GroupName,Properties,GroupsAndTestCases}" n + "%% GroupName = atom()" n + "%% The name of the group." n + "%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]" n + "%% Group properties that may be combined." n + "%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]" n + "%% TestCase = atom()" n + "%% The name of a test case." n + "%% Shuffle = shuffle | {shuffle,Seed}" n + "%% To get cases executed in random order." n + "%% Seed = {integer(),integer(),integer()}" n + "%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |" n + "%% repeat_until_any_ok | repeat_until_any_fail" n + "%% To get execution of cases repeated." n + "%% N = integer() | forever" n + "%%" n + "%% Description: Returns a list of test case group definitions." n + (erlang-skel-separator 2) + "groups() ->" n > + "[]." n n + + (erlang-skel-separator 2) + "%% Function: all() -> GroupsAndTestCases | {skip,Reason}" n + "%%" n + "%% GroupsAndTestCases = [{group,GroupName} | TestCase]" n + "%% GroupName = atom()" n + "%% Name of a test case group." n + "%% TestCase = atom()" n + "%% Name of a test case." n + "%% Reason = term()" n + "%% The reason for skipping all groups and test cases." n + "%%" n + "%% Description: Returns the list of groups and test cases that" n + "%% are to be executed." n + (erlang-skel-separator 2) + "all() -> " n > + "[my_test_case]." n n + + n + (erlang-skel-separator 2) + "%% TEST CASES" n + (erlang-skel-separator 2) + n + + (erlang-skel-separator 2) + "%% Function: TestCase() -> Info" n + "%%" n + "%% Info = [tuple()]" n + "%% List of key/value pairs." n + "%%" n + "%% Description: Test case info function - returns list of tuples to set" n + "%% properties for the test case." n + "%%" n + "%% Note: This function is only meant to be used to return a list of" n + "%% values, not perform any other operations." n + (erlang-skel-separator 2) + "my_test_case() -> " n > + "[]." n n + + (erlang-skel-separator 2) + "%% Function: TestCase(Config0) ->" n + "%% ok | exit() | {skip,Reason} | {comment,Comment} |" n + "%% {save_config,Config1} | {skip_and_save,Reason,Config1}" n + "%%" n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Reason = term()" n + "%% The reason for skipping the test case." n + "%% Comment = term()" n + "%% A comment about the test case that will be printed in the html log." n + "%%" n + "%% Description: Test case function. (The name of it must be specified in" n + "%% the all/0 list or in a test case group for the test case" n + "%% to be executed)." n + (erlang-skel-separator 2) + "my_test_case(_Config) -> " n > + "ok." n + ) + "*The template of a library module. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-ct-test-suite-s + '((erlang-skel-include erlang-skel-large-header) + "-compile(export_all)." n n + + "-include_lib(\"common_test/include/ct.hrl\")." n n + + (erlang-skel-separator 2) + "%% Function: suite() -> Info" n + "%% Info = [tuple()]" n + (erlang-skel-separator 2) + "suite() ->" n > + "[{timetrap,{seconds,30}}]." n n + + (erlang-skel-separator 2) + "%% Function: init_per_suite(Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + "%% Config0 = Config1 = [tuple()]" n + "%% Reason = term()" n + (erlang-skel-separator 2) + "init_per_suite(Config) ->" n > + "Config." n n + + (erlang-skel-separator 2) + "%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}" n + "%% Config0 = Config1 = [tuple()]" n + (erlang-skel-separator 2) + "end_per_suite(_Config) ->" n > + "ok." n n + + (erlang-skel-separator 2) + "%% Function: init_per_group(GroupName, Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + "%% GroupName = atom()" n + "%% Config0 = Config1 = [tuple()]" n + "%% Reason = term()" n + (erlang-skel-separator 2) + "init_per_group(_GroupName, Config) ->" n > + "Config." n n + + (erlang-skel-separator 2) + "%% Function: end_per_group(GroupName, Config0) ->" n + "%% void() | {save_config,Config1}" n + "%% GroupName = atom()" n + "%% Config0 = Config1 = [tuple()]" n + (erlang-skel-separator 2) + "end_per_group(_GroupName, _Config) ->" n > + "ok." n n + + (erlang-skel-separator 2) + "%% Function: init_per_testcase(TestCase, Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + "%% TestCase = atom()" n + "%% Config0 = Config1 = [tuple()]" n + "%% Reason = term()" n + (erlang-skel-separator 2) + "init_per_testcase(_TestCase, Config) ->" n > + "Config." n n + + (erlang-skel-separator 2) + "%% Function: end_per_testcase(TestCase, Config0) ->" n + "%% void() | {save_config,Config1} | {fail,Reason}" n + "%% TestCase = atom()" n + "%% Config0 = Config1 = [tuple()]" n + "%% Reason = term()" n + (erlang-skel-separator 2) + "end_per_testcase(_TestCase, _Config) ->" n > + "ok." n n + + (erlang-skel-separator 2) + "%% Function: groups() -> [Group]" n + "%% Group = {GroupName,Properties,GroupsAndTestCases}" n + "%% GroupName = atom()" n + "%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]" n + "%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]" n + "%% TestCase = atom()" n + "%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}" n + "%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |" n + "%% repeat_until_any_ok | repeat_until_any_fail" n + "%% N = integer() | forever" n + (erlang-skel-separator 2) + "groups() ->" n > + "[]." n n + + (erlang-skel-separator 2) + "%% Function: all() -> GroupsAndTestCases | {skip,Reason}" n + "%% GroupsAndTestCases = [{group,GroupName} | TestCase]" n + "%% GroupName = atom()" n + "%% TestCase = atom()" n + "%% Reason = term()" n + (erlang-skel-separator 2) + "all() -> " n > + "[my_test_case]." n n + + (erlang-skel-separator 2) + "%% Function: TestCase() -> Info" n + "%% Info = [tuple()]" n + (erlang-skel-separator 2) + "my_test_case() -> " n > + "[]." n n + + (erlang-skel-separator 2) + "%% Function: TestCase(Config0) ->" n + "%% ok | exit() | {skip,Reason} | {comment,Comment} |" n + "%% {save_config,Config1} | {skip_and_save,Reason,Config1}" n + "%% Config0 = Config1 = [tuple()]" n + "%% Reason = term()" n + "%% Comment = term()" n + (erlang-skel-separator 2) + "my_test_case(_Config) -> " n > + "ok." n + ) + "*The template of a library module. +Please see the function `tempo-define-template'.") diff --git a/emacs.d/elpa/erlang-20141104.17/erlang-skels.el b/emacs.d/elpa/erlang-20141104.17/erlang-skels.el new file mode 100644 index 0000000..78929ac --- /dev/null +++ b/emacs.d/elpa/erlang-20141104.17/erlang-skels.el @@ -0,0 +1,1701 @@ +;; +;; %CopyrightBegin% +;; +;; Copyright Ericsson AB 2010. All Rights Reserved. +;; +;; The contents of this file are subject to the Erlang Public License, +;; Version 1.1, (the "License"); you may not use this file except in +;; compliance with the License. You should have received a copy of the +;; Erlang Public License along with this software. If not, it can be +;; retrieved online at http://www.erlang.org/. +;; +;; Software distributed under the License is distributed on an "AS IS" +;; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +;; the License for the specific language governing rights and limitations +;; under the License. +;; +;; %CopyrightEnd% +;;; +;;; Purpose: Provide Erlang code skeletons. +;;; See 'erlang-skel-file' variable. + +(defvar erlang-tempo-tags nil + "Tempo tags for erlang mode") + +(defvar erlang-skel + '(("If" "if" erlang-skel-if) + ("Case" "case" erlang-skel-case) + ("Receive" "receive" erlang-skel-receive) + ("Receive After" "after" erlang-skel-receive-after) + ("Receive Loop" "loop" erlang-skel-receive-loop) + ("Module" "module" erlang-skel-module) + ("Author" "author" erlang-skel-author) + ("Function" "function" erlang-skel-function) + ("Spec" "spec" erlang-skel-spec) + () + ("Small Header" "small-header" + erlang-skel-small-header erlang-skel-header) + ("Normal Header" "normal-header" + erlang-skel-normal-header erlang-skel-header) + ("Large Header" "large-header" + erlang-skel-large-header erlang-skel-header) + () + ("Small Server" "small-server" + erlang-skel-small-server erlang-skel-header) + () + ("Application" "application" + erlang-skel-application erlang-skel-header) + ("Supervisor" "supervisor" + erlang-skel-supervisor erlang-skel-header) + ("supervisor_bridge" "supervisor-bridge" + erlang-skel-supervisor-bridge erlang-skel-header) + ("gen_server" "generic-server" + erlang-skel-generic-server erlang-skel-header) + ("gen_event" "gen-event" + erlang-skel-gen-event erlang-skel-header) + ("gen_fsm" "gen-fsm" + erlang-skel-gen-fsm erlang-skel-header) + ("wx_object" "wx-object" + erlang-skel-wx-object erlang-skel-header) + ("Library module" "gen-lib" + erlang-skel-lib erlang-skel-header) + ("Corba callback" "gen-corba-cb" + erlang-skel-corba-callback erlang-skel-header) + ("Small Common Test suite" "ct-test-suite-s" + erlang-skel-ct-test-suite-s erlang-skel-header) + ("Large Common Test suite" "ct-test-suite-l" + erlang-skel-ct-test-suite-l erlang-skel-header) + ("Erlang TS test suite" "ts-test-suite" + erlang-skel-ts-test-suite erlang-skel-header) + ) + "*Description of all skeleton templates. +Both functions and menu entries will be created. + +Each entry in `erlang-skel' should be a list with three or four +elements, or the empty list. + +The first element is the name which shows up in the menu. The second +is the `tempo' identifier (The string \"erlang-\" will be added in +front of it). The third is the skeleton descriptor, a variable +containing `tempo' attributes as described in the function +`tempo-define-template'. The optional fourth elements denotes a +function which should be called when the menu is selected. + +Functions corresponding to every template will be created. The name +of the function will be `tempo-template-erlang-X' where `X' is the +tempo identifier as specified in the second argument of the elements +in this list. + +A list with zero elements means that the a horizontal line should +be placed in the menu.") + +(defvar erlang-skel-use-separators t + "A boolean than determines whether the skeletons include horizontal +separators. + +Should this variable be nil, the documentation for functions will not +include separators of the form %%--...") + +;; In XEmacs `user-mail-address' returns "x@y.z (Foo Bar)" ARGH! +;; What's wrong with that? RFC 822 says it's legal. [sverkerw] +;; This needs to use the customized value. If that's not sane, things like +;; add-log will lose anyhow. Avoid it if there _is_ a paren. +(defvar erlang-skel-mail-address + (if (or (not user-mail-address) (string-match "(" user-mail-address)) + (concat (user-login-name) "@" + (or (and (boundp 'mail-host-address) + mail-host-address) + (system-name))) + user-mail-address) + "Mail address of the user.") + +;; Expression templates: +(defvar erlang-skel-case + '((erlang-skel-skip-blank) o > + "case " p " of" n> p "_ ->" n> p "ok" n "end" > p) + "*The skeleton of a `case' expression. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-if + '((erlang-skel-skip-blank) o > + "if" n> p " ->" n> p "ok" n "end" > p) + "The skeleton of an `if' expression. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-receive + '((erlang-skel-skip-blank) o > + "receive" n> p "_ ->" n> p "ok" n "end" > p) + "*The skeleton of a `receive' expression. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-receive-after + '((erlang-skel-skip-blank) o > + "receive" n> p "_ ->" n> p "ok" n "after " > p "T ->" n> + p "ok" n "end" > p) + "*The skeleton of a `receive' expression with an `after' clause. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-receive-loop + '(& o "loop(" p ") ->" n> "receive" n> p "_ ->" n> + "loop(" p ")" n "end." >) + "*The skeleton of a simple `receive' loop. +Please see the function `tempo-define-template'.") + + +(defvar erlang-skel-function + '((erlang-skel-separator-start 2) + "%% @doc" n + "%% @spec" n + (erlang-skel-separator-end 2)) + "*The template of a function skeleton. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-spec + '("-spec " (erlang-skel-get-function-name) "(" (erlang-skel-get-function-args) ") -> undefined.") + "*The template of a -spec for the function following point. +Please see the function `tempo-define-template'.") + +;; Attribute templates + +(defvar erlang-skel-module + '(& "-module(" + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) + ")." n) + "*The skeleton of a `module' attribute. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-author + '(& "-author('" erlang-skel-mail-address "')." n) + "*The skeleton of a `author' attribute. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-vc nil + "*The skeleton template to generate a version control attribute. +The default is to insert nothing. Example of usage: + + (setq erlang-skel-vc '(& \"-rcs(\\\"$\Id: $ \\\").\") n) + +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-export + '(& "-export([" n> "])." n) + "*The skeleton of an `export' attribute. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-import + '(& "%%-import(Module, [Function/Arity, ...])." n) + "*The skeleton of an `import' attribute. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-compile nil + ;; '(& "%%-compile(export_all)." n) + "*The skeleton of a `compile' attribute. +Please see the function `tempo-define-template'.") + + +;; Comment templates. + +(defvar erlang-skel-date-function 'erlang-skel-dd-mmm-yyyy + "*Function which returns date string. +Look in the module `time-stamp' for a battery of functions.") + +(defvar erlang-skel-copyright-comment + (if (boundp '*copyright-organization*) + '(& "%%% @copyright (C) " (format-time-string "%Y") ", " + *copyright-organization* n) + '(& "%%% @copyright (C) " (format-time-string "%Y") ", " + (user-full-name) n)) + "*The template for a copyright line in the header, normally empty. +This variable should be bound to a `tempo' template, for example: + '(& \"%%% Copyright (C) 2000, Yoyodyne, Inc.\" n) +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-created-comment + '(& "%%% Created : " (funcall erlang-skel-date-function) " by " + (user-full-name) " <" erlang-skel-mail-address ">" n) + "*The template for the \"Created:\" comment line.") + +(defvar erlang-skel-author-comment + '(& "%%% @author " (user-full-name) " <" erlang-skel-mail-address ">" n) + "*The template for creating the \"Author:\" line in the header. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-small-header + '(o (erlang-skel-include erlang-skel-module) + n + (erlang-skel-include erlang-skel-compile erlang-skel-vc)) + "*The template of a small header without any comments. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-normal-header + '(o (erlang-skel-include erlang-skel-author-comment) + (erlang-skel-include erlang-skel-copyright-comment) + "%%% @doc" n + "%%%" p n + "%%% @end" n + (erlang-skel-include erlang-skel-created-comment) n + (erlang-skel-include erlang-skel-small-header) n) + "*The template of a normal header. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-large-header + '(o (erlang-skel-separator) + (erlang-skel-include erlang-skel-author-comment) + (erlang-skel-include erlang-skel-copyright-comment) + "%%% @doc" n + "%%%" p n + "%%% @end" n + (erlang-skel-include erlang-skel-created-comment) + (erlang-skel-separator) + (erlang-skel-include erlang-skel-small-header) ) + "*The template of a large header. +Please see the function `tempo-define-template'.") + + + ;; Server templates. +(defvar erlang-skel-small-server + '((erlang-skel-include erlang-skel-large-header) + "-export([start/0, init/1])." n n n + "start() ->" n> "spawn(" (erlang-get-module-from-file-name) + ", init, [self()])." n n + "init(From) ->" n> + "loop(From)." n n + "loop(From) ->" n> + "receive" n> + p "_ ->" n> + "loop(From)" n + "end." > n + ) + "*Template of a small server. +Please see the function `tempo-define-template'.") + +;; Behaviour templates. +(defvar erlang-skel-application + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(application)." n n + "%% Application callbacks" n + "-export([start/2, stop/1])." n n + (erlang-skel-double-separator-start 3) + "%%% Application callbacks" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% This function is called whenever an application is started using" n + "%% application:start/[1,2], and should start the processes of the" n + "%% application. If the application is structured according to the OTP" n + "%% design principles as a supervision tree, this means starting the" n + "%% top supervisor of the tree." n + "%%" n + "%% @spec start(StartType, StartArgs) -> {ok, Pid} |" n + "%% {ok, Pid, State} |" n + "%% {error, Reason}" n + "%% StartType = normal | {takeover, Node} | {failover, Node}" n + "%% StartArgs = term()" n + (erlang-skel-separator-end 2) + "start(_StartType, _StartArgs) ->" n> + "case 'TopSupervisor':start_link() of" n> + "{ok, Pid} ->" n> + "{ok, Pid};" n> + "Error ->" n> + "Error" n + "end." > n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% This function is called whenever an application has stopped. It" n + "%% is intended to be the opposite of Module:start/2 and should do" n + "%% any necessary cleaning up. The return value is ignored." n + "%%" n + "%% @spec stop(State) -> void()" n + (erlang-skel-separator-end 2) + "stop(_State) ->" n> + "ok." n + n + (erlang-skel-double-separator-start 3) + "%%% Internal functions" n + (erlang-skel-double-separator-end 3) + ) + "*The template of an application behaviour. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-supervisor + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(supervisor)." n n + + "%% API" n + "-export([start_link/0])." n n + + "%% Supervisor callbacks" n + "-export([init/1])." n n + + "-define(SERVER, ?MODULE)." n n + + (erlang-skel-double-separator-start 3) + "%%% API functions" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Starts the supervisor" n + "%%" n + "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n + (erlang-skel-separator-end 2) + "start_link() ->" n> + "supervisor:start_link({local, ?SERVER}, ?MODULE, [])." n + n + (erlang-skel-double-separator-start 3) + "%%% Supervisor callbacks" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Whenever a supervisor is started using supervisor:start_link/[2,3]," n + "%% this function is called by the new process to find out about" n + "%% restart strategy, maximum restart frequency and child" n + "%% specifications." n + "%%" n + "%% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |" n + "%% ignore |" n + "%% {error, Reason}" n + (erlang-skel-separator-end 2) + "init([]) ->" n> + "RestartStrategy = one_for_one," n> + "MaxRestarts = 1000," n> + "MaxSecondsBetweenRestarts = 3600," n + "" n> + "SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}," n + "" n> + "Restart = permanent," n> + "Shutdown = 2000," n> + "Type = worker," n + "" n> + "AChild = {'AName', {'AModule', start_link, []}," n> + "Restart, Shutdown, Type, ['AModule']}," n + "" n> + "{ok, {SupFlags, [AChild]}}." n + n + (erlang-skel-double-separator-start 3) + "%%% Internal functions" n + (erlang-skel-double-separator-end 3) + ) + "*The template of an supervisor behaviour. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-supervisor-bridge + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(supervisor_bridge)." n n + + "%% API" n + "-export([start_link/0])." n n + + "%% supervisor_bridge callbacks" n + "-export([init/1, terminate/2])." n n + + "-define(SERVER, ?MODULE)." n n + + "-record(state, {})." n n + + (erlang-skel-double-separator-start 3) + "%%% API" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Starts the supervisor bridge" n + "%%" n + "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n + (erlang-skel-separator-end 2) + "start_link() ->" n> + "supervisor_bridge:start_link({local, ?SERVER}, ?MODULE, [])." n + n + (erlang-skel-double-separator-start 3) + "%%% supervisor_bridge callbacks" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Creates a supervisor_bridge process, linked to the calling process," n + "%% which calls Module:init/1 to start the subsystem. To ensure a" n + "%% synchronized start-up procedure, this function does not return" n + "%% until Module:init/1 has returned." n + "%%" n + "%% @spec init(Args) -> {ok, Pid, State} |" n + "%% ignore |" n + "%% {error, Reason}" n + (erlang-skel-separator-end 2) + "init([]) ->" n> + "case 'AModule':start_link() of" n> + "{ok, Pid} ->" n> + "{ok, Pid, #state{}};" n> + "Error ->" n> + "Error" n + "end." > n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% This function is called by the supervisor_bridge when it is about" n + "%% to terminate. It should be the opposite of Module:init/1 and stop" n + "%% the subsystem and do any necessary cleaning up.The return value is" n + "%% ignored." n + "%%" n + "%% @spec terminate(Reason, State) -> void()" n + (erlang-skel-separator-end 2) + "terminate(Reason, State) ->" n> + "'AModule':stop()," n> + "ok." n + n + (erlang-skel-double-separator-start 3) + "%%% Internal functions" n + (erlang-skel-double-separator-end 3) + ) + "*The template of an supervisor_bridge behaviour. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-generic-server + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(gen_server)." n n + + "%% API" n + "-export([start_link/0])." n n + + "%% gen_server callbacks" n + "-export([init/1, handle_call/3, handle_cast/2, " + "handle_info/2," n> + "terminate/2, code_change/3])." n n + + "-define(SERVER, ?MODULE)." n n + + "-record(state, {})." n n + + (erlang-skel-double-separator-start 3) + "%%% API" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Starts the server" n + "%%" n + "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n + (erlang-skel-separator-end 2) + "start_link() ->" n> + "gen_server:start_link({local, ?SERVER}, ?MODULE, [], [])." n + n + (erlang-skel-double-separator-start 3) + "%%% gen_server callbacks" n + (erlang-skel-double-separator-end 3) + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Initializes the server" n + "%%" n + "%% @spec init(Args) -> {ok, State} |" n + "%% {ok, State, Timeout} |" n + "%% ignore |" n + "%% {stop, Reason}" n + (erlang-skel-separator-end 2) + "init([]) ->" n> + "{ok, #state{}}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Handling call messages" n + "%%" n + "%% @spec handle_call(Request, From, State) ->" n + "%% {reply, Reply, State} |" n + "%% {reply, Reply, State, Timeout} |" n + "%% {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, Reply, State} |" n + "%% {stop, Reason, State}" n + (erlang-skel-separator-end 2) + "handle_call(_Request, _From, State) ->" n> + "Reply = ok," n> + "{reply, Reply, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Handling cast messages" n + "%%" n + "%% @spec handle_cast(Msg, State) -> {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, State}" n + (erlang-skel-separator-end 2) + "handle_cast(_Msg, State) ->" n> + "{noreply, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Handling all non call/cast messages" n + "%%" n + "%% @spec handle_info(Info, State) -> {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, State}" n + (erlang-skel-separator-end 2) + "handle_info(_Info, State) ->" n> + "{noreply, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% This function is called by a gen_server when it is about to" n + "%% terminate. It should be the opposite of Module:init/1 and do any" n + "%% necessary cleaning up. When it returns, the gen_server terminates" n + "%% with Reason. The return value is ignored." n + "%%" n + "%% @spec terminate(Reason, State) -> void()" n + (erlang-skel-separator-end 2) + "terminate(_Reason, _State) ->" n> + "ok." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Convert process state when code is changed" n + "%%" n + "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n + (erlang-skel-separator-end 2) + "code_change(_OldVsn, State, _Extra) ->" n> + "{ok, State}." n + n + (erlang-skel-double-separator-start 3) + "%%% Internal functions" n + (erlang-skel-double-separator-end 3) + ) + "*The template of a generic server. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-gen-event + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(gen_event)." n n + + "%% API" n + "-export([start_link/0, add_handler/0])." n n + + "%% gen_event callbacks" n + "-export([init/1, handle_event/2, handle_call/2, " n> + "handle_info/2, terminate/2, code_change/3])." n n + + "-define(SERVER, ?MODULE)." n n + + "-record(state, {})." n n + + (erlang-skel-double-separator-start 3) + "%%% API" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Creates an event manager" n + "%%" n + "%% @spec start_link() -> {ok, Pid} | {error, Error}" n + (erlang-skel-separator-end 2) + "start_link() ->" n> + "gen_event:start_link({local, ?SERVER})." n + n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Adds an event handler" n + "%%" n + "%% @spec add_handler() -> ok | {'EXIT', Reason} | term()" n + (erlang-skel-separator-end 2) + "add_handler() ->" n> + "gen_event:add_handler(?SERVER, ?MODULE, [])." n + n + (erlang-skel-double-separator-start 3) + "%%% gen_event callbacks" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Whenever a new event handler is added to an event manager," n + "%% this function is called to initialize the event handler." n + "%%" n + "%% @spec init(Args) -> {ok, State}" n + (erlang-skel-separator-end 2) + "init([]) ->" n> + "{ok, #state{}}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Whenever an event manager receives an event sent using" n + "%% gen_event:notify/2 or gen_event:sync_notify/2, this function is" n + "%% called for each installed event handler to handle the event." n + "%%" n + "%% @spec handle_event(Event, State) ->" n + "%% {ok, State} |" n + "%% {swap_handler, Args1, State1, Mod2, Args2} |"n + "%% remove_handler" n + (erlang-skel-separator-end 2) + "handle_event(_Event, State) ->" n> + "{ok, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Whenever an event manager receives a request sent using" n + "%% gen_event:call/3,4, this function is called for the specified" n + "%% event handler to handle the request." n + "%%" n + "%% @spec handle_call(Request, State) ->" n + "%% {ok, Reply, State} |" n + "%% {swap_handler, Reply, Args1, State1, Mod2, Args2} |" n + "%% {remove_handler, Reply}" n + (erlang-skel-separator-end 2) + "handle_call(_Request, State) ->" n> + "Reply = ok," n> + "{ok, Reply, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% This function is called for each installed event handler when" n + "%% an event manager receives any other message than an event or a" n + "%% synchronous request (or a system message)." n + "%%" n + "%% @spec handle_info(Info, State) ->" n + "%% {ok, State} |" n + "%% {swap_handler, Args1, State1, Mod2, Args2} |" n + "%% remove_handler" n + (erlang-skel-separator-end 2) + "handle_info(_Info, State) ->" n> + "{ok, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Whenever an event handler is deleted from an event manager, this" n + "%% function is called. It should be the opposite of Module:init/1 and" n + "%% do any necessary cleaning up." n + "%%" n + "%% @spec terminate(Reason, State) -> void()" n + (erlang-skel-separator-end 2) + "terminate(_Reason, _State) ->" n> + "ok." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Convert process state when code is changed" n + "%%" n + "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n + (erlang-skel-separator-end 2) + "code_change(_OldVsn, State, _Extra) ->" n> + "{ok, State}." n + n + (erlang-skel-double-separator-start 3) + "%%% Internal functions" n + (erlang-skel-double-separator-end 3) + ) + "*The template of a gen_event. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-gen-fsm + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(gen_fsm)." n n + + "%% API" n + "-export([start_link/0])." n n + + "%% gen_fsm callbacks" n + "-export([init/1, state_name/2, state_name/3, handle_event/3," n> + "handle_sync_event/4, handle_info/3, terminate/3, code_change/4])." n n + + "-define(SERVER, ?MODULE)." n n + + "-record(state, {})." n n + + (erlang-skel-double-separator-start 3) + "%%% API" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Creates a gen_fsm process which calls Module:init/1 to" n + "%% initialize. To ensure a synchronized start-up procedure, this" n + "%% function does not return until Module:init/1 has returned." n + "%%" n + "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n + (erlang-skel-separator-end 2) + "start_link() ->" n> + "gen_fsm:start_link({local, ?SERVER}, ?MODULE, [], [])." n + n + (erlang-skel-double-separator-start 3) + "%%% gen_fsm callbacks" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Whenever a gen_fsm is started using gen_fsm:start/[3,4] or" n + "%% gen_fsm:start_link/[3,4], this function is called by the new" n + "%% process to initialize." n + "%%" n + "%% @spec init(Args) -> {ok, StateName, State} |" n + "%% {ok, StateName, State, Timeout} |" n + "%% ignore |" n + "%% {stop, StopReason}" n + (erlang-skel-separator-end 2) + "init([]) ->" n> + "{ok, state_name, #state{}}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% There should be one instance of this function for each possible" n + "%% state name. Whenever a gen_fsm receives an event sent using" n + "%% gen_fsm:send_event/2, the instance of this function with the same" n + "%% name as the current state name StateName is called to handle" n + "%% the event. It is also called if a timeout occurs." n + "%%" n + "%% @spec state_name(Event, State) ->" n + "%% {next_state, NextStateName, NextState} |" n + "%% {next_state, NextStateName, NextState, Timeout} |" n + "%% {stop, Reason, NewState}" n + (erlang-skel-separator-end 2) + "state_name(_Event, State) ->" n> + "{next_state, state_name, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% There should be one instance of this function for each possible" n + "%% state name. Whenever a gen_fsm receives an event sent using" n + "%% gen_fsm:sync_send_event/[2,3], the instance of this function with" n + "%% the same name as the current state name StateName is called to" n + "%% handle the event." n + "%%" n + "%% @spec state_name(Event, From, State) ->" n + "%% {next_state, NextStateName, NextState} |"n + "%% {next_state, NextStateName, NextState, Timeout} |" n + "%% {reply, Reply, NextStateName, NextState} |" n + "%% {reply, Reply, NextStateName, NextState, Timeout} |" n + "%% {stop, Reason, NewState} |" n + "%% {stop, Reason, Reply, NewState}" n + (erlang-skel-separator-end 2) + "state_name(_Event, _From, State) ->" n> + "Reply = ok," n> + "{reply, Reply, state_name, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Whenever a gen_fsm receives an event sent using" n + "%% gen_fsm:send_all_state_event/2, this function is called to handle" n + "%% the event." n + "%%" n + "%% @spec handle_event(Event, StateName, State) ->" n + "%% {next_state, NextStateName, NextState} |" n + "%% {next_state, NextStateName, NextState, Timeout} |" n + "%% {stop, Reason, NewState}" n + (erlang-skel-separator-end 2) + "handle_event(_Event, StateName, State) ->" n> + "{next_state, StateName, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Whenever a gen_fsm receives an event sent using" n + "%% gen_fsm:sync_send_all_state_event/[2,3], this function is called" n + "%% to handle the event." n + "%%" n + "%% @spec handle_sync_event(Event, From, StateName, State) ->" n + "%% {next_state, NextStateName, NextState} |" n + "%% {next_state, NextStateName, NextState, Timeout} |" n + "%% {reply, Reply, NextStateName, NextState} |" n + "%% {reply, Reply, NextStateName, NextState, Timeout} |" n + "%% {stop, Reason, NewState} |" n + "%% {stop, Reason, Reply, NewState}" n + (erlang-skel-separator-end 2) + "handle_sync_event(_Event, _From, StateName, State) ->" n> + "Reply = ok," n> + "{reply, Reply, StateName, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% This function is called by a gen_fsm when it receives any" n + "%% message other than a synchronous or asynchronous event" n + "%% (or a system message)." n + "%%" n + "%% @spec handle_info(Info,StateName,State)->" n + "%% {next_state, NextStateName, NextState} |" n + "%% {next_state, NextStateName, NextState, Timeout} |" n + "%% {stop, Reason, NewState}" n + (erlang-skel-separator-end 2) + "handle_info(_Info, StateName, State) ->" n> + "{next_state, StateName, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% This function is called by a gen_fsm when it is about to" n + "%% terminate. It should be the opposite of Module:init/1 and do any" n + "%% necessary cleaning up. When it returns, the gen_fsm terminates with" n + "%% Reason. The return value is ignored." n + "%%" n + "%% @spec terminate(Reason, StateName, State) -> void()" n + (erlang-skel-separator-end 2) + "terminate(_Reason, _StateName, _State) ->" n> + "ok." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Convert process state when code is changed" n + "%%" n + "%% @spec code_change(OldVsn, StateName, State, Extra) ->" n + "%% {ok, StateName, NewState}" n + (erlang-skel-separator-end 2) + "code_change(_OldVsn, StateName, State, _Extra) ->" n> + "{ok, StateName, State}." n + n + (erlang-skel-double-separator-start 3) + "%%% Internal functions" n + (erlang-skel-double-separator-end 3) + ) + "*The template of a gen_fsm. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-wx-object + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(wx_object)." n n + + "-include_lib(\"wx/include/wx.hrl\")." n n + + "%% API" n + "-export([start_link/0])." n n + + "%% wx_object callbacks" n + "-export([init/1, handle_call/3, handle_cast/2, " + "handle_info/2," n> + "handle_event/2, terminate/2, code_change/3])." n n + + "-record(state, {})." n n + + (erlang-skel-double-separator-start 3) + "%%% API" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Starts the server" n + "%%" n + "%% @spec start_link() -> wxWindow()" n + (erlang-skel-separator-end 2) + "start_link() ->" n> + "wx_object:start_link(?MODULE, [], [])." n + n + (erlang-skel-double-separator-start 3) + "%%% wx_object callbacks" n + (erlang-skel-double-separator-end 3) + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Initializes the server" n + "%%" n + "%% @spec init(Args) -> {wxWindow(), State} |" n + "%% {wxWindow(), State, Timeout} |" n + "%% ignore |" n + "%% {stop, Reason}" n + (erlang-skel-separator-end 2) + "init([]) ->" n> + "wx:new()," n> + "Frame = wxFrame:new()," n> + "{Frame, #state{}}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Handling events" n + "%%" n + "%% @spec handle_event(wx{}, State) ->" n + "%% {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, State}" n + (erlang-skel-separator-end 2) + "handle_event(#wx{}, State) ->" n> + "{noreply, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Handling call messages" n + "%%" n + "%% @spec handle_call(Request, From, State) ->" n + "%% {reply, Reply, State} |" n + "%% {reply, Reply, State, Timeout} |" n + "%% {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, Reply, State} |" n + "%% {stop, Reason, State}" n + (erlang-skel-separator-end 2) + "handle_call(_Request, _From, State) ->" n> + "Reply = ok," n> + "{reply, Reply, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Handling cast messages" n + "%%" n + "%% @spec handle_cast(Msg, State) -> {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, State}" n + (erlang-skel-separator-end 2) + "handle_cast(_Msg, State) ->" n> + "{noreply, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Handling all non call/cast messages" n + "%%" n + "%% @spec handle_info(Info, State) -> {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, State}" n + (erlang-skel-separator-end 2) + "handle_info(_Info, State) ->" n> + "{noreply, State}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% This function is called by a wx_object when it is about to" n + "%% terminate. It should be the opposite of Module:init/1 and do any" n + "%% necessary cleaning up. When it returns, the wx_object terminates" n + "%% with Reason. The return value is ignored." n + "%%" n + "%% @spec terminate(Reason, State) -> void()" n + (erlang-skel-separator-end 2) + "terminate(_Reason, _State) ->" n> + "ok." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Convert process state when code is changed" n + "%%" n + "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n + (erlang-skel-separator-end 2) + "code_change(_OldVsn, State, _Extra) ->" n> + "{ok, State}." n + n + (erlang-skel-double-separator-start 3) + "%%% Internal functions" n + (erlang-skel-double-separator-end 3) + ) + "*The template of a generic server. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-lib + '((erlang-skel-include erlang-skel-large-header) + + "%% API" n + "-export([])." n n + + (erlang-skel-double-separator-start 3) + "%%% API" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% @spec" n + (erlang-skel-separator-end 2) + n + (erlang-skel-double-separator-start 3) + "%%% Internal functions" n + (erlang-skel-double-separator-end 3) + ) + "*The template of a library module. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-corba-callback + '((erlang-skel-include erlang-skel-large-header) + "%% Include files" n n + + "%% API" n + "-export([])." n n + + "%% Corba callbacks" n + "-export([init/1, terminate/2, code_change/3])." n n + + "-record(state, {})." n n + + (erlang-skel-double-separator-start 3) + "%%% Corba callbacks" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Initializes the server" n + "%%" n + "%% @spec init(Args) -> {ok, State} |" n + "%% {ok, State, Timeout} |" n + "%% ignore |" n + "%% {stop, Reason}" n + (erlang-skel-separator-end 2) + "init([]) ->" n> + "{ok, #state{}}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Shutdown the server" n + "%%" n + "%% @spec terminate(Reason, State) -> void()" n + (erlang-skel-separator-end 2) + "terminate(_Reason, _State) ->" n> + "ok." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Convert process state when code is changed" n + "%%" n + "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n + (erlang-skel-separator-end 2) + "code_change(_OldVsn, State, _Extra) ->" n> + "{ok, State}." n + n + (erlang-skel-double-separator-start 3) + "%%% Internal functions" n + (erlang-skel-double-separator-end 3) + ) + "*The template of a library module. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-ts-test-suite + '((erlang-skel-include erlang-skel-large-header) + "%% Note: This directive should only be used in test suites." n + "-compile(export_all)." n n + + "-include_lib(\"test_server/include/test_server.hrl\")." n n + + (erlang-skel-separator-start 2) + "%% TEST SERVER CALLBACK FUNCTIONS" n + (erlang-skel-separator 2) + n + (erlang-skel-separator-start 2) + "%%" n + "%% @doc" n + "%% Initialization before the suite." n + "%%" n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Reason = term()" n + "%% The reason for skipping the suite." n + "%%" n + "%% Note: This function is free to add any key/value pairs to the Config" n + "%% variable, but should NOT alter/remove any existing entries." n + "%%" n + "%% @spec init_per_suite(Config) -> Config" n + (erlang-skel-separator-end 2) + "init_per_suite(Config) ->" n > + "Config." n n + + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Cleanup after the suite." n + "%% Config - [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%%" n + "%% @spec end_per_suite(Config) -> _" n + (erlang-skel-separator-end 2) + "end_per_suite(_Config) ->" n > + "ok." n n + + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Initialization before each test case" n + "%%" n + "%% TestCase - atom()" n + "%% Name of the test case that is about to be run." n + "%% Config - [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Reason = term()" n + "%% The reason for skipping the test case." n + "%%" n + "%% Note: This function is free to add any key/value pairs to the Config" n + "%% variable, but should NOT alter/remove any existing entries." n + "%%" n + "%% @spec init_per_testcase(TestCase, Config) -> Config" n + (erlang-skel-separator-end 2) + "init_per_testcase(_TestCase, Config) ->" n > + "Config." n n + + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Cleanup after each test case" n + "%%" n + "%% TestCase = atom()" n + "%% Name of the test case that is finished." n + "%% Config = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%%" n + "%% @spec end_per_testcase(TestCase, Config) -> _" n + (erlang-skel-separator-end 2) + "end_per_testcase(_TestCase, _Config) ->" n > + "ok."n n + + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Returns a description of the test suite when" n + "%% Clause == doc, and a test specification (list" n + "%% of the conf and test cases in the suite) when" n + "%% Clause == suite." n + "%% Returns a list of all test cases in this test suite" n + "%%" n + "%% Clause = doc | suite" n + "%% Indicates expected return value." n + "%% Descr = [string()] | []" n + "%% String that describes the test suite." n + "%% Spec = [TestCase]" n + "%% A test specification." n + "%% TestCase = ConfCase | atom()" n + "%% Configuration case, or the name of a test case function." n + "%% ConfCase = {conf,Init,Spec,End} |" n + "%% {conf,Properties,Init,Spec,End}" n + "%% Init = End = {Mod,Func} | Func" n + "%% Initialization and cleanup function." n + "%% Mod = Func = atom()" n + "%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]" n + "%% Execution properties of the test cases (may be combined)." n + "%% Shuffle = shuffle | {shuffle,Seed}" n + "%% To get cases executed in random order." n + "%% Seed = {integer(),integer(),integer()}" n + "%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |" n + "%% repeat_until_any_ok | repeat_until_any_fail" n + "%% To get execution of cases repeated." n + "%% N = integer() | forever" n + "%% Reason = term()" n + "%% The reason for skipping the test suite." n + "%%" n + "%% @spec all(Clause) -> TestCases" n + (erlang-skel-separator-end 2) + "all(doc) ->" n > + "[\"Describe the main purpose of this suite\"];" n n + "all(suite) -> " n > + "[a_test_case]." n n + n + (erlang-skel-separator-start 2) + "%% TEST CASES" n + (erlang-skel-separator 2) + n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Test case function. Returns a description of the test" n + "%% case (doc), then returns a test specification (suite)," n + "%% or performs the actual test (Config)." n + "%%" n + "%% Arg = doc | suite | Config" n + "%% Indicates expected behaviour and return value." n + "%% Config = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Descr = [string()] | []" n + "%% String that describes the test case." n + "%% Spec = [tuple()] | []" n + "%% A test specification, see all/1." n + "%% Reason = term()" n + "%% The reason for skipping the test case." n + "%%" n + "%% @spec TestCase(Arg) -> Descr | Spec | ok | exit() | {skip,Reason}" n + + (erlang-skel-separator-end 2) + "a_test_case(doc) -> " n > + "[\"Describe the main purpose of this test case\"];" n n + "a_test_case(suite) -> " n > + "[];" n n + "a_test_case(Config) when is_list(Config) -> " n > + "ok." n + ) + "*The template of a library module. +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-ct-test-suite-s + '((erlang-skel-include erlang-skel-large-header) + "-compile(export_all)." n n + + "-include_lib(\"common_test/include/ct.hrl\")." n n + + (erlang-skel-separator-start 2) + "%% @spec suite() -> Info" n + "%% Info = [tuple()]" n + (erlang-skel-separator-end 2) + "suite() ->" n > + "[{timetrap,{seconds,30}}]." n n + + (erlang-skel-separator-start 2) + "%% @spec init_per_suite(Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + "%% Config0 = Config1 = [tuple()]" n + "%% Reason = term()" n + (erlang-skel-separator-end 2) + "init_per_suite(Config) ->" n > + "Config." n n + + (erlang-skel-separator-start 2) + "%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}" n + "%% Config0 = Config1 = [tuple()]" n + (erlang-skel-separator-end 2) + "end_per_suite(_Config) ->" n > + "ok." n n + + (erlang-skel-separator-start 2) + "%% @spec init_per_group(GroupName, Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + "%% GroupName = atom()" n + "%% Config0 = Config1 = [tuple()]" n + "%% Reason = term()" n + (erlang-skel-separator-end 2) + "init_per_group(_GroupName, Config) ->" n > + "Config." n n + + (erlang-skel-separator-start 2) + "%% @spec end_per_group(GroupName, Config0) ->" n + "%% void() | {save_config,Config1}" n + "%% GroupName = atom()" n + "%% Config0 = Config1 = [tuple()]" n + (erlang-skel-separator-end 2) + "end_per_group(_GroupName, _Config) ->" n > + "ok." n n + + (erlang-skel-separator-start 2) + "%% @spec init_per_testcase(TestCase, Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + "%% TestCase = atom()" n + "%% Config0 = Config1 = [tuple()]" n + "%% Reason = term()" n + (erlang-skel-separator-end 2) + "init_per_testcase(_TestCase, Config) ->" n > + "Config." n n + + (erlang-skel-separator-start 2) + "%% @spec end_per_testcase(TestCase, Config0) ->" n + "%% void() | {save_config,Config1} | {fail,Reason}" n + "%% TestCase = atom()" n + "%% Config0 = Config1 = [tuple()]" n + "%% Reason = term()" n + (erlang-skel-separator-end 2) + "end_per_testcase(_TestCase, _Config) ->" n > + "ok." n n + + (erlang-skel-separator-start 2) + "%% @spec groups() -> [Group]" n + "%% Group = {GroupName,Properties,GroupsAndTestCases}" n + "%% GroupName = atom()" n + "%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]" n + "%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]" n + "%% TestCase = atom()" n + "%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}" n + "%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |" n + "%% repeat_until_any_ok | repeat_until_any_fail" n + "%% N = integer() | forever" n + (erlang-skel-separator-end 2) + "groups() ->" n > + "[]." n n + + (erlang-skel-separator-start 2) + "%% @spec all() -> GroupsAndTestCases | {skip,Reason}" n + "%% GroupsAndTestCases = [{group,GroupName} | TestCase]" n + "%% GroupName = atom()" n + "%% TestCase = atom()" n + "%% Reason = term()" n + (erlang-skel-separator-end 2) + "all() -> " n > + "[my_test_case]." n n + + (erlang-skel-separator-start 2) + "%% @spec TestCase() -> Info" n + "%% Info = [tuple()]" n + (erlang-skel-separator-end 2) + "my_test_case() -> " n > + "[]." n n + + (erlang-skel-separator-start 2) + "%% @spec TestCase(Config0) ->" n + "%% ok | exit() | {skip,Reason} | {comment,Comment} |" n + "%% {save_config,Config1} | {skip_and_save,Reason,Config1}" n + "%% Config0 = Config1 = [tuple()]" n + "%% Reason = term()" n + "%% Comment = term()" n + (erlang-skel-separator-end 2) + "my_test_case(_Config) -> " n > + "ok." n + ) + "*The template of a library module. +Please see the function `tempo-define-template'.") + + +(defvar erlang-skel-ct-test-suite-l + '((erlang-skel-include erlang-skel-large-header) + "%% Note: This directive should only be used in test suites." n + "-compile(export_all)." n n + + "-include_lib(\"common_test/include/ct.hrl\")." n n + + (erlang-skel-separator-start 2) + "%% COMMON TEST CALLBACK FUNCTIONS" n + (erlang-skel-separator 2) + n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Returns list of tuples to set default properties" n + "%% for the suite." n + "%%" n + "%% Function: suite() -> Info" n + "%%" n + "%% Info = [tuple()]" n + "%% List of key/value pairs." n + "%%" n + "%% Note: The suite/0 function is only meant to be used to return" n + "%% default data values, not perform any other operations." n + "%%" n + "%% @spec suite() -> Info" n + (erlang-skel-separator-end 2) + "suite() ->" n > + "[{timetrap,{minutes,10}}]." n n + + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Initialization before the whole suite" n + "%%" n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Reason = term()" n + "%% The reason for skipping the suite." n + "%%" n + "%% Note: This function is free to add any key/value pairs to the Config" n + "%% variable, but should NOT alter/remove any existing entries." n + "%%" n + "%% @spec init_per_suite(Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + (erlang-skel-separator-end 2) + "init_per_suite(Config) ->" n > + "Config." n n + + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Cleanup after the whole suite" n + "%%" n + "%% Config - [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%%" n + "%% @spec end_per_suite(Config) -> _" n + (erlang-skel-separator-end 2) + "end_per_suite(_Config) ->" n > + "ok." n n + + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Initialization before each test case group." n + "%%" n + "%% GroupName = atom()" n + "%% Name of the test case group that is about to run." n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding configuration data for the group." n + "%% Reason = term()" n + "%% The reason for skipping all test cases and subgroups in the group." n + "%%" n + "%% @spec init_per_group(GroupName, Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + (erlang-skel-separator-end 2) + "init_per_group(_GroupName, Config) ->" n > + "Config." n n + + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Cleanup after each test case group." n + "%%" n + "%% GroupName = atom()" n + "%% Name of the test case group that is finished." n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding configuration data for the group." n + "%%" n + "%% @spec end_per_group(GroupName, Config0) ->" n + "%% void() | {save_config,Config1}" n + (erlang-skel-separator-end 2) + "end_per_group(_GroupName, _Config) ->" n > + "ok." n n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Initialization before each test case" n + "%%" n + "%% TestCase - atom()" n + "%% Name of the test case that is about to be run." n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Reason = term()" n + "%% The reason for skipping the test case." n + "%%" n + "%% Note: This function is free to add any key/value pairs to the Config" n + "%% variable, but should NOT alter/remove any existing entries." n + "%%" n + "%% @spec init_per_testcase(TestCase, Config0) ->" n + "%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}" n + (erlang-skel-separator-end 2) + "init_per_testcase(_TestCase, Config) ->" n > + "Config." n n + + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Cleanup after each test case" n + "%%" n + "%% TestCase - atom()" n + "%% Name of the test case that is finished." n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%%" n + "%% @spec end_per_testcase(TestCase, Config0) ->" n + "%% void() | {save_config,Config1} | {fail,Reason}" n + (erlang-skel-separator-end 2) + "end_per_testcase(_TestCase, _Config) ->" n > + "ok." n n + + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Returns a list of test case group definitions." n + "%%" n + "%% Group = {GroupName,Properties,GroupsAndTestCases}" n + "%% GroupName = atom()" n + "%% The name of the group." n + "%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]" n + "%% Group properties that may be combined." n + "%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]" n + "%% TestCase = atom()" n + "%% The name of a test case." n + "%% Shuffle = shuffle | {shuffle,Seed}" n + "%% To get cases executed in random order." n + "%% Seed = {integer(),integer(),integer()}" n + "%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |" n + "%% repeat_until_any_ok | repeat_until_any_fail" n + "%% To get execution of cases repeated." n + "%% N = integer() | forever" n + "%%" n + "%% @spec: groups() -> [Group]" n + (erlang-skel-separator-end 2) + "groups() ->" n > + "[]." n n + + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Returns the list of groups and test cases that" n + "%% are to be executed." n + "%%" n + "%% GroupsAndTestCases = [{group,GroupName} | TestCase]" n + "%% GroupName = atom()" n + "%% Name of a test case group." n + "%% TestCase = atom()" n + "%% Name of a test case." n + "%% Reason = term()" n + "%% The reason for skipping all groups and test cases." n + "%%" n + "%% @spec all() -> GroupsAndTestCases | {skip,Reason}" n + (erlang-skel-separator-end 2) + "all() -> " n > + "[my_test_case]." n n + + n + (erlang-skel-separator-start 2) + "%% TEST CASES" n + (erlang-skel-separator 2) + n + + (erlang-skel-separator-start 2) + "%% @doc " n + "%% Test case info function - returns list of tuples to set" n + "%% properties for the test case." n + "%%" n + "%% Info = [tuple()]" n + "%% List of key/value pairs." n + "%%" n + "%% Note: This function is only meant to be used to return a list of" n + "%% values, not perform any other operations." n + "%%" n + "%% @spec TestCase() -> Info " n + (erlang-skel-separator-end 2) + "my_test_case() -> " n > + "[]." n n + + (erlang-skel-separator 2) + "%% @doc Test case function. (The name of it must be specified in" n + "%% the all/0 list or in a test case group for the test case" n + "%% to be executed)." n + "%%" n + "%% Config0 = Config1 = [tuple()]" n + "%% A list of key/value pairs, holding the test case configuration." n + "%% Reason = term()" n + "%% The reason for skipping the test case." n + "%% Comment = term()" n + "%% A comment about the test case that will be printed in the html log." n + "%%" n + "%% @spec TestCase(Config0) ->" n + "%% ok | exit() | {skip,Reason} | {comment,Comment} |" n + "%% {save_config,Config1} | {skip_and_save,Reason,Config1}" n + (erlang-skel-separator-end 2) + "my_test_case(_Config) -> " n > + "ok." n + + ) + "*The template of a library module. + Please see the function `tempo-define-template'.") + +;; Skeleton code: + +;; This code is based on the package `tempo' which is part of modern +;; Emacsen. (GNU Emacs 19.25 (?) and XEmacs 19.14.) + +(defun erlang-skel-init () + "Generate the skeleton functions and menu items. +The variable `erlang-skel' contains the name and descriptions of +all skeletons. + +The skeleton routines are based on the `tempo' package. Should this +package not be present, this function does nothing." + (interactive) + (condition-case nil + (require 'tempo) + (error t)) + (if (featurep 'tempo) + (let ((skel erlang-skel) + (menu '())) + (while skel + (cond ((null (car skel)) + (setq menu (cons nil menu))) + (t + (funcall (symbol-function 'tempo-define-template) + (concat "erlang-" (nth 1 (car skel))) + ;; The tempo template used contains an `include' + ;; function call only, hence changes to the + ;; variables describing the templates take effect + ;; immdiately. + (list (list 'erlang-skel-include (nth 2 (car skel)))) + (nth 1 (car skel))) + (setq menu (cons (erlang-skel-make-menu-item + (car skel)) menu)))) + (setq skel (cdr skel))) + (setq erlang-menu-skel-items + (list nil (list "Skeletons" (nreverse menu)))) + (setq erlang-menu-items + (erlang-menu-add-above 'erlang-menu-skel-items + 'erlang-menu-version-items + erlang-menu-items)) + (erlang-menu-init)))) + +(defun erlang-skel-make-menu-item (skel) + (let ((func (intern (concat "tempo-template-erlang-" (nth 1 skel))))) + (cond ((null (nth 3 skel)) + (list (car skel) func)) + (t + (list (car skel) + (list 'lambda '() + '(interactive) + (list 'funcall + (list 'quote (nth 3 skel)) + (list 'quote func)))))))) + +;; Functions designed to be added to the skeleton menu. +;; (Not normally used) +(defun erlang-skel-insert (func) + "Insert skeleton generated by FUNC and goto first tempo mark." + (save-excursion (funcall func)) + (funcall (symbol-function 'tempo-forward-mark))) + +(defun erlang-skel-header (func) + "Insert the header generated by FUNC at the beginning of the buffer." + (goto-char (point-min)) + (save-excursion (funcall func)) + (funcall (symbol-function 'tempo-forward-mark))) + + +;; Functions used inside the skeleton descriptions. +(defun erlang-skel-skip-blank () + (skip-chars-backward " \t") + nil) + +(defun erlang-skel-include (&rest args) + "Include a template inside another template. + +Example of use, assuming that `erlang-skel-func' is defined: + + (defvar foo-skeleton '(\"%%% New function:\" + (erlang-skel-include erlang-skel-func))) + +Technically, this function returns the `tempo' attribute`(l ...)' which +can contain other `tempo' attributes. Please see the function +`tempo-define-template' for a description of the `(l ...)' attribute." + (let ((res '()) + entry) + (while args + (setq entry (car args)) + (while entry + (setq res (cons (car entry) res)) + (setq entry (cdr entry))) + (setq args (cdr args))) + (cons 'l (nreverse res)))) + +(defun erlang-skel-separator (&optional percent) + "Return a comment separator." + (let ((percent (or percent 3))) + (concat (make-string percent ?%) + (make-string (- 70 percent) ?-) + "\n"))) + +(defun erlang-skel-separator-start (&optional percent) + "Return a comment separator or an empty string if separators +are configured off." + (if erlang-skel-use-separators + (erlang-skel-separator percent) + "")) + +(defun erlang-skel-separator-end (&optional percent) + "Return a comment separator to end a function comment block or an +empty string if separators are configured off." + (if erlang-skel-use-separators + (concat "%% @end\n" (erlang-skel-separator percent)) + "")) + +(defun erlang-skel-double-separator (&optional percent) + "Return a double line (equals sign) comment separator." + (let ((percent (or percent 3))) + (concat (make-string percent ?%) + (make-string (- 70 percent) ?=) + "\n"))) + +(defun erlang-skel-double-separator-start (&optional percent) + "Return a double separator or a newline if separators are configured off." + (if erlang-skel-use-separators + (erlang-skel-double-separator percent) + "\n")) + +(defun erlang-skel-double-separator-end (&optional percent) + "Return a double separator or an empty string if separators are +configured off." + (if erlang-skel-use-separators + (erlang-skel-double-separator percent) + "")) + +(defun erlang-skel-dd-mmm-yyyy () + "Return the current date as a string in \"DD Mon YYYY\" form. +The first character of DD is space if the value is less than 10." + (let ((date (current-time-string))) + (format "%2d %s %s" + (string-to-int (substring date 8 10)) + (substring date 4 7) + (substring date -4)))) + +(defun erlang-skel-get-function-name () + (save-excursion + (erlang-beginning-of-function -1) + (erlang-get-function-name))) + +(defun erlang-skel-get-function-args () + (save-excursion + (erlang-beginning-of-function -1) + (erlang-get-function-arguments))) + +;; Local variables: +;; coding: iso-8859-1 +;; End: + +;;; erlang-skels.el ends here diff --git a/emacs.d/elpa/erlang-20141104.17/erlang-start.el b/emacs.d/elpa/erlang-20141104.17/erlang-start.el new file mode 100644 index 0000000..76e0575 --- /dev/null +++ b/emacs.d/elpa/erlang-20141104.17/erlang-start.el @@ -0,0 +1,124 @@ +;; erlang-start.el --- Load this file to initialize the Erlang package. + +;; Copyright (C) 1998 Ericsson Telecom AB + +;; Author: Anders Lindgren +;; Version: 2.3 +;; Keywords: erlang, languages, processes +;; Created: 1996-09-18 +;; Date: 1998-03-16 + +;;; Commentary: + +;; Introduction: +;; ------------ +;; +;; This package provides support for the programming language Erlang. +;; The package provides an editing mode with lots of bells and +;; whistles, compilation support, and it makes it possible for the +;; user to start Erlang shells that run inside Emacs. +;; +;; See the Erlang distribution for full documentation of this package. + +;; Installation: +;; ------------ +;; +;; Place this file in Emacs load path, byte-compile it, and add the +;; following line to the appropriate init file: +;; +;; (require 'erlang-start) +;; +;; The full documentation contains much more extensive description of +;; the installation procedure. + +;; Reporting Bugs: +;; -------------- +;; +;; Please send bug reports to the following email address: +;; support@erlang.ericsson.se +;; +;; Please state as exactly as possible: +;; - Version number of Erlang Mode (see the menu), Emacs, Erlang, +;; and of any other relevant software. +;; - What the expected result was. +;; - What you did, preferably in a repeatable step-by-step form. +;; - A description of the unexpected result. +;; - Relevant pieces of Erlang code causing the problem. +;; - Personal Emacs customisations, if any. +;; +;; Should the Emacs generate an error, please set the emacs variable +;; `debug-on-error' to `t'. Repeat the error and enclose the debug +;; information in your bug-report. +;; +;; To set the variable you can use the following command: +;; M-x set-variable RET debug-on-error RET t RET + +;;; Code: + +;; +;; Declare functions in "erlang.el". +;; + +(autoload 'erlang-mode "erlang" "Major mode for editing Erlang code." t) +(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) + +(autoload 'erlang-compile "erlang" + "Compile Erlang module in current buffer." t) + +(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) + +(autoload 'erlang-find-tag "erlang" + "Like `find-tag'. Capable of retreiving Erlang modules.") +(autoload 'erlang-find-tag-other-window "erlang" + "Like `find-tag-other-window'. Capable of retreiving Erlang modules.") + + +;; +;; Associate files extensions ".erl" and ".hrl" with Erlang mode. +;; + +;;;###autoload +(let ((a '("\\.erl\\'" . erlang-mode)) + (b '("\\.hrl\\'" . erlang-mode))) + (or (assoc (car a) auto-mode-alist) + (setq auto-mode-alist (cons a auto-mode-alist))) + (or (assoc (car b) auto-mode-alist) + (setq auto-mode-alist (cons b auto-mode-alist)))) + +;; +;; 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. +;; + +;;;###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)))) + (setq erl-ext (cdr erl-ext)))) + + +;; +;; The end. +;; + +(provide 'erlang-start) + +;; erlang-start.el ends here. diff --git a/emacs.d/elpa/erlang-20141104.17/erlang.el b/emacs.d/elpa/erlang-20141104.17/erlang.el new file mode 100644 index 0000000..c56759e --- /dev/null +++ b/emacs.d/elpa/erlang-20141104.17/erlang.el @@ -0,0 +1,5753 @@ +;;; erlang.el --- Major modes for editing and running Erlang + +;; Copyright (C) 2004 Free Software Foundation, Inc. +;; Author: Anders Lindgren +;; Keywords: erlang, languages, processes +;; Date: 2011-12-11 + +;; %CopyrightBegin% +;; +;; Copyright Ericsson AB 1996-2014. All Rights Reserved. +;; +;; The contents of this file are subject to the Erlang Public License, +;; Version 1.1, (the "License"); you may not use this file except in +;; compliance with the License. You should have received a copy of the +;; Erlang Public License along with this software. If not, it can be +;; retrieved online at http://www.erlang.org/. +;; +;; Software distributed under the License is distributed on an "AS IS" +;; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +;; the License for the specific language governing rights and limitations +;; under the License. +;; +;; %CopyrightEnd% +;; + +;; Lars Thorsén's modifications of 2000-06-07 included. +;; The original version of this package was written by Robert Virding. +;; +;;; Commentary: + +;; Introduction: +;; ------------ +;; +;; This package provides support for the programming language Erlang. +;; The package provides an editing mode with lots of bells and +;; whistles, compilation support, and it makes it possible for the +;; user to start Erlang shells that run inside Emacs. +;; +;; See the Erlang distribution for full documentation of this package. + +;; Installation: +;; ------------ +;; +;; Place this file in Emacs load path, byte-compile it, and add the +;; following line to the appropriate init file: +;; +;; (require 'erlang-start) +;; +;; The full documentation contains much more extensive description of +;; the installation procedure. + +;; Reporting Bugs: +;; -------------- +;; +;; Please send bug reports to the following email address: +;; erlang-bugs@erlang.org +;; or if you have a patch suggestion to: +;; erlang-patches@erlang.org +;; Please state as exactly as possible: +;; - Version number of Erlang Mode (see the menu), Emacs, Erlang, +;; and of any other relevant software. +;; - What the expected result was. +;; - What you did, preferably in a repeatable step-by-step form. +;; - A description of the unexpected result. +;; - Relevant pieces of Erlang code causing the problem. +;; - Personal Emacs customisations, if any. +;; +;; Should the Emacs generate an error, please set the Emacs variable +;; `debug-on-error' to `t'. Repeat the error and enclose the debug +;; information in your bug-report. +;; +;; To set the variable you can use the following command: +;; M-x set-variable RET debug-on-error RET t RET +;;; Code: + +(eval-when-compile (require 'cl)) + +;; Variables: + +(defconst erlang-version "2.7" + "The version number of Erlang mode.") + +(defvar erlang-root-dir nil + "The directory where the Erlang system is installed. +The name should not contain the 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!") + +(defvar erlang-menu-items '(erlang-menu-base-items + erlang-menu-skel-items + erlang-menu-shell-items + erlang-menu-compile-items + erlang-menu-man-items + erlang-menu-personal-items + erlang-menu-version-items) + "*List of menu item list to combine to create 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 +items. + +Please call the function `erlang-menu-init' after every change to this +variable.") + +(defvar erlang-menu-base-items + '(("Indent" + (("Indent Line" erlang-indent-command) + ("Indent Region " erlang-indent-region + (if erlang-xemacs-p (mark) mark-active)) + ("Indent Clause" erlang-indent-clause) + ("Indent Function" erlang-indent-function) + ("Indent Buffer" erlang-indent-current-buffer))) + ("Edit" + (("Fill Comment" erlang-fill-paragraph) + ("Comment Region" comment-region + (if erlang-xemacs-p (mark) mark-active)) + ("Uncomment Region" erlang-uncomment-region + (if erlang-xemacs-p (mark) mark-active)) + nil + ("Beginning of Function" erlang-beginning-of-function) + ("End of Function" erlang-end-of-function) + ("Mark Function" erlang-mark-function) + nil + ("Beginning of Clause" erlang-beginning-of-clause) + ("End of Clause" erlang-end-of-clause) + ("Mark Clause" erlang-mark-clause) + nil + ("New Clause" erlang-generate-new-clause) + ("Clone Arguments" erlang-clone-arguments) + nil + ("Align Arrows" erlang-align-arrows))) + ("Syntax Highlighting" + (("Level 4" erlang-font-lock-level-4) + ("Level 3" erlang-font-lock-level-3) + ("Level 2" erlang-font-lock-level-2) + ("Level 1" erlang-font-lock-level-1) + ("Off" erlang-font-lock-level-0))) + ("TAGS" + (("Find Tag" find-tag) + ("Find Next Tag" erlang-find-next-tag) + ;("Find Regexp" find-tag-regexp) + ("Complete Word" erlang-complete-tag) + ("Tags Apropos" tags-apropos) + ("Search Files" tags-search)))) + "Description of menu used in Erlang mode. + +This variable must be a list. The elements are either nil representing +a horizontal line or a list with two or three elements. The first is +the name of the menu item, the second is the function to call, or a +submenu, on the same same form as ITEMS. The third optional argument +is an expression which is evaluated every time the menu is displayed. +Should the expression evaluate to nil the menu item is ghosted. + +Example: + '((\"Func1\" function-one) + (\"SubItem\" + ((\"Yellow\" function-yellow) + (\"Blue\" function-blue))) + nil + (\"Region Function\" spook-function midnight-variable)) + +Call the function `erlang-menu-init' after modifying this variable.") + +(defvar erlang-menu-shell-items + '(nil + ("Shell" + (("Start New Shell" erlang-shell) + ("Display Shell" erlang-shell-display)))) + "Description of the Shell menu used by Erlang mode. + +Please see the documentation of `erlang-menu-base-items'.") + +(defvar erlang-menu-compile-items + '(("Compile" + (("Compile Buffer" erlang-compile) + ("Display Result" erlang-compile-display) + ("Next Error" erlang-next-error)))) + "Description of the Compile menu used by Erlang mode. + +Please see the documentation of `erlang-menu-base-items'.") + +(defvar erlang-menu-version-items + '(nil + ("Version" erlang-version)) + "Description of the version menu used in Erlang mode.") + +(defvar erlang-menu-personal-items nil + "Description of personal menu items used in Erlang mode. + +Please see the variable `erlang-menu-base-items' for a description +of the format.") + +(defvar erlang-menu-man-items nil + "The menu containing man pages. + +The format of the menu should be compatible with `erlang-menu-base-items'. +This variable is added to the list of Erlang menus stored in +`erlang-menu-items'.") + +(defvar erlang-menu-skel-items '() + "Description of the menu containing the skeleton entries. +The menu is in the form described by the variable `erlang-menu-base-items'.") + +(defvar erlang-mode-hook nil + "*Functions to run when Erlang mode is activated. + +This hook is used to change the behaviour of Erlang mode. It is +normally used by the user to personalise the programming environment. +When used in a site init file, it could be used to customise Erlang +mode for all users on the system. + +The functions added to this hook are run every time Erlang mode is +started. See also `erlang-load-hook', a hook which is run once, +when Erlang mode is loaded into Emacs, and `erlang-shell-mode-hook' +which is run every time a new inferior Erlang shell is started. + +To use a hook, create an Emacs lisp function to perform your actions +and add the function to the hook by calling `add-hook'. + +The following example binds the key sequence C-c C-c to the command +`erlang-compile' (normally bound to C-c C-k). The example also +activates Font Lock mode to fontify the buffer and adds a menu +containing all functions defined in the current buffer. + +To use the example, copy the following lines to your `~/.emacs' file: + + (add-hook 'erlang-mode-hook 'my-erlang-mode-hook) + + (defun my-erlang-mode-hook () + (local-set-key \"\\C-c\\C-c\" 'erlang-compile) + (if window-system + (progn + (setq font-lock-maximum-decoration t) + (font-lock-mode 1))) + (if (and window-system (fboundp 'imenu-add-to-menubar)) + (imenu-add-to-menubar \"Imenu\")))") + +(defvar erlang-load-hook nil + "*Functions to run when Erlang mode is loaded. + +This hook is used to change the behaviour of Erlang mode. It is +normally used by the user to personalise the programming environment. +When used in a site init file, it could be used to customize Erlang +mode for all users on the system. + +The difference between this hook and `erlang-mode-hook' and +`erlang-shell-mode-hook' is that the functions in this hook +is only called once, when the Erlang mode is loaded into Emacs +the first time. + +Natural actions for the functions added to this hook are actions which +only should be performed once, and actions which should be performed +before starting Erlang mode. For example, a number of variables are +used by Erlang mode before `erlang-mode-hook' is run. + +The following example sets the variable `erlang-root-dir' so that the +manual pages can be retrieved (note that you must set the value of +`erlang-root-dir' to match the location of Erlang on your system): + + (add-hook 'erlang-load-hook 'my-erlang-load-hook) + + (defun my-erlang-load-hook () + (setq erlang-root-dir \"/usr/local/erlang\"))") + +(defvar erlang-new-file-hook nil + "Functions to run when a new Erlang source file is being edited. + +A useful function is `tempo-template-erlang-normal-header'. +\(This function only exists when the `tempo' package is available.)") + +(defvar erlang-check-module-name 'ask + "*Non-nil means check that module name and file name agrees when saving. + +If the value of this variable is the atom `ask', the user is +prompted. If the value is t the source is silently changed.") + +(defvar erlang-electric-commands + '(erlang-electric-comma + erlang-electric-semicolon + erlang-electric-gt) + "*List of activated electric commands. + +The list should contain the electric commands which should be active. +Currently, the available electric commands are: + `erlang-electric-comma' + `erlang-electric-semicolon' + `erlang-electric-gt' + `erlang-electric-newline' + +Should the variable be bound to t, all electric commands +are activated. + +To deactivate all electric commands, set this variable to nil.") + +(defvar erlang-electric-newline-inhibit t + "*Set to non-nil to inhibit newline after electric command. + +This is useful since a lot of people press return after executing an +electric command. + +In order to work, the command must also be in the +list `erlang-electric-newline-inhibit-list'. + +Note that commands in this list are required to set the variable +`erlang-electric-newline-inhibit' to nil when the newline shouldn't be +inhibited.") + +(defvar erlang-electric-newline-inhibit-list + '(erlang-electric-semicolon + erlang-electric-comma + erlang-electric-gt) + "*Commands which can inhibit the next newline.") + +(defvar erlang-electric-semicolon-insert-blank-lines nil + "*Number of blank lines inserted before header, or nil. + +This variable controls the behaviour of `erlang-electric-semicolon' +when a new function header is generated. When nil, no blank line is +inserted between the current line and the new header. When bound to a +number it represents the number of blank lines which should be +inserted.") + +(defvar erlang-electric-semicolon-criteria + '(erlang-next-lines-empty-p + erlang-at-keyword-end-p + erlang-at-end-of-function-p) + "*List of functions controlling `erlang-electric-semicolon'. +The functions in this list are called, in order, whenever a semicolon +is typed. Each function in the list is called with no arguments, +and should return one of the following values: + + nil -- no determination made, continue checking + 'stop -- do not create prototype for next line + (anything else) -- insert prototype, and stop checking + +If every function in the list is called with no determination made, +then no prototype is inserted. + +The test is performed by the function `erlang-test-criteria-list'.") + +(defvar erlang-electric-comma-criteria + '(erlang-stop-when-inside-argument-list + erlang-stop-when-at-guard + erlang-next-lines-empty-p + erlang-at-keyword-end-p + erlang-at-end-of-clause-p + erlang-at-end-of-function-p) + "*List of functions controlling `erlang-electric-comma'. +The functions in this list are called, in order, whenever a comma +is typed. Each function in the list is called with no arguments, +and should return one of the following values: + + nil -- no determination made, continue checking + 'stop -- do not create prototype for next line + (anything else) -- insert prototype, and stop checking + +If every function in the list is called with no determination made, +then no prototype is inserted. + +The test is performed by the function `erlang-test-criteria-list'.") + +(defvar erlang-electric-arrow-criteria + '(erlang-stop-when-in-type-spec + erlang-next-lines-empty-p + erlang-at-end-of-function-p) + "*List of functions controlling the arrow aspect of `erlang-electric-gt'. +The functions in this list are called, in order, whenever a `>' +is typed. Each function in the list is called with no arguments, +and should return one of the following values: + + nil -- no determination made, continue checking + 'stop -- do not create prototype for next line + (anything else) -- insert prototype, and stop checking + +If every function in the list is called with no determination made, +then no prototype is inserted. + +The test is performed by the function `erlang-test-criteria-list'.") + +(defvar erlang-electric-newline-criteria + '(t) + "*List of functions controlling `erlang-electric-newline'. + +The electric newline commands indents the next line. Should the +current line begin with a comment the comment start is copied to +the newly created line. + +The functions in this list are called, in order, whenever a comma +is typed. Each function in the list is called with no arguments, +and should return one of the following values: + + nil -- no determination made, continue checking + 'stop -- do not create prototype for next line + (anything else) -- trigger the electric command. + +If every function in the list is called with no determination made, +then no prototype is inserted. Should the atom t be a member of the +list, it is treated as a function triggering the electric command. + +The test is performed by the function `erlang-test-criteria-list'.") + +(defvar erlang-next-lines-empty-threshold 2 + "*Number of blank lines required to activate an electric command. + +Actually, this value controls the behaviour of the function +`erlang-next-lines-empty-p' which normally is a member of the +criteria lists controlling the electric commands. (Please see +the variables `erlang-electric-semicolon-criteria' and +`erlang-electric-comma-criteria'.) + +The variable is bound to a threshold value, a number, representing the +number of lines which must be empty. + +Setting this variable to zero, electric commands will always be +triggered by `erlang-next-lines-empty-p', unless inhibited by other +rules. + +Should this variable be nil, `erlang-next-lines-empty-p' will never +trigger an electric command. The same effect would be reached if the +function `erlang-next-lines-empty-p' would be removed from the criteria +lists. + +Note that even if `erlang-next-lines-empty-p' should not trigger an +electric command, other functions in the criteria list could.") + +(defvar erlang-new-clause-with-arguments nil + "*Non-nil means that the arguments are cloned when a clause is generated. + +A new function header can be generated by calls to the function +`erlang-generate-new-clause' and by use of the electric semicolon.") + +(defvar erlang-compile-use-outdir t + "*When nil, go to the directory containing source file when compiling. + +This is a workaround for a bug in the `outdir' option of compile. If the +outdir is not in the current load path, Erlang doesn't load the object +module after it has been compiled. + +To activate the workaround, place the following in your `~/.emacs' file: + (setq erlang-compile-use-outdir nil)") + +(defvar erlang-indent-level 4 + "*Indentation of Erlang calls/clauses within blocks.") +(put 'erlang-indent-level 'safe-local-variable 'integerp) + +(defvar erlang-indent-guard 2 + "*Indentation of Erlang guards.") +(put 'erlang-indent-guard 'safe-local-variable 'integerp) + +(defvar erlang-argument-indent 2 + "*Indentation of the first argument in a function call. +When nil, indent to the column after the `(' of the +function.") +(put 'erlang-argument-indent 'safe-local-variable '(lambda (val) (or (null val) (integerp val)))) + +(defvar erlang-tab-always-indent t + "*Non-nil means TAB in Erlang mode should always re-indent the current line, +regardless of where in the line point is when the TAB command is used.") + +(defvar erlang-man-inhibit (eq system-type 'windows-nt) + "Inhibit the creation of the Erlang Manual Pages menu. + +The Windows distribution of Erlang does not include man pages, hence +there is no attempt to create the menu.") + +(defvar erlang-man-dirs + '(("Man - Commands" "/man/man1" t) + ("Man - Modules" "/man/man3" t) + ("Man - Files" "/man/man4" t) + ("Man - Applications" "/man/man6" t)) + "*The man directories displayed in the Erlang menu. + +Each item in the list should be a list with three elements, the first +the name of the menu, the second the directory, and the last a flag. +Should the flag the nil, the directory is absolute, should it be non-nil +the directory is relative to the variable `erlang-root-dir'.") + +(defvar erlang-man-max-menu-size 35 + "*The maximum number of menu items in one menu allowed.") + +(defvar erlang-man-display-function 'erlang-man-display + "*Function used to display man page. + +The function is called with one argument, the name of the file +containing the man page. Use this variable when the default +function, `erlang-man-display', does not work on your system.") + +(defvar erlang-compile-extra-opts '() + "*Additional options to the compilation command. +This is an elisp list of options. Each option can be either: +- an atom +- a dotted pair +- a string +Example: '(bin_opt_info (i . \"/path1/include\") (i . \"/path2/include\"))") + +(defvar erlang-compile-command-function-alist + '((".erl\\'" . inferior-erlang-compute-erl-compile-command) + (".xrl\\'" . inferior-erlang-compute-leex-compile-command) + (".yrl\\'" . inferior-erlang-compute-yecc-compile-command) + ("." . inferior-erlang-compute-erl-compile-command)) + "*Alist of filename patterns vs corresponding compilation functions. +Each element looks like (REGEXP . FUNCTION). Compiling a file whose name +matches REGEXP specifies FUNCTION to use to compute the compilation +command. The FUNCTION will be called with two arguments: module name and +default compilation options, like output directory. The FUNCTION +is expected to return a string.") + +(defvar erlang-leex-compile-opts '() + "*Options to pass to leex when compiling xrl files. +This is an elisp list of options. Each option can be either: +- an atom +- a dotted pair +- a string") + +(defvar erlang-yecc-compile-opts '() + "*Options to pass to yecc when compiling yrl files. +This is an elisp list of options. Each option can be either: +- an atom +- a dotted pair +- a string") + +(eval-and-compile + (defvar erlang-regexp-modern-p + (if (> erlang-emacs-major-version 21) t nil) + "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.")) + +(eval-and-compile + (defconst erlang-atom-quoted-regexp + "'\\(?:[^\\']\\|\\(?:\\\\.\\)\\)*'" + "Regexp describing a single-quoted atom")) + +(eval-and-compile + (defconst erlang-atom-regular-regexp + (if erlang-regexp-modern-p + "\\_<[[:lower:]]\\(?:\\sw\\|\\s_\\)*\\_>" + "\\<[[:lower:]]\\(?:\\sw\\|\\s_\\)*\\>") + "Regexp describing a regular (non-quoted) atom")) + +(eval-and-compile + (defconst erlang-atom-regexp + (concat "\\(" erlang-atom-quoted-regexp "\\|" + erlang-atom-regular-regexp "\\)") + "Regexp describing an Erlang atom.")) + +(eval-and-compile + (defconst erlang-atom-regexp-matches 1 + "Number of regexp parenthesis pairs in `erlang-atom-regexp'. + +This is used to determine parenthesis matches in complex regexps which +contains `erlang-atom-regexp'.")) + + +(eval-and-compile + (defconst erlang-variable-regexp + (if erlang-regexp-modern-p + "\\_<\\([[:upper:]_]\\(?:\\sw\\|\\s_\\)*\\)\\_>" + "\\<\\([[:upper:]_]\\(?:\\sw\\|\\s_\\)*\\)\\>") + "Regexp which should match an Erlang variable. + +The regexp must be surrounded with a pair of regexp parentheses.")) + +(eval-and-compile + (defconst erlang-variable-regexp-matches 1 + "Number of regexp parenthesis pairs in `erlang-variable-regexp'. + +This is used to determine matches in complex regexps which contains +`erlang-variable-regexp'.")) + + +(eval-and-compile + (defun erlang-regexp-opt (strings &optional paren) + "Like `regexp-opt', except if PAREN is `symbols', then the +resulting regexp is surrounded by \\_< and \\_>." + (if (eq paren 'symbols) + (if erlang-regexp-modern-p + (concat "\\_<" (regexp-opt strings t) "\\_>") + (concat "\\<" (regexp-opt strings t) "\\>")) + (regexp-opt strings paren)))) + + +(eval-and-compile + (defvar erlang-keywords + '("after" + "begin" + "catch" + "case" + "cond" + "end" + "fun" + "if" + "let" + "of" + "receive" + "try" + "when") + "Erlang reserved keywords")) + +(eval-and-compile + (defconst erlang-keywords-regexp (erlang-regexp-opt erlang-keywords 'symbols))) + +(eval-and-compile + (defvar erlang-operators + '("and" + "andalso" + "band" + "bnot" + "bor" + "bsl" + "bsr" + "bxor" + "div" + "not" + "or" + "orelse" + "rem" + "xor") + "Erlang operators")) +;; What about these? +;; '+' '-' '*' '/' '>', '>=', '<', '=<', '=:=', '==', '=/=', '/=' + +(eval-and-compile + (defconst erlang-operators-regexp (erlang-regexp-opt erlang-operators 'symbols))) + + +(eval-and-compile + (defvar erlang-guards + '("is_atom" + "is_binary" + "is_bitstring" + "is_boolean" + "is_float" + "is_function" + "is_integer" + "is_list" + "is_map" + "is_number" + "is_pid" + "is_port" + "is_record" + "is_reference" + "is_tuple" + "atom" + "binary" + "bitstring" + "boolean" + ;;"float" ; Not included to avoid clashes with the bif float/1 + "function" + "integer" + "list" + "number" + "pid" + "port" + "record" + "reference" + "tuple") + "Erlang guards")) + +(eval-and-compile + (defconst erlang-guards-regexp (erlang-regexp-opt erlang-guards 'symbols))) + +(eval-and-compile + (defvar erlang-predefined-types + '("any" + "arity" + "boolean" + "byte" + "char" + "cons" + "deep_string" + "iodata" + "iolist" + "maybe_improper_list" + "module" + "mfa" + "nil" + "neg_integer" + "none" + "non_neg_integer" + "nonempty_list" + "nonempty_improper_list" + "nonempty_maybe_improper_list" + "nonempty_string" + "no_return" + "pos_integer" + "string" + "term" + "timeout" + "map") + "Erlang type specs types")) + +(eval-and-compile + (defconst erlang-predefined-types-regexp + (erlang-regexp-opt erlang-predefined-types 'symbols))) + + +(eval-and-compile + (defvar erlang-int-bifs + '("abs" + "apply" + "atom_to_binary" + "atom_to_list" + "binary_to_atom" + "binary_to_existing_atom" + "binary_to_float" + "binary_to_integer" + "binary_to_list" + "binary_to_term" + "binary_part" + "bit_size" + "bitsize" + "bitstring_to_list" + "byte_size" + "check_old_code" + "check_process_code" + "date" + "delete_module" + "demonitor" + "disconnect_node" + "element" + "erase" + "error" + "exit" + "float" + "float_to_binary" + "float_to_list" + "garbage_collect" + "get" + "get_keys" + "group_leader" + "halt" + "hd" + "integer_to_list" + "integer_to_binary" + "iolist_size" + "iolist_to_binary" + "is_alive" + "is_atom" + "is_binary" + "is_bitstring" + "is_boolean" + "is_float" + "is_function" + "is_integer" + "is_list" + "is_map" + "is_number" + "is_pid" + "is_port" + "is_process_alive" + "is_record" + "is_reference" + "is_tuple" + "length" + "link" + "list_to_atom" + "list_to_binary" + "list_to_bitstring" + "list_to_existing_atom" + "list_to_float" + "list_to_integer" + "list_to_pid" + "list_to_tuple" + "load_module" + "make_ref" + "map_size" + "max" + "min" + "module_loaded" + "monitor" + "monitor_node" + "node" + "nodes" + "now" + "open_port" + "pid_to_list" + "port_close" + "port_command" + "port_connect" + "port_control" + "pre_loaded" + "process_flag" + "process_info" + "processes" + "purge_module" + "put" + "register" + "registered" + "round" + "self" + "setelement" + "size" + "spawn" + "spawn_link" + "spawn_monitor" + "spawn_opt" + "split_binary" + "statistics" + "term_to_binary" + "time" + "throw" + "tl" + "trunc" + "tuple_size" + "tuple_to_list" + "unlink" + "unregister" + "whereis") + "Erlang built-in functions (BIFs)")) + +(eval-and-compile + (defconst erlang-int-bif-regexp (erlang-regexp-opt erlang-int-bifs 'symbols))) + + +(eval-and-compile + (defvar erlang-ext-bifs + '("adler32" + "adler32_combine" + "alloc_info" + "alloc_sizes" + "append" + "append_element" + "await_proc_exit" + "await_sched_wall_time_modifications" + "bump_reductions" + "call_on_load_function" + "cancel_timer" + "crasher" + "crc32" + "crc32_combine" + "decode_packet" + "delay_trap" + "delete_element" + "dexit" + "dgroup_leader" + "display" + "display_nl" + "display_string" + "dist_exit" + "dlink" + "dmonitor_node" + "dmonitor_p" + "dsend" + "dt_append_vm_tag_data" + "dt_get_tag" + "dt_get_tag_data" + "dt_prepend_vm_tag_data" + "dt_put_tag" + "dt_restore_tag" + "dt_spread_tag" + "dunlink" + "external_size" + "finish_after_on_load" + "finish_loading" + "flush_monitor_message" + "format_cpu_topology" + "fun_info" + "fun_info_mfa" + "fun_to_list" + "function_exported" + "garbage_collect_message_area" + "gather_gc_info_result" + "gather_sched_wall_time_result" + "get_cookie" + "get_module_info" + "get_stacktrace" + "hash" + "hibernate" + "insert_element" + "is_builtin" + "load_nif" + "loaded" + "localtime" + "localtime_to_universaltime" + "make_fun" + "make_tuple" + "match_spec_test" + "md5" + "md5_final" + "md5_init" + "md5_update" + "memory" + "module_info" + "monitor_node" + "nif_error" + "phash" + "phash2" + "port_call" + "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" + "send_nosuspend" + "seq_trace" + "seq_trace_info" + "seq_trace_print" + "set_cookie" + "set_cpu_topology" + "setnode" + "spawn_opt" + "start_timer" + "subtract" + "suspend_process" + "system_flag" + "system_info" + "system_monitor" + "system_profile" + "trace" + "trace_delivered" + "trace_info" + "trace_pattern" + "universaltime" + "universaltime_to_localtime" + "universaltime_to_posixtime" + "yield") + "Erlang built-in functions (BIFs) that needs erlang: prefix")) + +(eval-and-compile + (defconst erlang-ext-bif-regexp + (erlang-regexp-opt (append erlang-int-bifs erlang-ext-bifs) 'symbols))) + + +(defvar erlang-defun-prompt-regexp (concat "^" erlang-atom-regexp "\\s *(") + "Regexp which should match beginning of a clause.") + +(defvar erlang-file-name-extension-regexp "\\.[eh]rl$" + "*Regexp which should match an Erlang file name. + +This regexp is used when an Erlang module name is extracted from the +name of an Erlang source file. + +The regexp should only match the section of the file name which should +be excluded from the module name. + +To match all files set this variable to \"\\\\(\\\\..*\\\\|\\\\)$\". +The matches all except the extension. This is useful if the Erlang +tags system should 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 nil, the inferior shell replaces the window. This is the traditional +behaviour.") + +(defconst inferior-erlang-use-cmm (boundp 'minor-mode-overriding-map-alist) + "Non-nil means use `compilation-minor-mode' in Erlang shell.") + +(defvar erlang-mode-map + (let ((map (make-sparse-keymap))) + (unless (boundp 'indent-line-function) + (define-key map "\t" 'erlang-indent-command)) + (define-key map ";" 'erlang-electric-semicolon) + (define-key map "," 'erlang-electric-comma) + (define-key map "<" 'erlang-electric-lt) + (define-key map ">" 'erlang-electric-gt) + (define-key map "\C-m" 'erlang-electric-newline) + (if (not (boundp 'delete-key-deletes-forward)) + (define-key map "\177" 'backward-delete-char-untabify) + (define-key map [(backspace)] 'backward-delete-char-untabify)) + ;;(unless (boundp 'fill-paragraph-function) + (define-key map "\M-q" 'erlang-fill-paragraph) + (unless (boundp 'beginning-of-defun-function) + (define-key map "\M-\C-a" 'erlang-beginning-of-function) + (define-key map "\M-\C-e" 'erlang-end-of-function) + (define-key map '(meta control h) 'erlang-mark-function)) ; Xemacs + (define-key map "\M-\t" 'erlang-complete-tag) + (define-key map "\C-c\M-\t" 'tempo-complete-tag) + (define-key map "\M-+" 'erlang-find-next-tag) + (define-key map "\C-c\M-a" 'erlang-beginning-of-clause) + (define-key map "\C-c\M-b" 'tempo-backward-mark) + (define-key map "\C-c\M-e" 'erlang-end-of-clause) + (define-key map "\C-c\M-f" 'tempo-forward-mark) + (define-key map "\C-c\M-h" 'erlang-mark-clause) + (define-key map "\C-c\C-c" 'comment-region) + (define-key map "\C-c\C-j" 'erlang-generate-new-clause) + (define-key map "\C-c\C-k" 'erlang-compile) + (define-key map "\C-c\C-l" 'erlang-compile-display) + (define-key map "\C-c\C-s" 'erlang-show-syntactic-information) + (define-key map "\C-c\C-q" 'erlang-indent-function) + (define-key map "\C-c\C-u" 'erlang-uncomment-region) + (define-key map "\C-c\C-y" 'erlang-clone-arguments) + (define-key map "\C-c\C-a" 'erlang-align-arrows) + (define-key map "\C-c\C-z" 'erlang-shell-display) + (unless inferior-erlang-use-cmm + (define-key map "\C-x`" 'erlang-next-error)) + map) + "*Keymap used in Erlang mode.") +(defvar erlang-mode-abbrev-table nil + "Abbrev table in use in Erlang-mode buffers.") +(defvar erlang-mode-syntax-table nil + "Syntax table in use in Erlang-mode buffers.") + + + +(defvar erlang-skel-file "erlang-skels" + "The type of erlang-skeletons that should be used, default + uses edoc type, for the old type, standard comments, + set \"erlang-skels-old\" in your .emacs and restart. + + Or define your own and set the variable to that file.") + +;; Tempo skeleton templates: +(load erlang-skel-file) + +;; Font-lock variables + +;; The next few variables define different Erlang font-lock patterns. +;; They could be appended to form a custom font-lock appearance. +;; +;; The function `erlang-font-lock-set-face' could be used to change +;; the face of a pattern. +;; +;; Note that Erlang strings and atoms are highlighted with using +;; syntactic analysis. + +(defvar erlang-font-lock-keywords-function-header + (list + (list (concat "^" erlang-atom-regexp "\\s-*(") + 1 'font-lock-function-name-face t)) + "Font lock keyword highlighting a function header.") + +(defface erlang-font-lock-exported-function-name-face + '((default (:inherit font-lock-function-name-face))) + "Face used for highlighting exported functions.") + +(defvar erlang-font-lock-exported-function-name-face + 'erlang-font-lock-exported-function-name-face) + +(defvar erlang-inhibit-exported-function-name-face nil + "Inhibit separate face for exported functions") + +(defvar erlang-font-lock-keywords-exported-function-header + (list + (list #'erlang-match-next-exported-function + 1 'erlang-font-lock-exported-function-name-face t)) + "Font lock keyword highlighting an exported function header.") + +(defvar erlang-font-lock-keywords-int-bifs + (list + (list (concat erlang-int-bif-regexp "\\s-*(") + 1 'font-lock-builtin-face)) + "Font lock keyword highlighting built in functions.") + +(defvar erlang-font-lock-keywords-ext-bifs + (list + (list (concat "\\<\\(erlang\\)\\s-*:\\s-*" erlang-ext-bif-regexp "\\s-*(") + '(1 'font-lock-builtin-face) + '(2 'font-lock-builtin-face))) + "Font lock keyword highlighting built in functions.") + +(defvar erlang-font-lock-keywords-int-function-calls + (list + (list (concat erlang-atom-regexp "\\s-*(") + 1 'font-lock-type-face)) + "Font lock keyword highlighting an internal function call.") + +(defvar erlang-font-lock-keywords-ext-function-calls + (list + (list (concat erlang-atom-regexp "\\s-*:\\s-*" + erlang-atom-regexp "\\s-*(") + '(1 'font-lock-type-face) + '(2 'font-lock-type-face))) + "Font lock keyword highlighting an external function call.") + +(defvar erlang-font-lock-keywords-fun-n + (list + (list (concat "\\(" erlang-atom-regexp "/[0-9]+\\)") + 1 'font-lock-type-face)) + "Font lock keyword highlighting a fun descriptor in F/N format.") + +(defvar erlang-font-lock-keywords-operators + (list + (list erlang-operators-regexp + 1 'font-lock-builtin-face)) + "Font lock keyword highlighting Erlang operators.") + +(defvar erlang-font-lock-keywords-dollar + (list + (list "\\(\\$\\([^\\]\\|\\\\\\([^0-7^\n]\\|[0-7]+\\|\\^[a-zA-Z]\\)\\)\\)" + 1 'font-lock-constant-face)) + "Font lock keyword highlighting numbers in ASCII form (e.g. $A).") + +(defvar erlang-font-lock-keywords-arrow + (list + (list "->\\(\\s \\|$\\)" 1 'font-lock-function-name-face)) + "Font lock keyword highlighting clause arrow.") + +(defvar erlang-font-lock-keywords-lc + (list + (list "\\(<-\\|<=\\|||\\)\\(\\s \\|$\\)" 1 'font-lock-keyword-face)) + "Font lock keyword highlighting list comprehension operators.") + +(defvar erlang-font-lock-keywords-keywords + (list + (list erlang-keywords-regexp 1 'font-lock-keyword-face)) + "Font lock keyword highlighting Erlang keywords.") + +(defvar erlang-font-lock-keywords-attr + (list + (list (concat "^\\(-" erlang-atom-regexp "\\)\\(\\s-\\|\\.\\|(\\)") + 1 (if (boundp 'font-lock-preprocessor-face) + 'font-lock-preprocessor-face + 'font-lock-constant-face))) + "Font lock keyword highlighting attributes.") + +(defvar erlang-font-lock-keywords-quotes + (list + (list "`\\([-+a-zA-Z0-9_:*][-+a-zA-Z0-9_:*]+\\)'" + 1 + 'font-lock-keyword-face + t)) + "Font lock keyword highlighting words in single quotes in comments. + +This is not the highlighting of Erlang strings and atoms, which +are highlighted by syntactic analysis.") + +(defvar erlang-font-lock-keywords-guards + (list + (list (concat "[^:]" erlang-guards-regexp "\\s-*(") + 1 'font-lock-builtin-face)) + "Font lock keyword highlighting guards.") + +(defvar erlang-font-lock-keywords-predefined-types + (list + (list (concat "[^:]" erlang-predefined-types-regexp "\\s-*(") + 1 'font-lock-builtin-face)) + "Font lock keyword highlighting predefined types.") + + +(defvar erlang-font-lock-keywords-macros + (list + (list (concat "?\\s-*\\(" erlang-atom-regexp + "\\|" erlang-variable-regexp "\\)") + 1 'font-lock-constant-face) + (list (concat "^\\(-\\(?:define\\|ifn?def\\)\\)\\s-*(\\s-*\\(" erlang-atom-regexp + "\\|" erlang-variable-regexp "\\)") + (if (boundp 'font-lock-preprocessor-face) + (list 1 'font-lock-preprocessor-face t) + (list 1 'font-lock-constant-face t)) + (list 3 'font-lock-type-face t t)) + (list "^-e\\(lse\\|ndif\\)\\>" 0 'font-lock-preprocessor-face t)) + "Font lock keyword highlighting macros. +This must be placed in front of `erlang-font-lock-keywords-vars'.") + +(defvar erlang-font-lock-keywords-records + (list + (list (concat "#\\s *" erlang-atom-regexp) + 1 'font-lock-type-face) + ;; Don't highlight numerical constants. + (list (if erlang-regexp-modern-p + "\\_<[0-9]+#\\([0-9a-zA-Z]+\\)" + "\\<[0-9]+#\\([0-9a-zA-Z]+\\)") + 1 nil t) + (list (concat "^-record\\s-*(\\s-*" erlang-atom-regexp) + 1 'font-lock-type-face)) + "Font lock keyword highlighting Erlang records. +This must be placed in front of `erlang-font-lock-keywords-vars'.") + +(defvar erlang-font-lock-keywords-vars + (list + (list (concat "[^#]" erlang-variable-regexp) ; no numerical constants + 1 'font-lock-variable-name-face)) + "Font lock keyword highlighting Erlang variables. +Must be preceded by `erlang-font-lock-keywords-macros' to work properly.") + +(defvar erlang-font-lock-descr-string + "Font-lock keywords used by Erlang Mode. + +There exists three levels of Font Lock keywords for Erlang: + `erlang-font-lock-keywords-1' - Function headers and reserved keywords. + `erlang-font-lock-keywords-2' - Bifs, guards and `single quotes'. + `erlang-font-lock-keywords-3' - Variables, macros and records. + `erlang-font-lock-keywords-4' - Exported functions, Function names, + Funs, LCs (not Atoms). + +To use a specific level, please set the variable +`font-lock-maximum-decoration' to the appropriate level. Note that the +variable must be set before Erlang mode is activated. + +Example: + (setq font-lock-maximum-decoration 2)") + +(defvar erlang-font-lock-keywords-1 + (append erlang-font-lock-keywords-function-header + erlang-font-lock-keywords-dollar + erlang-font-lock-keywords-arrow + erlang-font-lock-keywords-keywords + ) + ;; DocStringOrig: erlang-font-lock-keywords + erlang-font-lock-descr-string) + +(defvar erlang-font-lock-keywords-2 + (append erlang-font-lock-keywords-1 + erlang-font-lock-keywords-int-bifs + erlang-font-lock-keywords-ext-bifs + erlang-font-lock-keywords-attr + erlang-font-lock-keywords-quotes + erlang-font-lock-keywords-guards + ) + ;; DocStringCopy: erlang-font-lock-keywords + erlang-font-lock-descr-string) + +(defvar erlang-font-lock-keywords-3 + (append erlang-font-lock-keywords-2 + erlang-font-lock-keywords-operators + erlang-font-lock-keywords-macros + erlang-font-lock-keywords-records + erlang-font-lock-keywords-vars + erlang-font-lock-keywords-predefined-types + ) + ;; DocStringCopy: erlang-font-lock-keywords + erlang-font-lock-descr-string) + +(defvar erlang-font-lock-keywords-4 + (append erlang-font-lock-keywords-3 + erlang-font-lock-keywords-exported-function-header + erlang-font-lock-keywords-int-function-calls + erlang-font-lock-keywords-ext-function-calls + erlang-font-lock-keywords-fun-n + erlang-font-lock-keywords-lc + ) + ;; DocStringCopy: erlang-font-lock-keywords + erlang-font-lock-descr-string) + +(defvar erlang-font-lock-keywords erlang-font-lock-keywords-4 + ;; DocStringCopy: erlang-font-lock-keywords + erlang-font-lock-descr-string) + +(defvar erlang-font-lock-syntax-table nil + "Syntax table used by Font Lock mode. + +The difference between this and the standard Erlang Mode +syntax table is that `_' is treated as part of words by +this syntax table. + +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.") + + +;;; 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 + (defvar comment-indent-hook) + (defvar dabbrev-case-fold-search) + (defvar tempo-match-finder) + (defvar compilation-menu-map) + (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)))) + + +(defun erlang-version () + "Return the current version of Erlang mode." + (interactive) + (if (interactive-p) + (message "Erlang mode version %s, written by Anders Lindgren" + erlang-version)) + erlang-version) + + +;;;###autoload +(defun erlang-mode () + "Major mode for editing Erlang source files in Emacs. +It knows about syntax and comment, it can indent code, it is capable +of fontifying the source file, the TAGS commands are aware of Erlang +modules, and the Erlang man pages can be accessed. + +Should this module, \"erlang.el\", be installed properly, Erlang mode +is activated whenever an Erlang source or header file is loaded into +Emacs. To indicate this, the mode line should contain the word +\"Erlang\". + +The main feature of Erlang mode is indentation, press TAB and the +current line will be indented correctly. + +Comments starting with only one `%' are indented to the column stored +in the variable `comment-column'. Comments starting with two `%':s +are indented with the same indentation as code. Comments starting +with at least three `%':s are indented to the first column. + +However, Erlang mode contains much more, this is a list of the most +useful commands: + TAB - Indent the line. + C-c C-q - Indent current function. + M-; - Create a comment at the end of the line. + M-q - Fill a comment, i.e. wrap lines so that they (hopefully) + will look better. + M-a - Goto the beginning of an Erlang clause. + M-C-a - Ditto for function. + M-e - Goto the end of an Erlang clause. + M-C-e - Ditto for function. + M-h - Mark current Erlang clause. + M-C-h - Ditto for function. + C-c C-z - Start, or switch to, an inferior Erlang shell. + C-c C-k - Compile current file. + C-x ` - Next error. + , - Electric comma. + ; - Electric semicolon. + +Erlang mode check the name of the file against the module name when +saving, whenever a mismatch occurs Erlang mode offers to modify the +source. + +The variable `erlang-electric-commands' controls the electric +commands. To deactivate all of them, set it to nil. + +There exists a large number of commands and variables in the Erlang +module. Please press `M-x apropos RET erlang RET' to see a complete +list. Press `C-h f name-of-function RET' and `C-h v name-of-variable +RET'to see the full description of functions and variables, +respectively. + +On entry to this mode the contents of the hook `erlang-mode-hook' is +executed. + +Please see the beginning of the file `erlang.el' for more information +and examples of hooks. + +Other commands: +\\{erlang-mode-map}" + (interactive) + (kill-all-local-variables) + (setq major-mode 'erlang-mode) + (setq mode-name "Erlang") + (erlang-syntax-table-init) + (use-local-map erlang-mode-map) + (erlang-electric-init) + (erlang-menu-init) + (erlang-mode-variables) + (erlang-check-module-name-init) + (erlang-man-init) + (erlang-tags-init) + (erlang-font-lock-init) + (erlang-skel-init) + (tempo-use-tag-list 'erlang-tempo-tags) + (run-hooks 'erlang-mode-hook) + (if (zerop (buffer-size)) + (run-hooks 'erlang-new-file-hook)) + ;; Doesn't exist in Emacs v21.4; required by Emacs v23. + (if (boundp 'after-change-major-mode-hook) + (run-hooks 'after-change-major-mode-hook))) + +;;;###autoload +(dolist (r '("\\.erl$" "\\.app\\.src$" "\\.escript" + "\\.hrl$" "\\.xrl$" "\\.yrl" "/ebin/.+\\.app")) + (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))) + + (set-syntax-table erlang-mode-syntax-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) + '(erlang-electric-semicolon + erlang-electric-comma + erlang-electric-gt)) + + (put 'bitsyntax-open-outer 'syntax-table '(4 . ?>)) + (put 'bitsyntax-open-outer 'rear-nonsticky '(category)) + (put 'bitsyntax-open-inner 'rear-nonsticky '(category)) + (put 'bitsyntax-close-inner 'rear-nonsticky '(category)) + (put 'bitsyntax-close-outer 'syntax-table '(5 . ?<)) + (put 'bitsyntax-close-outer 'rear-nonsticky '(category)) + (make-local-variable 'parse-sexp-lookup-properties) + (setq parse-sexp-lookup-properties 't)) + + +(defun erlang-mode-variables () + (or erlang-mode-abbrev-table + (define-abbrev-table 'erlang-mode-abbrev-table ())) + (setq local-abbrev-table erlang-mode-abbrev-table) + (make-local-variable 'paragraph-start) + (setq paragraph-start (concat "^$\\|" page-delimiter)) + (make-local-variable 'paragraph-separate) + (setq paragraph-separate paragraph-start) + (make-local-variable 'paragraph-ignore-fill-prefix) + (setq paragraph-ignore-fill-prefix t) + (make-local-variable 'require-final-newline) + (setq require-final-newline t) + (make-local-variable 'defun-prompt-regexp) + (setq defun-prompt-regexp erlang-defun-prompt-regexp) + (make-local-variable 'comment-start) + (setq comment-start "%") + (make-local-variable 'comment-start-skip) + (setq comment-start-skip "%+\\s *") + (make-local-variable 'comment-column) + (setq comment-column 48) + (make-local-variable 'indent-line-function) + (setq indent-line-function 'erlang-indent-command) + (make-local-variable 'indent-region-function) + (setq indent-region-function 'erlang-indent-region) + (set (make-local-variable 'comment-indent-function) 'erlang-comment-indent) + (if (<= erlang-emacs-major-version 18) + (set (make-local-variable 'comment-indent-hook) 'erlang-comment-indent)) + (set (make-local-variable 'parse-sexp-ignore-comments) t) + (set (make-local-variable 'dabbrev-case-fold-search) nil) + (set (make-local-variable 'imenu-prev-index-position-function) + 'erlang-beginning-of-function) + (set (make-local-variable 'imenu-extract-index-name-function) + 'erlang-get-function-name-and-arity) + (set (make-local-variable 'tempo-match-finder) + "[^-a-zA-Z0-9_]\\([-a-zA-Z0-9_]*\\)\\=") + (set (make-local-variable 'beginning-of-defun-function) + 'erlang-beginning-of-function) + (set (make-local-variable 'end-of-defun-function) 'erlang-end-of-function) + (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil) + (set (make-local-variable 'fill-paragraph-function) 'erlang-fill-paragraph) + (set (make-local-variable 'comment-add) 1) + (set (make-local-variable 'outline-regexp) "[[:lower:]0-9_]+ *(.*) *-> *$") + (set (make-local-variable 'outline-level) (lambda () 1)) + (set (make-local-variable 'add-log-current-defun-function) + 'erlang-current-defun)) + +(defun erlang-font-lock-init () + "Initialize Font Lock for Erlang mode." + (or erlang-font-lock-syntax-table + (setq erlang-font-lock-syntax-table + (let ((table (copy-syntax-table erlang-mode-syntax-table))) + (modify-syntax-entry ?_ "w" table) + table))) + (set (make-local-variable 'font-lock-syntax-table) + erlang-font-lock-syntax-table) + (set (make-local-variable 'font-lock-beginning-of-syntax-function) + 'erlang-beginning-of-clause) + (make-local-variable 'font-lock-keywords) + (let ((level (cond ((boundp 'font-lock-maximum-decoration) + (symbol-value 'font-lock-maximum-decoration)) + ((boundp 'font-lock-use-maximal-decoration) + (symbol-value 'font-lock-use-maximal-decoration)) + (t nil)))) + (if (consp level) + (setq level (cdr-safe (or (assq 'erlang-mode level) + (assq t level))))) + ;; `level' can here be: + ;; A number - The fontification level + ;; nil - Use the default + ;; t - Use maximum + (cond ((eq level nil) + (set 'font-lock-keywords erlang-font-lock-keywords)) + ((eq level 1) + (set 'font-lock-keywords erlang-font-lock-keywords-1)) + ((eq level 2) + (set 'font-lock-keywords erlang-font-lock-keywords-2)) + ((eq level 3) + (set 'font-lock-keywords erlang-font-lock-keywords-3)) + (t + (set 'font-lock-keywords erlang-font-lock-keywords-4)))) + + ;; Modern font-locks can handle the above much more elegantly: + (set (make-local-variable 'font-lock-defaults) + '((erlang-font-lock-keywords erlang-font-lock-keywords-1 + erlang-font-lock-keywords-2 + erlang-font-lock-keywords-3 + erlang-font-lock-keywords-4) + nil nil ((?_ . "w")) erlang-beginning-of-clause + (font-lock-mark-block-function . erlang-mark-clause) + (font-lock-syntactic-keywords + ;; A dollar sign right before the double quote that ends a + ;; string is not a character escape. + ;; + ;; And a "string" consists of a double quote not escaped by a + ;; dollar sign, any number of non-backslash non-newline + ;; characters or escaped backslashes, a dollar sign + ;; (otherwise we wouldn't care) and a double quote. This + ;; doesn't match multi-line strings, but this is probably + ;; the best we can get, since while font-locking we don't + ;; know whether matching started inside a string: limiting + ;; search to a single line keeps things sane. + . (("\\(?:^\\|[^$]\\)\"\\(?:[^\"\n]\\|\\\\\"\\)*\\(\\$\\)\"" 1 "w") + ;; Likewise for atoms + ("\\(?:^\\|[^$]\\)'\\(?:[^'\n]\\|\\\\'\\)*\\(\\$\\)'" 1 "w") + ;; And the dollar sign in $\" or $\' escapes two + ;; characters, not just one. + ("\\(\\$\\)\\\\[\"']" 1 "'")))))) + + + +;; Useful when defining your own keywords. +(defun erlang-font-lock-set-face (ks &rest faces) + "Replace the face components in a list of keywords. + +The first argument, KS, is a list of keywords. The rest of the +arguments are expressions to replace the face information with. The +first expression replaces the face of the first keyword, the second +expression the second keyword etc. + +Should an expression be nil, the face of the corresponding keyword is +not changed. + +Should fewer expressions than keywords be given, the last expression +is used for all remaining keywords. + +Normally, the expressions are just atoms representing the new face. +They could however be more complex, returning different faces in +different situations. + +This function only handles keywords with elements on the forms: + (REGEXP NUMBER FACE) + (REGEXP NUMBER FACE OVERWRITE) + +This could be used when defining your own special font-lock setup, e.g: + +\(setq my-font-lock-keywords + (append erlang-font-lock-keywords-function-header + erlang-font-lock-keywords-dollar + (erlang-font-lock-set-face + erlang-font-lock-keywords-macros 'my-neon-green-face) + (erlang-font-lock-set-face + erlang-font-lock-keywords-lc 'my-deep-red 'my-light-red) + erlang-font-lock-keywords-attr)) + +For a more elaborate example, please see the beginning of the file +`erlang.el'." + (let ((res '())) + (while ks + (let* ((regexp (car (car ks))) + (number (car (cdr (car ks)))) + (new-face (if (and faces (car faces)) + (car faces) + (car (cdr (cdr (car ks)))))) + (overwrite (car (cdr (cdr (cdr (car ks)))))) + (new-keyword (list regexp number new-face))) + (if overwrite (nconc new-keyword (list overwrite))) + (setq res (cons new-keyword res)) + (setq ks (cdr ks)) + (if (and faces (cdr faces)) + (setq faces (cdr faces))))) + (nreverse res))) + + +(defun erlang-font-lock-level-0 () + ;; DocStringOrig: font-cmd + "Unfontify current buffer." + (interactive) + (font-lock-mode 0)) + + +(defun erlang-font-lock-level-1 () + ;; DocStringCopy: font-cmd + "Fontify current buffer at level 1. +This highlights function headers, reserved keywords, strings and comments." + (interactive) + (require 'font-lock) + (set 'font-lock-keywords erlang-font-lock-keywords-1) + (font-lock-mode 1) + (funcall (symbol-function 'font-lock-fontify-buffer))) + + +(defun erlang-font-lock-level-2 () + ;; DocStringCopy: font-cmd + "Fontify current buffer at level 2. +This highlights level 1 features (see `erlang-font-lock-level-1') +plus bifs, guards and `single quotes'." + (interactive) + (require 'font-lock) + (set 'font-lock-keywords erlang-font-lock-keywords-2) + (font-lock-mode 1) + (funcall (symbol-function 'font-lock-fontify-buffer))) + + +(defun erlang-font-lock-level-3 () + ;; DocStringCopy: font-cmd + "Fontify current buffer at level 3. +This highlights level 2 features (see `erlang-font-lock-level-2') +plus variables, macros and records." + (interactive) + (require 'font-lock) + (set 'font-lock-keywords erlang-font-lock-keywords-3) + (font-lock-mode 1) + (funcall (symbol-function 'font-lock-fontify-buffer))) + +(defun erlang-font-lock-level-4 () + ;; DocStringCopy: font-cmd + "Fontify current buffer at level 4. +This highlights level 3 features (see `erlang-font-lock-level-2') +plus variables, macros and records." + (interactive) + (require 'font-lock) + (set 'font-lock-keywords erlang-font-lock-keywords-4) + (font-lock-mode 1) + (funcall (symbol-function 'font-lock-fontify-buffer))) + + +(defun erlang-menu-init () + "Init menus for Erlang mode. + +The variable `erlang-menu-items' contain a description of the Erlang +mode menu. Normally, the list contains atoms, representing variables +bound to pieces of the menu. + +Personal extensions could be added to `erlang-menu-personal-items'. + +This function should be called if any variable describing the +menu configuration is changed." + (erlang-menu-install "Erlang" erlang-menu-items erlang-mode-map t)) + + +(defun erlang-menu-install (name items keymap &optional popup) + "Install a menu in Emacs or XEmacs based on an abstract description. + +NAME is the name of the menu. + +ITEMS is a list. The elements are either nil representing a horizontal +line or a list with two or three elements. The first is the name of +the menu item, the second the function to call, or a submenu, on the +same same form as ITEMS. The third optional element is an expression +which is evaluated every time the menu is displayed. Should the +expression evaluate to nil the menu item is ghosted. + +KEYMAP is the keymap to add to menu to. (When using XEmacs, the menu +will only be visible when this 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. + +Please see the variable `erlang-menu-base-items'." + (cond (erlang-xemacs-p + (let ((menu (erlang-menu-xemacs name items keymap))) + ;; We add the menu to the global menubar. + ;;(funcall (symbol-function 'set-buffer-menubar) + ;; (symbol-value 'current-menubar)) + (funcall (symbol-function 'add-submenu) nil menu) + (setcdr erlang-xemacs-popup-menu (cdr menu)) + (if (and popup (boundp 'mode-popup-menu)) + (funcall (symbol-function 'set) + 'mode-popup-menu erlang-xemacs-popup-menu)))) + ((>= erlang-emacs-major-version 19) + (define-key keymap (vector 'menu-bar (intern name)) + (erlang-menu-make-keymap name items))) + (t nil))) + + +(defun erlang-menu-make-keymap (name items) + "Build a menu for Emacs 19." + (let ((menumap (funcall (symbol-function 'make-sparse-keymap) + name)) + (count 0) + id def first second third) + (setq items (reverse items)) + (while items + ;; Replace any 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 count (+ count 1)) + (setq id (intern (format "separator-%d" count))) + (setq def '("--" . nil))) + ((and (consp second) (eq (car second) 'lambda)) + (setq count (+ count 1)) + (setq id (intern (format "lambda-%d" count))) + (setq def (cons first second))) + ((symbolp second) + (setq id second) + (setq def (cons first second))) + (t + (setq count (+ count 1)) + (setq id (intern (format "submenu-%d" count))) + (setq def (erlang-menu-make-keymap first second)))) + (define-key menumap (vector id) def) + (if third + (put id 'menu-enable third)) + (setq items (cdr items))) + (cons name menumap))) + + +(defun erlang-menu-xemacs (name items &optional keymap) + "Build a menu for XEmacs." + (let ((res '()) + first second third entry) + (while items + ;; Replace any 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. + +The menu ITEMS is updated destructively. + +ALIST is list of pairs where the car is the old function and cdr the new." + (let (first second pair) + (while items + (setq first (car-safe (car items))) + (setq second (car-safe (cdr-safe (car items)))) + (cond ((null first)) + ((symbolp second) + (setq pair (and second (assq second alist))) + (if pair + (setcar (cdr (car items)) (cdr pair)))) + ((and (consp second) (eq (car second) 'lambda))) + (t + (erlang-menu-substitute second alist))) + (setq items (cdr items))))) + + +(defun erlang-menu-add-above (entry above items) + "Add menu ENTRY above menu entry ABOVE in menu ITEMS. +Do nothing if the items already should be in the menu. +Should ABOVE not be in the list, the entry is added at +the bottom of the menu. + +The new menu is returned. No guarantee is given that the original +menu is left unchanged. + +The equality test is performed by `eq'. + +Example: (erlang-menu-add-above 'my-erlang-menu-items + 'erlang-menu-man-items)" + (erlang-menu-add-below entry above items t)) + + +(defun erlang-menu-add-below (entry below items &optional above-p) + "Add menu ENTRY below menu items BELOW in the Erlang menu. +Do nothing if the items already should be in the menu. +Should BELOW not be in the list, items is added at the bottom +of the menu. + +The new menu is returned. No guarantee is given that the original +menu is left unchanged. + +The equality test is performed by `eq'. + +Example: + +\(setq erlang-menu-items + (erlang-menu-add-below 'my-erlang-menu-items + 'erlang-menu-base-items + erlang-menu-items))" + (if (memq entry items) + items ; Return the original menu. + (let ((head '()) + (done nil) + res) + (while (not done) + (cond ((null items) + (setq res (append head (list entry))) + (setq done t)) + ((eq below (car items)) + (setq res + (if above-p + (append head (cons entry items)) + (append head (cons (car items) + (cons entry (cdr items)))))) + (setq done t)) + (t + (setq head (append head (list (car items)))) + (setq items (cdr items))))) + res))) + +(defun erlang-menu-delete (entry items) + "Delete ENTRY from menu ITEMS. + +The new menu is returned. No guarantee is given that the original +menu is left unchanged." + (delq entry items)) + +;; Man code: + +(defun erlang-man-init () + "Add menus containing the manual pages of the Erlang. + +The variable `erlang-man-dirs' contains entries describing +the location of the manual pages." + (interactive) + (if erlang-man-inhibit + () + (setq erlang-menu-man-items + '(nil + ("Man - Function" erlang-man-function))) + (if erlang-man-dirs + (setq erlang-menu-man-items + (append erlang-menu-man-items + (erlang-man-make-top-menu erlang-man-dirs)))) + (setq erlang-menu-items + (erlang-menu-add-above 'erlang-menu-man-items + 'erlang-menu-version-items + erlang-menu-items)) + (erlang-menu-init))) + + +(defun erlang-man-uninstall () + "Remove the man pages from the Erlang mode." + (interactive) + (setq erlang-menu-items + (erlang-menu-delete 'erlang-menu-man-items erlang-menu-items)) + (erlang-menu-init)) + + +;; The man menu is a hierarchal structure, with the manual sections +;; at the top, described by `erlang-man-dirs'. The next level could +;; either be the manual pages if not to many, otherwise it is an index +;; menu whose submenus will contain up to `erlang-man-max-menu-size' +;; manual pages. + +(defun erlang-man-make-top-menu (dir-list) + "Create one menu entry per element of DIR-LIST. +The format is described in the documentation of `erlang-man-dirs'." + (let ((menu '()) + dir) + (while dir-list + (setq dir (cond ((nth 2 (car dir-list)) + ;; Relative to `erlang-root-dir'. + (and (stringp erlang-root-dir) + (concat erlang-root-dir (nth 1 (car dir-list))))) + (t + ;; Absolute + (nth 1 (car dir-list))))) + (if (and dir + (file-readable-p dir)) + (setq menu (cons (list (car (car dir-list)) + (erlang-man-make-middle-menu + (erlang-man-get-files dir))) + menu))) + (setq dir-list (cdr dir-list))) + ;; Should no menus be found, generate a menu item which + ;; will display a help text, when selected. + (if menu + (nreverse menu) + '(("Man Pages" + (("Error! Why?" erlang-man-describe-error))))))) + + +;; Should the menu be to long, let's split it into a number of +;; smaller menus. Warning, this code contains beautiful +;; destructive operations! +(defun erlang-man-make-middle-menu (filelist) + "Create the second level menu from FILELIST. + +Should the list be longer than `erlang-man-max-menu-size', a tree of +menus is created." + (if (<= (length filelist) erlang-man-max-menu-size) + (erlang-man-make-menu filelist) + (let ((menu '()) + (filelist (copy-sequence filelist)) + segment submenu pair) + (while filelist + (setq pair (nthcdr (- erlang-man-max-menu-size 1) filelist)) + (setq segment filelist) + (if (null pair) + (setq filelist nil) + (setq filelist (cdr pair)) + (setcdr pair nil)) + (setq submenu (erlang-man-make-menu segment)) + (setq menu (cons (list (concat (car (car submenu)) + " -- " + (car (car (reverse submenu)))) + submenu) + menu))) + (nreverse menu)))) + + +(defun erlang-man-make-menu (filelist) + "Make a leaf menu based on FILELIST." + (let ((menu '()) + item) + (while filelist + (setq item (erlang-man-make-menu-item (car filelist))) + (if item + (setq menu (cons item menu))) + (setq filelist (cdr filelist))) + (nreverse menu))) + + +(defun erlang-man-make-menu-item (file) + "Create a menu item containing the name of the man page." + (and (string-match ".+/\\([^/]+\\)\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$" file) + (let ((page (substring file (match-beginning 1) (match-end 1)))) + (list (capitalize page) + (list 'lambda '() + '(interactive) + (list 'funcall 'erlang-man-display-function + file)))))) + + +(defun erlang-man-get-files (dir) + "Return files in directory DIR." + (directory-files dir t ".+\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?\\'")) + + +(defun erlang-man-module (&optional module) + "Find manual page for MODULE, defaults to module of function under point. +This function is aware of imported functions." + (interactive + (list (let* ((mod (car-safe (erlang-get-function-under-point))) + (input (read-string + (format "Manual entry for module%s: " + (if (or (null mod) (string= mod "")) + "" + (format " (default %s)" mod)))))) + (if (string= input "") + mod + input)))) + (or module (setq module (car (erlang-get-function-under-point)))) + (if (or (null module) (string= module "")) + (error "No Erlang module name given")) + (let ((dir-list erlang-man-dirs) + (pat (concat "/" (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))) + (if file + (funcall erlang-man-display-function file) + (error "No manual page for module %s found" module)))) + + +;; Warning, the function `erlang-man-function' is a hack! +;; It links itself into the man code in a non-clean way. I have +;; chosen to keep it since it provides a very useful functionality +;; which is not possible to achieve using a clean approach. +;; / AndersL + +(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* ((mod-func (erlang-get-function-under-point)) + (mod (car-safe mod-func)) + (func (nth 1 mod-func)) + (input (read-string + (format + "Manual entry for `module:func' or `module'%s: " + (if (or (null mod) (string= mod "")) + "" + (format " (default %s:%s)" mod func)))))) + (if (string= input "") + (if (and mod func) + (concat mod ":" func) + mod) + input)))) + ;; Emacs 18 doesn't provide `man'... + (condition-case nil + (require 'man) + (error nil)) + (let ((modname nil) + (funcname nil)) + (cond ((null name) + (let ((mod-func (erlang-get-function-under-point))) + (setq modname (car-safe mod-func)) + (setq funcname (nth 1 mod-func)))) + ((string-match ":" name) + (setq modname (substring name 0 (match-beginning 0))) + (setq funcname (substring name (match-end 0) nil))) + ((stringp name) + (setq modname name))) + (if (or (null modname) (string= modname "")) + (error "No Erlang module name given")) + (cond ((fboundp 'Man-notify-when-ready) + ;; Emacs 19: The man command could possibly start an + ;; asynchronous process, i.e. we must hook ourselves into + ;; the system to be activated when the man-process + ;; terminates. + (if (null funcname) + () + (erlang-man-patch-notify) + (setq erlang-man-function-name funcname)) + (condition-case nil + (erlang-man-module modname) + (error (setq erlang-man-function-name nil)))) + (t + (erlang-man-module modname) + (if funcname + (erlang-man-find-function + (or (get-buffer "*Manual Entry*") ; Emacs 18 + (current-buffer)) ; XEmacs + funcname)))))) + + +;; Should the defadvice be at the top level, the package `advice' would +;; be required. Now it is only required when this functionality +;; is used. (Emacs 19 specific.) +(defun erlang-man-patch-notify () + "Patch the function `Man-notify-when-ready' to search for function. +The variable `erlang-man-function-name' is assumed to be bound to +the function name, or to nil. + +The reason for patching a function is that under Emacs 19, the man +command is executed asynchronously." + (condition-case nil + (require 'advice) + ;; This should never happened since this is only called when + ;; running under Emacs 19. + (error (error (concat "This command needs the package `advice', " + "please upgrade your Emacs.")))) + (require 'man) + (defadvice Man-notify-when-ready + (after erlang-Man-notify-when-ready activate) + "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))))))) + + +(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)) + (if file + (let ((process-environment (copy-sequence process-environment))) + (if (string-match "\\(.*\\)/man[^/]*/\\([^.]+\\)\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$" file) + (let ((dir (substring file (match-beginning 1) (match-end 1))) + (page (substring file (match-beginning 2) (match-end 2)))) + (if (fboundp 'setenv) + (setenv "MANPATH" dir) + ;; Emacs 18 + (setq process-environment (cons (concat "MANPATH=" dir) + process-environment))) + (cond ((not (and (not erlang-xemacs-p) + (= erlang-emacs-major-version 19) + (< erlang-emacs-minor-version 29))) + (manual-entry page)) + (t + ;; Emacs 19.28 and earlier versions of 19: + ;; The manual-entry command unconditionally prompts + ;; the user :-( + (funcall (symbol-function 'Man-getpage-in-background) + page)))) + (error "Can't find man page for %s\n" file))))) + + +(defun erlang-man-describe-error () + "Describe why the manual pages weren't found." + (interactive) + (with-output-to-temp-buffer "*Erlang Man Error*" + (princ "Normally, this menu should contain Erlang manual pages. + +In order to find the manual pages, the variable `erlang-root-dir' +should be bound to the name of the directory containing the Erlang +installation. The name should not include the final slash. + +Practically, you should add a line on the following form to +your ~/.emacs, or ask your system administrator to add it to +the site init file: + (setq erlang-root-dir \"/the/erlang/root/dir/goes/here\") + +For example: + (setq erlang-root-dir \"/usr/local/erlang\") + +After installing the line, kill and restart Emacs, or restart Erlang +mode with the command `M-x erlang-mode RET'."))) + +;; Skeleton code: + +;; This code is based on the package `tempo' which is part of modern +;; Emacsen. (GNU Emacs 19.25 (?) and XEmacs 19.14.) + +(defun erlang-skel-init () + "Generate the skeleton functions and menu items. +The variable `erlang-skel' contains the name and descriptions of +all skeletons. + +The skeleton routines are based on the `tempo' package. Should this +package not be present, this function does nothing." + (interactive) + (condition-case nil + (require 'tempo) + (error t)) + (if (featurep 'tempo) + (let ((skel erlang-skel) + (menu '())) + (while skel + (cond ((null (car skel)) + (setq menu (cons nil menu))) + (t + (funcall (symbol-function 'tempo-define-template) + (concat "erlang-" (nth 1 (car skel))) + ;; The tempo template used contains an `include' + ;; function call only, hence changes to the + ;; variables describing the templates take effect + ;; immdiately. + (list (list 'erlang-skel-include (nth 2 (car skel)))) + (nth 1 (car skel)) + (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))))) + (cond ((null (nth 3 skel)) + (list (car skel) func)) + (t + (list (car skel) + (list 'lambda '() + '(interactive) + (list 'funcall + (list 'quote (nth 3 skel)) + (list 'quote func)))))))) + +;; Functions designed to be added to the skeleton menu. +;; (Not normally used) +(defun erlang-skel-insert (func) + "Insert skeleton generated by FUNC and goto first tempo mark." + (save-excursion (funcall func)) + (funcall (symbol-function 'tempo-forward-mark))) + +(defun erlang-skel-header (func) + "Insert the header generated by FUNC at the beginning of the buffer." + (goto-char (point-min)) + (save-excursion (funcall func)) + (funcall (symbol-function 'tempo-forward-mark))) + + +;; Functions used inside the skeleton descriptions. +(defun erlang-skel-skip-blank () + (skip-chars-backward " \t") + nil) + +(defun erlang-skel-include (&rest args) + "Include a template inside another template. + +Example of use, assuming that `erlang-skel-func' is defined: + + (defvar foo-skeleton '(\"%%% New function:\" + (erlang-skel-include erlang-skel-func))) + +Technically, this function returns the `tempo' attribute`(l ...)' which +can contain other `tempo' attributes. Please see the function +`tempo-define-template' for a description of the `(l ...)' attribute." + (let ((res '()) + entry) + (while args + (setq entry (car args)) + (while entry + (setq res (cons (car entry) res)) + (setq entry (cdr entry))) + (setq args (cdr args))) + (cons 'l (nreverse res)))) + +(defvar erlang-skel-separator-length 70) + +(defun erlang-skel-separator (&optional percent) + "Return a comment separator." + (let ((percent (or percent 3))) + (concat (make-string percent ?%) + (make-string (- erlang-skel-separator-length percent) ?-) + "\n"))) + +(defun erlang-skel-double-separator (&optional percent) + "Return a comment separator." + (let ((percent (or percent 3))) + (concat (make-string percent ?%) + (make-string (- erlang-skel-separator-length percent) ?=) + "\n"))) + +(defun erlang-skel-dd-mmm-yyyy () + "Return the current date as a string in \"DD Mon YYYY\" form. +The first character of DD is space if the value is less than 10." + (let ((date (current-time-string))) + (format "%2d %s %s" + (erlang-string-to-int (substring date 8 10)) + (substring date 4 7) + (substring date -4)))) + +;; Indentation code: + +(defun erlang-indent-command (&optional whole-exp) + "Indent current line as Erlang code. +With argument, indent any additional lines of the same clause +rigidly along with this one." + (interactive "P") + (if whole-exp + ;; If arg, always indent this line as Erlang + ;; and shift remaining lines of clause the same amount. + (let ((shift-amt (erlang-indent-line)) + beg end) + (save-excursion + (if erlang-tab-always-indent + (beginning-of-line)) + (setq beg (point)) + (erlang-end-of-clause 1) + (setq end (point)) + (goto-char beg) + (forward-line 1) + (setq beg (point))) + (if (> end beg) + (indent-code-rigidly beg end shift-amt "\n"))) + (if (and (not erlang-tab-always-indent) + (save-excursion + (skip-chars-backward " \t") + (not (bolp)))) + (insert-tab) + (erlang-indent-line)))) + + +(defun erlang-indent-line () + "Indent current line as Erlang code. +Return the amount the indentation changed by." + (let ((pos (- (point-max) (point))) + indent beg + shift-amt) + (beginning-of-line 1) + (setq beg (point)) + (skip-chars-forward " \t") + (cond ((looking-at "%") + (setq indent (funcall comment-indent-function)) + (setq shift-amt (- indent (current-column)))) + (t + (setq indent (erlang-calculate-indent)) + (cond ((null indent) + (setq indent (current-indentation))) + ((eq indent t) + ;; This should never occur here. + (error "Erlang mode error")) + ;;((= (char-syntax (following-char)) ?\)) + ;; (setq indent (1- indent))) + ) + (setq shift-amt (- indent (current-column))))) + (if (zerop shift-amt) + nil + (delete-region beg (point)) + (indent-to indent)) + ;; If initial point was within line's indentation, position + ;; after the indentation. Else stay at same point in text. + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos))) + shift-amt)) + + +(defun erlang-indent-region (beg end) + "Indent region of Erlang code. + +This is automagically called by the user level function `indent-region'." + (interactive "r") + (save-excursion + (let ((case-fold-search nil) + (continue t) + (from-end (- (point-max) end)) + indent-point;; The beginning of the current line + indent;; The indent amount + state) + (goto-char beg) + (beginning-of-line) + (setq indent-point (point)) + (erlang-beginning-of-clause) + ;; Parse the Erlang code from the beginning of the clause to + ;; the beginning of the region. + (while (< (point) indent-point) + (setq state (erlang-partial-parse (point) indent-point state))) + ;; Indent every line in the region + (while continue + (goto-char indent-point) + (skip-chars-forward " \t") + (cond ((looking-at "%") + ;; Do not use our stack to help the user to customize + ;; comment indentation. + (setq indent (funcall comment-indent-function))) + ((looking-at "$") + ;; Don't indent empty lines. + (setq indent 0)) + (t + (setq indent + (save-excursion + (erlang-calculate-stack-indent (point) state))) + (cond ((null indent) + (setq indent (current-indentation))) + ((eq indent t) + ;; This should never occur here. + (error "Erlang mode error")) + ;;((= (char-syntax (following-char)) ?\)) + ;; (setq indent (1- indent))) + ))) + (if (zerop (- indent (current-column))) + nil + (delete-region indent-point (point)) + (indent-to indent)) + ;; Find the next line in the region + (goto-char indent-point) + (save-excursion + (forward-line 1) + (setq indent-point (point))) + (if (>= from-end (- (point-max) indent-point)) + (setq continue nil) + (while (< (point) indent-point) + (setq state (erlang-partial-parse + (point) indent-point state)))))))) + + +(defun erlang-indent-current-buffer () + "Indent current buffer as Erlang code." + (interactive) + (save-excursion + (save-restriction + (widen) + (erlang-indent-region (point-min) (point-max))))) + + +(defun erlang-indent-function () + "Indent current Erlang function." + (interactive) + (save-excursion + (let ((end (progn (erlang-end-of-function 1) (point))) + (beg (progn (erlang-beginning-of-function 1) (point)))) + (erlang-indent-region beg end)))) + + +(defun erlang-indent-clause () + "Indent current Erlang clause." + (interactive) + (save-excursion + (let ((end (progn (erlang-end-of-clause 1) (point))) + (beg (progn (erlang-beginning-of-clause 1) (point)))) + (erlang-indent-region beg end)))) + + +(defmacro erlang-push (x stack) (list 'setq stack (list 'cons x stack))) +(defmacro erlang-pop (stack) (list 'setq stack (list 'cdr stack))) +;; Would much prefer to make caddr a macro but this clashes. +(defun erlang-caddr (x) (car (cdr (cdr x)))) + + +(defun erlang-calculate-indent (&optional parse-start) + "Compute appropriate indentation for current line as Erlang code. +Return nil if line starts inside string, t if in a comment." + (save-excursion + (let ((indent-point (point)) + (case-fold-search nil) + (state nil)) + (if parse-start + (goto-char parse-start) + (erlang-beginning-of-clause)) + (while (< (point) indent-point) + (setq state (erlang-partial-parse (point) indent-point state))) + (erlang-calculate-stack-indent indent-point state)))) + +(defun erlang-show-syntactic-information () + "Show syntactic information for current line." + + (interactive) + + (save-excursion + (let ((starting-point (point)) + (case-fold-search nil) + (state nil)) + (erlang-beginning-of-clause) + (while (< (point) starting-point) + (setq state (erlang-partial-parse (point) starting-point state))) + (message "%S" state)))) + + +(defun erlang-partial-parse (from to &optional state) + "Parse Erlang syntax starting at FROM until TO, with an optional STATE. +Value is list (stack token-start token-type in-what)." + (goto-char from) ; Start at the beginning + (erlang-skip-blank to) + (let ((cs (char-syntax (following-char))) + (stack (car state)) + (token (point)) + in-what) + (cond + + ;; Done: Return previous state. + ((>= token to) + (setq token (nth 1 state)) + (setq cs (nth 2 state)) + (setq in-what (nth 3 state))) + + ;; Word constituent: check and handle keywords. + ((= cs ?w) + (cond ((looking-at "\\(end\\|after\\)[^_a-zA-Z0-9]") + ;; Must pop top icr layer, `after' will push a new + ;; layer next. + (progn + (while (and stack (eq (car (car stack)) '->)) + (erlang-pop stack)) + (if (and stack (memq (car (car stack)) '(icr begin fun try))) + (erlang-pop stack)))) + ((looking-at "catch\\b.*of") + t) + ((looking-at "catch\\b\\s *\\($\\|%\\|.*->\\)") + ;; Must pop top icr layer, `catch' in try/catch + ;;will push a new layer next. + (progn + (while (and stack (eq (car (car stack)) '->)) + (erlang-pop stack)) + (if (and stack (memq (car (car stack)) '(icr begin try))) + (erlang-pop stack)))) + ) + (cond ((looking-at "\\(if\\|case\\|receive\\)[^_a-zA-Z0-9]") + ;; Must push a new icr (if/case/receive) layer. + (erlang-push (list 'icr token (current-column)) stack)) + ((looking-at "\\(try\\|after\\)[^_a-zA-Z0-9]") + ;; Must handle separately, try catch or try X of -> catch + ;; same for `after', it could be + ;; receive after Time -> X end, or + ;; try after X end + (erlang-push (list 'try token (current-column)) stack)) + ((looking-at "\\(of\\)[^_a-zA-Z0-9]") + ;; Must handle separately, try X of -> catch + (if (and stack (eq (car (car stack)) 'try)) + (let ((try-column (nth 2 (car stack))) + (try-pos (nth 1 (car stack)))) + (erlang-pop stack) + (erlang-push (list 'icr try-pos try-column) stack)))) + + ((looking-at "\\(fun\\)[^_a-zA-Z0-9]") + ;; Push a new layer if we are defining a `fun' + ;; expression, not when we are refering an existing + ;; function. 'fun's defines are only indented one level now. + (if (save-excursion + (goto-char (match-end 1)) + (erlang-skip-blank to) + ;; Use erlang-variable-regexp here to look for an + ;; optional variable name to match EEP37 named funs. + (if (looking-at erlang-variable-regexp) + (progn + (goto-char (match-end 0)) + (erlang-skip-blank to))) + (eq (following-char) ?\()) + (erlang-push (list 'fun token (current-column)) stack))) + ((looking-at "\\(begin\\)[^_a-zA-Z0-9]") + (erlang-push (list 'begin token (current-column)) stack)) + ;; Normal when case + ;;((looking-at "when\\s ") + ;;((looking-at "when\\s *\\($\\|%\\)") + ((looking-at "when[^_a-zA-Z0-9]") + (erlang-push (list 'when token (current-column)) stack)) + ((looking-at "catch\\b.*of") + t) + ((looking-at "catch\\b\\s *\\($\\|%\\|.*->\\)") + (erlang-push (list 'icr token (current-column)) stack)) + ;;(erlang-push (list '-> token (current-column)) stack)) + ;;((looking-at "^of$") + ;; (erlang-push (list 'icr token (current-column)) stack) + ;;(erlang-push (list '-> token (current-column)) stack)) + ) + (forward-sexp 1)) + ;; String: Try to skip over it. (Catch error if not complete.) + ((= cs ?\") + (condition-case nil + (progn + (forward-sexp 1) + (if (> (point) to) + (progn + (setq in-what 'string) + (goto-char to)))) + (error + (setq in-what 'string) + (goto-char to)))) + + ;; Expression prefix e.i. $ or ^ (Note ^ can be in the character + ;; literal $^ or part of string and $ outside of a string denotes + ;; a character literal) + ((= cs ?') + (cond + ((= (following-char) ?\") ;; $ or ^ was the last char in a string + (forward-char 1)) + (t + ;; Maybe a character literal, quote the next char to avoid + ;; situations as $" being seen as the begining of a string. + ;; Note the quoting something in the middle of a string is harmless. + (quote (following-char)) + (forward-char 1)))) + + ;; Symbol constituent or punctuation + + ((memq cs '(?. ?_)) + (cond + + ;; Clause end + ((= (following-char) ?\;) + (if (eq (car (car (last stack))) 'spec) + (while (memq (car (car stack)) '(when ::)) + (erlang-pop stack))) + (if (and stack (eq (car (car stack)) '->)) + (erlang-pop stack)) + (forward-char 1)) + + ;; Parameter separator + ((looking-at ",") + (forward-char 1) + (if (and stack (eq (car (car stack)) '::)) + ;; Type or spec + (erlang-pop stack))) + + ;; Function end + ((looking-at "\\.\\(\\s \\|\n\\|\\s<\\)") + (setq stack nil) + (forward-char 1)) + + ;; Function head + ((looking-at "->") + (if (and stack (eq (car (car stack)) 'when)) + (erlang-pop stack)) + (erlang-push (list '-> token (current-column)) stack) + (forward-char 2)) + + ;; List-comprehension divider + ((looking-at "||") + (erlang-push (list '|| token (current-column)) stack) + (forward-char 2)) + + ;; Bit-syntax open paren + ((looking-at "<<") + (erlang-push (list '<< token (current-column)) stack) + (forward-char 2)) + + ;; Bbit-syntax close paren + ((looking-at ">>") + (while (memq (car (car stack)) '(|| ->)) + (erlang-pop stack)) + (cond ((eq (car (car stack)) '<<) + (erlang-pop stack)) + ((memq (car (car stack)) '(icr begin fun)) + (error "Missing `end'")) + (t + (error "Unbalanced parentheses"))) + (forward-char 2)) + + ;; Macro + ((= (following-char) ??) + ;; Skip over the ? + (forward-char 1) + ) + + ;; Type spec's + ((looking-at "-type\\s \\|-opaque\\s ") + (if stack + (forward-char 1) + (erlang-push (list 'icr token (current-column)) stack) + (forward-char 6))) + ((looking-at "-spec\\s ") + (if stack + (forward-char 1) + (forward-char 6) + (skip-chars-forward "^(\n") + (erlang-push (list 'spec (point) (current-column)) stack) + )) + + ;; Type spec delimiter + ((looking-at "::") + (erlang-push (list ':: token (current-column)) stack) + (forward-char 2)) + + ;; Don't follow through in the clause below + ;; '|' don't need spaces around it + ((looking-at "|") + (forward-char 1)) + + ;; Other punctuation: Skip over it and any following punctuation + ((= cs ?.) + ;; Skip over all characters in the operand. + (skip-syntax-forward ".")) + + ;; Other char: Skip over it. + (t + (forward-char 1)))) + + ;; Open parenthesis + ((= cs ?\() + (erlang-push (list '\( token (current-column)) stack) + (forward-char 1)) + + ;; Close parenthesis + ((= cs ?\)) + (while (memq (car (car stack)) '(|| -> :: when)) + (erlang-pop stack)) + (cond ((eq (car (car stack)) '\() + (erlang-pop stack) + (if (and (eq (car (car stack)) 'fun) + (or (eq (car (car (last stack))) 'spec) + (eq (car (car (cdr stack))) '::))) ;; -type() + ;; Inside fun type def ') closes fun definition + (erlang-pop stack))) + ((eq (car (car stack)) 'icr) + (erlang-pop stack) + ;; Normal catch not try-catch might have caused icr + ;; and then incr should be removed and is not an error. + (if (eq (car (car stack)) '\() + (erlang-pop stack) + (error "Missing `end'") + )) + ((eq (car (car stack)) 'begin) + (error "Missing `end'")) + (t + (error "Unbalanced parenthesis")) + ) + (forward-char 1)) + + ;; Character quote: Skip it and the quoted char. + ((= cs ?/) + (forward-char 2)) + + ;; Character escape: Skip it and the escape sequence. + ((= cs ?\\) + (forward-char 1) + (skip-syntax-forward "w")) + + ;; Everything else + (t + (forward-char 1))) + (list stack token cs in-what))) + +(defun erlang-calculate-stack-indent (indent-point state) + "From the given last position and state (stack) calculate indentation. +Return nil if inside string, t if in a comment." + (let* ((stack (and state (car state))) + (token (nth 1 state)) + (stack-top (and stack (car stack)))) + (cond ((null state) ;No state + 0) + ((nth 3 state) + ;; Return nil or t. + (eq (nth 3 state) 'comment)) + ((null stack) + (if (looking-at "when[^_a-zA-Z0-9]") + erlang-indent-guard + 0)) + ((eq (car stack-top) '\() + ;; Element of list, tuple or part of an expression, + (cond ((null erlang-argument-indent) + ;; indent to next column. + (1+ (nth 2 stack-top))) + ((= (char-syntax (following-char)) ?\)) + (goto-char (nth 1 stack-top)) + (cond ((looking-at "[({]\\s *\\($\\|%\\)") + ;; Line ends with parenthesis. + (let ((previous (erlang-indent-find-preceding-expr)) + (stack-pos (nth 2 stack-top))) + (if (>= previous stack-pos) stack-pos + (- (+ previous erlang-argument-indent) 1)))) + (t + (nth 2 stack-top)))) + ((= (following-char) ?,) + ;; a comma at the start of the line: line up with opening parenthesis. + (nth 2 stack-top)) + (t + (goto-char (nth 1 stack-top)) + (let ((base (cond ((looking-at "[({]\\s *\\($\\|%\\)") + ;; Line ends with parenthesis. + (erlang-indent-parenthesis (nth 2 stack-top))) + (t + ;; Indent to the same column as the first + ;; argument. + (goto-char (1+ (nth 1 stack-top))) + (skip-chars-forward " \t") + (current-column))))) + (erlang-indent-standard indent-point token base 't))))) + ;; + ((eq (car stack-top) '<<) + ;; Element of binary (possible comprehension) expression, + (cond ((null erlang-argument-indent) + ;; indent to next column. + (+ 2 (nth 2 stack-top))) + ((looking-at "\\(>>\\)[^_a-zA-Z0-9]") + (nth 2 stack-top)) + (t + (goto-char (nth 1 stack-top)) + ;; Indent to the same column as the first + ;; argument. + (goto-char (+ 2 (nth 1 stack-top))) + (skip-chars-forward " \t") + (current-column)))) + + ((memq (car stack-top) '(icr fun spec)) + ;; The default indentation is the column of the option + ;; directly following the keyword. (This does not apply to + ;; `case'.) Should no option be on the same line, the + ;; indentation is the indentation of the keyword + + ;; `erlang-indent-level'. + ;; + ;; `after' should be indented to the same level as the + ;; corresponding receive. + (cond ((looking-at "\\(after\\|of\\)\\($\\|[^_a-zA-Z0-9]\\)") + (nth 2 stack-top)) + ((looking-at "when[^_a-zA-Z0-9]") + ;; Handling one when part + (+ (nth 2 stack-top) erlang-indent-level erlang-indent-guard)) + (t + (save-excursion + (goto-char (nth 1 stack-top)) + (if (looking-at "case[^_a-zA-Z0-9]") + (+ (nth 2 stack-top) erlang-indent-level) + (skip-chars-forward "a-z") + (skip-chars-forward " \t") + (if (memq (following-char) '(?% ?\n)) + (+ (nth 2 stack-top) erlang-indent-level) + (current-column)))))) + ) + ((and (eq (car stack-top) '||) (looking-at "\\(]\\|>>\\)[^_a-zA-Z0-9]")) + (nth 2 (car (cdr stack)))) + ;; Real indentation, where operators create extra indentation etc. + ((memq (car stack-top) '(-> || try begin)) + (if (looking-at "\\(of\\)[^_a-zA-Z0-9]") + (nth 2 stack-top) + (goto-char (nth 1 stack-top)) + ;; Check if there is more code after the '->' on the + ;; same line. If so use this indentation as base, else + ;; use parent indentation + 2 * level as base. + (let ((off erlang-indent-level) + (skip 2)) + (cond ((null (cdr stack))) ; Top level in function. + ((eq (car stack-top) 'begin) + (setq skip 5)) + ((eq (car stack-top) 'try) + (setq skip 5)) + ((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 + (setq off (* 2 erlang-indent-level)))) ;; ) + (let ((base (erlang-indent-find-base stack indent-point off skip))) + ;; Special cases + (goto-char indent-point) + (cond ((looking-at "\\(end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)") + (if (eq (car stack-top) '->) + (erlang-pop stack)) + (if stack + (erlang-caddr (car stack)) + 0)) + ((looking-at "catch\\b\\($\\|[^_a-zA-Z0-9]\\)") + ;; Are we in a try + (let ((start (if (eq (car stack-top) '->) + (car (cdr stack)) + stack-top))) + (if (null start) nil + (goto-char (nth 1 start))) + (cond ((looking-at "try\\($\\|[^_a-zA-Z0-9]\\)") + (progn + (if (eq (car stack-top) '->) + (erlang-pop stack)) + (if stack + (erlang-caddr (car stack)) + 0))) + (t (erlang-indent-standard indent-point token base 'nil))))) ;; old catch + (t + (erlang-indent-standard indent-point token base 'nil) + )))) + )) + ((eq (car stack-top) 'when) + (goto-char (nth 1 stack-top)) + (if (looking-at "when\\s *\\($\\|%\\)") + (progn + (erlang-pop stack) + (if (and stack (memq (nth 0 (car stack)) '(icr fun))) + (progn + (goto-char (nth 1 (car stack))) + (+ (nth 2 (car stack)) erlang-indent-guard + ;; receive XYZ or receive + ;; XYZ + ;; This if thing does not seem to be needed + ;;(if (looking-at "[a-z]+\\s *\\($\\|%\\)") + ;; erlang-indent-level + ;; (* 2 erlang-indent-level)))) + (* 2 erlang-indent-level))) + ;;erlang-indent-level)) + (+ erlang-indent-level erlang-indent-guard))) + ;; "when" is followed by code, let's indent to the same + ;; column. + (forward-char 4) ; Skip "when" + (skip-chars-forward " \t") + (current-column))) + ;; Type and Spec indentation + ((eq (car stack-top) '::) + (if (looking-at "}") + ;; Closing record definition with types + ;; pop stack and recurse + (erlang-calculate-stack-indent indent-point + (cons (erlang-pop stack) (cdr state))) + (cond ((null erlang-argument-indent) + ;; indent to next column. + (+ 2 (nth 2 stack-top))) + ((looking-at "::[^_a-zA-Z0-9]") + (nth 2 stack-top)) + (t + (let ((start-alternativ (if (looking-at "|") 2 0))) + (goto-char (nth 1 stack-top)) + (- (cond ((looking-at "::\\s *\\($\\|%\\)") + ;; Line ends with :: + (if (eq (car (car (last stack))) 'spec) + (+ (erlang-indent-find-preceding-expr 1) + erlang-argument-indent) + (+ (erlang-indent-find-preceding-expr 2) + erlang-argument-indent))) + (t + ;; Indent to the same column as the first + ;; argument. + (goto-char (+ 2 (nth 1 stack-top))) + (skip-chars-forward " \t") + (current-column))) start-alternativ)))))) + ))) + +(defun erlang-indent-standard (indent-point token base inside-parenthesis) + "Standard indent when in blocks or tuple or arguments. + Look at last thing to see in what state we are, move relative to the base." + (goto-char token) + (cond ((looking-at "||\\|,\\|->\\||") + base) + ((erlang-at-keyword) + (+ (current-column) erlang-indent-level)) + ((or (= (char-syntax (following-char)) ?.) + (erlang-at-operator)) + (+ base erlang-indent-level)) + (t + (goto-char indent-point) + (cond ((memq (following-char) '(?\( )) + ;; Function application. + (+ (erlang-indent-find-preceding-expr) + erlang-argument-indent)) + ;; Empty line, or end; treat it as the end of + ;; the block. (Here we have a choice: should + ;; the user be forced to reindent continued + ;; lines, or should the "end" be reindented?) + + ;; Avoid treating comments a continued line. + ((= (following-char) ?%) + base) + ;; Continued line (e.g. line beginning + ;; with an operator.) + (t + (if (or (erlang-at-operator) (not inside-parenthesis)) + (+ base erlang-indent-level) + base)))))) + +(defun erlang-indent-find-base (stack indent-point &optional offset skip) + "Find the base column for current stack." + (or skip (setq skip 2)) + (or offset (setq offset erlang-indent-level)) + (save-excursion + (let* ((stack-top (car stack))) + (goto-char (nth 1 stack-top)) + (if (< skip (- (point-max) (point))) + (progn + (forward-char skip) + (if (looking-at "\\s *\\($\\|%\\)") + (progn + (if (memq (car stack-top) '(-> ||)) + (erlang-pop stack)) + ;; Take parent identation + offset, + ;; else just erlang-indent-level if no parent + (if stack + (+ (erlang-caddr (car stack)) + offset) + erlang-indent-level)) + (erlang-skip-blank indent-point) + (current-column))) + (+ (current-column) skip))))) + + +;; Does not handle `begin' .. `end'. +(defun erlang-indent-find-preceding-expr (&optional arg) + "Return the first column of the preceding expression. +This assumes that the preceding expression is either simple +\(i.e. an atom) or parenthesized." + (save-excursion + (or arg (setq arg 1)) + (ignore-errors (forward-sexp (- arg))) + (let ((col (current-column))) + (skip-chars-backward " \t") + ;; Special hack to handle: (note line break) + ;; [#myrecord{ + ;; foo = foo}] + ;; where the call (forward-sexp -1) will fail when point is at the `#'. + (or + (ignore-errors + ;; Needed to match the colon in "'foo':'bar'". + (cond ((eq (preceding-char) ?:) + (backward-char 1) + (forward-sexp -1) + (current-column)) + ((eq (preceding-char) ?#) + ;; We may now be at: + ;; - either a construction of a new record + ;; - or update of a record, in which case we want + ;; the column of the expression to be updated. + ;; + ;; To see which of the two cases we are at, we first + ;; move an expression backwards, check for keywords, + ;; then immediately an expression forwards. Moving + ;; backwards skips past tokens like `,' or `->', but + ;; when moving forwards again, we won't skip past such + ;; tokens. We use this: if, after having moved + ;; forwards, we're back where we started, then it was + ;; a record update. + ;; The check for keywords is to detect cases like: + ;; case Something of #record_construction{...} + (backward-char 1) + (let ((record-start (point)) + (record-start-col (current-column))) + (forward-sexp -1) + (let ((preceding-expr-col (current-column)) + ;; white space definition according to erl_scan + (white-space "\000-\040\200-\240")) + (if (erlang-at-keyword) + ;; The (forward-sexp -1) call moved past a keyword + (1+ record-start-col) + (forward-sexp 1) + (skip-chars-forward white-space record-start) + ;; Are we back where we started? If so, it was an update. + (if (= (point) record-start) + preceding-expr-col + (goto-char record-start) + (1+ (current-column))))))) + (t col))) + col)))) + +(defun erlang-indent-parenthesis (stack-position) + (let ((previous (erlang-indent-find-preceding-expr))) + (if (> previous stack-position) + (+ stack-position erlang-argument-indent) + (+ previous erlang-argument-indent)))) + +(defun erlang-skip-blank (&optional lim) + "Skip over whitespace and comments until limit reached." + (or lim (setq lim (point-max))) + (let (stop) + (while (and (not stop) (< (point) lim)) + (cond ((= (following-char) ?%) + (skip-chars-forward "^\n" lim)) + ((= (following-char) ?\n) + (skip-chars-forward "\n" lim)) + ((looking-at "\\s ") + (if (re-search-forward "\\S " lim 'move) + (forward-char -1))) + (t + (setq stop t)))) + stop)) + +(defun erlang-at-keyword () + "Are we looking at an Erlang keyword which will increase indentation?" + (looking-at (concat "\\(when\\|if\\|fun\\|case\\|begin\\|" + "of\\|receive\\|after\\|catch\\|try\\)\\b"))) + +(defun erlang-at-operator () + "Are we looking at an Erlang operator?" + (looking-at + "\\(bnot\\|div\\|mod\\|band\\|bor\\|bxor\\|bsl\\|bsr\\)\\b")) + +(defun erlang-comment-indent () + "Compute Erlang comment indentation. + +Used both by `indent-for-comment' and the Erlang specific indentation +commands." + (cond ((looking-at "%%%") 0) + ((looking-at "%%") + (or (erlang-calculate-indent) + (current-indentation))) + (t + (save-excursion + (skip-chars-backward " \t") + (max (if (bolp) 0 (1+ (current-column))) + comment-column))))) + +;;; Erlang movement commands + +;; All commands below work as movement commands. I.e. if the point is +;; at the end of the clause, and the command `erlang-end-of-clause' is +;; executed, the point is moved to the end of the NEXT clause. (This +;; mimics the behaviour of `end-of-defun'.) +;; +;; Personally I would like to rewrite them to be "pure", and add a set +;; of movement functions, like `erlang-next-clause', +;; `erlang-previous-clause', and the same for functions. +;; +;; The current implementation makes it hopeless to use the functions as +;; subroutines in more complex commands. /andersl + +(defun erlang-beginning-of-clause (&optional arg) + "Move backward to previous start of clause. +With argument, do this that many times. +Return t unless search stops due to end of buffer." + (interactive "p") + (or arg (setq arg 1)) + (if (< arg 0) + ;; Step back to the end of the previous line, unless we are at + ;; the beginning of the buffer. The reason for this move is + ;; that the regexp below includes the last character of the + ;; previous line. + (if (bobp) + (or (looking-at "\n") + (forward-char 1)) + (forward-char -1) + (if (looking-at "\\`\n") + (forward-char 1)))) + ;; The regexp matches a function header that isn't + ;; included in a string. + (and (re-search-forward "\\(\\`\\|\\`\n\\|[^\\]\n\\)\\(-?[a-z]\\|'\\|-\\)" + nil 'move (- arg)) + (let ((beg (match-beginning 2))) + (and beg (goto-char beg)) + t))) + +(defun erlang-end-of-clause (&optional arg) + "Move to the end of the current clause. +With argument, do this that many times." + (interactive "p") + (or arg (setq arg 1)) + (while (and (looking-at "[ \t]*[%\n]") + (zerop (forward-line 1)))) + ;; Move to the next clause. + (erlang-beginning-of-clause (- arg)) + (beginning-of-line);; Just to be sure... + (let ((continue t)) + (while (and (not (bobp)) continue) + (forward-line -1) + (skip-chars-forward " \t") + (if (looking-at "[%\n]") + nil + (end-of-line) + (setq continue nil))))) + +(defun erlang-mark-clause () + "Put mark at end of clause, point at beginning." + (interactive) + (push-mark (point)) + (erlang-end-of-clause 1) + ;; Sets the region. In Emacs 19 and XEmacs, we want to activate + ;; the region. + (condition-case nil + (push-mark (point) nil t) + (error (push-mark (point)))) + (erlang-beginning-of-clause 1) + ;; The above function deactivates the mark. + (if (boundp 'deactivate-mark) + (funcall (symbol-function 'set) 'deactivate-mark nil))) + +(defun erlang-beginning-of-function (&optional arg) + "Move backward to previous start of function. +With positive argument, do this that many times. +With negative argument, search forward. + +Return t unless search stops due to end of buffer." + (interactive "p") + (or arg (setq arg 1)) + (cond + ;; Search backward + ((> arg 0) + (while (and (> arg 0) + (and (erlang-beginning-of-clause 1) + (let ((start (point)) + (name (erlang-name-of-function)) + (arity (erlang-get-function-arity))) + ;; Note: "arity" is nil for e.g. "-import", hence + ;; two "-import" clauses are not considered to + ;; be part of the same function. + (while (and (erlang-beginning-of-clause 1) + (string-equal name + (erlang-name-of-function)) + arity + (equal arity + (erlang-get-function-arity))) + (setq start (point))) + (goto-char start) + t))) + (setq arg (1- arg)))) + ;; Search forward + ((< arg 0) + (end-of-line) + (erlang-beginning-of-clause 1) + ;; Step -arg functions forward. + (while (and (< arg 0) + ;; Step one function forward, or stop if the end of + ;; the buffer was reached. Return t if we found the + ;; function. + (let ((name (erlang-name-of-function)) + (arity (erlang-get-function-arity)) + (found (erlang-beginning-of-clause -1))) + (while (and found + (string-equal name (erlang-name-of-function)) + arity + (equal arity + (erlang-get-function-arity))) + (setq found (erlang-beginning-of-clause -1))) + found)) + (setq arg (1+ arg))))) + (zerop arg)) + + +(defun erlang-end-of-function (&optional arg) + "Move forward to next end of function. + +With argument, do this that many times. +With negative argument go towards the beginning of the buffer." + (interactive "p") + (or arg (setq arg 1)) + (let ((first t)) + ;; Forward + (while (and (> arg 0) (< (point) (point-max))) + (let ((pos (point))) + (while (progn + (if (and first + (progn + (forward-char 1) + (erlang-beginning-of-clause 1))) + nil + (or (bobp) (forward-char -1)) + (erlang-beginning-of-clause -1)) + (setq first nil) + (erlang-pass-over-function) + (skip-chars-forward " \t") + (if (looking-at "[%\n]") + (forward-line 1)) + (<= (point) pos)))) + (setq arg (1- arg))) + ;; Backward + (while (< arg 0) + (let ((pos (point))) + (erlang-beginning-of-clause 1) + (erlang-pass-over-function) + (forward-line 1) + (if (>= (point) pos) + (if (erlang-beginning-of-function 2) + (progn + (erlang-pass-over-function) + (skip-chars-forward " \t") + (if (looking-at "[%\n]") + (forward-line 1))) + (goto-char (point-min))))) + (setq arg (1+ arg))))) + +(eval-and-compile + (if (default-boundp 'beginning-of-defun-function) + (defalias 'erlang-mark-function 'mark-defun) + (defun erlang-mark-function () + "Put mark at end of function, point at beginning." + (interactive) + (push-mark (point)) + (erlang-end-of-function 1) + ;; Sets the region. In Emacs 19 and XEmacs, we want to activate + ;; the region. + (condition-case nil + (push-mark (point) nil t) + (error (push-mark (point)))) + (erlang-beginning-of-function 1) + ;; The above function deactivates the mark. + (if (boundp 'deactivate-mark) + (funcall (symbol-function 'set) 'deactivate-mark nil))))) + +(defun erlang-pass-over-function () + (while (progn + (erlang-skip-blank) + (and (not (looking-at "\\.\\(\\s \\|\n\\|\\s<\\)")) + (not (eobp)))) + (forward-sexp 1)) + (if (not (eobp)) + (forward-char 1))) + +(defun erlang-name-of-function () + (save-excursion + ;; Skip over attribute leader. + (if (looking-at "-[ \t]*") + (re-search-forward "-[ \t]*" nil 'move)) + (let ((start (point))) + (forward-sexp 1) + (buffer-substring start (point))))) + + +;;; Miscellaneous + +(defun erlang-fill-paragraph (&optional justify) + "Like \\[fill-paragraph], but handle Erlang comments. +If any of the current line is a comment, fill the comment or the +paragraph of it that point is in, preserving the comment's indentation +and initial `%':s." + (interactive "P") + (let ((has-comment nil) + ;; If has-comment, the appropriate fill-prefix for the comment. + comment-fill-prefix) + ;; Figure out what kind of comment we are looking at. + (save-excursion + (beginning-of-line) + (cond + ;; Find the command prefix. + ((looking-at (concat "\\s *" comment-start-skip)) + (setq has-comment t) + (setq comment-fill-prefix (buffer-substring (match-beginning 0) + (match-end 0)))) + ;; A line with some code, followed by a comment? Remember that the + ;; % which starts the comment shouldn't be part of a string or + ;; character. + ((progn + (while (not (looking-at "%\\|$")) + (skip-chars-forward "^%\n\"\\\\") + (cond + ((eq (char-after (point)) ?\\) (forward-char 2)) + ((eq (char-after (point)) ?\") (forward-sexp 1)))) + (looking-at comment-start-skip)) + (setq has-comment t) + (setq comment-fill-prefix + (concat (make-string (current-column) ? ) + (buffer-substring (match-beginning 0) (match-end 0))))))) + (if (not has-comment) + (fill-paragraph justify) + ;; Narrow to include only the comment, and then fill the region. + (save-restriction + (narrow-to-region + ;; Find the first line we should include in the region to fill. + (save-excursion + (while (and (zerop (forward-line -1)) + (looking-at "^\\s *%"))) + ;; We may have gone to far. Go forward again. + (or (looking-at "^\\s *%") + (forward-line 1)) + (point)) + ;; Find the beginning of the first line past the region to fill. + (save-excursion + (while (progn (forward-line 1) + (looking-at "^\\s *%"))) + (point))) + ;; Lines with only % on them can be paragraph boundaries. + (let ((paragraph-start (concat paragraph-start "\\|^[ \t%]*$")) + (paragraph-separate (concat paragraph-start "\\|^[ \t%]*$")) + (fill-prefix comment-fill-prefix)) + (fill-paragraph justify)))))) + + +(defun erlang-uncomment-region (beg end) + "Uncomment all commented lines in the region." + (interactive "r") + (uncomment-region beg end)) + + +(defun erlang-generate-new-clause () + "Create additional Erlang clause header. + +Parses the source file for the name of the current Erlang function. +Create the header containing the name, A pair of parentheses, +and an arrow. The space between the function name and the +first parenthesis is preserved. The point is placed between +the parentheses." + (interactive) + (let ((name (save-excursion + (and (erlang-beginning-of-clause) + (erlang-get-function-name t)))) + (arrow (save-excursion + (and (erlang-beginning-of-clause) + (erlang-get-function-arrow))))) + (if (or (null arrow) (null name)) + (error "Can't find name of current Erlang function")) + (if (and (bolp) (eolp)) + nil + (end-of-line) + (newline)) + (insert name) + (save-excursion + (insert ") " arrow)) + (if erlang-new-clause-with-arguments + (erlang-clone-arguments)))) + + +(defun erlang-clone-arguments () + "Insert, at the point, the argument list of the previous clause. + +The mark is set at the beginning of the inserted text, the point +at the end." + (interactive) + (let ((args (save-excursion + (beginning-of-line) + (and (erlang-beginning-of-clause) + (erlang-get-function-arguments)))) + (p (point))) + (if (null args) + (error "Can't clone argument list")) + (insert args) + (set-mark p))) + +;;; Information retrieval functions. + +(defun erlang-buffer-substring (beg end) + "Like `buffer-substring-no-properties'. +Although, this function works on all versions of Emacs." + (if (fboundp 'buffer-substring-no-properties) + (funcall (symbol-function 'buffer-substring-no-properties) beg end) + (buffer-substring beg end))) + + +(defun erlang-get-module () + "Return the name of the module as specified by `-module'. + +Return nil if file contains no `-module' attribute." + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (let ((md (match-data))) + (unwind-protect + (if (re-search-forward + (eval-when-compile + (concat "^-module\\s *(\\s *\\(\\(" + erlang-atom-regexp + "\\)?\\)\\s *)\\s *\\.")) + (point-max) t) + (erlang-remove-quotes + (erlang-buffer-substring (match-beginning 1) + (match-end 1))) + nil) + (store-match-data md)))))) + + +(defun erlang-get-module-from-file-name (&optional file) + "Extract the module name from a file name. + +First, the directory part is removed. Second, the part of the file name +matching `erlang-file-name-extension-regexp' is removed. + +Should the match fail, nil is returned. + +By modifying `erlang-file-name-extension-regexp' to match files other +than Erlang source files, Erlang specific functions could be applied on +non-Erlang files. Most notably; the support for Erlang modules in the +tags system could be used by files written in other languages." + (or file (setq file buffer-file-name)) + (if (null file) + nil + (setq file (file-name-nondirectory file)) + (if (string-match erlang-file-name-extension-regexp file) + (substring file 0 (match-beginning 0)) + nil))) + + +;; Used by `erlang-get-export' and `erlang-get-import'. + +(defun erlang-get-function-arity-list () + "Parse list of `function/arity' as used by `-import' and `-export'. + +Point must be before the opening bracket. When the +function returns the point will be placed after the closing bracket. + +The function does not return an error if the list is incorrectly +formatted. + +Return list of (function . arity). The order of the returned list +corresponds to the order of the parsed Erlang list." + (let ((res '())) + (erlang-skip-blank) + (forward-char 1) + (if (not (eq (preceding-char) ?\[)) + '() ; Not looking at an Erlang list. + (while ; Note: `while' has no body. + (progn + (erlang-skip-blank) + (and (looking-at (eval-when-compile + (concat erlang-atom-regexp "/\\([0-9]+\\)\\>"))) + (progn + (setq res (cons + (cons + (erlang-remove-quotes + (erlang-buffer-substring + (match-beginning 1) (match-end 1))) + (erlang-string-to-int + (erlang-buffer-substring + (match-beginning + (+ 1 erlang-atom-regexp-matches)) + (match-end + (+ 1 erlang-atom-regexp-matches))))) + res)) + (goto-char (match-end 0)) + (erlang-skip-blank) + (forward-char 1) + ;; Test if there are more exported functions. + (eq (preceding-char) ?,)))))) + (nreverse res))) + + +;;; Note that `-export' and the open parenthesis must be written on +;;; the same line. + +(defun erlang-get-export () + "Return a list of `(function . arity)' as specified by `-export'." + (save-excursion + (goto-char (point-min)) + (let ((md (match-data)) + (res '())) + (unwind-protect + (progn + (while (re-search-forward "^-export\\s *(" (point-max) t) + (erlang-skip-blank) + (setq res (nconc res (erlang-get-function-arity-list)))) + res) + (store-match-data md))))) + + +(defun erlang-get-import () + "Parse an Erlang source file for imported functions. + +Return an alist with module name as car part and list of conses containing +function and arity as cdr part." + (save-excursion + (goto-char (point-min)) + (let ((md (match-data)) + (res '())) + (unwind-protect + (progn + (while (re-search-forward "^-import\\s *(" (point-max) t) + (erlang-skip-blank) + (if (looking-at erlang-atom-regexp) + (let ((module (erlang-remove-quotes + (erlang-buffer-substring + (match-beginning 0) + (match-end 0))))) + (goto-char (match-end 0)) + (erlang-skip-blank) + (if (eq (following-char) ?,) + (progn + (forward-char 1) + (erlang-skip-blank) + (let ((funcs (erlang-get-function-arity-list)) + (pair (assoc module res))) + (if pair + (setcdr pair (nconc (cdr pair) funcs)) + (setq res (cons (cons module funcs) + res))))))))) + (nreverse res)) + (store-match-data md))))) + + +(defun erlang-get-function-name (&optional arg) + "Return name of current function, or nil. + +If optional argument is non-nil, everything up to and including +the first `(' is returned. + +Normally used in conjunction with `erlang-beginning-of-clause', e.g.: + (save-excursion + (if (not (eobp)) (forward-char 1)) + (and (erlang-beginning-of-clause) + (erlang-get-function-name t)))" + (let ((n (if arg 0 1))) + (and (looking-at (eval-when-compile + (concat "^" erlang-atom-regexp "\\s *("))) + (erlang-buffer-substring (match-beginning n) (match-end n))))) + + +(defun erlang-get-function-arrow () + "Return arrow of current function, could be \"->\" or nil. + +Normally used in conjunction with `erlang-beginning-of-clause', e.g.: + (save-excursion + (if (not (eobp)) (forward-char 1)) + (and (erlang-beginning-of-clause) + (erlang-get-function-arrow)))" + (and + (save-excursion + (re-search-forward "->" (point-max) t) + (erlang-buffer-substring (- (point) 2) (+ (point) 1))))) + +(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))))) + +(defun erlang-get-function-name-and-arity () + "Return the name and arity of the function at point, or nil. +The return value is a string of the form \"foo/1\"." + (let ((name (erlang-get-function-name)) + (arity (erlang-get-function-arity))) + (and name arity (format "%s/%d" name arity)))) + +(defun erlang-get-function-arguments () + "Return arguments of current function, or nil." + (if (not (looking-at (eval-when-compile + (concat "^" erlang-atom-regexp "\\s *(")))) + nil + (save-excursion + (condition-case nil + (let ((start (match-end 0))) + (goto-char (- start 1)) + (forward-sexp) + (erlang-buffer-substring start (- (point) 1))) + (error nil))))) + + +(defun erlang-get-function-under-point () + "Return the module and function under the point, or nil. + +Should no explicit module name be present at the point, the +list of imported functions is searched. + +The following could be returned: + (\"module\" \"function\") -- Both module and function name found. + (nil \"function\") -- No module name was found. + nil -- No function name found + +In the future the list may contain more elements." + (save-excursion + (let ((md (match-data)) + (res nil)) + (if (eq (char-syntax (following-char)) ? ) + (skip-chars-backward " \t")) + (skip-chars-backward "a-zA-Z0-9_:'") + (cond ((looking-at (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)))) + (setq res (list mod fk))))) + (store-match-data md) + res))) + + +;; TODO: Escape single quotes inside the string without +;; replace-regexp-in-string. +(defun erlang-add-quotes-if-needed (str) + "Return STR, possibly with quotes." + (let ((case-fold-search nil)) ; force string matching to be case sensitive + (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 "'")) + str))) + + +(defun erlang-remove-quotes (str) + "Return STR without quotes, if present." + (let ((md (match-data))) + (prog1 + (if (string-match "\\`'\\(.*\\)'\\'" str) + (substring str 1 -1) + str) + (store-match-data md)))) + +(defun erlang-match-next-exported-function (max) + "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)))))) + +(defun erlang-match-next-function (max) + "Searches forward in current buffer for the next erlang function, +bounded by position MAX." + (re-search-forward erlang-defun-prompt-regexp max 'move-point)) + +(defun erlang-last-match-exported-p () + "Returns non-nil if match-data describes the name and arity of an +exported function." + (save-excursion + (save-match-data + (goto-char (match-beginning 1)) + (erlang-function-exported-p + (erlang-remove-quotes (erlang-get-function-name)) + (erlang-get-function-arity))))) + +(defun erlang-function-exported-p (name arity) + "Returns non-nil if function of name and arity is exported in current buffer." + (save-excursion + (let* ((old-match-data (match-data)) + (exports (erlang-get-export))) + (store-match-data old-match-data) + (member (cons name arity) exports)))) + + +;;; Check module name + +;; The function `write-file', bound to C-x C-w, calls +;; `set-visited-file-name' which clears the hook. :-( +;; To make sure that the hook always is present, we advise +;; `set-visited-file-name'. +(defun erlang-check-module-name-init () + "Initialize the functionality to compare file and module names. + +Unless we have `before-save-hook', we redefine the function +`set-visited-file-name' since it clears the variable +`local-write-file-hooks'. The original function definition is +stored in `erlang-orig-set-visited-file-name'." + (if (boundp 'before-save-hook) + ;; If we have that, `make-local-hook' is obsolete. + (add-hook 'before-save-hook 'erlang-check-module-name nil t) + (require 'advice) + (unless (ad-advised-definition-p 'set-visited-file-name) + (defadvice set-visited-file-name (after erlang-set-visited-file-name + activate) + (if (eq major-mode 'erlang-mode) + (add-hook 'local-write-file-hooks 'erlang-check-module-name)))) + (add-hook 'local-write-file-hooks 'erlang-check-module-name))) + + +(defun erlang-check-module-name () + "If the module name doesn't match file name, ask for permission to change. + +The variable `erlang-check-module-name' controls the behaviour of this +function. It it is nil, this function does nothing. If it is t, the +source is silently changed. If it is set to the atom `ask', the user +is prompted. + +This function is normally placed in the hook `local-write-file-hooks'." + (if erlang-check-module-name + (let ((mn (erlang-add-quotes-if-needed + (erlang-get-module))) + (fn (erlang-add-quotes-if-needed + (erlang-get-module-from-file-name (buffer-file-name))))) + (if (and (stringp mn) (stringp fn)) + (or (string-equal mn fn) + (if (or (eq erlang-check-module-name t) + (y-or-n-p + "Module does not match file name. Modify source? ")) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (if (re-search-forward + (eval-when-compile + (concat "^-module\\s *(\\s *\\(\\(" + erlang-atom-regexp + "\\)?\\)\\s *)\\s *\\.")) + (point-max) t) + (progn + (goto-char (match-beginning 1)) + (delete-region (match-beginning 1) + (match-end 1)) + (insert fn)))))))))) + ;; Must return nil since it is added to `local-write-file-hook'. + nil) + + +;;; Electric functions. + +(defun erlang-electric-semicolon (&optional arg) + "Insert a semicolon character and possibly a prototype for the next line. + +The variable `erlang-electric-semicolon-criteria' states a criterion, +when fulfilled a newline is inserted, the next line is indented and a +prototype for the next line is inserted. Normally the prototype +consists of \" ->\". Should the semicolon end the clause a new clause +header is generated. + +The variable `erlang-electric-semicolon-insert-blank-lines' controls +the number of blank lines inserted between the current line and new +function header. + +Behaves just like the normal semicolon when supplied with a +numerical arg, point is inside string or comment, or when there are +non-whitespace characters following the point on the current line." + (interactive "P") + (self-insert-command (prefix-numeric-value arg)) + (if (or arg + (and (listp erlang-electric-commands) + (not (memq 'erlang-electric-semicolon + erlang-electric-commands))) + (erlang-in-literal) + (not (looking-at "\\s *\\(%.*\\)?$")) + (null (erlang-test-criteria-list + erlang-electric-semicolon-criteria))) + (setq erlang-electric-newline-inhibit nil) + (setq erlang-electric-newline-inhibit t) + (undo-boundary) + (erlang-indent-line) + (end-of-line) + (newline) + (if (condition-case nil + (progn (erlang-indent-line) t) + (error (if (bolp) (delete-backward-char 1)))) + (if (not (bolp)) + (save-excursion + (insert " ->")) + (condition-case nil + (progn + (erlang-generate-new-clause) + (if erlang-electric-semicolon-insert-blank-lines + (save-excursion + (beginning-of-line) + (newline + erlang-electric-semicolon-insert-blank-lines)))) + (error (if (bolp) (delete-backward-char 1)))))))) + + +(defun erlang-electric-comma (&optional arg) + "Insert a comma character and possibly a new indented line. +The variable `erlang-electric-comma-criteria' states a criterion, +when fulfilled a newline is inserted and the next line is indented. + +Behaves just like the normal comma when supplied with a +numerical arg, point is inside string or comment, or when there are +non-whitespace characters following the point on the current line." + (interactive "P") + + (self-insert-command (prefix-numeric-value arg)) + + (if (or arg + (and (listp erlang-electric-commands) + (not (memq 'erlang-electric-comma erlang-electric-commands))) + (erlang-in-literal) + (not (looking-at "\\s *\\(%.*\\)?$")) + (null (erlang-test-criteria-list + erlang-electric-comma-criteria))) + (setq erlang-electric-newline-inhibit nil) + (setq erlang-electric-newline-inhibit t) + (undo-boundary) + (erlang-indent-line) + (end-of-line) + (newline) + (condition-case nil + (erlang-indent-line) + (error (if (bolp) (delete-backward-char 1)))))) + +(defun erlang-electric-lt (&optional arg) + "Insert a less-than sign, and optionally mark it as an open paren." + + (interactive "p") + + (self-insert-command arg) + + ;; Was this the second char in bit-syntax open (`<<')? + (unless (<= (point) 2) + (save-excursion + (backward-char 2) + (when (and (eq (char-after (point)) ?<) + (not (eq (get-text-property (point) 'category) + 'bitsyntax-open-inner))) + ;; Then mark the two chars... + (put-text-property (point) (1+ (point)) + 'category 'bitsyntax-open-outer) + (forward-char 1) + (put-text-property (point) (1+ (point)) + 'category 'bitsyntax-open-inner) + ;;...and unmark any subsequent less-than chars. + (forward-char 1) + (while (eq (char-after (point)) ?<) + (remove-text-properties (point) (1+ (point)) + '(category nil)) + (forward-char 1)))))) + +(defun erlang-after-bitsyntax-close () + "Return t if point is immediately after a bit-syntax close parenthesis (`>>')." + (and (>= (point) 3) + (save-excursion + (backward-char 2) + (and (eq (char-after (point)) ?>) + (not (eq (get-text-property (point) 'category) + 'bitsyntax-close-outer)))))) + +(defun erlang-after-arrow () + "Return true if point is immediately after a function arrow (`->')." + (and (>= (point) 2) + (and + (save-excursion + (backward-char) + (eq (char-before (point)) ?-)) + (or (not (listp erlang-electric-commands)) + (memq 'erlang-electric-gt + erlang-electric-commands)) + (not (erlang-in-literal)) + (looking-at "\\s *\\(%.*\\)?$") + (erlang-test-criteria-list erlang-electric-arrow-criteria)))) + + +(defun erlang-electric-gt (&optional arg) + "Insert a greater-than sign, and optionally mark it as a close paren." + + (interactive "p") + + (self-insert-command arg) + + (cond + ;; Did we just write a bit-syntax close (`>>')? + ((erlang-after-bitsyntax-close) + (save-excursion + ;; Then mark the two chars... + (backward-char 2) + (put-text-property (point) (1+ (point)) + 'category 'bitsyntax-close-inner) + (forward-char) + (put-text-property (point) (1+ (point)) + 'category 'bitsyntax-close-outer) + ;;...and unmark any subsequent greater-than chars. + (forward-char) + (while (eq (char-after (point)) ?>) + (remove-text-properties (point) (1+ (point)) + '(category nil)) + (forward-char)))) + + ;; Did we just write a function arrow (`->')? + ((erlang-after-arrow) + (let ((erlang-electric-newline-inhibit t)) + (undo-boundary) + (end-of-line) + (newline) + (condition-case nil + (erlang-indent-line) + (error (if (bolp) (delete-backward-char 1)))))) + + ;; Then it's just a plain greater-than. + (t + nil))) + + +(defun erlang-electric-arrow\ off (&optional arg) + "Insert a '>'-sign and possibly a new indented line. + +This command is only `electric' when the `>' is part of an `->' arrow. +The variable `erlang-electric-arrow-criteria' states a sequence of +criteria, which decides when a newline should be inserted and the next +line indented. + +It behaves just like the normal greater than sign when supplied with a +numerical arg, point is inside string or comment, or when there are +non-whitespace characters following the point on the current line. + +After being split/merged into `erlang-after-arrow' and +`erlang-electric-gt', it is now unused and disabled." + (interactive "P") + (let ((prec (preceding-char))) + (self-insert-command (prefix-numeric-value arg)) + (if (or arg + (and (listp erlang-electric-commands) + (not (memq 'erlang-electric-arrow + erlang-electric-commands))) + (not (eq prec ?-)) + (erlang-in-literal) + (not (looking-at "\\s *\\(%.*\\)?$")) + (null (erlang-test-criteria-list + erlang-electric-arrow-criteria))) + (setq erlang-electric-newline-inhibit nil) + (setq erlang-electric-newline-inhibit t) + (undo-boundary) + (end-of-line) + (newline) + (condition-case nil + (erlang-indent-line) + (error (if (bolp) (delete-backward-char 1))))))) + + +(defun erlang-electric-newline (&optional arg) + "Break line at point and indent, continuing comment if within one. +The variable `erlang-electric-newline-criteria' states a criterion, +when fulfilled a newline is inserted and the next line is indented. + +Should the current line begin with a comment, and the variable +`comment-multi-line' be non-nil, a new comment start is inserted. + +Should the previous command be another electric command we assume that +the user pressed newline out of old habit, hence we will do nothing." + (interactive "P") + (cond ((and (not arg) + erlang-electric-newline-inhibit + (memq last-command erlang-electric-newline-inhibit-list)) + ()) ; Do nothing! + ((or arg + (and (listp erlang-electric-commands) + (not (memq 'erlang-electric-newline + erlang-electric-commands))) + (null (erlang-test-criteria-list + erlang-electric-newline-criteria))) + (newline (prefix-numeric-value arg))) + (t + (if (and comment-multi-line + (save-excursion + (beginning-of-line) + (looking-at (concat "\\s *" comment-start-skip)))) + (let ((str (buffer-substring + (or (match-end 1) (match-beginning 0)) + (min (match-end 0) (point))))) + (newline) + (undo-boundary) + (insert str)) + (newline) + (undo-boundary) + (indent-according-to-mode))))) + + +(defun erlang-test-criteria-list (criteria) + "Given a list of criterion functions, test if criteria are fulfilled. + +Each element in the criteria list can a function returning nil, t or +the atom `stop'. t means that the criterion is fulfilled, `stop' means +that it isn't fulfilled and that the search should stop, +and nil means continue searching. + +Should the list contain the atom t the criterion is assumed to be +fulfilled, unless preceded by a function returning `stop', of course. + +Should the argument be the atom t instead of a list, the criterion is +assumed to be trivially true. + +Should all functions return nil, the criteria are assumed not to be +fulfilled. + +Return t if criteria fulfilled, nil otherwise." + (if (eq criteria t) + t + (save-excursion + (let ((answer nil)) + (while (and criteria (null answer)) + (if (eq (car criteria) t) + (setq answer t) + (setq answer (funcall (car criteria)))) + (setq criteria (cdr criteria))) + (if (and answer (not (eq answer 'stop))) + t + nil))))) + + +(defun erlang-in-literal (&optional lim) + "Test if point is in string, quoted atom or comment. + +Return one of the three atoms `atom', `string', and `comment'. +Should the point be inside none of the above mentioned types of +context, nil is returned." + (save-excursion + (let* ((lim (or lim (save-excursion + (erlang-beginning-of-clause) + (point)))) + (state (if (fboundp 'syntax-ppss) ; post Emacs 21.3 + (funcall (symbol-function 'syntax-ppss)) + (parse-partial-sexp lim (point))))) + (cond + ((eq (nth 3 state) ?') 'atom) + ((nth 3 state) 'string) + ((nth 4 state) 'comment) + (t nil))))) + + +(defun erlang-at-end-of-function-p () + "Test if point is at end of an Erlang function. + +This function is designed to be a member of a criteria list." + (eq (save-excursion (erlang-skip-blank) (point)) + (save-excursion + (erlang-beginning-of-function -1) (point)))) + + +(defun erlang-at-end-of-clause-p () + "Test if point is at end of an Erlang clause. + +This function is designed to be a member of a criteria list." + (eq (save-excursion (erlang-skip-blank) (point)) + (save-excursion + (erlang-beginning-of-clause -1) (point)))) + + +(defun erlang-stop-when-inside-argument-list () + "Return `stop' if inside parenthesis list, nil otherwise. + +Knows about the list comprehension syntax. When the point is +after `||', `stop' is not returned. + +This function is designed to be a member of a criteria list." + (save-excursion + (condition-case nil + (let ((orig-point (point)) + (state nil)) + (up-list -1) + (if (not (eq (following-char) ?\[)) + 'stop + ;; Do not return `stop' when inside a list comprehension + ;; construction. (The point must be after `||'). + (while (< (point) orig-point) + (setq state (erlang-partial-parse (point) orig-point state))) + (if (and (car state) (eq (car (car (car state))) '||)) + nil + 'stop))) + (error + nil)))) + + +(defun erlang-stop-when-at-guard () + "Return `stop' when at function guards. + +This function is designed to be a member of a criteria list." + (save-excursion + (beginning-of-line) + (if (and (looking-at (eval-when-compile + (concat "^" erlang-atom-regexp "\\s *("))) + (not (looking-at + (eval-when-compile + (concat "^" erlang-atom-regexp ".*->"))))) + 'stop + nil))) + + +(defun erlang-stop-when-in-type-spec () + "Return `stop' when in a type spec line. + +This function is designed to be a member of a criteria list." + (save-excursion + (beginning-of-line) + (when (save-match-data (looking-at "-\\(spec\\|type\\)")) + 'stop))) + + +(defun erlang-next-lines-empty-p () + "Return non-nil if next lines are empty. + +The variable `erlang-next-lines-empty-threshold' contains the number +of lines required to be empty. + +A line containing only spaces and tabs is considered empty. + +This function is designed to be a member of a criteria list." + (and erlang-next-lines-empty-threshold + (save-excursion + (let ((left erlang-next-lines-empty-threshold) + (cont t)) + (while (and cont (> left 0)) + (forward-line 1) + (setq cont (looking-at "\\s *$")) + (setq left (- left 1))) + cont)))) + + +(defun erlang-at-keyword-end-p () + "Test if next readable token is the keyword end. + +This function is designed to be a member of a criteria list." + (save-excursion + (erlang-skip-blank) + (looking-at "end[^_a-zA-Z0-9]"))) + + +;; Erlang tags support which is aware of erlang modules. +;; +;; Not yet implemented under XEmacs. (Hint: The Emacs 19 etags +;; package works under XEmacs.) + +(eval-when-compile + (if (or (featurep 'bytecomp) + (featurep 'byte-compile)) + (progn + (require 'etags)))) + + +;; Variables: + +(defvar erlang-tags-function-alist + '((find-tag . erlang-find-tag) + (find-tag-other-window . erlang-find-tag-other-window) + (find-tag-regexp . erlang-find-tag-regexp) + (find-tag-other-frame . erlang-find-tag-other-frame)) + "Alist of old tags commands and the replacement functions.") + +(defvar erlang-tags-installed nil + "Non-nil when the Erlang tags system is installed.") +(defvar erlang-tags-file-list '() + "List of files in tag list. Used when finding tag on form `module:'.") +(defvar erlang-tags-completion-table nil + "Like `tags-completion-table', this table contains `tag' and `module:tag'.") +(defvar erlang-tags-buffer-installed-p nil + "Non-nil when Erlang module recognising functions installed.") +(defvar erlang-tags-buffer-list '() + "Temporary list of buffers.") +(defvar erlang-tags-orig-completion-table nil + "Temporary storage for `tags-completion-table'.") +(defvar erlang-tags-orig-tag-order nil + "Temporary storage for `find-tag-tag-order'.") +(defvar erlang-tags-orig-regexp-tag-order nil + "Temporary storage for `find-tag-regexp-tag-order'.") +(defvar erlang-tags-orig-search-function nil + "Temporary storage for `find-tag-search-function'.") +(defvar erlang-tags-orig-regexp-search-function nil + "Temporary storage for `find-tag-regexp-search-function'.") +(defvar erlang-tags-orig-format-hooks nil + "Temporary storage for `tags-table-format-hooks'.") ;v19 +(defvar erlang-tags-orig-format-functions nil + "Temporary storage for `tags-table-format-functions'.") ;v > 19 + +(defun erlang-tags-init () + "Install an alternate version of tags, aware of Erlang modules. + +After calling this function, the tags functions are aware of +Erlang modules. Tags can be entered on the for `module:tag' as well +as on the old form `tag'. + +In the completion list, `module:tag' and `module:' shows up. + +Call this function from an appropriate init file, or add it to +Erlang mode hook with the commands: + (add-hook 'erlang-mode-hook 'erlang-tags-init) + (add-hook 'erlang-shell-mode-hook 'erlang-tags-init) + +This function only works under Emacs 18 and Emacs 19. Currently, It +is not implemented under XEmacs. (Hint: The Emacs 19 etags module +works under XEmacs.)" + (interactive) + (cond ((= erlang-emacs-major-version 18) + (require 'tags) + (erlang-tags-define-keys (current-local-map)) + (setq erlang-tags-installed t)) + (t + (require 'etags) + ;; Test on a function available in the Emacs 19 version + ;; of tags but not in the XEmacs version. + (if (not (fboundp 'find-tag-noselect)) + () + (erlang-tags-define-keys (current-local-map)) + (setq erlang-tags-installed t))))) + + +;; Set all keys bound to `find-tag' et.al. in the global map and the +;; menu to `erlang-find-tag' et.al. in `map'. +;; +;; The function `substitute-key-definition' does not work properly +;; in all version of Emacs. + +(defun erlang-tags-define-keys (map) + "Bind tags commands to keymap MAP aware of Erlang modules." + (let ((alist erlang-tags-function-alist)) + (while alist + (let* ((old (car (car alist))) + (new (cdr (car alist))) + (keys (append (where-is-internal old global-map)))) + (while keys + (define-key map (car keys) new) + (setq keys (cdr keys)))) + (setq alist (cdr alist)))) + ;; Update the menu. + (erlang-menu-substitute erlang-menu-base-items erlang-tags-function-alist) + (erlang-menu-init)) + + +;; There exists a variable `find-tag-default-function'. It is not used +;; since `complete-tag' uses it to get current word under point. In that +;; situation we don't want the module to be prepended. + +(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) + "Like `find-tag'. Capable of retrieving Erlang modules. + +Tags can be given on the forms `tag', `module:', `module:tag'." + (interactive (erlang-tag-interactive "Find `module:tag' or `tag': ")) + (switch-to-buffer (erlang-find-tag-noselect modtagname next-p regexp-p)) + t) + + +;; Code mainly from `find-tag-other-window' in `etags.el'. +;;;###autoload +(defun erlang-find-tag-other-window (tagname &optional next-p regexp-p) + "Like `find-tag-other-window' but aware of Erlang modules." + (interactive (erlang-tag-interactive + "Find `module:tag' or `tag' other window: ")) + + ;; This is to deal with the case where the tag is found in the + ;; selected window's buffer; without this, point is moved in both + ;; windows. To prevent this, we save the selected window's point + ;; before doing find-tag-noselect, and restore it afterwards. + (let* ((window-point (window-point (selected-window))) + (tagbuf (erlang-find-tag-noselect tagname next-p regexp-p)) + (tagpoint (progn (set-buffer tagbuf) (point)))) + (set-window-point (prog1 + (selected-window) + (switch-to-buffer-other-window tagbuf) + ;; We have to set this new window's point; it + ;; might already have been displaying a + ;; different portion of tagbuf, in which case + ;; switch-to-buffer-other-window doesn't set + ;; the window's point from the buffer. + (set-window-point (selected-window) tagpoint)) + window-point))) + + +(defun erlang-find-tag-other-frame (tagname &optional next-p) + "Like `find-tag-other-frame' but aware of Erlang modules." + (interactive (erlang-tag-interactive + "Find `module:tag' or `tag' other frame: ")) + (let ((pop-up-frames t)) + (erlang-find-tag-other-window tagname next-p))) + + +(defun erlang-find-tag-regexp (regexp &optional next-p other-window) + "Like `find-tag-regexp' but aware of Erlang modules." + (interactive (if (fboundp 'find-tag-regexp) + (erlang-tag-interactive + "Find `module:regexp' or `regexp': ") + (error "This version of Emacs can't find tags by regexps"))) + (funcall (if other-window + 'erlang-find-tag-other-window + 'erlang-find-tag) + regexp next-p t)) + + +;; Just like C-u M-. This could be added to the menu. +(defun erlang-find-next-tag () + "Find next tag, like \\[find-tag] with prefix arg." + (interactive) + (let ((current-prefix-arg '(4))) + (if erlang-tags-installed + (call-interactively 'erlang-find-tag) + (call-interactively 'find-tag)))) + + +;; Mimics `find-tag-noselect' found in `etags.el', but uses `find-tag' to +;; be compatible with `tags.el'. +;; +;; Handles three cases: +;; * `module:' Loop over all possible file names. Stop if a file-name +;; without extension and directory matches the module. +;; +;; * `module:tag' +;; Emacs 19: Replace test functions with functions aware of +;; Erlang modules. Tricky because the etags system wasn't +;; built for these kind of operations... +;; +;; Emacs 18: We loop over `find-tag' until we find a file +;; whose module matches the requested module. The +;; drawback is that a lot of files could be loaded into +;; Emacs. +;; +;; * `tag' Just give it to `find-tag'. + +(defun erlang-find-tag-noselect (modtagname &optional next-p regexp-p) + "Like `find-tag-noselect' but aware of Erlang modules." + (interactive (erlang-tag-interactive "Find `module:tag' or `tag': ")) + (or modtagname + (setq modtagname (symbol-value 'last-tag))) + (funcall (symbol-function 'set) 'last-tag modtagname) + ;; `tags.el' uses this variable to record how M-, would + ;; know where to restart a tags command. + (if (boundp 'tags-loop-form) + (funcall (symbol-function 'set) + 'tags-loop-form '(erlang-find-tag nil t))) + (save-window-excursion + (cond + ((string-match ":$" modtagname) + ;; Only the module name was given. Read all files whose file name + ;; match. + (let ((modname (substring modtagname 0 (match-beginning 0))) + (file nil)) + (if (not next-p) + (save-excursion + (visit-tags-table-buffer) + (setq erlang-tags-file-list + (funcall (symbol-function 'tags-table-files))))) + (while (null file) + (or erlang-tags-file-list + (save-excursion + (if (and (featurep 'etags) + (funcall + (symbol-function 'visit-tags-table-buffer) 'same) + (funcall + (symbol-function 'visit-tags-table-buffer) t)) + (setq erlang-tags-file-list + (funcall (symbol-function 'tags-table-files))) + (error "No %stags containing %s" (if next-p "more " "") + modtagname)))) + (if erlang-tags-file-list + (let ((this-module (erlang-get-module-from-file-name + (car erlang-tags-file-list)))) + (if (and (stringp this-module) + (string= modname this-module)) + (setq file (car erlang-tags-file-list))) + (setq erlang-tags-file-list (cdr erlang-tags-file-list))))) + (set-buffer (or (get-file-buffer file) + (find-file-noselect file))))) + + ((string-match ":" modtagname) + (if (boundp 'find-tag-tag-order) + ;; Method one: Add module-recognising functions to the + ;; list of order functions. However, the tags system + ;; from Emacs 18, and derives thereof (read: XEmacs) + ;; hasn't got this feature. + (progn + (erlang-tags-install-module-check) + (unwind-protect + (funcall (symbol-function 'find-tag) + modtagname next-p regexp-p) + (erlang-tags-remove-module-check))) + ;; Method two: Call the tags system until a file matching + ;; the module is found. This could result in that many + ;; files are read. (e.g. The tag "foo:file" will take a + ;; while to process.) + (let* ((modname (substring modtagname 0 (match-beginning 0))) + (tagname (substring modtagname (match-end 0) nil)) + (last-tag tagname) + file) + (while + (progn + (funcall (symbol-function 'find-tag) tagname next-p regexp-p) + (setq next-p t) + ;; Determine the module form the file name. (The + ;; alternative, to check `-module', would make this + ;; code useless for non-Erlang programs.) + (setq file (erlang-get-module-from-file-name buffer-file-name)) + (not (and (stringp file) + (string= modname file)))))))) + (t + (funcall (symbol-function 'find-tag) modtagname next-p regexp-p))) + (current-buffer))) ; Return the new buffer. + + +;; Process interactive arguments for erlang-find-tag-*. +;; +;; Negative arguments work only for `etags', not `tags'. This is not +;; a problem since negative arguments means step back into the +;; history list, a feature not implemented in `tags'. + +(defun erlang-tag-interactive (prompt) + (condition-case nil + (require 'etags) + (error + (require 'tags))) + (if current-prefix-arg + (list nil (if (< (prefix-numeric-value current-prefix-arg) 0) + '- + t)) + (let* ((default (erlang-find-tag-default)) + (prompt (if default + (format "%s(default %s) " prompt default) + prompt)) + (spec (if (featurep 'etags) + (completing-read prompt 'erlang-tags-complete-tag) + (read-string prompt)))) + (list (if (equal spec "") + (or default (error "There is no default tag")) + spec))))) + + +;; Search tag functions which are aware of Erlang modules. The tactic +;; is to store new search functions into the local variables of the +;; TAGS buffers. The variables are restored directly after the +;; search. The situation is complicated by the fact that new TAGS +;; files can be loaded during the search. +;; + +(defun erlang-tags-install-module-check () + "Install our own tag search functions." + ;; Make sure our functions are installed in TAGS files loaded + ;; into Emacs while searching. + (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 '()) + )) + + ;; Install our functions in the TAGS files already resident. + (save-excursion + (let ((files (symbol-value 'tags-table-computed-list))) + (while files + (if (stringp (car files)) + (if (get-file-buffer (car files)) + (progn + (set-buffer (get-file-buffer (car files))) + (erlang-tags-install-local)))) + (setq files (cdr files)))))) + + +(defun erlang-tags-install-local () + "Install our tag search functions in current buffer." + (if erlang-tags-buffer-installed-p + () + ;; Mark this buffer as "installed" and record. + (set (make-local-variable 'erlang-tags-buffer-installed-p) t) + (setq erlang-tags-buffer-list + (cons (current-buffer) erlang-tags-buffer-list)) + + ;; Save the original values. + (set (make-local-variable 'erlang-tags-orig-tag-order) + (symbol-value 'find-tag-tag-order)) + (set (make-local-variable 'erlang-tags-orig-regexp-tag-order) + (symbol-value 'find-tag-regexp-tag-order)) + (set (make-local-variable 'erlang-tags-orig-search-function) + (symbol-value 'find-tag-search-function)) + (set (make-local-variable 'erlang-tags-orig-regexp-search-function) + (symbol-value 'find-tag-regexp-search-function)) + + ;; Install our own functions. + (set (make-local-variable 'find-tag-search-function) + 'erlang-tags-search-forward) + (set (make-local-variable 'find-tag-regexp-search-function) + 'erlang-tags-regexp-search-forward) + (set (make-local-variable 'find-tag-tag-order) + '(erlang-tag-match-module-p)) + (set (make-local-variable 'find-tag-regexp-tag-order) + '(erlang-tag-match-module-regexp-p)))) + + +(defun erlang-tags-remove-module-check () + "Remove our own tags search functions." + (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) + )) + + ;; Remove our functions from the TAGS files. (Note that + ;; `tags-table-computed-list' need not be the same list as when + ;; the search was started.) + (save-excursion + (let ((buffers erlang-tags-buffer-list)) + (while buffers + (if (buffer-name (car buffers)) + (progn + (set-buffer (car buffers)) + (erlang-tags-remove-local))) + (setq buffers (cdr buffers)))))) + + +(defun erlang-tags-remove-local () + "Remove our tag search functions from current buffer." + (if (null erlang-tags-buffer-installed-p) + () + (funcall (symbol-function 'set) 'erlang-tags-buffer-installed-p nil) + (funcall (symbol-function 'set) + 'find-tag-tag-order erlang-tags-orig-tag-order) + (funcall (symbol-function 'set) + 'find-tag-regexp-tag-order erlang-tags-orig-regexp-tag-order) + (funcall (symbol-function 'set) + 'find-tag-search-function erlang-tags-orig-search-function) + (funcall (symbol-function 'set) + 'find-tag-regexp-search-function + erlang-tags-orig-regexp-search-function))) + + +(defun erlang-tags-recognize-tags-table () + "Install our functions in all loaded TAGS files. + +This function is added to `tags-table-format-hooks/functions' when searching +for a tag on the form `module:tag'." + (if (null (funcall (symbol-function 'etags-recognize-tags-table))) + nil + (erlang-tags-install-local) + t)) + + +(defun erlang-tags-search-forward (tag &optional bound noerror count) + "Forward search function, aware of Erlang module prefix." + (if (string-match ":" tag) + (setq tag (substring tag (match-end 0) nil))) + ;; Avoid unintended recursion. + (if (eq erlang-tags-orig-search-function 'erlang-tags-search-forward) + (search-forward tag bound noerror count) + (funcall erlang-tags-orig-search-function tag bound noerror count))) + + +(defun erlang-tags-regexp-search-forward (tag &optional bound noerror count) + "Forward regexp search function, aware of Erlang module prefix." + (if (string-match ":" tag) + (setq tag (substring tag (match-end 0) nil))) + (if (eq erlang-tags-orig-regexp-search-function + 'erlang-tags-regexp-search-forward) + (re-search-forward tag bound noerror count) + (funcall erlang-tags-orig-regexp-search-function + tag bound noerror count))) + + +;; t if point is at a tag line that matches TAG, containing +;; module information. Assumes that all other order functions +;; are stored in `erlang-tags-orig-[regex]-tag-order'. + +(defun erlang-tag-match-module-p (tag) + (erlang-tag-match-module-common-p tag erlang-tags-orig-tag-order)) + +(defun erlang-tag-match-module-regexp-p (tag) + (erlang-tag-match-module-common-p tag erlang-tags-orig-regexp-tag-order)) + +(defun erlang-tag-match-module-common-p (tag order) + (let ((mod nil) + (found nil)) + (if (string-match ":" tag) + (progn + (setq mod (substring tag 0 (match-beginning 0))) + (setq tag (substring tag (match-end 0) nil)))) + (while (and order (not found)) + (setq found + (and (not (memq (car order) + '(erlang-tag-match-module-p + erlang-tag-match-module-regexp-p))) + (funcall (car order) tag))) + (setq order (cdr order))) + (and found + (or (null mod) + (string= mod (erlang-get-module-from-file-name + (file-of-tag))))))) + + +;;; Tags completion, Emacs 19 `etags' specific. +;;; +;;; The basic idea is to create a second completion table `erlang-tags- +;;; completion-table' containing all normal tags plus tags on the form +;;; `module:tag'. + + +(defun erlang-complete-tag () + "Perform tags completion on the text around point. +Completes to the set of names listed in the current tags table. + +Should the Erlang tags system be installed this command knows +about Erlang modules." + (interactive) + (condition-case nil + (require 'etags) + (error nil)) + (cond ((and erlang-tags-installed + (fboundp 'complete-tag)) ; Emacs 19 + (let ((orig-tags-complete-tag (symbol-function 'tags-complete-tag))) + (fset 'tags-complete-tag + (symbol-function 'erlang-tags-complete-tag)) + (unwind-protect + (funcall (symbol-function 'complete-tag)) + (fset 'tags-complete-tag orig-tags-complete-tag)))) + ((fboundp 'complete-tag) ; Emacs 19 + (funcall (symbol-function 'complete-tag))) + ((fboundp 'tag-complete-symbol) ; XEmacs + (funcall (symbol-function 'tag-complete-symbol))) + (t + (error "This version of Emacs can't complete tags")))) + + +;; Based on `tags-complete-tag', but this one uses +;; `erlang-tags-completion-table' instead of `tags-completion-table'. +;; +;; This is the entry-point called by system function `completing-read'. +(defun erlang-tags-complete-tag (string predicate what) + (save-excursion + ;; If we need to ask for the tag table, allow that. + (let ((enable-recursive-minibuffers t)) + (visit-tags-table-buffer)) + (if (eq what t) + (all-completions string (erlang-tags-completion-table) predicate) + (try-completion string (erlang-tags-completion-table) predicate)))) + + +;; `tags-completion-table' calls itself recursively, make it +;; call our own wedge instead. Note that the recursive call +;; is very rare; it only occurs when a tags-file contains +;; `include'-statements. +(defun erlang-tags-completion-table () + "Build completion table. Tags on the form `tag' or `module:tag'." + (setq erlang-tags-orig-completion-table + (symbol-function 'tags-completion-table)) + (fset 'tags-completion-table + (symbol-function 'erlang-tags-completion-table-1)) + (unwind-protect + (erlang-tags-completion-table-1) + (fset 'tags-completion-table + erlang-tags-orig-completion-table))) + + +(defun erlang-tags-completion-table-1 () + (make-local-variable 'erlang-tags-completion-table) + (or erlang-tags-completion-table + (let ((tags-completion-table nil) + (tags-completion-table-function + 'erlang-etags-tags-completion-table)) + (funcall erlang-tags-orig-completion-table) + (setq erlang-tags-completion-table tags-completion-table)))) + + +;; Based on `etags-tags-completion-table'. The difference is that we +;; add three symbols to the vector, the tag, module: and module:tag. +;; The module is extracted from the file name of a tag. (This one +;; only works if we are looking at an `etags' file. However, this is +;; the only format supported by Emacs, so far.) +(defun erlang-etags-tags-completion-table () + (let ((table (make-vector 511 0)) + (file nil)) + (save-excursion + (goto-char (point-min)) + ;; This monster regexp matches an etags tag line. + ;; \1 is the string to match; + ;; \2 is not interesting; + ;; \3 is the guessed tag name; XXX guess should be better eg DEFUN + ;; \4 is not interesting; + ;; \5 is the explicitly-specified tag name. + ;; \6 is the line to start searching at; + ;; \7 is the char to start searching at. + (while (progn + (while (and + (eq (following-char) ?\f) + (looking-at "\f\n\\([^,\n]*\\),.*\n")) + (setq file (buffer-substring + (match-beginning 1) (match-end 1))) + (goto-char (match-end 0))) + (re-search-forward + "\ +^\\(\\([^\177]+[^-a-zA-Z0-9_$\177]+\\)?\\([-a-zA-Z0-9_$?:]+\\)\ +\[^-a-zA-Z0-9_$?:\177]*\\)\177\\(\\([^\n\001]+\\)\001\\)?\ +\\([0-9]+\\)?,\\([0-9]+\\)?\n" + nil t)) + (let ((tag (if (match-beginning 5) + ;; There is an explicit tag name. + (buffer-substring (match-beginning 5) (match-end 5)) + ;; No explicit tag name. Best guess. + (buffer-substring (match-beginning 3) (match-end 3)))) + (module (and file + (erlang-get-module-from-file-name file)))) + (intern tag table) + (if (stringp module) + (progn + (intern (concat module ":" tag) table) + ;; Only the first one will be stored in the table. + (intern (concat module ":") table)))))) + table)) + +;;; +;;; Prepare for other methods to run an Erlang slave process. +;;; + +(defvar erlang-shell-function 'inferior-erlang + "Command to execute start a new Erlang shell. + +Change this variable to use your favorite +Erlang compilation package.") + +(defvar erlang-shell-display-function 'inferior-erlang-run-or-select + "Command to execute to display Erlang shell. + +Change this variable to use your favorite +Erlang compilation package.") + +(defvar erlang-compile-function 'inferior-erlang-compile + "Command to execute to compile current buffer. + +Change this variable to use your favorite +Erlang compilation package.") + +(defvar erlang-compile-erlang-function "c" + "Erlang function to call to compile an erlang file.") + +(defvar erlang-compile-display-function 'inferior-erlang-run-or-select + "Command to execute to view last compilation. + +Change this variable to use your favorite +Erlang compilation package.") + +(defvar erlang-next-error-function 'inferior-erlang-next-error + "Command to execute to go to the next error. + +Change this variable to use your favorite Erlang compilation +package. Not used in Emacs 21.") + + +;;;###autoload +(defun erlang-shell () + "Start a new Erlang shell. + +The variable `erlang-shell-function' decides which method to use, +default is to start a new Erlang host. It is possible that, in the +future, a new shell on an already running host will be started." + (interactive) + (call-interactively erlang-shell-function)) + + +;;;###autoload (autoload 'run-erlang "erlang" "Start a new Erlang shell." t) + +;; It is customary for Emacs packages to supply a function on this +;; form, even though it violates the `erlang-*' name convention. +(defalias 'run-erlang 'erlang-shell) + + +(defun erlang-shell-display () + "Display an Erlang shell, or start a new." + (interactive) + (call-interactively erlang-shell-display-function)) + + +;;;###autoload +(defun erlang-compile () + "Compile Erlang module in current buffer." + (interactive) + (call-interactively erlang-compile-function)) + + +(defun erlang-compile-display () + "Display compilation output." + (interactive) + (call-interactively erlang-compile-display-function)) + + +(defun erlang-next-error () + "Display next error message from the latest compilation." + (interactive) + (call-interactively erlang-next-error-function)) + + + +;;; +;;; Erlang Shell Mode -- Major mode used for Erlang shells. +;;; + +;; This mode is designed to be implementation independent, +;; e.g. it does not assume that we are running an inferior +;; Erlang, there exists a lot of other possibilities. + + +(defvar erlang-shell-buffer-name "*erlang*" + "The name of the Erlang link shell buffer.") + +(defvar erlang-shell-mode-map nil + "Keymap used by Erlang shells.") + + +(defvar erlang-shell-mode-hook nil + "*User functions to run when an Erlang shell is started. + +This hook is used to change the behaviour of Erlang mode. It is +normally used by the user to personalise the programming environment. +When used in a site init file, it could be used to customise Erlang +mode for all users on the system. + +The function added to this hook is run every time a new Erlang +shell is started. + +See also `erlang-load-hook', a hook which is run once, when Erlang +mode is loaded, and `erlang-mode-hook' which is run every time a new +Erlang source file is loaded into Emacs.") + + +(defvar erlang-input-ring-file-name "~/.erlang_history" + "*When non-nil, file name used to store Erlang shell history information.") + + +(defun erlang-shell-mode () + "Major mode for interacting with an Erlang shell. + +We assume that we already are in Comint mode. + +The following special commands are available: +\\{erlang-shell-mode-map}" + (interactive) + (setq major-mode 'erlang-shell-mode) + (setq mode-name "Erlang Shell") + (erlang-mode-variables) + (if erlang-shell-mode-map + nil + (setq erlang-shell-mode-map (copy-keymap comint-mode-map)) + (erlang-shell-mode-commands erlang-shell-mode-map)) + (use-local-map erlang-shell-mode-map) + (unless inferior-erlang-use-cmm + ;; This was originally not a marker, but it needs to be, at least + ;; in Emacs 21, and should be backwards-compatible. Otherwise, + ;; would need to test whether compilation-parsing-end is a marker + ;; after requiring `compile'. + (set (make-local-variable 'compilation-parsing-end) (copy-marker 1)) + (set (make-local-variable 'compilation-error-list) nil) + (set (make-local-variable 'compilation-old-error-list) nil)) + ;; Needed when compiling directly from the Erlang shell. + (setq compilation-last-buffer (current-buffer)) + (setq comint-prompt-regexp "^[^>=]*> *") + (setq comint-eol-on-send t) + (setq comint-input-ignoredups t) + (setq comint-scroll-show-maximum-output t) + (setq comint-scroll-to-bottom-on-output t) + ;; In Emacs 19.30, `add-hook' has got a `local' flag, use it. If + ;; the call fails, just call the normal `add-hook'. + (condition-case nil + (progn + (add-hook 'comint-output-filter-functions + 'inferior-erlang-strip-delete nil t) + (add-hook 'comint-output-filter-functions + 'inferior-erlang-strip-ctrl-m nil t)) + (error + (funcall (symbol-function 'make-local-hook) + 'comint-output-filter-functions) ; obsolete as of Emacs 21.1 + (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-delete) + (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-ctrl-m))) + ;; Some older versions of comint 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))) + ;; 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. + (when inferior-erlang-use-cmm + (compilation-minor-mode 1) + (set (make-local-variable 'minor-mode-overriding-map-alist) + `((compilation-minor-mode + . ,(let ((map (make-sparse-keymap))) + ;; It would be useful to put keymap properties on the + ;; error lines so that we could use RET and mouse-2 + ;; on them directly. + (when (boundp 'compilation-skip-threshold) ; new compile.el + (define-key map [mouse-2] #'erlang-mouse-2-command) + (define-key map "\C-m" #'erlang-RET-command)) + (if (boundp 'compilation-menu-map) + (define-key map [menu-bar compilation] + (cons "Errors" compilation-menu-map))) + map))))) + (run-hooks 'erlang-shell-mode-hook)) + + +(defun erlang-mouse-2-command (event) + "Command bound to `mouse-2' in inferior Erlang buffer. +Selects Comint or Compilation mode command as appropriate." + (interactive "e") + (if (save-window-excursion + (save-excursion + (mouse-set-point event) + (consp (get-text-property (line-beginning-position) 'message)))) + (call-interactively (lookup-key compilation-mode-map [mouse-2])) + (call-interactively (lookup-key comint-mode-map [mouse-2])))) + +(defun erlang-RET-command () + "Command bound to `RET' in inferior Erlang buffer. +Selects Comint or Compilation mode command as appropriate." + (interactive) + (if (consp (get-text-property (line-beginning-position) 'message)) + (call-interactively (lookup-key compilation-mode-map "\C-m")) + (call-interactively (lookup-key comint-mode-map "\C-m")))) + +(defun erlang-shell-mode-commands (map) + (define-key map "\M-\t" 'erlang-complete-tag) + (define-key map "\C-a" 'comint-bol) ; Normally the other way around. + (define-key map "\C-c\C-a" 'beginning-of-line) + (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof' + (define-key map "\M-\C-m" 'compile-goto-error) + (unless inferior-erlang-use-cmm + (define-key map "\C-x`" 'erlang-next-error))) + +;;; +;;; Inferior Erlang -- Run an Erlang shell as a subprocess. +;;; + +(defvar inferior-erlang-display-buffer-any-frame nil + "*When nil, `inferior-erlang-display-buffer' use only selected frame. +When t, all frames are searched. When 'raise, the frame is raised.") + +(defvar inferior-erlang-shell-type 'newshell + "The type of Erlang shell to use. + +When this variable is set to the atom `oldshell', the old shell is used. +When set to `newshell' the new shell is used. Should the variable be +nil, the default shell is used. + +This variable influence the setting of other variables.") + +(defvar inferior-erlang-machine "erl" + "*The name of the Erlang shell.") + +(defvar inferior-erlang-machine-options '() + "*The options used when activating the Erlang shell. + +This must be a list of strings.") + +(defvar inferior-erlang-process-name "inferior-erlang" + "The name of the inferior Erlang process.") + +(defvar inferior-erlang-buffer-name erlang-shell-buffer-name + "The name of the inferior Erlang buffer.") + +(defvar inferior-erlang-prompt-timeout 60 + "*Number of seconds before `inferior-erlang-wait-prompt' timeouts. + +The time specified is waited after every output made by the inferior +Erlang shell. When this variable is t, we assume that we always have +a prompt. When nil, we will wait forever, or until \\[keyboard-quit].") + +(defvar inferior-erlang-process nil + "Process of last invoked inferior Erlang, or nil.") + +(defvar inferior-erlang-buffer nil + "Buffer of last invoked inferior Erlang, or nil.") + +;; Enable uniquifying Erlang shell buffers based on directory name. +(eval-after-load "uniquify" + '(add-to-list 'uniquify-list-buffers-directory-modes 'erlang-shell-mode)) + +;;;###autoload +(defun inferior-erlang (&optional command) + "Run an inferior Erlang. +With prefix command, prompt for command to start Erlang with. + +This is just like running Erlang in a normal shell, except that +an Emacs buffer is used for input and output. +\\ +The command line history can be accessed with \\[comint-previous-input] and \\[comint-next-input]. +The history is saved between sessions. + +Entry to this mode calls the functions in the variables +`comint-mode-hook' and `erlang-shell-mode-hook' with no arguments. + +The following commands imitate the usual Unix interrupt and +editing control characters: +\\{erlang-shell-mode-map}" + (interactive + (when current-prefix-arg + (list (if (fboundp 'read-shell-command) + ;; `read-shell-command' is a new function in Emacs 23. + (read-shell-command "Erlang command: ") + (read-string "Erlang command: "))))) + (require 'comint) + (let (cmd opts) + (if command + (setq cmd "sh" + opts (list "-c" command)) + (setq cmd inferior-erlang-machine + opts inferior-erlang-machine-options) + (cond ((eq inferior-erlang-shell-type 'oldshell) + (setq opts (cons "-oldshell" opts))) + ((eq inferior-erlang-shell-type 'newshell) + (setq opts (append '("-newshell" "-env" "TERM" "vt100") opts))))) + + ;; Using create-file-buffer and list-buffers-directory in this way + ;; makes uniquify give each buffer a unique name based on the + ;; directory. + (let ((fake-file-name (expand-file-name inferior-erlang-buffer-name default-directory))) + (setq inferior-erlang-buffer (create-file-buffer fake-file-name)) + (apply 'make-comint-in-buffer + inferior-erlang-process-name + inferior-erlang-buffer + cmd + nil opts) + (with-current-buffer inferior-erlang-buffer + (setq list-buffers-directory fake-file-name)))) + + (setq inferior-erlang-process + (get-buffer-process inferior-erlang-buffer)) + (if (> 21 erlang-emacs-major-version) ; funcalls to avoid compiler warnings + (funcall (symbol-function 'set-process-query-on-exit-flag) + inferior-erlang-process nil) + (funcall (symbol-function 'process-kill-without-query) inferior-erlang-process)) + (if erlang-inferior-shell-split-window + (switch-to-buffer-other-window inferior-erlang-buffer) + (switch-to-buffer inferior-erlang-buffer)) + (if (and (not (eq system-type 'windows-nt)) + (eq inferior-erlang-shell-type 'newshell)) + (setq comint-process-echoes t)) + (erlang-shell-mode)) + + +(defun inferior-erlang-run-or-select () + "Switch to an inferior Erlang buffer, possibly starting new process." + (interactive) + (if (null (inferior-erlang-running-p)) + (inferior-erlang) + (inferior-erlang-display-buffer t))) + + +(defun inferior-erlang-display-buffer (&optional select) + "Make the inferior Erlang process visible. +The window is returned. + +Should `inferior-erlang-display-buffer-any-frame' be nil the buffer is +displayed in the current frame. Should it be non-nil, and the buffer +already is visible in any other frame, no new window will be created. +Should it be the atom 'raise, the frame containing the window will +be raised. + +Should the optional argument SELECT be non-nil, the window is +selected. Should the window be in another frame, that frame is raised. + +Note, should the mouse pointer be places outside the raised frame, that +frame will become deselected before the next command." + (interactive) + (or (inferior-erlang-running-p) + (error "No inferior Erlang process is running")) + (let ((win (inferior-erlang-window + inferior-erlang-display-buffer-any-frame)) + (frames-p (fboundp 'selected-frame))) + (if (null win) + (let ((old-win (selected-window))) + (save-excursion + (switch-to-buffer-other-window inferior-erlang-buffer) + (setq win (selected-window))) + (select-window old-win)) + (if (and window-system + frames-p + (or select + (eq inferior-erlang-display-buffer-any-frame 'raise)) + (not (eq (selected-frame) (window-frame win)))) + (raise-frame (window-frame win)))) + (if select + (select-window win)) + (sit-for 0) + win)) + + +(defun inferior-erlang-running-p () + "Non-nil when an inferior Erlang is running." + (and inferior-erlang-process + (memq (process-status inferior-erlang-process) '(run open)) + inferior-erlang-buffer + (buffer-name inferior-erlang-buffer))) + + +(defun inferior-erlang-window (&optional all-frames) + "Return the window containing the inferior Erlang, or nil." + (and (inferior-erlang-running-p) + (if (and all-frames (>= erlang-emacs-major-version 19)) + (get-buffer-window inferior-erlang-buffer t) + (get-buffer-window inferior-erlang-buffer)))) + + +(defun inferior-erlang-wait-prompt () + "Wait until the inferior Erlang shell prompt appears." + (if (eq inferior-erlang-prompt-timeout t) + () + (or (inferior-erlang-running-p) + (error "No inferior Erlang shell is running")) + (save-excursion + (set-buffer inferior-erlang-buffer) + (let ((msg nil)) + (while (save-excursion + (goto-char (process-mark inferior-erlang-process)) + (forward-line 0) + (not (looking-at comint-prompt-regexp))) + (if msg + () + (setq msg t) + (message "Waiting for Erlang shell prompt (press C-g to abort).")) + (or (accept-process-output inferior-erlang-process + inferior-erlang-prompt-timeout) + (error "No Erlang shell prompt before timeout"))) + (if msg (message "")))))) + +(defun inferior-erlang-send-empty-cmd-unless-already-at-prompt () + "If not already at a prompt, try to send an empty cmd to get a prompt. +The empty command resembles hitting RET. This is useful in some +situations, for instance if a crash or error report from sasl +has been printed after the last prompt." + (save-excursion + (set-buffer inferior-erlang-buffer) + (if (> (point-max) 1) + ;; make sure we get a prompt if buffer contains data + (if (save-excursion + (goto-char (process-mark inferior-erlang-process)) + (forward-line 0) + (not (looking-at comint-prompt-regexp))) + (inferior-erlang-send-command ""))))) + +(autoload 'comint-send-input "comint") + +(defun inferior-erlang-send-command (cmd &optional hist) + "Send command CMD to the inferior Erlang. + +The contents of the current command line (if any) will +be placed at the next prompt. + +If optional second argument is non-nil the command is inserted into +the history list. + +Return the position after the newly inserted command." + (or (inferior-erlang-running-p) + (error "No inferior Erlang process is running")) + (let ((old-buffer (current-buffer)) + (insert-point (marker-position (process-mark inferior-erlang-process))) + (insert-length (if comint-process-echoes + 0 + (1+ (length cmd))))) + (set-buffer inferior-erlang-buffer) + (goto-char insert-point) + (insert cmd) + ;; Strange things happened if `comint-eol-on-send' is declared + ;; in the `let' expression above, but setq:d here. The + ;; `set-buffer' statement obviously makes the buffer local + ;; instance of `comint-eol-on-send' shadow this one. + ;; I'm considering this a bug in Elisp. + ;; + ;; This was previously cautioned against in the Lisp manual. It + ;; 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))) + ;; Adjust all windows whose points are incorrect. + (if (null comint-process-echoes) + (walk-windows + (function + (lambda (window) + (if (and (eq (window-buffer window) inferior-erlang-buffer) + (= (window-point window) insert-point)) + (set-window-point window + (+ insert-point insert-length))))) + nil t)) + (set-buffer old-buffer) + (+ insert-point insert-length))) + + +(defun inferior-erlang-strip-delete (&optional s) + "Remove `^H' (delete) and the characters it was supposed to remove." + (interactive) + (if (and (boundp 'comint-last-input-end) + (boundp 'comint-last-output-start)) + (save-excursion + (goto-char + (if (interactive-p) + (symbol-value 'comint-last-input-end) + (symbol-value 'comint-last-output-start))) + (while (progn (skip-chars-forward "^\C-h") + (not (eq (point) (point-max)))) + (delete-char 1) + (or (bolp) + (backward-delete-char 1)))))) + + +;; Basically `comint-strip-ctrl-m', with a few extra checks. +(defun inferior-erlang-strip-ctrl-m (&optional string) + "Strip trailing `^M' characters from the current output group." + (interactive) + (if (and (boundp 'comint-last-input-end) + (boundp 'comint-last-output-start)) + (let ((pmark (process-mark (get-buffer-process (current-buffer))))) + (save-excursion + (goto-char + (if (interactive-p) + (symbol-value 'comint-last-input-end) + (symbol-value 'comint-last-output-start))) + (while (re-search-forward "\r+$" pmark t) + (replace-match "" t t)))))) + + +(defun inferior-erlang-compile (arg) + "Compile the file in the current buffer. + +With prefix arg, compiles for debug. + +Should Erlang return `{error, nofile}' it could not load the object +module after completing the compilation. This is due to a bug in the +compile command `c' when using the option `outdir'. + +There exists two workarounds for this bug: + + 1) Place the directory in the Erlang load path. + + 2) Set the Emacs variable `erlang-compile-use-outdir' to nil. + To do so, place the following line in your `~/.emacs'-file: + (setq erlang-compile-use-outdir nil)" + (interactive "P") + (save-some-buffers) + (inferior-erlang-prepare-for-input) + (let* ((dir (inferior-erlang-compile-outdir)) +;;; (file (file-name-nondirectory (buffer-file-name))) + (noext (substring (erlang-local-buffer-file-name) 0 -4)) + (opts (append (list (cons 'outdir dir)) + (if current-prefix-arg + (list 'debug_info 'export_all)) + erlang-compile-extra-opts)) + end) + (save-excursion + (set-buffer inferior-erlang-buffer) + (compilation-forget-errors)) + (setq end (inferior-erlang-send-command + (inferior-erlang-compute-compile-command noext opts) + nil)) + (sit-for 0) + (inferior-erlang-wait-prompt) + (save-excursion + (set-buffer inferior-erlang-buffer) + (setq compilation-error-list nil) + (set-marker compilation-parsing-end end)) + (setq compilation-last-buffer inferior-erlang-buffer))) + +(defun inferior-erlang-prepare-for-input (&optional no-display) + "Create an inferior erlang buffer if needed and ready it for input. +The buffer is displayed, according to `inferior-erlang-display-buffer' +unless the optional NO-DISPLAY is non-nil." + (or (inferior-erlang-running-p) + (save-excursion + (inferior-erlang))) + (or (inferior-erlang-running-p) + (error "Error starting inferior Erlang shell")) + (if (not no-display) + (inferior-erlang-display-buffer)) + (inferior-erlang-send-empty-cmd-unless-already-at-prompt) + (sit-for 0) + (inferior-erlang-wait-prompt)) + +(defun inferior-erlang-compile-outdir () + "Return the directory to compile the current buffer into." + (let* ((buffer-dir (directory-file-name + (file-name-directory (erlang-local-buffer-file-name)))) + (parent-dir (directory-file-name + (file-name-directory buffer-dir))) + (ebin-dir (concat (file-name-as-directory parent-dir) "ebin")) + (buffer-dir-base-name (file-name-nondirectory + (expand-file-name + (concat (file-name-as-directory buffer-dir) + "."))))) + (if (and (string= buffer-dir-base-name "src") + (file-directory-p ebin-dir)) + (file-name-as-directory ebin-dir) + (file-name-as-directory buffer-dir)))) + +(defun inferior-erlang-compute-compile-command (module-name opts) + (let ((ccfn erlang-compile-command-function-alist) + (res (inferior-erlang-compute-erl-compile-command module-name opts)) + ccfn-entry + done) + (if (not (null (erlang-local-buffer-file-name))) + (while (and (not done) (not (null ccfn))) + (setq ccfn-entry (car ccfn)) + (setq ccfn (cdr ccfn)) + (if (string-match (car ccfn-entry) (erlang-local-buffer-file-name)) + (let ((c-fn (cdr ccfn-entry))) + (setq done t) + (if (not (null c-fn)) + (setq result (funcall c-fn module-name opts))))))) + result)) + +(defun inferior-erlang-compute-erl-compile-command (module-name opts) + (let* ((out-dir-opt (assoc 'outdir opts)) + (out-dir (cdr out-dir-opt))) + (if erlang-compile-use-outdir + (format "%s(\"%s\"%s)." + erlang-compile-erlang-function + module-name + (inferior-erlang-format-comma-opts opts)) + (let (;; Hopefully, noone else will ever use these... + (tmpvar "Tmp7236") + (tmpvar2 "Tmp8742")) + (format + (concat + "f(%s), {ok, %s} = file:get_cwd(), " + "file:set_cwd(\"%s\"), " + "%s = %s(\"%s\"%s), file:set_cwd(%s), f(%s), %s.") + tmpvar2 tmpvar + out-dir + tmpvar2 + erlang-compile-erlang-function + module-name (inferior-erlang-format-comma-opts + (remq out-dir-opt opts)) + tmpvar tmpvar tmpvar2))))) + +(defun inferior-erlang-compute-leex-compile-command (module-name opts) + (let ((file-name (erlang-local-buffer-file-name)) + (erl-compile-expr (inferior-erlang-remove-any-trailing-dot + (inferior-erlang-compute-erl-compile-command + module-name opts)))) + (format (concat "f(LErr1__), f(LErr2__), " + "case case leex:file(\"%s\", [%s]) of" + " ok -> ok;" + " {ok,_} -> ok;" + " {ok,_,_} -> ok;" + " LErr1__ -> LErr1__ " + "end of" + " ok -> %s;" + " LErr2__ -> LErr2__ " + "end.") + file-name + (inferior-erlang-format-comma-opts erlang-leex-compile-opts) + erl-compile-expr))) + +(defun inferior-erlang-compute-yecc-compile-command (module-name opts) + (let ((file-name (erlang-local-buffer-file-name)) + (erl-compile-expr (inferior-erlang-remove-any-trailing-dot + (inferior-erlang-compute-erl-compile-command + module-name opts)))) + (format (concat "f(YErr1__), f(YErr2__), " + "case case yecc:file(\"%s\", [%s]) of" + " {ok,_} -> ok;" + " {ok,_,_} -> ok;" + " YErr1__ -> YErr1__ " + "end of" + " ok -> %s;" + " YErr2__ -> YErr2__ " + "end.") + file-name + (inferior-erlang-format-comma-opts erlang-yecc-compile-opts) + erl-compile-expr))) + +(defun inferior-erlang-remove-any-trailing-dot (str) + (if (string= (substring str -1) ".") + (substring str 0 (1- (length str))) + str)) + +(defun inferior-erlang-format-comma-opts (opts) + (if (null opts) + "" + (concat ", " (inferior-erlang-format-opts opts)))) + +(defun inferior-erlang-format-opts (opts) + (concat "[" (inferior-erlang-string-join (mapcar 'inferior-erlang-format-opt + opts) + ", ") + "]")) + +(defun inferior-erlang-format-opt (opt) + (cond ((stringp opt) (concat "\"" opt "\"")) + ((atom opt) (format "%s" opt)) + ((consp opt) (concat "{" (inferior-erlang-string-join + (mapcar 'inferior-erlang-format-opt + (list (car opt) (cdr opt))) + ", ") + "}")) + (t (error (format "Unexpected opt %s" opt))))) + +(defun inferior-erlang-string-join (strs sep) + (let ((result (or (car strs) ""))) + (setq strs (cdr strs)) + (while strs + (setq result (concat result sep (car strs))) + (setq strs (cdr strs))) + result)) + +(defun erlang-local-buffer-file-name () + ;; When editing a file remotely via tramp, + ;; the buffer's file name may be for example + ;; "/ssh:host.example.com:/some/path/x.erl" + ;; + ;; If I try to compile such a file using C-c C-k, an + ;; erlang shell on the remote host is automatically + ;; started if needed, but for it to successfully compile + ;; the file, the c(...) command that is sent must contain + ;; the file name "/some/path/x.erl" without the + ;; tramp-prefix "/ssh:host.example.com:". + (cond ((null (buffer-file-name)) + nil) + ((erlang-tramp-remote-file-p) + (erlang-tramp-get-localname)) + (t + (buffer-file-name)))) + +(defun erlang-tramp-remote-file-p () + (and (fboundp 'tramp-tramp-file-p) + (tramp-tramp-file-p (buffer-file-name)))) + +(defun erlang-tramp-get-localname () + (let ((tramp-info (tramp-dissect-file-name (buffer-file-name)))) + (if (fboundp 'tramp-file-name-localname) + (tramp-file-name-localname tramp-info) + ;; In old versions of tramp, it was `tramp-file-name-path' + ;; instead of the newer `tramp-file-name-localname' + (tramp-file-name-path tramp-info)))) + +;; `next-error' only accepts buffers with major mode `compilation-mode' +;; or with the minor mode `compilation-minor-mode' activated. +;; (To activate the minor mode is out of the question, since it will +;; ruin the inferior Erlang keymap.) +;; This is done differently in Emacs 21. +(defun inferior-erlang-next-error (&optional argp) + "Just like `next-error'. +Capable of finding error messages in an inferior Erlang buffer." + (interactive "P") + (let ((done nil) + (buf (or (and (boundp 'next-error-last-buffer) + next-error-last-buffer) + (and (boundp 'compilation-last-buffer) + compilation-last-buffer)))) + (if (and (bufferp buf) + (save-excursion + (set-buffer buf) + (and (eq major-mode 'erlang-shell-mode) + (setq major-mode 'compilation-mode)))) + (unwind-protect + (progn + (setq done t) + (next-error argp)) + (save-excursion + (set-buffer buf) + (setq major-mode 'erlang-shell-mode)))) + (or done + (next-error argp)))) + + +(defun inferior-erlang-change-directory (&optional dir) + "Make the inferior Erlang change directory. +The default is to go to the directory of the current buffer." + (interactive) + (or dir (setq dir (file-name-directory (erlang-local-buffer-file-name)))) + (or (inferior-erlang-running-p) + (error "No inferior Erlang is running")) + (inferior-erlang-display-buffer) + (inferior-erlang-send-empty-cmd-unless-already-at-prompt) + (inferior-erlang-wait-prompt) + (inferior-erlang-send-command (format "cd('%s')." dir) nil)) + +(defun erlang-align-arrows (start end) + "Align arrows (\"->\") in function clauses from START to END. +When called interactively, aligns arrows after function clauses inside +the region. + +With a prefix argument, aligns all arrows, not just those in function +clauses. + +Example: + +sum(L) -> sum(L, 0). +sum([H|T], Sum) -> sum(T, Sum + H); +sum([], Sum) -> Sum. + +becomes: + +sum(L) -> sum(L, 0). +sum([H|T], Sum) -> sum(T, Sum + H); +sum([], Sum) -> Sum." + (interactive "r") + (save-excursion + (let (;; regexp for matching arrows. without a prefix argument, + ;; the regexp matches function heads. With a prefix, it + ;; matches any arrow. + (re (if current-prefix-arg + "^.*\\(\\)->" + (eval-when-compile + (concat "^" erlang-atom-regexp ".*\\(\\)->")))) + ;; part of regexp matching directly before the arrow + (arrow-match-pos (if current-prefix-arg + 1 + (1+ erlang-atom-regexp-matches))) + ;; accumulator for positions where arrows are found, ordered + ;; by buffer position (from greatest to smallest) + (arrow-positions '()) + ;; accumulator for longest distance from start of line to arrow + (most-indent 0) + ;; marker to track the end of the region we're aligning + (end-marker (progn (goto-char end) + (point-marker)))) + ;; Pass 1: Find the arrow positions, adjust the whitespace + ;; before each arrow to one space, and find the greatest + ;; indentation level. + (goto-char start) + (while (re-search-forward re end-marker t) + (goto-char (match-beginning arrow-match-pos)) + (just-one-space) ; adjust whitespace + (setq arrow-positions (cons (point) arrow-positions)) + (setq most-indent (max most-indent (erlang-column-number)))) + (set-marker end-marker nil) ; free the marker + ;; Pass 2: Insert extra padding so that all arrow indentation is + ;; equal. This is done last-to-first by buffer position, so that + ;; inserting spaces before one arrow doesn't change the + ;; positions of the next ones. + (mapc (lambda (arrow-pos) + (goto-char arrow-pos) + (let* ((pad (- most-indent (erlang-column-number)))) + (when (> pad 0) + (insert-char ?\ pad)))) + arrow-positions)))) + +(defun erlang-column-number () + "Return the column number of the current position in the buffer. +Tab characters are counted by their visual width." + (string-width (buffer-substring (line-beginning-position) (point)))) + +(defun erlang-current-defun () + "`add-log-current-defun-function' for Erlang." + (save-excursion + (erlang-beginning-of-function) + (if (looking-at "[a-z0-9_]+") + (match-string 0)))) + +;; Aliases for backward compatibility with older versions of Erlang Mode. +;; +;; Unfortuantely, older versions of Emacs doesn't have `defalias' and +;; `make-obsolete' so we have to define our own `obsolete' function. + +(defun erlang-obsolete (sym newdef) + "Make the obsolete function SYM refer to the defined function NEWDEF. + +Simplified version of a combination `defalias' and `make-obsolete', +it assumes that NEWDEF is loaded." + (defalias sym (symbol-function newdef)) + (if (fboundp 'make-obsolete) + (make-obsolete sym newdef))) + + +(erlang-obsolete 'calculate-erlang-indent 'erlang-calculate-indent) +(erlang-obsolete 'calculate-erlang-stack-indent + 'erlang-calculate-stack-indent) +(erlang-obsolete 'at-erlang-keyword 'erlang-at-keyword) +(erlang-obsolete 'at-erlang-operator 'erlang-at-operator) +(erlang-obsolete 'beginning-of-erlang-clause 'erlang-beginning-of-clause) +(erlang-obsolete 'end-of-erlang-clause 'erlang-end-of-clause) +(erlang-obsolete 'mark-erlang-clause 'erlang-mark-clause) +(erlang-obsolete 'beginning-of-erlang-function 'erlang-beginning-of-function) +(erlang-obsolete 'end-of-erlang-function 'erlang-end-of-function) +(erlang-obsolete 'mark-erlang-function 'erlang-mark-function) +(erlang-obsolete 'pass-over-erlang-clause 'erlang-pass-over-function) +(erlang-obsolete 'name-of-erlang-function 'erlang-name-of-function) + + +;; Fixme: shouldn't redefine `set-visited-file-name' anyhow -- see above. +(defconst erlang-unload-hook + (list (lambda () + (defalias 'set-visited-file-name + 'erlang-orig-set-visited-file-name) + (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))) + +;; The end... + +(provide 'erlang) + +(run-hooks 'erlang-load-hook) + +;; Local variables: +;; coding: iso-8859-1 +;; End: + +;;; erlang.el ends here diff --git a/emacs.d/elpa/erlang-20141104.17/erlang_appwiz.el b/emacs.d/elpa/erlang-20141104.17/erlang_appwiz.el new file mode 100644 index 0000000..ecbce66 --- /dev/null +++ b/emacs.d/elpa/erlang-20141104.17/erlang_appwiz.el @@ -0,0 +1,1345 @@ +;;; -*- Emacs-Lisp -*- +;;; File: erlang_appwiz.el +;;; Author: Johan Bevermyr +;;; Created: Tue Dec 9 13:14:24 1997 +;;; Purpose: Adds a simple application wizard to erlang.el. + +;; OBS! Must be loaded before the erlang.el file is loaded. +;; Add the following to your .emacs file before erlang.el is loaded. +;; +;; (load "erlang_appwiz" t nil) +;; +;; Customisation of makefile generation: +;; +;; The templates for generating makefiles are stored in the +;; variables erlang-skel-makefile-src and erlang-skel-makefile-middle. +;; +;; These can be modified by setting the variables before or after this +;; file is loaded. +;; +;; For example, to generate OTP-style make files: +;; +;; +;;(defvar erlang-skel-makefile-src +;; '((erlang-skel-include erlang-skel-nomodule-header) +;; "CC_ROOT := $(shell pwd | sed 's/erts.*$$//')" n +;; "AUTOCONF := $(CC_ROOT)/erts/autoconf" n +;; "TARGET := $(shell $(AUTOCONF)/config.guess)" +;; "include $(CC_ROOT)/internal_tools/make/$(TARGET)/otp.mk" n +;; n +;; "# ----------------------------------------------------" n +;; "# Application version " n +;; "# ----------------------------------------------------" n +;; "include ../vsn.mk" n +;; "VSN=$(KERNEL_VSN)" n +;; n +;; "# ----------------------------------------------------" n +;; "# Release directory specification" n +;; "# ----------------------------------------------------" n +;; "RELEASE_PATH= ../../../release/$(TARGET)" n +;; "RELSYSDIR = $(RELEASE_PATH)/lib/kernel-$(VSN)" n +;; n +;; "# ----------------------------------------------------" n +;; "# Target Specs" n +;; "# ----------------------------------------------------" n +;; n +;; "MODULES= " appwiz-erlang-modulename n +;; n +;; "HRL_FILES=" +;; n +;; INTERNAL_HRL_FILES= appwiz-erlang-modulename "_sup.hrl" n +;; n +;; "ERL_FILES= $(MODULES:%=%.erl)" n +;; n +;; "TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET)" n +;; n +;; "APP_FILE= " appwiz-erlang-modulename ".app" n +;; n +;; "APP_SRC= $(APP_FILE).src" n +;; "APP_TARGET= ../ebin/$(APP_FILE)" n +;; n +;; "# ----------------------------------------------------" n +;; "# FLAGS " n +;; "# ----------------------------------------------------" n +;; "ERL_FLAGS += " n +;; "ERL_COMPILE_FLAGS += -I../include" n +;; n +;; "# ----------------------------------------------------" n +;; "# Targets" n +;; "# ----------------------------------------------------" n +;; n +;; "debug opt: $(TARGET_FILES)" n +;; n +;; "clean:" n +;; " rm -f $(TARGET_FILES) $(GEN_FILES)" n +;; " rm -f core" n +;; n +;; "docs:" n +;; n +;; "# ----------------------------------------------------" n +;; "# Special Build Targets " n +;; "# ----------------------------------------------------" n +;; " " n +;; "$(APP_TARGET): $(APP_SRC) " n +;; " sed -e 's;%VSN%;$(VSN);' $(APP_SRC) > $(APP_TARGET)" n +;; " " n +;; "# ----------------------------------------------------" n +;; "# Release Target " n +;; "# ----------------------------------------------------" n +;; "include $(CC_ROOT)/internal_tools/make/otp_release_targets.mk" n +;; n +;; "release_spec: opt" n +;; " $(INSTALL_DIR) $(RELSYSDIR)/src " n +;; " $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src " n +;; " $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src " n +;; " $(INSTALL_DIR) $(RELSYSDIR)/include " n +;; " $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include " n +;; " $(INSTALL_DIR) $(RELSYSDIR)/ebin " n +;; " $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin " n +;; n +;; "release_docs_spec:" n +;; )) +;; +;; +;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Erlang application wizard +;; + +(defun erlang-application-wizard (directory name) + "Creates all files and directories needed for an application. +The top-level directory is placed in DIRECTORY. NAME is used when +creating the root directory and for naming application files." + + (interactive "DApplication root directory: \nsName of application: ") + (let ((dir nil) + (lastchar (substring directory (- (length directory) 1))) + (apptype (completing-read "Type of application: " + '(("gen_server" 1) + ("gen_event" 2) + ("gen_fsm" 3) + ("other" 4)) + nil t "gen_server")) + (appname nil) + (apptemplate nil) + (apitemplate nil) + (extension nil)) + + (if (string= lastchar "/") + (setq dir directory) + (setq dir (concat directory "/"))) + + ;; determine type of application + (cond ((string= apptype "gen_server") + (setq extension "_server") + (setq appname (concat name extension)) + (setq apptemplate 'tempo-template-erlang-generic-server) + (setq apitemplate 'tempo-template-erlang-large-header)) + ((string= apptype "gen_event") + (setq extension "_event") + (setq appname (concat name extension)) + (setq apptemplate 'tempo-template-erlang-gen-event) + (setq apitemplate 'tempo-template-erlang-large-header)) + ((string= apptype "gen_fsm") + (setq extension "_fsm") + (setq appname (concat name extension)) + (setq apptemplate 'tempo-template-erlang-gen-fsm) + (setq apitemplate 'tempo-template-large-header)) + (t + ;; use defaults _work + (setq extension "_work") + (setq appname (concat name extension)) + (setq apptemplate 'tempo-template-erlang-large-header) + (setq apitemplate 'tempo-template-erlang-large-header))) + + (setq appwiz-erlang-modulename appname) + (setq appwiz-erlang-ext extension) + + ;; create directories + (make-directory (concat dir name "/" "src") t) + (make-directory (concat dir name "/" "ebin") t) + (make-directory (concat dir name "/" "include") t) + + ;; create directory content + ;;;;;;;;; .erl + (find-file (concat dir name "/" "src/" name ".erl")) + (funcall apitemplate) + (insert "API module for the application " name ".") + (save-buffer) + + ;;;;;;;;; _app.erl + (find-file (concat dir name "/" "src/" name "_app.erl")) + (tempo-template-erlang-application) + (insert "Application callback module for the application " name ".") + + (let ((quotedname (erlang-add-quotes-if-needed + (concat name "_sup"))) + (start (point))) + (while (search-forward "'TopSupervisor':start_link" nil t) + (replace-match (concat quotedname ":start_link") nil t)) + (goto-char start)) + + (save-buffer) + + ;;;;;;;;; _sup.erl + (find-file (concat dir name "/" "src/" name "_sup.erl")) + (tempo-template-erlang-supervisor) + (insert "Top level supervisor for the application " name ".") + + + (let ((quotedname (erlang-add-quotes-if-needed appname)) + (start (point))) + (while (search-forward "'AName'" nil t) + (replace-match quotedname nil t)) + (goto-char start)) + + (let ((quotedname (erlang-add-quotes-if-needed appname)) + (start (point))) + (goto-char 0) + (while (search-forward "'AMODULE'" nil t) + (replace-match quotedname nil t)) + (goto-char start)) + + (save-buffer) + + ;;;;;;;;; _sup.hrl + (find-file (concat dir name "/" "src/" name "_sup.hrl")) + (tempo-template-erlang-nomodule-header) + (save-buffer) + + ;;;;;;;;; _(application).erl + (find-file (concat dir name "/" "src/" appname ".erl")) + (funcall apptemplate) + (save-buffer) + + ;;;;;;;;; makefile (src) + (find-file (concat dir name "/" "src/makefile")) + (setq appwiz-erlang-modulename name) + (setq appwiz-erlang-ext extension) + (tempo-template-erlang-makefile-src) + (insert "Makefile for application " name ".") + (let ((start (point))) + (goto-char 0) + (while (search-forward "%" nil t) + (replace-match "#" nil t)) + (goto-char start)) + (save-buffer) + + ;;;;;;;;; makefile (middle) + (find-file (concat dir name "/" "makefile")) + (tempo-template-erlang-makefile-middle) + (insert "Makefile for application " name ".") + (let ((start (point))) + (goto-char 0) + (while (search-forward "%" nil t) + (replace-match "#" nil t)) + (goto-char start)) + (save-buffer) + + ;;;;;;;;; .app + (find-file (concat dir name "/" "ebin/" name ".app")) + (erlang-mode) + (tempo-template-erlang-app) + (insert "Application specification file for " name ".") + (save-buffer))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; These are setq:ed +;; + +(defvar appwiz-erlang-modulename "foo") +(defvar appwiz-erlang-ext "_work") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Skeletons. +;; Skeletons for nomodule header and .app file added by JB. +;; + +(defvar erlang-skel + '(("If" "if" erlang-skel-if) + ("Case" "case" erlang-skel-case) + ("Receive" "receive" erlang-skel-receive) + ("Receive After" "after" erlang-skel-receive-after) + ("Receive Loop" "loop" erlang-skel-receive-loop) + ("Module" "module" erlang-skel-module) + ("Author" "author" erlang-skel-author) + ("Query" "query" erlang-skel-query) + () + ("Small Header" "small-header" + erlang-skel-small-header erlang-skel-header) + ("Normal Header" "normal-header" + erlang-skel-normal-header erlang-skel-header) + ("Large Header" "large-header" + erlang-skel-large-header erlang-skel-header) + ("No Moudle Header" "nomodule-header" + erlang-skel-nomodule-header erlang-skel-header) + () + ("Small Server" "small-server" + erlang-skel-small-server erlang-skel-header) + () + ("application" "application" + erlang-skel-application erlang-skel-header) + ("app" "app" + erlang-skel-app erlang-skel-header) + ("supervisor" "supervisor" + erlang-skel-supervisor erlang-skel-header) + ("supervisor_bridge" "supervisor-bridge" + erlang-skel-supervisor-bridge erlang-skel-header) + ("gen_server" "generic-server" + erlang-skel-generic-server erlang-skel-header) + ("gen_event" "gen-event" + erlang-skel-gen-event erlang-skel-header) + ("gen_fsm" "gen-fsm" + erlang-skel-gen-fsm erlang-skel-header)) + "*Description of all skeletons templates. +Both functions and menu entries will be created. + +Each entry in `erlang-skel' should be a list with three or four +elements, or the empty list. + +The first element is the name which shows up in the menu. The second +is the `tempo' identfier (The string \"erlang-\" will be added in +front of it). The third is the skeleton descriptor, a variable +containing `tempo' attributes as described in the function +`tempo-define-template'. The optinal fourth elements denotes a +function which should be called when the menu is selected. + +Functions corresponding to every template will be created. The name +of the function will be `tempo-template-erlang-X' where `X' is the +tempo identifier as specified in the second argument of the elements +in this list. + +A list with zero elemets means that the a horisontal line should +be placed in the menu.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Template for .app file skeleton +;; + +(defvar erlang-skel-app + '((erlang-skel-include erlang-skel-nomodule-header) + "{application, " + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "," n> + "[{description, \"" (erlang-get-module-from-file-name) "\"}," n> + "{vsn, \"0.1\"}," n> + "{modules, [" + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "," n> + (erlang-add-quotes-if-needed + (concat (erlang-get-module-from-file-name) "_app")) "," n> + (erlang-add-quotes-if-needed + (concat (erlang-get-module-from-file-name) "_sup")) "," n> + (erlang-add-quotes-if-needed + (concat (erlang-get-module-from-file-name) appwiz-erlang-ext)) "]}," n> + "{registered, [" + (erlang-add-quotes-if-needed + (concat (erlang-get-module-from-file-name) appwiz-erlang-ext)) "," + (erlang-add-quotes-if-needed + (concat (erlang-get-module-from-file-name) "_sup")) "]}," n> + "{applications, [kernel," n> + "stdlib," n> + "sasl," n> + "mnesia]}," n> + "{env, []}," n> + "{mod, {" + (erlang-add-quotes-if-needed + (concat (erlang-get-module-from-file-name) "_app")) + ", []}}]}." n + ) + "*The template of an application file +Please see the function `tempo-define-template'.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Template for no-module header skeleton. +;; + +(defvar erlang-skel-nomodule-header + '(o (erlang-skel-separator) + (erlang-skel-include erlang-skel-copyright-comment + erlang-skel-file-comment + erlang-skel-author-comment) + "%%% Purpose : " p n + (erlang-skel-include erlang-skel-created-comment) + (erlang-skel-separator) n) + "*The template of a normal header. +Please see the function `tempo-define-template'.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; .app extension added. +;; + +(defvar erlang-file-name-extension-regexp "\\.\\(erl\\|hrl\\|app\\)$" + "*Regexp which should match an erlang file name. + +This regexp is used when an Erlang module name is extracted from the +name of an Erlang source file. + +The regexp should only match the section of the file name which should +be excluded from the module name. + +To match all files set this variable to \"\\\\(\\\\..*\\\\|\\\\)$\". +The matches all except the extension. This is useful if the Erlang +tags system should interpretate tags on the form `module:tag' for +files written in other languages than Erlang.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Wizard menu added. +;; + +(defvar erlang-menu-items + '(("Indent" + (("Indent Line" erlang-indent-command) + ("Indent Region " erlang-indent-region + (if erlang-xemacs-p (mark) mark-active)) + ("Indent Clause" erlang-indent-caluse) + ("Indent Function" erlang-indent-function) + ("Indent Buffer" erlang-indent-current-buffer))) + ("Edit" + (("Fill Comment" erlang-fill-paragraph) + ("Comment Region" comment-region + (if erlang-xemacs-p (mark) mark-active)) + ("Uncomment Region" erlang-uncomment-region + (if erlang-xemacs-p (mark) mark-active)) + nil + ("beginning of Function" erlang-beginning-of-function) + ("End of Function" erlang-end-of-function) + ("Mark Function" erlang-mark-function) + nil + ("beginning of Clause" erlang-beginning-of-clause) + ("End of Clause" erlang-end-of-clause) + ("Mark Clause" erlang-mark-clause) + nil + ("New Clause" erlang-generate-new-clause) + ("Clone Arguments" erlang-clone-arguments))) + ("Font Lock Mode" + (("Level 3" erlang-font-lock-level-3) + ("Level 2" erlang-font-lock-level-2) + ("Level 1" erlang-font-lock-level-1) + ("Off" erlang-font-lock-level-0))) + ("TAGS" + (("Find Tag" find-tag) + ("Find Next Tag" erlang-find-next-tag) + ;("Find Regexp" find-tag-regexp) + ("Complete Word" erlang-complete-tag) + ("Tags Apropos" tags-apropos) + ("Search Files" tags-search))) + nil + ("Erlang Shell" inferior-erlang-run-or-select) + ("Compile" erlang-compile) + ("Next Error" inferior-erlang-next-error) + nil + ("Version" erlang-version) + nil + ("Wizards" + (("Application Wizard" erlang-application-wizard)))) + "*Description of menu used in Erlang mode. + +This variable must be a list. The elements are either nil representing +a horisontal line or a list with two or three elements. The first is +the name of the menu item, the second is the function to call, or a +submenu, on the same same form as ITEMS. The third optional argument +is an expression which is evaluated every time the menu is displayed. +Should the expression evaluate to nil the menu item is ghosted. + +Example: + '((\"Func1\" function-one) + (\"SubItem\" + ((\"Yellow\" function-yellow) + (\"Blue\" function-blue))) + nil + (\"Region Funtion\" spook-function midnight-variable)) + +Call the function `erlang-menu-init' after modifying this variable.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Prefixing space removed from date string +;; + +(defun erlang-skel-d-mmm-yyyy () + "Return the current date as a string in \"DD Mon YYYY\" form. +The first character of DD is *not* space if the value is less than 10." + (let ((date (current-time-string))) + (format "%d %s %s" + (string-to-int (substring date 8 10)) + (substring date 4 7) + (substring date -4)))) + +(defvar erlang-skel-date-function 'erlang-skel-d-mmm-yyyy + "*Function which returns date string. +Look in the module `time-stamp' for a battery of functions.") + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Fixed skeletons. erlang-add-quotes-if-needed introduced where needed. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Server templates. + +(defvar erlang-skel-small-server + '((erlang-skel-include erlang-skel-large-header) + "-export([start/0,init/1])." n n n + "start() ->" n> "spawn(" + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) + ", init, [self()])." n n + "init(From) ->" n> + "loop(From)." n n + "loop(From) ->" n> + "receive" n> + p "_ ->" n> + "loop(From)" n> + "end." + ) + "*Template of a small server. +Please see the function `tempo-define-template'.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Behaviour templates. + +(defvar erlang-skel-application + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(application)." n + n + "%% application callbacks" n + "-export([start/2, stop/1])." n n + (erlang-skel-separator) + "%%% Callback functions from application" n + (erlang-skel-separator) + n + (erlang-skel-separator 2) + "%% Func: start/2" n + "%% Returns: {ok, Pid} |" n + "%% {ok, Pid, State} |" n + "%% {error, Reason} " n + (erlang-skel-separator 2) + "start(Type, StartArgs) ->" n> + "case 'TopSupervisor':start_link(StartArgs) of" n> + "{ok, Pid} -> " n> + "{ok, Pid};" n> + "Error ->" n> + "Error" n> + "end." n + n + (erlang-skel-separator 2) + "%% Func: stop/1" n + "%% Returns: any "n + (erlang-skel-separator 2) + "stop(State) ->" n> + "ok." n + n + (erlang-skel-separator) + "%%% Internal functions" n + (erlang-skel-separator) + ) + "*The template of an application behaviour. +Please see the function `tempo-define-template'.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar erlang-skel-supervisor + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(supervisor)." n + n + "%% External exports" n + "-export([start_link/1])." n + n + "%% supervisor callbacks" n + "-export([init/1])." n n + (erlang-skel-separator) + "%%% API" n + (erlang-skel-separator) + "start_link(StartArgs) ->" n> + "supervisor:start_link({local, " + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "}, " + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) + ", StartArgs)." n + n + (erlang-skel-separator) + "%%% Callback functions from supervisor" n + (erlang-skel-separator) + n + (erlang-skel-separator 2) + "%% Func: init/1" n + "%% Returns: {ok, {SupFlags, [ChildSpec]}} |" n + "%% ignore |" n + "%% {error, Reason} " n + (erlang-skel-separator 2) + "init(StartArgs) ->" n> + "AChild = {'AName',{'AModule',start_link,[]}," n> + "permanent,2000,worker,['AModule']}," n> + "{ok,{{one_for_all,4,3600}, [AChild]}}." n + n + (erlang-skel-separator) + "%%% Internal functions" n + (erlang-skel-separator) + ) + "*The template of an supervisor behaviour. +Please see the function `tempo-define-template'.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar erlang-skel-supervisor-bridge + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(supervisor_bridge)." n + n + "%% External exports" n + "-export([start_link/0])." n + n + "%% supervisor callbacks" n + "-export([init/1, terminate/2])." n n + "-record(state, {})." n + n + (erlang-skel-separator) + "%%% API" n + (erlang-skel-separator) + "start_link() -> " n> + "supervisor_bridge:start_link({local, " + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "}, " + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) + ", [])." n + n + (erlang-skel-separator) + "%%% Callback functions from supervisor_bridge" n + (erlang-skel-separator) + n + (erlang-skel-separator 2) + "%% Func: init/1" n + "%% Returns: {ok, Pid, State} |" n + "%% ignore |" n + "%% {error, Reason} " n + (erlang-skel-separator 2) + "init([]) ->" n> + "case 'AModule':start_link() of" n> + "{ok, Pid} ->" n> + "{ok, Pid, #state{}};" n> + "Error ->" n> + "Error" n> + "end." n + n + (erlang-skel-separator 2) + "%% Func: terminate/2" n + "%% Purpose: Synchronized shutdown of the underlying sub system." n + "%% Returns: any" n + (erlang-skel-separator 2) + "terminate(Reason, State) ->" n> + "'AModule':stop()," n> + "ok." n + n + (erlang-skel-separator) + "%%% Internal functions" n + (erlang-skel-separator) + ) + "*The template of an supervisor_bridge behaviour. +Please see the function `tempo-define-template'.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar erlang-skel-generic-server + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(gen_server)." n + n + "%% External exports" n + "-export([start_link/0])." n + n + "%% gen_server callbacks" n + "-export([init/1, handle_call/3, handle_cast/2, " + "handle_info/2, terminate/2])." n n + "-record(state, {})." n + n + (erlang-skel-separator) + "%%% API" n + (erlang-skel-separator) + "start_link() -> " n> + "gen_server:start_link({local, " + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "}, " + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) + ", [], [])." n + n + (erlang-skel-separator) + "%%% Callback functions from gen_server" n + (erlang-skel-separator) + n + (erlang-skel-separator 2) + "%% Func: init/1" n + "%% Returns: {ok, State} |" n + "%% {ok, State, Timeout} |" n + "%% ignore |" n + "%% {stop, Reason}" n + (erlang-skel-separator 2) + "init([]) ->" n> + "{ok, #state{}}." n + n + (erlang-skel-separator 2) + "%% Func: handle_call/3" n + "%% Returns: {reply, Reply, State} |" n + "%% {reply, Reply, State, Timeout} |" n + "%% {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, Reply, State} | (terminate/2 is called)" n + "%% {stop, Reason, State} (terminate/2 is called)" n + (erlang-skel-separator 2) + "handle_call(Request, From, State) ->" n> + "Reply = ok," n> + "{reply, Reply, State}." n + n + (erlang-skel-separator 2) + "%% Func: handle_cast/2" n + "%% Returns: {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, State} (terminate/2 is called)" n + (erlang-skel-separator 2) + "handle_cast(Msg, State) ->" n> + "{noreply, State}." n + n + (erlang-skel-separator 2) + "%% Func: handle_info/2" n + "%% Returns: {noreply, State} |" n + "%% {noreply, State, Timeout} |" n + "%% {stop, Reason, State} (terminate/2 is called)" n + (erlang-skel-separator 2) + "handle_info(Info, State) ->" n> + "{noreply, State}." n + n + (erlang-skel-separator 2) + "%% Func: terminate/2" n + "%% Purpose: Shutdown the server" n + "%% Returns: any (ignored by gen_server)" n + (erlang-skel-separator 2) + "terminate(Reason, State) ->" n> + "ok." n + n + (erlang-skel-separator) + "%%% Internal functions" n + (erlang-skel-separator) + ) + "*The template of a generic server. +Please see the function `tempo-define-template'.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar erlang-skel-gen-event + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(gen_event)." n + n + "%% External exports" n + "-export([start_link/0, add_handler/0])." n + n + "%% gen_event callbacks" n + "-export([init/1, handle_event/2, handle_call/2, " + "handle_info/2, terminate/2])." n n + "-record(state, {})." n + n + (erlang-skel-separator) + "%%% API" n + (erlang-skel-separator) + "start_link() ->" n> + "gen_event:start_link({local, " + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "}). " n + n + "add_handler() ->" n> + "gen_event:add_handler(" + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) ", " + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) + ", [])." n + n + (erlang-skel-separator) + "%%% Callback functions from gen_event" n + (erlang-skel-separator) + n + (erlang-skel-separator 2) + "%% Func: init/1" n + "%% Returns: {ok, State} |" n + "%% Other" n + (erlang-skel-separator 2) + "init([]) ->" n> + "{ok, #state{}}." n + n + (erlang-skel-separator 2) + "%% Func: handle_event/2" n + "%% Returns: {ok, State} |" n + "%% {swap_handler, Args1, State1, Mod2, Args2} |" n + "%% remove_handler " n + (erlang-skel-separator 2) + "handle_event(Event, State) ->" n> + "{ok, State}." n + n + (erlang-skel-separator 2) + "%% Func: handle_call/2" n + "%% Returns: {ok, Reply, State} |" n + "%% {swap_handler, Reply, Args1, State1, Mod2, Args2} |" n + "%% {remove_handler, Reply} " n + (erlang-skel-separator 2) + "handle_call(Request, State) ->" n> + "Reply = ok," n> + "{ok, Reply, State}." n + n + (erlang-skel-separator 2) + "%% Func: handle_info/2" n + "%% Returns: {ok, State} |" n + "%% {swap_handler, Args1, State1, Mod2, Args2} |" n + "%% remove_handler " n + (erlang-skel-separator 2) + "handle_info(Info, State) ->" n> + "{ok, State}." n + n + (erlang-skel-separator 2) + "%% Func: terminate/2" n + "%% Purpose: Shutdown the server" n + "%% Returns: any" n + (erlang-skel-separator 2) + "terminate(Reason, State) ->" n> + "ok." n + n + (erlang-skel-separator) + "%%% Internal functions" n + (erlang-skel-separator) + ) + "*The template of a gen_event. +Please see the function `tempo-define-template'.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar erlang-skel-gen-fsm + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(gen_fsm)." n + n + "%% External exports" n + "-export([start_link/0])." n + n + "%% gen_fsm callbacks" n + "-export([init/1, state_name/2, state_name/3, handle_event/3," n> + "handle_sync_event/4, handle_info/3, terminate/3])." n n + "-record(state, {})." n + n + (erlang-skel-separator) + "%%% API" n + (erlang-skel-separator) + "start_link() ->" n> + "gen_fsm:start_link({local, " + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) "}, " + (erlang-add-quotes-if-needed (erlang-get-module-from-file-name)) + ", [], [])." n + n + (erlang-skel-separator) + "%%% Callback functions from gen_fsm" n + (erlang-skel-separator) + n + (erlang-skel-separator 2) + "%% Func: init/1" n + "%% Returns: {ok, StateName, StateData} |" n + "%% {ok, StateName, StateData, Timeout} |" n + "%% ignore |" n + "%% {stop, StopReason} " n + (erlang-skel-separator 2) + "init([]) ->" n> + "{ok, state_name, #state{}}." n + n + (erlang-skel-separator 2) + "%% Func: StateName/2" n + "%% Returns: {next_state, NextStateName, NextStateData} |" n + "%% {next_state, NextStateName, NextStateData, Timeout} |" n + "%% {stop, Reason, NewStateData} " n + (erlang-skel-separator 2) + "state_name(Event, StateData) ->" n> + "{nextstate, state_name, StateData}." n + n + (erlang-skel-separator 2) + "%% Func: StateName/3" n + "%% Returns: {next_state, NextStateName, NextStateData} |" n + "%% {next_state, NextStateName, NextStateData, Timeout} |" n + "%% {reply, Reply, NextStateName, NextStateData} |" n + "%% {reply, Reply, NextStateName, NextStateData, Timeout} |" n + "%% {stop, Reason, NewStateData} |" n + "%% {stop, Reason, Reply, NewStateData} " n + (erlang-skel-separator 2) + "state_name(Event, From, StateData) ->" n> + "Reply = ok," n> + "{reply, Reply, state_name, StateData}." n + n + (erlang-skel-separator 2) + "%% Func: handle_event/3" n + "%% Returns: {next_state, NextStateName, NextStateData} |" n + "%% {next_state, NextStateName, NextStateData, Timeout} |" n + "%% {stop, Reason, NewStateData} " n + (erlang-skel-separator 2) + "handle_event(Event, StateName, StateData) ->" n> + "{nextstate, StateName, StateData}." n + n + (erlang-skel-separator 2) + "%% Func: handle_sync_event/4" n + "%% Returns: {next_state, NextStateName, NextStateData} |" n + "%% {next_state, NextStateName, NextStateData, Timeout} |" n + "%% {reply, Reply, NextStateName, NextStateData} |" n + "%% {reply, Reply, NextStateName, NextStateData, Timeout} |" n + "%% {stop, Reason, NewStateData} |" n + "%% {stop, Reason, Reply, NewStateData} " n + (erlang-skel-separator 2) + "handle_sync_event(Event, From, StateName, StateData) ->" n> + "Reply = ok," n> + "{reply, Reply, StateName, StateData}." n + n + (erlang-skel-separator 2) + "%% Func: handle_info/3" n + "%% Returns: {next_state, NextStateName, NextStateData} |" n + "%% {next_state, NextStateName, NextStateData, Timeout} |" n + "%% {stop, Reason, NewStateData} " n + (erlang-skel-separator 2) + "handle_info(Info, StateName, StateData) ->" n> + "{nextstate, StateName, StateData}." n + n + (erlang-skel-separator 2) + "%% Func: terminate/3" n + "%% Purpose: Shutdown the fsm" n + "%% Returns: any" n + (erlang-skel-separator 2) + "terminate(Reason, StateName, StatData) ->" n> + "ok." n + n + (erlang-skel-separator) + "%%% Internal functions" n + (erlang-skel-separator) + ) + "*The template of a gen_fsm. +Please see the function `tempo-define-template'.") + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Original erlang-add-quotes-if-needed is broken, we install a +;; new version. +;; + +(add-hook 'erlang-load-hook 'my-erlang-load-mods) + +(defun fixed-erlang-add-quotes-if-needed (str) + "Return STR, possibly with quotes." + (let ((saved-case-fold-search case-fold-search) + (result nil)) + (setq case-fold-search nil) + (setq result (if (string-match (concat "\\`" erlang-atom-regexp "\\'") str) + str + (concat "'" str "'"))) + (setq case-fold-search saved-case-fold-search) + result)) + +(defun my-erlang-load-mods () + (fset 'erlang-add-quotes-if-needed + (symbol-function 'fixed-erlang-add-quotes-if-needed)) + (appwiz-skel-init)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Additional skeletons which are not shown in the Erlang menu. +;; + +(defvar appwiz-skel + '( +; ("generic-server-no-api" erlang-skel-generic-server-no-api) +; ("generic-server-api" erlang-skel-generic-server-api) +; ("gen-event-no-api" erlang-skel-gen-event-no-api) +; ("gen-event-api" erlang-skel-gen-event-api) +; ("gen-fsm-no-api" erlang-skel-gen-fsm-no-api) +; ("gen-fsm-api" erlang-skel-gen-fsm-api) + ("makefile-middle" erlang-skel-makefile-middle) + ("makefile-src" erlang-skel-makefile-src))) + +(defun appwiz-skel-init () + "Generate the skeleton functions." + (interactive) + (condition-case nil + (require 'tempo) + (error t)) + (if (featurep 'tempo) + (let ((skel appwiz-skel)) + (while skel + (funcall (symbol-function 'tempo-define-template) + (concat "erlang-" (nth 0 (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 1 (car skel)))) + (nth 0 (car skel))) + (setq skel (cdr skel)))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; +;; +;;(defvar erlang-skel-generic-server-no-api +;; '((erlang-skel-include erlang-skel-large-header) +;; "-behaviour(gen_server)." n +;; n +;; "%% gen_server callbacks" n +;; "-export([init/1, handle_call/3, handle_cast/2, " +;; "handle_info/2, terminate/2])." n n +;; "-record(state, {})." n +;; n +;; (erlang-skel-separator) +;; "%%% Callback functions from gen_server" n +;; (erlang-skel-separator) +;; n +;; (erlang-skel-separator 2) +;; "%% Func: init/1" n +;; "%% Returns: {ok, State} |" n +;; "%% {ok, State, Timeout} |" n +;; "%% ignore |" n +;; "%% {stop, Reason}" n +;; (erlang-skel-separator 2) +;; "init([]) ->" n> +;; "{ok, #state{}}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: handle_call/3" n +;; "%% Returns: {reply, Reply, State} |" n +;; "%% {reply, Reply, State, Timeout} |" n +;; "%% {noreply, State} |" n +;; "%% {noreply, State, Timeout} |" n +;; "%% {stop, Reason, Reply, State} | (terminate/2 is called)" n +;; "%% {stop, Reason, State} (terminate/2 is called)" n +;; (erlang-skel-separator 2) +;; "handle_call(Request, From, State) ->" n> +;; "Reply = ok," n> +;; "{reply, Reply, State}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: handle_cast/2" n +;; "%% Returns: {noreply, State} |" n +;; "%% {noreply, State, Timeout} |" n +;; "%% {stop, Reason, State} (terminate/2 is called)" n +;; (erlang-skel-separator 2) +;; "handle_cast(Msg, State) ->" n> +;; "{noreply, State}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: handle_info/2" n +;; "%% Returns: {noreply, State} |" n +;; "%% {noreply, State, Timeout} |" n +;; "%% {stop, Reason, State} (terminate/2 is called)" n +;; (erlang-skel-separator 2) +;; "handle_info(Info, State) ->" n> +;; "{noreply, State}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: terminate/2" n +;; "%% Purpose: Shutdown the server" n +;; "%% Returns: any (ignored by gen_server)" n +;; (erlang-skel-separator 2) +;; "terminate(Reason, State) ->" n> +;; "ok." n +;; n +;; (erlang-skel-separator) +;; "%%% Internal functions" n +;; (erlang-skel-separator) +;; ) +;; "*The template of a generic server. +;;Please see the function `tempo-define-template'.") +;; +;;(defvar erlang-skel-generic-server-api +;; '((erlang-skel-include erlang-skel-large-header) +;; "%% External exports" n +;; "-export([start_link/0])." n +;; n +;; (erlang-skel-separator) +;; "%%% API" n +;; (erlang-skel-separator) +;; "start_link() ->" n> +;; "gen_server:start_link({local, " +;; (erlang-add-quotes-if-needed +;; (concat (erlang-get-module-from-file-name) "_server")) "}, " +;; (erlang-add-quotes-if-needed +;; (concat (erlang-get-module-from-file-name) "_server")) ", [], [])." n +;; n +;; )) +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; +;; +;;(defvar erlang-skel-gen-event-no-api +;; '((erlang-skel-include erlang-skel-large-header) +;; "-behaviour(gen_event)." n +;; n +;; "%% gen_event callbacks" n +;; "-export([init/1, handle_event/2, handle_call/2, " +;; "handle_info/2, terminate/2])." n n +;; "-record(state, {})." n +;; n +;; (erlang-skel-separator) +;; "%%% Callback functions from gen_event" n +;; (erlang-skel-separator) +;; n +;; (erlang-skel-separator 2) +;; "%% Func: init/1" n +;; "%% Returns: {ok, State} |" n +;; "%% Other" n +;; (erlang-skel-separator 2) +;; "init([]) ->" n> +;; "{ok, #state{}}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: handle_event/2" n +;; "%% Returns: {ok, State} |" n +;; "%% {swap_handler, Args1, State1, Mod2, Args2} |" n +;; "%% remove_handler " n +;; (erlang-skel-separator 2) +;; "handle_event(Event, State) ->" n> +;; "{ok, State}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: handle_call/2" n +;; "%% Returns: {ok, Reply, State} |" n +;; "%% {swap_handler, Reply, Args1, State1, Mod2, Args2} |" n +;; "%% {remove_handler, Reply} " n +;; (erlang-skel-separator 2) +;; "handle_call(Request, State) ->" n> +;; "Reply = ok," n> +;; "{ok, Reply, State}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: handle_info/2" n +;; "%% Returns: {ok, State} |" n +;; "%% {swap_handler, Args1, State1, Mod2, Args2} |" n +;; "%% remove_handler " n +;; (erlang-skel-separator 2) +;; "handle_info(Info, State) ->" n> +;; "{ok, State}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: terminate/2" n +;; "%% Purpose: Shutdown the server" n +;; "%% Returns: any" n +;; (erlang-skel-separator 2) +;; "terminate(Reason, State) ->" n> +;; "ok." n +;; n +;; (erlang-skel-separator) +;; "%%% Internal functions" n +;; (erlang-skel-separator) +;; ) +;; "*The template of a gen_event. +;;Please see the function `tempo-define-template'.") +;; +;;(defvar erlang-skel-gen-event-api +;; '((erlang-skel-include erlang-skel-large-header) +;; "%% External exports" n +;; "-export([start_link/0, add_handler/0])." n +;; n +;; (erlang-skel-separator) +;; "%%% API" n +;; (erlang-skel-separator) +;; "start_link() ->" n> +;; "gen_event:start_link({local, " +;; (erlang-add-quotes-if-needed +;; (concat (erlang-get-module-from-file-name) "_event")) "}). " n +;; n +;; "add_handler() ->" n> +;; "gen_event:add_handler(" +;; (erlang-add-quotes-if-needed +;; (concat (erlang-get-module-from-file-name) "_event")) ", " +;; (erlang-add-quotes-if-needed +;; (concat (erlang-get-module-from-file-name) "_event")) ", [])." n +;; n)) +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; +;; +;;(defvar erlang-skel-gen-fsm +;; '((erlang-skel-include erlang-skel-large-header) +;; "-behaviour(gen_fsm)." n +;; n +;; "%% gen_fsm callbacks" n +;; "-export([init/1, state_name/2, state_name/3, handle_event/3," n> +;; "handle_sync_event/4, handle_info/3, terminate/3])." n n +;; "-record(state, {})." n +;; n +;; (erlang-skel-separator) +;; "%%% Callback functions from gen_fsm" n +;; (erlang-skel-separator) +;; n +;; (erlang-skel-separator 2) +;; "%% Func: init/1" n +;; "%% Returns: {ok, StateName, StateData} |" n +;; "%% {ok, StateName, StateData, Timeout} |" n +;; "%% ignore |" n +;; "%% {stop, StopReason} " n +;; (erlang-skel-separator 2) +;; "init([]) ->" n> +;; "{ok, state_name, #state{}}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: StateName/2" n +;; "%% Returns: {next_state, NextStateName, NextStateData} |" n +;; "%% {next_state, NextStateName, NextStateData, Timeout} |" n +;; "%% {stop, Reason, NewStateData} " n +;; (erlang-skel-separator 2) +;; "state_name(Event, StateData) ->" n> +;; "{nextstate, state_name, StateData}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: StateName/3" n +;; "%% Returns: {next_state, NextStateName, NextStateData} |" n +;; "%% {next_state, NextStateName, NextStateData, Timeout} |" n +;; "%% {reply, Reply, NextStateName, NextStateData} |" n +;; "%% {reply, Reply, NextStateName, NextStateData, Timeout} |" n +;; "%% {stop, Reason, NewStateData} |" n +;; "%% {stop, Reason, Reply, NewStateData} " n +;; (erlang-skel-separator 2) +;; "state_name(Event, From, StateData) ->" n> +;; "Reply = ok," n> +;; "{reply, Reply, state_name, StateData}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: handle_event/3" n +;; "%% Returns: {next_state, NextStateName, NextStateData} |" n +;; "%% {next_state, NextStateName, NextStateData, Timeout} |" n +;; "%% {stop, Reason, NewStateData} " n +;; (erlang-skel-separator 2) +;; "handle_event(Event, StateName, StateData) ->" n> +;; "{nextstate, StateName, StateData}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: handle_sync_event/4" n +;; "%% Returns: {next_state, NextStateName, NextStateData} |" n +;; "%% {next_state, NextStateName, NextStateData, Timeout} |" n +;; "%% {reply, Reply, NextStateName, NextStateData} |" n +;; "%% {reply, Reply, NextStateName, NextStateData, Timeout} |" n +;; "%% {stop, Reason, NewStateData} |" n +;; "%% {stop, Reason, Reply, NewStateData} " n +;; (erlang-skel-separator 2) +;; "handle_sync_event(Event, From, StateName, StateData) ->" n> +;; "Reply = ok," n> +;; "{reply, Reply, StateName, StateData}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: handle_info/3" n +;; "%% Returns: {next_state, NextStateName, NextStateData} |" n +;; "%% {next_state, NextStateName, NextStateData, Timeout} |" n +;; "%% {stop, Reason, NewStateData} " n +;; (erlang-skel-separator 2) +;; "handle_info(Info, StateName, StateData) ->" n> +;; "{nextstate, StateName, StateData}." n +;; n +;; (erlang-skel-separator 2) +;; "%% Func: terminate/3" n +;; "%% Purpose: Shutdown the fsm" n +;; "%% Returns: any" n +;; (erlang-skel-separator 2) +;; "terminate(Reason, StateName, StatData) ->" n> +;; "ok." n +;; n +;; (erlang-skel-separator) +;; "%%% Internal functions" n +;; (erlang-skel-separator) +;; ) +;; "*The template of a gen_fsm. +;;Please see the function `tempo-define-template'.") +;; +;;(defvar erlang-skel-gen-fsm-no-api +;; '((erlang-skel-include erlang-skel-large-header) +;; "%% External exports" n +;; "-export([start_link/0])." n +;; n +;; (erlang-skel-separator) +;; "%%% API" n +;; (erlang-skel-separator) +;; "start_link() ->" n> +;; "gen_fsm:start_link({local, " +;; (erlang-add-quotes-if-needed +;; (concat (erlang-get-module-from-file-name) "_fsm")) "}, " +;; (erlang-add-quotes-if-needed +;; (concat (erlang-get-module-from-file-name) "_fsm")) ", [], [])." n +;; n +;; )) +;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; requires that the variables appwiz-erlang-modulename and +;; appwiz-erlang-ext are defined. +;; + +(defvar erlang-skel-makefile-src + '((erlang-skel-include erlang-skel-nomodule-header) + "MAKE = make" n + n + "ERL = erlc" n + n + "EBIN = ../ebin" n + n + (erlang-skel-makefile-separator) + n + (upcase appwiz-erlang-modulename) "_HEADER_FILES = " + appwiz-erlang-modulename "_sup.hrl" n + n + (upcase appwiz-erlang-modulename) "_SOURCE_FILES = \\" n + " " appwiz-erlang-modulename ".erl" " " + appwiz-erlang-modulename "_sup.erl \\" n + " " appwiz-erlang-modulename "_app.erl" " " + appwiz-erlang-modulename appwiz-erlang-ext ".erl" n + n + (upcase appwiz-erlang-modulename) "_OBJECT_FILES = $(" + (upcase appwiz-erlang-modulename) "_SOURCE_FILES:.erl=.jam)" n + n + n + (erlang-skel-makefile-separator) + "#" n + "# Transformations " n + "#" n + n + ".erl.jam:" n + " $(ERL) $<" n + n + (erlang-skel-makefile-separator) n + n + n + "def : " + appwiz-erlang-modulename n + n + appwiz-erlang-modulename ": $(" + (upcase appwiz-erlang-modulename) "_OBJECT_FILES)" n + " cp $(" (upcase appwiz-erlang-modulename) "_OBJECT_FILES) " + "$(EBIN)" n + n + "clean :" n + " /bin/rm -f $(" (upcase appwiz-erlang-modulename) + "_OBJECT_FILES)" n + n + "$(" (upcase appwiz-erlang-modulename) "_OBJECT_FILES): $(" + (upcase appwiz-erlang-modulename) "_HEADER_FILES)" n + n + ".SUFFIXES : .erl .jam" n + n + )) + +(defvar erlang-skel-makefile-middle + '((erlang-skel-include erlang-skel-nomodule-header) + "MAKE = make" n + n + (erlang-skel-makefile-separator) + n + "def:" n + " (cd src ; $(MAKE))" n + n + "clean:" n + " (cd src ; $(MAKE) clean)" n + n + )) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun erlang-skel-makefile-separator () + "Return a comment separator." + (concat (make-string 70 ?\#) "\n")) diff --git a/emacs.d/elpa/flymake-elixir-0.5/flymake-elixir-autoloads.el b/emacs.d/elpa/flymake-elixir-0.5/flymake-elixir-autoloads.el new file mode 100644 index 0000000..dfc34e3 --- /dev/null +++ b/emacs.d/elpa/flymake-elixir-0.5/flymake-elixir-autoloads.el @@ -0,0 +1,22 @@ +;;; flymake-elixir-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "flymake-elixir" "flymake-elixir.el" (21721 +;;;;;; 24080 0 0)) +;;; Generated autoloads from flymake-elixir.el + +(autoload 'flymake-elixir-load "flymake-elixir" "\ +Configure flymake mode to check the current buffer's elixir syntax. + +\(fn)" t nil) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; flymake-elixir-autoloads.el ends here diff --git a/emacs.d/elpa/flymake-elixir-0.5/flymake-elixir-pkg.el b/emacs.d/elpa/flymake-elixir-0.5/flymake-elixir-pkg.el new file mode 100644 index 0000000..43f88fa --- /dev/null +++ b/emacs.d/elpa/flymake-elixir-0.5/flymake-elixir-pkg.el @@ -0,0 +1 @@ +(define-package "flymake-elixir" "0.5" "A flymake handler for elixir-mode .ex files." 'nil) diff --git a/emacs.d/elpa/flymake-elixir-0.5/flymake-elixir.el b/emacs.d/elpa/flymake-elixir-0.5/flymake-elixir.el new file mode 100644 index 0000000..6c988a2 --- /dev/null +++ b/emacs.d/elpa/flymake-elixir-0.5/flymake-elixir.el @@ -0,0 +1,71 @@ +;;; flymake-elixir.el --- A flymake handler for elixir-mode .ex files. +;; +;; Copyright (C) 2010-2013 Sylvain Benner +;; +;;; Author: Sylvain Benner +;;; Created: 10 Apr 2013 +;; Version: 0.5 +;;; Package-Pequires: ((flymake-easy "0.1")) +;; +;; This file is not part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . +;; +;;; Commentary: +;; +;; Usage: +;; (require 'flymake-elixir) +;; (add-hook 'elixir-mode-hook 'flymake-elixir-load) +;; +;; Based on flymake-ruby, from Steve Purcell +;; +;; Uses flymake-easy, from Steve Purcell + +;;; Code: + +(require 'flymake-easy) + +(defconst flymake-elixir-err-line-patterns + '(("^\\(** (.*) \\)?\\(.*\.ex\\):\\([0-9]+\\): \\(.*\\)$" 2 3 nil 4))) +(defconst flymake-elixir-warn-regex + (regexp-opt (list "^redefining" "^export_all" "future reserved" "deprecated" + "shadowed" "always matches$" "obsolete$" "unused$") t)) + +(defvar flymake-elixir-executable "elixirc" + "The elixir executable to use for syntax checking.") + +(defun flymake-elixir-command (filename) + "Construct a command that flymake can use to check elixir source." + (list flymake-elixir-executable + "--ignore-module-conflict" ; needed to prevent from module redefinition warning. + "+warn_obsolete_guard" + "+warn_unused_import" + "+warn_shadow_vars" + "+warn_export_vars" + "+strong_validation" + "+report" + filename)) + +;;;###autoload +(defun flymake-elixir-load () + "Configure flymake mode to check the current buffer's elixir syntax." + (interactive) + (flymake-easy-load 'flymake-elixir-command + flymake-elixir-err-line-patterns + 'tempdir + "ex" + flymake-elixir-warn-regex)) + +(provide 'flymake-elixir) +;;; flymake-elixir.el ends here diff --git a/emacs.d/elpa/flymake-jslint-20130613.202/flymake-jslint-autoloads.el b/emacs.d/elpa/flymake-jslint-20130613.202/flymake-jslint-autoloads.el new file mode 100644 index 0000000..03783c3 --- /dev/null +++ b/emacs.d/elpa/flymake-jslint-20130613.202/flymake-jslint-autoloads.el @@ -0,0 +1,37 @@ +;;; flymake-jslint-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "flymake-jslint" "flymake-jslint.el" (21806 +;;;;;; 34593 0 0)) +;;; Generated autoloads from flymake-jslint.el + +(defvar flymake-jslint-detect-trailing-comma t "\ +Whether or not to report warnings about trailing commas.") + +(custom-autoload 'flymake-jslint-detect-trailing-comma "flymake-jslint" t) + +(defvar flymake-jslint-command (if (executable-find "jsl") "jsl" "jslint") "\ +Name (and optionally full path) of jslint executable.") + +(custom-autoload 'flymake-jslint-command "flymake-jslint" t) + +(defvar flymake-jslint-args (unless (string-equal "jsl" flymake-jslint-command) (mapcar 'symbol-name '(--white --undef --nomen --regexp --plusplus --bitwise --newcap --sloppy --vars --eqeq))) "\ +Command-line args for jslint executable.") + +(custom-autoload 'flymake-jslint-args "flymake-jslint" t) + +(autoload 'flymake-jslint-load "flymake-jslint" "\ +Configure flymake mode to check the current buffer's javascript syntax. + +\(fn)" t nil) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; flymake-jslint-autoloads.el ends here diff --git a/emacs.d/elpa/flymake-jslint-20130613.202/flymake-jslint-pkg.el b/emacs.d/elpa/flymake-jslint-20130613.202/flymake-jslint-pkg.el new file mode 100644 index 0000000..81989a6 --- /dev/null +++ b/emacs.d/elpa/flymake-jslint-20130613.202/flymake-jslint-pkg.el @@ -0,0 +1 @@ +(define-package "flymake-jslint" "20130613.202" "A flymake handler for javascript using jslint" '((flymake-easy "0.1")) :url "https://github.com/purcell/flymake-jslint") diff --git a/emacs.d/elpa/flymake-jslint-20130613.202/flymake-jslint.el b/emacs.d/elpa/flymake-jslint-20130613.202/flymake-jslint.el new file mode 100644 index 0000000..649bd9d --- /dev/null +++ b/emacs.d/elpa/flymake-jslint-20130613.202/flymake-jslint.el @@ -0,0 +1,88 @@ +;;; flymake-jslint.el --- A flymake handler for javascript using jslint +;; +;;; Author: Steve Purcell +;;; Homepage: https://github.com/purcell/flymake-jslint +;;; Version: DEV +;; Package-Version: 20130613.202 +;;; Package-Requires: ((flymake-easy "0.1")) +;; +;;; Commentary: +;; +;; References: +;; http://www.emacswiki.org/cgi-bin/wiki/FlymakeJavaScript +;; http://d.hatena.ne.jp/kazu-yamamoto/mobile?date=20071029 +;; +;; Works with either "jslint" from jslint.com, or "jsl" from +;; javascriptlint.com. The default is "jsl", if that executable is +;; found at load-time. Otherwise, "jslint" is the default. If you want +;; to use the non-default checker, you can customize the values of +;; `flymake-jslint-command' and `flymake-jslint-args' accordingly. +;; +;; Usage: +;; (require 'flymake-jslint) +;; (add-hook 'js-mode-hook 'flymake-jslint-load) +;; +;; Uses flymake-easy, from https://github.com/purcell/flymake-easy + +;;; Code: + +(require 'flymake-easy) + +(defgroup flymake-jslint nil + "Flymake checking of Javascript using jslint" + :group 'programming + :prefix "flymake-jslint-") + +;;;###autoload +(defcustom flymake-jslint-detect-trailing-comma t + "Whether or not to report warnings about trailing commas." + :type 'boolean :group 'flymake-jslint) + +;;;###autoload +(defcustom flymake-jslint-command + (if (executable-find "jsl") "jsl" "jslint") + "Name (and optionally full path) of jslint executable." + :type 'string :group 'flymake-jslint) + +;;;###autoload +(defcustom flymake-jslint-args + (unless (string-equal "jsl" flymake-jslint-command) + (mapcar + 'symbol-name + '(--white --undef --nomen --regexp --plusplus --bitwise --newcap --sloppy --vars --eqeq))) + "Command-line args for jslint executable." + :type '(repeat string) :group 'flymake-jslint) + +(defconst flymake-jslint-err-line-patterns + '(("^ *#[0-9]+ \\(.*?\\)\n.*?// Line \\([0-9]+\\), Pos \\([0-9]+\\)$" nil 2 3 1) + ;; jsl + ("^\\(.+\\)\:\\([0-9]+\\)\: \\(SyntaxError\:.+\\)\:$" nil 2 nil 3) + ("^\\(.+\\)(\\([0-9]+\\)): \\(SyntaxError:.+\\)$" nil 2 nil 3) + ("^\\(.+\\)(\\([0-9]+\\)): \\(lint \\)?\\(warning:.+\\)$" nil 2 nil 4))) +(defconst flymake-jslint-trailing-comma-err-line-pattern + '("^\\(.+\\)\:\\([0-9]+\\)\: strict \\(warning: trailing comma.+\\)\:$" nil 2 nil 3)) + +(defun flymake-jslint-command (filename) + "Construct a command that flymake can use to check javascript source." + (append + (list flymake-jslint-command) + flymake-jslint-args + (unless (string-match "jslint" flymake-jslint-command) + ;; jsl required option + (list "-process")) + (list filename))) + +;;;###autoload +(defun flymake-jslint-load () + "Configure flymake mode to check the current buffer's javascript syntax." + (interactive) + (flymake-easy-load 'flymake-jslint-command + (append flymake-jslint-err-line-patterns + (when flymake-jslint-detect-trailing-comma + (list flymake-jslint-trailing-comma-err-line-pattern))) + 'tempdir + "js")) + + +(provide 'flymake-jslint) +;;; flymake-jslint.el ends here diff --git a/emacs.d/elpa/go-mode-20140905.2210/go-mode-autoloads.el b/emacs.d/elpa/go-mode-20140905.2210/go-mode-autoloads.el new file mode 100644 index 0000000..5e94fae --- /dev/null +++ b/emacs.d/elpa/go-mode-20140905.2210/go-mode-autoloads.el @@ -0,0 +1,95 @@ +;;; go-mode-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "go-mode" "go-mode.el" (21573 46911 0 0)) +;;; Generated autoloads from go-mode.el + +(autoload 'go-mode "go-mode" "\ +Major mode for editing Go source text. + +This mode provides (not just) basic editing capabilities for +working with Go code. It offers almost complete syntax +highlighting, indentation that is almost identical to gofmt and +proper parsing of the buffer content to allow features such as +navigation by function, manipulation of comments or detection of +strings. + +In addition to these core features, it offers various features to +help with writing Go code. You can directly run buffer content +through gofmt, read godoc documentation from within Emacs, modify +and clean up the list of package imports or interact with the +Playground (uploading and downloading pastes). + +The following extra functions are defined: + +- `gofmt' +- `godoc' +- `go-import-add' +- `go-remove-unused-imports' +- `go-goto-imports' +- `go-play-buffer' and `go-play-region' +- `go-download-play' +- `godef-describe' and `godef-jump' +- `go-coverage' + +If you want to automatically run `gofmt' before saving a file, +add the following hook to your emacs configuration: + +\(add-hook 'before-save-hook #'gofmt-before-save) + +If you want to use `godef-jump' instead of etags (or similar), +consider binding godef-jump to `M-.', which is the default key +for `find-tag': + +\(add-hook 'go-mode-hook (lambda () + (local-set-key (kbd \"M-.\") #'godef-jump))) + +Please note that godef is an external dependency. You can install +it with + +go get code.google.com/p/rog-go/exp/cmd/godef + + +If you're looking for even more integration with Go, namely +on-the-fly syntax checking, auto-completion and snippets, it is +recommended that you look at flycheck +\(see URL `https://github.com/flycheck/flycheck') or flymake in combination +with goflymake (see URL `https://github.com/dougm/goflymake'), gocode +\(see URL `https://github.com/nsf/gocode'), go-eldoc +\(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go +\(see URL `https://github.com/dominikh/yasnippet-go') + +\(fn)" t nil) + +(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode)) + +(autoload 'gofmt-before-save "go-mode" "\ +Add this to .emacs to run gofmt on the current buffer when saving: + (add-hook 'before-save-hook 'gofmt-before-save). + +Note that this will cause go-mode to get loaded the first time +you save any file, kind of defeating the point of autoloading. + +\(fn)" t nil) + +(autoload 'godoc "go-mode" "\ +Show Go documentation for a query, much like M-x man. + +\(fn QUERY)" t nil) + +(autoload 'go-download-play "go-mode" "\ +Downloads a paste from the playground and inserts it in a Go +buffer. Tries to look for a URL at point. + +\(fn URL)" t nil) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; go-mode-autoloads.el ends here diff --git a/emacs.d/elpa/go-mode-20140905.2210/go-mode-pkg.el b/emacs.d/elpa/go-mode-20140905.2210/go-mode-pkg.el new file mode 100644 index 0000000..717221e --- /dev/null +++ b/emacs.d/elpa/go-mode-20140905.2210/go-mode-pkg.el @@ -0,0 +1 @@ +(define-package "go-mode" "20140905.2210" "Major mode for the Go programming language" 'nil :url "https://github.com/dominikh/go-mode.el" :keywords '("languages" "go")) diff --git a/emacs.d/elpa/go-mode-20140905.2210/go-mode.el b/emacs.d/elpa/go-mode-20140905.2210/go-mode.el new file mode 100644 index 0000000..f76ced3 --- /dev/null +++ b/emacs.d/elpa/go-mode-20140905.2210/go-mode.el @@ -0,0 +1,1505 @@ +;;; go-mode.el --- Major mode for the Go programming language + +;; Copyright 2013 The go-mode Authors. All rights reserved. +;; Use of this source code is governed by a BSD-style +;; license that can be found in the LICENSE file. + +;; Author: The go-mode Authors +;; Version: 20140905.2210 +;; X-Original-Version: 10 +;; Keywords: languages go +;; URL: https://github.com/dominikh/go-mode.el +;; +;; This file is not part of GNU Emacs. + +;;; Code: + +(require 'cl) +(require 'etags) +(require 'ffap) +(require 'find-file) +(require 'ring) +(require 'url) + +;; XEmacs compatibility guidelines +;; - Minimum required version of XEmacs: 21.5.32 +;; - Feature that cannot be backported: POSIX character classes in +;; regular expressions +;; - Functions that could be backported but won't because 21.5.32 +;; covers them: plenty. +;; - Features that are still partly broken: +;; - godef will not work correctly if multibyte characters are +;; being used +;; - Fontification will not handle unicode correctly +;; +;; - Do not use \_< and \_> regexp delimiters directly; use +;; go--regexp-enclose-in-symbol +;; +;; - The character `_` must not be a symbol constituent but a +;; character constituent +;; +;; - Do not use process-lines +;; +;; - Use go--old-completion-list-style when using a plain list as the +;; collection for completing-read +;; +;; - Use go--position-bytes instead of position-bytes +(defmacro go--xemacs-p () + `(featurep 'xemacs)) + +;; Delete the current line without putting it in the kill-ring. +(defun go--delete-whole-line (&optional arg) + ;; Derived from `kill-whole-line'. + ;; ARG is defined as for that function. + (setq arg (or arg 1)) + (if (and (> arg 0) + (eobp) + (save-excursion (forward-visible-line 0) (eobp))) + (signal 'end-of-buffer nil)) + (if (and (< arg 0) + (bobp) + (save-excursion (end-of-visible-line) (bobp))) + (signal 'beginning-of-buffer nil)) + (cond ((zerop arg) + (delete-region (progn (forward-visible-line 0) (point)) + (progn (end-of-visible-line) (point)))) + ((< arg 0) + (delete-region (progn (end-of-visible-line) (point)) + (progn (forward-visible-line (1+ arg)) + (unless (bobp) + (backward-char)) + (point)))) + (t + (delete-region (progn (forward-visible-line 0) (point)) + (progn (forward-visible-line arg) (point)))))) + +;; declare-function is an empty macro that only byte-compile cares +;; about. Wrap in always false if to satisfy Emacsen without that +;; macro. +(if nil + (declare-function go--position-bytes "go-mode" (point))) + +;; XEmacs unfortunately does not offer position-bytes. We can fall +;; back to just using (point), but it will be incorrect as soon as +;; multibyte characters are being used. +(if (fboundp 'position-bytes) + (defalias 'go--position-bytes #'position-bytes) + (defun go--position-bytes (point) point)) + +(defun go--old-completion-list-style (list) + (mapcar (lambda (x) (cons x nil)) list)) + +;; GNU Emacs 24 has prog-mode, older GNU Emacs and XEmacs do not, so +;; copy its definition for those. +(if (not (fboundp 'prog-mode)) + (define-derived-mode prog-mode fundamental-mode "Prog" + "Major mode for editing source code." + (set (make-local-variable 'require-final-newline) mode-require-final-newline) + (set (make-local-variable 'parse-sexp-ignore-comments) t) + (setq bidi-paragraph-direction 'left-to-right))) + +(defun go--regexp-enclose-in-symbol (s) + ;; XEmacs does not support \_<, GNU Emacs does. In GNU Emacs we make + ;; extensive use of \_< to support unicode in identifiers. Until we + ;; come up with a better solution for XEmacs, this solution will + ;; break fontification in XEmacs for identifiers such as "typeµ". + ;; XEmacs will consider "type" a keyword, GNU Emacs won't. + + (if (go--xemacs-p) + (concat "\\<" s "\\>") + (concat "\\_<" s "\\_>"))) + +;; Move up one level of parentheses. +(defun go-goto-opening-parenthesis (&optional legacy-unused) + ;; The old implementation of go-goto-opening-parenthesis had an + ;; optional argument to speed up the function. It didn't change the + ;; function's outcome. + + ;; Silently fail if there's no matching opening parenthesis. + (condition-case nil + (backward-up-list) + (scan-error nil))) + + +(defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]") +(defconst go-identifier-regexp "[[:word:][:multibyte:]]+") +(defconst go-type-name-no-prefix-regexp "\\(?:[[:word:][:multibyte:]]+\\.\\)?[[:word:][:multibyte:]]+") +(defconst go-qualified-identifier-regexp (concat go-identifier-regexp "\\." go-identifier-regexp)) +(defconst go-label-regexp go-identifier-regexp) +(defconst go-type-regexp "[[:word:][:multibyte:]*]+") +(defconst go-func-regexp (concat (go--regexp-enclose-in-symbol "func") "\\s *\\(" go-identifier-regexp "\\)")) +(defconst go-func-meth-regexp (concat + (go--regexp-enclose-in-symbol "func") "\\s *\\(?:(\\s *" + "\\(" go-identifier-regexp "\\s +\\)?" go-type-regexp + "\\s *)\\s *\\)?\\(" + go-identifier-regexp + "\\)(")) + +(defconst go-builtins + '("append" "cap" "close" "complex" "copy" + "delete" "imag" "len" "make" "new" + "panic" "print" "println" "real" "recover") + "All built-in functions in the Go language. Used for font locking.") + +(defconst go-mode-keywords + '("break" "default" "func" "interface" "select" + "case" "defer" "go" "map" "struct" + "chan" "else" "goto" "package" "switch" + "const" "fallthrough" "if" "range" "type" + "continue" "for" "import" "return" "var") + "All keywords in the Go language. Used for font locking.") + +(defconst go-constants '("nil" "true" "false" "iota")) +(defconst go-type-name-regexp (concat "\\(?:[*(]\\)*\\(\\(?:" go-identifier-regexp "\\.\\)?" go-identifier-regexp "\\)")) + +;; Maximum number of identifiers that can be highlighted as type names +;; in one function type/declaration. +(defconst go--font-lock-func-param-num-groups 16) + +(defvar go-dangling-cache) +(defvar go-godoc-history nil) +(defvar go--coverage-current-file-name) + +(defgroup go nil + "Major mode for editing Go code" + :group 'languages) + +(defgroup go-cover nil + "Options specific to `cover`" + :group 'go) + +(defcustom go-fontify-function-calls t + "Fontify function and method calls if this is non-nil." + :type 'boolean + :group 'go) + +(defcustom go-mode-hook nil + "Hook called by `go-mode'." + :type 'hook + :group 'go) + +(defcustom go-command "go" + "The 'go' command. Some users have multiple Go development +trees and invoke the 'go' tool via a wrapper that sets GOROOT and +GOPATH based on the current directory. Such users should +customize this variable to point to the wrapper script." + :type 'string + :group 'go) + +(defcustom gofmt-command "gofmt" + "The 'gofmt' command. Some users may replace this with 'goimports' +from https://github.com/bradfitz/goimports." + :type 'string + :group 'go) + +(defcustom gofmt-show-errors 'buffer + "Where to display gofmt error output. It can either be +displayed in its own buffer, in the echo area, or not at all. + +Please note that Emacs outputs to the echo area when writing +files and will overwrite gofmt's echo output if used from inside +a before-save-hook." + :type '(choice + (const :tag "Own buffer" buffer) + (const :tag "Echo area" echo) + (const :tag "None" nil)) + :group 'go) + +(defcustom go-other-file-alist + '(("_test\\.go\\'" (".go")) + ("\\.go\\'" ("_test.go"))) + "See the documentation of `ff-other-file-alist' for details." + :type '(repeat (list regexp (choice (repeat string) function))) + :group 'go) + +(defcustom go-coverage-display-buffer-func 'display-buffer-reuse-window + "How go-coverage should display the coverage buffer. See +`display-buffer' for a list of possible functions." + :type 'function + :group 'go-cover) + +(defface go-coverage-untracked + '((t (:foreground "#505050"))) + "Coverage color of untracked code." + :group 'go-cover) + +(defface go-coverage-0 + '((t (:foreground "#c00000"))) + "Coverage color for uncovered code." + :group 'go-cover) +(defface go-coverage-1 + '((t (:foreground "#808080"))) + "Coverage color for covered code with weight 1." + :group 'go-cover) +(defface go-coverage-2 + '((t (:foreground "#748c83"))) + "Coverage color for covered code with weight 2." + :group 'go-cover) +(defface go-coverage-3 + '((t (:foreground "#689886"))) + "Coverage color for covered code with weight 3." + :group 'go-cover) +(defface go-coverage-4 + '((t (:foreground "#5ca489"))) + "Coverage color for covered code with weight 4." + :group 'go-cover) +(defface go-coverage-5 + '((t (:foreground "#50b08c"))) + "Coverage color for covered code with weight 5." + :group 'go-cover) +(defface go-coverage-6 + '((t (:foreground "#44bc8f"))) + "Coverage color for covered code with weight 6." + :group 'go-cover) +(defface go-coverage-7 + '((t (:foreground "#38c892"))) + "Coverage color for covered code with weight 7." + :group 'go-cover) +(defface go-coverage-8 + '((t (:foreground "#2cd495"))) + "Coverage color for covered code with weight 8. +For mode=set, all covered lines will have this weight." + :group 'go-cover) +(defface go-coverage-9 + '((t (:foreground "#20e098"))) + "Coverage color for covered code with weight 9." + :group 'go-cover) +(defface go-coverage-10 + '((t (:foreground "#14ec9b"))) + "Coverage color for covered code with weight 10." + :group 'go-cover) +(defface go-coverage-covered + '((t (:foreground "#2cd495"))) + "Coverage color of covered code." + :group 'go-cover) + +(defvar go-mode-syntax-table + (let ((st (make-syntax-table))) + (modify-syntax-entry ?+ "." st) + (modify-syntax-entry ?- "." st) + (modify-syntax-entry ?% "." st) + (modify-syntax-entry ?& "." st) + (modify-syntax-entry ?| "." st) + (modify-syntax-entry ?^ "." st) + (modify-syntax-entry ?! "." st) + (modify-syntax-entry ?= "." st) + (modify-syntax-entry ?< "." st) + (modify-syntax-entry ?> "." st) + (modify-syntax-entry ?/ (if (go--xemacs-p) ". 1456" ". 124b") st) + (modify-syntax-entry ?* ". 23" st) + (modify-syntax-entry ?\n "> b" st) + (modify-syntax-entry ?\" "\"" st) + (modify-syntax-entry ?\' "\"" st) + (modify-syntax-entry ?` "\"" st) + (modify-syntax-entry ?\\ "\\" st) + ;; It would be nicer to have _ as a symbol constituent, but that + ;; would trip up XEmacs, which does not support the \_< anchor + (modify-syntax-entry ?_ "w" st) + + st) + "Syntax table for Go mode.") + +(defun go--build-font-lock-keywords () + ;; we cannot use 'symbols in regexp-opt because GNU Emacs <24 + ;; doesn't understand that + (append + `((go--match-func + ,@(mapcar (lambda (x) `(,x font-lock-type-face)) + (number-sequence 1 go--font-lock-func-param-num-groups))) + (,(go--regexp-enclose-in-symbol (regexp-opt go-mode-keywords t)) . font-lock-keyword-face) + (,(concat "\\(" (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) "\\)[[:space:]]*(") 1 font-lock-builtin-face) + (,(go--regexp-enclose-in-symbol (regexp-opt go-constants t)) . font-lock-constant-face) + (,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name + + (if go-fontify-function-calls + `((,(concat "\\(" go-identifier-regexp "\\)[[:space:]]*(") 1 font-lock-function-name-face) ;; function call/method name + (,(concat "[^[:word:][:multibyte:]](\\(" go-identifier-regexp "\\))[[:space:]]*(") 1 font-lock-function-name-face)) ;; bracketed function call + `((,go-func-meth-regexp 2 font-lock-function-name-face))) ;; method name + + `( + ("\\(`[^`]*`\\)" 1 font-lock-multiline) ;; raw string literal, needed for font-lock-syntactic-keywords + (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+\\([^[:space:]]+\\)") 1 font-lock-type-face) ;; types + (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types + (,(concat "[^[:word:][:multibyte:]]\\[\\([[:digit:]]+\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 2 font-lock-type-face) ;; Arrays/slices + (,(concat "\\(" go-type-name-regexp "\\)" "{") 1 font-lock-type-face) + (,(concat (go--regexp-enclose-in-symbol "map") "\\[[^]]+\\]" go-type-name-regexp) 1 font-lock-type-face) ;; map value type + (,(concat (go--regexp-enclose-in-symbol "map") "\\[" go-type-name-regexp) 1 font-lock-type-face) ;; map key type + (,(concat (go--regexp-enclose-in-symbol "chan") "[[:space:]]*\\(?:<-[[:space:]]*\\)?" go-type-name-regexp) 1 font-lock-type-face) ;; channel type + (,(concat (go--regexp-enclose-in-symbol "\\(?:new\\|make\\)") "\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) ;; new/make type + ;; TODO do we actually need this one or isn't it just a function call? + (,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face) ;; Type conversion + ;; Like the original go-mode this also marks compound literal + ;; fields. There, it was marked as to fix, but I grew quite + ;; accustomed to it, so it'll stay for now. + (,(concat "^[[:space:]]*\\(" go-label-regexp "\\)[[:space:]]*:\\(\\S.\\|$\\)") 1 font-lock-constant-face) ;; Labels and compound literal fields + (,(concat (go--regexp-enclose-in-symbol "\\(goto\\|break\\|continue\\)") "[[:space:]]*\\(" go-label-regexp "\\)") 2 font-lock-constant-face)))) ;; labels in goto/break/continue + +(defconst go--font-lock-syntactic-keywords + ;; Override syntax property of raw string literal contents, so that + ;; backslashes have no special meaning in ``. Used in Emacs 23 or older. + '((go--match-raw-string-literal + (1 (7 . ?`)) + (2 (15 . nil)) ;; 15 = "generic string" + (3 (7 . ?`))))) + +(defvar go-mode-map + (let ((m (make-sparse-keymap))) + (define-key m "}" #'go-mode-insert-and-indent) + (define-key m ")" #'go-mode-insert-and-indent) + (define-key m "," #'go-mode-insert-and-indent) + (define-key m ":" #'go-mode-insert-and-indent) + (define-key m "=" #'go-mode-insert-and-indent) + (define-key m (kbd "C-c C-a") #'go-import-add) + (define-key m (kbd "C-c C-j") #'godef-jump) + (define-key m (kbd "C-x 4 C-c C-j") #'godef-jump-other-window) + (define-key m (kbd "C-c C-d") #'godef-describe) + m) + "Keymap used by Go mode to implement electric keys.") + +(defun go-mode-insert-and-indent (key) + "Invoke the global binding of KEY, then reindent the line." + + (interactive (list (this-command-keys))) + (call-interactively (lookup-key (current-global-map) key)) + (indent-according-to-mode)) + +(defmacro go-paren-level () + `(car (syntax-ppss))) + +(defmacro go-in-string-or-comment-p () + `(nth 8 (syntax-ppss))) + +(defmacro go-in-string-p () + `(nth 3 (syntax-ppss))) + +(defmacro go-in-comment-p () + `(nth 4 (syntax-ppss))) + +(defmacro go-goto-beginning-of-string-or-comment () + `(goto-char (nth 8 (syntax-ppss)))) + +(defun go--backward-irrelevant (&optional stop-at-string) + "Skips backwards over any characters that are irrelevant for +indentation and related tasks. + +It skips over whitespace, comments, cases and labels and, if +STOP-AT-STRING is not true, over strings." + + (let (pos (start-pos (point))) + (skip-chars-backward "\n\s\t") + (if (and (save-excursion (beginning-of-line) (go-in-string-p)) (looking-back "`") (not stop-at-string)) + (backward-char)) + (if (and (go-in-string-p) (not stop-at-string)) + (go-goto-beginning-of-string-or-comment)) + (if (looking-back "\\*/") + (backward-char)) + (if (go-in-comment-p) + (go-goto-beginning-of-string-or-comment)) + (setq pos (point)) + (beginning-of-line) + (if (or (looking-at (concat "^" go-label-regexp ":")) (looking-at "^[[:space:]]*\\(case .+\\|default\\):")) + (end-of-line 0) + (goto-char pos)) + (if (/= start-pos (point)) + (go--backward-irrelevant stop-at-string)) + (/= start-pos (point)))) + +(defun go--buffer-narrowed-p () + "Return non-nil if the current buffer is narrowed." + (/= (buffer-size) + (- (point-max) + (point-min)))) + +(defun go--match-raw-string-literal (end) + "Search for a raw string literal. Set point to the end of the +occurence found on success. Returns nil on failure." + (unless (go-in-string-or-comment-p) + (when (search-forward "`" end t) + (goto-char (match-beginning 0)) + (if (go-in-string-or-comment-p) + (progn (goto-char (match-end 0)) + (go--match-raw-string-literal end)) + (when (looking-at "\\(`\\)\\([^`]*\\)\\(`\\)") + (goto-char (match-end 0)) + t))))) + +(defun go-previous-line-has-dangling-op-p () + "Returns non-nil if the current line is a continuation line." + (let* ((cur-line (line-number-at-pos)) + (val (gethash cur-line go-dangling-cache 'nope))) + (if (or (go--buffer-narrowed-p) (equal val 'nope)) + (save-excursion + (beginning-of-line) + (go--backward-irrelevant t) + (setq val (looking-back go-dangling-operators-regexp)) + (if (not (go--buffer-narrowed-p)) + (puthash cur-line val go-dangling-cache)))) + val)) + +(defun go--at-function-definition () + "Return non-nil if point is on the opening curly brace of a +function definition. + +We do this by first calling (beginning-of-defun), which will take +us to the start of *some* function. We then look for the opening +curly brace of that function and compare its position against the +curly brace we are checking. If they match, we return non-nil." + (if (= (char-after) ?\{) + (save-excursion + (let ((old-point (point)) + start-nesting) + (beginning-of-defun) + (when (looking-at "func ") + (setq start-nesting (go-paren-level)) + (skip-chars-forward "^{") + (while (> (go-paren-level) start-nesting) + (forward-char) + (skip-chars-forward "^{") 0) + (if (and (= (go-paren-level) start-nesting) (= old-point (point))) + t)))))) + +(defun go--indentation-for-opening-parenthesis () + "Return the semantic indentation for the current opening parenthesis. + +If point is on an opening curly brace and said curly brace +belongs to a function declaration, the indentation of the func +keyword will be returned. Otherwise the indentation of the +current line will be returned." + (save-excursion + (if (go--at-function-definition) + (progn + (beginning-of-defun) + (current-indentation)) + (current-indentation)))) + +(defun go-indentation-at-point () + (save-excursion + (let (start-nesting) + (back-to-indentation) + (setq start-nesting (go-paren-level)) + + (cond + ((go-in-string-p) + (current-indentation)) + ((looking-at "[])}]") + (go-goto-opening-parenthesis) + (if (go-previous-line-has-dangling-op-p) + (- (current-indentation) tab-width) + (go--indentation-for-opening-parenthesis))) + ((progn (go--backward-irrelevant t) (looking-back go-dangling-operators-regexp)) + ;; only one nesting for all dangling operators in one operation + (if (go-previous-line-has-dangling-op-p) + (current-indentation) + (+ (current-indentation) tab-width))) + ((zerop (go-paren-level)) + 0) + ((progn (go-goto-opening-parenthesis) (< (go-paren-level) start-nesting)) + (if (go-previous-line-has-dangling-op-p) + (current-indentation) + (+ (go--indentation-for-opening-parenthesis) tab-width))) + (t + (current-indentation)))))) + +(defun go-mode-indent-line () + (interactive) + (let (indent + shift-amt + (pos (- (point-max) (point))) + (point (point)) + (beg (line-beginning-position))) + (back-to-indentation) + (if (go-in-string-or-comment-p) + (goto-char point) + (setq indent (go-indentation-at-point)) + (if (looking-at (concat go-label-regexp ":\\([[:space:]]*/.+\\)?$\\|case .+:\\|default:")) + (decf indent tab-width)) + (setq shift-amt (- indent (current-column))) + (if (zerop shift-amt) + nil + (delete-region beg (point)) + (indent-to indent)) + ;; If initial point was within line's indentation, + ;; position after the indentation. Else stay at same point in text. + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos)))))) + +(defun go-beginning-of-defun (&optional count) + (unless (bolp) + (end-of-line)) + (setq count (or count 1)) + (let (first failure) + (dotimes (i (abs count)) + (setq first t) + (while (and (not failure) + (or first (go-in-string-or-comment-p))) + (if (>= count 0) + (progn + (go--backward-irrelevant) + (if (not (re-search-backward go-func-meth-regexp nil t)) + (setq failure t))) + (if (looking-at go-func-meth-regexp) + (forward-char)) + (if (not (re-search-forward go-func-meth-regexp nil t)) + (setq failure t))) + (setq first nil))) + (if (< count 0) + (beginning-of-line)) + (not failure))) + +(defun go-end-of-defun () + (let (orig-level) + ;; It can happen that we're not placed before a function by emacs + (if (not (looking-at "func")) + (go-beginning-of-defun -1)) + ;; Find the { that starts the function, i.e., the next { that isn't + ;; preceded by struct or interface, or a comment or struct tag. BUG: + ;; breaks if there's a comment between the struct/interface keyword and + ;; bracket, like this: + ;; + ;; struct /* why? */ { + (while (progn + (skip-chars-forward "^{") + (forward-char) + (or (go-in-string-or-comment-p) + (looking-back "\\(struct\\|interface\\)\\s-*{")))) + (setq orig-level (go-paren-level)) + (while (>= (go-paren-level) orig-level) + (skip-chars-forward "^}") + (forward-char)))) + +(defun go--find-enclosing-parentheses (position) + "Return points of outermost '(' and ')' surrounding POSITION if +such parentheses exist. + +If outermost '(' exists but ')' does not, it returns the next blank +line or end-of-buffer position instead of the position of the closing +parenthesis. + +If the starting parenthesis is not found, it returns (POSITION +POSITION). +" + (save-excursion + (let (beg end) + (goto-char position) + (while (> (go-paren-level) 0) + (re-search-backward "[(\\[{]" nil t) + (when (looking-at "(") + (setq beg (point)))) + (if (null beg) + (list position position) + (goto-char position) + (while (and (> (go-paren-level) 0) + (search-forward ")" nil t))) + (when (> (go-paren-level) 0) + (unless (re-search-forward "^[[:space:]]*$" nil t) + (goto-char (point-max)))) + (list beg (point)))))) + +(defun go--search-next-comma (end) + "Search forward from point for a comma whose nesting level is +the same as point. If it reaches the end of line or a closing +parenthesis before a comma, it stops at it." + (let ((orig-level (go-paren-level))) + (while (and (< (point) end) + (or (looking-at "[^,)\n]") + (> (go-paren-level) orig-level))) + (forward-char)) + (when (and (looking-at ",") + (< (point) (1- end))) + (forward-char)))) + +(defun go--looking-at-keyword () + (and (looking-at (concat "\\(" go-identifier-regexp "\\)")) + (member (match-string 1) go-mode-keywords))) + +(defun go--match-func (end) + "Search for identifiers used as type names from a function +parameter list, and set the identifier positions as the results +of last search. Return t if search succeeded." + (when (re-search-forward (go--regexp-enclose-in-symbol "func") end t) + (let ((regions (go--match-func-type-names end))) + (if (null regions) + ;; Nothing to highlight. This can happen if the current func + ;; is "func()". Try next one. + (go--match-func end) + ;; There are something to highlight. Set those positions as + ;; last search results. + (setq regions (go--filter-match-data regions end)) + (when regions + (set-match-data (go--make-match-data regions)) + t))))) + +(defun go--match-func-type-names (end) + (cond + ;; Function declaration (e.g. "func foo(") + ((looking-at (concat "[[:space:]\n]*" go-identifier-regexp "[[:space:]\n]*(")) + (goto-char (match-end 0)) + (nconc (go--match-parameter-list end) + (go--match-function-result end))) + ;; Method declaration, function literal, or function type + ((looking-at "[[:space:]]*(") + (goto-char (match-end 0)) + (let ((regions (go--match-parameter-list end))) + ;; Method declaration (e.g. "func (x y) foo(") + (when (looking-at (concat "[[:space:]]*" go-identifier-regexp "[[:space:]\n]*(")) + (goto-char (match-end 0)) + (setq regions (nconc regions (go--match-parameter-list end)))) + (nconc regions (go--match-function-result end)))))) + +(defun go--parameter-list-type (end) + "Return 'present if the parameter list has names, or 'absent if +not, assuming point is at the beginning of a parameter list, just +after '('." + (save-excursion + (skip-chars-forward "[:space:]\n" end) + (cond ((> (point) end) + nil) + ((looking-at (concat go-identifier-regexp "[[:space:]\n]*,")) + (goto-char (match-end 0)) + (go--parameter-list-type end)) + ((or (looking-at go-qualified-identifier-regexp) + (looking-at (concat go-type-name-no-prefix-regexp "[[:space:]\n]*\\(?:)\\|\\'\\)")) + (go--looking-at-keyword) + (looking-at "[*\\[]\\|\\.\\.\\.\\|\\'")) + 'absent) + (t 'present)))) + +(defconst go--opt-dotdotdot-regexp "\\(?:\\.\\.\\.\\)?") +(defconst go--parameter-type-regexp + (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(" go-type-name-no-prefix-regexp "\\)[[:space:]\n]*\\([,)]\\|\\'\\)")) +(defconst go--func-type-in-parameter-list-regexp + (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(" (go--regexp-enclose-in-symbol "func") "\\)")) + +(defun go--match-parameters-common (identifier-regexp end) + (let ((acc ()) + (start -1)) + (while (progn (skip-chars-forward "[:space:]\n" end) + (and (not (looking-at "\\(?:)\\|\\'\\)")) + (< start (point)) + (<= (point) end))) + (setq start (point)) + (cond + ((looking-at (concat identifier-regexp go--parameter-type-regexp)) + (setq acc (nconc acc (list (match-beginning 1) (match-end 1)))) + (goto-char (match-beginning 2))) + ((looking-at (concat identifier-regexp go--func-type-in-parameter-list-regexp)) + (goto-char (match-beginning 1)) + (setq acc (nconc acc (go--match-func-type-names end))) + (go--search-next-comma end)) + (t + (go--search-next-comma end)))) + (when (and (looking-at ")") + (< (point) end)) + (forward-char)) + acc)) + +(defun go--match-parameters-with-identifier-list (end) + (go--match-parameters-common + (concat go-identifier-regexp "[[:space:]\n]+") + end)) + +(defun go--match-parameters-without-identifier-list (end) + (go--match-parameters-common "" end)) + +(defun go--filter-match-data (regions end) + "Remove points from regions if they are beyond end. Regions are +a list whose size is multiple of 2. Element 2n is beginning of a +region and 2n+1 is end of it. + +This function is used to make sure we don't override end point +that font-lock-mode gave to us." + (when regions + (let* ((vec (vconcat regions)) + (i 0) + (len (length vec))) + (while (and (< i len) + (<= (nth i regions) end) + (<= (nth (1+ i) regions) end)) + (setq i (+ i 2))) + (cond ((= i len) + regions) + ((zerop i) + nil) + (t + (setcdr (nth i regions) nil) + regions))))) + +(defun go--make-match-data (regions) + (let ((deficit (- (* 2 go--font-lock-func-param-num-groups) + (length regions)))) + (when (> deficit 0) + (let ((last (car (last regions)))) + (setq regions (nconc regions (make-list deficit last)))))) + `(,(car regions) ,@(last regions) ,@regions)) + +(defun go--match-parameter-list (end) + "Returns a list of identifier positions that are used as type +names in a function parameter list, assuming point is at the +beginning of a parameter list. Returns nil if the text after +point does not look like a parameter list. + +Set point to end of closing parenthesis on success. + +In Go, the names must either all be present or all be absent +within a list of parameters. + +Parsing a parameter list is a little bit complicated because we +have to scan through the parameter list to determine whether or +not the list has names. Until a type name is found or reaching +end of a parameter list, we are not sure which form the parameter +list is. + +For example, X and Y are type names in a parameter list \"(X, +Y)\" but are parameter names in \"(X, Y int)\". We cannot say if +X is a type name until we see int after Y. + +Note that even \"(int, float T)\" is a valid parameter +list. Builtin type names are not reserved words. In this example, +int and float are parameter names and only T is a type name. + +In this function, we first scan the parameter list to see if the +list has names, and then handle it accordingly." + (let ((name (go--parameter-list-type end))) + (cond ((eq name 'present) + (go--match-parameters-with-identifier-list end)) + ((eq name 'absent) + (go--match-parameters-without-identifier-list end)) + (t nil)))) + +(defun go--match-function-result (end) + "Returns a list of identifier positions that are used as type +names in a function result, assuming point is at the beginning of +a result. + +Function result is a unparenthesized type or a parameter list." + (cond ((and (looking-at (concat "[[:space:]*]*\\(" go-type-name-no-prefix-regexp "\\)")) + (not (member (match-string 1) go-mode-keywords))) + (list (match-beginning 1) (match-end 1))) + ((looking-at "[[:space:]]*(") + (goto-char (match-end 0)) + (go--match-parameter-list end)) + (t nil))) + +;;;###autoload +(define-derived-mode go-mode prog-mode "Go" + "Major mode for editing Go source text. + +This mode provides (not just) basic editing capabilities for +working with Go code. It offers almost complete syntax +highlighting, indentation that is almost identical to gofmt and +proper parsing of the buffer content to allow features such as +navigation by function, manipulation of comments or detection of +strings. + +In addition to these core features, it offers various features to +help with writing Go code. You can directly run buffer content +through gofmt, read godoc documentation from within Emacs, modify +and clean up the list of package imports or interact with the +Playground (uploading and downloading pastes). + +The following extra functions are defined: + +- `gofmt' +- `godoc' +- `go-import-add' +- `go-remove-unused-imports' +- `go-goto-imports' +- `go-play-buffer' and `go-play-region' +- `go-download-play' +- `godef-describe' and `godef-jump' +- `go-coverage' + +If you want to automatically run `gofmt' before saving a file, +add the following hook to your emacs configuration: + +\(add-hook 'before-save-hook #'gofmt-before-save) + +If you want to use `godef-jump' instead of etags (or similar), +consider binding godef-jump to `M-.', which is the default key +for `find-tag': + +\(add-hook 'go-mode-hook (lambda () + (local-set-key (kbd \"M-.\") #'godef-jump))) + +Please note that godef is an external dependency. You can install +it with + +go get code.google.com/p/rog-go/exp/cmd/godef + + +If you're looking for even more integration with Go, namely +on-the-fly syntax checking, auto-completion and snippets, it is +recommended that you look at flycheck +\(see URL `https://github.com/flycheck/flycheck') or flymake in combination +with goflymake \(see URL `https://github.com/dougm/goflymake'), gocode +\(see URL `https://github.com/nsf/gocode'), go-eldoc +\(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go +\(see URL `https://github.com/dominikh/yasnippet-go')" + + ;; Font lock + (set (make-local-variable 'font-lock-defaults) + '(go--build-font-lock-keywords)) + + ;; Indentation + (set (make-local-variable 'indent-line-function) #'go-mode-indent-line) + + ;; Comments + (set (make-local-variable 'comment-start) "// ") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-use-syntax) t) + (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *") + + (set (make-local-variable 'beginning-of-defun-function) #'go-beginning-of-defun) + (set (make-local-variable 'end-of-defun-function) #'go-end-of-defun) + + (set (make-local-variable 'parse-sexp-lookup-properties) t) + (if (boundp 'syntax-propertize-function) + (set (make-local-variable 'syntax-propertize-function) #'go-propertize-syntax) + (set (make-local-variable 'font-lock-syntactic-keywords) + go--font-lock-syntactic-keywords) + (set (make-local-variable 'font-lock-multiline) t)) + + (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql)) + (add-hook 'before-change-functions (lambda (x y) (setq go-dangling-cache (make-hash-table :test 'eql))) t t) + + ;; ff-find-other-file + (setq ff-other-file-alist 'go-other-file-alist) + + (setq imenu-generic-expression + '(("type" "^type *\\([^ \t\n\r\f]*\\)" 1) + ("func" "^func *\\(.*\\) {" 1))) + (imenu-add-to-menubar "Index") + + ;; Go style + (setq indent-tabs-mode t) + + ;; Handle unit test failure output in compilation-mode + ;; + ;; Note the final t argument to add-to-list for append, ie put these at the + ;; *ends* of compilation-error-regexp-alist[-alist]. We want go-test to be + ;; handled first, otherwise other elements will match that don't work, and + ;; those alists are traversed in *reverse* order: + ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-12/msg00674.html + (when (and (boundp 'compilation-error-regexp-alist) + (boundp 'compilation-error-regexp-alist-alist)) + (add-to-list 'compilation-error-regexp-alist 'go-test t) + (add-to-list 'compilation-error-regexp-alist-alist + '(go-test . ("^\t+\\([^()\t\n]+\\):\\([0-9]+\\):? .*$" 1 2)) t))) + +;;;###autoload +(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode)) + +(defun go--apply-rcs-patch (patch-buffer) + "Apply an RCS-formatted diff from PATCH-BUFFER to the current +buffer." + (let ((target-buffer (current-buffer)) + ;; Relative offset between buffer line numbers and line numbers + ;; in patch. + ;; + ;; Line numbers in the patch are based on the source file, so + ;; we have to keep an offset when making changes to the + ;; buffer. + ;; + ;; Appending lines decrements the offset (possibly making it + ;; negative), deleting lines increments it. This order + ;; simplifies the forward-line invocations. + (line-offset 0)) + (save-excursion + (with-current-buffer patch-buffer + (goto-char (point-min)) + (while (not (eobp)) + (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)") + (error "invalid rcs patch or internal error in go--apply-rcs-patch")) + (forward-line) + (let ((action (match-string 1)) + (from (string-to-number (match-string 2))) + (len (string-to-number (match-string 3)))) + (cond + ((equal action "a") + (let ((start (point))) + (forward-line len) + (let ((text (buffer-substring start (point)))) + (with-current-buffer target-buffer + (decf line-offset len) + (goto-char (point-min)) + (forward-line (- from len line-offset)) + (insert text))))) + ((equal action "d") + (with-current-buffer target-buffer + (go--goto-line (- from line-offset)) + (incf line-offset len) + (go--delete-whole-line len))) + (t + (error "invalid rcs patch or internal error in go--apply-rcs-patch"))))))))) + +(defun gofmt () + "Formats the current buffer according to the gofmt tool." + + (interactive) + (let ((tmpfile (make-temp-file "gofmt" nil ".go")) + (patchbuf (get-buffer-create "*Gofmt patch*")) + (errbuf (if gofmt-show-errors (get-buffer-create "*Gofmt Errors*"))) + (coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8)) + + (save-restriction + (widen) + (if errbuf + (with-current-buffer errbuf + (setq buffer-read-only nil) + (erase-buffer))) + (with-current-buffer patchbuf + (erase-buffer)) + + (write-region nil nil tmpfile) + + ;; We're using errbuf for the mixed stdout and stderr output. This + ;; is not an issue because gofmt -w does not produce any stdout + ;; output in case of success. + (if (zerop (call-process gofmt-command nil errbuf nil "-w" tmpfile)) + (progn + (if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile)) + (message "Buffer is already gofmted") + (go--apply-rcs-patch patchbuf) + (message "Applied gofmt")) + (if errbuf (kill-buffer errbuf))) + (message "Could not apply gofmt") + (if errbuf (gofmt--process-errors (buffer-file-name) tmpfile errbuf))) + + (kill-buffer patchbuf) + (delete-file tmpfile)))) + + +(defun gofmt--process-errors (filename tmpfile errbuf) + (with-current-buffer errbuf + (if (eq gofmt-show-errors 'echo) + (progn + (message "%s" (buffer-string)) + (kill-buffer errbuf)) + ;; Convert the gofmt stderr to something understood by the compilation mode. + (goto-char (point-min)) + (insert "gofmt errors:\n") + (while (search-forward-regexp (concat "^\\(" (regexp-quote tmpfile) "\\):") nil t) + (replace-match (file-name-nondirectory filename) t t nil 1)) + (compilation-mode) + (display-buffer errbuf)))) + +;;;###autoload +(defun gofmt-before-save () + "Add this to .emacs to run gofmt on the current buffer when saving: + (add-hook 'before-save-hook 'gofmt-before-save). + +Note that this will cause go-mode to get loaded the first time +you save any file, kind of defeating the point of autoloading." + + (interactive) + (when (eq major-mode 'go-mode) (gofmt))) + +(defun godoc--read-query () + "Read a godoc query from the minibuffer." + ;; Compute the default query as the symbol under the cursor. + ;; TODO: This does the wrong thing for e.g. multipart.NewReader (it only grabs + ;; half) but I see no way to disambiguate that from e.g. foobar.SomeMethod. + (let* ((bounds (bounds-of-thing-at-point 'symbol)) + (symbol (if bounds + (buffer-substring-no-properties (car bounds) + (cdr bounds))))) + (completing-read (if symbol + (format "godoc (default %s): " symbol) + "godoc: ") + (go--old-completion-list-style (go-packages)) nil nil nil 'go-godoc-history symbol))) + +(defun godoc--get-buffer (query) + "Get an empty buffer for a godoc query." + (let* ((buffer-name (concat "*godoc " query "*")) + (buffer (get-buffer buffer-name))) + ;; Kill the existing buffer if it already exists. + (when buffer (kill-buffer buffer)) + (get-buffer-create buffer-name))) + +(defun godoc--buffer-sentinel (proc event) + "Sentinel function run when godoc command completes." + (with-current-buffer (process-buffer proc) + (cond ((string= event "finished\n") ;; Successful exit. + (goto-char (point-min)) + (view-mode 1) + (display-buffer (current-buffer) t)) + ((/= (process-exit-status proc) 0) ;; Error exit. + (let ((output (buffer-string))) + (kill-buffer (current-buffer)) + (message (concat "godoc: " output))))))) + +;;;###autoload +(defun godoc (query) + "Show Go documentation for a query, much like M-x man." + (interactive (list (godoc--read-query))) + (unless (string= query "") + (set-process-sentinel + (start-process-shell-command "godoc" (godoc--get-buffer query) + (concat "godoc " query)) + 'godoc--buffer-sentinel) + nil)) + +(defun godoc-at-point (point) + "Show Go documentation for the identifier at POINT. + +`godoc-at-point' requires godef to work. + +Due to a limitation in godoc, it is not possible to differentiate +between functions and methods, which may cause `godoc-at-point' +to display more documentation than desired." + ;; TODO(dominikh): Support executing godoc-at-point on a package + ;; name. + (interactive "d") + (condition-case nil + (let* ((output (godef--call point)) + (file (car output)) + (name-parts (split-string (cadr output) " ")) + (first (car name-parts))) + (if (not (godef--successful-p file)) + (message "%s" (godef--error file)) + (godoc (format "%s %s" + (file-name-directory file) + (if (or (string= first "type") (string= first "const")) + (cadr name-parts) + (car name-parts)))))) + (file-error (message "Could not run godef binary")))) + +(defun go-goto-imports () + "Move point to the block of imports. + +If using + + import ( + \"foo\" + \"bar\" + ) + +it will move point directly behind the last import. + +If using + + import \"foo\" + import \"bar\" + +it will move point to the next line after the last import. + +If no imports can be found, point will be moved after the package +declaration." + (interactive) + ;; FIXME if there's a block-commented import before the real + ;; imports, we'll jump to that one. + + ;; Generally, this function isn't very forgiving. it'll bark on + ;; extra whitespace. It works well for clean code. + (let ((old-point (point))) + (goto-char (point-min)) + (cond + ((re-search-forward "^import ()" nil t) + (backward-char 1) + 'block-empty) + ((re-search-forward "^import ([^)]+)" nil t) + (backward-char 2) + 'block) + ((re-search-forward "\\(^import \\([^\"]+ \\)?\"[^\"]+\"\n?\\)+" nil t) + 'single) + ((re-search-forward "^[[:space:]\n]*package .+?\n" nil t) + (message "No imports found, moving point after package declaration") + 'none) + (t + (goto-char old-point) + (message "No imports or package declaration found. Is this really a Go file?") + 'fail)))) + +(defun go-play-buffer () + "Like `go-play-region', but acts on the entire buffer." + (interactive) + (go-play-region (point-min) (point-max))) + +(defun go-play-region (start end) + "Send the region to the Playground and stores the resulting +link in the kill ring." + (interactive "r") + (let* ((url-request-method "POST") + (url-request-extra-headers + '(("Content-Type" . "application/x-www-form-urlencoded"))) + (url-request-data + (encode-coding-string + (buffer-substring-no-properties start end) + 'utf-8)) + (content-buf (url-retrieve + "http://play.golang.org/share" + (lambda (arg) + (cond + ((equal :error (car arg)) + (signal 'go-play-error (cdr arg))) + (t + (re-search-forward "\n\n") + (kill-new (format "http://play.golang.org/p/%s" (buffer-substring (point) (point-max)))) + (message "http://play.golang.org/p/%s" (buffer-substring (point) (point-max))))))))))) + +;;;###autoload +(defun go-download-play (url) + "Downloads a paste from the playground and inserts it in a Go +buffer. Tries to look for a URL at point." + (interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p (ffap-string-at-point 'url))))) + (with-current-buffer + (let ((url-request-method "GET") url-request-data url-request-extra-headers) + (url-retrieve-synchronously (concat url ".go"))) + (let ((buffer (generate-new-buffer (concat (car (last (split-string url "/"))) ".go")))) + (goto-char (point-min)) + (re-search-forward "\n\n") + (copy-to-buffer buffer (point) (point-max)) + (kill-buffer) + (with-current-buffer buffer + (go-mode) + (switch-to-buffer buffer))))) + +(defun go-propertize-syntax (start end) + (save-excursion + (goto-char start) + (while (search-forward "\\" end t) + (put-text-property (1- (point)) (point) 'syntax-table (if (= (char-after) ?`) '(1) '(9)))))) + +(defun go-import-add (arg import) + "Add a new import to the list of imports. + +When called with a prefix argument asks for an alternative name +to import the package as. + +If no list exists yet, one will be created if possible. + +If an identical import has been commented, it will be +uncommented, otherwise a new import will be added." + + ;; - If there's a matching `// import "foo"`, uncomment it + ;; - If we're in an import() block and there's a matching `"foo"`, uncomment it + ;; - Otherwise add a new import, with the appropriate syntax + (interactive + (list + current-prefix-arg + (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go--old-completion-list-style (go-packages)))))) + (save-excursion + (let (as line import-start) + (if arg + (setq as (read-from-minibuffer "Import as: "))) + (if as + (setq line (format "%s \"%s\"" as import)) + (setq line (format "\"%s\"" import))) + + (goto-char (point-min)) + (if (re-search-forward (concat "^[[:space:]]*//[[:space:]]*import " line "$") nil t) + (uncomment-region (line-beginning-position) (line-end-position)) + (case (go-goto-imports) + ('fail (message "Could not find a place to add import.")) + ('block-empty + (insert "\n\t" line "\n")) + ('block + (save-excursion + (re-search-backward "^import (") + (setq import-start (point))) + (if (re-search-backward (concat "^[[:space:]]*//[[:space:]]*" line "$") import-start t) + (uncomment-region (line-beginning-position) (line-end-position)) + (insert "\n\t" line))) + ('single (insert "import " line "\n")) + ('none (insert "\nimport (\n\t" line "\n)\n"))))))) + +(defun go-root-and-paths () + (let* ((output (split-string (shell-command-to-string (concat go-command " env GOROOT GOPATH")) + "\n")) + (root (car output)) + (paths (split-string (cadr output) path-separator))) + (append (list root) paths))) + +(defun go--string-prefix-p (s1 s2 &optional ignore-case) + "Return non-nil if S1 is a prefix of S2. +If IGNORE-CASE is non-nil, the comparison is case-insensitive." + (eq t (compare-strings s1 nil nil + s2 0 (length s1) ignore-case))) + +(defun go--directory-dirs (dir) + "Recursively return all subdirectories in DIR." + (if (file-directory-p dir) + (let ((dir (directory-file-name dir)) + (dirs '()) + (files (directory-files dir nil nil t))) + (dolist (file files) + (unless (member file '("." "..")) + (let ((file (concat dir "/" file))) + (if (file-directory-p file) + (setq dirs (append (cons file + (go--directory-dirs file)) + dirs)))))) + dirs) + '())) + + +(defun go-packages () + (sort + (delete-dups + (mapcan + (lambda (topdir) + (let ((pkgdir (concat topdir "/pkg/"))) + (mapcan (lambda (dir) + (mapcar (lambda (file) + (let ((sub (substring file (length pkgdir) -2))) + (unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub)) + (mapconcat #'identity (cdr (split-string sub "/")) "/")))) + (if (file-directory-p dir) + (directory-files dir t "\\.a$")))) + (if (file-directory-p pkgdir) + (go--directory-dirs pkgdir))))) + (go-root-and-paths))) + #'string<)) + +(defun go-unused-imports-lines () + ;; FIXME Technically, -o /dev/null fails in quite some cases (on + ;; Windows, when compiling from within GOPATH). Practically, + ;; however, it has the same end result: There won't be a + ;; compiled binary/archive, and we'll get our import errors when + ;; there are any. + (reverse (remove nil + (mapcar + (lambda (line) + (if (string-match "^\\(.+\\):\\([[:digit:]]+\\): imported and not used: \".+\".*$" line) + (if (string= (file-truename (match-string 1 line)) (file-truename buffer-file-name)) + (string-to-number (match-string 2 line))))) + (split-string (shell-command-to-string + (concat go-command + (if (string-match "_test\.go$" buffer-file-truename) + " test -c" + " build -o /dev/null"))) "\n"))))) + +(defun go-remove-unused-imports (arg) + "Removes all unused imports. If ARG is non-nil, unused imports +will be commented, otherwise they will be removed completely." + (interactive "P") + (save-excursion + (let ((cur-buffer (current-buffer)) flymake-state lines) + (when (boundp 'flymake-mode) + (setq flymake-state flymake-mode) + (flymake-mode-off)) + (save-some-buffers nil (lambda () (equal cur-buffer (current-buffer)))) + (if (buffer-modified-p) + (message "Cannot operate on unsaved buffer") + (setq lines (go-unused-imports-lines)) + (dolist (import lines) + (go--goto-line import) + (beginning-of-line) + (if arg + (comment-region (line-beginning-position) (line-end-position)) + (go--delete-whole-line))) + (message "Removed %d imports" (length lines))) + (if flymake-state (flymake-mode-on))))) + +(defun godef--find-file-line-column (specifier other-window) + "Given a file name in the format of `filename:line:column', +visit FILENAME and go to line LINE and column COLUMN." + (if (not (string-match "\\(.+\\):\\([0-9]+\\):\\([0-9]+\\)" specifier)) + ;; We've only been given a directory name + (funcall (if other-window #'find-file-other-window #'find-file) specifier) + (let ((filename (match-string 1 specifier)) + (line (string-to-number (match-string 2 specifier))) + (column (string-to-number (match-string 3 specifier)))) + (with-current-buffer (funcall (if other-window #'find-file-other-window #'find-file) filename) + (go--goto-line line) + (beginning-of-line) + (forward-char (1- column)) + (if (buffer-modified-p) + (message "Buffer is modified, file position might not have been correct")))))) + +(defun godef--call (point) + "Call godef, acquiring definition position and expression +description at POINT." + (if (go--xemacs-p) + (error "godef does not reliably work in XEmacs, expect bad results")) + (if (not (buffer-file-name (go--coverage-origin-buffer))) + (error "Cannot use godef on a buffer without a file name") + (let ((outbuf (get-buffer-create "*godef*"))) + (with-current-buffer outbuf + (erase-buffer)) + (call-process-region (point-min) + (point-max) + "godef" + nil + outbuf + nil + "-i" + "-t" + "-f" + (file-truename (buffer-file-name (go--coverage-origin-buffer))) + "-o" + (number-to-string (go--position-bytes point))) + (with-current-buffer outbuf + (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n"))))) + +(defun godef--successful-p (output) + (not (or (string= "-" output) + (string= "godef: no identifier found" output) + (go--string-prefix-p "godef: no declaration found for " output) + (go--string-prefix-p "error finding import path for " output)))) + +(defun godef--error (output) + (cond + ((godef--successful-p output) + nil) + ((string= "-" output) + "godef: expression is not defined anywhere") + (t + output))) + +(defun godef-describe (point) + "Describe the expression at POINT." + (interactive "d") + (condition-case nil + (let ((description (cdr (butlast (godef--call point) 1)))) + (if (not description) + (message "No description found for expression at point") + (message "%s" (mapconcat #'identity description "\n")))) + (file-error (message "Could not run godef binary")))) + +(defun godef-jump (point &optional other-window) + "Jump to the definition of the expression at POINT." + (interactive "d") + (condition-case nil + (let ((file (car (godef--call point)))) + (if (not (godef--successful-p file)) + (message "%s" (godef--error file)) + (push-mark) + (ring-insert find-tag-marker-ring (point-marker)) + (godef--find-file-line-column file other-window))) + (file-error (message "Could not run godef binary")))) + +(defun godef-jump-other-window (point) + (interactive "d") + (godef-jump point t)) + +(defun go--goto-line (line) + (goto-char (point-min)) + (forward-line (1- line))) + +(defun go--line-column-to-point (line column) + (save-excursion + (go--goto-line line) + (forward-char (1- column)) + (point))) + +(defstruct go--covered + start-line start-column end-line end-column covered count) + +(defun go--coverage-file () + "Return the coverage file to use, either by reading it from the +current coverage buffer or by prompting for it." + (if (boundp 'go--coverage-current-file-name) + go--coverage-current-file-name + (read-file-name "Coverage file: " nil nil t))) + +(defun go--coverage-origin-buffer () + "Return the buffer to base the coverage on." + (or (buffer-base-buffer) (current-buffer))) + +(defun go--coverage-face (count divisor) + "Return the intensity face for COUNT when using DIVISOR +to scale it to a range [0,10]. + +DIVISOR scales the absolute cover count to values from 0 to 10. +For DIVISOR = 0 the count will always translate to 8." + (let* ((norm (cond + ((= count 0) + -0.1) ;; Uncovered code, set to -0.1 so n becomes 0. + ((= divisor 0) + 0.8) ;; covermode=set, set to 0.8 so n becomes 8. + (t + (/ (log count) divisor)))) + (n (1+ (floor (* norm 9))))) ;; Convert normalized count [0,1] to intensity [0,10] + (concat "go-coverage-" (number-to-string n)))) + +(defun go--coverage-make-overlay (range divisor) + "Create a coverage overlay for a RANGE of covered/uncovered +code. Uses DIVISOR to scale absolute counts to a [0,10] scale." + (let* ((count (go--covered-count range)) + (face (go--coverage-face count divisor)) + (ov (make-overlay (go--line-column-to-point (go--covered-start-line range) + (go--covered-start-column range)) + (go--line-column-to-point (go--covered-end-line range) + (go--covered-end-column range))))) + + (overlay-put ov 'face face) + (overlay-put ov 'help-echo (format "Count: %d" count)))) + +(defun go--coverage-clear-overlays () + "Remove existing overlays and put a single untracked overlay +over the entire buffer." + (remove-overlays) + (overlay-put (make-overlay (point-min) (point-max)) + 'face + 'go-coverage-untracked)) + +(defun go--coverage-parse-file (coverage-file file-name) + "Parse COVERAGE-FILE and extract coverage information and +divisor for FILE-NAME." + (let (ranges + (max-count 0)) + (with-temp-buffer + (insert-file-contents coverage-file) + (go--goto-line 2) ;; Skip over mode + (while (not (eobp)) + (let* ((parts (split-string (buffer-substring (point-at-bol) (point-at-eol)) ":")) + (file (car parts)) + (rest (split-string (nth 1 parts) "[., ]"))) + + (destructuring-bind + (start-line start-column end-line end-column num count) + (mapcar #'string-to-number rest) + + (when (string= (file-name-nondirectory file) file-name) + (if (> count max-count) + (setq max-count count)) + (push (make-go--covered :start-line start-line + :start-column start-column + :end-line end-line + :end-column end-column + :covered (/= count 0) + :count count) + ranges))) + + (forward-line))) + + (list ranges (if (> max-count 0) (log max-count) 0))))) + +(defun go-coverage (&optional coverage-file) + "Open a clone of the current buffer and overlay it with +coverage information gathered via go test -coverprofile=COVERAGE-FILE. + +If COVERAGE-FILE is nil, it will either be inferred from the +current buffer if it's already a coverage buffer, or be prompted +for." + (interactive) + (let* ((cur-buffer (current-buffer)) + (origin-buffer (go--coverage-origin-buffer)) + (gocov-buffer-name (concat (buffer-name origin-buffer) "")) + (coverage-file (or coverage-file (go--coverage-file))) + (ranges-and-divisor (go--coverage-parse-file + coverage-file + (file-name-nondirectory (buffer-file-name origin-buffer)))) + (cov-mtime (nth 5 (file-attributes coverage-file))) + (cur-mtime (nth 5 (file-attributes (buffer-file-name origin-buffer))))) + + (if (< (float-time cov-mtime) (float-time cur-mtime)) + (message "Coverage file is older than the source file.")) + + (with-current-buffer (or (get-buffer gocov-buffer-name) + (make-indirect-buffer origin-buffer gocov-buffer-name t)) + (set (make-local-variable 'go--coverage-current-file-name) coverage-file) + + (save-excursion + (go--coverage-clear-overlays) + (dolist (range (car ranges-and-divisor)) + (go--coverage-make-overlay range (cadr ranges-and-divisor)))) + + (if (not (eq cur-buffer (current-buffer))) + (display-buffer (current-buffer) `(,go-coverage-display-buffer-func)))))) + +(provide 'go-mode) + +;;; go-mode.el ends here diff --git a/emacs.d/elpa/jade-mode-20150402.2053/jade-mode-autoloads.el b/emacs.d/elpa/jade-mode-20150402.2053/jade-mode-autoloads.el new file mode 100644 index 0000000..7eac119 --- /dev/null +++ b/emacs.d/elpa/jade-mode-20150402.2053/jade-mode-autoloads.el @@ -0,0 +1,23 @@ +;;; jade-mode-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "jade-mode" "jade-mode.el" (21796 17145 0 0)) +;;; Generated autoloads from jade-mode.el + +(autoload 'jade-mode "jade-mode" "\ +Major mode for editing jade node.js templates + +\(fn)" t nil) + +(add-to-list 'auto-mode-alist '("\\.jade\\'" . jade-mode)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; jade-mode-autoloads.el ends here diff --git a/emacs.d/elpa/jade-mode-20150402.2053/jade-mode-pkg.el b/emacs.d/elpa/jade-mode-20150402.2053/jade-mode-pkg.el new file mode 100644 index 0000000..4b17c83 --- /dev/null +++ b/emacs.d/elpa/jade-mode-20150402.2053/jade-mode-pkg.el @@ -0,0 +1 @@ +(define-package "jade-mode" "20150402.2053" "Major mode for editing .jade files" 'nil :url "https://github.com/brianc/jade-mode") diff --git a/emacs.d/elpa/jade-mode-20150402.2053/jade-mode.el b/emacs.d/elpa/jade-mode-20150402.2053/jade-mode.el new file mode 100644 index 0000000..d078c30 --- /dev/null +++ b/emacs.d/elpa/jade-mode-20150402.2053/jade-mode.el @@ -0,0 +1,395 @@ +;;; jade-mode.el --- Major mode for editing .jade files +;;; +;;; URL: https://github.com/brianc/jade-mode +;; Package-Version: 20150402.2053 +;;; Author: Brian M. Carlson and other contributors +;;; inspired by http://xahlee.org/emacs/elisp_syntax_coloring.html +(require 'font-lock) +(require 'js) + +(defvar jade-tab-width) + +(defun jade-debug (string &rest args) + "Prints a debug message" + (apply 'message (append (list string) args))) + +(defmacro jade-line-as-string () + "Returns the current line as a string." + `(buffer-substring-no-properties (point-at-bol) (point-at-eol))) + +(defun jade-empty-line-p () + "If line is empty or not." + (= (point-at-eol) (point-at-bol))) + +(defun jade-blank-line-p () + "Returns t when line contains only whitespace chars, nil otherwise." + (string-match-p "^\\s-*$" (jade-line-as-string))) + +(defun jade-comment-dwim (arg) + "Comment or uncomment current line or region in a smart way." + (interactive "*P") + (require 'newcomment) + (let ((start (if (region-active-p) + + ;; when region is active, use beginning of line at + ;; beginning of region (this way we don't start + ;; commenting in the middle of a line) + (progn + (save-excursion + (goto-char (region-beginning)) + (point-at-bol))) + + ;; without a region, just use beginning of current line + (point-at-bol))) + + ;; same logic applies for end of line/region + (end (if (region-active-p) + (progn + (save-excursion + (goto-char (region-end)) + (point-at-eol))) + (point-at-eol)))) + + ;; once we pick good values for start/end of region, simply use + ;; `comment-or-uncomment-region' from `newcomment' lib, and skip + ;; to next line for convenience + (comment-or-uncomment-region start end) + (forward-line))) + +(defconst jade-keywords + (eval-when-compile + (regexp-opt + '("if" "else" "for" "in" "each" "case" "when" "default" "block" "extends" + "block append" "block prepend" "append" "prepend" + "include" "yield" "mixin") 'words)) + "Jade keywords.") + +(defvar jade-tag-re "[a-z][a-z0-9]*" + "Regexp used to match a basic html tag, e.g. link, a, div") + +(defvar jade-id-re "#[a-zA-Z][0-9a-zA-Z_\\-]*" + "Regexp used to match an ID literal, e.g. #id, #id-one_23") + +(defvar jade-class-re "[.][a-zA-Z][0-9a-zA-Z_\\-]*" + "Regexp used to match a class literal, e.g. .class, .class_name-123") + +(defvar jade-double-quote-string-re "[\"]\\(\\\\.\\|[^\"\n]\\)*[\"]" + "Regexp used to match a double-quoted string literal") + +(defvar jade-single-quote-string-re "[']\\(\\\\.\\|[^'\n]\\)*[']" + "Regexp used to match a single-quoted string literal") + +(defvar jade-tag-declaration-char-re "[-a-zA-Z0-9_.#]" + "Regexp used to match a character in a tag declaration") + +(defvar jade-font-lock-keywords + `( + (,jade-keywords . font-lock-keyword-face) ;; keywords + ("#\\(\\w\\|_\\|-\\)*" . font-lock-variable-name-face) ;; id + ("\\(?:^[ {2,}]*\\(?:[a-z0-9_:\\-]*\\)\\)?\\(#[A-Za-z0-9\-\_]*[^ ]\\)" 1 font-lock-variable-name-face) ;; id + ("\\(?:^[ {2,}]*\\(?:[a-z0-9_:\\-]*\\)\\)?\\(\\.[A-Za-z0-9\-\_]*\\)" 1 font-lock-type-face) ;; class name + ("^[ \t]*\\([a-zA-Z0-9]+\\)" 1 font-lock-function-name-face) ;; tag name + ("^[ \t]*\\(-?//.*\\)" 1 font-lock-comment-face t) ;; jade block comments + + ;; remove highlighting from literal content following tag/class/id + ;; e.g. tag Inner text + ;; tag#id.class INNER text + (,(concat "^\\s-*" + + ;; start with a basic html tag, an ID, or a class + "\\(" jade-tag-re "\\|" jade-id-re "\\|" jade-class-re "\\)" + + ;; followed by zero or more of either an ID or a class + "\\(" jade-id-re "\\|" jade-class-re "\\)*" + + ;; then an optional set of parens with JS inside + ;; TODO highlight JS in a meaningful way + "\\(" "(.*)" "\\)?" + + ;; then a space (not an equals sign), and match the rest of the line + ;; and remove any font-lock faces applied + "[ ]\\(.+\\)") 4 nil t) + + ;; remove highlighting from lines opening with a pipe `|' + ;; e.g. | keywords like for should not be highlighted here + ;; | I'm not supposed to highlight single quotes either + (,(concat "^[ \t]*" "\\(" "|.*" "\\)") 1 nil t) + + ;; we abuse font-lock a bit here; these functions inject js-mode + ;; highlighting under the guise of matching text for more standard + ;; font-lock face application (like we do with regexps above) + (jade-highlight-js-in-parens 1 font-lock-preprocessor-face) + (jade-highlight-js-after-tag 1 font-lock-preprocessor-face) + + ;; doctype re-overrides some of the fontification rules + ("^!!!\\|doctype[ ]?.*" 0 font-lock-comment-face t))) + +(defun jade-highlight-js-in-parens (limit) + "Search for a tag declaration (up to LIMIT) which contains a paren +block, then highlight the region between the parentheses as +javascript." + (when (re-search-forward (concat "^[ \t]*" jade-tag-declaration-char-re "+" "(") limit t) + (forward-char -1) + (let ((start (point)) + (end (progn + (forward-sexp) + (1- (point))))) + (jade-fontify-region-as-js start end)) + + ;; return some empty match data to appease the font-lock gods + (looking-at "\\(\\)"))) + +(defun jade-highlight-js-after-tag (limit) + "Search for a valid js block, then highlight its contents with js-mode syntax highlighting" + (when (re-search-forward "^[ \t]*" limit t) + (when (not (eolp)) + + ;; before parsing/skipping ahead, check the first char; if it's + ;; - (not a comment starter!) or =, then we know it's a JS block + (if (or (looking-at "-[^/]") (looking-at "=")) + (jade-fontify-region-as-js (point) (point-at-eol)) + + ;; no luck with the first char, so parse to the end of the tag + ;; (including optional paren block) and check for '=' + (jade-goto-end-of-tag) + (if (and (looking-at "=") (not (eolp))) + (jade-fontify-region-as-js (point) (point-at-eol))))) + + ;; return some empty match data to appease the font-lock gods + (looking-at "\\(\\)"))) + +(defun jade-goto-end-of-tag () + "Skip ahead over whitespace, tag characters (defined in +`jade-tag-declaration-char-re'), and paren blocks (using +`forward-sexp') to put point at the end of a full tag declaration (but +before its content). Use when point is inside or to the left of a tag +declaration" + (interactive) + + ;; skip indentation characters + (while (looking-at "[ \t]") + (forward-char 1)) + + (while (looking-at jade-tag-declaration-char-re) + (forward-char 1)) + (if (looking-at "(") + (forward-sexp 1))) + + +(defvar jade-syntax-table + (let ((table (make-syntax-table))) + (modify-syntax-entry ?\" "\"" table) + (modify-syntax-entry ?\' "\"" table) + (modify-syntax-entry ?_ "w" table) + table) + "Syntax table for `jade-mode'.") + +(defun jade-region-for-sexp () + "Selects the current sexp as the region" + (interactive) + (beginning-of-line) + (let ((ci (current-indentation))) + (push-mark nil nil t) + (while (> (jade-next-line-indentation) ci) + (forward-line) + (end-of-line)))) + +(defun jade-indent () + "Indent current region or line. +Calls `jade-indent-region' with an active region or `jade-indent-line' +without." + (interactive) + (if (region-active-p) + (jade-indent-region + + ;; use beginning of line at region-beginning + (save-excursion + (goto-char (region-beginning)) + (line-beginning-position)) + + ;; use end of line at region-end + (save-excursion + (goto-char (region-end)) + (line-end-position))) + (jade-indent-line))) + +(defun jade-indent-line () + "Indent current line of jade code. +If the cursor is left of the current indentation, then the first call +will simply jump to the current indent. Subsequent calls will indent +the current line by `jade-tab-width' until current indentation is +nested one tab-width deeper than its parent tag. At that point, an +additional call will reset indentation to column 0." + (interactive) + (let ((left-of-indent (>= (current-column) (current-indentation))) + (indent (jade-calculate-indent-target))) + (if left-of-indent + + ;; if cursor is at or beyond current indent, indent normally + (indent-line-to indent) + + ;; if cursor is trailing current indent, first indentation should + ;; jump to the current indentation column (subsequent calls + ;; will indent normally) + (indent-line-to (current-indentation))))) + +(defun jade-indent-region (start end) + "Indent active region according to indentation of region's first +line relative to its parent. Keep region active after command +terminates (to facilitate subsequent indentations of the same region)" + (interactive "r") + (save-excursion + + ;; go to start of region so we can find out its target indent + (goto-char start) + + ;; keep region active after command + (let* ((deactivate-mark) + + ;; find indent target for first line + (first-line-indent-target (jade-calculate-indent-target)) + + ;; use current-indentation to turn target indent into + ;; a relative indent to apply to each line in region + (first-line-relative-indent + (- first-line-indent-target (current-indentation)))) + + ;; apply relative indent + (indent-rigidly start end first-line-relative-indent)))) + +(defun jade-calculate-indent-target () + "Return the column to which the current line should be indented." + (let ((max-indent (+ (jade-previous-line-indentation) jade-tab-width))) + (if (>= (current-indentation) max-indent) ;; if at max indentation + 0 + (+ (current-indentation) jade-tab-width)))) + +(defun jade-unindent () + "Unindent active region or current line." + (interactive) + (if (region-active-p) + (jade-unindent-region + + ;; use beginning of line at region-beginning + (save-excursion + (goto-char (region-beginning)) + (line-beginning-position)) + + ;; use end of line at region-end + (save-excursion + (goto-char (region-end)) + (line-end-position))) + + ;; when no region is active + (jade-unindent-line))) + +(defun jade-unindent-line () + "Unindent line under point by `jade-tab-width'. +Calling when `current-indentation' is 0 will have no effect." + (indent-line-to + (max + (- (current-indentation) jade-tab-width) + 0))) + +(defun jade-unindent-region (start end) + "Unindent active region by `jade-tab-width'. +Follows indentation behavior of `indent-rigidly'." + (interactive "r") + (let (deactivate-mark) + (indent-rigidly start end (- jade-tab-width)))) + +(defun jade-previous-line-indentation () + "Get the indentation of the previous (non-blank) line (from point)." + (interactive) + (save-excursion + + ;; move up to the nearest non-blank line (or buffer start) + (while (progn ;; progn used to get do...while control flow + (forward-line -1) + (and (jade-blank-line-p) (not (= (point-at-bol) (point-min)))))) + (let ((prev-line-indent (current-indentation))) + prev-line-indent))) + +(defun jade-next-line-indentation () + "Get the indentation of the next (non-blank) line (from point)." + (interactive) + (save-excursion + + ;; move down to the next non-blank line (or buffer end) + (while (progn ;; progn used to get do...while control flow + (forward-line 1) + (and (jade-blank-line-p) (not (= (point-at-eol) (point-max)))))) + (let ((next-line-indent (current-indentation))) + next-line-indent))) + +(defun jade-newline-and-indent () + "Insert newline and indent to parent's indentation level." + (interactive) + (newline) + (indent-line-to (max (jade-previous-line-indentation) 0))) + +(defun jade-fontify-region-as-js (beg end) + "Fontify a region between BEG and END using js-mode fontification. +Inspired by (read: stolen from) from `haml-mode'. Note the clever use +of `narrow-to-region' by the author of `haml-mode' to keep syntactic +highlighting (maybe other things too?) from looking beyond the +region defined by BEG and END." + (save-excursion + (save-match-data + (let ((font-lock-keywords js--font-lock-keywords-3) + (font-lock-syntax-table js-mode-syntax-table) + (font-lock-syntactic-keywords nil) + (syntax-propertize-function nil) + (font-lock-multiline 'undecided) + (font-lock-dont-widen t) + font-lock-keywords-only + font-lock-extend-region-functions + font-lock-keywords-case-fold-search) + (when (and (fboundp 'js--update-quick-match-re) (null js--quick-match-re-func)) + (js--update-quick-match-re)) + (save-restriction + (narrow-to-region beg end) + (font-lock-fontify-region beg end)))))) + +(defvar jade-mode-map (make-sparse-keymap)) + +;; mode declaration +;;;###autoload +(define-derived-mode jade-mode fundamental-mode + "Jade" + "Major mode for editing jade node.js templates" + :syntax-table jade-syntax-table + + ;; turn off electric indent mode for jade buffers (by default, at least) + (when (fboundp 'electric-indent-local-mode) + (electric-indent-local-mode 0)) + (setq mode-name "Jade") + (setq major-mode 'jade-mode) + + ;; comment syntax + (set (make-local-variable 'comment-start) "//- ") + (set (make-local-variable 'comment-start-skip) "//-\\s-*") + + (set (make-local-variable 'jade-tab-width) 2) + (set (make-local-variable 'indent-line-function) 'jade-indent-line) + (set (make-local-variable 'indent-region-function) 'jade-indent-region) + (set (make-local-variable 'indent-tabs-mode) nil) + + ;; keymap + (use-local-map jade-mode-map) + + ;; modify the keymap + (define-key jade-mode-map [remap comment-dwim] 'jade-comment-dwim) + (define-key jade-mode-map [tab] 'jade-indent) + (define-key jade-mode-map [backtab] 'jade-unindent) + (define-key jade-mode-map (kbd "RET") 'jade-newline-and-indent) + + ;; highlight keywords, ignore syntactic font-lock + (setq font-lock-defaults '(jade-font-lock-keywords t))) + + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.jade\\'" . jade-mode)) + +(provide 'jade-mode) +;;; jade-mode.el ends here diff --git a/emacs.d/elpa/jinja2-mode-20141128.207/jinja2-mode-autoloads.el b/emacs.d/elpa/jinja2-mode-20141128.207/jinja2-mode-autoloads.el new file mode 100644 index 0000000..0a2d6a8 --- /dev/null +++ b/emacs.d/elpa/jinja2-mode-20141128.207/jinja2-mode-autoloads.el @@ -0,0 +1,24 @@ +;;; jinja2-mode-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "jinja2-mode" "jinja2-mode.el" (21811 52554 +;;;;;; 0 0)) +;;; Generated autoloads from jinja2-mode.el + +(autoload 'jinja2-mode "jinja2-mode" "\ +Major mode for editing jinja2 files + +\(fn)" t nil) + +(add-to-list 'auto-mode-alist '("\\.jinja2\\'" . jinja2-mode)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; jinja2-mode-autoloads.el ends here diff --git a/emacs.d/elpa/jinja2-mode-20141128.207/jinja2-mode-pkg.el b/emacs.d/elpa/jinja2-mode-20141128.207/jinja2-mode-pkg.el new file mode 100644 index 0000000..dca35fb --- /dev/null +++ b/emacs.d/elpa/jinja2-mode-20141128.207/jinja2-mode-pkg.el @@ -0,0 +1 @@ +(define-package "jinja2-mode" "20141128.207" "A major mode for jinja2" 'nil) diff --git a/emacs.d/elpa/jinja2-mode-20141128.207/jinja2-mode.el b/emacs.d/elpa/jinja2-mode-20141128.207/jinja2-mode.el new file mode 100644 index 0000000..3488d9b --- /dev/null +++ b/emacs.d/elpa/jinja2-mode-20141128.207/jinja2-mode.el @@ -0,0 +1,329 @@ +;;; jinja2-mode.el --- A major mode for jinja2 + +;; Copyright (C) 2011 Florian Mounier aka paradoxxxzero + +;; Author: Florian Mounier aka paradoxxxzero +;; Version: 0.2 +;; Package-Version: 20141128.207 + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This is an emacs major mode for jinja2 with: +;; syntax highlighting +;; sgml/html integration +;; indentation (working with sgml) +;; more to come + +;; This file comes from http://github.com/paradoxxxzero/jinja2-mode + +;;; Code: + +(require 'sgml-mode) + +(defgroup jinja2 nil + "Major mode for editing jinja2 code." + :prefix "jinja2-" + :group 'languages) + +(defcustom jinja2-user-keywords nil + "Custom keyword names" + :type '(repeat string) + :group 'jinja2) + +(defcustom jinja2-user-functions nil + "Custom function names" + :type '(repeat string) + :group 'jinja2) + +;; (defcustom jinja2-debug nil +;; "Log indentation logic" +;; :type 'boolean +;; :group 'jinja2) + +(defun jinja2-closing-keywords () + (append + jinja2-user-keywords + '("if" "for" "block" "filter" "with" + "raw" "macro" "autoescape" "trans" "call"))) + +(defun jinja2-indenting-keywords () + (append + (jinja2-closing-keywords) + '("else" "elif"))) + +(defun jinja2-builtin-keywords () + '("as" "autoescape" "debug" "extends" + "firstof" "in" "include" "load" + "now" "regroup" "ssi" "templatetag" + "url" "widthratio" "elif" "true" + "false" "none" "False" "True" "None" + "loop" "super" "caller" "varargs" + "kwargs" "break" "continue" "is" + "not" "or" "and" + "do" "pluralize" "set" "from" "import" + "context" "with" "without" "ignore" + "missing" "scoped")) + +(defun jinja2-functions-keywords () + (append + jinja2-user-functions + '("abs" "attr" "batch" "capitalize" + "center" "default" "dictsort" + "escape" "filesizeformat" "first" + "float" "forceescape" "format" + "groupby" "indent" "int" "join" + "last" "length" "list" "lower" + "pprint" "random" "replace" + "reverse" "round" "safe" "slice" + "sort" "string" "striptags" "sum" + "title" "trim" "truncate" "upper" + "urlize" "wordcount" "wordwrap" "xmlattr"))) + +(defun jinja2-find-open-tag () + (if (search-backward-regexp + (rx-to-string + `(and "{%" + (? "-") + (* whitespace) + (? (group + "end")) + (group + ,(append '(or) + (jinja2-closing-keywords) + )) + (group + (*? anything)) + (* whitespace) + (? "-") + "%}")) nil t) + (if (match-string 1) ;; End tag, going on + (let ((matches (jinja2-find-open-tag))) + (if (string= (car matches) (match-string 2)) + (jinja2-find-open-tag) + (list (match-string 2) (match-string 3)))) + (list (match-string 2) (match-string 3))) + nil)) + +(defun jinja2-close-tag () + "Close the previously opened template tag." + (interactive) + (let ((open-tag (save-excursion (jinja2-find-open-tag)))) + (if open-tag + (insert + (if (string= (car open-tag) "block") + (format "{%% end%s%s %%}" + (car open-tag)(nth 1 open-tag)) + (format "{%% end%s %%}" + (match-string 2)))) + (error "Nothing to close"))) + (save-excursion (jinja2-indent-line))) + +(defun jinja2-insert-tag () + "Insert an empty tag" + (interactive) + (insert "{% ") + (save-excursion + (insert " %}") + (jinja2-indent-line))) + +(defun jinja2-insert-var () + "Insert an empty tag" + (interactive) + (insert "{{ ") + (save-excursion + (insert " }}") + (jinja2-indent-line))) + +(defun jinja2-insert-comment () + "Insert an empty tag" + (interactive) + (insert "{# ") + (save-excursion + (insert " #}") + (jinja2-indent-line))) + +(defconst jinja2-font-lock-comments + `( + (,(rx "{#" + (* whitespace) + (group + (*? anything) + ) + (* whitespace) + "#}") + . (1 font-lock-comment-face t)))) + +(defconst jinja2-font-lock-keywords-1 + (append + jinja2-font-lock-comments + sgml-font-lock-keywords-1)) + +(defconst jinja2-font-lock-keywords-2 + (append + jinja2-font-lock-keywords-1 + sgml-font-lock-keywords-2)) + +(defconst jinja2-font-lock-keywords-3 + (append + jinja2-font-lock-keywords-1 + jinja2-font-lock-keywords-2 + `( + (,(rx "{{" + (* whitespace) + (group + (*? anything) + ) + (* + "|" (* whitespace) (*? anything)) + (* whitespace) + "}}") (1 font-lock-variable-name-face t)) + (,(rx (group "|" (* whitespace)) + (group (+ word)) + ) + (1 font-lock-keyword-face t) + (2 font-lock-warning-face t)) + (,(rx-to-string `(and (group "|" (* whitespace)) + (group + ,(append '(or) + (jinja2-functions-keywords) + )))) + (1 font-lock-keyword-face t) + (2 font-lock-function-name-face t) + ) + (,(rx-to-string `(and word-start + (? "end") + ,(append '(or) + (jinja2-indenting-keywords) + ) + word-end)) (0 font-lock-keyword-face)) + (,(rx-to-string `(and word-start + ,(append '(or) + (jinja2-builtin-keywords) + ) + word-end)) (0 font-lock-builtin-face)) + + (,(rx (or "{%" "%}" "{%-" "-%}")) (0 font-lock-function-name-face t)) + (,(rx (or "{{" "}}")) (0 font-lock-type-face t)) + (,(rx "{#" + (* whitespace) + (group + (*? anything) + ) + (* whitespace) + "#}") + (1 font-lock-comment-face t)) + (,(rx (or "{#" "#}")) (0 font-lock-comment-delimiter-face t)) + ))) + +(defvar jinja2-font-lock-keywords + jinja2-font-lock-keywords-1) + +(defun sgml-indent-line-num () + "Indent the current line as SGML." + (let* ((savep (point)) + (indent-col + (save-excursion + (back-to-indentation) + (if (>= (point) savep) (setq savep nil)) + (sgml-calculate-indent)))) + (if (null indent-col) + 0 + (if savep + (save-excursion indent-col) + indent-col)))) + +(defun jinja2-calculate-indent-backward (default) + "Return indent column based on previous lines" + (let ((indent-width sgml-basic-offset) (default (sgml-indent-line-num))) + (forward-line -1) + (if (looking-at "^[ \t]*{%-? *end") ; Don't indent after end + (current-indentation) + (if (looking-at (concat "^[ \t]*{%-? *.*?{%-? *end" (regexp-opt (jinja2-indenting-keywords)))) + (current-indentation) + (if (looking-at (concat "^[ \t]*{%-? *" (regexp-opt (jinja2-indenting-keywords)))) ; Check start tag + (+ (current-indentation) indent-width) + (if (looking-at "^[ \t]*<") ; Assume sgml block trust sgml + default + (if (bobp) + 0 + (jinja2-calculate-indent-backward default)))))))) + + +(defun jinja2-calculate-indent () + "Return indent column" + (if (bobp) ; Check begining of buffer + 0 + (let ((indent-width sgml-basic-offset) (default (sgml-indent-line-num))) + (if (looking-at "^[ \t]*{%-? *e\\(nd\\|lse\\|lif\\)") ; Check close tag + (save-excursion + (forward-line -1) + (if + (and + (looking-at (concat "^[ \t]*{%-? *" (regexp-opt (jinja2-indenting-keywords)))) + (not (looking-at (concat "^[ \t]*{%-? *.*?{% *end" (regexp-opt (jinja2-indenting-keywords)))))) + (current-indentation) + (- (current-indentation) indent-width))) + (if (looking-at "^[ \t]* +;; Keywords: languages, javascript, imenu + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; This package adds Imenu support for additional framework constructs and +;; structural patterns to `js2-mode'. + +;; Usage: + +;; (add-hook 'js2-mode-hook 'js2-imenu-extras-mode) + +;; To customize how it works: +;; M-x customize-group RET js2-imenu RET + +(eval-when-compile + (require 'cl)) + +(require 'js2-mode) + +(defvar js2-imenu-extension-styles + `((:framework jquery + :call-re "\\_<\\(?:jQuery\\|\\$\\|_\\)\\.extend\\s-*(" + :recorder js2-imenu-record-jquery-extend) + + (:framework jquery-ui + :call-re "^\\s-*\\(?:jQuery\\|\\$\\)\\.widget\\s-*(" + :recorder js2-imenu-record-string-declare) + + (:framework dojo + :call-re "^\\s-*dojo.declare\\s-*(" + :recorder js2-imenu-record-string-declare) + + (:framework backbone + :call-re ,(concat "\\_<" js2-mode-identifier-re "\\.extend\\s-*(") + :recorder js2-imenu-record-backbone-extend) + + (:framework enyo + :call-re "\\_ +;; mooz +;; Dmitry Gutov +;; URL: https://github.com/mooz/js2-mode/ +;; http://code.google.com/p/js2-mode/ +;; Version: 20150202 +;; Keywords: languages, javascript +;; Package-Requires: ((emacs "24.1") (cl-lib "0.5")) + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; This JavaScript editing mode supports: + +;; - strict recognition of the Ecma-262 language standard +;; - support for most Rhino and SpiderMonkey extensions from 1.5 and up +;; - parsing support for ECMAScript for XML (E4X, ECMA-357) +;; - accurate syntax highlighting using a recursive-descent parser +;; - on-the-fly reporting of syntax errors and strict-mode warnings +;; - undeclared-variable warnings using a configurable externs framework +;; - "bouncing" line indentation to choose among alternate indentation points +;; - smart line-wrapping within comments and strings +;; - code folding: +;; - show some or all function bodies as {...} +;; - show some or all block comments as /*...*/ +;; - context-sensitive menu bar and popup menus +;; - code browsing using the `imenu' package +;; - many customization options + +;; Installation: +;; +;; To install it as your major mode for JavaScript editing: + +;; (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode)) + +;; Alternatively, to install it as a minor mode just for JavaScript linting, +;; you must add it to the appropriate major-mode hook. Normally this would be: + +;; (add-hook 'js-mode-hook 'js2-minor-mode) + +;; You may also want to hook it in for shell scripts running via node.js: + +;; (add-to-list 'interpreter-mode-alist '("node" . js2-mode)) + +;; To customize how it works: +;; M-x customize-group RET js2-mode RET + +;; Notes: + +;; This mode includes a port of Mozilla Rhino's scanner, parser and +;; symbol table. Ideally it should stay in sync with Rhino, keeping +;; `js2-mode' current as the EcmaScript language standard evolves. + +;; Unlike cc-engine based language modes, js2-mode's line-indentation is not +;; customizable. It is a surprising amount of work to support customizable +;; indentation. The current compromise is that the tab key lets you cycle among +;; various likely indentation points, similar to the behavior of python-mode. + +;; This mode does not yet work with "multi-mode" modes such as `mmm-mode' +;; and `mumamo', although it could be made to do so with some effort. +;; This means that `js2-mode' is currently only useful for editing JavaScript +;; files, and not for editing JavaScript within + + + + diff --git a/emacs.d/elpa/skewer-mode-20150422.1818/skewer-bower.el b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-bower.el new file mode 100644 index 0000000..69bfbe6 --- /dev/null +++ b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-bower.el @@ -0,0 +1,217 @@ +;;; skewer-bower.el --- dynamic library loading -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;;; Commentary: + +;; This package loads libraries into the current page using the bower +;; infrastructure. Note: bower is not actually used by this package +;; and so does *not* need to be installed. Only git is required (see +;; `skewer-bower-git-executable'). It will try to learn how to run git +;; from Magit if available. + +;; The interactive command for loading libraries is +;; `skewer-bower-load'. It will prompt for a library and a version, +;; automatically fetching it from the bower infrastructure if needed. +;; For example, I often find it handy to load some version of jQuery +;; when poking around at a page that doesn't already have it loaded. + +;; Caveat: unfortunately the bower infrastructure is a mess; many +;; packages are in some sort of broken state -- missing dependencies, +;; missing metadata, broken metadata, or an invalid repository URL. +;; Some of this is due to under-specification of the metadata by the +;; bower project. Broken packages are unlikely to be loadable by +;; skewer-bower. + +;;; Code: + +(require 'cl-lib) +(require 'skewer-mode) +(require 'simple-httpd) +(require 'magit nil t) ; optional + +(defcustom skewer-bower-cache-dir (locate-user-emacs-file "skewer-cache") + "Location of library cache (git repositories)." + :type 'string + :group 'skewer) + +(defcustom skewer-bower-endpoint "https://bower.herokuapp.com" + "Endpoint for accessing package information." + :type 'string + :group 'skewer) + +(defcustom skewer-bower-json '("bower.json" "package.json" "component.json") + "Files to search for package metadata." + :type 'list + :group 'skewer) + +; Try to match Magit's configuration if available +(defcustom skewer-bower-git-executable "git" + "Name of the git executable." + :type 'string + :group 'skewer) + +(defvar skewer-bower-packages nil + "Alist of all packages known to bower.") + +(defvar skewer-bower-refreshed nil + "List of packages that have been refreshed recently. This keeps +them from hitting the network frequently.") + +;;;###autoload +(defun skewer-bower-refresh () + "Update the package listing and packages synchronously." + (interactive) + (cl-declare (special url-http-end-of-headers)) + (setf skewer-bower-refreshed nil) + (with-current-buffer + (url-retrieve-synchronously (concat skewer-bower-endpoint "/packages")) + (setf (point) url-http-end-of-headers) + (setf skewer-bower-packages + (cl-sort + (cl-loop for package across (json-read) + collect (cons (cdr (assoc 'name package)) + (cdr (assoc 'url package)))) + #'string< :key #'car)))) + +;; Git functions + +(defun skewer-bower-cache (package) + "Return the cache repository directory for PACKAGE." + (unless (file-exists-p skewer-bower-cache-dir) + (make-directory skewer-bower-cache-dir t)) + (expand-file-name package skewer-bower-cache-dir)) + +(defun skewer-bower-git (package &rest args) + "Run git for PACKAGE's repository with ARGS." + (with-temp-buffer + (when (zerop (apply #'call-process skewer-bower-git-executable nil t nil + (format "--git-dir=%s" (skewer-bower-cache package)) + args)) + (buffer-string)))) + +(defun skewer-bower-git-clone (url package) + "Clone or fetch PACKAGE's repository from URL if needed." + (if (member package skewer-bower-refreshed) + t + (let* ((cache (skewer-bower-cache package)) + (status + (if (file-exists-p cache) + (when (skewer-bower-git package "fetch") + (push package skewer-bower-refreshed)) + (skewer-bower-git package "clone" "--bare" url cache)))) + (not (null status))))) + +(defun skewer-bower-git-show (package version file) + "Grab FILE from PACKAGE at version VERSION." + (when (string-match-p "^\\./" file) ; avoid relative paths + (setf file (substring file 2))) + (skewer-bower-git package "show" (format "%s:%s" version file))) + +(defun skewer-bower-git-tag (package) + "List all the tags in PACKAGE's repository." + (split-string (skewer-bower-git package "tag"))) + +;; Bower functions + +(defun skewer-bower-package-ensure (package) + "Ensure a package is installed in the cache and up to date. +Emit an error if the package could not be ensured." + (when (null skewer-bower-packages) (skewer-bower-refresh)) + (let ((url (cdr (assoc package skewer-bower-packages)))) + (when (null url) + (error "Unknown package: %s" package)) + (when (null (skewer-bower-git-clone url package)) + (error "Failed to fetch: %s" url)) + t)) + +(defun skewer-bower-package-versions (package) + "List the available versions for a package. Always returns at +least one version." + (skewer-bower-package-ensure package) + (or (sort (skewer-bower-git-tag package) #'string<) + (list "master"))) + +(defun skewer-bower-get-config (package &optional version) + "Get the configuration alist for PACKAGE at VERSION. Return nil +if no configuration could be found." + (skewer-bower-package-ensure package) + (unless version (setf version "master")) + (json-read-from-string + (cl-loop for file in skewer-bower-json + for config = (skewer-bower-git-show package version file) + when config return it + finally (return "null")))) + +;; Serving the library + +(defvar skewer-bower-history () + "Library selection history for `completing-read'.") + +(defun skewer-bowser--path (package version main) + "Return the simple-httpd hosted path for PACKAGE." + (format "/skewer/bower/%s/%s/%s" package (or version "master") main)) + +(defun skewer-bower-prompt-package () + "Prompt for a package and version from the user." + (when (null skewer-bower-packages) (skewer-bower-refresh)) + ;; ido-completing-read bug workaround: + (when (> (length skewer-bower-history) 32) + (setf skewer-bower-history (cl-subseq skewer-bower-history 0 16))) + (let* ((packages (mapcar #'car skewer-bower-packages)) + (selection (nconc skewer-bower-history packages)) + (package (completing-read "Library: " selection nil t nil + 'skewer-bower-history)) + (versions (reverse (skewer-bower-package-versions package))) + (version (completing-read "Version: " versions + nil t nil nil (car versions)))) + (list package version))) + +(defun skewer-bower--js-p (filename) + "Return non-nil if FILENAME looks like JavaScript." + (string-match "\\.js$" filename)) + +(defun skewer-bower-guess-main (package version config) + "Attempt to determine the main entrypoints from a potentially +incomplete or incorrect bower configuration. Returns nil if +guessing failed." + (let ((check (apply-partially #'skewer-bower-git-show package version)) + (main (cdr (assoc 'main config)))) + (cond ((and (vectorp main) (cl-some check main)) + (cl-coerce (cl-remove-if-not #'skewer-bower--js-p main) 'list)) + ((and (stringp main) (funcall check main)) + (list main)) + ((funcall check (concat package ".js")) + (list (concat package ".js"))) + ((funcall check package) + (list package))))) + +;;;###autoload +(defun skewer-bower-load (package &optional version) + "Dynamically load a library from bower into the current page." + (interactive (skewer-bower-prompt-package)) + (let* ((config (skewer-bower-get-config package version)) + (deps (cdr (assoc 'dependencies config))) + (main (skewer-bower-guess-main package version config))) + (when (null main) + (error "Could not load %s (%s): no \"main\" entrypoint specified" + package version)) + (cl-loop for (dep . version) in deps + do (skewer-bower-load (format "%s" dep) version)) + (cl-loop for entrypoint in main + for path = (skewer-bowser--path package version entrypoint) + do (skewer-eval path nil :type "script")))) + +(defservlet skewer/bower "application/javascript; charset=utf-8" (path) + "Serve a script from the local bower repository cache." + (cl-destructuring-bind (_ _skewer _bower package version . parts) + (split-string path "/") + (let* ((file (mapconcat #'identity parts "/")) + (contents (skewer-bower-git-show package version file))) + (if contents + (insert contents) + (httpd-error t 404))))) + +(provide 'skewer-bower) + +;;; skewer-bower.el ends here diff --git a/emacs.d/elpa/skewer-mode-20150422.1818/skewer-css.el b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-css.el new file mode 100644 index 0000000..457c742 --- /dev/null +++ b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-css.el @@ -0,0 +1,134 @@ +;;; skewer-css.el --- skewer support for live-interaction CSS -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;;; Commentary: + +;; This minor mode provides functionality for CSS like plain Skewer +;; does for JavaScript. + +;; * C-x C-e -- `skewer-css-eval-current-declaration' +;; * C-M-x -- `skewer-css-eval-current-rule' +;; * C-c C-k -- `skewer-css-eval-buffer' + +;; These functions assume there are no comments within a CSS rule, +;; *especially* not within a declaration. In the former case, if you +;; keep the comment free of CSS syntax it should be able to manage +;; reasonably well. This may be fixed someday. + +;;; Code: + +(require 'css-mode) +(require 'skewer-mode) + +(defun skewer-css-trim (string) + "Trim and compress whitespace in the string." + (let ((cleaned (replace-regexp-in-string "[\t\n ]+" " " string))) + (replace-regexp-in-string "^[\t\n ]+\\|[\t\n ]+$" "" cleaned))) + +;; Parsing + +(defun skewer-css-beginning-of-rule () + "Move to the beginning of the current rule and return point." + (skewer-css-end-of-rule) + (re-search-backward "{") + (when (re-search-backward "[}/]" nil 'start) + (forward-char)) + (re-search-forward "[^ \t\n]") + (backward-char) + (point)) + +(defun skewer-css-end-of-rule () + "Move to the end of the current rule and return point." + (if (eql (char-before) ?}) + (point) + (re-search-forward "}"))) + +(defun skewer-css-end-of-declaration () + "Move to the end of the current declaration and return point." + (if (eql (char-before) ?\;) + (point) + (re-search-forward ";"))) + +(defun skewer-css-beginning-of-declaration () + "Move to the end of the current declaration and return point." + (skewer-css-end-of-declaration) + (re-search-backward ":") + (backward-sexp 1) + (point)) + +(defun skewer-css-selectors () + "Return the selectors for the current rule." + (save-excursion + (let ((start (skewer-css-beginning-of-rule)) + (end (1- (re-search-forward "{")))) + (skewer-css-trim + (buffer-substring-no-properties start end))))) + +(defun skewer-css-declaration () + "Return the current declaration as a pair of strings." + (save-excursion + (let ((start (skewer-css-beginning-of-declaration)) + (end (skewer-css-end-of-declaration))) + (let* ((clip (buffer-substring-no-properties start end)) + (pair (split-string clip ":"))) + (mapcar #'skewer-css-trim pair))))) + +;; Evaluation + +(defun skewer-css (rule) + "Add RULE as a new stylesheet." + (skewer-eval rule nil :type "css")) + +(defun skewer-css-eval-current-declaration () + "Evaluate the declaration at the point." + (interactive) + (save-excursion + (let ((selectors (skewer-css-selectors)) + (rule (skewer-css-declaration)) + (start (skewer-css-beginning-of-declaration)) + (end (skewer-css-end-of-declaration))) + (skewer-flash-region start end) + (skewer-css (apply #'format "%s { %s: %s }" selectors rule))))) + +(defun skewer-css-eval-current-rule () + "Evaluate the rule at the point." + (interactive) + (save-excursion + (let* ((start (skewer-css-beginning-of-rule)) + (end (skewer-css-end-of-rule)) + (rule (buffer-substring-no-properties start end))) + (skewer-flash-region start end) + (skewer-css (skewer-css-trim rule))))) + +(defun skewer-css-eval-buffer () + "Send the entire current buffer as a new stylesheet." + (interactive) + (skewer-css (buffer-substring-no-properties (point-min) (point-max)))) + +(defun skewer-css-clear-all () + "Remove *all* Skewer-added styles from the document." + (interactive) + (skewer-eval nil nil :type "cssClearAll")) + +;; Minor mode definition + +(defvar skewer-css-mode-map + (let ((map (make-sparse-keymap))) + (prog1 map + (define-key map (kbd "C-x C-e") 'skewer-css-eval-current-declaration) + (define-key map (kbd "C-M-x") 'skewer-css-eval-current-rule) + (define-key map (kbd "C-c C-k") 'skewer-css-eval-buffer) + (define-key map (kbd "C-c C-c") 'skewer-css-clear-all))) + "Keymap for skewer-css-mode.") + +;;;###autoload +(define-minor-mode skewer-css-mode + "Minor mode for interactively loading new CSS rules." + :lighter " skewer-css" + :keymap skewer-css-mode-map + :group 'skewer) + +(provide 'skewer-css) + +;;; skewer-css.el ends here diff --git a/emacs.d/elpa/skewer-mode-20150422.1818/skewer-everything.user.js b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-everything.user.js new file mode 100644 index 0000000..ad0158d --- /dev/null +++ b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-everything.user.js @@ -0,0 +1,51 @@ +// ==UserScript== +// @name Skewer Everything +// @description Add a toggle button to run Skewer on the current page +// @lastupdated 2013-01-24 +// @version 1.1 +// @license Public Domain +// @include /^https?:/// +// ==/UserScript== + +var host = 'http://localhost:8080'; + +var toggle = document.createElement('div'); +toggle.onclick = inject; +toggle.style.width = '0px'; +toggle.style.height = '0px'; +toggle.style.borderStyle = 'solid'; +toggle.style.borderWidth = '0 12px 12px 0'; +toggle.style.borderColor = 'transparent #F00 transparent transparent'; +toggle.style.position = 'absolute'; +toggle.style.right = 0; +toggle.style.top = 0; +toggle.style.zIndex = 214748364; + +var injected = false; + +function inject() { + if (!injected) { + var script = document.createElement('script'); + script.src = host + '/skewer'; + document.body.appendChild(script); + toggle.style.borderRightColor = '#0F0'; + } else { + /* break skewer to disable it */ + if (unsafeWindow.skewer) { // Greasemonkey + unsafeWindow.skewer.fn = null; + } else if (unsafeWindow.window.skewer) { // Tampermonkey + unsafeWindow.window.skewer.fn = null; + } + toggle.style.borderRightColor = '#F00'; + } + injected = !injected; + GM_setValue('auto.' + location, injected); +} + +/* Don't use on iframes. */ +if (window.top === window.self) { + document.body.appendChild(toggle); + if (GM_getValue('auto.' + location)) { + inject(); + } +} diff --git a/emacs.d/elpa/skewer-mode-20150422.1818/skewer-html.el b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-html.el new file mode 100644 index 0000000..e8fecbe --- /dev/null +++ b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-html.el @@ -0,0 +1,126 @@ +;;; skewer-html.el --- skewer support for live-interaction HTML -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;;; Commentary: + +;; This minor mode provides functionality for HTML like plain Skewer +;; does for JavaScript. There's no clean way to replace the body and +;; head elements of a live document, so "evaluating" these elements is +;; not supported. + +;; * C-M-x -- `skewer-html-eval-tag' + +;; See also `skewer-html-fetch-selector-into-buffer' for grabbing the +;; page as it current exists. + +;;; Code: + +(require 'cl-lib) +(require 'sgml-mode) +(require 'skewer-mode) + +;; Selector computation + +(defun skewer-html--cleanup (name) + "Cleanup tag names provided by sgml-mode." + (replace-regexp-in-string "/$" "" name)) + +(defun skewer-html--tag-after-point () + "Return the tag struct for the tag immediately following point." + (save-excursion + (forward-char 1) + (sgml-parse-tag-backward))) + +(defun skewer-html-compute-tag-nth () + "Compute the position of this tag within its parent." + (save-excursion + (let ((tag (car (last (sgml-get-context))))) + (if (null tag) + 1 + (cl-loop with start = (sgml-tag-name tag) + with stop = (save-excursion (sgml-get-context) (point)) + with n = 1 + do (sgml-skip-tag-backward 1) + while (> (point) stop) + when (equal start (sgml-tag-name + (skewer-html--tag-after-point))) + do (cl-incf n) + finally (return n)))))) + +(defun skewer-html-compute-tag-ancestry () + "Compute the ancestry chain at point." + (save-excursion + (nreverse + (cl-loop for nth = (skewer-html-compute-tag-nth) + for tag = (car (last (sgml-get-context))) + while tag + for name = (skewer-html--cleanup (sgml-tag-name tag)) + for type = (sgml-tag-type tag) + when (not (or (string= name "html") + (eq type 'close))) + collect (list name nth))))) + +(defun skewer-html-compute-selector () + "Compute the selector for exactly the tag around point." + (let ((ancestry (skewer-html-compute-tag-ancestry))) + (mapconcat (lambda (tag) + (format "%s:nth-of-type(%d)" (cl-first tag) (cl-second tag))) + ancestry " > "))) + +;; Fetching + +(defun skewer-html-fetch-selector (selector) + "Fetch the innerHTML of a selector." + (let ((result (skewer-eval-synchronously selector :type "fetchselector"))) + (if (skewer-success-p result) + (cdr (assoc 'value result)) + ""))) + +(defun skewer-html-fetch-selector-into-buffer (selector) + "Fetch the innerHTML of a selector and insert it into the active buffer." + (interactive "sSelector: ") + (insert (skewer-html-fetch-selector selector))) + +;; Evaluation + +(defun skewer-html-eval (string ancestry &optional append) + "Load HTML into a selector, optionally appending." + (let ((ancestry* (cl-coerce ancestry 'vector))) ; for JSON + (skewer-eval string nil :type "html" :extra `((ancestry . ,ancestry*) + (append . ,append))))) + +(defun skewer-html-eval-tag () + "Load HTML from the immediately surrounding tag." + (interactive) + (let ((ancestry (skewer-html-compute-tag-ancestry))) + (save-excursion + ;; Move to beginning of opening tag + (cl-loop for tag = (car (last (sgml-get-context))) + while (and tag (eq 'close (sgml-tag-type tag)))) + (let* ((beg (progn (point))) + (end (progn (sgml-skip-tag-forward 1) (point))) + (region (buffer-substring-no-properties beg end))) + (skewer-flash-region beg end) + (if (= (length ancestry) 1) + (error "Error: cannot eval body and head tags.") + (skewer-html-eval region ancestry nil)))))) + +;; Minor mode definition + +(defvar skewer-html-mode-map + (let ((map (make-sparse-keymap))) + (prog1 map + (define-key map (kbd "C-M-x") 'skewer-html-eval-tag))) + "Keymap for skewer-html-mode") + +;;;###autoload +(define-minor-mode skewer-html-mode + "Minor mode for interactively loading new HTML." + :lighter " skewer-html" + :keymap skewer-html-mode-map + :group 'skewer) + +(provide 'skewer-html) + +;;; skewer-html.el ends here diff --git a/emacs.d/elpa/skewer-mode-20150422.1818/skewer-mode-autoloads.el b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-mode-autoloads.el new file mode 100644 index 0000000..3c9dbbc --- /dev/null +++ b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-mode-autoloads.el @@ -0,0 +1,110 @@ +;;; skewer-mode-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "skewer-bower" "skewer-bower.el" (21836 47293 +;;;;;; 0 0)) +;;; Generated autoloads from skewer-bower.el + +(autoload 'skewer-bower-refresh "skewer-bower" "\ +Update the package listing and packages synchronously. + +\(fn)" t nil) + +(autoload 'skewer-bower-load "skewer-bower" "\ +Dynamically load a library from bower into the current page. + +\(fn PACKAGE &optional VERSION)" t nil) + +;;;*** + +;;;### (autoloads nil "skewer-css" "skewer-css.el" (21836 47293 0 +;;;;;; 0)) +;;; Generated autoloads from skewer-css.el + +(autoload 'skewer-css-mode "skewer-css" "\ +Minor mode for interactively loading new CSS rules. + +\(fn &optional ARG)" t nil) + +;;;*** + +;;;### (autoloads nil "skewer-html" "skewer-html.el" (21836 47293 +;;;;;; 0 0)) +;;; Generated autoloads from skewer-html.el + +(autoload 'skewer-html-mode "skewer-html" "\ +Minor mode for interactively loading new HTML. + +\(fn &optional ARG)" t nil) + +;;;*** + +;;;### (autoloads nil "skewer-mode" "skewer-mode.el" (21836 47293 +;;;;;; 0 0)) +;;; Generated autoloads from skewer-mode.el + +(autoload 'list-skewer-clients "skewer-mode" "\ +List the attached browsers in a buffer. + +\(fn)" t nil) + +(autoload 'skewer-mode "skewer-mode" "\ +Minor mode for interacting with a browser. + +\(fn &optional ARG)" t nil) + +(autoload 'run-skewer "skewer-mode" "\ +Attach a browser to Emacs for a skewer JavaScript REPL. Uses +`browse-url' to launch a browser. + +\(fn)" t nil) + +(autoload 'skewer-run-phantomjs "skewer-mode" "\ +Connect an inferior PhantomJS process to Skewer, returning the process. + +\(fn)" t nil) + +;;;*** + +;;;### (autoloads nil "skewer-repl" "skewer-repl.el" (21836 47293 +;;;;;; 0 0)) +;;; Generated autoloads from skewer-repl.el + +(autoload 'skewer-repl--response-hook "skewer-repl" "\ +Catches all browser messages logging some to the REPL. + +\(fn RESPONSE)" nil nil) + +(autoload 'skewer-repl "skewer-repl" "\ +Start a JavaScript REPL to be evaluated in the visiting browser. + +\(fn)" t nil) + +(eval-after-load 'skewer-mode '(progn (add-hook 'skewer-response-hook #'skewer-repl--response-hook) (add-hook 'skewer-repl-mode-hook #'skewer-repl-mode-compilation-shell-hook) (define-key skewer-mode-map (kbd "C-c C-z") #'skewer-repl))) + +;;;*** + +;;;### (autoloads nil "skewer-setup" "skewer-setup.el" (21836 47293 +;;;;;; 0 0)) +;;; Generated autoloads from skewer-setup.el + +(autoload 'skewer-setup "skewer-setup" "\ +Fully integrate Skewer into js2-mode, css-mode, and html-mode buffers. + +\(fn)" nil nil) + +;;;*** + +;;;### (autoloads nil nil ("cache-table.el" "skewer-mode-pkg.el") +;;;;;; (21836 47293 128442 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; skewer-mode-autoloads.el ends here diff --git a/emacs.d/elpa/skewer-mode-20150422.1818/skewer-mode-pkg.el b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-mode-pkg.el new file mode 100644 index 0000000..580593b --- /dev/null +++ b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-mode-pkg.el @@ -0,0 +1,7 @@ +(define-package "skewer-mode" "20150422.1818" "live browser JavaScript, CSS, and HTML interaction" + '((simple-httpd "1.4.0") + (js2-mode "20090723") + (emacs "24"))) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs.d/elpa/skewer-mode-20150422.1818/skewer-mode.el b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-mode.el new file mode 100644 index 0000000..69ffc74 --- /dev/null +++ b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-mode.el @@ -0,0 +1,599 @@ +;;; skewer-mode.el --- live browser JavaScript, CSS, and HTML interaction -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;; Author: Christopher Wellons +;; URL: https://github.com/skeeto/skewer-mode + +;;; Commentary: + +;; Quick start (without package.el): + +;; 1. Put this directory in your `load-path' +;; 2. Load skewer-mode.el +;; 3. M-x `run-skewer' to attach a browser to Emacs +;; 4. From a `js2-mode' buffer with `skewer-mode' minor mode enabled, +;; send forms to the browser to evaluate + +;; The function `skewer-setup' can be used to configure all of mode +;; hooks (previously this was the default). This can also be done +;; manually like so, + +;; (add-hook 'js2-mode-hook 'skewer-mode) +;; (add-hook 'css-mode-hook 'skewer-css-mode) +;; (add-hook 'html-mode-hook 'skewer-html-mode) + +;; The keybindings for evaluating expressions in the browser are just +;; like the Lisp modes. These are provided by the minor mode +;; `skewer-mode'. + +;; * C-x C-e -- `skewer-eval-last-expression' +;; * C-M-x -- `skewer-eval-defun' +;; * C-c C-k -- `skewer-load-buffer' + +;; The result of the expression is echoed in the minibuffer. + +;; Additionally, `css-mode' and `html-mode' get a similar set of +;; bindings for modifying the CSS rules and updating HTML on the +;; current page. + +;; Note: `run-skewer' uses `browse-url' to launch the browser. This +;; may require further setup depending on your operating system and +;; personal preferences. + +;; Multiple browsers and browser tabs can be attached to Emacs at +;; once. JavaScript forms are sent to all attached clients +;; simultaneously, and each will echo back the result +;; individually. Use `list-skewer-clients' to see a list of all +;; currently attached clients. + +;; Sometimes Skewer's long polls from the browser will timeout after a +;; number of hours of inactivity. If you find the browser disconnected +;; from Emacs for any reason, use the browser's console to call +;; skewer() to reconnect. This avoids a page reload, which would lose +;; any fragile browser state you might care about. + +;; To skewer your own document rather than the provided blank page, + +;; 1. Load the dependencies +;; 2. Load skewer-mode.el +;; 3. Start the HTTP server (`httpd-start') +;; 4. Include "http://localhost:8080/skewer" as a script +;; (see `example.html' and check your `httpd-port') +;; 5. Visit the document from your browser + +;; Skewer fully supports CORS, so the document need not be hosted by +;; Emacs itself. A Greasemonkey userscript and a bookmarklet are +;; provided for injecting Skewer into any arbitrary page you're +;; visiting without needing to modify the page on the host. + +;; With skewer-repl.el loaded, a REPL into the browser can be created +;; with M-x `skewer-repl', or C-c C-z. This should work like a console +;; within the browser. Messages can be logged to this REPL with +;; skewer.log() (just like console.log()). + +;; Extending Skewer: + +;; Skewer is flexible and open to extension. The REPL and the CSS and +;; HTML minor modes are a partial examples of this. You can extend +;; skewer.js with your own request handlers and talk to them from +;; Emacs using `skewer-eval' (or `skewer-eval-synchronously') with +;; your own custom :type. The :type string chooses the dispatch +;; function under the skewer.fn object. To inject your own JavaScript +;; into skewer.js, use `skewer-js-hook'. + +;; You can also catch messages sent from the browser not in response +;; to an explicit request. Use `skewer-response-hook' to see all +;; incoming objects. + +;;; History: + +;; Version 1.6.2: fixes +;; * skewer.log() takes multiple arguments +;; * comint and encoding fixes +;; Version 1.6.1: fixes +;; * Add `skewer-css-clear-all' +;; * Better IE8 compatibility +;; * User interface tweaks +;; Version 1.6.0: fixes +;; * Bring up to speed with Emacs 24.3 +;; * Switch to cl-lib from cl +;; Version 1.5.3: features +;; * Add `skewer-run-phantomjs' +;; Version 1.5.2: small cleanup +;; * Add `skewer-apply' and `skewer-funall' +;; * Improved safeStringify +;; Version 1.5.1: features +;; * No more automatic hook setup (see `skewer-setup') +;; * Support for HTML interaction +;; * Support for loading Bower packages +;; * Drop jQuery dependency +;; * Many small improvements +;; Version 1.4: features +;; * Full CSS interaction +;; * Greasemonkey userscript for injection +;; * Full, working CORS support +;; * Better browser presence detection +;; Version 1.3: features and fixes +;; * Full offline support +;; * No more callback registering +;; * Fix 64-bit support +;; * Two new hooks for improved extension support +;; * More uniform keybindings with other interactive modes +;; Version 1.2: features +;; * Add a skewer-eval-print-last-expression +;; * Display evaluation time when it's long +;; * Flash the region on eval +;; * Improve JS stringification +;; Version 1.1: features and fixes +;; * Added `list-skewer-clients' +;; * Reduce the number of HTTP requests needed +;; * Fix stringification issues +;; Version 1.0: initial release + +;;; Code: + +(require 'cl-lib) +(require 'json) +(require 'url-util) +(require 'simple-httpd) +(require 'js2-mode) +(require 'cache-table) + +(defgroup skewer nil + "Live browser JavaScript interaction." + :group 'languages) + +(defvar skewer-mode-map + (let ((map (make-sparse-keymap))) + (prog1 map + (define-key map (kbd "C-x C-e") 'skewer-eval-last-expression) + (define-key map (kbd "C-M-x") 'skewer-eval-defun) + (define-key map (kbd "C-c C-k") 'skewer-load-buffer))) + "Keymap for skewer-mode.") + +(defvar skewer-data-root (file-name-directory load-file-name) + "Location of data files needed by impatient-mode.") + +(defvar skewer-js-hook () + "Hook to run when skewer.js is being served to the browser. + +When hook functions are called, the current buffer is the buffer +to be served to the client (a defservlet), with skewer.js script +already inserted. This is the chance for other packages to insert +their own JavaScript to extend skewer in the browser, such as +adding a new type handler.") + +(defvar skewer-response-hook () + "Hook to run when a response arrives from the browser. Used for +catching messages from the browser with no associated +callback. The response object is passed to the hook function.") + +(defvar skewer-timeout 3600 + "Maximum time to wait on the browser to respond, in seconds.") + +(defvar skewer-clients () + "Browsers awaiting JavaScript snippets.") + +(defvar skewer-callbacks (cache-table-create skewer-timeout :test 'equal) + "Maps evaluation IDs to local callbacks.") + +(defvar skewer-queue () + "Queued messages for the browser.") + +(defvar skewer--last-timestamp 0 + "Timestamp of the last browser response. Use +`skewer-last-seen-seconds' to access this.") + +(cl-defstruct skewer-client + "A client connection awaiting a response." + proc agent) + +(defun skewer-process-queue () + "Send all queued messages to clients." + (when (and skewer-queue skewer-clients) + (let ((message (pop skewer-queue)) + (sent nil)) + (while skewer-clients + (ignore-errors + (progn + (let ((proc (skewer-client-proc (pop skewer-clients)))) + (with-temp-buffer + (insert (json-encode message)) + (httpd-send-header proc "text/plain" 200 + :Cache-Control "no-cache" + :Access-Control-Allow-Origin "*"))) + (setq skewer--last-timestamp (float-time)) + (setq sent t)))) + (if (not sent) (push message skewer-queue))) + (skewer-process-queue))) + +(defun skewer-clients-tabulate () + "Prepare client list for tabulated-list-mode." + (cl-loop for client in skewer-clients collect + (let ((proc (skewer-client-proc client)) + (agent (skewer-client-agent client))) + (cl-destructuring-bind (host port) (process-contact proc) + `(,client [,host ,(format "%d" port) ,agent]))))) + +(define-derived-mode skewer-clients-mode tabulated-list-mode "skewer-clients" + "Mode for listing browsers attached to Emacs for skewer-mode." + (setq tabulated-list-format [("Host" 12 t) + ("Port" 5 t) + ("User Agent" 0 t)]) + (setq tabulated-list-entries #'skewer-clients-tabulate) + (tabulated-list-init-header)) + +(define-key skewer-clients-mode-map (kbd "g") + (lambda () + (interactive) + (skewer-ping) + (revert-buffer))) + +(defun skewer-update-list-buffer () + "Revert the client list, due to an update." + (save-window-excursion + (let ((list-buffer (get-buffer "*skewer-clients*"))) + (when list-buffer + (with-current-buffer list-buffer + (revert-buffer)))))) + +;;;###autoload +(defun list-skewer-clients () + "List the attached browsers in a buffer." + (interactive) + (pop-to-buffer (get-buffer-create "*skewer-clients*")) + (skewer-clients-mode) + (tabulated-list-print)) + +(defun skewer-queue-client (proc req) + "Add a client to the queue, given the HTTP header." + (let ((agent (cl-second (assoc "User-Agent" req)))) + (push (make-skewer-client :proc proc :agent agent) skewer-clients)) + (skewer-update-list-buffer) + (skewer-process-queue)) + +;; Servlets + +(defservlet skewer text/javascript () + (insert-file-contents (expand-file-name "skewer.js" skewer-data-root)) + (goto-char (point-max)) + (run-hooks 'skewer-js-hook)) + +(defun httpd/skewer/get (proc _path _query req &rest _args) + (skewer-queue-client proc req)) + +(defun httpd/skewer/post (proc _path _query req &rest _args) + (let* ((result (json-read-from-string (cadr (assoc "Content" req)))) + (id (cdr (assoc 'id result))) + (callback (cache-table-get id skewer-callbacks))) + (setq skewer--last-timestamp (float-time)) + (when callback + (funcall callback result)) + (if id + (skewer-queue-client proc req) + (with-temp-buffer + (httpd-send-header proc "text/plain" 200 + :Access-Control-Allow-Origin "*"))) + (dolist (hook skewer-response-hook) + (funcall hook result)))) + +(defservlet skewer/demo text/html () + (insert-file-contents (expand-file-name "example.html" skewer-data-root))) + +;; Minibuffer display + +(defun skewer-success-p (result) + "Return T if result was a success." + (equal "success" (cdr (assoc 'status result)))) + +(define-derived-mode skewer-error-mode special-mode "skewer-error" + :group 'skewer + "Mode for displaying JavaScript errors returned by skewer-mode." + (setq truncate-lines t)) + +(defface skewer-error-face + '((((class color) (background light)) + :foreground "red" :underline t) + (((class color) (background dark)) + :foreground "red" :underline t)) + "Face for JavaScript errors." + :group 'skewer) + +(defun skewer--error (string) + "Return STRING propertized as an error message." + (propertize (or string "") 'font-lock-face 'skewer-error-face)) + +(defun skewer-post-minibuffer (result) + "Report results in the minibuffer or the error buffer." + (if (skewer-success-p result) + (let ((value (cdr (assoc 'value result))) + (time (cdr (assoc 'time result)))) + (if (and time (> time 1.0)) + (message "%s (%.3f seconds)" value time) + (message "%s" value))) + (with-current-buffer (pop-to-buffer (get-buffer-create "*skewer-error*")) + (let ((inhibit-read-only t) + (error (cdr (assoc 'error result)))) + (erase-buffer) + (skewer-error-mode) + (insert (skewer--error (cdr (assoc 'name error))) ": ") + (insert (or (cdr (assoc 'message error)) "") "\n\n") + (insert (or (cdr (assoc 'stack error)) "") "\n\n") + (insert (format "Expression: %s\n\n" + (if (cdr (assoc 'strict result)) "(strict)" "")) + (cdr (assoc 'eval error))) + (goto-char (point-min)))))) + +;; Evaluation functions + +(cl-defun skewer-eval (string &optional callback + &key verbose strict (type "eval") extra) + "Evaluate STRING in the waiting browsers, giving the result to CALLBACK. + +:VERBOSE -- if T, the return will try to be JSON encoded +:STRICT -- if T, expression is evaluated with 'use strict' +:TYPE -- chooses the JavaScript handler (default: eval) +:EXTRA -- additional alist keys to append to the request object" + (let* ((id (format "%x" (random most-positive-fixnum))) + (request `((type . ,type) + (eval . ,string) + (id . ,id) + (verbose . ,verbose) + (strict . ,strict) + ,@extra))) + (prog1 request + (setf (cache-table-get id skewer-callbacks) callback) + (setq skewer-queue (append skewer-queue (list request))) + (skewer-process-queue)))) + +(defun skewer-eval-synchronously (string &rest args) + "Just like `skewer-eval' but synchronously, so don't provide a +callback. Use with caution." + (let ((result nil)) + (apply #'skewer-eval string (lambda (v) (setq result v)) args) + (cl-loop until result + do (accept-process-output nil 0.01) + finally (return result)))) + +(defun skewer-apply (function args) + "Synchronously apply FUNCTION in the browser with the supplied +arguments, returning the result. All ARGS must be printable by +`json-encode'. For example, + + (skewer-apply \"Math.atan2\" '(1 -2)) ; => 2.677945044588987 + +Uncaught exceptions propagate to Emacs as an error." + (let ((specials '(("undefined" . nil) + ("NaN" . 0.0e+NaN) + ("Infinity" . 1.0e+INF) + ("-Infinity" . -1.0e+INF)))) + (let* ((expr (concat function "(" (mapconcat #'json-encode args ", ") ")")) + (result (skewer-eval-synchronously expr :verbose t)) + (value (cdr (assoc 'value result)))) + (if (skewer-success-p result) + (if (assoc value specials) + (cdr (assoc value specials)) + (condition-case _ + (json-read-from-string value) + (json-readtable-error value))) + (signal 'javascript + (list (cdr (assoc 'message (cdr (assoc'error result)))))))))) + +(defun skewer-funcall (function &rest args) + "Synchronously call FUNCTION with the supplied ARGS. All ARGS +must be printable by `json-read-from-string. For example, + + (skewer-funcall \"Math.sin\" 0.5) ; => 0.479425538604203 + +Uncaught exceptions propagate to Emacs as an error." + (skewer-apply function args)) + +(defun skewer--save-point (f &rest args) + "Return a function that calls F with point at the current point." + (let ((saved-point (point))) + (lambda (&rest more) + (save-excursion + (goto-char saved-point) + (apply f (append args more)))))) + +(defun skewer-ping () + "Ping the browser to test that it's still alive." + (unless (null skewer-clients) ; don't queue pings + (skewer-eval (prin1-to-string (float-time)) nil :type "ping"))) + +(defun skewer-last-seen-seconds () + "Return the number of seconds since the browser was last seen." + (skewer-ping) ; make sure it's still alive next request + (- (float-time) skewer--last-timestamp)) + +(defun skewer-mode-strict-p () + "Return T if buffer contents indicates strict mode." + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (js2-forward-sws) + (forward-char 1) + (let* ((stricts '("\"use strict\"" "'use strict'")) + (node (js2-node-at-point)) + (code (buffer-substring-no-properties (js2-node-abs-pos node) + (js2-node-abs-end node)))) + (and (member code stricts) t))))) + +(defun skewer-flash-region (start end &optional timeout) + "Temporarily highlight region from START to END." + (let ((overlay (make-overlay start end))) + (overlay-put overlay 'face 'secondary-selection) + (run-with-timer (or timeout 0.2) nil 'delete-overlay overlay))) + +(defun skewer-get-last-expression () + "Return the JavaScript expression before the point as a +list: (string start end)." + (save-excursion + (js2-backward-sws) + (backward-char) + (let ((node (js2-node-at-point nil t))) + (when (eq js2-FUNCTION (js2-node-type (js2-node-parent node))) + (setq node (js2-node-parent node))) + (when (js2-ast-root-p node) + (error "no expression found")) + (let ((start (js2-node-abs-pos node)) + (end (js2-node-abs-end node))) + (list (buffer-substring-no-properties start end) start end))))) + +(defun skewer-eval-last-expression (&optional prefix) + "Evaluate the JavaScript expression before the point in the +waiting browser. If invoked with a prefix argument, insert the +result into the current buffer." + (interactive "P") + (if prefix + (skewer-eval-print-last-expression) + (if js2-mode-buffer-dirty-p + (js2-mode-wait-for-parse + (skewer--save-point #'skewer-eval-last-expression)) + (cl-destructuring-bind (string start end) (skewer-get-last-expression) + (skewer-flash-region start end) + (skewer-eval string #'skewer-post-minibuffer))))) + +(defun skewer-get-defun () + "Return the toplevel JavaScript expression around the point as +a list: (string start end)." + (save-excursion + (js2-backward-sws) + (backward-char) + (let ((node (js2-node-at-point nil t))) + (when (js2-ast-root-p node) + (error "no expression found")) + (while (and (js2-node-parent node) + (not (js2-ast-root-p (js2-node-parent node)))) + (setf node (js2-node-parent node))) + (let ((start (js2-node-abs-pos node)) + (end (js2-node-abs-end node))) + (list (buffer-substring-no-properties start end) start end))))) + +(defun skewer-eval-defun () + "Evaluate the JavaScript expression before the point in the +waiting browser." + (interactive) + (if js2-mode-buffer-dirty-p + (js2-mode-wait-for-parse (skewer--save-point #'skewer-eval-defun)) + (cl-destructuring-bind (string start end) (skewer-get-defun) + (skewer-flash-region start end) + (skewer-eval string #'skewer-post-minibuffer)))) + +;; Print last expression + +(defvar skewer-eval-print-map (cache-table-create skewer-timeout :test 'equal) + "A mapping of evaluation IDs to insertion points.") + +(defun skewer-post-print (result) + "Insert the result after its source expression." + (if (not (skewer-success-p result)) + (skewer-post-minibuffer result) + (let* ((id (cdr (assoc 'id result))) + (pos (cache-table-get id skewer-eval-print-map))) + (when pos + (with-current-buffer (car pos) + (goto-char (cdr pos)) + (insert (cdr (assoc 'value result)) "\n")))))) + +(defun skewer-eval-print-last-expression () + "Evaluate the JavaScript expression before the point in the +waiting browser and insert the result in the buffer at point." + (interactive) + (if js2-mode-buffer-dirty-p + (js2-mode-wait-for-parse + (skewer--save-point #'skewer-eval-print-last-expression)) + (cl-destructuring-bind (string start end) (skewer-get-defun) + (skewer-flash-region start end) + (insert "\n") + (let* ((request (skewer-eval string #'skewer-post-print :verbose t)) + (id (cdr (assoc 'id request))) + (pos (cons (current-buffer) (point)))) + (setf (cache-table-get id skewer-eval-print-map) pos))))) + +;; Script loading + +(defvar skewer-hosted-scripts (cache-table-create skewer-timeout) + "Map of hosted scripts to IDs.") + +(defun skewer-host-script (string) + "Host script STRING from the script servlet, returning the script ID." + (let ((id (random most-positive-fixnum))) + (prog1 id + (setf (cache-table-get id skewer-hosted-scripts) string)))) + +(defun skewer-load-buffer () + "Load the entire current buffer into the browser. A snapshot of +the buffer is hosted so that browsers visiting late won't see an +inconsistent buffer." + (interactive) + (let ((id (skewer-host-script (buffer-string))) + (buffer-name (buffer-name))) + (skewer-eval (format "/skewer/script/%d/%s" + id (url-hexify-string buffer-name)) + (lambda (_) (message "%s loaded" buffer-name)) + :type "script"))) + +(defservlet skewer/script text/javascript (path) + (let ((id (string-to-number (nth 3 (split-string path "/"))))) + (insert (cache-table-get id skewer-hosted-scripts "")))) + +;; Define the minor mode + +;;;###autoload +(define-minor-mode skewer-mode + "Minor mode for interacting with a browser." + :lighter " skewer" + :keymap skewer-mode-map + :group 'skewer) + +;;;###autoload +(defun run-skewer () + "Attach a browser to Emacs for a skewer JavaScript REPL. Uses +`browse-url' to launch a browser." + (interactive) + (httpd-start) + (browse-url (format "http://127.0.0.1:%d/skewer/demo" httpd-port))) + +;; PhantomJS + +(defvar phantomjs-program-name "/usr/bin/phantomjs" + "Path to the phantomjs executable.") + +(defvar skewer-phantomjs-processes () + "List of phantomjs processes connected to Skewer.") + +(defun skewer-phantomjs-sentinel (proc event) + "Cleanup after phantomjs exits." + (when (cl-some (lambda (s) (string-match-p s event)) + '("finished" "abnormal" "killed")) + (delete-file (process-get proc 'tempfile)))) + +;;;###autoload +(defun skewer-run-phantomjs () + "Connect an inferior PhantomJS process to Skewer, returning the process." + (interactive) + (httpd-start) + (let ((script (make-temp-file "phantomjs-")) + (url (format "http://0:%d/skewer/demo" httpd-port))) + (with-temp-buffer + (insert (format "require('webpage').create().open('%s')" url)) + (write-region nil nil script nil 0) + (let ((proc (start-process "phantomjs" nil + phantomjs-program-name script))) + (prog1 proc + (push proc skewer-phantomjs-processes) + (process-put proc 'tempfile script) + (set-process-sentinel proc 'skewer-phantomjs-sentinel)))))) + +(defun skewer-phantomjs-kill () + "Kill all inferior phantomjs processes connected to Skewer." + (interactive) + (mapc #'delete-process skewer-phantomjs-processes) + (setf skewer-phantomjs-processes nil)) + +(provide 'skewer-mode) + +;;; skewer-mode.el ends here diff --git a/emacs.d/elpa/skewer-mode-20150422.1818/skewer-repl.el b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-repl.el new file mode 100644 index 0000000..b2d786f --- /dev/null +++ b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-repl.el @@ -0,0 +1,156 @@ +;;; skewer-repl.el --- create a REPL in a visiting browser -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;;; Commentary: + +;; This is largely based on of IELM's code. Run `skewer-repl' to +;; switch to the REPL buffer and evaluate code. Use +;; `skewer-repl-toggle-strict-mode' to turn strict mode on and off. + +;; If `compilation-search-path' is set up properly, along with +;; `skewer-path-strip-level', asynchronous errors will provide +;; clickable error messages that will take you to the source file of +;; the error. This is done using `compilation-shell-minor-mode'. + +;;; Code: + +(require 'comint) +(require 'compile) +(require 'skewer-mode) + +(defcustom skewer-repl-strict-p nil + "When non-NIL, all REPL evaluations are done in strict mode." + :type 'boolean + :group 'skewer) + +(defcustom skewer-repl-prompt "js> " + "Prompt string for JavaScript REPL." + :type 'string + :group 'skewer) + +(defvar skewer-repl-welcome + (propertize "*** Welcome to Skewer ***\n" + 'font-lock-face 'font-lock-comment-face) + "Header line to show at the top of the REPL buffer. Hack +notice: this allows log messages to appear before anything is +evaluated because it provides insertable space at the top of the +buffer.") + +(defun skewer-repl-process () + "Return the process for the skewer REPL." + (get-buffer-process (current-buffer))) + +(defface skewer-repl-log-face + '((((class color) (background light)) + :foreground "#77F") + (((class color) (background dark)) + :foreground "#77F")) + "Face for skewer.log() messages." + :group 'skewer) + +(define-derived-mode skewer-repl-mode comint-mode "js-REPL" + "Provide a REPL into the visiting browser." + :group 'skewer + :syntax-table emacs-lisp-mode-syntax-table + (setq comint-prompt-regexp (concat "^" (regexp-quote skewer-repl-prompt)) + comint-input-sender 'skewer-input-sender + comint-process-echoes nil) + (unless (comint-check-proc (current-buffer)) + (insert skewer-repl-welcome) + (start-process "skewer-repl" (current-buffer) nil) + (set-process-query-on-exit-flag (skewer-repl-process) nil) + (goto-char (point-max)) + (set (make-local-variable 'comint-inhibit-carriage-motion) t) + (comint-output-filter (skewer-repl-process) skewer-repl-prompt) + (set-process-filter (skewer-repl-process) 'comint-output-filter))) + +(defun skewer-repl-toggle-strict-mode () + "Toggle strict mode for expressions evaluated by the REPL." + (interactive) + (setq skewer-repl-strict-p (not skewer-repl-strict-p)) + (message "REPL strict mode %s" + (if skewer-repl-strict-p "enabled" "disabled"))) + +(defun skewer-input-sender (_ input) + "REPL comint handler." + (skewer-eval input 'skewer-post-repl + :verbose t :strict skewer-repl-strict-p)) + +(defun skewer-post-repl (result) + "Callback for reporting results in the REPL." + (let ((buffer (get-buffer "*skewer-repl*")) + (output (cdr (assoc 'value result)))) + (when buffer + (with-current-buffer buffer + (comint-output-filter (skewer-repl-process) + (concat output "\n" skewer-repl-prompt)))))) + +(defvar skewer-repl-types + '(("log" . skewer-repl-log-face) + ("error" . skewer-error-face)) + "Faces to use for different types of log messages.") + +(defun skewer-log-filename (log) + "Create a log string for the source file in LOG if present." + (let ((name (cdr (assoc 'filename log))) + (line (cdr (assoc 'line log))) + (column (cdr (assoc 'column log)))) + (when name + (concat (format "\n at %s:%s" name line) + (if column (format ":%s" column)))))) + +(defun skewer-post-log (log) + "Callback for logging messages to the REPL." + (let* ((buffer (get-buffer "*skewer-repl*")) + (face (cdr (assoc (cdr (assoc 'type log)) skewer-repl-types))) + (value (or (cdr (assoc 'value log)) "")) + (output (propertize value 'font-lock-face face))) + (when buffer + (with-current-buffer buffer + (save-excursion + (goto-char (point-max)) + (forward-line 0) + (backward-char) + (insert (concat "\n" output (skewer-log-filename log)))))))) + +(defcustom skewer-path-strip-level 1 + "Number of folders which will be stripped from url when discovering paths. +Use this to limit path matching to files in your filesystem. You +may want to add some folders to `compilation-search-path', so +matched files can be found." + :type 'number + :group 'skewer) + +(defun skewer-repl-mode-compilation-shell-hook () + "Setup compilation shell minor mode for highlighting files" + (let ((error-re (format "^[ ]*at https?://[^/]+/\\(?:[^/]+/\\)\\{%d\\}\\([^:?#]+\\)\\(?:[?#][^:]*\\)?:\\([[:digit:]]+\\)\\(?::\\([[:digit:]]+\\)\\)?$" skewer-path-strip-level))) + (setq-local compilation-error-regexp-alist `((,error-re 1 2 3 2)))) + (compilation-shell-minor-mode 1)) + +;;;###autoload +(defun skewer-repl--response-hook (response) + "Catches all browser messages logging some to the REPL." + (let ((type (cdr (assoc 'type response)))) + (when (member type '("log" "error")) + (skewer-post-log response)))) + +;;;###autoload +(defun skewer-repl () + "Start a JavaScript REPL to be evaluated in the visiting browser." + (interactive) + (when (not (get-buffer "*skewer-repl*")) + (with-current-buffer (get-buffer-create "*skewer-repl*") + (skewer-repl-mode))) + (pop-to-buffer (get-buffer "*skewer-repl*"))) + +;;;###autoload +(eval-after-load 'skewer-mode + '(progn + (add-hook 'skewer-response-hook #'skewer-repl--response-hook) + (add-hook 'skewer-repl-mode-hook #'skewer-repl-mode-compilation-shell-hook) + (define-key skewer-mode-map (kbd "C-c C-z") #'skewer-repl))) + +(provide 'skewer-repl) + +;;; skewer-repl.el ends here diff --git a/emacs.d/elpa/skewer-mode-20150422.1818/skewer-setup.el b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-setup.el new file mode 100644 index 0000000..77aea1b --- /dev/null +++ b/emacs.d/elpa/skewer-mode-20150422.1818/skewer-setup.el @@ -0,0 +1,21 @@ +;;; skewer-setup.el --- automatic setup for the Skewer minor modes -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;;; Commentary: + +;; This exists as a separate file so that Skewer need not be fully +;; loaded just to use this setup function. + +;;; Code: + +;;;###autoload +(defun skewer-setup () + "Fully integrate Skewer into js2-mode, css-mode, and html-mode buffers." + (add-hook 'js2-mode-hook 'skewer-mode) + (add-hook 'css-mode-hook 'skewer-css-mode) + (add-hook 'html-mode-hook 'skewer-html-mode)) + +(provide 'skewer-setup) + +;;; skewer-setup.el ends here diff --git a/emacs.d/elpa/skewer-mode-20150422.1818/skewer.js b/emacs.d/elpa/skewer-mode-20150422.1818/skewer.js new file mode 100644 index 0000000..07b9c0b --- /dev/null +++ b/emacs.d/elpa/skewer-mode-20150422.1818/skewer.js @@ -0,0 +1,413 @@ +/** + * @fileOverview Live browser interaction with Emacs + * @version 1.4 + */ + +/** + * Connects to Emacs and waits for a request. After handling the + * request it sends back the results and queues itself for another + * request. + * @namespace Holds all of Skewer's functionality. + */ +function skewer() { + function callback(request) { + var result = skewer.fn[request.type](request); + if (result) { + result = skewer.extend({ + id: request.id, + type: request.type, + status: 'success', + value: '' + }, result); + skewer.postJSON(skewer.host + "/skewer/post", result, callback); + } else { + skewer.getJSON(skewer.host + "/skewer/get", callback); + } + }; + skewer.getJSON(skewer.host + "/skewer/get", callback); +} + +/** + * Get a JSON-encoded object from a server. + * @param {String} url The location of the remote server + * @param {Function} [callback] The callback to receive a response object + */ +skewer.getJSON = function(url, callback) { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + if (xhr.readyState === 4 && xhr.status === 200) { + callback(JSON.parse(xhr.responseText)); + } + }; + xhr.open('GET', url, true); + xhr.send(); +}; + +/** + * Send a JSON-encoded object to a server. + * @param {String} url The location of the remote server + * @param {Object} object The object to transmit to the server + * @param {Function} [callback] The callback to receive a response object + */ +skewer.postJSON = function(url, object, callback) { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + if (callback && xhr.readyState === 4 && xhr.status === 200) { + callback(JSON.parse(xhr.responseText)); + } + }; + xhr.open('POST', url, true); + xhr.setRequestHeader("Content-Type", "text/plain"); // CORS + xhr.send(JSON.stringify(object)); +}; + +/** + * Add the properties other objects to a target object (jQuery.extend). + * @param {Object} target The object to receive new properties + * @param {...Object} objects Source objects for properties + * @returns The target object + */ +skewer.extend = function(target) { + for (var i = 1; i < arguments.length; i++) { + var object = arguments[i]; + for (var key in object) { + if (object.hasOwnProperty(key)) { + target[key] = object[key]; + } + } + } + return target; +}; + +/** + * Globally evaluate an expression and return the result. This + * only works when the implementation's indirect eval performs + * a global eval. If not, there's no alternative, since a return value + * is essential. + * + * @see http://perfectionkills.com/global-eval-what-are-the-options/ + * + * @param expression A string containing an expression to evaluate + * @returns The result of the evaluation + */ +skewer.globalEval = (function() { + var eval0 = (function(original, Object) { + try { + return [eval][0]('Object') === original; + } catch (e) { + return false; + } + }(Object, false)); + if (eval0) { + return function(expression) { + return [eval][0](expression); + }; + } else { + return function(expression) { // Safari + return eval.call(window, expression); + }; + } +}()); + +/** + * Same as Date.now(), supplied for pre-ES5 JS (<=IE8). + * @returns {number} The epoch time in milliseconds + */ +skewer.now = function() { + return new Date().valueOf(); +}; + +/** + * Handlers accept a request object from Emacs and return either a + * logical false (no response) or an object to return to Emacs. + * @namespace Request handlers. + */ +skewer.fn = {}; + +/** + * Handles an code evaluation request from Emacs. + * @param request The request object sent by Emacs + * @returns The result object to be returned to Emacs + */ +skewer.fn.eval = function(request) { + var result = { + strict: request.strict + }; + var start = skewer.now(); + try { + var prefix = request.strict ? '"use strict";\n' : ""; + var value = skewer.globalEval(prefix + request.eval); + result.value = skewer.safeStringify(value, request.verbose); + } catch (error) { + result = skewer.errorResult(error, result, request); + } + result.time = (skewer.now() - start) / 1000; + return result; +}; + +/** + * Load a hosted script named by the request. + * @param request The request object sent by Emacs + * @returns The result object to be returned to Emacs + */ +skewer.fn.script = function(request) { + var script = document.createElement('script'); + script.src = skewer.host + request.eval; + document.body.appendChild(script); + return {value: JSON.stringify(request.eval)}; +}; + +/** + * A keep-alive and connecton testing handler. + * @param request The request object sent by Emacs + * @returns The result object to be returned to Emacs + */ +skewer.fn.ping = function(request) { + return { + type: 'pong', + date: skewer.now() / 1000, + value: request.eval + }; +}; + +/** + * Establish a new stylesheet with the provided value. + */ +skewer.fn.css = function(request) { + var style = document.createElement('style'); + style.type = 'text/css'; + style.className = 'skewer'; + if (style.styleSheet) { // < IE9 + style.styleSheet.cssText = request.eval; + } else { + style.appendChild(document.createTextNode(request.eval)); + } + document.body.appendChild(style); + return {}; +}; + +/** + * Remove all of Skewer's style tags from the document. + */ +skewer.fn.cssClearAll = function(request) { + var styles = document.body.querySelectorAll('style.skewer'); + for (var i = 0; i < styles.length; i++) { + styles[i].parentNode.removeChild(styles[i]); + } + return {}; +}; + +/** + * HTML evaluator, appends or replaces a selection with given HTML. + */ +skewer.fn.html = function(request) { + function buildSelector(ancestry) { + return ancestry.map(function(tag) { + return tag[0] + ':nth-of-type(' + tag[1] + ')'; + }).join(' > '); + } + function query(ancestry) { + return document.querySelector(buildSelector(ancestry)); + } + function htmlToNode(html) { + var wrapper = document.createElement('div'); + wrapper.innerHTML = html; + return wrapper.firstChild; + } + + var target = query(request.ancestry); + + if (target == null) { + /* Determine missing part of the ancestry. */ + var path = request.ancestry.slice(0); // copy + var missing = []; + while (query(path) == null) { + missing.push(path.pop()); + } + + /* Build up the missing elements. */ + target = query(path); + while (missing.length > 0) { + var tag = missing.pop(), + name = tag[0], + nth = tag[1]; + var empty = null; + var count = target.querySelectorAll(name).length; + for (; count < nth; count++) { + empty = document.createElement(tag[0]); + target.appendChild(empty); + } + target = empty; + } + } + + target.parentNode.replaceChild(htmlToNode(request.eval), target); + return {}; +}; + +/** + * Fetch the HTML contents of selector. + */ +skewer.fn.fetchselector = function(request) { + var element = document.querySelector(request.eval); + return { value: element.innerHTML }; +}; + +/** + * Host of the skewer script (CORS support). + * @type string + */ +(function() { + var script = document.querySelector('script[src$="/skewer"]'); + if (script) { + skewer.host = script.src.match(/\w+:\/\/[^/]+/)[0]; + } else { + skewer.host = ''; // default to the current host + } +}()); + +/** + * Stringify a potentially circular object without throwing an exception. + * @param object The object to be printed. + * @param {boolean} verbose Enable more verbose output. + * @returns {string} The printed object. + */ +skewer.safeStringify = function (object, verbose) { + "use strict"; + var circular = "#"; + var seen = []; + + var stringify = function(obj) { + if (obj === true) { + return "true"; + } else if (obj === false) { + return "false"; + } else if (obj === undefined) { + return "undefined"; + } else if (obj === null) { + return "null"; + } else if (typeof obj === "number") { + return obj.toString(); + } else if (obj instanceof Array) { + if (seen.indexOf(obj) >= 0) { + return circular; + } else { + seen.push(obj); + return "[" + obj.map(function(e) { + return stringify(e); + }).join(", ") + "]"; + } + } else if (typeof obj === "string") { + return JSON.stringify(obj); + } else if (window.Node != null && obj instanceof Node) { + return obj.toString(); // DOM elements can't stringify + } else if (typeof obj === "function") { + if (verbose) + return obj.toString(); + else + return "Function"; + } else if (Object.prototype.toString.call(obj) === '[object Date]') { + if (verbose) + return JSON.stringify(obj); + else + return obj.toString(); + } else { + if (verbose) { + if (seen.indexOf(obj) >= 0) + return circular; + else + seen.push(obj); + var pairs = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + var pair = JSON.stringify(key) + ":"; + pair += stringify(obj[key]); + pairs.push(pair); + } + } + return "{" + pairs.join(',') + "}"; + } else { + try { + return obj.toString(); + } catch (error) { + return ({}).toString(); + } + } + } + }; + + try { + return stringify(object); + } catch (error) { + return skewer.safeStringify(object, false); + } +}; + +/** + * Log an object to the Skewer REPL in Emacs (console.log). + * @param message The object to be logged. + */ +skewer.log = function() { + "use strict"; + for (var i = 0; i < arguments.length; i++) { + var log = { + type: "log", + value: skewer.safeStringify(arguments[i], true) + }; + skewer.postJSON(skewer.host + "/skewer/post", log); + } +}; + +/** + * Report an error event to the REPL. + * @param event An error event object. + */ +skewer.error = function(event) { + "use strict"; + var log = { + type: "error", + value: event.message, + filename: event.filename, + line: event.lineno, + column: event.column + }; + skewer.postJSON(skewer.host + "/skewer/post", log); +}; + +/** + * Prepare a result when an error occurs evaluating Javascript code. + * @param error The error object given by catch. + * @param result The resutl object to return to Emacs. + * @param request The request object from Emacs. + * @return The result object to send back to Emacs. + */ +skewer.errorResult = function(error, result, request) { + "use strict"; + return skewer.extend({}, result, { + value: error.toString(), + status: 'error', + error: { + name: error.name, + stack: error.stack, + type: error.type, + message: error.message, + eval: request.eval + } + }); +}; + +if (window.addEventListener) { + window.addEventListener('error', skewer.error); + if (document.readyState === 'complete') { + skewer(); + } else { + window.addEventListener('load', skewer); + } +} else { // < IE9 + window.attachEvent('onerror', skewer.error); + if (document.readyState === 'complete') { + skewer(); + } else { + window.attachEvent('onload', skewer); + } +} diff --git a/emacs.d/elpa/yaml-mode-0.0.9/yaml-mode-autoloads.el b/emacs.d/elpa/yaml-mode-0.0.9/yaml-mode-autoloads.el new file mode 100644 index 0000000..127bc25 --- /dev/null +++ b/emacs.d/elpa/yaml-mode-0.0.9/yaml-mode-autoloads.el @@ -0,0 +1,27 @@ +;;; yaml-mode-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "yaml-mode" "yaml-mode.el" (21666 48115 0 0)) +;;; Generated autoloads from yaml-mode.el + +(let ((loads (get 'yaml 'custom-loads))) (if (member '"yaml-mode" loads) nil (put 'yaml 'custom-loads (cons '"yaml-mode" loads)))) + +(autoload 'yaml-mode "yaml-mode" "\ +Simple mode to edit YAML. + +\\{yaml-mode-map} + +\(fn)" t nil) + +(add-to-list 'auto-mode-alist '("\\.ya?ml$" . yaml-mode)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; yaml-mode-autoloads.el ends here diff --git a/emacs.d/elpa/yaml-mode-0.0.9/yaml-mode-pkg.el b/emacs.d/elpa/yaml-mode-0.0.9/yaml-mode-pkg.el new file mode 100644 index 0000000..2a496dc --- /dev/null +++ b/emacs.d/elpa/yaml-mode-0.0.9/yaml-mode-pkg.el @@ -0,0 +1 @@ +(define-package "yaml-mode" "0.0.9" "Major mode for editing YAML files" 'nil) diff --git a/emacs.d/elpa/yaml-mode-0.0.9/yaml-mode.el b/emacs.d/elpa/yaml-mode-0.0.9/yaml-mode.el new file mode 100644 index 0000000..66ea331 --- /dev/null +++ b/emacs.d/elpa/yaml-mode-0.0.9/yaml-mode.el @@ -0,0 +1,415 @@ +;;; yaml-mode.el --- Major mode for editing YAML files + +;; Copyright (C) 2010-2013 Yoshiki Kurihara + +;; Author: Yoshiki Kurihara +;; Marshall T. Vandegrift +;; Keywords: data yaml +;; Version: 0.0.9 + +;; This file is not part of Emacs + +;; This file is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; This is a major mode for editing files in the YAML data +;; serialization format. It was initially developed by Yoshiki +;; Kurihara and many features were added by Marshall Vandegrift. As +;; YAML and Python share the fact that indentation determines +;; structure, this mode provides indentation and indentation command +;; behavior very similar to that of python-mode. + +;;; Installation: + +;; To install, just drop this file into a directory in your +;; `load-path' and (optionally) byte-compile it. To automatically +;; handle files ending in '.yml', add something like: +;; +;; (require 'yaml-mode) +;; (add-to-list 'auto-mode-alist '("\\.yml$" . yaml-mode)) +;; +;; to your .emacs file. +;; +;; Unlike python-mode, this mode follows the Emacs convention of not +;; binding the ENTER key to `newline-and-indent'. To get this +;; behavior, add the key definition to `yaml-mode-hook': +;; +;; (add-hook 'yaml-mode-hook +;; '(lambda () +;; (define-key yaml-mode-map "\C-m" 'newline-and-indent))) + +;;; Known Bugs: + +;; YAML is easy to write but complex to parse, and this mode doesn't +;; even really try. Indentation and highlighting will break on +;; abnormally complicated structures. + +;;; Code: + + +;; User definable variables + +;;;###autoload +(defgroup yaml nil + "Support for the YAML serialization format" + :group 'languages + :prefix "yaml-") + +(defcustom yaml-mode-hook nil + "*Hook run by `yaml-mode'." + :type 'hook + :group 'yaml) + +(defcustom yaml-indent-offset 2 + "*Amount of offset per level of indentation." + :type 'integer + :group 'yaml) + +(defcustom yaml-backspace-function 'backward-delete-char-untabify + "*Function called by `yaml-electric-backspace' when deleting backwards. +It will receive one argument, the numeric prefix value." + :type 'function + :group 'yaml) + +(defcustom yaml-block-literal-search-lines 100 + "*Maximum number of lines to search for start of block literals." + :type 'integer + :group 'yaml) + +(defcustom yaml-block-literal-electric-alist + '((?| . "") (?> . "-")) + "*Characters for which to provide electric behavior. +The association list key should be a key code and the associated value +should be a string containing additional characters to insert when +that key is pressed to begin a block literal." + :type 'alist + :group 'yaml) + +(defface yaml-tab-face + '((((class color)) (:background "red" :foreground "red" :bold t)) + (t (:reverse-video t))) + "Face to use for highlighting tabs in YAML files." + :group 'faces + :group 'yaml) + +(defcustom yaml-imenu-generic-expression + '((nil "^\\(:?[a-zA-Z_-]+\\):" 1)) + "The imenu regex to parse an outline of the yaml file." + :type 'string + :group 'yaml) + + +;; Constants + +(defconst yaml-mode-version "0.0.9" "Version of `yaml-mode'.") + +(defconst yaml-blank-line-re "^ *$" + "Regexp matching a line containing only (valid) whitespace.") + +(defconst yaml-comment-re "\\(?:^\\|\\s-+\\)\\(#.*\\)" + "Regexp matching a line containing a YAML comment or delimiter.") + +(defconst yaml-directive-re "^\\(?:--- \\)? *%\\(\\w+\\)" + "Regexp matching a line contatining a YAML directive.") + +(defconst yaml-document-delimiter-re "^ *\\(?:---\\|[.][.][.]\\)" + "Rexexp matching a YAML document delimiter line.") + +(defconst yaml-node-anchor-alias-re "[&*][a-zA-Z0-9_-]+" + "Regexp matching a YAML node anchor or alias.") + +(defconst yaml-tag-re "!!?[^ \n]+" + "Rexexp matching a YAML tag.") + +(defconst yaml-bare-scalar-re + "\\(?:[^-:,#!\n{\\[ ]\\|[^#!\n{\\[ ]\\S-\\)[^#\n]*?" + "Rexexp matching a YAML bare scalar.") + +(defconst yaml-hash-key-re + (concat "\\(?:^\\(?:--- \\)?\\|{\\|\\(?:[-,] +\\)+\\) *" + "\\(?:" yaml-tag-re " +\\)?" + "\\(" yaml-bare-scalar-re "\\) *:" + "\\(?: +\\|$\\)") + "Regexp matching a single YAML hash key.") + +(defconst yaml-scalar-context-re + (concat "\\(?:^\\(?:--- \\)?\\|{\\|\\(?:[-,] +\\)+\\) *" + "\\(?:" yaml-bare-scalar-re " *: \\)?") + "Regexp indicating the begininng of a scalar context.") + +(defconst yaml-nested-map-re + (concat ".*: *\\(?:&.*\\|{ *\\|" yaml-tag-re " *\\)?$") + "Regexp matching a line beginning a YAML nested structure.") + +(defconst yaml-block-literal-base-re " *[>|][-+0-9]* *\\(?:\n\\|\\'\\)" + "Regexp matching the substring start of a block literal.") + +(defconst yaml-block-literal-re + (concat yaml-scalar-context-re + "\\(?:" yaml-tag-re "\\)?" + yaml-block-literal-base-re) + "Regexp matching a line beginning a YAML block literal.") + +(defconst yaml-nested-sequence-re + (concat "^\\(?: *- +\\)+" + "\\(?:" yaml-bare-scalar-re " *:\\(?: +.*\\)?\\)?$") + "Regexp matching a line containing one or more nested YAML sequences.") + +(defconst yaml-constant-scalars-re + (concat "\\(?:^\\|\\(?::\\|-\\|,\\|{\\|\\[\\) +\\) *" + (regexp-opt + '("~" "null" "Null" "NULL" + ".nan" ".NaN" ".NAN" + ".inf" ".Inf" ".INF" + "-.inf" "-.Inf" "-.INF" + "y" "Y" "yes" "Yes" "YES" "n" "N" "no" "No" "NO" + "true" "True" "TRUE" "false" "False" "FALSE" + "on" "On" "ON" "off" "Off" "OFF") t) + " *$") + "Regexp matching certain scalar constants in scalar context.") + + +;; Mode setup + +(defvar yaml-mode-map () + "Keymap used in `yaml-mode' buffers.") +(if yaml-mode-map + nil + (setq yaml-mode-map (make-sparse-keymap)) + (define-key yaml-mode-map "|" 'yaml-electric-bar-and-angle) + (define-key yaml-mode-map ">" 'yaml-electric-bar-and-angle) + (define-key yaml-mode-map "-" 'yaml-electric-dash-and-dot) + (define-key yaml-mode-map "." 'yaml-electric-dash-and-dot) + (define-key yaml-mode-map [backspace] 'yaml-electric-backspace) + (define-key yaml-mode-map "\C-j" 'newline-and-indent)) + +(defvar yaml-mode-syntax-table nil + "Syntax table in use in `yaml-mode' buffers.") +(if yaml-mode-syntax-table + nil + (setq yaml-mode-syntax-table (make-syntax-table)) + (modify-syntax-entry ?\' "\"" yaml-mode-syntax-table) + (modify-syntax-entry ?\" "\"" yaml-mode-syntax-table) + (modify-syntax-entry ?# "<" yaml-mode-syntax-table) + (modify-syntax-entry ?\n ">" yaml-mode-syntax-table) + (modify-syntax-entry ?\\ "\\" yaml-mode-syntax-table) + (modify-syntax-entry ?- "w" yaml-mode-syntax-table) + (modify-syntax-entry ?_ "_" yaml-mode-syntax-table) + (modify-syntax-entry ?\( "." yaml-mode-syntax-table) + (modify-syntax-entry ?\) "." yaml-mode-syntax-table) + (modify-syntax-entry ?\{ "(}" yaml-mode-syntax-table) + (modify-syntax-entry ?\} "){" yaml-mode-syntax-table) + (modify-syntax-entry ?\[ "(]" yaml-mode-syntax-table) + (modify-syntax-entry ?\] ")[" yaml-mode-syntax-table)) + +;;;###autoload +(define-derived-mode yaml-mode fundamental-mode "YAML" + "Simple mode to edit YAML. + +\\{yaml-mode-map}" + (set (make-local-variable 'comment-start) "# ") + (set (make-local-variable 'comment-start-skip) "#+ *") + (set (make-local-variable 'indent-line-function) 'yaml-indent-line) + (set (make-local-variable 'indent-tabs-mode) nil) + (set (make-local-variable 'font-lock-defaults) + '(yaml-font-lock-keywords + nil nil nil nil + (font-lock-syntactic-keywords . yaml-font-lock-syntactic-keywords)))) + + +;; Font-lock support + +(defvar yaml-font-lock-keywords + (list + (cons yaml-comment-re '(1 font-lock-comment-face)) + (cons yaml-constant-scalars-re '(1 font-lock-constant-face)) + (cons yaml-tag-re '(0 font-lock-type-face)) + (cons yaml-node-anchor-alias-re '(0 font-lock-function-name-face t)) + (cons yaml-hash-key-re '(1 font-lock-variable-name-face t)) + (cons yaml-document-delimiter-re '(0 font-lock-comment-face)) + (cons yaml-directive-re '(1 font-lock-builtin-face)) + '(yaml-font-lock-block-literals 0 font-lock-string-face t) + '("^[\t]+" 0 'yaml-tab-face t)) + "Additional expressions to highlight in YAML mode.") + +(defvar yaml-font-lock-syntactic-keywords + (list '(yaml-syntactic-block-literals 0 "." t)) + "Additional syntax features to highlight in YAML mode.") + + +(defun yaml-font-lock-block-literals (bound) + "Find lines within block literals. +Find the next line of the first (if any) block literal after point and +prior to BOUND. Returns the beginning and end of the block literal +line in the match data, as consumed by `font-lock-keywords' matcher +functions. The function begins by searching backwards to determine +whether or not the current line is within a block literal. This could +be time-consuming in large buffers, so the number of lines searched is +artificially limitted to the value of +`yaml-block-literal-search-lines'." + (if (eolp) (goto-char (1+ (point)))) + (unless (or (eobp) (>= (point) bound)) + (let ((begin (point)) + (end (min (1+ (point-at-eol)) bound))) + (goto-char (point-at-bol)) + (while (and (looking-at yaml-blank-line-re) (not (bobp))) + (forward-line -1)) + (let ((nlines yaml-block-literal-search-lines) + (min-level (current-indentation))) + (forward-line -1) + (while (and (/= nlines 0) + (/= min-level 0) + (not (looking-at yaml-block-literal-re)) + (not (bobp))) + (set 'nlines (1- nlines)) + (unless (looking-at yaml-blank-line-re) + (set 'min-level (min min-level (current-indentation)))) + (forward-line -1)) + (cond + ((and (< (current-indentation) min-level) + (looking-at yaml-block-literal-re)) + (goto-char end) (set-match-data (list begin end)) t) + ((progn + (goto-char begin) + (re-search-forward (concat yaml-block-literal-re + " *\\(.*\\)\n") + bound t)) + (set-match-data (nthcdr 2 (match-data))) t)))))) + +(defun yaml-syntactic-block-literals (bound) + "Find quote characters within block literals. +Finds the first quote character within a block literal (if any) after +point and prior to BOUND. Returns the position of the quote character +in the match data, as consumed by matcher functions in +`font-lock-syntactic-keywords'. This allows the mode to treat ['\"] +characters in block literals as punctuation syntax instead of string +syntax, preventing unmatched quotes in block literals from painting +the entire buffer in `font-lock-string-face'." + (let ((found nil)) + (while (and (not found) + (/= (point) bound) + (yaml-font-lock-block-literals bound)) + (let ((begin (match-beginning 0)) (end (match-end 0))) + (goto-char begin) + (cond + ((re-search-forward "['\"]" end t) (setq found t)) + ((goto-char end))))) + found)) + + +;; Indentation and electric keys + +(defun yaml-compute-indentation () + "Calculate the maximum sensible indentation for the current line." + (save-excursion + (beginning-of-line) + (if (looking-at yaml-document-delimiter-re) 0 + (forward-line -1) + (while (and (looking-at yaml-blank-line-re) + (> (point) (point-min))) + (forward-line -1)) + (+ (current-indentation) + (if (looking-at yaml-nested-map-re) yaml-indent-offset 0) + (if (looking-at yaml-nested-sequence-re) yaml-indent-offset 0) + (if (looking-at yaml-block-literal-re) yaml-indent-offset 0))))) + +(defun yaml-indent-line () + "Indent the current line. +The first time this command is used, the line will be indented to the +maximum sensible indentation. Each immediately subsequent usage will +back-dent the line by `yaml-indent-offset' spaces. On reaching column +0, it will cycle back to the maximum sensible indentation." + (interactive "*") + (let ((ci (current-indentation)) + (cc (current-column)) + (need (yaml-compute-indentation))) + (save-excursion + (beginning-of-line) + (delete-horizontal-space) + (if (and (equal last-command this-command) (/= ci 0)) + (indent-to (* (/ (- ci 1) yaml-indent-offset) yaml-indent-offset)) + (indent-to need))) + (if (< (current-column) (current-indentation)) + (forward-to-indentation 0)))) + +(defun yaml-electric-backspace (arg) + "Delete characters or back-dent the current line. +If invoked following only whitespace on a line, will back-dent to the +immediately previous multiple of `yaml-indent-offset' spaces." + (interactive "*p") + (if (or (/= (current-indentation) (current-column)) (bolp)) + (funcall yaml-backspace-function arg) + (let ((ci (current-column))) + (beginning-of-line) + (delete-horizontal-space) + (indent-to (* (/ (- ci (* arg yaml-indent-offset)) + yaml-indent-offset) + yaml-indent-offset))))) + +(defun yaml-electric-bar-and-angle (arg) + "Insert the bound key and possibly begin a block literal. +Inserts the bound key. If inserting the bound key causes the current +line to match the initial line of a block literal, then inserts the +matching string from `yaml-block-literal-electric-alist', a newline, +and indents appropriately." + (interactive "*P") + (self-insert-command (prefix-numeric-value arg)) + (let ((extra-chars + (assoc last-command-event + yaml-block-literal-electric-alist))) + (cond + ((and extra-chars (not arg) (eolp) + (save-excursion + (beginning-of-line) + (looking-at yaml-block-literal-re))) + (insert (cdr extra-chars)) + (newline-and-indent))))) + +(defun yaml-electric-dash-and-dot (arg) + "Insert the bound key and possibly de-dent line. +Inserts the bound key. If inserting the bound key causes the current +line to match a document delimiter, de-dent the line to the left +margin." + (interactive "*P") + (self-insert-command (prefix-numeric-value arg)) + (save-excursion + (beginning-of-line) + (if (and (not arg) (looking-at yaml-document-delimiter-re)) + (delete-horizontal-space)))) + + +(defun yaml-set-imenu-generic-expression () + (make-local-variable 'imenu-generic-expression) + (make-local-variable 'imenu-create-index-function) + (setq imenu-create-index-function 'imenu-default-create-index-function) + (setq imenu-generic-expression yaml-imenu-generic-expression)) + +(add-hook 'yaml-mode-hook 'yaml-set-imenu-generic-expression) + + +(defun yaml-mode-version () + "Diplay version of `yaml-mode'." + (interactive) + (message "yaml-mode %s" yaml-mode-version) + yaml-mode-version) + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.ya?ml$" . yaml-mode)) + +(provide 'yaml-mode) + +;;; yaml-mode.el ends here diff --git a/emacs.d/manual/go-autocomplete.el b/emacs.d/manual/go-autocomplete.el new file mode 100644 index 0000000..9c12cd1 --- /dev/null +++ b/emacs.d/manual/go-autocomplete.el @@ -0,0 +1,201 @@ +;;; go-autocomplete.el --- auto-complete-mode backend for go-mode + +;; Copyright (C) 2010 + +;; Author: Mikhail Kuryshev +;; Keywords: languages +;; Package-Requires: ((auto-complete "1.4.0")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Ensure that go-autocomplete in your load-path and add to your ~/.emacs +;; following line: +;; +;; (require 'go-autocomplete) + +;; Also you could setup any combination (for example M-TAB) +;; for invoking auto-complete: +;; +;; (require 'auto-complete-config) +;; (define-key ac-mode-map (kbd "M-TAB") 'auto-complete) + +;;; Code: + +(eval-when-compile + (require 'cl) + (require 'auto-complete)) + +(defgroup go-autocomplete nil + "auto-complete for go language." + :prefix "ac-go-" + :group 'auto-complete) + +(defcustom ac-go-expand-arguments-into-snippets t + "Expand function arguments into snippets. This feature requires `yasnippet'." + :type 'boolean + :group 'go-autocomplete) + +;; Close gocode daemon at exit unless it was already running +(eval-after-load "go-mode" + '(progn + (let* ((user (or (getenv "USER") "all")) + (sock (format (concat temporary-file-directory "gocode-daemon.%s") user))) + (unless (file-exists-p sock) + (add-hook 'kill-emacs-hook #'(lambda () + (ignore-errors + (call-process "gocode" nil nil nil "close")))))))) + +;(defvar go-reserved-keywords +; '("break" "case" "chan" "const" "continue" "default" "defer" "else" +; "fallthrough" "for" "func" "go" "goto" "if" "import" "interface" +; "map" "package" "range" "return" "select" "struct" "switch" "type" "var") +; "Go reserved keywords.") + +(defun ac-comphist-sort (db collection prefix &optional threshold) +;; redefine to disable sorting + (let (result + (n 0) + (total 0) + (cur 0)) + (setq result (mapcar (lambda (a) + (when (and cur threshold) + (if (>= cur (* total threshold)) + (setq cur nil) + (incf n) + (incf cur (cdr a)))) + (car a)) + (mapcar (lambda (string) + (let ((score (ac-comphist-score db string prefix))) + (incf total score) + (cons string score))) + collection))) + (if threshold + (cons n result) + result))) + +(defun ac-go-invoke-autocomplete () + (let ((temp-buffer (generate-new-buffer "*gocode*"))) + (unwind-protect + (progn + (call-process-region (point-min) + (point-max) + "gocode" + nil + temp-buffer + nil + "-f=emacs" + "autocomplete" + (or (buffer-file-name) "") + (concat "c" (int-to-string (- (point) 1)))) + (with-current-buffer temp-buffer (buffer-string))) + (kill-buffer temp-buffer)))) + +(defun ac-go-format-autocomplete (buffer-contents) + (sort + (split-string buffer-contents "\n" t) + (lambda (a b) (string< (downcase a) + (downcase b))))) + +(defun ac-go-get-candidates (strings) + (let ((prop (lambda (entry) + (let ((name (nth 0 entry)) + (summary (nth 1 entry))) + (propertize name + 'summary summary)))) + (split (lambda (strings) + (mapcar (lambda (str) + (split-string str ",," t)) + strings)))) + (mapcar prop (funcall split strings)))) + +(defun ac-go-action () + (let ((item (cdr ac-last-completion))) + (when (stringp item) + (setq symbol (get-text-property 0 'summary item)) + (message "%s" symbol) + (when (and (featurep 'yasnippet) ac-go-expand-arguments-into-snippets) + (ac-go-insert-yas-snippet-string symbol))))) + +(defun ac-go-insert-yas-snippet-string (s) + (let ((ret "") (pos (point)) match-res match args) + (save-match-data + (setq match-res (string-match "func(." s)) + (when (and match-res (= 0 match-res)) + (setq match (match-string 0 s)) + (unless (string= match "func()") + (setq args (ac-go-split-args s)) + (dolist (arg args) + (setq ret (concat ret "${" arg "}, "))) + (when (> (length ret) 2) + (setq ret (substring ret 0 (- (length ret) 2))))) + (setq ret (concat "(" ret ")")) + (yas-expand-snippet ret pos pos))))) + +(defun ac-go-split-args (args-str) + (let ((cur 5) + (pre 5) + (unmatch-l-paren-count 1) + (args (list)) + c) + (while (> unmatch-l-paren-count 0) + (setq c (aref args-str cur)) + (cond ((= ?\( c) + (setq unmatch-l-paren-count (1+ unmatch-l-paren-count))) + ((= ?\) c) + (setq unmatch-l-paren-count (1- unmatch-l-paren-count)) + (when (= 0 unmatch-l-paren-count) + (push (substring args-str pre cur) args))) + ((= ?\, c) + (when (= 1 unmatch-l-paren-count) + (push (substring args-str pre cur) args) + (setq cur (+ cur 2)) + (setq pre cur)))) + (setq cur (1+ cur))) + (nreverse args))) + +(defun ac-go-document (item) + (if (stringp item) + (let ((s (get-text-property 0 'summary item))) + (message "%s" s) + nil))) + +(defun ac-go-candidates () + (ac-go-get-candidates (ac-go-format-autocomplete (ac-go-invoke-autocomplete)))) + +(defun ac-go-prefix () + (or (ac-prefix-symbol) + (let ((c (char-before))) + (when (eq ?\. c) + (point))))) + +(ac-define-source go + '((candidates . ac-go-candidates) + (candidate-face . ac-candidate-face) + (selection-face . ac-selection-face) + (document . ac-go-document) + (action . ac-go-action) + (prefix . ac-go-prefix) + (requires . 0) + (cache) + (symbol . "g"))) + +(add-to-list 'ac-modes 'go-mode) + +(add-hook 'go-mode-hook #'(lambda () + (add-to-list 'ac-sources 'ac-source-go))) + +(provide 'go-autocomplete) +;;; go-autocomplete.el ends here