From 3b6e0b859262970b43672ed7c9207187b2518976 Mon Sep 17 00:00:00 2001 From: Jose Antonio Ortega Ruiz Date: Mon, 9 Mar 2009 23:52:04 +0100 Subject: Support for multiple Scheme implementations, Chapter 1. * Evaluation system is now pluggable * The rest of the system understands said pluggability * Guile provides its own implementation (geiser-guile) * The reload system is aware of the new kids on the block --- elisp/geiser-completion.el | 12 ++-- elisp/geiser-doc.el | 69 ++++++++++-------- elisp/geiser-edit.el | 2 +- elisp/geiser-eval.el | 39 +++++----- elisp/geiser-impl.el | 176 +++++++++++++++++++++++++++++++++++++++++++++ elisp/geiser-mode.el | 12 +++- elisp/geiser-popup.el | 14 ++++ elisp/geiser-repl.el | 7 +- elisp/geiser.el | 15 ++-- 9 files changed, 285 insertions(+), 61 deletions(-) create mode 100644 elisp/geiser-impl.el diff --git a/elisp/geiser-completion.el b/elisp/geiser-completion.el index e2569eb..d2991b2 100644 --- a/elisp/geiser-completion.el +++ b/elisp/geiser-completion.el @@ -181,11 +181,13 @@ terminates a current completion." (defun geiser-completion--read-module (&optional prompt default history) (let ((minibuffer-local-completion-map geiser-completion--module-minibuffer-map)) - (completing-read (or prompt "Module name: ") - (geiser-completion--module-list) - nil nil - (or default (format "%s" (or (geiser-syntax--buffer-module) "("))) - (or history geiser-completion--module-history)))) + (geiser-eval--get-module + (completing-read (or prompt "Module name: ") + (geiser-completion--module-list) + nil nil + (or default + (format "%s" (or (geiser-syntax--buffer-module) "("))) + (or history geiser-completion--module-history))))) (defun geiser--respecting-message (format &rest format-args) "Display TEXT as a message, without hiding any minibuffer contents." diff --git a/elisp/geiser-doc.el b/elisp/geiser-doc.el index 6d2eb40..af1e402 100644 --- a/elisp/geiser-doc.el +++ b/elisp/geiser-doc.el @@ -60,7 +60,7 @@ (car geiser-doc--history)) (defun geiser-doc--history-push (link) - (unless (equal link (car geiser-doc--history)) + (unless (or (null link) (equal link (car geiser-doc--history))) (let ((next (geiser-doc--history-next))) (unless (equal link next) (when next (geiser-doc--history-previous)) @@ -80,13 +80,14 @@ (ring-insert (nth 2 geiser-doc--history) (car geiser-doc--history))) (setcar geiser-doc--history (ring-remove (nth 1 geiser-doc--history) 0)))) -(defvar geiser-doc--history (geiser-doc--make-history)) +(defvar geiser-doc--history nil) +(setq geiser-doc--history (geiser-doc--make-history)) ;;; Links -(defsubst geiser-doc--make-link (target module) - (list target module)) +(defsubst geiser-doc--make-link (target module impl) + (list target module impl)) (defsubst geiser-doc--link-target (link) (nth 0 link)) @@ -94,13 +95,19 @@ (defsubst geiser-doc--link-module (link) (nth 1 link)) +(defsubst geiser-doc--link-impl (link) + (nth 2 link)) + (defun geiser-doc--follow-link (link) (let ((target (geiser-doc--link-target link)) - (module (geiser-doc--link-module link))) - (when target - (if (symbolp target) - (geiser-doc-symbol target module) - (geiser-doc-module (format "%s" target)))))) + (module (geiser-doc--link-module link)) + (impl (or (geiser-doc--link-impl link) + (geiser-impl--default-implementation)))) + (when (and (or target module) impl) + (with--geiser-implementation impl + `(lambda () (if (null ',target) + (geiser-doc-module ',module ',impl) + (geiser-doc-symbol ',target ',module ',impl))))))) (defun geiser-doc--button-action (button) (let ((link (button-get button 'geiser-link))) @@ -111,8 +118,8 @@ 'face 'geiser-font-lock-doc-link 'follow-link t) -(defun geiser-doc--insert-button (target module) - (let ((link (geiser-doc--make-link target module)) +(defun geiser-doc--insert-button (target module impl) + (let ((link (geiser-doc--make-link target module impl)) (text (format "%s" target)) (help (if module (format "%s in module %s" target module) ""))) (insert-text-button text @@ -134,13 +141,13 @@ (put-text-property p (point) 'face 'geiser-font-lock-doc-title) (newline))) -(defun geiser-doc--insert-list (title lst module) +(defun geiser-doc--insert-list (title lst module impl) (when lst (geiser-doc--insert-title title) (newline) (dolist (w lst) (insert (format "\t- ")) - (geiser-doc--insert-button w module) + (geiser-doc--insert-button w module impl) (newline)) (newline))) @@ -154,11 +161,11 @@ (geiser-eval--send/result `(:eval ((:ge symbol-documentation) ',symbol) ,module))) (defun geiser-doc--get-module-children (module) - (geiser-eval--send/result `(:eval ((:ge module-children) (quote (:scm ,module)))))) + (geiser-eval--send/result `(:eval ((:ge module-children) (:module ,module))))) -(defun geiser-doc-symbol (symbol &optional module) - (let* ((module (or module - (geiser-syntax--buffer-module))) +(defun geiser-doc-symbol (symbol &optional module impl) + (let* ((module (or module (geiser-eval--get-module))) + (impl (or impl geiser-impl--implementation)) (ds (geiser-doc--get-docstring symbol module))) (if (or (not ds) (not (listp ds))) (message "No documentation available for '%s'" symbol) @@ -167,9 +174,10 @@ (geiser-doc--insert-title (cdr (assoc 'signature ds))) (newline) (insert (or (cdr (assoc 'docstring ds)) "")) - (goto-line (point-min))) - (setq geiser-doc--buffer-link - (geiser-doc--history-push (geiser-doc--make-link symbol module))) + (goto-line (point-min)) + (setq geiser-doc--buffer-link + (geiser-doc--history-push + (geiser-doc--make-link symbol module impl)))) (geiser-doc--pop-to-buffer)))) (defun geiser-doc-symbol-at-point (&optional arg) @@ -181,11 +189,11 @@ With prefix argument, ask for symbol (with completion)." (when symbol (geiser-doc-symbol symbol)))) -(defun geiser-doc-module (module) +(defun geiser-doc-module (module &optional impl) "Display information about a given module." (interactive (list (geiser-completion--read-module))) (let ((children (geiser-doc--get-module-children module)) - (mod-sym (car (read-from-string module)))) + (impl (or impl geiser-impl--implementation))) (if (not children) (message "No info available for %s" module) (geiser-doc--with-buffer @@ -194,17 +202,19 @@ With prefix argument, ask for symbol (with completion)." (newline) (geiser-doc--insert-list "Procedures:" (cdr (assoc 'procs children)) - mod-sym) + module + impl) (geiser-doc--insert-list "Variables:" (cdr (assoc 'vars children)) - mod-sym) + module + impl) (geiser-doc--insert-list "Submodules:" (cdr (assoc 'modules children)) - mod-sym) - (goto-char (point-min))) - (setq geiser-doc--buffer-link - (geiser-doc--history-push (geiser-doc--make-link (car (read-from-string module)) - nil))) + module + impl) + (goto-char (point-min)) + (setq geiser-doc--buffer-link + (geiser-doc--history-push (geiser-doc--make-link nil module impl)))) (geiser-doc--pop-to-buffer)))) (defun geiser-doc-next (&optional forget-current) @@ -279,6 +289,7 @@ With prefix, the current page is deleted from history." (geiser-popup--define doc "*Geiser documentation*" geiser-doc-mode) + (provide 'geiser-doc) ;;; geiser-doc.el ends here diff --git a/elisp/geiser-edit.el b/elisp/geiser-edit.el index e5c30eb..6938b60 100644 --- a/elisp/geiser-edit.el +++ b/elisp/geiser-edit.el @@ -127,7 +127,7 @@ With prefix, asks for the symbol to edit." (defun geiser-edit-module (module) "Asks for a module and opens it in a new buffer." (interactive (list (geiser-completion--read-module))) - (let ((cmd `(:eval ((:ge module-location) (quote (:scm ,module)))))) + (let ((cmd `(:eval ((:ge module-location) ,module)))) (geiser-edit--try-edit module (geiser-eval--send/wait cmd)))) diff --git a/elisp/geiser-eval.el b/elisp/geiser-eval.el index 3e5e7aa..8574cdc 100644 --- a/elisp/geiser-eval.el +++ b/elisp/geiser-eval.el @@ -34,14 +34,22 @@ ;;; Plug-able functions: (make-variable-buffer-local - (defvar geiser-eval--current-module-function 'geiser-syntax--buffer-module)) + (defvar geiser-eval--get-module-function 'geiser-syntax--buffer-module + "Function used to obtain the module for current buffer. It +takes an optional argument, for cases where we want to force its value.")) -(defsubst geiser-eval--current-module-function (fun) - (setq geiser-eval--current-module-function fun)) +(defsubst geiser-eval--get-module (&optional module) + (and geiser-eval--get-module-function + (funcall geiser-eval--get-module-function module))) -(defsubst geiser-eval--current-module () - (and geiser-eval--current-module-function - (funcall geiser-eval--current-module-function))) +(make-variable-buffer-local + (defvar geiser-eval--geiser-procedure-function nil + "Translate a bare procedure symbol to one executable in Guile's +context. Return NULL for unsupported ones; at the very least, +EVAL, COMPILE, LOAD-FILE and COMPILE-FILE should be supported.")) + +(defsubst geiser-eval--form (proc) + (funcall geiser-eval--geiser-procedure-function proc)) ;;; Code formatting: @@ -64,28 +72,27 @@ (defsubst geiser-eval--eval (code) (geiser-eval--scheme-str - `((@ (geiser emacs) ge:eval) (quote ,(nth 0 code)) (:module ,(nth 1 code))))) + `(,(geiser-eval--form 'eval) (quote ,(nth 0 code)) (:module ,(nth 1 code))))) (defsubst geiser-eval--comp (code) (geiser-eval--scheme-str - `((@ (geiser emacs) ge:compile) (quote ,(nth 0 code)) (:module ,(nth 1 code))))) + `(,(geiser-eval--form 'compile (quote ,(nth 0 code)) (:module ,(nth 1 code)))))) (defsubst geiser-eval--load-file (file) - (geiser-eval--scheme-str `((@ (geiser emacs) ge:load-file) ,file))) + (geiser-eval--scheme-str `(,(geiser-eval--form 'load-file) ,file))) (defsubst geiser-eval--comp-file (file) - (geiser-eval--scheme-str `((@ (geiser emacs) ge:compile-file) ,file))) + (geiser-eval--scheme-str `(,(geiser-eval--form 'compile-file) ,file))) (defsubst geiser-eval--module (code) (geiser-eval--scheme-str - (cond ((or (eq code '(())) (null code)) - `(quote ,(or (geiser-eval--current-module) :f))) - ((listp code) `(quote ,code)) - ((stringp code) (:scm code)) - (t (error "Invalid module spec: %S" code))))) + (cond ((or (null code) (eq code :t) (eq code :buffer)) + (list 'quote (funcall geiser-eval--get-module-function))) + ((or (eq code :repl) (eq code :f)) :f) + (t (list 'quote (funcall geiser-eval--get-module-function code)))))) (defsubst geiser-eval--ge (proc) - (format "(@ (geiser emacs) ge:%s)" proc)) + (geiser-eval--scheme-str (geiser-eval--form proc))) ;;; Code sending: diff --git a/elisp/geiser-impl.el b/elisp/geiser-impl.el new file mode 100644 index 0000000..741002a --- /dev/null +++ b/elisp/geiser-impl.el @@ -0,0 +1,176 @@ +;; geiser-impl.el -- generic support for scheme implementations + +;; Copyright (C) 2009 Jose Antonio Ortega Ruiz + +;; Author: Jose Antonio Ortega Ruiz +;; Start date: Sat Mar 07, 2009 23:32 + +;; 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 3 of the License, 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 this program. If not, see . + +;;; Comentary: + +;; Functions to handle setup of Scheme implementations supported by +;; Geiser. + +;;; Code: + +(require 'geiser-eval) +(require 'geiser-base) + + +;;; Customization: + +(defgroup geiser-impl nil + "Generic support for multiple Scheme implementations." + :group 'geiser) + +(defcustom geiser-impl-default-implementation 'guile + "Symbol naming the default Scheme implementation." + :type 'symbol + :group 'geiser-impl) + + +;;; Registering implementations: + +(defvar geiser-impl--impls nil) + +(defun geiser-impl--register (impl) + (add-to-list 'geiser-impl--impls impl)) + +(defun geiser-impl--unregister (impl) + (remove impl geiser-impl--impls)) + +(defvar geiser-impl--default-implementation + geiser-impl-default-implementation) + +(defun geiser-impl--default-implementation (&optional new) + (when new (setq geiser-impl--default-implementation new)) + (or geiser-impl--default-implementation + geiser-impl-default-implementation + (car geiser-impl--impls))) + + +;;; Installing Scheme implementations: + +(make-local-variable + (defvar geiser-impl--implementation nil)) + +(defsubst geiser-impl--impl-feature (impl) + (intern (format "geiser-%s" impl))) + +(defun geiser-impl--set-buffer-implementation (&optional impl) + (let ((impl (or impl + (geiser-impl--guess) + (intern (read-string "Scheme implementation: "))))) + (require (geiser-impl--impl-feature impl)) + (setq geiser-impl--implementation impl) + (geiser-impl--install-eval impl) + (geiser-impl--register impl))) + + +(defsubst geiser-impl--sym (imp name) + (intern (format "geiser-%s-%s" imp name))) + +(defsubst geiser-impl--boundp (imp name) + (boundp (geiser-impl--sym imp name))) + +(defsubst geiser-impl--fboundp (imp name) + (fboundp (geiser-impl--sym imp name))) + +(defun geiser-impl--value (imp name &optional fun) + (let ((sym (geiser-impl--sym imp name))) + (unless (or (and (not fun) (boundp sym)) + (and fun (fboundp sym))) + (error "Unbound %s '%s' in Geiser Scheme implementation %s" + (if fun "function" "variable") sym imp)) + (if fun (symbol-function sym) (symbol-value sym)))) + +(defsubst geiser-impl--call-if-bound (imp name &rest args) + (when (geiser-impl--fboundp imp name) + (apply (geiser-impl--value imp name t) args))) + +(defun geiser-impl--install-eval (imp) + (setq geiser-eval--get-module-function + (geiser-impl--sym imp "get-module") + geiser-eval--geiser-procedure-function + (geiser-impl--sym imp "geiser-procedure"))) + + +;;; Evaluating Elisp in a given implementation context: + +(defun with--geiser-implementation (imp thunk) + (let ((geiser-impl--implementation imp) + (geiser-eval--get-module-function + (geiser-impl--sym imp "get-module")) + (geiser-eval--geiser-procedure-function + (geiser-impl--sym imp "geiser-procedure"))) + (funcall thunk))) + +(put 'with--geiser-implementation 'lisp-indent-function 1) + + +;;; Default evaluation environment: + +(defun geiser-impl-module (&optional module) + (geiser-impl--call-if-bound (geiser-impl--default-implementation) + "get-module" + module)) +(set-default 'geiser-eval--get-module-function 'geiser-impl-module) + +(defun geiser-impl-geiser-procedure (proc) + (geiser-impl--call-if-bound (geiser-impl--default-implementation) + "geiser-procedure" + proc)) +(set-default 'geiser-eval--geiser-procedure-function 'geiser-impl-geiser-procedure) + + +;;; Access to implementation specific execution parameters: + +(defsubst geiser-impl--binary (impl) + (or (geiser-impl--call-if-bound impl "binary") + (geiser-impl--value imp "binary"))) + +(defsubst geiser-impl--parameters (impl) + (or (geiser-impl--call-if-bound impl "parameters") + (ignore-errors (geiser-impl--value imp "parameters")))) + +(defsubst geiser-impl--prompt-regexp (impl) + (or (geiser-impl--call-if-bound impl "prompt-regexp") + (geiser-impl--value imp "prompt-regexp"))) + + +;;; Access to implementation guessing function: + +(defun geiser-impl--guess () + (catch 'impl + (dolist (impl geiser-impl--impls) + (when (geiser-impl--call-if-bound impl "guess") + (throw 'impl impl))) + (geiser-impl--default-implementation))) + + +;;; Unload support + +(defun geiser-impl-unload-function () + (dolist (imp (mapcar 'geiser-impl--impl-feature geiser-impl--impls)) + (when (featurep imp) (unload-feature imp))) + t) + +(defun geiser-impl--reload-implementations (impls) + (dolist (impl impls) + (load-library (format "geiser-%s" impl)))) + + +(provide 'geiser-impl) +;;; geiser-impl.el ends here diff --git a/elisp/geiser-mode.el b/elisp/geiser-mode.el index 4bb2dcf..640a7e9 100644 --- a/elisp/geiser-mode.el +++ b/elisp/geiser-mode.el @@ -31,6 +31,7 @@ (require 'geiser-edit) (require 'geiser-autodoc) (require 'geiser-debug) +(require 'geiser-impl) (require 'geiser-eval) (require 'geiser-repl) (require 'geiser-popup) @@ -153,6 +154,7 @@ interacting with the Geiser REPL is at your disposal. :lighter geiser-mode-string :group 'geiser-mode :keymap geiser-mode-map + (when geiser-mode (geiser-impl--set-buffer-implementation)) (setq geiser-autodoc-mode-string "/A") (setq geiser-smart-tab-mode-string "/T") (when geiser-mode-autodoc-p (geiser-autodoc-mode geiser-mode)) @@ -212,9 +214,17 @@ interacting with the Geiser REPL is at your disposal. (dolist (buffer (buffer-list)) (when (buffer-live-p buffer) (set-buffer buffer) - (when geiser-mode (push buffer buffers)))) + (when geiser-mode + (push (cons buffer geiser-impl--implementation) buffers)))) buffers)) +(defun geiser-mode--restore (buffers) + (dolist (b buffers) + (when (buffer-live-p (car b)) + (set-buffer (car b)) + (geiser-mode 1) + (when (cdr b) (geiser-impl--set-buffer-implementation (cdr b)))))) + (provide 'geiser-mode) ;;; geiser-mode.el ends here diff --git a/elisp/geiser-popup.el b/elisp/geiser-popup.el index d412029..486d1d8 100644 --- a/elisp/geiser-popup.el +++ b/elisp/geiser-popup.el @@ -58,12 +58,18 @@ '(("q" . geiser-popup--quit)) (setq buffer-read-only t)) + +;;; Support for defining popup buffers and accessors: + +(defvar geiser-popup--registry nil) + (defmacro geiser-popup--define (base name mode) (let ((get-buff (intern (format "geiser-%s--buffer" base))) (pop-buff (intern (format "geiser-%s--pop-to-buffer" base))) (with-macro (intern (format "geiser-%s--with-buffer" base))) (method (make-symbol "method"))) `(progn + (add-to-list 'geiser-popup--registry ,name) (defun ,get-buff () (or (get-buffer ,name) (with-current-buffer (get-buffer-create ,name) @@ -83,6 +89,14 @@ (put 'geiser-popup--define 'lisp-indent-function 1) + +;;; Reload support: + +(defun geiser-popup-unload-function () + (dolist (name geiser-popup--registry) + (when (buffer-live-p (get-buffer name)) + (kill-buffer name)))) + (provide 'geiser-popup) ;;; geiser-popup.el ends here diff --git a/elisp/geiser-repl.el b/elisp/geiser-repl.el index a5f424b..7232b99 100644 --- a/elisp/geiser-repl.el +++ b/elisp/geiser-repl.el @@ -26,6 +26,7 @@ (require 'geiser-autodoc) (require 'geiser-edit) +(require 'geiser-impl) (require 'geiser-eval) (require 'geiser-connection) (require 'geiser-custom) @@ -193,6 +194,8 @@ REPL buffer." (when (not (eq (char-after (point)) ?\()) (skip-syntax-forward "^(" p)))) +(defun geiser-repl--module-function (&optional ignore) :f) + (define-derived-mode geiser-repl-mode comint-mode "Geiser REPL" "Major mode for interacting with an inferior Guile repl process. \\{geiser-repl-mode-map}" @@ -203,7 +206,9 @@ REPL buffer." (set (make-local-variable 'beginning-of-defun-function) 'geiser-repl--beginning-of-defun) (set-syntax-table scheme-mode-syntax-table) - (geiser-eval--current-module-function nil) + ;;; TODO: fix this call when we add support to multiple implementations + (geiser-impl--set-buffer-implementation) + (setq geiser-eval--get-module-function 'geiser-repl--module-function) (when geiser-repl-autodoc-p (geiser-autodoc-mode 1))) (define-key geiser-repl-mode-map "\C-cz" 'run-guile) diff --git a/elisp/geiser.el b/elisp/geiser.el index 7415390..f827864 100644 --- a/elisp/geiser.el +++ b/elisp/geiser.el @@ -92,6 +92,7 @@ geiser-autodoc geiser-compile geiser-debug + geiser-impl geiser-eval geiser-connection geiser-syntax @@ -122,6 +123,7 @@ loaded." geiser-root-dir)) geiser-root-dir)) (geiser-main-file (expand-file-name "elisp/geiser.el" dir)) + (impls (and (featurep 'geiser-impl) geiser-impl--impls)) (repl (and (featurep 'geiser-repl) (geiser-repl--live-p))) (buffers (and (featurep 'geiser-mode) (geiser-mode--buffers)))) (unless (file-exists-p geiser-main-file) @@ -130,14 +132,11 @@ loaded." (geiser-unload) (load-file geiser-main-file) (geiser-setup) - (when repl - (load-library "geiser-repl") - (geiser 'repl)) - (when buffers - (load-library "geiser-mode") - (dolist (b buffers) - (set-buffer b) - (geiser-mode 1))) + (dolist (feature (geiser--features-list)) + (load-library (format "%s" feature))) + (when impls (geiser-impl--reload-implementations impls)) + (when repl (geiser 'repl)) + (when buffers (geiser-mode--restore buffers)) (message "Geiser reloaded!"))) -- cgit v1.2.3