1;;; gforth.el --- major mode for editing (G)Forth sources
2
3;; Copyright (C) 1995,1996,1997,1998,2000,2001,2003,2004,2007,2008 Free Software Foundation, Inc.
4
5;; This file is part of Gforth.
6
7;; GForth is distributed in the hope that it will be useful,
8;; but WITHOUT ANY WARRANTY.  No author or distributor
9;; accepts responsibility to anyone for the consequences of using it
10;; or for whether it serves any particular purpose or works at all,
11;; unless he says so in writing.  Refer to the GNU Emacs General Public
12;; License for full details.
13
14;; Everyone is granted permission to copy, modify and redistribute
15;; GNU Emacs, but only under the conditions described in the
16;; GNU Emacs General Public License.   A copy of this license is
17;; supposed to have been given to you along with Gforth so you
18;; can know your rights and responsibilities.  It should be in a
19;; file named COPYING.  Among other things, the copyright notice
20;; and this notice must be preserved on all copies.
21
22;; Author: Goran Rydqvist <gorry@ida.liu.se>
23;; Maintainer: David K�hling <dvdkhlng@gmx.de>
24;; Created: 16 July 88 by Goran Rydqvist
25;; Keywords: forth, gforth
26
27;; Changes by anton
28;; This is a variant of forth.el that came with TILE.
29;; I left most of this stuff untouched and made just a few changes for
30;; the things I use (mainly indentation and syntax tables).
31;; So there is still a lot of work to do to adapt this to gforth.
32
33;; Changes by David
34;; Added a syntax-hilighting engine, rewrote auto-indentation engine.
35;; Added support for block files.
36;; Replaced forth-process code with comint-based implementation.
37
38;; Tested with Emacs 19.34, 20.5, 21 and XEmacs 21
39
40;;-------------------------------------------------------------------
41;; A Forth indentation, documentation search and interaction library
42;;-------------------------------------------------------------------
43;;
44;; Written by Goran Rydqvist, gorry@ida.liu.se, Summer 1988
45;; Started:	16 July 88
46;; Version:	2.10
47;; Last update:	5 December 1989 by Mikael Patel, mip@ida.liu.se
48;; Last update:	25 June 1990 by Goran Rydqvist, gorry@ida.liu.se
49;;
50;; Documentation: See forth-mode (^HF forth-mode)
51;;-------------------------------------------------------------------
52
53;;; Code:
54
55;(setq debug-on-error t)
56
57;; Code ripped from `version.el' for compatability with Emacs versions
58;; prior to 19.23.
59(if (not (boundp 'emacs-major-version))
60    (defconst emacs-major-version
61      (progn (string-match "^[0-9]+" emacs-version)
62	     (string-to-number (match-string 0 emacs-version)))))
63
64;; Code ripped from `subr.el' for compatability with Emacs versions
65;; prior to 20.1
66(eval-when-compile
67(defun forth-emacs-older (major minor)
68  (or (< emacs-major-version major)
69      (and (= emacs-major-version major) (< emacs-minor-version minor))))
70
71  (if (forth-emacs-older 20 1)
72      (progn
73	(defmacro when (cond &rest body)
74	  "If COND yields non-nil, do BODY, else return nil."
75	  (list 'if cond (cons 'progn body)))
76	(defmacro unless (cond &rest body)
77	  "If COND yields nil, do BODY, else return nil."
78	  (cons 'if (cons cond (cons nil body)))))))
79
80;; `no-error' argument of require not supported in Emacs versions
81;; prior to 20.4 :-(
82(eval-and-compile
83(defun forth-require (feature)
84    (condition-case err (require feature) (error nil))))
85
86(require 'font-lock)
87
88;; define `font-lock-warning-face' in emacs-versions prior to 20.1
89;; (ripped from `font-lock.el')
90(unless (boundp 'font-lock-warning-face)
91  (message "defining font-lock-warning-face")
92  (make-face 'font-lock-warning-face)
93  (defvar font-lock-warning-face 'font-lock-warning-face)
94  (set-face-foreground font-lock-warning-face "red")
95  (make-face-bold font-lock-warning-face))
96
97;; define `font-lock-constant-face' in XEmacs (just copy
98;; `font-lock-preprocessor-face')
99(unless (boundp 'font-lock-constant-face)
100  (copy-face font-lock-preprocessor-face 'font-lock-constant-face))
101
102
103;; define `regexp-opt' in emacs versions prior to 20.1
104;; (this implementation is extremely inefficient, though)
105(eval-and-compile (forth-require 'regexp-opt))
106(unless (memq 'regexp-opt features)
107  (message (concat
108	    "Warning: your Emacs version doesn't support `regexp-opt'. "
109            "Hilighting will be slow."))
110  (defun regexp-opt (STRINGS &optional PAREN)
111    (let ((open (if PAREN "\\(" "")) (close (if PAREN "\\)" "")))
112      (concat open (mapconcat 'regexp-quote STRINGS "\\|") close)))
113  (defun regexp-opt-depth (re)
114    (if (string= (substring re 0 2) "\\(") 1 0)))
115
116; todo:
117;
118
119; screen-height existiert nicht in XEmacs, frame-height ersetzen?
120;
121
122; W�rter ordentlich hilighten, die nicht auf Whitespace beginnen ( ..)IF
123; -- mit aktueller Konzeption nicht m�glich??
124;
125; Konfiguration �ber customization groups
126;
127; Bereich nur auf Wortanfang/ende ausweiten, wenn Anfang bzw Ende in einem
128; Wort liegen (?) -- speed!
129;
130; 'forth-word' property muss eindeutig sein!
131;
132; Forth-Menu
133;
134; Interface zu GForth Prozessen (Patches von Michael Scholz)
135;
136; Byte-compile-Code rausschmei�en, Compilieren im Makefile �ber Emacs
137; batch-Modus
138;
139; forth-help Kram rausschmei�en
140;
141; XEmacs Kompatibilit�t? imenu/speedbar -> fume?
142;
143; Folding neuschreiben (neue Parser-Informationen benutzen)
144
145;;; Motion-hooking (dk)
146;;;
147(defun forth-idle-function ()
148  "Function that is called when Emacs is idle to detect cursor motion
149in forth-block-mode buffers (which is mainly used for screen number
150display in).  Currently ignores forth-mode buffers but that may change
151in the future."
152  (if (eq major-mode 'forth-block-mode)
153      (forth-check-motion)))
154
155(defvar forth-idle-function-timer nil
156  "Timer that runs `forth-idle-function' or nil if no timer installed.")
157
158(defun forth-install-motion-hook ()
159  "Install the motion-hooking mechanism.  Currently uses idle timers
160but might be transparently changed in the future."
161  (unless forth-idle-function-timer
162    ;; install idle function only once (first time forth-mode is used)
163    (setq forth-idle-function-timer
164	  (run-with-idle-timer .05 t 'forth-idle-function))))
165
166(defvar forth-was-point nil)
167
168(defun forth-check-motion ()
169  "Run `forth-motion-hooks', if `point' changed since last call.  This
170used to be called via `post-command-hook' but uses idle timers now as
171users complaint about lagging performance."
172  (when (or (eq forth-was-point nil) (/= forth-was-point (point)))
173    (setq forth-was-point (point))
174    (run-hooks 'forth-motion-hooks)))
175
176
177;;; Hilighting and indentation engine (dk)
178;;;
179(defvar forth-disable-parser nil
180  "*Non-nil means to disable on-the-fly parsing of Forth-code.
181
182This will disable hilighting of forth-mode buffers and will decrease
183the smartness of the indentation engine. Only set it to non-nil, if
184your computer is very slow. To disable hilighting, set
185`forth-hilight-level' to zero.")
186
187(defvar forth-jit-parser nil
188  "*Non-nil means to parse Forth-code just-in-time.
189
190This eliminates the need for initially parsing forth-mode buffers and
191thus speeds up loading of Forth files. That feature is only available
192in Emacs21 (and newer versions).")
193
194(defvar forth-words nil
195  "List of words for hilighting and recognition of parsed text areas.
196
197Hilighting of object-oriented Forth code is achieved, by appending either
198`forth-objects-words' or `forth-oof-words' to the list, depending on the values of `forth-use-objects' or `forth-use-oof'.
199
200After `forth-words' changed, `forth-compile-words' must be called to
201make the changes take effect.
202
203Each item of `forth-words' has the form
204   (MATCHER TYPE HILIGHT . &optional PARSED-TEXT ...)
205
206MATCHER is either a list of strings to match, or a REGEXP.
207   If it's a REGEXP, it should not be surrounded by '\\<' or '\\>', since
208   that'll be done automatically by the search routines.
209
210TYPE should be one of 'definiton-starter', 'definition-ender', 'compile-only',
211   'immediate' or 'non-immediate'. Those information are required to determine
212   whether a word actually parses (and whether that parsed text needs to be
213   hilighted).
214
215HILIGHT is a cons cell of the form (FACE . MINIMUM-LEVEL)
216   Where MINIMUM-LEVEL specifies the minimum value of `forth-hilight-level',
217   that's required for matching text to be hilighted.
218
219PARSED-TEXT specifies whether and how a word parses following text. You can
220   specify as many subsequent PARSED-TEXT as you wish, but that shouldn't be
221   necessary very often. It has the following form:
222   (DELIM-REGEXP SKIP-LEADING-FLAG PARSED-TYPE HILIGHT)
223
224DELIM-REGEXP is a regular expression that should match strings of length 1,
225   which are delimiters for the parsed text.
226
227A non-nil value for PARSE-LEADING-FLAG means, that leading delimiter strings
228   before parsed text should be skipped. This is the parsing behaviour of the
229   Forth word WORD. Set it to t for name-parsing words, nil for comments and
230   strings.
231
232PARSED-TYPE specifies what kind of text is parsed. It should be on of 'name',
233   'string' or 'comment'.")
234(setq forth-words
235      '(
236	(("[") definition-ender (font-lock-keyword-face . 1))
237	(("]" "]l") definition-starter (font-lock-keyword-face . 1))
238	((":") definition-starter (font-lock-keyword-face . 1)
239	 "[ \t\n]" t name (font-lock-function-name-face . 3))
240	(("immediate" "compile-only" "restrict")
241	 immediate (font-lock-keyword-face . 1))
242	(("does>") compile-only (font-lock-keyword-face . 1))
243	((":noname") definition-starter (font-lock-keyword-face . 1))
244	((";" ";code") definition-ender (font-lock-keyword-face . 1))
245	(("include" "require" "needs" "use")
246	 non-immediate (font-lock-keyword-face . 1)
247	 "[\n\t ]" t string (font-lock-string-face . 1))
248	(("included" "required" "thru" "load")
249	 non-immediate (font-lock-keyword-face . 1))
250	(("[char]") compile-only (font-lock-keyword-face . 1)
251	 "[ \t\n]" t string (font-lock-string-face . 1))
252	(("char") non-immediate (font-lock-keyword-face . 1)
253	 "[ \t\n]" t string (font-lock-string-face . 1))
254	(("s\"" "c\"") immediate (font-lock-string-face . 1)
255	 "[\"\n]" nil string (font-lock-string-face . 1))
256	((".\"") compile-only (font-lock-string-face . 1)
257	 "[\"\n]" nil string (font-lock-string-face . 1))
258	(("abort\"") compile-only (font-lock-keyword-face . 1)
259	 "[\"\n]" nil string (font-lock-string-face . 1))
260	(("{") compile-only (font-lock-variable-name-face . 1)
261	 "[\n}]" nil name (font-lock-variable-name-face . 1))
262	((".(" "(") immediate (font-lock-comment-face . 1)
263	  ")" nil comment (font-lock-comment-face . 1))
264	(("\\" "\\G") immediate (font-lock-comment-face . 1)
265	 "[\n]" nil comment (font-lock-comment-face . 1))
266
267	(("[if]" "[?do]" "[do]" "[for]" "[begin]"
268	  "[endif]" "[then]" "[loop]" "[+loop]" "[next]" "[until]" "[repeat]"
269	  "[again]" "[while]" "[else]")
270	 immediate (font-lock-keyword-face . 2))
271	(("[ifdef]" "[ifundef]") immediate (font-lock-keyword-face . 2)
272	 "[ \t\n]" t name (font-lock-function-name-face . 3))
273	(("if" "begin" "ahead" "do" "?do" "+do" "u+do" "-do" "u-do" "for"
274	  "case" "of" "?dup-if" "?dup-0=-if" "then" "endif" "until"
275	  "repeat" "again" "leave" "?leave"
276	  "loop" "+loop" "-loop" "next" "endcase" "endof" "else" "while" "try"
277	  "recover" "endtry" "iferror" "restore" "endtry-iferror"
278	  "assert(" "assert0(" "assert1(" "assert2("
279	  "assert3(" ")" "<interpretation" "<compilation" "interpretation>"
280	  "compilation>")
281	 compile-only (font-lock-keyword-face . 2))
282
283	(("true" "false" "c/l" "bl" "cell" "pi" "w/o" "r/o" "r/w")
284	 non-immediate (font-lock-constant-face . 2))
285	(("~~" "break:" "dbg") compile-only (font-lock-warning-face . 2))
286	(("break\"") compile-only (font-lock-warning-face . 1)
287	 "[\"\n]" nil string (font-lock-string-face . 1))
288	(("postpone" "[is]" "defers" "[']" "[compile]")
289	 compile-only (font-lock-keyword-face . 2)
290	 "[ \t\n]" t name (font-lock-function-name-face . 3))
291	(("is" "what's") immediate (font-lock-keyword-face . 2)
292	 "[ \t\n]" t name (font-lock-function-name-face . 3))
293	(("<is>" "'" "see") non-immediate (font-lock-keyword-face . 2)
294	 "[ \t\n]" t name (font-lock-function-name-face . 3))
295	(("[to]") compile-only (font-lock-keyword-face . 2)
296	 "[ \t\n]" t name (font-lock-variable-name-face . 3))
297	(("to") immediate (font-lock-keyword-face . 2)
298	 "[ \t\n]" t name (font-lock-variable-name-face . 3))
299	(("<to>") non-immediate (font-lock-keyword-face . 2)
300	 "[ \t\n]" t name (font-lock-variable-name-face . 3))
301
302	(("create" "variable" "constant" "2variable" "2constant" "fvariable"
303	  "fconstant" "value" "field" "user" "vocabulary"
304	  "create-interpret/compile")
305	 non-immediate (font-lock-type-face . 2)
306	 "[ \t\n]" t name (font-lock-variable-name-face . 3))
307	("\\S-+%" non-immediate (font-lock-type-face . 2))
308	(("defer" "alias" "create-interpret/compile:")
309	 non-immediate (font-lock-type-face . 1)
310	 "[ \t\n]" t name (font-lock-function-name-face . 3))
311	(("end-struct") non-immediate (font-lock-keyword-face . 2)
312	 "[ \t\n]" t name (font-lock-type-face . 3))
313	(("struct") non-immediate (font-lock-keyword-face . 2))
314	("-?[0-9]+\\(\\.[0-9]*e\\(-?[0-9]+\\)?\\|\\.?[0-9a-f]*\\)"
315	 immediate (font-lock-constant-face . 3))
316	))
317
318(defvar forth-use-objects nil
319  "*Non-nil makes forth-mode also hilight words from the \"Objects\" package.")
320(defvar forth-objects-words
321  '(((":m") definition-starter (font-lock-keyword-face . 1)
322     "[ \t\n]" t name (font-lock-function-name-face . 3))
323    (("m:") definition-starter (font-lock-keyword-face . 1))
324    ((";m") definition-ender (font-lock-keyword-face . 1))
325    (("[current]" "[parent]") compile-only (font-lock-keyword-face . 1)
326     "[ \t\n]" t name (font-lock-function-name-face . 3))
327    (("current" "overrides") non-immediate (font-lock-keyword-face . 2)
328     "[ \t\n]" t name (font-lock-function-name-face . 3))
329    (("[to-inst]") compile-only (font-lock-keyword-face . 2)
330     "[ \t\n]" t name (font-lock-variable-name-face . 3))
331    (("[bind]") compile-only (font-lock-keyword-face . 2)
332     "[ \t\n]" t name (font-lock-type-face . 3)
333     "[ \t\n]" t name (font-lock-function-name-face . 3))
334    (("bind") non-immediate (font-lock-keyword-face . 2)
335     "[ \t\n]" t name (font-lock-type-face . 3)
336     "[ \t\n]" t name (font-lock-function-name-face . 3))
337    (("inst-var" "inst-value") non-immediate (font-lock-type-face . 2)
338     "[ \t\n]" t name (font-lock-variable-name-face . 3))
339    (("method" "selector")
340     non-immediate (font-lock-type-face . 1)
341     "[ \t\n]" t name (font-lock-function-name-face . 3))
342    (("end-class" "end-interface")
343     non-immediate (font-lock-keyword-face . 2)
344     "[ \t\n]" t name (font-lock-type-face . 3))
345    (("public" "protected" "class" "exitm" "implementation" "interface"
346      "methods" "end-methods" "this")
347     non-immediate (font-lock-keyword-face . 2))
348    (("object") non-immediate (font-lock-type-face . 2)))
349  "Hilighting description for words of the \"Objects\" package")
350
351
352(defvar forth-use-oof nil
353  "*Non-nil makes forth-mode also hilight words from the \"OOF\" package.")
354(defvar forth-oof-words
355  '((("class") non-immediate (font-lock-keyword-face . 2)
356     "[ \t\n]" t name (font-lock-type-face . 3))
357    (("var") non-immediate (font-lock-type-face . 2)
358     "[ \t\n]" t name (font-lock-variable-name-face . 3))
359    (("method" "early") non-immediate (font-lock-type-face . 2)
360     "[ \t\n]" t name (font-lock-function-name-face . 3))
361    (("::" "super" "bind" "bound" "link")
362     immediate (font-lock-keyword-face . 2)
363     "[ \t\n]" t name (font-lock-function-name-face . 3))
364    (("ptr" "asptr" "[]")
365     immediate (font-lock-keyword-face . 2)
366     "[ \t\n]" t name (font-lock-variable-name-face . 3))
367    (("class;" "how:" "self" "new" "new[]" "definitions" "class?" "with"
368      "endwith")
369     non-immediate (font-lock-keyword-face . 2))
370    (("object") non-immediate (font-lock-type-face . 2)))
371  "Hilighting description for words of the \"OOF\" package")
372
373(defvar forth-local-words nil
374  "List of Forth words to prepend to `forth-words'. Should be set by a
375 forth source, using a local variables list at the end of the file
376 (\"Local Variables: ... forth-local-words: ... End:\" construct).")
377
378(defvar forth-custom-words nil
379  "List of Forth words to prepend to `forth-words'. Should be set in your
380 .emacs.")
381
382(defvar forth-hilight-level 3 "*Level of hilighting of Forth code.")
383
384(defvar forth-compiled-words nil "Compiled representation of `forth-words'.")
385
386(defvar forth-indent-words nil
387  "List of words that have indentation behaviour.
388Each element of `forth-indent-words' should have the form
389   (MATCHER INDENT1 INDENT2 &optional TYPE)
390
391MATCHER is either a list of strings to match, or a REGEXP.
392   If it's a REGEXP, it should not be surrounded by `\\<` or `\\>`, since
393   that'll be done automatically by the search routines.
394
395TYPE might be omitted. If it's specified, the only allowed value is
396   currently the symbol `non-immediate', meaning that the word will not
397   have any effect on indentation inside definitions. (:NONAME is a good
398   example for this kind of word).
399
400INDENT1 specifies how to indent a word that's located at the beginning
401   of a line, following any number of whitespaces.
402
403INDENT2 specifies how to indent words that are not located at the
404   beginning of a line.
405
406INDENT1 and INDENT2 are indentation specifications of the form
407   (SELF-INDENT . NEXT-INDENT), where SELF-INDENT is a numerical value,
408   specifying how the matching line and all following lines are to be
409   indented, relative to previous lines. NEXT-INDENT specifies how to indent
410   following lines, relative to the matching line.
411
412   Even values of SELF-INDENT and NEXT-INDENT correspond to multiples of
413   `forth-indent-level'. Odd values get an additional
414   `forth-minor-indent-level' added/substracted. Eg a value of -2 indents
415   1 * forth-indent-level  to the left, wheras 3 indents
416   1 * forth-indent-level + forth-minor-indent-level  columns to the right.")
417
418(setq forth-indent-words
419      '((("if" "begin" "do" "?do" "+do" "-do" "u+do"
420	  "u-do" "?dup-if" "?dup-0=-if" "case" "of" "try" "iferror"
421	  "[if]" "[ifdef]" "[ifundef]" "[begin]" "[for]" "[do]" "[?do]")
422	 (0 . 2) (0 . 2))
423	((":" ":noname" "code" "struct" "m:" ":m" "class" "interface")
424	 (0 . 2) (0 . 2) non-immediate)
425	("\\S-+%$" (0 . 2) (0 . 0) non-immediate)
426	((";" ";m") (-2 . 0) (0 . -2))
427	(("again" "then" "endif" "endtry" "endcase" "endof"
428	  "[then]" "[endif]" "[loop]" "[+loop]" "[next]"
429	  "[until]" "[again]" "loop")
430	 (-2 . 0) (0 . -2))
431	(("end-code" "end-class" "end-interface" "end-class-noname"
432	  "end-interface-noname" "end-struct" "class;")
433	 (-2 . 0) (0 . -2) non-immediate)
434	(("protected" "public" "how:") (-1 . 1) (0 . 0) non-immediate)
435	(("+loop" "-loop" "until") (-2 . 0) (-2 . 0))
436	(("else" "recover" "restore" "endtry-iferror" "[else]")
437	 (-2 . 2) (0 . 0))
438	(("does>") (-1 . 1) (0 . 0))
439	(("while" "[while]") (-2 . 4) (0 . 2))
440	(("repeat" "[repeat]") (-4 . 0) (0 . -4))))
441
442(defvar forth-local-indent-words nil
443  "List of Forth words to prepend to `forth-indent-words', when a forth-mode
444buffer is created. Should be set by a Forth source, using a local variables
445list at the end of the file (\"Local Variables: ... forth-local-words: ...
446End:\" construct).")
447
448(defvar forth-custom-indent-words nil
449  "List of Forth words to prepend to `forth-indent-words'. Should be set in
450 your .emacs.")
451
452(defvar forth-indent-level 4
453  "*Indentation of Forth statements.")
454(defvar forth-minor-indent-level 2
455  "*Minor indentation of Forth statements.")
456(defvar forth-compiled-indent-words nil)
457
458;(setq debug-on-error t)
459
460;; Filter list by predicate. This is a somewhat standard function for
461;; functional programming languages. So why isn't it already implemented
462;; in Lisp??
463(defun forth-filter (predicate list)
464  (let ((filtered nil))
465    (dolist (item list)
466	      (when (funcall predicate item)
467		(if filtered
468		    (nconc filtered (list item))
469          (setq filtered (cons item nil)))))
470    filtered))
471
472;; Helper function for `forth-compile-word': return whether word has to be
473;; added to the compiled word list, for syntactic parsing and hilighting.
474(defun forth-words-filter (word)
475  (let* ((hilight (nth 2 word))
476	 (level (cdr hilight))
477	 (parsing-flag (nth 3 word)))
478    (or parsing-flag
479	(<= level forth-hilight-level))))
480
481;; Helper function for `forth-compile-word': translate one entry from
482;; `forth-words' into the form  (regexp regexp-depth word-description)
483(defun forth-compile-words-mapper (word)
484  ;; warning: we cannot rely on regexp-opt's PAREN argument, since
485  ;; XEmacs will use shy parens by default :-(
486  (let* ((matcher (car word))
487	 (regexp
488	  (concat "\\(" (cond ((stringp matcher) matcher)
489			      ((listp matcher) (regexp-opt matcher))
490			      (t (error "Invalid matcher")))
491		  "\\)"))
492	 (depth (regexp-opt-depth regexp))
493	 (description (cdr word)))
494    (list regexp depth description)))
495
496;; Read `words' and create a compiled representation suitable for efficient
497;; parsing of the form
498;; (regexp (subexp-count word-description) (subexp-count2 word-description2)
499;;  ...)
500(defun forth-compile-wordlist (words)
501  (let* ((mapped (mapcar 'forth-compile-words-mapper words))
502	 (regexp (concat "\\<\\("
503			 (mapconcat 'car mapped "\\|")
504			 "\\)\\>"))
505	 (sub-count 2)
506	 (sub-list (mapcar
507		    (lambda (i)
508		      (let ((sub (cons sub-count (nth 2 i))))
509			(setq sub-count (+ sub-count (nth 1 i)))
510			sub
511			))
512		    mapped)))
513    (cons regexp sub-list)))
514
515(defun forth-compile-words ()
516  "Compile the the words from `forth-words' and `forth-indent-words' into
517 the format that's later used for doing the actual hilighting/indentation.
518 Store the resulting compiled wordlists in `forth-compiled-words' and
519`forth-compiled-indent-words', respective"
520  (setq forth-compiled-words
521	(forth-compile-wordlist
522	 (forth-filter 'forth-words-filter forth-words)))
523  (setq forth-compiled-indent-words
524	(forth-compile-wordlist forth-indent-words)))
525
526(defun forth-hack-local-variables ()
527  "Parse and bind local variables, set in the contents of the current
528 forth-mode buffer. Prepend `forth-local-words' to `forth-words' and
529 `forth-local-indent-words' to `forth-indent-words'."
530  (hack-local-variables)
531  (setq forth-words (append forth-local-words forth-words))
532  (setq forth-indent-words (append forth-local-indent-words
533				   forth-indent-words)))
534
535(defun forth-customize-words ()
536  "Add the words from `forth-custom-words' and `forth-custom-indent-words'
537 to `forth-words' and `forth-indent-words', respective. Add
538 `forth-objects-words' and/or `forth-oof-words' to `forth-words', if
539 `forth-use-objects' and/or `forth-use-oof', respective is set."
540  (setq forth-words (append forth-custom-words forth-words
541			    (if forth-use-oof forth-oof-words nil)
542			    (if forth-use-objects forth-objects-words nil)))
543  (setq forth-indent-words (append
544			    forth-custom-indent-words forth-indent-words)))
545
546
547
548;; get location of first character of previous forth word that's got
549;; properties
550(defun forth-previous-start (pos)
551  (let* ((word (get-text-property pos 'forth-word))
552	 (prev (previous-single-property-change
553		(min (point-max) (1+ pos)) 'forth-word
554		(current-buffer) (point-min))))
555    (if (or (= (point-min) prev) word) prev
556      (if (get-text-property (1- prev) 'forth-word)
557	  (previous-single-property-change
558	   prev 'forth-word (current-buffer) (point-min))
559	(point-min)))))
560
561;; Get location of the last character of the current/next forth word that's
562;; got properties, text that's parsed by the word is considered as parts of
563;; the word.
564(defun forth-next-end (pos)
565  (let* ((word (get-text-property pos 'forth-word))
566	 (next (next-single-property-change pos 'forth-word
567					    (current-buffer) (point-max))))
568    (if word next
569      (if (get-text-property next 'forth-word)
570	  (next-single-property-change
571	   next 'forth-word (current-buffer) (point-max))
572	(point-max)))))
573
574(defun forth-next-whitespace (pos)
575  (save-excursion
576    (goto-char pos)
577    (skip-syntax-forward "-" (point-max))
578    (point)))
579(defun forth-previous-word (pos)
580  (save-excursion
581    (goto-char pos)
582    (re-search-backward "\\<" pos (point-min) 1)
583    (point)))
584
585;; Delete all properties, used by Forth mode, from `from' to `to'.
586(defun forth-delete-properties (from to)
587  (remove-text-properties
588   from to '(face nil fontified nil
589		  forth-parsed nil forth-word nil forth-state nil)))
590
591;; Get the index of the branch of the most recently evaluated regular
592;; expression that matched. (used for identifying branches "a\\|b\\|c...")
593(defun forth-get-regexp-branch ()
594  (let ((count 2))
595    (while (not (condition-case err (match-beginning count)
596		  (args-out-of-range t)))  ; XEmacs requires error handling
597      (setq count (1+ count)))
598    count))
599
600;; seek to next forth-word and return its "word-description"
601(defun forth-next-known-forth-word (to)
602  (if (<= (point) to)
603      (progn
604	(let* ((regexp (car forth-compiled-words))
605	       (pos (re-search-forward regexp to t)))
606	  (if pos (let ((branch (forth-get-regexp-branch))
607			(descr (cdr forth-compiled-words)))
608		    (goto-char (match-beginning 0))
609		    (cdr (assoc branch descr)))
610	    'nil)))
611    nil))
612
613;; Set properties of forth word at `point', eventually parsing subsequent
614;; words, and parsing all whitespaces. Set point to delimiter after word.
615;; The word, including it's parsed text gets the `forth-word' property, whose
616;; value is unique, and may be used for getting the word's start/end
617;; positions.
618(defun forth-set-word-properties (state data)
619  (let* ((start (point))
620	 (end (progn (re-search-forward "[ \t]\\|$" (point-max) 1)
621		     (point)))
622	 (type (car data))
623	 (hilight (nth 1 data))
624	 (bad-word (and (not state) (eq type 'compile-only)))
625	 (hlface (if bad-word font-lock-warning-face
626		   (if (<= (cdr hilight) forth-hilight-level)
627		       (car hilight) nil))))
628    (when hlface (put-text-property start end 'face hlface))
629    ;; if word parses in current state, process parsed range of text
630    (when (or (not state) (eq type 'compile-only) (eq type 'immediate))
631      (let ((parse-data (nthcdr 2 data)))
632	(while parse-data
633	  (let ((delim (nth 0 parse-data))
634		(skip-leading (nth 1 parse-data))
635		(parse-type (nth 2 parse-data))
636		(parsed-hilight (nth 3 parse-data))
637		(parse-start (point))
638		(parse-end))
639	    (when skip-leading
640	      (while (and (looking-at delim) (> (match-end 0) (point))
641			  (not (looking-at "\n")))
642		(forward-char)))
643	    (re-search-forward delim (point-max) 1)
644	    (setq parse-end (point))
645	    (forth-delete-properties end parse-end)
646	    (when (<= (cdr parsed-hilight) forth-hilight-level)
647	      (put-text-property
648	       parse-start parse-end 'face (car parsed-hilight)))
649	    (put-text-property
650	     parse-start parse-end 'forth-parsed parse-type)
651	    (setq end parse-end)
652	    (setq parse-data (nthcdr 4 parse-data))))))
653    (put-text-property start end 'forth-word start)))
654
655;; Search for known Forth words in the range `from' to `to', using
656;; `forth-next-known-forth-word' and set their properties via
657;; `forth-set-word-properties'.
658(defun forth-update-properties (from to &optional loudly)
659  (save-excursion
660    (let ((msg-count 0) (state) (word-descr) (last-location))
661      (goto-char (forth-previous-word (forth-previous-start
662				       (max (point-min) (1- from)))))
663      (setq to (forth-next-end (min (point-max) (1+ to))))
664      ;; `to' must be on a space delimiter, if a parsing word was changed
665      (setq to (forth-next-whitespace to))
666      (setq state (get-text-property (point) 'forth-state))
667      (setq last-location (point))
668      (forth-delete-properties (point) to)
669      (put-text-property (point) to 'fontified t)
670      ;; hilight loop...
671      (while (setq word-descr (forth-next-known-forth-word to))
672	(when loudly
673	  (when (equal 0 (% msg-count 100))
674	    (message "Parsing Forth code...%s"
675		     (make-string (/ msg-count 100) ?.)))
676	  (setq msg-count (1+ msg-count)))
677	(forth-set-word-properties state word-descr)
678	(when state (put-text-property last-location (point) 'forth-state t))
679	(let ((type (car word-descr)))
680	  (if (eq type 'definition-starter) (setq state t))
681	  (if (eq type 'definition-ender) (setq state nil))
682	  (setq last-location (point))))
683      ;; update state property up to `to'
684      (if (and state (< (point) to))
685	  (put-text-property last-location to 'forth-state t))
686      ;; extend search if following state properties differ from current state
687      (if (< to (point-max))
688	  (if (not (equal state (get-text-property (1+ to) 'forth-state)))
689	      (let ((extend-to (next-single-property-change
690				to 'forth-state (current-buffer) (point-max))))
691		(forth-update-properties to extend-to))
692	    ))
693      )))
694
695;; save-buffer-state borrowed from `font-lock.el'
696(eval-when-compile
697  (defmacro forth-save-buffer-state (varlist &rest body)
698    "Bind variables according to VARLIST and eval BODY restoring buffer state."
699    `(let* (,@(append varlist
700		   '((modified (buffer-modified-p)) (buffer-undo-list t)
701		     (inhibit-read-only t) (inhibit-point-motion-hooks t)
702		     before-change-functions after-change-functions
703                        deactivate-mark buffer-file-name buffer-file-truename)))
704       ,@body
705	 (when (and (not modified) (buffer-modified-p))
706         (set-buffer-modified-p nil)))))
707
708;; Function that is added to the `change-functions' hook. Calls
709;; `forth-update-properties' and keeps care of disabling undo information
710;; and stuff like that.
711(defun forth-change-function (from to len &optional loudly)
712  (save-match-data
713    (forth-save-buffer-state
714     ()
715     (unless forth-disable-parser (forth-update-properties from to loudly))
716     (forth-update-warn-long-lines))))
717
718(defun forth-fontification-function (from)
719  "Function to be called from `fontification-functions' of Emacs 21."
720  (save-match-data
721    (forth-save-buffer-state
722     ((to (min (point-max) (+ from 100))))
723     (unless (or forth-disable-parser (not forth-jit-parser)
724		 (get-text-property from 'fontified))
725       (forth-update-properties from to)))))
726
727(byte-compile 'forth-set-word-properties)
728(byte-compile 'forth-next-known-forth-word)
729(byte-compile 'forth-update-properties)
730(byte-compile 'forth-delete-properties)
731(byte-compile 'forth-get-regexp-branch)
732
733;;; imenu support
734;;;
735(defvar forth-defining-words
736  '("VARIABLE" "CONSTANT" "2VARIABLE" "2CONSTANT" "FVARIABLE" "FCONSTANT"
737   "USER" "VALUE" "field" "end-struct" "VOCABULARY" "CREATE" ":" "CODE"
738   "DEFER" "ALIAS")
739  "List of words, that define the following word.
740Used for imenu index generation.")
741
742(defvar forth-defining-words-regexp nil
743  "Regexp that's generated for matching `forth-defining-words'")
744
745(defun forth-next-definition-starter ()
746  (progn
747    (let* ((pos (re-search-forward forth-defining-words-regexp (point-max) t)))
748      (if pos
749	  (if (or (text-property-not-all (match-beginning 0) (match-end 0)
750					 'forth-parsed nil)
751		  (text-property-not-all (match-beginning 0) (match-end 0)
752					 'forth-state nil))
753	      (forth-next-definition-starter)
754	    t)
755	nil))))
756
757(defun forth-create-index ()
758  (let* ((forth-defining-words-regexp
759	  (concat "\\<\\(" (regexp-opt forth-defining-words) "\\)\\>"))
760	 (index nil))
761    (goto-char (point-min))
762    (while (forth-next-definition-starter)
763      (if (looking-at "[ \t]*\\([^ \t\n]+\\)")
764	  (setq index (cons (cons (match-string 1) (point)) index))))
765    index))
766
767;; top-level require is executed at byte-compile and load time
768(eval-and-compile (forth-require 'speedbar))
769
770;; this code is executed at load-time only
771(when (memq 'speedbar features)
772  (speedbar-add-supported-extension ".fs")
773  (speedbar-add-supported-extension ".fb"))
774
775;; (require 'profile)
776;; (setq profile-functions-list '(forth-set-word-properties forth-next-known-forth-word forth-update-properties forth-delete-properties forth-get-regexp-branch))
777
778;;; Indentation
779;;;
780
781;; Return, whether `pos' is the first forth word on its line
782(defun forth-first-word-on-line-p (pos)
783  (save-excursion
784    (beginning-of-line)
785    (skip-chars-forward " \t")
786    (= pos (point))))
787
788;; Return indentation data (SELF-INDENT . NEXT-INDENT) of next known
789;; indentation word, or nil if there is no word up to `to'.
790;; Position `point' at location just after found word, or at `to'. Parsed
791;; ranges of text will not be taken into consideration!
792(defun forth-next-known-indent-word (to)
793  (if (<= (point) to)
794      (progn
795	(let* ((regexp (car forth-compiled-indent-words))
796	       (pos (re-search-forward regexp to t)))
797	  (if pos
798	      (let* ((start (match-beginning 0))
799		     (end (match-end 0))
800		     (branch (forth-get-regexp-branch))
801		     (descr (cdr forth-compiled-indent-words))
802		     (indent (cdr (assoc branch descr)))
803		     (type (nth 2 indent)))
804		;; skip words that are parsed (strings/comments) and
805		;; non-immediate words inside definitions
806		(if (or (text-property-not-all start end 'forth-parsed nil)
807			(and (eq type 'non-immediate)
808			     (text-property-not-all start end
809						    'forth-state nil)))
810		    (forth-next-known-indent-word to)
811		  (if (forth-first-word-on-line-p (match-beginning 0))
812		      (nth 0 indent) (nth 1 indent))))
813	    nil)))
814    nil))
815
816;; Translate indentation value `indent' to indentation column. Multiples of
817;; 2 correspond to multiples of `forth-indent-level'. Odd numbers get an
818;; additional `forth-minor-indent-level' added (or substracted).
819(defun forth-convert-to-column (indent)
820  (let* ((sign (if (< indent 0) -1 1))
821	 (value (abs indent))
822	 (major (* (/ value 2) forth-indent-level))
823	 (minor (* (% value 2) forth-minor-indent-level)))
824    (* sign (+ major minor))))
825
826;; Return the column increment, that the current line of forth code does to
827;; the current or following lines. `which' specifies which indentation values
828;; to use. 1 means the indentation of following lines relative to current
829;; line, 0 means the indentation of the current line relative to the previous
830;; line. Return `nil', if there are no indentation words on the current line.
831(defun forth-get-column-incr (which)
832  (save-excursion
833    (let ((regexp (car forth-compiled-indent-words))
834	  (word-indent)
835	  (self-indent nil)
836	  (next-indent nil)
837	  (to (save-excursion (end-of-line) (point))))
838      (beginning-of-line)
839      (while (setq word-indent (forth-next-known-indent-word to))
840	(let* ((self-incr (car word-indent))
841	       (next-incr (cdr word-indent))
842	       (self-column-incr (forth-convert-to-column self-incr))
843	       (next-column-incr (forth-convert-to-column next-incr)))
844	  (setq next-indent (if next-indent next-indent 0))
845	  (setq self-indent (if self-indent self-indent 0))
846	  (if (or (and (> next-indent 0) (< self-column-incr 0))
847		  (and (< next-indent 0) (> self-column-incr 0)))
848	      (setq next-indent (+ next-indent self-column-incr))
849	    (setq self-indent (+ self-indent self-column-incr)))
850	  (setq next-indent (+ next-indent next-column-incr))))
851      (nth which (list self-indent next-indent)))))
852
853;; Find previous line that contains indentation words, return the column,
854;; to which following text should be indented to.
855(defun forth-get-anchor-column ()
856  (save-excursion
857    (if (/= 0 (forward-line -1)) 0
858      (let ((indent))
859	(while (not (or (setq indent (forth-get-column-incr 1))
860			(<= (point) (point-min))))
861	  (forward-line -1))
862	(+ (current-indentation) (if indent indent 0))))))
863
864(defun forth-indent-line (&optional flag)
865  "Correct indentation of the current Forth line."
866  (let* ((anchor (forth-get-anchor-column))
867	 (column-incr (forth-get-column-incr 0)))
868    (forth-indent-to (if column-incr (+ anchor column-incr) anchor))))
869
870(defun forth-current-column ()
871  (- (point) (save-excursion (beginning-of-line) (point))))
872(defun forth-current-indentation ()
873  (- (save-excursion (beginning-of-line) (forward-to-indentation 0) (point))
874     (save-excursion (beginning-of-line) (point))))
875
876(defun forth-indent-to (x)
877  (let ((p nil))
878    (setq p (- (forth-current-column) (forth-current-indentation)))
879    (forth-delete-indentation)
880    (beginning-of-line)
881    (indent-to x)
882    (if (> p 0) (forward-char p))))
883
884(defun forth-delete-indentation ()
885  (save-excursion
886    (delete-region
887     (progn (beginning-of-line) (point))
888     (progn (back-to-indentation) (point)))))
889
890(defun forth-indent-command ()
891  (interactive)
892  (forth-indent-line t))
893
894;; remove trailing whitespaces in current line
895(defun forth-remove-trailing ()
896  (save-excursion
897    (end-of-line)
898    (delete-region (point) (progn (skip-chars-backward " \t") (point)))))
899
900;; insert newline, removing any trailing whitespaces in the current line
901(defun forth-newline-remove-trailing ()
902  (save-excursion
903    (delete-region (point) (progn (skip-chars-backward " \t") (point))))
904  (newline))
905;  (let ((was-point (point-marker)))
906;    (unwind-protect
907;	(progn (forward-line -1) (forth-remove-trailing))
908;      (goto-char (was-point)))))
909
910;; workaround for bug in `reindent-then-newline-and-indent'
911(defun forth-reindent-then-newline-and-indent ()
912  (interactive "*")
913  (indent-according-to-mode)
914  (forth-newline-remove-trailing)
915  (indent-according-to-mode))
916
917
918;;; Block file encoding/decoding  (dk)
919;;;
920
921(defconst forth-c/l 64 "Number of characters per block line")
922(defconst forth-l/b 16 "Number of lines per block")
923
924;; Check whether the unconverted block file line, point is in, does not
925;; contain `\n' and `\t' characters.
926(defun forth-check-block-line (line)
927  (let ((end (save-excursion (beginning-of-line) (forward-char forth-c/l)
928			     (point))))
929    (save-excursion
930      (beginning-of-line)
931      (when (search-forward "\n" end t)
932	(message "Warning: line %i contains newline character #10" line)
933	(ding t))
934      (beginning-of-line)
935      (when (search-forward "\t" end t)
936	(message "Warning: line %i contains tab character #8" line)
937	(ding t)))))
938
939(defun forth-convert-from-block (from to)
940  "Convert block file format to stream source in current buffer."
941  (let ((line (count-lines (point-min) from)))
942    (save-excursion
943      (goto-char from)
944      (set-mark to)
945      (while (< (+ (point) forth-c/l) (mark t))
946	(setq line (1+ line))
947	(forth-check-block-line line)
948	(forward-char forth-c/l)
949	(forth-newline-remove-trailing))
950      (when (= (+ (point) forth-c/l) (mark t))
951	(forth-remove-trailing))
952      (mark t))))
953
954;; Pad a line of a block file up to `forth-c/l' characters, positioning `point'
955;; at the end of line.
956(defun forth-pad-block-line ()
957  (save-excursion
958    (end-of-line)
959    (if (<= (current-column) forth-c/l)
960	(move-to-column forth-c/l t)
961      (message "Line %i longer than %i characters, truncated"
962	       (count-lines (point-min) (point)) forth-c/l)
963      (ding t)
964      (move-to-column forth-c/l t)
965      (delete-region (point) (progn (end-of-line) (point))))))
966
967;; Replace tab characters in current line by spaces.
968(defun forth-convert-tabs-in-line ()
969  (save-excursion
970    (beginning-of-line)
971    (while (search-forward "\t" (save-excursion (end-of-line) (point)) t)
972      (backward-char)
973      (delete-region (point) (1+ (point)))
974      (insert-char ?\  (- tab-width (% (current-column) tab-width))))))
975
976;; Delete newline at end of current line, concatenating it with the following
977;; line. Place `point' at end of newly formed line.
978(defun forth-delete-newline ()
979  (end-of-line)
980  (delete-region (point) (progn (beginning-of-line 2) (point))))
981
982(defun forth-convert-to-block (from to &optional original-buffer)
983  "Convert range of text to block file format in current buffer."
984  (let* ((lines 0)) ; I have to count lines myself, since `count-lines' has
985		    ; problems with trailing newlines...
986    (save-excursion
987      (goto-char from)
988      (set-mark to)
989      ;; pad lines to full length (`forth-c/l' characters per line)
990      (while (< (save-excursion (end-of-line) (point)) (mark t))
991	(setq lines (1+ lines))
992	(forth-pad-block-line)
993	(forth-convert-tabs-in-line)
994	(forward-line))
995      ;; also make sure the last line is padded, if `to' is at its end
996      (end-of-line)
997      (when (= (point) (mark t))
998	(setq lines (1+ lines))
999	(forth-pad-block-line)
1000	(forth-convert-tabs-in-line))
1001      ;; remove newlines between lines
1002      (goto-char from)
1003      (while (< (save-excursion (end-of-line) (point)) (mark t))
1004	(forth-delete-newline))
1005      ;; append empty lines, until last block is complete
1006      (goto-char (mark t))
1007      (let* ((required (* (/ (+ lines (1- forth-l/b)) forth-l/b) forth-l/b))
1008	     (pad-lines (- required lines)))
1009	(while (> pad-lines 0)
1010	  (insert-char ?\  forth-c/l)
1011	  (setq pad-lines (1- pad-lines))))
1012      (point))))
1013
1014(defun forth-detect-block-file-p ()
1015  "Return non-nil if the current buffer is in block file format. Detection is
1016done by checking whether the first line has 1024 characters or more."
1017  (save-restriction
1018    (widen)
1019    (save-excursion
1020       (goto-char (point-min))
1021       (end-of-line)
1022       (>= (current-column) 1024))))
1023
1024;; add block file conversion routines to `format-alist'
1025(defconst forth-block-format-description
1026  '(forth-blocks "Forth block source file" nil
1027		 forth-convert-from-block forth-convert-to-block
1028		 t normal-mode))
1029(unless (memq forth-block-format-description format-alist)
1030  (setq format-alist (cons forth-block-format-description format-alist)))
1031
1032;;; End block file encoding/decoding
1033
1034;;; Block file editing
1035;;;
1036(defvar forth-overlay-arrow-string ">>")
1037(defvar forth-block-base 1 "Number of first block in block file")
1038(defvar forth-show-screen nil
1039  "Non-nil means to show screen starts and numbers (for block files)")
1040(defvar forth-warn-long-lines nil
1041  "Non-nil means to warn about lines that are longer than 64 characters")
1042
1043(defvar forth-screen-marker nil)
1044(defvar forth-screen-number-string nil)
1045
1046(defun forth-update-show-screen ()
1047  "If `forth-show-screen' is non-nil, put overlay arrow to start of screen,
1048`point' is in. If arrow now points to different screen than before, display
1049screen number."
1050  (if (not forth-show-screen)
1051      (setq overlay-arrow-string nil)
1052    (save-excursion
1053      (let* ((line (count-lines (point-min) (min (point-max) (1+ (point)))))
1054	     (first-line (1+ (* (/ (1- line) forth-l/b) forth-l/b)))
1055	     (scr (+ forth-block-base (/ first-line forth-l/b))))
1056	(setq overlay-arrow-string forth-overlay-arrow-string)
1057	(goto-line first-line)
1058	(setq overlay-arrow-position forth-screen-marker)
1059	(set-marker forth-screen-marker
1060		    (save-excursion (goto-line first-line) (point)))
1061	(setq forth-screen-number-string (format "%d" scr))))))
1062
1063(add-hook 'forth-motion-hooks 'forth-update-show-screen)
1064
1065(defun forth-update-warn-long-lines ()
1066  "If `forth-warn-long-lines' is non-nil, display a warning whenever a line
1067exceeds 64 characters."
1068  (when forth-warn-long-lines
1069    (when (> (save-excursion (end-of-line) (current-column)) forth-c/l)
1070      (message "Warning: current line exceeds %i characters"
1071	       forth-c/l))))
1072
1073(add-hook 'forth-motion-hooks 'forth-update-warn-long-lines)
1074
1075;;; End block file editing
1076
1077
1078(defvar forth-mode-abbrev-table nil
1079  "Abbrev table in use in Forth-mode buffers.")
1080
1081(define-abbrev-table 'forth-mode-abbrev-table ())
1082
1083(defvar forth-mode-map nil
1084  "Keymap used in Forth mode.")
1085
1086(if (not forth-mode-map)
1087    (setq forth-mode-map (make-sparse-keymap)))
1088
1089;(define-key forth-mode-map "\M-\C-x" 'compile)
1090(define-key forth-mode-map "\C-x\\" 'comment-region)
1091(define-key forth-mode-map "\C-x~" 'forth-remove-tracers)
1092(define-key forth-mode-map "\C-x\C-m" 'forth-split)
1093(define-key forth-mode-map "\e " 'forth-reload)
1094(define-key forth-mode-map "\t" 'forth-indent-command)
1095(define-key forth-mode-map "\C-m" 'forth-reindent-then-newline-and-indent)
1096(define-key forth-mode-map "\M-q" 'forth-fill-paragraph)
1097(define-key forth-mode-map "\e." 'forth-find-tag)
1098
1099;; setup for C-h C-i to work
1100(eval-and-compile (forth-require 'info-look))
1101(when (memq 'info-look features)
1102  (defvar forth-info-lookup '(symbol (forth-mode "\\S-+" t
1103						  (("(gforth)Word Index"))
1104						  "\\S-+")))
1105  (unless (memq forth-info-lookup info-lookup-alist)
1106    (setq info-lookup-alist (cons forth-info-lookup info-lookup-alist)))
1107  ;; in X-Emacs C-h C-i is by default bound to Info-query
1108  (define-key forth-mode-map [?\C-h ?\C-i] 'info-lookup-symbol))
1109
1110;;   (info-lookup-add-help
1111;;    :topic 'symbol
1112;;    :mode 'forth-mode
1113;;    :regexp "[^
1114;; ]+"
1115;;    :ignore-case t
1116;;    :doc-spec '(("(gforth)Name Index" nil "`" "'  "))))
1117
1118(require 'etags)
1119
1120(defun forth-find-tag (tagname &optional next-p regexp-p)
1121  (interactive (find-tag-interactive "Find tag: "))
1122  (unless (or regexp-p next-p)
1123    (setq tagname (concat "\\(^\\|\\s-+\\)\\(" (regexp-quote tagname)
1124			    "\\)\\s-*\x7f")))
1125  (switch-to-buffer
1126   (find-tag-noselect tagname next-p t)))
1127
1128(defvar forth-mode-syntax-table nil
1129  "Syntax table in use in Forth-mode buffers.")
1130
1131;; Important: hilighting/indentation now depends on a correct syntax table.
1132;; All characters, except whitespace *must* belong to the "word constituent"
1133;; syntax class. If different behaviour is required, use of Categories might
1134;; help.
1135(if (not forth-mode-syntax-table)
1136    (progn
1137      (setq forth-mode-syntax-table (make-syntax-table))
1138      (let ((char 0))
1139	(while (< char ?!)
1140	  (modify-syntax-entry char " " forth-mode-syntax-table)
1141	  (setq char (1+ char)))
1142	(while (< char 256)
1143	  (modify-syntax-entry char "w" forth-mode-syntax-table)
1144	  (setq char (1+ char))))
1145      ))
1146
1147(defun forth-mode-variables ()
1148  (set-syntax-table forth-mode-syntax-table)
1149  (setq local-abbrev-table forth-mode-abbrev-table)
1150  (make-local-variable 'paragraph-start)
1151  (setq paragraph-start (concat "^$\\|" page-delimiter))
1152  (make-local-variable 'paragraph-separate)
1153  (setq paragraph-separate paragraph-start)
1154  (make-local-variable 'indent-line-function)
1155  (setq indent-line-function 'forth-indent-line)
1156;  (make-local-variable 'require-final-newline)
1157;  (setq require-final-newline t)
1158  (make-local-variable 'comment-start)
1159  (setq comment-start "\\ ")
1160  ;(make-local-variable 'comment-end)
1161  ;(setq comment-end " )")
1162  (make-local-variable 'comment-column)
1163  (setq comment-column 40)
1164  (make-local-variable 'comment-start-skip)
1165  (setq comment-start-skip "\\\\ ")
1166  (make-local-variable 'comment-indent-function)
1167  (setq comment-indent-function 'forth-comment-indent)
1168  (make-local-variable 'parse-sexp-ignore-comments)
1169  (setq parse-sexp-ignore-comments t)
1170  (setq case-fold-search t)
1171  (make-local-variable 'forth-was-point)
1172  (setq forth-was-point -1)
1173  (make-local-variable 'forth-words)
1174  (make-local-variable 'forth-compiled-words)
1175  (make-local-variable 'forth-compiled-indent-words)
1176  (make-local-variable 'forth-hilight-level)
1177  (make-local-variable 'after-change-functions)
1178  (make-local-variable 'forth-show-screen)
1179  (make-local-variable 'forth-screen-marker)
1180  (make-local-variable 'forth-warn-long-lines)
1181  (make-local-variable 'forth-screen-number-string)
1182  (make-local-variable 'forth-use-oof)
1183  (make-local-variable 'forth-use-objects)
1184  (setq forth-screen-marker (copy-marker 0))
1185  (add-hook 'after-change-functions 'forth-change-function)
1186  (if (and forth-jit-parser (>= emacs-major-version 21))
1187      (add-hook 'fontification-functions 'forth-fontification-function))
1188  (setq imenu-create-index-function 'forth-create-index))
1189
1190;;;###autoload
1191(defun forth-mode ()
1192  "\
1193Major mode for editing Forth code. Tab indents for Forth code. Comments
1194are delimited with \\ and newline. Paragraphs are separated by blank lines
1195only. Block files are autodetected, when read, and converted to normal
1196stream source format. See also `forth-block-mode'.
1197\\{forth-mode-map}
1198
1199Variables controlling syntax hilighting/recognition of parsed text:
1200 `forth-words'
1201    List of words that have a special parsing behaviour and/or should be
1202    hilighted. Add custom words by setting forth-custom-words in your
1203    .emacs, or by setting forth-local-words, in source-files' local
1204    variables lists.
1205 forth-use-objects
1206    Set this variable to non-nil in your .emacs, or in a local variables
1207    list, to hilight and recognize the words from the \"Objects\" package
1208    for object-oriented programming.
1209 forth-use-oof
1210    Same as above, just for the \"OOF\" package.
1211 forth-custom-words
1212    List of custom Forth words to prepend to `forth-words'. Should be set
1213    in your .emacs.
1214 forth-local-words
1215    List of words to prepend to `forth-words', whenever a forth-mode
1216    buffer is created. That variable should be set by Forth sources, using
1217    a local variables list at the end of file, to get file-specific
1218    hilighting.
1219    0 [IF]
1220       Local Variables: ...
1221       forth-local-words: ...
1222       End:
1223    [THEN]
1224 forth-hilight-level
1225    Controls how much syntax hilighting is done. Should be in the range
1226    0..3
1227
1228Variables controlling indentation style:
1229 `forth-indent-words'
1230    List of words that influence indentation.
1231 forth-local-indent-words
1232    List of words to prepend to `forth-indent-words', similar to
1233    forth-local-words. Should be used for specifying file-specific
1234    indentation, using a local variables list.
1235 forth-custom-indent-words
1236    List of words to prepend to `forth-indent-words'. Should be set in your
1237    .emacs.
1238 forth-indent-level
1239    Indentation increment/decrement of Forth statements.
1240 forth-minor-indent-level
1241    Minor indentation increment/decrement of Forth statemens.
1242
1243Variables controlling block-file editing:
1244 forth-show-screen
1245    Non-nil means, that the start of the current screen is marked by an
1246    overlay arrow, and screen numbers are displayed in the mode line.
1247    This variable is by default nil for `forth-mode' and t for
1248    `forth-block-mode'.
1249 forth-overlay-arrow-string
1250    String to display as the overlay arrow, when `forth-show-screen' is t.
1251    Setting this variable to nil disables the overlay arrow.
1252 forth-block-base
1253    Screen number of the first block in a block file. Defaults to 1.
1254 forth-warn-long-lines
1255    Non-nil means that a warning message is displayed whenever you edit or
1256    move over a line that is longer than 64 characters (the maximum line
1257    length that can be stored into a block file). This variable defaults to
1258    t for `forth-block-mode' and to nil for `forth-mode'.
1259
1260Variables controlling interaction with the Forth-process (also see
1261`run-forth'):
1262  forth-program-name
1263    Program invoked by the `run-forth' command (including arguments).
1264  inferior-forth-mode-hook
1265    Hook for customising inferior-forth-mode.
1266  forth-compile-command
1267    Default command to execute on `compile'.
1268"
1269  (interactive)
1270  (kill-all-local-variables)
1271  (use-local-map forth-mode-map)
1272  (setq mode-name "Forth")
1273  (setq major-mode 'forth-mode)
1274  (forth-install-motion-hook)
1275  ;; convert buffer contents from block file format, if necessary
1276  (when (forth-detect-block-file-p)
1277    (widen)
1278    (message "Converting from Forth block source...")
1279    (forth-convert-from-block (point-min) (point-max))
1280    (message "Converting from Forth block source...done"))
1281  ;; if user switched from forth-block-mode to forth-mode, make sure the file
1282  ;; is now stored as normal strem source
1283  (when (equal buffer-file-format '(forth-blocks))
1284    (setq buffer-file-format nil))
1285  (forth-mode-variables)
1286;  (if (not (forth-process-running-p))
1287;      (run-forth forth-program-name))
1288  (run-hooks 'forth-mode-hook))
1289
1290;;;###autoload
1291(define-derived-mode forth-block-mode forth-mode "Forth Block Source"
1292  "Major mode for editing Forth block source files, derived from
1293`forth-mode'. Differences to `forth-mode' are:
1294 * files are converted to block format, when written (`buffer-file-format'
1295   is set to `(forth-blocks)')
1296 * `forth-show-screen' and `forth-warn-long-lines' are t by default
1297
1298Note that the length of lines in block files is limited to 64 characters.
1299When writing longer lines to a block file, a warning is displayed in the
1300echo area and the line is truncated.
1301
1302Another problem is imposed by block files that contain newline or tab
1303characters. When Emacs converts such files back to block file format,
1304it'll translate those characters to a number of spaces. However, when
1305you read such a file, a warning message is displayed in the echo area,
1306including a line number that may help you to locate and fix the problem.
1307
1308So have a look at the *Messages* buffer, whenever you hear (or see) Emacs'
1309bell during block file read/write operations."
1310  (setq buffer-file-format '(forth-blocks))
1311  (setq forth-show-screen t)
1312  (setq forth-warn-long-lines t)
1313  (setq forth-screen-number-string (format "%d" forth-block-base))
1314  (setq mode-line-format (append (reverse (cdr (reverse mode-line-format)))
1315				 '("--S" forth-screen-number-string "-%-"))))
1316
1317(add-hook 'forth-mode-hook
1318      '(lambda ()
1319	 (make-local-variable 'compile-command)
1320	 (setq compile-command "gforth ")
1321	 (forth-hack-local-variables)
1322	 (forth-customize-words)
1323	 (forth-compile-words)
1324	 (unless (and forth-jit-parser (>= emacs-major-version 21))
1325	   (forth-change-function (point-min) (point-max) nil t))))
1326
1327(defun forth-fill-paragraph ()
1328  "Fill comments (starting with '\'; do not fill code (block style
1329programmers who tend to fill code won't use emacs anyway:-)."
1330  ; Currently only comments at the start of the line are filled.
1331  ; Something like lisp-fill-paragraph may be better.  We cannot use
1332  ; fill-paragraph, because it removes the \ from the first comment
1333  ; line. Therefore we have to look for the first line of the comment
1334  ; and use fill-region.
1335  (interactive)
1336  (save-excursion
1337    (beginning-of-line)
1338    (while (and
1339	     (= (forward-line -1) 0)
1340	     (looking-at "[ \t]*\\\\g?[ \t]+")))
1341    (if (not (looking-at "[ \t]*\\\\g?[ \t]+"))
1342	(forward-line 1))
1343    (let ((from (point))
1344	  (to (save-excursion (forward-paragraph) (point))))
1345      (if (looking-at "[ \t]*\\\\g?[ \t]+")
1346	  (progn (goto-char (match-end 0))
1347		 (set-fill-prefix)
1348		 (fill-region from to nil))))))
1349
1350(defun forth-comment-indent ()
1351  (save-excursion
1352    (beginning-of-line)
1353    (if (looking-at ":[ \t]*")
1354	(progn
1355	  (end-of-line)
1356	  (skip-chars-backward " \t\n")
1357	  (1+ (current-column)))
1358      comment-column)))
1359
1360
1361;; Forth commands
1362
1363(defun forth-remove-tracers ()
1364  "Remove tracers of the form `~~ '. Queries the user for each occurrence."
1365  (interactive)
1366  (query-replace-regexp "\\(~~ \\| ~~$\\)" "" nil))
1367
1368(define-key forth-mode-map "\C-x\C-e" 'compile)
1369(define-key forth-mode-map "\C-x\C-n" 'next-error)
1370(require 'compile)
1371
1372(defvar forth-compile-command "gforth ")
1373;(defvar forth-compilation-window-percent-height 30)
1374
1375(defun forth-split ()
1376  (interactive)
1377  (forth-split-1 "*forth*"))
1378
1379(defun forth-split-1 (buffer)
1380  (if (not (eq (window-buffer) (get-buffer buffer)))
1381      (progn
1382	(delete-other-windows)
1383	(split-window-vertically
1384	 (/ (frame-height) 2))
1385	(other-window 1)
1386	(switch-to-buffer buffer)
1387	(goto-char (point-max))
1388	(other-window 1))))
1389
1390;;; Forth menu
1391;;; Mikael Karlsson <qramika@eras70.ericsson.se>
1392
1393;; (dk) code commented out due to complaints of XEmacs users.  After
1394;; all, there's imenu/speedbar, which uses much smarter scanning
1395;; rules.
1396
1397;; (cond ((string-match "XEmacs\\|Lucid" emacs-version)
1398;;        (require 'func-menu)
1399
1400;;   (defconst fume-function-name-regexp-forth
1401;;    "^\\(:\\)[ \t]+\\([^ \t]*\\)"
1402;;    "Expression to get word definitions in Forth.")
1403
1404;;   (setq fume-function-name-regexp-alist
1405;;       (append '((forth-mode . fume-function-name-regexp-forth)
1406;;              ) fume-function-name-regexp-alist))
1407
1408;;   ;; Find next forth word in the buffer
1409;;   (defun fume-find-next-forth-function-name (buffer)
1410;;     "Searches for the next forth word in BUFFER."
1411;;     (set-buffer buffer)
1412;;     (if (re-search-forward fume-function-name-regexp nil t)
1413;;       (let ((beg (match-beginning 2))
1414;;             (end (match-end 2)))
1415;;         (cons (buffer-substring beg end) beg))))
1416
1417;;   (setq fume-find-function-name-method-alist
1418;;   (append '((forth-mode    . fume-find-next-forth-function-name))))
1419
1420;;   ))
1421;;; End Forth menu
1422
1423;;; File folding of forth-files
1424;;; uses outline
1425;;; Toggle activation with M-x fold-f (when editing a forth-file)
1426;;; Use f9 to expand, f10 to hide, Or the menubar in xemacs
1427;;;
1428;;; Works most of the times but loses sync with the cursor occasionally
1429;;; Could be improved by also folding on comments
1430
1431;; (dk) This code needs a rewrite; just too ugly and doesn't use the
1432;; newer and smarter scanning rules of `imenu'. Who needs it anyway??
1433
1434;; (require 'outline)
1435
1436;; (defun f-outline-level ()
1437;;   (cond	((looking-at "\\`\\\\")
1438;; 	 0)
1439;; 	((looking-at "\\\\ SEC")
1440;; 	 0)
1441;; 	((looking-at "\\\\ \\\\ .*")
1442;; 	 0)
1443;; 	((looking-at "\\\\ DEFS")
1444;; 	 1)
1445;; 	((looking-at "\\/\\* ")
1446;; 	 1)
1447;; 	((looking-at ": .*")
1448;; 	 1)
1449;; 	((looking-at "\\\\G")
1450;; 	 2)
1451;; 	((looking-at "[ \t]+\\\\")
1452;; 	 3)))
1453
1454;; (defun fold-f  ()
1455;;    (interactive)
1456;;    (add-hook 'outline-minor-mode-hook 'hide-body)
1457
1458;;    ; outline mode header start, i.e. find word definitions
1459;; ;;;   (setq  outline-regexp  "^\\(:\\)[ \t]+\\([^ \t]*\\)")
1460;;    (setq  outline-regexp  "\\`\\\\\\|:\\|\\\\ SEC\\|\\\\G\\|[ \t]+\\\\\\|\\\\ DEFS\\|\\/\\*\\|\\\\ \\\\ .*")
1461;;    (setq outline-level 'f-outline-level)
1462
1463;;    (outline-minor-mode)
1464;;    (define-key outline-minor-mode-map '(shift up) 'hide-sublevels)
1465;;    (define-key outline-minor-mode-map '(shift right) 'show-children)
1466;;    (define-key outline-minor-mode-map '(shift left) 'hide-subtree)
1467;;    (define-key outline-minor-mode-map '(shift down) 'show-subtree))
1468
1469
1470;;(define-key global-map '(shift up) 'fold-f)
1471
1472;;; end file folding
1473
1474;;; func-menu is a package that scans your source file for function definitions
1475;;; and makes a menubar entry that lets you jump to any particular function
1476;;; definition by selecting it from the menu.  The following code turns this on
1477;;; for all of the recognized languages.  Scanning the buffer takes some time,
1478;;; but not much.
1479;;;
1480;; (cond ((string-match "XEmacs\\|Lucid" emacs-version)
1481;;        (require 'func-menu)
1482;; ;;       (define-key global-map 'f8 'function-menu)
1483;;        (add-hook 'find-fible-hooks 'fume-add-menubar-entry)
1484;; ;       (define-key global-map "\C-cg" 'fume-prompt-function-goto)
1485;; ;       (define-key global-map '(shift button3) 'mouse-function-menu)
1486;; ))
1487
1488;;;
1489;;; Inferior Forth interpreter
1490;;;	-- mostly copied from `cmuscheme.el' of Emacs 21.2
1491;;;
1492
1493(eval-and-compile (forth-require 'comint))
1494
1495  (defvar forth-program-name "gforth"
1496    "*Program invoked by the `run-forth' command, including program arguments")
1497
1498  (defcustom inferior-forth-mode-hook nil
1499    "*Hook for customising inferior-forth-mode."
1500    :type 'hook
1501    :group 'forth)
1502
1503  (defvar inferior-forth-mode-map
1504    (let ((m (make-sparse-keymap)))
1505      (define-key m "\r" 'comint-send-input)
1506      (define-key m "\M-\C-x" 'forth-send-paragraph-and-go)
1507      (define-key m "\C-c\C-l" 'forth-load-file)
1508      m))
1509  ;; Install the process communication commands in the forth-mode keymap.
1510  (define-key forth-mode-map "\e\C-m" 'forth-send-paragraph-and-go)
1511  (define-key forth-mode-map "\eo" 'forth-send-buffer-and-go)
1512
1513  (define-key forth-mode-map "\M-\C-x" 'forth-send-paragraph-and-go)
1514  (define-key forth-mode-map "\C-c\C-r" 'forth-send-region)
1515  (define-key forth-mode-map "\C-c\M-r" 'forth-send-region-and-go)
1516  (define-key forth-mode-map "\C-c\C-z" 'forth-switch-to-interactive)
1517  (define-key forth-mode-map "\C-c\C-l" 'forth-load-file)
1518
1519  (defvar forth-process-buffer)
1520
1521  (define-derived-mode inferior-forth-mode comint-mode "Inferior Forth"
1522    "Major mode for interacting with an inferior Forth process.
1523
1524The following commands are available:
1525\\{inferior-forth-mode-map}
1526
1527A Forth process can be fired up with M-x run-forth.
1528
1529Customisation: Entry to this mode runs the hooks on comint-mode-hook and
1530inferior-forth-mode-hook (in that order).
1531
1532You can send text to the inferior Forth process from other buffers containing
1533Forth source.
1534    forth-switch-to-interactive switches the current buffer to the Forth
1535        process buffer.
1536    forth-send-paragraph sends the current paragraph to the Forth process.
1537    forth-send-region sends the current region to the Forth process.
1538    forth-send-buffer sends the current buffer to the Forth process.
1539
1540    forth-send-paragraph-and-go, forth-send-region-and-go,
1541        forth-send-buffer-and-go switch to the Forth process buffer after
1542        sending their text.
1543For information on running multiple processes in multiple buffers, see
1544documentation for variable `forth-process-buffer'.
1545
1546Commands:
1547Return after the end of the process' output sends the text from the
1548end of process to point. If you accidentally suspend your process, use
1549\\[comint-continue-subjob] to continue it. "
1550    ;; Customise in inferior-forth-mode-hook
1551    (setq comint-prompt-regexp "^")
1552    (setq mode-line-process '(":%s")))
1553
1554  (defun forth-args-to-list (string)
1555    (let ((where (string-match "[ \t]" string)))
1556      (cond ((null where) (list string))
1557	    ((not (= where 0))
1558	     (cons (substring string 0 where)
1559		   (forth-args-to-list (substring string (+ 1 where)
1560						  (length string)))))
1561	    (t (let ((pos (string-match "[^ \t]" string)))
1562		 (if (null pos)
1563		     nil
1564		   (forth-args-to-list (substring string pos
1565						  (length string)))))))))
1566
1567;;;###autoload
1568  (defun run-forth (cmd)
1569    "Run an inferior Forth process, input and output via buffer *forth*.
1570If there is a process already running in `*forth*', switch to that buffer.
1571With argument, allows you to edit the command line (default is value
1572of `forth-program-name').  Runs the hooks `inferior-forth-mode-hook'
1573\(after the `comint-mode-hook' is run).
1574\(Type \\[describe-mode] in the process buffer for a list of commands.)"
1575
1576    (interactive (list (if current-prefix-arg
1577			   (read-string "Run Forth: " forth-program-name)
1578			 forth-program-name)))
1579    (if (not (comint-check-proc "*forth*"))
1580	(let ((cmdlist (forth-args-to-list cmd)))
1581	  (set-buffer (apply 'make-comint "forth" (car cmdlist)
1582			     nil (cdr cmdlist)))
1583	  (inferior-forth-mode)))
1584    (setq forth-program-name cmd)
1585    (setq forth-process-buffer "*forth*")
1586    (pop-to-buffer "*forth*"))
1587
1588  (defun forth-send-region (start end)
1589    "Send the current region to the inferior Forth process."
1590    (interactive "r")
1591    (comint-send-region (forth-proc) start end)
1592    (comint-send-string (forth-proc) "\n"))
1593
1594  (defun forth-end-of-paragraph ()
1595    (if (looking-at "[\t\n ]+") (skip-chars-backward  "\t\n "))
1596    (if (not (re-search-forward "\n[ \t]*\n" nil t))
1597	(goto-char (point-max))))
1598
1599  (defun forth-send-paragraph ()
1600    "Send the current or the previous paragraph to the Forth process"
1601    (interactive)
1602    (let (end)
1603      (save-excursion
1604	(forth-end-of-paragraph)
1605	(skip-chars-backward  "\t\n ")
1606	(setq end (point))
1607	(if (null (re-search-backward "\n[ \t]*\n" nil t))
1608	  (goto-char (point-min)))
1609	(skip-chars-forward  "\t\n ")
1610	(forth-send-region (point) end))))
1611
1612  (defun forth-send-paragraph-and-go ()
1613    "Send the current or the previous paragraph to the Forth process.
1614Then switch to the process buffer."
1615    (interactive)
1616    (forth-send-paragraph)
1617    (forth-switch-to-interactive t))
1618
1619  (defun forth-send-buffer ()
1620    "Send the current buffer to the Forth process."
1621    (interactive)
1622    (if (eq (current-buffer) forth-process-buffer)
1623	(error "Not allowed to send this buffer's contents to Forth"))
1624    (forth-send-region (point-min) (point-max)))
1625
1626  (defun forth-send-buffer-and-go ()
1627    "Send the current buffer to the Forth process.
1628Then switch to the process buffer."
1629    (interactive)
1630    (forth-send-buffer)
1631    (forth-switch-to-interactive t))
1632
1633
1634  (defun forth-switch-to-interactive (eob-p)
1635    "Switch to the Forth process buffer.
1636With argument, position cursor at end of buffer."
1637    (interactive "P")
1638    (if (get-buffer forth-process-buffer)
1639	(pop-to-buffer forth-process-buffer)
1640      (error "No current process buffer.  See variable `forth-process-buffer'"))
1641    (cond (eob-p
1642	   (push-mark)
1643	   (goto-char (point-max)))))
1644
1645  (defun forth-send-region-and-go (my-start end)
1646    "Send the current region to the inferior Forth process.
1647Then switch to the process buffer."
1648    (interactive "r")
1649    (forth-send-region my-start end)
1650    (forth-switch-to-interactive t))
1651
1652  (defcustom forth-source-modes '(forth-mode forth-block-mode)
1653    "*Used to determine if a buffer contains Forth source code.
1654If it's loaded into a buffer that is in one of these major modes, it's
1655considered a Forth source file by `forth-load-file' and `forth-compile-file'.
1656Used by these commands to determine defaults."
1657    :type '(repeat function)
1658    :group 'forth)
1659
1660  (defvar forth-prev-l/c-dir/file nil
1661    "Caches the last (directory . file) pair.
1662Caches the last pair used in the last `forth-load-file' or
1663`forth-compile-file' command. Used for determining the default in the
1664next one.")
1665
1666  (defun forth-load-file (file-name)
1667    "Load a Forth file FILE-NAME into the inferior Forth process."
1668    (interactive (comint-get-source "Load Forth file: " forth-prev-l/c-dir/file
1669				    forth-source-modes t)) ; T because LOAD
1670					; needs an exact name
1671    (comint-check-source file-name) ; Check to see if buffer needs saved.
1672    (setq forth-prev-l/c-dir/file (cons (file-name-directory file-name)
1673					(file-name-nondirectory file-name)))
1674    (comint-send-string (forth-proc)
1675			(concat "s\" " file-name "\" included\n")))
1676
1677
1678  (defvar forth-process-buffer nil "*The current Forth process buffer.
1679
1680See `scheme-buffer' for an explanation on how to run multiple Forth
1681processes.")
1682
1683  (defun forth-proc ()
1684    "Return the current Forth process.  See variable `forth-process-buffer'."
1685    (let ((proc (get-buffer-process (if (eq major-mode 'inferior-forth-mode)
1686					(current-buffer)
1687				      forth-process-buffer))))
1688      (or proc
1689	  (error "No current process.  See variable `forth-process-buffer'"))))
1690
1691(provide 'forth-mode)
1692
1693;;; gforth.el ends here
1694