diff --git a/.gitmodules b/.gitmodules index 85d6abad..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +0,0 @@ -[submodule "lisp/multi-line-it"] - path = lisp/multi-line-it - url = https://github.com/IvanMalison/multi-line-it.git -[submodule "lisp/emacs-testify"] - path = lisp/emacs-testify - url = https://github.com/IvanMalison/emacs-testify.git diff --git a/init.el b/init.el index da2a1469..a98b1b39 100644 --- a/init.el +++ b/init.el @@ -19,6 +19,8 @@ (setq custom-file "~/.emacs.d/custom.el") (when (file-exists-p custom-file) (load custom-file)) +(add-to-list 'load-path "~/.emacs.d/lisp") + ;; ============================================================================= ;; ELPA/package.el/MELPA ;; ============================================================================= @@ -50,7 +52,7 @@ (defvar packages-python '(jedi pymacs pytest sphinx-doc)) (defvar packages-scala '(scala-mode2 ensime)) -(defvar packages-js '(js2-mode js3-mode web-beautify tern tern-auto-complete)) +(defvar packages-js '(js2-mode js3-mode web-beautify tern tern-auto-complete slime-js)) (defun ensure-packages-installed (packages) (dolist (p packages) diff --git a/lisp/emacs-testify b/lisp/emacs-testify deleted file mode 160000 index c64b99e7..00000000 --- a/lisp/emacs-testify +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c64b99e729eae0df9d9c65f8deecc602c972519f diff --git a/lisp/multi-line-it b/lisp/multi-line-it deleted file mode 160000 index c62ca553..00000000 --- a/lisp/multi-line-it +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c62ca553af2e23b7e06113b1b90f5c3165fe68a2 diff --git a/lisp/pymacs/pymacs.el b/lisp/pymacs/pymacs.el deleted file mode 100644 index 55f41ad8..00000000 --- a/lisp/pymacs/pymacs.el +++ /dev/null @@ -1,842 +0,0 @@ -;;; pymacs.el --- Interface between Emacs Lisp and Python - -;; Copyright © 2001-2003, 2012, 2013 Progiciels Bourbeau-Pinard inc. - -;; Author: François Pinard -;; Maintainer: François Pinard -;; URL: https://github.com/pinard/Pymacs -;; Version: 0.25 -;; Keywords: Python interface protocol - -;; 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -;;; Commentary: - -;; Pymacs is a powerful tool which, once started from Emacs, allows -;; both-way communication between Emacs Lisp and Python. Pymacs aims -;; Python as an extension language for Emacs rather than the other way -;; around. Visit http://pymacs.progiciels-bpi.ca to read its manual, -;; which also contains installation instructions. - -;;; Code: - -;; The code is organized into pages, grouping declarations by topic. -;; Such pages are introduced by a form feed and a topic description. - -;;; Portability stunts. - -(defvar pymacs-use-hash-tables - (and (fboundp 'make-hash-table) (fboundp 'gethash) (fboundp 'puthash)) - "Set to t if hash tables are available.") - -(eval-and-compile - - ;; pymacs-called-interactively-p - (defalias 'pymacs-called-interactively-p - (cond ((fboundp 'called-interactively-p) 'called-interactively-p) - ;; Emacs before 22. - (t 'interactive-p))) - - ;; pymacs-cancel-timer - (defalias 'pymacs-cancel-timer - (cond ((fboundp 'cancel-timer) 'cancel-timer) - ;; XEmacs case - yet having post-gc-hook, this is unused. - ((fboundp 'delete-itimer) 'delete-itimer) - (t 'ignore))) - - ;; pymacs-kill-without-query - (if (fboundp 'set-process-query-on-exit-flag) - (defun pymacs-kill-without-query (process) - "Tell recent Emacs how to quickly destroy PROCESS while exiting." - (set-process-query-on-exit-flag process nil)) - (defalias 'pymacs-kill-without-query - (if (fboundp 'process-kill-without-query-process) - 'process-kill-without-query-process - 'ignore))) - - ;; pymacs-multibyte-string-p - (cond ((fboundp 'multibyte-string-p) - (defalias 'pymacs-multibyte-string-p 'multibyte-string-p)) - ((fboundp 'find-charset-string) - (defun pymacs-multibyte-string-p (string) - "Tell XEmacs if STRING should be handled as multibyte." - (not (member (find-charset-string string) '(nil (ascii)))))) - (t - ;; Tell XEmacs that STRING is unibyte, when Mule is not around! - (defalias 'pymacs-multibyte-string-p 'ignore))) - - ;; pymacs-report-error - (defalias 'pymacs-report-error (symbol-function 'error)) - - ;; pymacs-set-buffer-multibyte - (if (fboundp 'set-buffer-multibyte) - (defalias 'pymacs-set-buffer-multibyte 'set-buffer-multibyte) - (defun pymacs-set-buffer-multibyte (flag) - "For use in Emacs 20.2 or earlier. Under XEmacs: no operation." - (setq enable-multibyte-characters flag))) - - ;; pymacs-timerp - (defalias 'pymacs-timerp - (cond ((fboundp 'timerp) 'timerp) - ;; XEmacs case - yet having post-gc-hook, this is unused. - ((fboundp 'itimerp) 'itimerp) - (t 'ignore))) - - ) - -;;; Published variables and functions. - -(defvar pymacs-python-command "python" - "Shell command used to start Python interpreter.") - -(defvar pymacs-load-path nil - "List of additional directories to search for Python modules. -The directories listed will be searched first, in the order given.") - -(defvar pymacs-trace-transit '(5000 . 30000) - "Keep the communication buffer growing, for debugging. -When this variable is nil, the `*Pymacs*' communication buffer gets erased -before each communication round-trip. Setting it to `t' guarantees that -the full communication is saved, which is useful for debugging. -It could also be given as (KEEP . LIMIT): whenever the buffer exceeds LIMIT -bytes, it is reduced to approximately KEEP bytes.") - -(defvar pymacs-forget-mutability nil - "Transmit copies to Python instead of Lisp handles, as much as possible. -When this variable is nil, most mutable objects are transmitted as handles. -This variable is meant to be temporarily rebound to force copies.") - -(defvar pymacs-mutable-strings nil - "Prefer transmitting Lisp strings to Python as handles. -When this variable is nil, strings are transmitted as copies, and the -Python side thus has no way for modifying the original Lisp strings. -This variable is ignored whenever `forget-mutability' is set.") - -(defvar pymacs-timeout-at-start 30 - "Maximum reasonable time, in seconds, for starting the Pymacs helper. -A machine should be pretty loaded before one needs to increment this.") - -(defvar pymacs-timeout-at-reply 5 - "Expected maximum time, in seconds, to get the first line of a reply. -The status of the Pymacs helper is checked at every such timeout.") - -(defvar pymacs-timeout-at-line 2 - "Expected maximum time, in seconds, to get another line of a reply. -The status of the Pymacs helper is checked at every such timeout.") - -(defvar pymacs-auto-restart 'ask - "Should the Pymacs helper be restarted whenever it dies? -Possible values are nil, t or ask.") - -(defvar pymacs-dreadful-zombies nil - "If zombies should trigger hard errors, whenever they get called. -If `nil', calling a zombie will merely produce a diagnostic message.") - -(defvar pymacs-load-history nil "Pymacs loading history.") - -(defvar pymacs-after-load-functions nil - "Special hook run after loading a Python module. -Each function there is called with a single argument, the Python -module name passed to as the first argument of `pymacs-load'.") - -;;;###autoload -(defun pymacs-load (module &optional prefix noerror) - "Import the Python module named MODULE into Emacs. -Each function in the Python module is made available as an Emacs function. -The Lisp name of each function is the concatenation of PREFIX with -the Python name, in which underlines are replaced by dashes. If PREFIX is -not given, it defaults to MODULE followed by a dash. -If NOERROR is not nil, do not raise error when the module is not found." - (interactive - (let* ((module (read-string "Python module? ")) - (default (concat (car (last (split-string module "\\."))) "-")) - (prefix (read-string (format "Prefix? [%s] " default) - nil nil default))) - (list module prefix))) - (message "Pymacs loading %s..." module) - (let ((lisp-code (pymacs-call "pymacs_load_helper" module prefix noerror))) - (cond (lisp-code (let ((result (eval lisp-code))) - (add-to-list 'pymacs-load-history - (list module prefix noerror) - ;; append so that order is kept - 'append) - (message "Pymacs loading %s...done" module) - (run-hook-with-args 'pymacs-after-load-functions module) - result)) - (noerror (message "Pymacs loading %s...failed" module) nil)))) - -;;;###autoload -(defun pymacs-autoload (function module &optional prefix docstring interactive) - "Pymacs's equivalent of the standard emacs facility `autoload'. -Define FUNCTION to autoload from Python MODULE using PREFIX. -If PREFIX is not given, it defaults to MODULE followed by a dash. -Optional DOCSTRING documents FUNCTION until it gets loaded. -INTERACTIVE is normally the argument to the function `interactive', -t means `interactive' without arguments, nil means not interactive, -which is the default." - (unless (symbolp function) - (error "`%s' should be a symbol" function)) - (unless (stringp module) - (error "`%s' should be a string" module)) - (unless (pymacs-python-reference function) - - (defalias function - `(lambda (&rest args) - ,(or docstring - (format "Function `%s' to be loaded from Python module `%s'" - function module)) - ,(cond ((eq interactive t) '(interactive)) - (interactive `(interactive ,interactive))) - (pymacs-load ,module ,prefix) - (unless (pymacs-python-reference ',function) - (error "Pymacs autoload failed to define function %s" ',function)) - (apply ',function args))))) - -;;;###autoload -(defun pymacs-eval (text) - "Compile TEXT as a Python expression, and return its value." - (interactive "sPython expression: ") - (let ((value (pymacs-serve-until-reply "eval" `(princ ,text)))) - (when (pymacs-called-interactively-p) - (message "%S" value)) - value)) - -;;;###autoload -(defun pymacs-exec (text) - "Compile and execute TEXT as a sequence of Python statements. -This functionality is experimental, and does not appear to be useful." - (interactive "sPython statements: ") - (let ((value (pymacs-serve-until-reply "exec" `(princ ,text)))) - (when (pymacs-called-interactively-p) - (message "%S" value)) - value)) - -;;;###autoload -(defun pymacs-call (function &rest arguments) - "Return the result of calling a Python function FUNCTION over ARGUMENTS. -FUNCTION is a string denoting the Python function, ARGUMENTS are separate -Lisp expressions, one per argument. Immutable Lisp constants are converted -to Python equivalents, other structures are converted into Lisp handles." - (pymacs-serve-until-reply - "eval" `(pymacs-print-for-apply ',function ',arguments))) - -;;;###autoload -(defun pymacs-apply (function arguments) - "Return the result of calling a Python function FUNCTION over ARGUMENTS. -FUNCTION is a string denoting the Python function, ARGUMENTS is a list of -Lisp expressions. Immutable Lisp constants are converted to Python -equivalents, other structures are converted into Lisp handles." - (pymacs-serve-until-reply - "eval" `(pymacs-print-for-apply ',function ',arguments))) - -;;; Integration details. - -;; This page tries to increase the integration seamlessness of Pymacs -;; with the reminder of Emacs. - -;; Module "desktop" savagely kills `*Pymacs*' in some circumstances. -;; Let's avoid such damage. - -(eval-after-load 'desktop - '(push "\\*Pymacs\\*" desktop-clear-preserve-buffers)) - -;; Python functions and modules should ideally look like Lisp -;; functions and modules. - -(when t - - (defadvice documentation (around pymacs-ad-documentation activate) - ;; Integration of doc-strings. - (let* ((reference (pymacs-python-reference function)) - (python-doc (when reference - (pymacs-eval (format "doc_string(%s)" reference))))) - (if (or reference python-doc) - (setq ad-return-value - (concat - "It interfaces to a Python function.\n\n" - (when python-doc - (if raw python-doc (substitute-command-keys python-doc))))) - ad-do-it)))) - -(defun pymacs-python-reference (object) - ;; Return the text reference of a Python object if possible, else nil. - (when (functionp object) - (let* ((definition (indirect-function object)) - (body (and (pymacs-proper-list-p definition) - (> (length definition) 2) - (eq (car definition) 'lambda) - (cddr definition)))) - (when (and body (listp (car body)) (eq (caar body) 'interactive)) - ;; Skip the interactive specification of a function. - (setq body (cdr body))) - (when (and body - ;; Advised functions start with a string. - (not (stringp (car body))) - ;; Python trampolines hold exactly one expression. - (= (length body) 1)) - (let ((expression (car body))) - ;; EXPRESSION might now hold something like: - ;; (pymacs-apply (quote (pymacs-python . N)) ARGUMENT-LIST) - (when (and (pymacs-proper-list-p expression) - (= (length expression) 3) - (eq (car expression) 'pymacs-apply) - (eq (car (cadr expression)) 'quote)) - (setq object (cadr (cadr expression)))))))) - (when (eq (car-safe object) 'pymacs-python) - (format "python[%d]" (cdr object)))) - -;; The following functions are experimental -- they are not satisfactory yet. - -(defun pymacs-file-handler (operation &rest arguments) - ;; Integration of load-file, autoload, etc. - ;; Emacs might want the contents of some `MODULE.el' which does not exist, - ;; while there is a `MODULE.py' or `MODULE.pyc' file in the same directory. - ;; The goal is to generate a virtual contents for this `MODULE.el' file, as - ;; a set of Lisp trampoline functions to the Python module functions. - ;; Python modules can then be loaded or autoloaded as if they were Lisp. - (cond ((and (eq operation 'file-readable-p) - (let ((module (substring (car arguments) 0 -3))) - (or (pymacs-file-force operation arguments) - (file-readable-p (concat module ".py")) - (file-readable-p (concat module ".pyc")))))) - ((and (eq operation 'load) - (not (pymacs-file-force - 'file-readable-p (list (car arguments)))) - (file-readable-p (car arguments))) - (let ((lisp-code (pymacs-call "pymacs_load_helper" - (substring (car arguments) 0 -3) - nil))) - (unless lisp-code - (pymacs-report-error "Python import error")) - (eval lisp-code))) - ((and (eq operation 'insert-file-contents) - (not (pymacs-file-force - 'file-readable-p (list (car arguments)))) - (file-readable-p (car arguments))) - (let ((lisp-code (pymacs-call "pymacs_load_helper" - (substring (car arguments) 0 -3) - nil))) - (unless lisp-code - (pymacs-report-error "Python import error")) - (insert (prin1-to-string lisp-code)))) - (t (pymacs-file-force operation arguments)))) - -(defun pymacs-file-force (operation arguments) - ;; Bypass the file handler. - (let ((inhibit-file-name-handlers - (cons 'pymacs-file-handler - (and (eq inhibit-file-name-operation operation) - inhibit-file-name-handlers))) - (inhibit-file-name-operation operation)) - (apply operation arguments))) - -;;(add-to-list 'file-name-handler-alist '("\\.el\\'" . pymacs-file-handler)) - -;;; Gargabe collection of Python IDs. - -;; Python objects which have no Lisp representation are allocated on the -;; Python side as `python[INDEX]', and INDEX is transmitted to Emacs, with -;; the value to use on the Lisp side for it. Whenever Lisp does not need a -;; Python object anymore, it should be freed on the Python side. The -;; following variables and functions are meant to fill this duty. - -(defvar pymacs-used-ids nil - "List of received IDs, currently allocated on the Python side.") - -;; This is set whenever the Pymacs helper successfully starts, and is -;; also used to later detect the death of a previous helper. If -;; pymacs-use-hash-tables is unset, this variable receives `t' when -;; the helper starts, so the detection works nevertheless. -(defvar pymacs-weak-hash nil - "Weak hash table, meant to find out which IDs are still needed.") - -(defvar pymacs-gc-wanted nil - "Flag that it is desirable to clean up unused IDs on the Python side.") - -(defvar pymacs-gc-inhibit nil - "Flag that a new Pymacs garbage collection should just not run now.") - -(defvar pymacs-gc-timer nil - "Timer to trigger Pymacs garbage collection at regular time intervals. -The timer is used only if `post-gc-hook' is not available.") - -(defun pymacs-schedule-gc (&optional xemacs-list) - (unless pymacs-gc-inhibit - (setq pymacs-gc-wanted t))) - -(defun pymacs-garbage-collect () - ;; Clean up unused IDs on the Python side. - (when (and pymacs-use-hash-tables (not pymacs-gc-inhibit)) - (let ((pymacs-gc-inhibit t) - (pymacs-forget-mutability t) - (ids pymacs-used-ids) - used-ids unused-ids) - (while ids - (let ((id (car ids))) - (setq ids (cdr ids)) - (if (gethash id pymacs-weak-hash) - (setq used-ids (cons id used-ids)) - (setq unused-ids (cons id unused-ids))))) - (setq pymacs-used-ids used-ids - pymacs-gc-wanted nil) - (when unused-ids - (let ((pymacs-forget-mutability t)) - (pymacs-call "free_python" unused-ids)))))) - -(defun pymacs-defuns (arguments) - ;; Take one argument, a list holding a number of items divisible by 3. The - ;; first argument is an INDEX, the second is a NAME, the third is the - ;; INTERACTION specification, and so forth. Register Python INDEX with a - ;; function with that NAME and INTERACTION on the Lisp side. The strange - ;; calling convention is to minimise quoting at call time. - (while (>= (length arguments) 3) - (let ((index (nth 0 arguments)) - (name (nth 1 arguments)) - (interaction (nth 2 arguments))) - (fset name (pymacs-defun index interaction)) - (setq arguments (nthcdr 3 arguments))))) - -(defun pymacs-defun (index interaction) - ;; Register INDEX on the Lisp side with a Python object that is a function, - ;; and return a lambda form calling that function. If the INTERACTION - ;; specification is nil, the function is not interactive. Otherwise, the - ;; function is interactive, INTERACTION is then either a string, or the - ;; index of an argument-less Python function returning the argument list. - (let ((object (pymacs-python index))) - (cond ((null interaction) - `(lambda (&rest arguments) - (pymacs-apply ',object arguments))) - ((stringp interaction) - `(lambda (&rest arguments) - (interactive ,interaction) - (pymacs-apply ',object arguments))) - (t `(lambda (&rest arguments) - (interactive (pymacs-call ',(pymacs-python interaction))) - (pymacs-apply ',object arguments)))))) - -(defun pymacs-python (index) - ;; Register on the Lisp side a Python object having INDEX, and return it. - ;; The result is meant to be recognised specially by `print-for-eval', and - ;; in the function position by `print-for-apply'. - (let ((object (cons 'pymacs-python index))) - (when pymacs-use-hash-tables - (puthash index object pymacs-weak-hash) - (setq pymacs-used-ids (cons index pymacs-used-ids))) - object)) - -;;; Generating Python code. - -;; Many Lisp expressions cannot fully be represented in Python, at least -;; because the object is mutable on the Lisp side. Such objects are allocated -;; somewhere into a vector of handles, and the handle index is used for -;; communication instead of the expression itself. - -(defvar pymacs-lisp nil - "Vector of handles to hold transmitted expressions.") - -(defvar pymacs-freed-list nil - "List of unallocated indices in Lisp.") - -;; When the Python GC is done with a Lisp object, a communication occurs so to -;; free the object on the Lisp side as well. - -(defun pymacs-allocate-lisp (expression) - ;; This function allocates some handle for an EXPRESSION, and return its - ;; index. - (unless pymacs-freed-list - (let* ((previous pymacs-lisp) - (old-size (length previous)) - (new-size (if (zerop old-size) 100 (+ old-size (/ old-size 2)))) - (counter new-size)) - (setq pymacs-lisp (make-vector new-size nil)) - (while (> counter 0) - (setq counter (1- counter)) - (if (< counter old-size) - (aset pymacs-lisp counter (aref previous counter)) - (setq pymacs-freed-list (cons counter pymacs-freed-list)))))) - (let ((index (car pymacs-freed-list))) - (setq pymacs-freed-list (cdr pymacs-freed-list)) - (aset pymacs-lisp index expression) - index)) - -(defun pymacs-free-lisp (indices) - ;; This function is triggered from Python side for Lisp handles which lost - ;; their last reference. These references should be cut on the Lisp side as - ;; well, or else, the objects will never be garbage-collected. - (while indices - (let ((index (car indices))) - (aset pymacs-lisp index nil) - (setq pymacs-freed-list (cons index pymacs-freed-list) - indices (cdr indices))))) - -(defun pymacs-print-for-apply (function arguments) - ;; This function prints a Python expression calling FUNCTION, which is a - ;; string naming a Python function, or a Python reference, over all its - ;; ARGUMENTS, which are Lisp expressions. - (let ((separator "") - argument) - (if (eq (car-safe function) 'pymacs-python) - (princ (format "python[%d]" (cdr function))) - (princ function)) - (princ "(") - (while arguments - (setq argument (car arguments) - arguments (cdr arguments)) - (princ separator) - (setq separator ", ") - (pymacs-print-for-eval argument)) - (princ ")"))) - -(defun pymacs-print-for-eval (expression) - ;; This function prints a Python expression out of a Lisp EXPRESSION. - (let (done) - (cond ((not expression) - (princ "None") - (setq done t)) - ((eq expression t) - (princ "True") - (setq done t)) - ((numberp expression) - (princ expression) - (setq done t)) - ((stringp expression) - (when (or pymacs-forget-mutability - (not pymacs-mutable-strings)) - (let* ((multibyte (pymacs-multibyte-string-p expression)) - (text (if multibyte - (encode-coding-string expression 'utf-8) - (copy-sequence expression)))) - (set-text-properties 0 (length text) nil text) - (when multibyte - (princ "b")) - (princ (mapconcat 'identity - (split-string (prin1-to-string text) "\n") - "\\n")) - (when multibyte - (princ ".decode('UTF-8')"))) - (setq done t))) - ((symbolp expression) - (let ((name (symbol-name expression))) - ;; The symbol can only be transmitted when in the main oblist. - (when (eq expression (intern-soft name)) - (princ "lisp[") - (prin1 name) - (princ "]") - (setq done t)))) - ((vectorp expression) - (when pymacs-forget-mutability - (let ((limit (length expression)) - (counter 0)) - (princ "(") - (while (< counter limit) - (unless (zerop counter) - (princ ", ")) - (pymacs-print-for-eval (aref expression counter)) - (setq counter (1+ counter))) - (when (= limit 1) - (princ ",")) - (princ ")") - (setq done t)))) - ((eq (car-safe expression) 'pymacs-python) - (princ "python[") - (princ (cdr expression)) - (princ "]") - (setq done t)) - ((pymacs-proper-list-p expression) - (when pymacs-forget-mutability - (princ "[") - (pymacs-print-for-eval (car expression)) - (while (setq expression (cdr expression)) - (princ ", ") - (pymacs-print-for-eval (car expression))) - (princ "]") - (setq done t)))) - (unless done - (let ((class (cond ((vectorp expression) "Vector") - ((and pymacs-use-hash-tables - (hash-table-p expression)) - "Table") - ((bufferp expression) "Buffer") - ((pymacs-proper-list-p expression) "List") - (t "Lisp")))) - (princ class) - (princ "(") - (princ (pymacs-allocate-lisp expression)) - (princ ")"))))) - -;;; Communication protocol. - -(defvar pymacs-transit-buffer nil - "Communication buffer between Emacs and Python.") - -;; The principle behind the communication protocol is that it is easier to -;; generate than parse, and that each language already has its own parser. -;; So, the Emacs side generates Python text for the Python side to interpret, -;; while the Python side generates Lisp text for the Lisp side to interpret. -;; About nothing but expressions are transmitted, which are evaluated on -;; arrival. The pseudo `reply' function is meant to signal the final result -;; of a series of exchanges following a request, while the pseudo `error' -;; function is meant to explain why an exchange could not have been completed. - -;; The protocol itself is rather simple, and contains human readable text -;; only. A message starts at the beginning of a line in the communication -;; buffer, either with `>' for the Lisp to Python direction, or `<' for the -;; Python to Lisp direction. This is followed by a decimal number giving the -;; length of the message text, a TAB character, and the message text itself. -;; Message direction alternates systematically between messages, it never -;; occurs that two successive messages are sent in the same direction. The -;; first message is received from the Python side, it is `(version VERSION)'. - -(defun pymacs-start-services () - ;; This function gets called automatically, as needed. - (let ((buffer (get-buffer-create "*Pymacs*"))) - (with-current-buffer buffer - ;; Erase the buffer in case some previous incarnation of the - ;; Pymacs helper died. Otherwise, the "(goto-char (point-min))" - ;; below might not find the proper synchronising reply and later - ;; trigger a spurious "Protocol error" diagnostic. - (erase-buffer) - (buffer-disable-undo) - (pymacs-set-buffer-multibyte nil) - (set-buffer-file-coding-system 'raw-text) - (save-match-data - ;; Launch the Pymacs helper. - (let ((process - (apply 'start-process "pymacs" buffer - (let ((python (getenv "PYMACS_PYTHON"))) - (if (or (null python) (equal python "")) - pymacs-python-command - python)) - "-c" (concat "import sys;" - " from Pymacs import main;" - " main(*sys.argv[1:])") - (append - (and (>= emacs-major-version 24) '("-f")) - (mapcar 'expand-file-name pymacs-load-path))))) - (pymacs-kill-without-query process) - ;; Receive the synchronising reply. - (while (progn - (goto-char (point-min)) - (not (re-search-forward "<\\([0-9]+\\)\t" nil t))) - (unless (accept-process-output process pymacs-timeout-at-start) - (pymacs-report-error - "Pymacs helper did not start within %d seconds" - pymacs-timeout-at-start))) - (let ((marker (process-mark process)) - (limit-position (+ (match-end 0) - (string-to-number (match-string 1))))) - (while (< (marker-position marker) limit-position) - (unless (accept-process-output process pymacs-timeout-at-start) - (pymacs-report-error - "Pymacs helper probably was interrupted at start"))))) - ;; Check that synchronisation occurred. - (goto-char (match-end 0)) - (let ((reply (read (current-buffer)))) - (if (and (pymacs-proper-list-p reply) - (= (length reply) 2) - (eq (car reply) 'version)) - (unless (string-equal (cadr reply) "0.25") - (pymacs-report-error - "Pymacs Lisp version is 0.25, Python is %s" - (cadr reply))) - (pymacs-report-error "Pymacs got an invalid initial reply"))))) - (if (not pymacs-use-hash-tables) - (setq pymacs-weak-hash t) - (when pymacs-used-ids - ;; A previous Pymacs session occurred in this Emacs session, - ;; some IDs hang around which do not correspond to anything on - ;; the Python side. Python should not recycle such IDs for - ;; new objects. - (let ((pymacs-transit-buffer buffer) - (pymacs-forget-mutability t) - (pymacs-gc-inhibit t)) - (pymacs-call "zombie_python" pymacs-used-ids)) - (setq pymacs-used-ids nil)) - (setq pymacs-weak-hash (make-hash-table :weakness 'value)) - (if (boundp 'post-gc-hook) - (add-hook 'post-gc-hook 'pymacs-schedule-gc) - (setq pymacs-gc-timer (run-at-time 20 20 'pymacs-schedule-gc)))) - ;; If nothing failed, only then declare that Pymacs has started! - (setq pymacs-transit-buffer buffer) - (let ((modules pymacs-load-history)) - (setq pymacs-load-history nil) - (when (and modules (yes-or-no-p "Reload modules in previous session? ")) - (mapc (lambda (args) - ;; Be defensive in case sys.path differs - (condition-case err - (apply 'pymacs-load args) - (error - (message "%s: %s" (car err) (error-message-string err))))) - modules))))) - -(defun pymacs-terminate-services () - ;; This function is mainly provided for documentation purposes. - (interactive) - (garbage-collect) - (pymacs-garbage-collect) - (when (or (not pymacs-used-ids) - (yes-or-no-p "\ -Killing the Pymacs helper might create zombie objects. Kill? ")) - (cond ((boundp 'post-gc-hook) - (remove-hook 'post-gc-hook 'pymacs-schedule-gc)) - ((pymacs-timerp pymacs-gc-timer) - (pymacs-cancel-timer pymacs-gc-timer))) - (when (buffer-live-p pymacs-transit-buffer) - (kill-buffer pymacs-transit-buffer)) - (setq pymacs-gc-inhibit nil - pymacs-gc-timer nil - pymacs-transit-buffer nil - pymacs-lisp nil - pymacs-freed-list nil))) - -(defun pymacs-serve-until-reply (action inserter) - ;; This function builds a Python request by printing ACTION and - ;; evaluating INSERTER, which itself prints an argument. It then - ;; sends the request to the Pymacs helper, and serves all - ;; sub-requests coming from the Python side, until either a reply or - ;; an error is finally received. - (unless (and pymacs-transit-buffer - (buffer-name pymacs-transit-buffer) - (get-buffer-process pymacs-transit-buffer)) - (when pymacs-weak-hash - (unless (or (eq pymacs-auto-restart t) - (and (eq pymacs-auto-restart 'ask) - (yes-or-no-p "The Pymacs helper died. Restart it? "))) - (pymacs-report-error "There is no Pymacs helper!"))) - (pymacs-start-services)) - (when pymacs-gc-wanted - (pymacs-garbage-collect)) - (let ((inhibit-quit t) - done value) - (while (not done) - (let ((form (pymacs-round-trip action inserter))) - (setq action (car form)) - (when (eq action 'free) - (pymacs-free-lisp (cadr form)) - (setq form (cddr form) - action (car form))) - (let* ((pair (pymacs-interruptible-eval (cadr form))) - (success (cdr pair))) - (setq value (car pair)) - (cond ((eq action 'eval) - (if success - (setq action "return" - inserter `(pymacs-print-for-eval ',value)) - (setq action "raise" - inserter `(let ((pymacs-forget-mutability t)) - (pymacs-print-for-eval ,value))))) - ((eq action 'expand) - (if success - (setq action "return" - inserter `(let ((pymacs-forget-mutability t)) - (pymacs-print-for-eval ,value))) - (setq action "raise" - inserter `(let ((pymacs-forget-mutability t)) - (pymacs-print-for-eval ,value))))) - ((eq action 'return) - (if success - (setq done t) - (pymacs-report-error "%s" value))) - ((eq action 'raise) - (if success - (pymacs-report-error "Python: %s" value) - (pymacs-report-error "%s" value))) - (t (pymacs-report-error "Protocol error: %s" form)))))) - value)) - -(defun pymacs-round-trip (action inserter) - ;; This function produces a Python request by printing and - ;; evaluating INSERTER, which itself prints an argument. It sends - ;; the request to the Pymacs helper, awaits for any kind of reply, - ;; and returns it. - (with-current-buffer pymacs-transit-buffer - ;; Possibly trim the beginning of the transit buffer. - (cond ((not pymacs-trace-transit) - (erase-buffer)) - ((consp pymacs-trace-transit) - (when (> (buffer-size) (cdr pymacs-trace-transit)) - (let ((cut (- (buffer-size) (car pymacs-trace-transit)))) - (when (> cut 0) - (save-excursion - (goto-char cut) - (unless (memq (preceding-char) '(0 ?\n)) - (forward-line 1)) - (delete-region (point-min) (point)))))))) - ;; Send the request, wait for a reply, and process it. - (let* ((process (get-buffer-process pymacs-transit-buffer)) - (status (process-status process)) - (marker (process-mark process)) - (moving (= (point) marker)) - send-position reply-position reply) - (save-excursion - (save-match-data - ;; Encode request. - (setq send-position (marker-position marker)) - (let ((standard-output marker)) - (princ action) - (princ " ") - (eval inserter)) - (goto-char marker) - (unless (= (preceding-char) ?\n) - (princ "\n" marker)) - ;; Send request text. - (goto-char send-position) - (insert (format ">%d\t" (- marker send-position))) - (setq reply-position (marker-position marker)) - (process-send-region process send-position marker) - ;; Receive reply text. - (while (and (eq status 'run) - (progn - (goto-char reply-position) - (not (re-search-forward "<\\([0-9]+\\)\t" nil t)))) - (unless (accept-process-output process pymacs-timeout-at-reply) - (setq status (process-status process)))) - (when (eq status 'run) - (let ((limit-position (+ (match-end 0) - (string-to-number (match-string 1))))) - (while (and (eq status 'run) - (< (marker-position marker) limit-position)) - (unless (accept-process-output process pymacs-timeout-at-line) - (setq status (process-status process)))))) - ;; Decode reply. - (if (not (eq status 'run)) - (pymacs-report-error "Pymacs helper status is `%S'" status) - (goto-char (match-end 0)) - (setq reply (read (current-buffer)))))) - (when (and moving (not pymacs-trace-transit)) - (goto-char marker)) - reply))) - -(defun pymacs-interruptible-eval (expression) - ;; This function produces a pair (VALUE . SUCCESS) for EXPRESSION. - ;; A cautious evaluation of EXPRESSION is attempted, and any - ;; error while evaluating is caught, including Emacs quit (C-g). - ;; Any Emacs quit also gets forward as a SIGINT to the Pymacs handler. - ;; With SUCCESS being true, VALUE is the expression value. - ;; With SUCCESS being false, VALUE is an interruption diagnostic. - (condition-case info - (cons (let ((inhibit-quit nil)) (eval expression)) t) - (quit (setq quit-flag t) - (interrupt-process pymacs-transit-buffer) - (cons "*Interrupted!*" nil)) - (error (cons (prin1-to-string info) nil)))) - -(defun pymacs-proper-list-p (expression) - ;; Tell if a list is proper, id est, that it is `nil' or ends with `nil'. - (cond ((not expression)) - ((consp expression) (not (cdr (last expression)))))) - -(provide 'pymacs) -;;; pymacs.el ends here diff --git a/lisp/setup-slime-js.el b/lisp/setup-slime-js.el new file mode 100644 index 00000000..6aa02e97 --- /dev/null +++ b/lisp/setup-slime-js.el @@ -0,0 +1,124 @@ +;; Taken from https://github.com/magnars/.emacs.d/blob/master/setup-slime-js.el +;; js2-refactor is commented out + +;; Set up slime-js +;; +;; To install, see https://github.com/swank-js/swank-js/wiki/Installation +;; +;; This is what I did: +;; +;; npm install swank-js -g +;; M-x package-install slime-js +;; +;; The slime-js version in marmalade requires swank 2010.04.04, or at least +;; one prior to the breaking change in 2011. +;; +;; It also requires js2-mode, which is a good choice in either case. I highly +;; recommend this fork: +;; +;; https://github.com/mooz/js2-mode +;; +;; My settings found in this file also requires js2-refactor: +;; +;; https://github.com/magnars/js2-refactor.el +;; +;; I have included this file in init.el like so: +;; +;; (add-hook 'after-init-hook +;; #'(lambda () +;; (when (locate-library "slime-js") +;; (require 'setup-slime-js)))) +;; + +(require 'slime) +(require 'slime-js) +;(require 'js2-refactor) + +(set-default 'slime-js-target-url "http://localhost:3000") +(set-default 'slime-js-connect-url "http://localhost:8009") +(set-default 'slime-js-starting-url "/") +(set-default 'slime-js-swank-command "swank-js") +(set-default 'slime-js-swank-args '()) +(set-default 'slime-js-browser-command "open -v \"Google Chrome\"") +(set-default 'slime-js-browser-jacked-in-p nil) + +(add-hook 'js2-mode-hook (lambda () (slime-js-minor-mode 1))) + +(defun slime-js-run-swank () + "Runs the swank side of the equation." + (interactive) + (apply #'make-comint "swank-js" slime-js-swank-command nil slime-js-swank-args)) + +(defun slime-js-jack-in-node () + "Start a swank-js server and connect to it, opening a repl." + (interactive) + (slime-js-run-swank) + (sleep-for 1) + (setq slime-protocol-version 'ignore) + (slime-connect "localhost" 4005)) + +(defun slime-js-jack-in-browser () + "Start a swank-js server, connect to it, open a repl, open a browser, connect to that." + (interactive) + (slime-js-jack-in-node) + (sleep-for 2) + (slime-js-set-target-url slime-js-target-url) + (shell-command (concat slime-js-browser-command " " slime-js-connect-url slime-js-starting-url)) + (sleep-for 3) + (setq slime-remote-history nil) + (slime-js-sticky-select-remote (caadr (slime-eval '(js:list-remotes)))) + (setq slime-js-browser-jacked-in-p t) + (global-set-key [f5] 'slime-js-reload)) + +(defadvice save-buffer (after save-css-buffer activate) + (when (and slime-js-browser-jacked-in-p (eq major-mode 'css-mode)) + (slime-js-refresh-css))) + +(defun js2-eval-friendly-node-p (n) + (or (and (js2-stmt-node-p n) (not (js2-block-node-p n))) + (and (js2-function-node-p n) (js2-function-node-name n)))) + +(defun slime-js--echo-result (result &rest _) + (message result)) + +(defun slime-js--replace-with-result (replacement beg end) + (save-excursion + (goto-char beg) + (delete-char (- end beg)) + (insert replacement))) + +(defun slime-js-eval-region (beg end &optional func) + (lexical-let ((func (or func 'slime-js--echo-result)) + (beg beg) + (end end)) + (slime-flash-region beg end) + (slime-js-eval + (buffer-substring-no-properties beg end) + #'(lambda (s) (funcall func (cadr s) beg end))))) + +(defun slime-js-eval-statement (&optional func) + (let ((node (js2r--closest 'js2-eval-friendly-node-p))) + (slime-js-eval-region (js2-node-abs-pos node) + (js2-node-abs-end node) + func))) + +(defun slime-js-eval-current () + (interactive) + (if (use-region-p) + (slime-js-eval-region (point) (mark)) + (slime-js-eval-statement))) + +(defun slime-js-eval-and-replace-current () + (interactive) + (if (use-region-p) + (slime-js-eval-region (point) (mark) 'slime-js--replace-with-result) + (slime-js-eval-statement 'slime-js--replace-with-result))) + +(define-key slime-js-minor-mode-map (kbd "C-x C-e") 'slime-js-eval-current) +(define-key slime-js-minor-mode-map (kbd "C-c C-e") 'slime-js-eval-and-replace-current) + +;; Remove slime-minor-mode from mode line if diminish.el is installed +(when (boundp 'diminish) + (diminish 'slime-js-minor-mode)) + +(provide 'setup-slime-js)