]> git.donarmstrong.com Git - lilypond.git/blobdiff - guile18/ice-9/emacs.scm
Import guile-1.8 as multiple upstream tarball component
[lilypond.git] / guile18 / ice-9 / emacs.scm
diff --git a/guile18/ice-9/emacs.scm b/guile18/ice-9/emacs.scm
new file mode 100644 (file)
index 0000000..12d8228
--- /dev/null
@@ -0,0 +1,276 @@
+;;;;   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2006 Free Software Foundation, Inc.
+;;;; 
+;;;; This library is free software; you can redistribute it and/or
+;;;; modify it under the terms of the GNU Lesser General Public
+;;;; License as published by the Free Software Foundation; either
+;;;; version 2.1 of the License, or (at your option) any later version.
+;;;; 
+;;;; This library 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
+;;;; Lesser General Public License for more details.
+;;;; 
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free Software
+;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+;;;;
+;;;; The author can be reached at djurfeldt@nada.kth.se
+;;;; Mikael Djurfeldt, SANS/NADA KTH, 10044 STOCKHOLM, SWEDEN
+;;;; (I didn't write this!)
+;;;;
+\f
+
+;;; *********************************************************************
+;;; * This is the Guile side of the Emacs interface                     *
+;;; * Experimental hACK---the real version will be coming soon (almost) *
+;;; *********************************************************************
+
+;;; {Session support for Emacs}
+;;;
+
+(define-module (ice-9 emacs)
+  :use-module (ice-9 debug)
+  :use-module (ice-9 threads)
+  :use-module (ice-9 session)
+  :no-backtrace)
+
+(define emacs-escape-character #\sub)
+
+(define emacs-output-port (current-output-port))
+
+(define (make-emacs-command char)
+  (let ((cmd (list->string (list emacs-escape-character char))))
+    (lambda ()
+      (display cmd emacs-output-port))))
+
+(define enter-input-wait  (make-emacs-command #\s))
+(define exit-input-wait   (make-emacs-command #\f))
+(define enter-read-character #\r)
+(define sending-error    (make-emacs-command #\F))
+(define sending-backtrace (make-emacs-command #\B))
+(define sending-result    (make-emacs-command #\x))
+(define end-of-text      (make-emacs-command #\.))
+(define no-stack         (make-emacs-command #\S))
+(define no-source        (make-emacs-command #\R))
+
+;; {Error handling}
+;;
+
+(add-hook! before-backtrace-hook sending-backtrace)
+(add-hook! after-backtrace-hook end-of-text)
+(add-hook! before-error-hook sending-error)
+(add-hook! after-error-hook end-of-text)
+
+;; {Repl}
+;;
+
+(set-current-error-port emacs-output-port)
+
+(add-hook! before-read-hook
+          (lambda ()
+            (enter-input-wait)
+            (force-output emacs-output-port)))
+
+(add-hook! after-read-hook
+          (lambda ()
+            (exit-input-wait)
+            (force-output emacs-output-port)))
+
+;;; {Misc.}
+
+(define (make-emacs-load-port orig-port)
+  (letrec ((read-char-fn  (lambda args
+                           (let ((c (read-char orig-port)))
+                             (if (eq? c #\soh)
+                                 (throw 'end-of-chunk)
+                                 c)))))
+    
+    (make-soft-port
+     (vector #f #f #f
+            read-char-fn
+            (lambda () (close-port orig-port)))
+     "r")))
+
+(set-current-input-port (make-emacs-load-port (current-input-port)))
+
+(define (result-to-emacs exp)
+  (sending-result)
+  (write exp emacs-output-port)
+  (end-of-text)
+  (force-output emacs-output-port))
+
+(define load-acknowledge (make-emacs-command #\l))
+
+(define load-port (current-input-port))
+
+(define (flush-line port)
+  (let loop ((c (read-char port)))
+    (if (not (eq? c #\nl))
+       (loop (read-char port)))))
+
+(define whitespace-chars (list #\space #\tab #\nl #\np))
+
+(define (flush-whitespace port)
+  (catch 'end-of-chunk
+        (lambda ()
+          (let loop ((c (read-char port)))
+            (cond ((eq? c the-eof-object)
+                   (error "End of file while receiving Emacs data"))
+                  ((memq c whitespace-chars) (loop (read-char port)))
+                  ((eq? c #\;) (flush-line port) (loop (read-char port)))
+                  (else (unread-char c port))))
+          #f)
+        (lambda args
+          (read-char port) ; Read final newline
+          #t)))
+
+(define (emacs-load filename linum colnum module interactivep)
+  (define (read-and-eval! port)
+    (let ((x (read port)))
+      (if (eof-object? x)
+         (throw 'end-of-file)
+         (primitive-eval x))))
+  (set-port-filename! %%load-port filename)
+  (set-port-line! %%load-port linum)
+  (set-port-column! %%load-port colnum)
+  (lazy-catch #t
+             (lambda ()
+               (let loop ((endp (flush-whitespace %%load-port)))
+                 (if (not endp)
+                     (begin
+                       (save-module-excursion
+                        (lambda ()
+                          (if module
+                              (set-current-module (resolve-module module #f)))
+                          (let ((result
+                                 (start-stack read-and-eval!
+                                              (read-and-eval! %%load-port))))
+                            (if interactivep
+                                (result-to-emacs result)))))
+                       (loop (flush-whitespace %%load-port)))
+                     (begin
+                       (load-acknowledge)))
+                 (set-port-filename! %%load-port #f))) ;reset port filename
+             (lambda (key . args)
+               (set-port-filename! %%load-port #f)
+               (cond ((eq? key 'end-of-chunk)
+                      (fluid-set! the-last-stack #f)
+                      (set! stack-saved? #t)
+                      (scm-error 'misc-error
+                                 #f
+                                 "Incomplete expression"
+                                 '()
+                                 '()))
+                     ((eq? key 'exit))
+                     (else
+                      (save-stack 2)
+                      (catch 'end-of-chunk
+                             (lambda ()
+                               (let loop ()
+                                 (read-char %%load-port)
+                                 (loop)))
+                             (lambda args
+                               #f))
+                      (apply throw key args))))))
+
+(define (emacs-eval-request form)
+  (result-to-emacs (eval form (interaction-environment))))
+
+;;*fixme* Not necessary to use flags no-stack and no-source
+(define (get-frame-source frame)
+  (if (or (not (fluid-ref the-last-stack))
+         (>= frame (stack-length (fluid-ref the-last-stack))))
+      (begin
+       (no-stack)
+       #f)
+      (let* ((frame (stack-ref (fluid-ref the-last-stack)
+                              (frame-number->index frame)))
+            (source (frame-source frame)))
+       (or source
+           (begin (no-source)
+                  #f)))))
+
+(define (emacs-select-frame frame)
+  (let ((source (get-frame-source frame)))
+    (if source
+       (let ((fname (source-property source 'filename))
+             (line (source-property source 'line))
+             (column (source-property source 'column)))
+         (if (and fname line column)
+             (list fname line column)
+             (begin (no-source)
+                    '())))
+       '())))
+
+(define (object->string x . method)
+  (with-output-to-string
+    (lambda ()
+      ((if (null? method)
+          write
+          (car method))
+       x))))
+
+(define (format template . rest)
+  (let loop ((chars (string->list template))
+            (result '())
+            (rest rest))
+    (cond ((null? chars) (list->string (reverse result)))
+         ((char=? (car chars) #\%)
+          (loop (cddr chars)
+                (append (reverse
+                         (string->list
+                          (case (cadr chars)
+                            ((#\S) (object->string (car rest)))
+                            ((#\s) (object->string (car rest) display)))))
+                        result)
+                (cdr rest)))
+         (else (loop (cdr chars) (cons (car chars) result) rest)))))
+
+(define (error-args->string args)
+  (let ((msg (apply format (caddr args) (cadddr args))))
+    (if (symbol? (cadr args))
+       (string-append (symbol->string (cadr args))
+                      ": "
+                      msg)
+       msg)))
+
+(define (emacs-frame-eval frame form)
+  (let ((source (get-frame-source frame)))
+    (if source
+       (catch #t
+              (lambda ()
+                (list 'result
+                      (object->string
+                       (local-eval (with-input-from-string form read)
+                                   (memoized-environment source)))))
+              (lambda args
+                (list (car args)
+                      (error-args->string args))))
+       (begin
+         (no-source)
+         '()))))
+
+(define (emacs-symdoc symbol)
+  (if (or (not (module-bound? (current-module) symbol))
+         (not (procedure? (eval symbol (interaction-environment)))))
+      'nil
+      (procedure-documentation (eval symbol (interaction-environment)))))
+
+;;; A fix to get the emacs interface to work together with the module system.
+;;;
+(for-each (lambda (name value)
+           (module-define! the-root-module name value))
+         '(%%load-port
+           %%emacs-load
+           %%emacs-eval-request
+           %%emacs-select-frame
+           %%emacs-frame-eval
+           %%emacs-symdoc
+           %%apropos-internal)
+         (list load-port
+               emacs-load
+               emacs-eval-request
+               emacs-select-frame
+               emacs-frame-eval
+               emacs-symdoc
+               apropos-internal))