--- gforth/gforth.el 2003/02/08 15:28:39 1.65 +++ gforth/gforth.el 2007/08/17 22:00:14 1.73 @@ -1,6 +1,6 @@ ;;; gforth.el --- major mode for editing (G)Forth sources -;; Copyright (C) 1995,1996,1997,1998,2000,2001 Free Software Foundation, Inc. +;; Copyright (C) 1995,1996,1997,1998,2000,2001,2003,2004 Free Software Foundation, Inc. ;; This file is part of Gforth. @@ -33,7 +33,9 @@ ;; Changes by David ;; Added a syntax-hilighting engine, rewrote auto-indentation engine. ;; Added support for block files. -;; Tested with Emacs 19.34, 20.5, 21.1 and XEmacs 21.1 +;; Replaced forth-process code with comint-based implementation. + +;; Tested with Emacs 19.34, 20.5, 21 and XEmacs 21 ;;------------------------------------------------------------------- ;; A Forth indentation, documentation search and interaction library @@ -59,13 +61,13 @@ (progn (string-match "^[0-9]+" emacs-version) (string-to-int (match-string 0 emacs-version))))) +;; Code ripped from `subr.el' for compatability with Emacs versions +;; prior to 20.1 +(eval-when-compile (defun forth-emacs-older (major minor) (or (< emacs-major-version major) (and (= emacs-major-version major) (< emacs-minor-version minor)))) -;; Code ripped from `subr.el' for compatability with Emacs versions -;; prior to 20.1 -(eval-when-compile (if (forth-emacs-older 20 1) (progn (defmacro when (cond &rest body) @@ -77,8 +79,9 @@ ;; `no-error' argument of require not supported in Emacs versions ;; prior to 20.4 :-( +(eval-and-compile (defun forth-require (feature) - (condition-case err (require feature) (error nil))) + (condition-case err (require feature) (error nil)))) (require 'font-lock) @@ -113,6 +116,9 @@ ; todo: ; +; screen-height existiert nicht in XEmacs, frame-height ersetzen? +; + ; Wörter ordentlich hilighten, die nicht auf Whitespace beginnen ( ..)IF ; -- mit aktueller Konzeption nicht möglich?? ; @@ -268,7 +274,8 @@ PARSED-TYPE specifies what kind of text "case" "of" "?dup-if" "?dup-0=-if" "then" "endif" "until" "repeat" "again" "leave" "?leave" "loop" "+loop" "-loop" "next" "endcase" "endof" "else" "while" "try" - "recover" "endtry" "assert(" "assert0(" "assert1(" "assert2(" + "recover" "endtry" "iferror" "restore" "endtry-iferror" + "assert(" "assert0(" "assert1(" "assert2(" "assert3(" ")" "" "compilation>") compile-only (font-lock-keyword-face . 2)) @@ -410,7 +417,7 @@ INDENT1 and INDENT2 are indentation spec (setq forth-indent-words '((("if" "begin" "do" "?do" "+do" "-do" "u+do" - "u-do" "?dup-if" "?dup-0=-if" "case" "of" "try" + "u-do" "?dup-if" "?dup-0=-if" "case" "of" "try" "iferror" "[if]" "[ifdef]" "[ifundef]" "[begin]" "[for]" "[do]" "[?do]") (0 . 2) (0 . 2)) ((":" ":noname" "code" "struct" "m:" ":m" "class" "interface") @@ -426,11 +433,11 @@ INDENT1 and INDENT2 are indentation spec (-2 . 0) (0 . -2) non-immediate) (("protected" "public" "how:") (-1 . 1) (0 . 0) non-immediate) (("+loop" "-loop" "until") (-2 . 0) (-2 . 0)) - (("else" "recover" "[else]") (-2 . 2) (0 . 0)) + (("else" "recover" "restore" "endtry-iferror" "[else]") + (-2 . 2) (0 . 0)) (("does>") (-1 . 1) (0 . 0)) (("while" "[while]") (-2 . 4) (0 . 2)) - (("repeat" "[repeat]") (-4 . 0) (0 . -4)) - (("\\g") (-2 . 2) (0 . 0)))) + (("repeat" "[repeat]") (-4 . 0) (0 . -4)))) (defvar forth-local-indent-words nil "List of Forth words to prepend to `forth-indent-words', when a forth-mode @@ -822,8 +829,8 @@ Used for imenu index generation.") ;; Return the column increment, that the current line of forth code does to ;; the current or following lines. `which' specifies which indentation values -;; to use. 0 means the indentation of following lines relative to current -;; line, 1 means the indentation of the current line relative to the previous +;; to use. 1 means the indentation of following lines relative to current +;; line, 0 means the indentation of the current line relative to the previous ;; line. Return `nil', if there are no indentation words on the current line. (defun forth-get-column-incr (which) (save-excursion @@ -1086,8 +1093,6 @@ exceeds 64 characters." ;(define-key forth-mode-map "\M-\C-x" 'compile) (define-key forth-mode-map "\C-x\\" 'comment-region) (define-key forth-mode-map "\C-x~" 'forth-remove-tracers) -(define-key forth-mode-map "\e\C-m" 'forth-send-paragraph) -(define-key forth-mode-map "\eo" 'forth-send-buffer) (define-key forth-mode-map "\C-x\C-m" 'forth-split) (define-key forth-mode-map "\e " 'forth-reload) (define-key forth-mode-map "\t" 'forth-indent-command) @@ -1104,8 +1109,7 @@ exceeds 64 characters." (unless (memq forth-info-lookup info-lookup-alist) (setq info-lookup-alist (cons forth-info-lookup info-lookup-alist))) ;; in X-Emacs C-h C-i is by default bound to Info-query - (define-key forth-mode-map "\C-h\C-i" 'info-lookup-symbol)) - + (define-key forth-mode-map [?\C-h ?\C-i] 'info-lookup-symbol)) ;; (info-lookup-add-help ;; :topic 'symbol @@ -1120,8 +1124,8 @@ exceeds 64 characters." (defun forth-find-tag (tagname &optional next-p regexp-p) (interactive (find-tag-interactive "Find tag: ")) (unless (or regexp-p next-p) - (setq tagname (concat "\\(^\\|\\s-\\)\\(" (regexp-quote tagname) - "\\)\\(\\s-\\|$\\)"))) + (setq tagname (concat "\\(^\\|\\s-+\\)\\(" (regexp-quote tagname) + "\\)\\s-*\x7f"))) (switch-to-buffer (find-tag-noselect tagname next-p t))) @@ -1195,31 +1199,6 @@ are delimited with \\ and newline. Parag only. Block files are autodetected, when read, and converted to normal stream source format. See also `forth-block-mode'. \\{forth-mode-map} - Forth-split - Positions the current buffer on top and a forth-interaction window - below. The window size is controlled by the forth-percent-height - variable (see below). - Forth-reload - Reloads the forth library and restarts the forth process. - Forth-send-buffer - Sends the current buffer, in text representation, as input to the - forth process. - Forth-send-paragraph - Sends the previous or the current paragraph to the forth-process. - Note that the cursor only need to be with in the paragraph to be sent. - forth-documentation - Search for documentation of forward adjacent to cursor. Note! To use - this mode you have to add a line, to your .emacs file, defining the - directories to search through for documentation files (se variable - forth-help-load-path below) e.g. (setq forth-help-load-path '(nil)). - -Variables controlling interaction and startup - forth-percent-height - Tells split how high to make the edit portion, in percent of the - current screen height. - forth-program-name - Tells the library which program name to execute in the interation - window. Variables controlling syntax hilighting/recognition of parsed text: `forth-words' @@ -1282,17 +1261,15 @@ Variables controlling block-file editing length that can be stored into a block file). This variable defaults to t for `forth-block-mode' and to nil for `forth-mode'. -Variables controling documentation search - forth-help-load-path - List of directories to search through to find *.doc - (forth-help-file-suffix) files. Nil means current default directory. - The specified directories must contain at least one .doc file. If it - does not and you still want the load-path to scan that directory, create - an empty file dummy.doc. - forth-help-file-suffix - The file names to search for in each directory specified by - forth-help-load-path. Defaulted to '*.doc'. -" +Variables controlling interaction with the Forth-process (also see +`run-forth'): + forth-program-name + Program invoked by the `run-forth' command (including arguments). + inferior-forth-mode-hook + Hook for customising inferior-forth-mode. + forth-compile-command + Default command to execute on `compile'. +" (interactive) (kill-all-local-variables) (use-local-map forth-mode-map) @@ -1392,311 +1369,12 @@ programmers who tend to fill code won't (interactive) (query-replace-regexp "\\(~~ \\| ~~$\\)" "" nil)) -(defvar forth-program-name "gforth" - "*Program invoked by the `run-forth' command.") - -(defvar forth-band-name nil - "*Band loaded by the `run-forth' command.") - -(defvar forth-program-arguments nil - "*Arguments passed to the Forth program by the `run-forth' command.") - -(defun run-forth (command-line) - "Run an inferior Forth process. Output goes to the buffer `*forth*'. -With argument, asks for a command line. Split up screen and run forth -in the lower portion. The current-buffer when called will stay in the -upper portion of the screen, and all other windows are deleted. -Call run-forth again to make the *forth* buffer appear in the lower -part of the screen." - (interactive - (list (let ((default - (or forth-process-command-line - (forth-default-command-line)))) - (if current-prefix-arg - (read-string "Run Forth: " default) - default)))) - (setq forth-process-command-line command-line) - (forth-start-process command-line) - (forth-split) - (forth-set-runlight forth-runlight:input)) - -(defun run-forth-if-not () - (if (not (forth-process-running-p)) - (run-forth forth-program-name))) - -(defun reset-forth () - "Reset the Forth process." - (interactive) - (let ((process (get-process forth-program-name))) - (cond ((or (not process) - (not (eq (process-status process) 'run)) - (yes-or-no-p -"The Forth process is running, are you SURE you want to reset it? ")) - (message "Resetting Forth process...") - (forth-reload) - (message "Resetting Forth process...done"))))) - -(defun forth-default-command-line () - (concat forth-program-name - (if forth-program-arguments - (concat " " forth-program-arguments) - ""))) - -;;;; Internal Variables - -(defvar forth-process-command-line nil - "Command used to start the most recent Forth process.") - -(defvar forth-previous-send "" - "Most recent expression transmitted to the Forth process.") - -(defvar forth-process-filter-queue '() - "Queue used to synchronize filter actions properly.") - -(defvar forth-prompt "ok" - "The current forth prompt string.") - -(defvar forth-start-hook nil - "If non-nil, a procedure to call when the Forth process is started. -When called, the current buffer will be the Forth process-buffer.") - -(defvar forth-signal-death-message nil - "If non-nil, causes a message to be generated when the Forth process dies.") - -(defvar forth-percent-height 50 - "Tells run-forth how high the upper window should be in percent.") - -(defconst forth-runlight:input ?I - "The character displayed when the Forth process is waiting for input.") - -(defvar forth-mode-string "" - "String displayed in the mode line when the Forth process is running.") - -;;;; Evaluation Commands - -(defun forth-send-string (&rest strings) - "Send the string arguments to the Forth process. -The strings are concatenated and terminated by a newline." - (cond ((forth-process-running-p) - (forth-send-string-1 strings)) - ((yes-or-no-p "The Forth process has died. Reset it? ") - (reset-forth) - (goto-char (point-max)) - (forth-send-string-1 strings)))) - -(defun forth-send-string-1 (strings) - (let ((string (apply 'concat strings))) - (forth-send-string-2 string))) - -(defun forth-send-string-2 (string) - (let ((process (get-process forth-program-name))) - (if (not (eq (current-buffer) (get-buffer forth-program-name))) - (progn - (forth-process-filter-output string) - (forth-process-filter:finish))) - (send-string process (concat string "\n")) - (if (eq (current-buffer) (process-buffer process)) - (set-marker (process-mark process) (point))))) - - -(defun forth-send-region (start end) - "Send the current region to the Forth process. -The region is sent terminated by a newline." - (interactive "r") - (let ((process (get-process forth-program-name))) - (if (and process (eq (current-buffer) (process-buffer process))) - (progn (goto-char end) - (set-marker (process-mark process) end)))) - (forth-send-string "\n" (buffer-substring start end) "\n")) - -(defun forth-end-of-paragraph () - (if (looking-at "[\t\n ]+") (skip-chars-backward "\t\n ")) - (if (not (re-search-forward "\n[ \t]*\n" nil t)) - (goto-char (point-max)))) - -(defun forth-send-paragraph () - "Send the current or the previous paragraph to the Forth process" - (interactive) - (let (end) - (save-excursion - (forth-end-of-paragraph) - (skip-chars-backward "\t\n ") - (setq end (point)) - (if (re-search-backward "\n[ \t]*\n" nil t) - (setq start (point)) - (goto-char (point-min))) - (skip-chars-forward "\t\n ") - (forth-send-region (point) end)))) - -(defun forth-send-buffer () - "Send the current buffer to the Forth process." - (interactive) - (if (eq (current-buffer) (forth-process-buffer)) - (error "Not allowed to send this buffer's contents to Forth")) - (forth-send-region (point-min) (point-max))) - - -;;;; Basic Process Control - -(defun forth-start-process (command-line) - (let ((buffer (get-buffer-create "*forth*"))) - (let ((process (get-buffer-process buffer))) - (save-excursion - (set-buffer buffer) - (progn (if process (delete-process process)) - (goto-char (point-max)) - (setq mode-line-process '(": %s")) - (add-to-global-mode-string 'forth-mode-string) - (setq process - (apply 'start-process - (cons forth-program-name - (cons buffer - (forth-parse-command-line - command-line))))) - (set-marker (process-mark process) (point-max)) - (forth-process-filter-initialize t) - (forth-modeline-initialize) - (set-process-sentinel process 'forth-process-sentinel) - (set-process-filter process 'forth-process-filter) - (run-hooks 'forth-start-hook))) - buffer))) - -(defun forth-parse-command-line (string) - (setq string (substitute-in-file-name string)) - (let ((start 0) - (result '())) - (while start - (let ((index (string-match "[ \t]" string start))) - (setq start - (cond ((not index) - (setq result - (cons (substring string start) - result)) - nil) - ((= index start) - (string-match "[^ \t]" string start)) - (t - (setq result - (cons (substring string start index) - result)) - (1+ index)))))) - (nreverse result))) - - -(defun forth-process-running-p () - "True iff there is a Forth process whose status is `run'." - (let ((process (get-process forth-program-name))) - (and process - (eq (process-status process) 'run)))) - -(defun forth-process-buffer () - (let ((process (get-process forth-program-name))) - (and process (process-buffer process)))) - -;;;; Process Filter - -(defun forth-process-sentinel (proc reason) - (let ((inhibit-quit nil)) - (forth-process-filter-initialize (eq reason 'run)) - (if (eq reason 'run) - (forth-modeline-initialize) - (setq forth-mode-string ""))) - (if (and (not (memq reason '(run stop))) - forth-signal-death-message) - (progn (beep) - (message -"The Forth process has died! Do M-x reset-forth to restart it")))) - -(defun forth-process-filter-initialize (running-p) - (setq forth-process-filter-queue (cons '() '())) - (setq forth-prompt "ok")) - - -(defun forth-process-filter (proc string) - (forth-process-filter-output string) - (forth-process-filter:finish)) - -(defun forth-process-filter:enqueue (action) - (let ((next (cons action '()))) - (if (cdr forth-process-filter-queue) - (setcdr (cdr forth-process-filter-queue) next) - (setcar forth-process-filter-queue next)) - (setcdr forth-process-filter-queue next))) - -(defun forth-process-filter:finish () - (while (car forth-process-filter-queue) - (let ((next (car forth-process-filter-queue))) - (setcar forth-process-filter-queue (cdr next)) - (if (not (cdr next)) - (setcdr forth-process-filter-queue '())) - (apply (car (car next)) (cdr (car next)))))) - -;;;; Process Filter Output - -(defun forth-process-filter-output (&rest args) - (if (not (and args - (null (cdr args)) - (stringp (car args)) - (string-equal "" (car args)))) - (forth-process-filter:enqueue - (cons 'forth-process-filter-output-1 args)))) - -(defun forth-process-filter-output-1 (&rest args) - (save-excursion - (forth-goto-output-point) - (apply 'insert-before-markers args))) - -(defun forth-guarantee-newlines (n) - (save-excursion - (forth-goto-output-point) - (let ((stop nil)) - (while (and (not stop) - (bolp)) - (setq n (1- n)) - (if (bobp) - (setq stop t) - (backward-char)))) - (forth-goto-output-point) - (while (> n 0) - (insert-before-markers ?\n) - (setq n (1- n))))) - -(defun forth-goto-output-point () - (let ((process (get-process forth-program-name))) - (set-buffer (process-buffer process)) - (goto-char (process-mark process)))) - -(defun forth-modeline-initialize () - (setq forth-mode-string " ")) - -(defun forth-set-runlight (runlight) - (aset forth-mode-string 0 runlight) - (forth-modeline-redisplay)) - -(defun forth-modeline-redisplay () - (save-excursion (set-buffer (other-buffer))) - (set-buffer-modified-p (buffer-modified-p)) - (sit-for 0)) - -;;;; Process Filter Operations - -(defun add-to-global-mode-string (x) - (cond ((null global-mode-string) - (setq global-mode-string (list "" x " "))) - ((not (memq x global-mode-string)) - (setq global-mode-string - (cons "" - (cons x - (cons " " - (if (equal "" (car global-mode-string)) - (cdr global-mode-string) - global-mode-string)))))))) - - -;; Misc +(define-key forth-mode-map "\C-x\C-e" 'compile) +(define-key forth-mode-map "\C-x\C-n" 'next-error) +(require 'compile) -(setq auto-mode-alist (append auto-mode-alist - '(("\\.fs$" . forth-mode)))) +(defvar forth-compile-command "gforth ") +;(defvar forth-compilation-window-percent-height 30) (defun forth-split () (interactive) @@ -1712,172 +1390,6 @@ The region is sent terminated by a newli (switch-to-buffer buffer) (goto-char (point-max)) (other-window 1)))) - -(defun forth-reload () - (interactive) - (let ((process (get-process forth-program-name))) - (if process (kill-process process t))) - (sleep-for 0 100) - (forth-mode)) - - -;; Special section for forth-help - -(defvar forth-help-buffer "*Forth-help*" - "Buffer used to display the requested documentation.") - -(defvar forth-help-load-path nil - "List of directories to search through to find *.doc - (forth-help-file-suffix) files. Nil means current default directory. - The specified directories must contain at least one .doc file. If it - does not and you still want the load-path to scan that directory, create - an empty file dummy.doc.") - -(defvar forth-help-file-suffix "*.doc" - "The file names to search for in each directory.") - -(setq forth-search-command-prefix "grep -n \"^ [^(]* ") -(defvar forth-search-command-suffix "/dev/null") -(defvar forth-grep-error-regexp ": No such file or directory") - -(defun forth-function-called-at-point () - "Return the space delimited word a point." - (save-excursion - (save-restriction - (narrow-to-region (max (point-min) (- (point) 1000)) (point-max)) - (skip-chars-backward "^ \t\n" (point-min)) - (if (looking-at "[ \t\n]") - (forward-char 1)) - (let (obj (p (point))) - (skip-chars-forward "^ \t\n") - (buffer-substring p (point)))))) - -(defun forth-help-names-extend-comp (path-list result) - (cond ((null path-list) result) - ((null (car path-list)) - (forth-help-names-extend-comp (cdr path-list) - (concat result forth-help-file-suffix " "))) - (t (forth-help-names-extend-comp - (cdr path-list) (concat result - (expand-file-name (car path-list)) "/" - forth-help-file-suffix " "))))) - -(defun forth-help-names-extended () - (if forth-help-load-path - (forth-help-names-extend-comp forth-help-load-path "") - (error "forth-help-load-path not specified"))) - - -;(define-key forth-mode-map "\C-hf" 'forth-documentation) - -(defun forth-documentation (function) - "Display the full documentation of FORTH word." - (interactive - (let ((fn (forth-function-called-at-point)) - (enable-recursive-minibuffers t) - search-list - val) - (setq val (read-string (format "Describe forth word (default %s): " fn))) - (list (if (equal val "") fn val)))) - (forth-get-doc (concat forth-search-command-prefix - (grep-regexp-quote (concat function " (")) - "[^)]*\-\-\" " (forth-help-names-extended) - forth-search-command-suffix)) - (message "C-x C-m switches back to the forth interaction window")) - -(defun forth-get-doc (command) - "Display the full documentation of command." - (let ((curwin (get-buffer-window (window-buffer))) - reswin - pointmax) - (with-output-to-temp-buffer forth-help-buffer - (progn - (call-process "sh" nil forth-help-buffer t "-c" command) - (setq reswin (get-buffer-window forth-help-buffer)))) - (setq reswin (get-buffer-window forth-help-buffer)) - (select-window reswin) - (save-excursion - (goto-char (setq pointmax (point-max))) - (insert "--------------------\n\n")) - (let (fd doc) - (while (setq fd (forth-get-file-data pointmax)) - (setq doc (forth-get-doc-string fd)) - (save-excursion - (goto-char (point-max)) - (insert (substring (car fd) (string-match "[^/]*$" (car fd))) - ":\n\n" doc "\n"))) - (if (not doc) - (progn (goto-char (point-max)) (insert "Not found")))) - (select-window curwin))) - -(defun forth-skip-error-lines () - (let ((lines 0)) - (save-excursion - (while (re-search-forward forth-grep-error-regexp nil t) - (beginning-of-line) - (forward-line 1) - (setq lines (1+ lines)))) - (forward-line lines))) - -(defun forth-get-doc-string (fd) - "Find file (car fd) and extract documentation from line (nth 1 fd)." - (let (result) - (save-window-excursion - (find-file (car fd)) - (goto-line (nth 1 fd)) - (if (not (eq (nth 1 fd) (1+ (count-lines (point-min) (point))))) - (error "forth-get-doc-string: serious error")) - (if (not (re-search-backward "\n[\t ]*\n" nil t)) - (goto-char (point-min)) - (goto-char (match-end 0))) - (let ((p (point))) - (if (not (re-search-forward "\n[\t ]*\n" nil t)) - (goto-char (point-max))) - (setq result (buffer-substring p (point)))) - (bury-buffer (current-buffer))) - result)) - -(defun forth-get-file-data (limit) - "Parse grep output and return '(filename line#) list. Return nil when - passing limit." - (forth-skip-error-lines) - (if (< (point) limit) - (let ((result (forth-get-file-data-cont limit))) - (forward-line 1) - (beginning-of-line) - result))) - -(defun forth-get-file-data-cont (limit) - (let (result) - (let ((p (point))) - (skip-chars-forward "^:") - (setq result (buffer-substring p (point)))) - (if (< (point) limit) - (let ((p (1+ (point)))) - (forward-char 1) - (skip-chars-forward "^:") - (list result (string-to-int (buffer-substring p (point)))))))) - -(defun grep-regexp-quote (str) - (let ((i 0) (m 1) (res "")) - (while (/= m 0) - (setq m (string-to-char (substring str i))) - (if (/= m 0) - (progn - (setq i (1+ i)) - (if (string-match (regexp-quote (char-to-string m)) - ".*\\^$[]") - (setq res (concat res "\\"))) - (setq res (concat res (char-to-string m)))))) - res)) - - -(define-key forth-mode-map "\C-x\C-e" 'compile) -(define-key forth-mode-map "\C-x\C-n" 'next-error) -(require 'compile) - -(defvar forth-compile-command "gforth ") -;(defvar forth-compilation-window-percent-height 30) (defun forth-compile (command) (interactive (list (setq forth-compile-command (read-string "Compile command: " forth-compile-command)))) @@ -1885,7 +1397,6 @@ The region is sent terminated by a newli (setq ctools-compile-command command) (compile1 ctools-compile-command "No more errors")) - ;;; Forth menu ;;; Mikael Karlsson @@ -1984,6 +1495,213 @@ The region is sent terminated by a newli ;; ; (define-key global-map '(shift button3) 'mouse-function-menu) ;; )) +;;; +;;; Inferior Forth interpreter +;;; -- mostly copied from `cmuscheme.el' of Emacs 21.2 +;;; + +(eval-and-compile (forth-require 'comint)) + +(when (memq 'comint features) + + (defvar forth-program-name "gforth" + "*Program invoked by the `run-forth' command, including program arguments") + + (defcustom inferior-forth-mode-hook nil + "*Hook for customising inferior-forth-mode." + :type 'hook + :group 'forth) + + (defvar inferior-forth-mode-map + (let ((m (make-sparse-keymap))) + (define-key m "\r" 'comint-send-input) + (define-key m "\M-\C-x" 'forth-send-paragraph-and-go) + (define-key m "\C-c\C-l" 'forth-load-file) + m)) + ;; Install the process communication commands in the forth-mode keymap. + (define-key forth-mode-map "\e\C-m" 'forth-send-paragraph-and-go) + (define-key forth-mode-map "\eo" 'forth-send-buffer-and-go) + + (define-key forth-mode-map "\M-\C-x" 'forth-send-paragraph-and-go) + (define-key forth-mode-map "\C-c\C-r" 'forth-send-region) + (define-key forth-mode-map "\C-c\M-r" 'forth-send-region-and-go) + (define-key forth-mode-map "\C-c\C-z" 'forth-switch-to-interactive) + (define-key forth-mode-map "\C-c\C-l" 'forth-load-file) + + (defvar forth-process-buffer) + + (define-derived-mode inferior-forth-mode comint-mode "Inferior Forth" + "Major mode for interacting with an inferior Forth process. + +The following commands are available: +\\{inferior-forth-mode-map} + +A Forth process can be fired up with M-x run-forth. + +Customisation: Entry to this mode runs the hooks on comint-mode-hook and +inferior-forth-mode-hook (in that order). + +You can send text to the inferior Forth process from other buffers containing +Forth source. + forth-switch-to-interactive switches the current buffer to the Forth + process buffer. + forth-send-paragraph sends the current paragraph to the Forth process. + forth-send-region sends the current region to the Forth process. + forth-send-buffer sends the current buffer to the Forth process. + + forth-send-paragraph-and-go, forth-send-region-and-go, + forth-send-buffer-and-go switch to the Forth process buffer after + sending their text. +For information on running multiple processes in multiple buffers, see +documentation for variable `forth-process-buffer'. + +Commands: +Return after the end of the process' output sends the text from the +end of process to point. If you accidentally suspend your process, use +\\[comint-continue-subjob] to continue it. " + ;; Customise in inferior-forth-mode-hook + (setq comint-prompt-regexp "^") + (setq mode-line-process '(":%s"))) + + (defun forth-args-to-list (string) + (let ((where (string-match "[ \t]" string))) + (cond ((null where) (list string)) + ((not (= where 0)) + (cons (substring string 0 where) + (forth-args-to-list (substring string (+ 1 where) + (length string))))) + (t (let ((pos (string-match "[^ \t]" string))) + (if (null pos) + nil + (forth-args-to-list (substring string pos + (length string))))))))) + +;;;###autoload + (defun run-forth (cmd) + "Run an inferior Forth process, input and output via buffer *forth*. +If there is a process already running in `*forth*', switch to that buffer. +With argument, allows you to edit the command line (default is value +of `forth-program-name'). Runs the hooks `inferior-forth-mode-hook' +\(after the `comint-mode-hook' is run). +\(Type \\[describe-mode] in the process buffer for a list of commands.)" + + (interactive (list (if current-prefix-arg + (read-string "Run Forth: " forth-program-name) + forth-program-name))) + (if (not (comint-check-proc "*forth*")) + (let ((cmdlist (forth-args-to-list cmd))) + (set-buffer (apply 'make-comint "forth" (car cmdlist) + nil (cdr cmdlist))) + (inferior-forth-mode))) + (setq forth-program-name cmd) + (setq forth-process-buffer "*forth*") + (pop-to-buffer "*forth*")) + + (defun forth-send-region (start end) + "Send the current region to the inferior Forth process." + (interactive "r") + (comint-send-region (forth-proc) start end) + (comint-send-string (forth-proc) "\n")) + + (defun forth-end-of-paragraph () + (if (looking-at "[\t\n ]+") (skip-chars-backward "\t\n ")) + (if (not (re-search-forward "\n[ \t]*\n" nil t)) + (goto-char (point-max)))) + + (defun forth-send-paragraph () + "Send the current or the previous paragraph to the Forth process" + (interactive) + (let (end) + (save-excursion + (forth-end-of-paragraph) + (skip-chars-backward "\t\n ") + (setq end (point)) + (if (re-search-backward "\n[ \t]*\n" nil t) + (setq start (point)) + (goto-char (point-min))) + (skip-chars-forward "\t\n ") + (forth-send-region (point) end)))) + + (defun forth-send-paragraph-and-go () + "Send the current or the previous paragraph to the Forth process. +Then switch to the process buffer." + (interactive) + (forth-send-paragraph) + (forth-switch-to-interactive t)) + + (defun forth-send-buffer () + "Send the current buffer to the Forth process." + (interactive) + (if (eq (current-buffer) forth-process-buffer) + (error "Not allowed to send this buffer's contents to Forth")) + (forth-send-region (point-min) (point-max))) + + (defun forth-send-buffer-and-go () + "Send the current buffer to the Forth process. +Then switch to the process buffer." + (interactive) + (forth-send-buffer) + (forth-switch-to-interactive t)) + + + (defun forth-switch-to-interactive (eob-p) + "Switch to the Forth process buffer. +With argument, position cursor at end of buffer." + (interactive "P") + (if (get-buffer forth-process-buffer) + (pop-to-buffer forth-process-buffer) + (error "No current process buffer. See variable `forth-process-buffer'")) + (cond (eob-p + (push-mark) + (goto-char (point-max))))) + + (defun forth-send-region-and-go (start end) + "Send the current region to the inferior Forth process. +Then switch to the process buffer." + (interactive "r") + (forth-send-region start end) + (forth-switch-to-interactive t)) + + (defcustom forth-source-modes '(forth-mode forth-block-mode) + "*Used to determine if a buffer contains Forth source code. +If it's loaded into a buffer that is in one of these major modes, it's +considered a Forth source file by `forth-load-file' and `forth-compile-file'. +Used by these commands to determine defaults." + :type '(repeat function) + :group 'forth) + + (defvar forth-prev-l/c-dir/file nil + "Caches the last (directory . file) pair. +Caches the last pair used in the last `forth-load-file' or +`forth-compile-file' command. Used for determining the default in the +next one.") + + (defun forth-load-file (file-name) + "Load a Forth file FILE-NAME into the inferior Forth process." + (interactive (comint-get-source "Load Forth file: " forth-prev-l/c-dir/file + forth-source-modes t)) ; T because LOAD + ; needs an exact name + (comint-check-source file-name) ; Check to see if buffer needs saved. + (setq forth-prev-l/c-dir/file (cons (file-name-directory file-name) + (file-name-nondirectory file-name))) + (comint-send-string (forth-proc) + (concat "s\" " file-name "\" included\n"))) + + + (defvar forth-process-buffer nil "*The current Forth process buffer. + +See `scheme-buffer' for an explanation on how to run multiple Forth +processes.") + + (defun forth-proc () + "Return the current Forth process. See variable `forth-process-buffer'." + (let ((proc (get-buffer-process (if (eq major-mode 'inferior-forth-mode) + (current-buffer) + forth-process-buffer)))) + (or proc + (error "No current process. See variable `forth-process-buffer'")))) + ) ; (memq 'comint features) + (provide 'forth-mode) ;;; gforth.el ends here