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