| ;;; gforth.el --- major mode for editing (G)Forth sources |
;;; gforth.el --- major mode for editing (G)Forth sources |
| |
|
| ;; Copyright (C) 1995,1996,1997,1998,2000 Free Software Foundation, Inc. |
;; Copyright (C) 1995,1996,1997,1998,2000,2001,2003,2004,2007 Free Software Foundation, Inc. |
| |
|
| ;; This file is part of Gforth. |
;; This file is part of Gforth. |
| |
|
| ;; Changes by David |
;; Changes by David |
| ;; Added a syntax-hilighting engine, rewrote auto-indentation engine. |
;; Added a syntax-hilighting engine, rewrote auto-indentation engine. |
| ;; Added support for block files. |
;; Added support for block files. |
| |
;; 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 |
;; A Forth indentation, documentation search and interaction library |
| |
|
| ;;; Code: |
;;; Code: |
| |
|
| |
;(setq debug-on-error t) |
| |
|
| |
;; Code ripped from `version.el' for compatability with Emacs versions |
| |
;; prior to 19.23. |
| |
(if (not (boundp 'emacs-major-version)) |
| |
(defconst emacs-major-version |
| |
(progn (string-match "^[0-9]+" emacs-version) |
| |
(string-to-number (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)))) |
| |
|
| |
(if (forth-emacs-older 20 1) |
| |
(progn |
| |
(defmacro when (cond &rest body) |
| |
"If COND yields non-nil, do BODY, else return nil." |
| |
(list 'if cond (cons 'progn body))) |
| |
(defmacro unless (cond &rest body) |
| |
"If COND yields nil, do BODY, else return nil." |
| |
(cons 'if (cons cond (cons nil body))))))) |
| |
|
| |
;; `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)))) |
| |
|
| |
(require 'font-lock) |
| |
|
| |
;; define `font-lock-warning-face' in emacs-versions prior to 20.1 |
| |
;; (ripped from `font-lock.el') |
| |
(unless (boundp 'font-lock-warning-face) |
| |
(message "defining font-lock-warning-face") |
| |
(make-face 'font-lock-warning-face) |
| |
(defvar font-lock-warning-face 'font-lock-warning-face) |
| |
(set-face-foreground font-lock-warning-face "red") |
| |
(make-face-bold font-lock-warning-face)) |
| |
|
| |
;; define `font-lock-constant-face' in XEmacs (just copy |
| |
;; `font-lock-preprocessor-face') |
| |
(unless (boundp 'font-lock-constant-face) |
| |
(copy-face font-lock-preprocessor-face 'font-lock-constant-face)) |
| |
|
| |
|
| |
;; define `regexp-opt' in emacs versions prior to 20.1 |
| |
;; (this implementation is extremely inefficient, though) |
| |
(eval-and-compile (forth-require 'regexp-opt)) |
| |
(unless (memq 'regexp-opt features) |
| |
(message (concat |
| |
"Warning: your Emacs version doesn't support `regexp-opt'. " |
| |
"Hilighting will be slow.")) |
| |
(defun regexp-opt (STRINGS &optional PAREN) |
| |
(let ((open (if PAREN "\\(" "")) (close (if PAREN "\\)" ""))) |
| |
(concat open (mapconcat 'regexp-quote STRINGS "\\|") close))) |
| |
(defun regexp-opt-depth (re) |
| |
(if (string= (substring re 0 2) "\\(") 1 0))) |
| |
|
| |
; 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?? |
| |
; |
| |
; Konfiguration über customization groups |
| |
; |
| |
; Bereich nur auf Wortanfang/ende ausweiten, wenn Anfang bzw Ende in einem |
| |
; Wort liegen (?) -- speed! |
| |
; |
| |
; 'forth-word' property muss eindeutig sein! |
| |
; |
| |
; Forth-Menu |
| |
; |
| |
; Interface zu GForth Prozessen (Patches von Michael Scholz) |
| |
; |
| |
; Byte-compile-Code rausschmeißen, Compilieren im Makefile über Emacs |
| |
; batch-Modus |
| |
; |
| |
; forth-help Kram rausschmeißen |
| |
; |
| |
; XEmacs Kompatibilität? imenu/speedbar -> fume? |
| |
; |
| |
; Folding neuschreiben (neue Parser-Informationen benutzen) |
| |
|
| |
;;; Motion-hooking (dk) |
| |
;;; |
| |
(defun forth-idle-function () |
| |
"Function that is called when Emacs is idle to detect cursor motion |
| |
in forth-block-mode buffers (which is mainly used for screen number |
| |
display in). Currently ignores forth-mode buffers but that may change |
| |
in the future." |
| |
(if (eq major-mode 'forth-block-mode) |
| |
(forth-check-motion))) |
| |
|
| |
(defvar forth-idle-function-timer nil |
| |
"Timer that runs `forth-idle-function' or nil if no timer installed.") |
| |
|
| |
(defun forth-install-motion-hook () |
| |
"Install the motion-hooking mechanism. Currently uses idle timers |
| |
but might be transparently changed in the future." |
| |
(unless forth-idle-function-timer |
| |
;; install idle function only once (first time forth-mode is used) |
| |
(setq forth-idle-function-timer |
| |
(run-with-idle-timer .05 t 'forth-idle-function)))) |
| |
|
| |
(defvar forth-was-point nil) |
| |
|
| |
(defun forth-check-motion () |
| |
"Run `forth-motion-hooks', if `point' changed since last call. This |
| |
used to be called via `post-command-hook' but uses idle timers now as |
| |
users complaint about lagging performance." |
| |
(when (or (eq forth-was-point nil) (/= forth-was-point (point))) |
| |
(setq forth-was-point (point)) |
| |
(run-hooks 'forth-motion-hooks))) |
| |
|
| |
|
| ;;; Hilighting and indentation engine (dk) |
;;; Hilighting and indentation engine (dk) |
| ;;; |
;;; |
| |
(defvar forth-disable-parser nil |
| |
"*Non-nil means to disable on-the-fly parsing of Forth-code. |
| |
|
| |
This will disable hilighting of forth-mode buffers and will decrease |
| |
the smartness of the indentation engine. Only set it to non-nil, if |
| |
your computer is very slow. To disable hilighting, set |
| |
`forth-hilight-level' to zero.") |
| |
|
| |
(defvar forth-jit-parser nil |
| |
"*Non-nil means to parse Forth-code just-in-time. |
| |
|
| |
This eliminates the need for initially parsing forth-mode buffers and |
| |
thus speeds up loading of Forth files. That feature is only available |
| |
in Emacs21 (and newer versions).") |
| |
|
| (defvar forth-words nil |
(defvar forth-words nil |
| "List of words for hilighting and recognition of parsed text areas. |
"List of words for hilighting and recognition of parsed text areas. |
| You can enable hilighting of object-oriented Forth code, by appending either |
|
| `forth-objects-words' or `forth-oof-words' to the list, depending on which |
Hilighting of object-oriented Forth code is achieved, by appending either |
| OOP package you're using. After `forth-words' changed, `forth-compile-words' |
`forth-objects-words' or `forth-oof-words' to the list, depending on the values of `forth-use-objects' or `forth-use-oof'. |
| must be called to make the changes take effect. |
|
| |
After `forth-words' changed, `forth-compile-words' must be called to |
| |
make the changes take effect. |
| |
|
| Each item of `forth-words' has the form |
Each item of `forth-words' has the form |
| (MATCHER TYPE HILIGHT . &optional PARSED-TEXT ...) |
(MATCHER TYPE HILIGHT . &optional PARSED-TEXT ...) |
| (("[ifdef]" "[ifundef]") immediate (font-lock-keyword-face . 2) |
(("[ifdef]" "[ifundef]") immediate (font-lock-keyword-face . 2) |
| "[ \t\n]" t name (font-lock-function-name-face . 3)) |
"[ \t\n]" t name (font-lock-function-name-face . 3)) |
| (("if" "begin" "ahead" "do" "?do" "+do" "u+do" "-do" "u-do" "for" |
(("if" "begin" "ahead" "do" "?do" "+do" "u+do" "-do" "u-do" "for" |
| "case" "of" "?dup-if" "?dup-0=-if" "then" "until" "repeat" "again" |
"case" "of" "?dup-if" "?dup-0=-if" "then" "endif" "until" |
| |
"repeat" "again" "leave" "?leave" |
| "loop" "+loop" "-loop" "next" "endcase" "endof" "else" "while" "try" |
"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(" ")" "<interpretation" "<compilation" "interpretation>" |
"assert3(" ")" "<interpretation" "<compilation" "interpretation>" |
| "compilation>") |
"compilation>") |
| compile-only (font-lock-keyword-face . 2)) |
compile-only (font-lock-keyword-face . 2)) |
| |
|
| (("true" "false" "c/l" "bl" "cell" "pi" "w/o" "r/o" "r/w") |
(("true" "false" "c/l" "bl" "cell" "pi" "w/o" "r/o" "r/w") |
| non-immediate (font-lock-constant-face . 2)) |
non-immediate (font-lock-constant-face . 2)) |
| (("~~") compile-only (font-lock-warning-face . 2)) |
(("~~" "break:" "dbg") compile-only (font-lock-warning-face . 2)) |
| |
(("break\"") compile-only (font-lock-warning-face . 1) |
| |
"[\"\n]" nil string (font-lock-string-face . 1)) |
| (("postpone" "[is]" "defers" "[']" "[compile]") |
(("postpone" "[is]" "defers" "[']" "[compile]") |
| compile-only (font-lock-keyword-face . 2) |
compile-only (font-lock-keyword-face . 2) |
| "[ \t\n]" t name (font-lock-function-name-face . 3)) |
"[ \t\n]" t name (font-lock-function-name-face . 3)) |
| (("is" "what's") immediate (font-lock-keyword-face . 2) |
(("is" "what's") immediate (font-lock-keyword-face . 2) |
| "[ \t\n]" t name (font-lock-function-name-face . 3)) |
"[ \t\n]" t name (font-lock-function-name-face . 3)) |
| (("<is>" "'") non-immediate (font-lock-keyword-face . 2) |
(("<is>" "'" "see") non-immediate (font-lock-keyword-face . 2) |
| "[ \t\n]" t name (font-lock-function-name-face . 3)) |
"[ \t\n]" t name (font-lock-function-name-face . 3)) |
| (("[to]") compile-only (font-lock-keyword-face . 2) |
(("[to]") compile-only (font-lock-keyword-face . 2) |
| "[ \t\n]" t name (font-lock-variable-name-face . 3)) |
"[ \t\n]" t name (font-lock-variable-name-face . 3)) |
| "create-interpret/compile") |
"create-interpret/compile") |
| non-immediate (font-lock-type-face . 2) |
non-immediate (font-lock-type-face . 2) |
| "[ \t\n]" t name (font-lock-variable-name-face . 3)) |
"[ \t\n]" t name (font-lock-variable-name-face . 3)) |
| |
("\\S-+%" non-immediate (font-lock-type-face . 2)) |
| (("defer" "alias" "create-interpret/compile:") |
(("defer" "alias" "create-interpret/compile:") |
| non-immediate (font-lock-type-face . 1) |
non-immediate (font-lock-type-face . 1) |
| "[ \t\n]" t name (font-lock-function-name-face . 3)) |
"[ \t\n]" t name (font-lock-function-name-face . 3)) |
| |
|
| (defvar forth-use-objects nil |
(defvar forth-use-objects nil |
| "*Non-nil makes forth-mode also hilight words from the \"Objects\" package.") |
"*Non-nil makes forth-mode also hilight words from the \"Objects\" package.") |
| (defvar forth-objects-words nil |
(defvar forth-objects-words |
| "Hilighting description for words of the \"Objects\" package") |
|
| (setq forth-objects-words |
|
| '(((":m") definition-starter (font-lock-keyword-face . 1) |
'(((":m") definition-starter (font-lock-keyword-face . 1) |
| "[ \t\n]" t name (font-lock-function-name-face . 3)) |
"[ \t\n]" t name (font-lock-function-name-face . 3)) |
| (("m:") definition-starter (font-lock-keyword-face . 1)) |
(("m:") definition-starter (font-lock-keyword-face . 1)) |
| (("public" "protected" "class" "exitm" "implementation" "interface" |
(("public" "protected" "class" "exitm" "implementation" "interface" |
| "methods" "end-methods" "this") |
"methods" "end-methods" "this") |
| non-immediate (font-lock-keyword-face . 2)) |
non-immediate (font-lock-keyword-face . 2)) |
| (("object") non-immediate (font-lock-type-face . 2)))) |
(("object") non-immediate (font-lock-type-face . 2))) |
| |
"Hilighting description for words of the \"Objects\" package") |
| |
|
| |
|
| (defvar forth-use-oof nil |
(defvar forth-use-oof nil |
| "*Non-nil makes forth-mode also hilight words from the \"OOF\" package.") |
"*Non-nil makes forth-mode also hilight words from the \"OOF\" package.") |
| (defvar forth-oof-words nil |
(defvar forth-oof-words |
| "Hilighting description for words of the \"OOF\" package") |
|
| (setq forth-oof-words |
|
| '((("class") non-immediate (font-lock-keyword-face . 2) |
'((("class") non-immediate (font-lock-keyword-face . 2) |
| "[ \t\n]" t name (font-lock-type-face . 3)) |
"[ \t\n]" t name (font-lock-type-face . 3)) |
| (("var") non-immediate (font-lock-type-face . 2) |
(("var") non-immediate (font-lock-type-face . 2) |
| "[ \t\n]" t name (font-lock-variable-name-face . 3)) |
"[ \t\n]" t name (font-lock-variable-name-face . 3)) |
| (("method") non-immediate (font-lock-type-face . 2) |
(("method" "early") non-immediate (font-lock-type-face . 2) |
| "[ \t\n]" t name (font-lock-function-name-face . 3)) |
"[ \t\n]" t name (font-lock-function-name-face . 3)) |
| (("::" "super" "bind" "bound" "link") |
(("::" "super" "bind" "bound" "link") |
| immediate (font-lock-keyword-face . 2) |
immediate (font-lock-keyword-face . 2) |
| (("class;" "how:" "self" "new" "new[]" "definitions" "class?" "with" |
(("class;" "how:" "self" "new" "new[]" "definitions" "class?" "with" |
| "endwith") |
"endwith") |
| non-immediate (font-lock-keyword-face . 2)) |
non-immediate (font-lock-keyword-face . 2)) |
| (("object") non-immediate (font-lock-type-face . 2)))) |
(("object") non-immediate (font-lock-type-face . 2))) |
| |
"Hilighting description for words of the \"OOF\" package") |
| |
|
| (defvar forth-local-words nil |
(defvar forth-local-words nil |
| "List of Forth words to prepend to `forth-words'. Should be set by a |
"List of Forth words to prepend to `forth-words'. Should be set by a |
| |
|
| (defvar forth-compiled-words nil "Compiled representation of `forth-words'.") |
(defvar forth-compiled-words nil "Compiled representation of `forth-words'.") |
| |
|
| |
(defvar forth-indent-words nil |
| |
"List of words that have indentation behaviour. |
| |
Each element of `forth-indent-words' should have the form |
| |
(MATCHER INDENT1 INDENT2 &optional TYPE) |
| |
|
| ; todo: |
MATCHER is either a list of strings to match, or a REGEXP. |
| ; |
If it's a REGEXP, it should not be surrounded by `\\<` or `\\>`, since |
| |
that'll be done automatically by the search routines. |
| |
|
| ; Wörter ordentlich hilighten, die nicht auf whitespace beginning ( ..)IF |
TYPE might be omitted. If it's specified, the only allowed value is |
| ; Additional `forth-use-objects' or |
currently the symbol `non-immediate', meaning that the word will not |
| ; `forth-use-oof' could be set to non-nil for automatical adding of those |
have any effect on indentation inside definitions. (:NONAME is a good |
| ; word-lists. Using local variable list? |
example for this kind of word). |
| ; |
|
| ; Konfiguration über customization groups |
INDENT1 specifies how to indent a word that's located at the beginning |
| ; |
of a line, following any number of whitespaces. |
| ; Bereich nur auf Wortanfang/ende ausweiten, wenn anfang bzw ende in einem |
|
| ; Wort liegen (?) -- speed! |
|
| ; |
|
| ; User interface |
|
| ; |
|
| ; 'forth-word' property muss eindeutig sein! |
|
| ; |
|
| ; imenu support schlauer machen |
|
| |
|
| (setq debug-on-error t) |
INDENT2 specifies how to indent words that are not located at the |
| |
beginning of a line. |
| |
|
| |
INDENT1 and INDENT2 are indentation specifications of the form |
| |
(SELF-INDENT . NEXT-INDENT), where SELF-INDENT is a numerical value, |
| |
specifying how the matching line and all following lines are to be |
| |
indented, relative to previous lines. NEXT-INDENT specifies how to indent |
| |
following lines, relative to the matching line. |
| |
|
| |
Even values of SELF-INDENT and NEXT-INDENT correspond to multiples of |
| |
`forth-indent-level'. Odd values get an additional |
| |
`forth-minor-indent-level' added/substracted. Eg a value of -2 indents |
| |
1 * forth-indent-level to the left, wheras 3 indents |
| |
1 * forth-indent-level + forth-minor-indent-level columns to the right.") |
| |
|
| |
(setq forth-indent-words |
| |
'((("if" "begin" "do" "?do" "+do" "-do" "u+do" |
| |
"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") |
| |
(0 . 2) (0 . 2) non-immediate) |
| |
("\\S-+%$" (0 . 2) (0 . 0) non-immediate) |
| |
((";" ";m") (-2 . 0) (0 . -2)) |
| |
(("again" "then" "endif" "endtry" "endcase" "endof" |
| |
"[then]" "[endif]" "[loop]" "[+loop]" "[next]" |
| |
"[until]" "[again]" "loop") |
| |
(-2 . 0) (0 . -2)) |
| |
(("end-code" "end-class" "end-interface" "end-class-noname" |
| |
"end-interface-noname" "end-struct" "class;") |
| |
(-2 . 0) (0 . -2) non-immediate) |
| |
(("protected" "public" "how:") (-1 . 1) (0 . 0) non-immediate) |
| |
(("+loop" "-loop" "until") (-2 . 0) (-2 . 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)))) |
| |
|
| |
(defvar forth-local-indent-words nil |
| |
"List of Forth words to prepend to `forth-indent-words', when a forth-mode |
| |
buffer is created. Should be set by a Forth source, using a local variables |
| |
list at the end of the file (\"Local Variables: ... forth-local-words: ... |
| |
End:\" construct).") |
| |
|
| |
(defvar forth-custom-indent-words nil |
| |
"List of Forth words to prepend to `forth-indent-words'. Should be set in |
| |
your .emacs.") |
| |
|
| |
(defvar forth-indent-level 4 |
| |
"*Indentation of Forth statements.") |
| |
(defvar forth-minor-indent-level 2 |
| |
"*Minor indentation of Forth statements.") |
| |
(defvar forth-compiled-indent-words nil) |
| |
|
| |
;(setq debug-on-error t) |
| |
|
| ;; Filter list by predicate. This is a somewhat standard function for |
;; Filter list by predicate. This is a somewhat standard function for |
| ;; functional programming languages. So why isn't it already implemented |
;; functional programming languages. So why isn't it already implemented |
| ;; in Lisp?? |
;; in Lisp?? |
| (defun forth-filter (predicate list) |
(defun forth-filter (predicate list) |
| (let ((filtered nil)) |
(let ((filtered nil)) |
| (mapcar (lambda (item) |
(dolist (item list) |
| (when (funcall predicate item) |
(when (funcall predicate item) |
| (if filtered |
(if filtered |
| (nconc filtered (list item)) |
(nconc filtered (list item)) |
| (setq filtered (cons item nil)))) |
(setq filtered (cons item nil))))) |
| nil) list) |
|
| filtered)) |
filtered)) |
| |
|
| ;; Helper function for `forth-compile-word': return whether word has to be |
;; Helper function for `forth-compile-word': return whether word has to be |
| ;; Helper function for `forth-compile-word': translate one entry from |
;; Helper function for `forth-compile-word': translate one entry from |
| ;; `forth-words' into the form (regexp regexp-depth word-description) |
;; `forth-words' into the form (regexp regexp-depth word-description) |
| (defun forth-compile-words-mapper (word) |
(defun forth-compile-words-mapper (word) |
| |
;; warning: we cannot rely on regexp-opt's PAREN argument, since |
| |
;; XEmacs will use shy parens by default :-( |
| (let* ((matcher (car word)) |
(let* ((matcher (car word)) |
| (regexp (if (stringp matcher) (concat "\\(" matcher "\\)") |
(regexp |
| (if (listp matcher) (regexp-opt matcher t) |
(concat "\\(" (cond ((stringp matcher) matcher) |
| (error "Invalid matcher (stringp or listp expected `%s'" |
((listp matcher) (regexp-opt matcher)) |
| matcher)))) |
(t (error "Invalid matcher"))) |
| |
"\\)")) |
| (depth (regexp-opt-depth regexp)) |
(depth (regexp-opt-depth regexp)) |
| (description (cdr word))) |
(description (cdr word))) |
| (list regexp depth description))) |
(list regexp depth description))) |
| ;; Delete all properties, used by Forth mode, from `from' to `to'. |
;; Delete all properties, used by Forth mode, from `from' to `to'. |
| (defun forth-delete-properties (from to) |
(defun forth-delete-properties (from to) |
| (remove-text-properties |
(remove-text-properties |
| from to '(face nil forth-parsed nil forth-word nil forth-state nil))) |
from to '(face nil fontified nil |
| |
forth-parsed nil forth-word nil forth-state nil))) |
| |
|
| ;; Get the index of the branch of the most recently evaluated regular |
;; Get the index of the branch of the most recently evaluated regular |
| ;; expression that matched. (used for identifying branches "a\\|b\\|c...") |
;; expression that matched. (used for identifying branches "a\\|b\\|c...") |
| (defun forth-get-regexp-branch () |
(defun forth-get-regexp-branch () |
| (let ((count 2)) |
(let ((count 2)) |
| (while (not (match-beginning count)) |
(while (not (condition-case err (match-beginning count) |
| |
(args-out-of-range t))) ; XEmacs requires error handling |
| (setq count (1+ count))) |
(setq count (1+ count))) |
| count)) |
count)) |
| |
|
| (setq state (get-text-property (point) 'forth-state)) |
(setq state (get-text-property (point) 'forth-state)) |
| (setq last-location (point)) |
(setq last-location (point)) |
| (forth-delete-properties (point) to) |
(forth-delete-properties (point) to) |
| |
(put-text-property (point) to 'fontified t) |
| ;; hilight loop... |
;; hilight loop... |
| (while (setq word-descr (forth-next-known-forth-word to)) |
(while (setq word-descr (forth-next-known-forth-word to)) |
| (when loudly |
(when loudly |
| (eval-when-compile |
(eval-when-compile |
| (defmacro forth-save-buffer-state (varlist &rest body) |
(defmacro forth-save-buffer-state (varlist &rest body) |
| "Bind variables according to VARLIST and eval BODY restoring buffer state." |
"Bind variables according to VARLIST and eval BODY restoring buffer state." |
| (` (let* ((,@ (append varlist |
`(let* (,@(append varlist |
| '((modified (buffer-modified-p)) (buffer-undo-list t) |
'((modified (buffer-modified-p)) (buffer-undo-list t) |
| (inhibit-read-only t) (inhibit-point-motion-hooks t) |
(inhibit-read-only t) (inhibit-point-motion-hooks t) |
| before-change-functions after-change-functions |
before-change-functions after-change-functions |
| deactivate-mark buffer-file-name buffer-file-truename)))) |
deactivate-mark buffer-file-name buffer-file-truename))) |
| (,@ body) |
,@body |
| (when (and (not modified) (buffer-modified-p)) |
(when (and (not modified) (buffer-modified-p)) |
| (set-buffer-modified-p nil)))))) |
(set-buffer-modified-p nil))))) |
| |
|
| ;; Function that is added to the `change-functions' hook. Calls |
;; Function that is added to the `change-functions' hook. Calls |
| ;; `forth-update-properties' and keeps care of disabling undo information |
;; `forth-update-properties' and keeps care of disabling undo information |
| ;; and stuff like that. |
;; and stuff like that. |
| (defun forth-change-function (from to len &optional loudly) |
(defun forth-change-function (from to len &optional loudly) |
| (save-match-data |
(save-match-data |
| (forth-save-buffer-state () |
(forth-save-buffer-state |
| (unwind-protect |
() |
| (progn |
(unless forth-disable-parser (forth-update-properties from to loudly)) |
| (forth-update-properties from to loudly) |
(forth-update-warn-long-lines)))) |
| (forth-update-show-screen) |
|
| (forth-update-warn-long-lines)))))) |
(defun forth-fontification-function (from) |
| |
"Function to be called from `fontification-functions' of Emacs 21." |
| |
(save-match-data |
| |
(forth-save-buffer-state |
| |
((to (min (point-max) (+ from 100)))) |
| |
(unless (or forth-disable-parser (not forth-jit-parser) |
| |
(get-text-property from 'fontified)) |
| |
(forth-update-properties from to))))) |
| |
|
| (eval-when-compile |
(eval-when-compile |
| (byte-compile 'forth-set-word-properties) |
(byte-compile 'forth-set-word-properties) |
| |
|
| ;;; imenu support |
;;; imenu support |
| ;;; |
;;; |
| |
(defvar forth-defining-words |
| |
'("VARIABLE" "CONSTANT" "2VARIABLE" "2CONSTANT" "FVARIABLE" "FCONSTANT" |
| |
"USER" "VALUE" "field" "end-struct" "VOCABULARY" "CREATE" ":" "CODE" |
| |
"DEFER" "ALIAS") |
| |
"List of words, that define the following word. |
| |
Used for imenu index generation.") |
| |
|
| |
(defvar forth-defining-words-regexp nil |
| |
"Regexp that's generated for matching `forth-defining-words'") |
| |
|
| (defun forth-next-definition-starter () |
(defun forth-next-definition-starter () |
| (progn |
(progn |
| (let* ((regexp (car forth-compiled-defining-words)) |
(let* ((pos (re-search-forward forth-defining-words-regexp (point-max) t))) |
| (pos (re-search-forward regexp (point-max) t))) |
|
| (message "regexp: %s pos:%s" regexp pos) |
|
| (if pos |
(if pos |
| (if (or (text-property-not-all (match-beginning 0) (match-end 0) |
(if (or (text-property-not-all (match-beginning 0) (match-end 0) |
| 'forth-parsed nil) |
'forth-parsed nil) |
| nil)))) |
nil)))) |
| |
|
| (defun forth-create-index () |
(defun forth-create-index () |
| (let* ((defwords |
(let* ((forth-defining-words-regexp |
| (forth-filter (lambda (word) |
(concat "\\<\\(" (regexp-opt forth-defining-words) "\\)\\>")) |
| (and (eq (nth 1 word) 'definition-starter) |
|
| (> (length word) 3))) |
|
| forth-words)) |
|
| (forth-compiled-defining-words (forth-compile-wordlist defwords)) |
|
| (index nil)) |
(index nil)) |
| (goto-char (point-min)) |
(goto-char (point-min)) |
| (while (forth-next-definition-starter) |
(while (forth-next-definition-starter) |
| (if (looking-at "[ \t]*\\([^ \t\n]+\\)") |
(if (looking-at "[ \t]*\\([^ \t\n]+\\)") |
| (setq index (cons (cons (match-string 1) (point)) index)))) |
(setq index (cons (cons (match-string 1) (point)) index)))) |
| (message "index: %s" index) |
|
| index)) |
index)) |
| |
|
| |
;; top-level require is executed at byte-compile and load time |
| |
(eval-and-compile (forth-require 'speedbar)) |
| |
|
| |
;; this code is executed at load-time only |
| |
(when (memq 'speedbar features) |
| (speedbar-add-supported-extension ".fs") |
(speedbar-add-supported-extension ".fs") |
| (speedbar-add-supported-extension ".fb") |
(speedbar-add-supported-extension ".fb")) |
| |
|
| ;; (require 'profile) |
;; (require 'profile) |
| ;; (setq profile-functions-list '(forth-set-word-properties forth-next-known-forth-word forth-update-properties forth-delete-properties forth-get-regexp-branch)) |
;; (setq profile-functions-list '(forth-set-word-properties forth-next-known-forth-word forth-update-properties forth-delete-properties forth-get-regexp-branch)) |
| ;;; Indentation |
;;; Indentation |
| ;;; |
;;; |
| |
|
| (defvar forth-indent-words nil |
|
| "List of words that have indentation behaviour. |
|
| Each element of `forth-indent-words' should have the form |
|
| (MATCHER INDENT1 INDENT2) |
|
| |
|
| MATCHER is either a list of strings to match, or a REGEXP. |
|
| If it's a REGEXP, it should not be surrounded by `\\<` or `\\>`, since |
|
| that'll be done automatically by the search routines. |
|
| |
|
| INDENT1 specifies how to indent a word that's located at a line's begin, |
|
| following any number of whitespaces. |
|
| |
|
| INDENT2 specifies how to indent words that are not located at a line's begin. |
|
| |
|
| INDENT1 and INDENT2 are indentation specifications of the form |
|
| (SELF-INDENT . NEXT-INDENT), where SELF-INDENT is a numerical value, |
|
| specifying how the matching line and all following lines are to be |
|
| indented, relative to previous lines. NEXT-INDENT specifies how to indent |
|
| following lines, relative to the matching line. |
|
| |
|
| Even values of SELF-INDENT and NEXT-INDENT correspond to multiples of |
|
| `forth-indent-level'. Odd values get an additional |
|
| `forth-minor-indent-level' added/substracted. Eg a value of -2 indents |
|
| 1 * forth-indent-level to the left, wheras 3 indents |
|
| 1 * forth-indent-level + forth-minor-indent-level columns to the right.") |
|
| |
|
| (setq forth-indent-words |
|
| '(((":" ":noname" "code" "if" "begin" "do" "?do" "+do" "-do" "u+do" |
|
| "u-do" "?dup-if" "?dup-0=-if" "case" "of" "try" "struct" |
|
| "[if]" "[ifdef]" "[ifundef]" "[begin]" "[for]" "[do]" "[?do]" |
|
| "class" "interface" "m:" ":m") |
|
| (0 . 2) (0 . 2)) |
|
| ((";" ";m") (-2 . 0) (0 . -2)) |
|
| (("end-code" "again" "repeat" "then" "endtry" "endcase" "endof" |
|
| "end-struct" "[then]" "[endif]" "[loop]" "[+loop]" "[next]" |
|
| "[until]" "[repeat]" "[again]" "end-class" "end-interface" |
|
| "end-class-noname" "end-interface-noname" "loop" |
|
| "class;") |
|
| (-2 . 0) (0 . -2)) |
|
| (("protected" "public" "how:") (-1 . 1) (0 . 0)) |
|
| (("+loop" "-loop" "until") (-2 . 0) (-2 . 0)) |
|
| (("else" "recover" "[else]") (-2 . 2) (0 . 0)) |
|
| (("while" "does>" "[while]") (-1 . 1) (0 . 0)) |
|
| (("\\g") (-2 . 2) (0 . 0)))) |
|
| |
|
| (defvar forth-local-indent-words nil |
|
| "List of Forth words to prepend to `forth-indent-words', when a forth-mode |
|
| buffer is created. Should be set by a Forth source, using a local variables |
|
| list at the end of the file (\"Local Variables: ... forth-local-words: ... |
|
| End:\" construct).") |
|
| |
|
| (defvar forth-custom-indent-words nil |
|
| "List of Forth words to prepend to `forth-indent-words'. Should be set in |
|
| your .emacs.") |
|
| |
|
| (defvar forth-indent-level 4 |
|
| "Indentation of Forth statements.") |
|
| (defvar forth-minor-indent-level 2 |
|
| "Minor indentation of Forth statements.") |
|
| (defvar forth-compiled-indent-words nil) |
|
| |
|
| ;; Return, whether `pos' is the first forth word on its line |
;; Return, whether `pos' is the first forth word on its line |
| (defun forth-first-word-on-line-p (pos) |
(defun forth-first-word-on-line-p (pos) |
| (save-excursion |
(save-excursion |
| (let* ((regexp (car forth-compiled-indent-words)) |
(let* ((regexp (car forth-compiled-indent-words)) |
| (pos (re-search-forward regexp to t))) |
(pos (re-search-forward regexp to t))) |
| (if pos |
(if pos |
| (if (text-property-not-all (match-beginning 0) (match-end 0) |
(let* ((start (match-beginning 0)) |
| 'forth-parsed nil) |
(end (match-end 0)) |
| (forth-next-known-indent-word to) |
(branch (forth-get-regexp-branch)) |
| (let* ((branch (forth-get-regexp-branch)) |
|
| (descr (cdr forth-compiled-indent-words)) |
(descr (cdr forth-compiled-indent-words)) |
| (indent (cdr (assoc branch descr)))) |
(indent (cdr (assoc branch descr))) |
| |
(type (nth 2 indent))) |
| |
;; skip words that are parsed (strings/comments) and |
| |
;; non-immediate words inside definitions |
| |
(if (or (text-property-not-all start end 'forth-parsed nil) |
| |
(and (eq type 'non-immediate) |
| |
(text-property-not-all start end |
| |
'forth-state nil))) |
| |
(forth-next-known-indent-word to) |
| (if (forth-first-word-on-line-p (match-beginning 0)) |
(if (forth-first-word-on-line-p (match-beginning 0)) |
| (nth 0 indent) (nth 1 indent)))) |
(nth 0 indent) (nth 1 indent)))) |
| nil))) |
nil))) |
| |
|
| ;; Return the column increment, that the current line of forth code does to |
;; Return the column increment, that the current line of forth code does to |
| ;; the current or following lines. `which' specifies which indentation values |
;; the current or following lines. `which' specifies which indentation values |
| ;; to use. 0 means the indentation of following lines relative to current |
;; to use. 1 means the indentation of following lines relative to current |
| ;; line, 1 means the indentation of the current line relative to the previous |
;; 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. |
;; line. Return `nil', if there are no indentation words on the current line. |
| (defun forth-get-column-incr (which) |
(defun forth-get-column-incr (which) |
| (save-excursion |
(save-excursion |
| (defun forth-get-anchor-column () |
(defun forth-get-anchor-column () |
| (save-excursion |
(save-excursion |
| (if (/= 0 (forward-line -1)) 0 |
(if (/= 0 (forward-line -1)) 0 |
| (let ((next-indent) |
(let ((indent)) |
| (self-indent)) |
|
| (while (not (or (setq indent (forth-get-column-incr 1)) |
(while (not (or (setq indent (forth-get-column-incr 1)) |
| (<= (point) (point-min)))) |
(<= (point) (point-min)))) |
| (forward-line -1)) |
(forward-line -1)) |
| (forth-newline-remove-trailing) |
(forth-newline-remove-trailing) |
| (indent-according-to-mode)) |
(indent-according-to-mode)) |
| |
|
| ;;; end hilighting/indentation |
|
| |
|
| ;;; Block file encoding/decoding (dk) |
;;; Block file encoding/decoding (dk) |
| ;;; |
;;; |
| (save-restriction |
(save-restriction |
| (widen) |
(widen) |
| (save-excursion |
(save-excursion |
| (beginning-of-buffer) |
(goto-char (point-min)) |
| (end-of-line) |
(end-of-line) |
| (>= (current-column) 1024)))) |
(>= (current-column) 1024)))) |
| |
|
| "Non-nil means to warn about lines that are longer than 64 characters") |
"Non-nil means to warn about lines that are longer than 64 characters") |
| |
|
| (defvar forth-screen-marker nil) |
(defvar forth-screen-marker nil) |
| |
(defvar forth-screen-number-string nil) |
| |
|
| (defun forth-update-show-screen () |
(defun forth-update-show-screen () |
| "If `forth-show-screen' is non-nil, put overlay arrow to start of screen, |
"If `forth-show-screen' is non-nil, put overlay arrow to start of screen, |
| ;(define-key forth-mode-map "\M-\C-x" 'compile) |
;(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\\" 'comment-region) |
| (define-key forth-mode-map "\C-x~" 'forth-remove-tracers) |
(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 "\C-x\C-m" 'forth-split) |
| (define-key forth-mode-map "\e " 'forth-reload) |
(define-key forth-mode-map "\e " 'forth-reload) |
| (define-key forth-mode-map "\t" 'forth-indent-command) |
(define-key forth-mode-map "\t" 'forth-indent-command) |
| (define-key forth-mode-map "\M-q" 'forth-fill-paragraph) |
(define-key forth-mode-map "\M-q" 'forth-fill-paragraph) |
| (define-key forth-mode-map "\e." 'forth-find-tag) |
(define-key forth-mode-map "\e." 'forth-find-tag) |
| |
|
| ;;; hook into motion events (realy ugly!) (dk) |
;; setup for C-h C-i to work |
| (define-key forth-mode-map "\C-n" 'forth-next-line) |
(eval-and-compile (forth-require 'info-look)) |
| (define-key forth-mode-map "\C-p" 'forth-previous-line) |
(when (memq 'info-look features) |
| (define-key forth-mode-map [down] 'forth-next-line) |
(defvar forth-info-lookup '(symbol (forth-mode "\\S-+" t |
| (define-key forth-mode-map [up] 'forth-previous-line) |
(("(gforth)Word Index")) |
| (define-key forth-mode-map "\C-f" 'forth-forward-char) |
"\\S-+"))) |
| (define-key forth-mode-map "\C-b" 'forth-backward-char) |
(unless (memq forth-info-lookup info-lookup-alist) |
| (define-key forth-mode-map [right] 'forth-forward-char) |
(setq info-lookup-alist (cons forth-info-lookup info-lookup-alist))) |
| (define-key forth-mode-map [left] 'forth-backward-char) |
;; in X-Emacs C-h C-i is by default bound to Info-query |
| (define-key forth-mode-map "\M-f" 'forth-forward-word) |
(define-key forth-mode-map [?\C-h ?\C-i] 'info-lookup-symbol)) |
| (define-key forth-mode-map "\M-b" 'forth-backward-word) |
|
| (define-key forth-mode-map [C-right] 'forth-forward-word) |
;; (info-lookup-add-help |
| (define-key forth-mode-map [C-left] 'forth-backward-word) |
;; :topic 'symbol |
| (define-key forth-mode-map "\M-v" 'forth-scroll-down) |
;; :mode 'forth-mode |
| (define-key forth-mode-map "\C-v" 'forth-scroll-up) |
;; :regexp "[^ |
| (define-key forth-mode-map [prior] 'forth-scroll-down) |
;; ]+" |
| (define-key forth-mode-map [next] 'forth-scroll-up) |
;; :ignore-case t |
| |
;; :doc-spec '(("(gforth)Name Index" nil "`" "' ")))) |
| (defun forth-next-line (arg) |
|
| (interactive "p") (next-line arg) (run-hooks 'forth-motion-hooks)) |
|
| (defun forth-previous-line (arg) |
|
| (interactive "p") (previous-line arg) (run-hooks 'forth-motion-hooks)) |
|
| (defun forth-backward-char (arg) |
|
| (interactive "p") (backward-char arg) (run-hooks 'forth-motion-hooks)) |
|
| (defun forth-forward-char (arg) |
|
| (interactive "p") (forward-char arg) (run-hooks 'forth-motion-hooks)) |
|
| (defun forth-forward-word (arg) |
|
| (interactive "p") (forward-word arg) (run-hooks 'forth-motion-hooks)) |
|
| (defun forth-backward-word (arg) |
|
| (interactive "p") (backward-word arg) (run-hooks 'forth-motion-hooks)) |
|
| (defun forth-scroll-down (arg) |
|
| (interactive "P") (scroll-down arg) (run-hooks 'forth-motion-hooks)) |
|
| (defun forth-scroll-up (arg) |
|
| (interactive "P") (scroll-up arg) (run-hooks 'forth-motion-hooks)) |
|
| |
|
| ;setup for C-h C-i to work |
|
| (if (fboundp 'info-lookup-add-help) |
|
| (info-lookup-add-help |
|
| :topic 'symbol |
|
| :mode 'forth-mode |
|
| :regexp "[^ |
|
| ]+" |
|
| :ignore-case t |
|
| :doc-spec '(("(gforth)Name Index" nil "`" "' ")))) |
|
| |
|
| (load "etags") |
(require 'etags) |
| |
|
| (defun forth-find-tag (tagname &optional next-p regexp-p) |
(defun forth-find-tag (tagname &optional next-p regexp-p) |
| (interactive (find-tag-interactive "Find tag: ")) |
(interactive (find-tag-interactive "Find tag: ")) |
| |
(unless (or regexp-p next-p) |
| |
(setq tagname (concat "\\(^\\|\\s-+\\)\\(" (regexp-quote tagname) |
| |
"\\)\\s-*\x7f"))) |
| (switch-to-buffer |
(switch-to-buffer |
| (find-tag-noselect (concat " " tagname " ") next-p regexp-p))) |
(find-tag-noselect tagname next-p t))) |
| |
|
| (defvar forth-mode-syntax-table nil |
(defvar forth-mode-syntax-table nil |
| "Syntax table in use in Forth-mode buffers.") |
"Syntax table in use in Forth-mode buffers.") |
| (setq char (1+ char)))) |
(setq char (1+ char)))) |
| )) |
)) |
| |
|
| |
|
| (defun forth-mode-variables () |
(defun forth-mode-variables () |
| (set-syntax-table forth-mode-syntax-table) |
(set-syntax-table forth-mode-syntax-table) |
| (setq local-abbrev-table forth-mode-abbrev-table) |
(setq local-abbrev-table forth-mode-abbrev-table) |
| (make-local-variable 'comment-column) |
(make-local-variable 'comment-column) |
| (setq comment-column 40) |
(setq comment-column 40) |
| (make-local-variable 'comment-start-skip) |
(make-local-variable 'comment-start-skip) |
| (setq comment-start-skip "\\ ") |
(setq comment-start-skip "\\\\ ") |
| (make-local-variable 'comment-indent-hook) |
(make-local-variable 'comment-indent-function) |
| (setq comment-indent-hook 'forth-comment-indent) |
(setq comment-indent-function 'forth-comment-indent) |
| (make-local-variable 'parse-sexp-ignore-comments) |
(make-local-variable 'parse-sexp-ignore-comments) |
| (setq parse-sexp-ignore-comments t) |
(setq parse-sexp-ignore-comments t) |
| (setq case-fold-search t) |
(setq case-fold-search t) |
| |
(make-local-variable 'forth-was-point) |
| |
(setq forth-was-point -1) |
| (make-local-variable 'forth-words) |
(make-local-variable 'forth-words) |
| (make-local-variable 'forth-compiled-words) |
(make-local-variable 'forth-compiled-words) |
| (make-local-variable 'forth-compiled-indent-words) |
(make-local-variable 'forth-compiled-indent-words) |
| (make-local-variable 'forth-use-objects) |
(make-local-variable 'forth-use-objects) |
| (setq forth-screen-marker (copy-marker 0)) |
(setq forth-screen-marker (copy-marker 0)) |
| (add-hook 'after-change-functions 'forth-change-function) |
(add-hook 'after-change-functions 'forth-change-function) |
| |
(if (and forth-jit-parser (>= emacs-major-version 21)) |
| |
(add-hook 'fontification-functions 'forth-fontification-function)) |
| (setq imenu-create-index-function 'forth-create-index)) |
(setq imenu-create-index-function 'forth-create-index)) |
| |
|
| ;;;###autoload |
;;;###autoload |
| only. Block files are autodetected, when read, and converted to normal |
only. Block files are autodetected, when read, and converted to normal |
| stream source format. See also `forth-block-mode'. |
stream source format. See also `forth-block-mode'. |
| \\{forth-mode-map} |
\\{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: |
Variables controlling syntax hilighting/recognition of parsed text: |
| `forth-words' |
`forth-words' |
| .emacs, or by setting forth-local-words, in source-files' local |
.emacs, or by setting forth-local-words, in source-files' local |
| variables lists. |
variables lists. |
| forth-use-objects |
forth-use-objects |
| Set this variable to non-nil in your .emacs, or a local variables |
Set this variable to non-nil in your .emacs, or in a local variables |
| list, to hilight and recognize the words from the \"Objects\" package |
list, to hilight and recognize the words from the \"Objects\" package |
| for object-oriented programming. |
for object-oriented programming. |
| forth-use-oof |
forth-use-oof |
| length that can be stored into a block file). This variable defaults to |
length that can be stored into a block file). This variable defaults to |
| t for `forth-block-mode' and to nil for `forth-mode'. |
t for `forth-block-mode' and to nil for `forth-mode'. |
| |
|
| Variables controling documentation search |
Variables controlling interaction with the Forth-process (also see |
| forth-help-load-path |
`run-forth'): |
| List of directories to search through to find *.doc |
forth-program-name |
| (forth-help-file-suffix) files. Nil means current default directory. |
Program invoked by the `run-forth' command (including arguments). |
| The specified directories must contain at least one .doc file. If it |
inferior-forth-mode-hook |
| does not and you still want the load-path to scan that directory, create |
Hook for customising inferior-forth-mode. |
| an empty file dummy.doc. |
forth-compile-command |
| forth-help-file-suffix |
Default command to execute on `compile'. |
| The file names to search for in each directory specified by |
|
| forth-help-load-path. Defaulted to '*.doc'. |
|
| " |
" |
| (interactive) |
(interactive) |
| (kill-all-local-variables) |
(kill-all-local-variables) |
| (use-local-map forth-mode-map) |
(use-local-map forth-mode-map) |
| (setq mode-name "Forth") |
(setq mode-name "Forth") |
| (setq major-mode 'forth-mode) |
(setq major-mode 'forth-mode) |
| |
(forth-install-motion-hook) |
| ;; convert buffer contents from block file format, if necessary |
;; convert buffer contents from block file format, if necessary |
| (when (forth-detect-block-file-p) |
(when (forth-detect-block-file-p) |
| (widen) |
(widen) |
| (forth-hack-local-variables) |
(forth-hack-local-variables) |
| (forth-customize-words) |
(forth-customize-words) |
| (forth-compile-words) |
(forth-compile-words) |
| (forth-change-function (point-min) (point-max) nil t))) |
(unless (and forth-jit-parser (>= emacs-major-version 21)) |
| |
(forth-change-function (point-min) (point-max) nil t)))) |
| |
|
| (defun forth-fill-paragraph () |
(defun forth-fill-paragraph () |
| "Fill comments (starting with '\'; do not fill code (block style |
"Fill comments (starting with '\'; do not fill code (block style |
| (interactive) |
(interactive) |
| (query-replace-regexp "\\(~~ \\| ~~$\\)" "" nil)) |
(query-replace-regexp "\\(~~ \\| ~~$\\)" "" nil)) |
| |
|
| (defvar forth-program-name "gforth" |
(define-key forth-mode-map "\C-x\C-e" 'compile) |
| "*Program invoked by the `run-forth' command.") |
(define-key forth-mode-map "\C-x\C-n" 'next-error) |
| |
(require 'compile) |
| (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 |
|
| |
|
| (setq auto-mode-alist (append auto-mode-alist |
(defvar forth-compile-command "gforth ") |
| '(("\\.fs$" . forth-mode)))) |
;(defvar forth-compilation-window-percent-height 30) |
| |
|
| (defun forth-split () |
(defun forth-split () |
| (interactive) |
(interactive) |
| (progn |
(progn |
| (delete-other-windows) |
(delete-other-windows) |
| (split-window-vertically |
(split-window-vertically |
| (/ (* (screen-height) forth-percent-height) 100)) |
(/ (frame-height) 2)) |
| (other-window 1) |
(other-window 1) |
| (switch-to-buffer buffer) |
(switch-to-buffer buffer) |
| (goto-char (point-max)) |
(goto-char (point-max)) |
| (other-window 1)))) |
(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 "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)))) |
|
| (forth-split-1 "*compilation*") |
|
| (setq ctools-compile-command command) |
|
| (compile1 ctools-compile-command "No more errors")) |
|
| |
|
| |
|
| ;;; Forth menu |
;;; Forth menu |
| ;;; Mikael Karlsson <qramika@eras70.ericsson.se> |
;;; Mikael Karlsson <qramika@eras70.ericsson.se> |
| |
|
| (cond ((string-match "XEmacs\\|Lucid" emacs-version) |
;; (dk) code commented out due to complaints of XEmacs users. After |
| (require 'func-menu) |
;; all, there's imenu/speedbar, which uses much smarter scanning |
| |
;; rules. |
| (defconst fume-function-name-regexp-forth |
|
| "^\\(:\\)[ \t]+\\([^ \t]*\\)" |
;; (cond ((string-match "XEmacs\\|Lucid" emacs-version) |
| "Expression to get word definitions in Forth.") |
;; (require 'func-menu) |
| |
|
| (setq fume-function-name-regexp-alist |
;; (defconst fume-function-name-regexp-forth |
| (append '((forth-mode . fume-function-name-regexp-forth) |
;; "^\\(:\\)[ \t]+\\([^ \t]*\\)" |
| ) fume-function-name-regexp-alist)) |
;; "Expression to get word definitions in Forth.") |
| |
|
| ;; Find next forth word in the buffer |
;; (setq fume-function-name-regexp-alist |
| (defun fume-find-next-forth-function-name (buffer) |
;; (append '((forth-mode . fume-function-name-regexp-forth) |
| "Searches for the next forth word in BUFFER." |
;; ) fume-function-name-regexp-alist)) |
| (set-buffer buffer) |
|
| (if (re-search-forward fume-function-name-regexp nil t) |
;; ;; Find next forth word in the buffer |
| (let ((beg (match-beginning 2)) |
;; (defun fume-find-next-forth-function-name (buffer) |
| (end (match-end 2))) |
;; "Searches for the next forth word in BUFFER." |
| (cons (buffer-substring beg end) beg)))) |
;; (set-buffer buffer) |
| |
;; (if (re-search-forward fume-function-name-regexp nil t) |
| |
;; (let ((beg (match-beginning 2)) |
| |
;; (end (match-end 2))) |
| |
;; (cons (buffer-substring beg end) beg)))) |
| |
|
| (setq fume-find-function-name-method-alist |
;; (setq fume-find-function-name-method-alist |
| (append '((forth-mode . fume-find-next-forth-function-name)))) |
;; (append '((forth-mode . fume-find-next-forth-function-name)))) |
| |
|
| )) |
;; )) |
| ;;; End Forth menu |
;;; End Forth menu |
| |
|
| ;;; File folding of forth-files |
;;; File folding of forth-files |
| ;;; Works most of the times but loses sync with the cursor occasionally |
;;; Works most of the times but loses sync with the cursor occasionally |
| ;;; Could be improved by also folding on comments |
;;; Could be improved by also folding on comments |
| |
|
| (require 'outline) |
;; (dk) This code needs a rewrite; just too ugly and doesn't use the |
| |
;; newer and smarter scanning rules of `imenu'. Who needs it anyway?? |
| |
|
| (defun f-outline-level () |
;; (require 'outline) |
| (cond ((looking-at "\\`\\\\") |
|
| 0) |
|
| ((looking-at "\\\\ SEC") |
|
| 0) |
|
| ((looking-at "\\\\ \\\\ .*") |
|
| 0) |
|
| ((looking-at "\\\\ DEFS") |
|
| 1) |
|
| ((looking-at "\\/\\* ") |
|
| 1) |
|
| ((looking-at ": .*") |
|
| 1) |
|
| ((looking-at "\\\\G") |
|
| 2) |
|
| ((looking-at "[ \t]+\\\\") |
|
| 3)) |
|
| ) |
|
| |
|
| (defun fold-f () |
|
| (interactive) |
|
| (add-hook 'outline-minor-mode-hook 'hide-body) |
|
| |
|
| ; outline mode header start, i.e. find word definitions |
;; (defun f-outline-level () |
| ;;; (setq outline-regexp "^\\(:\\)[ \t]+\\([^ \t]*\\)") |
;; (cond ((looking-at "\\`\\\\") |
| (setq outline-regexp "\\`\\\\\\|:\\|\\\\ SEC\\|\\\\G\\|[ \t]+\\\\\\|\\\\ DEFS\\|\\/\\*\\|\\\\ \\\\ .*") |
;; 0) |
| (setq outline-level 'f-outline-level) |
;; ((looking-at "\\\\ SEC") |
| |
;; 0) |
| (outline-minor-mode) |
;; ((looking-at "\\\\ \\\\ .*") |
| (define-key outline-minor-mode-map '(shift up) 'hide-sublevels) |
;; 0) |
| (define-key outline-minor-mode-map '(shift right) 'show-children) |
;; ((looking-at "\\\\ DEFS") |
| (define-key outline-minor-mode-map '(shift left) 'hide-subtree) |
;; 1) |
| (define-key outline-minor-mode-map '(shift down) 'show-subtree) |
;; ((looking-at "\\/\\* ") |
| |
;; 1) |
| |
;; ((looking-at ": .*") |
| |
;; 1) |
| |
;; ((looking-at "\\\\G") |
| |
;; 2) |
| |
;; ((looking-at "[ \t]+\\\\") |
| |
;; 3))) |
| |
|
| |
;; (defun fold-f () |
| |
;; (interactive) |
| |
;; (add-hook 'outline-minor-mode-hook 'hide-body) |
| |
|
| |
;; ; outline mode header start, i.e. find word definitions |
| |
;; ;;; (setq outline-regexp "^\\(:\\)[ \t]+\\([^ \t]*\\)") |
| |
;; (setq outline-regexp "\\`\\\\\\|:\\|\\\\ SEC\\|\\\\G\\|[ \t]+\\\\\\|\\\\ DEFS\\|\\/\\*\\|\\\\ \\\\ .*") |
| |
;; (setq outline-level 'f-outline-level) |
| |
|
| |
;; (outline-minor-mode) |
| |
;; (define-key outline-minor-mode-map '(shift up) 'hide-sublevels) |
| |
;; (define-key outline-minor-mode-map '(shift right) 'show-children) |
| |
;; (define-key outline-minor-mode-map '(shift left) 'hide-subtree) |
| |
;; (define-key outline-minor-mode-map '(shift down) 'show-subtree)) |
| |
|
| ) |
|
| |
|
| ;;(define-key global-map '(shift up) 'fold-f) |
;;(define-key global-map '(shift up) 'fold-f) |
| |
|
| ;;; for all of the recognized languages. Scanning the buffer takes some time, |
;;; for all of the recognized languages. Scanning the buffer takes some time, |
| ;;; but not much. |
;;; but not much. |
| ;;; |
;;; |
| (cond ((string-match "XEmacs\\|Lucid" emacs-version) |
;; (cond ((string-match "XEmacs\\|Lucid" emacs-version) |
| (require 'func-menu) |
;; (require 'func-menu) |
| ;; (define-key global-map 'f8 'function-menu) |
;; ;; (define-key global-map 'f8 'function-menu) |
| (add-hook 'find-fible-hooks 'fume-add-menubar-entry) |
;; (add-hook 'find-fible-hooks 'fume-add-menubar-entry) |
| ; (define-key global-map "\C-cg" 'fume-prompt-function-goto) |
;; ; (define-key global-map "\C-cg" 'fume-prompt-function-goto) |
| ; (define-key global-map '(shift button3) 'mouse-function-menu) |
;; ; (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 (null (re-search-backward "\n[ \t]*\n" nil t)) |
| |
(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 (my-start end) |
| |
"Send the current region to the inferior Forth process. |
| |
Then switch to the process buffer." |
| |
(interactive "r") |
| |
(forth-send-region my-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 |
;;; gforth.el ends here |