source: git/emacs/singular.el @ ef85f4

spielwiese
Last change on this file since ef85f4 was ef85f4, checked in by Tim Wichmann <wichmann@…>, 26 years ago
+ Added support for font-lock (first steps) + Added support for menus (first steps) git-svn-id: file:///usr/local/Singular/svn/trunk@2427 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100644
File size: 67.4 KB
Line 
1;;; singular.el --- Emacs support for Computer Algebra System Singular
2
3;; $Id: singular.el,v 1.17 1998-08-05 07:07:05 wichmann Exp $
4
5;;; Commentary:
6
7
8;;; Code:
9
10;;{{{ Style and coding conventions
11
12;; Style and coding conventions:
13;;
14;; - "Singular" is written with an upper-case `S' in comments, doc
15;;   strings, and messages.  As part of symbols, it is written with
16;;   a lower-case `s'.
17;; - use a `fill-column' of 70 for doc strings and comments
18;; - use foldings to structure the source code but try not to exceed a
19;;   maximal depth of two folding (one folding in another folding which is
20;;   on top-level)
21;; - use lowercase folding titles except for first word
22;; - folding-marks are `;;{{{' and `;;}}}' resp., for sake of standard
23;;   conformity
24;; - mark incomplete doc strings or code with `NOT READY' (optionally
25;;   followed by an explanation what exactly is missing)
26;; - documentation on the customization of the modes is in the
27;;   doc-strings to `singular-mode-configuration' and
28;;   `singular-interactive-mode-configuration', resp.
29;;
30;; - use `singular' as prefix for all global symbols
31;; - use `singular-debug' as prefix for all global symbols concerning
32;;   debugging.
33;;
34;; - mark dependencies on Emacs flavor/version with a comment of the form
35;;   `;; Emacs[ <version>]'     resp.
36;;   `;; XEmacs[ <version>][ <nasty comment>]' (in that order, if
37;;   possible)
38;; - use a `cond' statement to execute Emacs flavor/version-dependent code,
39;;   not `if'.  This is to make such checks more extensible.
40;; - try to define different functions for different flavors/version
41;;   and use `singular-fset' at library-loading time to set the function
42;;   you really need.  If the function is named `singular-<basename>', the
43;;   flavor/version-dependent functions should be named
44;;   `singular-<flavor>[-<version>]-<basename>'.
45
46;; - use `singular-debug' for debugging output/actions
47;; - to switch between buffer and process names, use the functions
48;;   `singular-process-name-to-buffer-name' and
49;;   `singular-buffer-name-to-process-name'
50;; - we assume that the buffer is *not* read-only
51
52;;}}}
53
54(require 'comint)
55
56;;{{{ Code common to both modes
57;;{{{ Debugging stuff
58(defvar singular-debug nil
59  "*List of modes to debug or t to debug all modes.
60Currently, there are the modes `interactive', `interactive-filter',
61`interactive-simple-secs', and `interactive-sections'.")
62
63(defun singular-debug-format (string)
64  "Return STRING in a nicer format."
65  (save-match-data
66    (while (string-match "\n" string)
67      (setq string (replace-match "^J" nil nil string)))
68
69    (if (> (length string) 16)
70        (concat "<" (substring string 0 7) ">...<" (substring string -8) ">")
71      (concat "<" string ">"))))
72
73(defmacro singular-debug (mode form &optional else-form)
74  "Major debugging hook for singular.el.
75Evaluates FORM if `singular-debug' equals t or if MODE is an element
76of `singular-debug', othwerwise ELSE-FORM."
77  `(if (or (eq singular-debug t)
78           (memq ,mode singular-debug))
79       ,form
80     ,else-form))
81;;}}}
82
83;;{{{ Determining version
84(defvar singular-emacs-flavor nil
85  "A symbol describing the current Emacs.
86Currently, only Emacs \(`emacs') and XEmacs are supported \(`xemacs').")
87
88(defvar singular-emacs-major-version nil
89  "An integer describing the major version of the current emacs.")
90
91(defvar singular-emacs-minor-version nil
92  "An integer describing the major version of the current emacs.")
93
94(defun singular-fset (real-function emacs-function xemacs-function
95                                    &optional emacs-19-function)
96  "Set REAL-FUNCTION to one of the functions, in dependency on Emacs flavor and version.
97Sets REAL-FUNCTION to XEMACS-FUNCTION if `singular-emacs-flavor' is
98`xemacs'.  Sets REAL-FUNCTION to EMACS-FUNCTION if `singular-emacs-flavor'
99is `emacs' and `singular-emacs-major-version' is 20.  Otherwise, sets
100REAL-FUNCTION to EMACS-19-FUNCTION which defaults to EMACS-FUNCTION.
101
102This is not as common as would be desirable.  But it is sufficient so far."
103  (cond
104   ;; XEmacs
105   ((eq singular-emacs-flavor 'xemacs)
106    (fset real-function xemacs-function))
107   ;; Emacs 20
108   ((eq singular-emacs-major-version 20)
109    (fset real-function emacs-function))
110   ;; Emacs 19
111   (t
112    (fset real-function (or emacs-19-function emacs-function)))))
113
114(defun singular-set-version ()
115  "Determine flavor, major version, and minor version of current emacs.
116singular.el is guaranteed to run on Emacs 19.34, Emacs 20.2, and XEmacs
11720.2.  It should run on newer version and on slightly older ones, too."
118
119  ;; get major and minor versions first
120  (if (and (boundp 'emacs-major-version)
121           (boundp 'emacs-minor-version))
122      (setq singular-emacs-major-version emacs-major-version
123            singular-emacs-minor-version emacs-minor-version)
124    (with-output-to-temp-buffer "*singular warnings*"
125      (princ
126"You seem to have quite an old Emacs or XEmacs version.  Some of the
127features from singular.el will not work properly.  Consider upgrading to a
128more recent version of Emacs or XEmacs.  singular.el is guaranteed to run
129on Emacs 19.34, Emacs 20.2, and XEmacs 20.2."))
130    ;; assume the oldest version we support
131    (setq singular-emacs-major-version 19
132          singular-emacs-minor-version 34))
133
134  ;; get flavor
135  (if (string-match "XEmacs\\|Lucid" emacs-version)
136      (setq singular-emacs-flavor 'xemacs)
137    (setq singular-emacs-flavor 'emacs)))
138
139(singular-set-version)
140;;}}}
141
142;;{{{ Faces
143
144;; Note:
145;;
146;; These fonts look quite good:
147;; "-adobe-courier-bold-r-*-*-18-*-*-*-*-*-*-*" for input
148;; "-adobe-courier-bold-o-*-*-18-*-*-*-*-*-*-*" for output
149;;
150;; For my (Jens) Emacs a quite good variant is:
151;; "-misc-fixed-bold-*-*-*-15-*-*-*-*-*-*-*" for input
152;; "-misc-fixed-medium-*-*-*-15-*-*-*-*-*-*-*" for output
153
154(make-face 'singular-input-face)
155(set-face-background 'singular-input-face "Orange")
156(defvar singular-input-face 'singular-input-face
157  "Face for user input.
158This face should have set background only.")
159
160(make-face 'singular-output-face)
161;(set-face-font 'singular-output-face "-adobe-courier-bold-o-*-*-18-*-*-*-*-*-*-*")
162(set-face-background 'singular-output-face "Wheat")
163(defvar singular-output-face 'singular-output-face
164  "Face for Singular output.
165This face should have set background only.")
166
167(defun singular-lookup-face (face-type)
168  "Return face belonging to FACE-TYPE.
169NOT READY [should be rewritten completely.  Interface should stay the same.]!"
170  (cond ((eq face-type 'input) singular-input-face)
171        ((eq face-type 'output) singular-output-face)))
172;;}}}
173
174;;{{{ Font-locking
175(defvar singular-font-lock-keywords-1
176  '(
177    ("\\<\\(poly\\|ideal\\)\\>" 1 font-lock-keyword-face)
178    ("\\<\\(ring\\)\\>)" 1 font-lock-variable-face)
179;;    ("\\<^   \\? no\\>" font-lock-warn-face)
180    )
181  "Subdued level for highlighting in singular-(interactive)-mode")
182
183(defvar singular-font-lock-keywords-2 
184  (append
185   singular-font-lock-keywords-1
186   '(
187     ("skipping" font-lock-warn-face)
188;;     ()))
189     ))
190  "Gaudy level for highlihgting in singular-(interactive)-mode") 
191
192(defvar singular-font-lock-keywords singular-font-lock-keywords-1
193  "Default highlighting for singular-(interactive)-mode")
194
195(defvar singular-emacs-font-lock-defaults 
196  '((singular-font-lock-keywords
197     singular-font-lock-keywords-1
198     singular-font-lock-keywords-2)
199    ;; KEYWORDS-ONLY (don't fontify comments/strings when non-nil)
200    nil
201    ;; CASE-FOLD (ignore case when non-nil)
202    nil
203    ;; SYNTAX-ALIST
204    ((?_ . "w"))
205;;; was soll ich da nehmen NOT READY    (( . ))
206    ;; SYNTAX_BEGIN
207    (beginning-of-defun)
208;;    beginning-of-line
209    (font-lock-comment-start-regexp . "//")) ;; gibt es nich im XEmacs
210  "Emacs-default for font-lock-mode in singular-(interactive)-mode")
211
212(defvar singular-xemacs-font-lock-defaults 
213  '((singular-font-lock-keywords
214     singular-font-lock-keywords-1
215     singular-font-lock-keywords-2)
216
217    ;; KEYWORDS-ONLY (don't fontify comments/strings when non-nil)
218    nil
219
220    ;; CASE-FOLD (ignore case when non-nil)
221    nil
222
223    ;; SYNTAX-ALIST
224    nil;;; was soll ich da nehmen NOT READY     (( . ))
225
226    ;; SYNTAX_BEGIN
227    nil) ;;    beginning-of-line
228  "XEmacs-default for font-lock-mode in singular-(interactive)-mode")
229
230(cond 
231 ;; XEmacs
232 ((eq singular-emacs-flavor 'xemacs)
233  (singular-debug 'interactive (message "setting up font-lock for XEmacs"))
234  (put 'singular-interactive-mode 'font-lock-defaults
235       singular-xemacs-font-lock-defaults)))
236
237
238;;}}}
239;;}}}
240
241;;{{{ Singular interactive mode
242;;{{{ Key map and menus
243(defvar singular-interactive-mode-map ()
244  "Key map to use in Singular interactive mode.")
245
246(if singular-interactive-mode-map
247    ()
248  (cond
249   ;; Emacs
250   ((eq singular-emacs-flavor 'emacs)
251    (setq singular-interactive-mode-map
252          (nconc (make-sparse-keymap) comint-mode-map)))
253   ;; XEmacs
254   (t
255    (setq singular-interactive-mode-map (make-keymap))
256    (set-keymap-parents singular-interactive-mode-map (list comint-mode-map))
257    (set-keymap-name singular-interactive-mode-map
258                     'singular-interactive-mode-map)))
259  (define-key singular-interactive-mode-map "\C-m" 'singular-send-or-copy-input))
260
261;; NOT READY
262;; This is just a temporary hack for XEmacs demo.
263(defvar singular-interactive-mode-menu nil)
264(defvar singular-interactive-mode-menu-2
265  (purecopy '("Singular"
266              ["start default" singular-other t]
267              ["start..." singular t]
268              ["exit" singular t])))
269
270(defvar singular-interactive-mode-menu-1
271  '("Commands"
272    ["load file..." singular-load-file t]
273    ("load library"
274     ["all.lib" (singular-load-library "all.lib" t) t]
275     ["general.lib" (singular-load-library "general.lib" t) t]
276     ["matrix.lib" (singular-load-library "matrix.lib" t) t]
277     ["sing.lib" (singular-load-library "sing.lib" t) t]
278     ["elim.lib" (singular-load-library "elim.lib" t) t]
279     ["inout.lib" (singular-load-library "inout.lib" t) t]
280     ["random.lib" (singular-load-library "random.lib" t) t]
281     ["deform.lib" (singular-load-library "deform.lib" t) t]
282     ["homolg.lib" (singular-load-library "homolog.lib" t) t]
283     ["poly.lib" (singular-load-library "poly.lib" t) t]
284     ["factor.lib" (singular-load-library "factor.lib" t) t]
285     ["ring.lib" (singular-load-library "ring.lib" t) t]
286     ["primdec.lib" (singular-load-library "primdex.lib" t) t]
287     "---"
288     ["other..." singular-load-library t])
289    "---"
290    ["load demo" singular-demo-load (not singular-demo-mode)]
291    ["exit demo" singular-demo-exit singular-demo-mode]
292    "---"
293    ["truncate lines" (setq truncate-lines (not truncate-lines))
294       :style toggle :selected truncate-lines]
295    ["fold last section" singular t]
296    ["fold current section" singular t]
297    ["fold all sections" singular t]
298    ))
299
300;;(add-menu-button nil ["Singular" (singular) t] "Start Singular")
301;;}}}
302
303;;{{{ Syntax table
304(defvar singular-interactive-mode-syntax-table nil
305  "Syntax table for singular-interactive-mode")
306(if singular-interactive-mode-syntax-table
307    ()
308  (setq singular-interactive-mode-syntax-table (make-syntax-table))
309  (modify-syntax-entry ?/ ". 1456" singular-interactive-mode-syntax-table))
310;;}}}
311
312;;{{{ Miscellaneous
313
314;; Note:
315;;
316;; We assume a one-to-one correspondance between Singular buffers
317;; and Singular processes.  We always have
318;; (equal buffer-name (concat "*" process-name "*")).
319
320(defun singular-buffer-name-to-process-name (buffer-name)
321  "Create the process name for BUFFER-NAME.
322The process name is the buffer name with surrounding `*' stripped
323off."
324  (substring buffer-name 1 -1))
325
326(defun singular-process-name-to-buffer-name (process-name)
327  "Create the buffer name for PROCESS-NAME.
328The buffer name is the process name with surrounding `*'."
329  (concat "*" process-name "*"))
330
331(defun singular-run-hook-with-arg-and-value (hook value)
332  "Call functions on HOOK.
333Provides argument VALUE.  If a function returns a non-nil value it
334replaces VALUE as new argument to the functions.  Returns final
335VALUE."
336  (let (result)
337    (while hook
338      (setq result (funcall (car hook) value))
339      (and result (setq value result))
340      (setq hook (cdr hook)))
341    value))
342
343(defmacro singular-process ()
344  "Return process of current buffer."
345  (get-buffer-process (current-buffer)))
346
347(defmacro singular-process-mark ()
348  "Return process mark of current buffer."
349  (process-mark (get-buffer-process (current-buffer))))
350
351(defun singular-load-file (file &optional noexpand)
352  "docu NOT READY"
353  (interactive "fLoad file: ")
354  (let ((filename (if noexpand
355                      file
356                    (expand-file-name file))))
357    (singular-send-string (get-buffer-process (current-buffer))
358                          (concat "< \"" filename "\";"))))
359
360(defun singular-load-library (file &optional noexpand)
361  "docu NOT READY"
362  (interactive "fLoad Library: ")
363  (let ((filename (if noexpand
364                      file
365                    (expand-file-name file))))
366    (singular-send-string (get-buffer-process (current-buffer))
367                          (concat "LIB \"" filename "\";"))))
368;;}}}
369
370;;{{{ Customizing variables of comint
371
372;; Note:
373;;
374;; In contrast to the variables from comint.el, all the variables
375;; below are global variables.  It would not make any sense to make
376;; them buffer-local since
377;; o they are read only when Singular interactive mode comes up;
378;; o since they are Singular-dependent and not user-dependent, i.e.,
379;;   the user would not mind to change them.
380;;
381;; For the same reasons these variables are not marked as
382;; "customizable" by a leading `*'.
383
384(defvar singular-prompt-regexp "^> "
385  "Regexp to match prompt patterns in Singular.
386Should not match the continuation prompt \(`.'), only the regular
387prompt \(`>').
388
389This variable is used to initialize `comint-prompt-regexp' when
390Singular interactive mode starts up.")
391
392(defvar singular-delimiter-argument-list '(?= ?\( ?\) ?, ?;)
393  "List of characters to recognize as separate arguments.
394
395This variable is used to initialize `comint-delimiter-argument-list'
396when Singular interactive mode starts up.")
397
398(defvar singular-input-ignoredups t
399  "If non-nil, don't add input matching the last on the input ring.
400
401This variable is used to initialize `comint-input-ignoredups' when
402Singular interactive mode starts up.")
403
404(defvar singular-buffer-maximum-size 2048
405  "The maximum size in lines for Singular buffers.
406
407This variable is used to initialize `comint-buffer-maximum-size' when
408Singular interactive mode starts up.")
409
410(defvar singular-input-ring-size 64
411  "Size of input history ring.
412
413This variable is used to initialize `comint-input-ring-size' when
414Singular interactive mode starts up.")
415
416(defvar singular-history-filter-regexp "\\`\\(..?\\|\\s *\\)\\'"
417  "Regular expression to filter strings *not* to insert in the history.
418By default, input consisting of less than three characters and input
419consisting of white-space only is not added to the history.")
420
421(defvar singular-history-filter
422  (function (lambda (string)
423              (not (string-match singular-history-filter-regexp string))))
424  "Predicate for filtering additions to input history.
425
426This variable is used to initialize `comint-input-filter' when
427Singular interactive mode starts up.")
428;;}}}
429
430;;{{{ Skipping and stripping prompts and newlines and other things
431
432;; Note:
433;;
434;; All of these functions modify the match data!
435
436(defun singular-strip-white-space (string &optional trailing leading)
437  "Strip off trailing or leading white-space from STRING.
438Strips off trailing white-space if optional argument TRAILING is
439non-nil.
440Strips off leading white-space if optional argument LEADING is
441non-nil."
442  (let ((beg 0)
443        (end (length string)))
444    (and leading
445         (string-match "\\`\\s-*" string)
446         (setq beg (match-end 0)))
447    (and trailing
448         (string-match "\\s-*\\'" string beg)
449         (setq end (match-beginning 0)))
450    (substring string beg end)))
451
452(defconst singular-extended-prompt-regexp "\\([?>.] \\)"
453  "Matches one Singular prompt.
454Should not be anchored neither to start nor to end!")
455
456(defconst singular-strip-leading-prompt-regexp
457  (concat "\\`" singular-extended-prompt-regexp "+")
458  "Matches Singular prompt anchored to string start.")
459
460(defun singular-strip-leading-prompt (string)
461  "Strip leading prompts from STRING.
462May or may not return STRING or a modified copy of it."
463  (if (string-match singular-strip-leading-prompt-regexp string)
464      (substring string (match-end 0))
465    string))
466
467(defconst singular-remove-prompt-regexp
468  (concat "^" singular-extended-prompt-regexp
469          "*" singular-extended-prompt-regexp)
470  "Matches a non-empty sequence of prompts at start of a line.")
471
472(defun singular-remove-prompt (beg end)
473  "Remove all superfluous prompts from region between BEG and END.
474Removes all but the last prompt of a sequence if that sequence ends at
475END.
476The region between BEG and END should be accessible.
477Leaves point after the last prompt found."
478  (let ((end (copy-marker end))
479        prompt-end)
480    (goto-char beg)
481    (while (and (setq prompt-end
482                      (re-search-forward singular-remove-prompt-regexp end t))
483                (not (= end prompt-end)))
484      (delete-region (match-beginning 0) prompt-end))
485
486    ;; check for trailing prompt
487    (if prompt-end
488        (delete-region (match-beginning 0)  (match-beginning 2)))
489    (set-marker end nil)))
490
491(defconst singular-skip-prompt-forward-regexp
492  (concat singular-extended-prompt-regexp "*")
493  "Matches an arbitary sequence of Singular prompts.")
494
495(defun singular-skip-prompt-forward ()
496  "Skip forward over prompts."
497  (looking-at singular-skip-prompt-forward-regexp)
498  (goto-char (match-end 0)))
499
500(defun singular-skip-prompt-backward ()
501  "Skip backward over prompts."
502  (while (re-search-backward singular-extended-prompt-regexp (- (point) 2) t)))
503;;}}}
504
505;;{{{ Simple section stuff for both Emacs and XEmacs
506
507;; Note:
508;;
509;; Sections and simple sections are used to mark Singular's input and
510;; output for further access.  Here are some general notes on simple
511;; sections.  Sections are explained in the respective folding.
512;;
513;; In general, simple sections are more or less Emacs' overlays or XEmacs
514;; extents, resp.  But they are more than simply an interface to overlays
515;; or sections.
516;;
517;; - Simple sections are non-empty portions of text.  They are interpreted
518;;   as left-closed, right-opened intervals, i.e., the start point of a
519;;   simple sections belongs to it whereas the end point does not.
520;; - Simple sections start and end at line borders only.
521;; - Simple sections do not overlap.  Thus, any point in the buffer may be
522;;   covered by at most one simple section.
523;; - Besides from their start and their end, simple sections have some type
524;;   associated.
525;; - Simple sections are realized using overlays (extents for XEmacs)
526;;   which define the start and, end, and type (via properties) of the
527;;   simple section.  Actually, as a lisp object a simple section is
528;;   nothing else but the underlying overlay.
529;; - There may be so-called clear simple sections.  Clear simple sections
530;;   have not an underlying overlay.  Instead, they start at the end of the
531;;   preceding non-clear simple section, end at the beginning of the next
532;;   non-clear simple section, and have the type defined by
533;;   `singular-simple-sec-clear-type'.  Clear simple sections are
534;;   represented by nil.
535;; - Buffer narrowing does not restrict the extent of completely or
536;;   partially inaccessible simple sections.  But one should note that
537;;   some of the functions assume that there is no narrowing in
538;;   effect.
539;; - After creation, simple sections are not modified any further.
540;;
541;; - In `singular-interactive-mode', the whole buffer is covered with
542;;   simple sections from the very beginning of the file up to the
543;;   beginning of the line containing the last input or output.  The
544;;   remaining text up to `(point-max)' may be interpreted as covered by
545;;   one clear simple section.  Thus, it is most reasonable to define
546;;   `input' to be the type of clear simple sections.
547
548(defvar singular-simple-sec-clear-type 'input
549  "Type of clear simple sections.
550If nil no clear simple sections are used.")
551
552(defvar singular-simple-sec-last-end nil
553  "Marker at the end of the last simple section.
554Should be initialized by `singular-simple-sec-init' before any calls to
555`singular-simple-sec-create' are done.
556
557This variable is buffer-local.")
558
559(defun singular-simple-sec-init (pos)
560  "Initialize global variables belonging to simple section management.
561Creates the buffer-local marker `singular-simple-sec-last-end' and
562initializes it to POS."
563  (make-local-variable 'singular-simple-sec-last-end)
564  (if (not (markerp singular-simple-sec-last-end))
565      (setq singular-simple-sec-last-end (make-marker)))
566  (set-marker singular-simple-sec-last-end pos))
567
568;; Note:
569;;
570;; The rest of the folding is either marked as
571;; Emacs
572;; or
573;; XEmacs
574
575(singular-fset 'singular-simple-sec-create
576               'singular-emacs-simple-sec-create
577               'singular-xemacs-simple-sec-create)
578
579(singular-fset 'singular-simple-sec-reset-last
580               'singular-emacs-simple-sec-reset-last
581               'singular-xemacs-simple-sec-reset-last)
582
583(singular-fset 'singular-simple-sec-start
584               'singular-emacs-simple-sec-start
585               'singular-xemacs-simple-sec-start)
586
587(singular-fset 'singular-simple-sec-end
588               'singular-emacs-simple-sec-end
589               'singular-xemacs-simple-sec-end)
590
591(singular-fset 'singular-simple-sec-start-at
592               'singular-emacs-simple-sec-start-at
593               'singular-xemacs-simple-sec-start-at)
594
595(singular-fset 'singular-simple-sec-end-at
596               'singular-emacs-simple-sec-end-at
597               'singular-xemacs-simple-sec-end-at)
598
599(singular-fset 'singular-simple-sec-type
600               'singular-emacs-simple-sec-type
601               'singular-xemacs-simple-sec-type)
602
603(singular-fset 'singular-simple-sec-at
604               'singular-emacs-simple-sec-at
605               'singular-xemacs-simple-sec-at)
606
607(singular-fset 'singular-simple-sec-before
608               'singular-emacs-simple-sec-before
609               'singular-xemacs-simple-sec-before)
610
611(singular-fset 'singular-simple-sec-in
612               'singular-emacs-simple-sec-in
613               'singular-xemacs-simple-sec-in)
614;;}}}
615
616;;{{{ Simple section stuff for Emacs
617(defun singular-emacs-simple-sec-create (type end)
618  "Create a new simple section of type TYPE.
619Creates the section from end of previous simple section up to END.
620END should be larger than `singular-simple-sec-last-end'.
621Returns the new simple section or `empty' if no simple section has
622been created.
623Assumes that no narrowing is in effect.
624Updates `singular-simple-sec-last-end'."
625  (let ((last-end (marker-position singular-simple-sec-last-end))
626        ;; `simple-sec' is the new simple section or `empty'
627        simple-sec)
628
629    ;; get beginning of line before END.  At this point we need that there
630    ;; are no restrictions.
631    (setq end (let ((old-point (point)))
632                (goto-char end) (beginning-of-line)
633                (prog1 (point) (goto-char old-point))))
634
635    (cond
636     ;; do not create empty sections
637     ((eq end last-end) (setq simple-sec 'empty))
638     ;; create only non-clear simple sections
639     ((not (eq type singular-simple-sec-clear-type))
640      ;; if type has not changed we only have to extend the previous
641      ;; simple section
642      (setq simple-sec (singular-emacs-simple-sec-before last-end))
643      (if (eq type (singular-emacs-simple-sec-type simple-sec))
644          ;; move existing overlay
645          (setq simple-sec (move-overlay simple-sec (overlay-start simple-sec) end))
646        ;; create new overlay
647        (setq simple-sec (make-overlay last-end end))
648        ;; set type property
649        (overlay-put simple-sec 'singular-type type)
650        ;; set face
651        (overlay-put simple-sec 'face (singular-lookup-face type))
652        ;; evaporate empty sections
653        (overlay-put simple-sec 'evaporate t))))
654           
655    ;; update end of last simple section
656    (set-marker singular-simple-sec-last-end end)
657    simple-sec))
658
659(defun singular-emacs-simple-sec-reset-last (pos)
660  "Reset end of last simple section to POS after accidental extension.
661Updates `singular-simple-sec-last-end', too."
662  (let ((simple-sec (singular-emacs-simple-sec-at pos)))
663    (if simple-sec (move-overlay simple-sec (overlay-start simple-sec) pos))
664    (set-marker singular-simple-sec-last-end pos)))
665
666(defun singular-emacs-simple-sec-start (simple-sec)
667  "Return start of non-clear simple section SIMPLE-SEC."
668  (overlay-start simple-sec))
669
670(defun singular-emacs-simple-sec-end (simple-sec)
671  "Return end of non-clear simple section SIMPLE-SEC."
672  (overlay-end simple-sec))
673
674(defun singular-emacs-simple-sec-start-at (pos)
675  "Return start of clear section at position POS.
676Assumes that no narrowing is in effect."
677  (let ((previous-overlay-change (1+ (point))))
678    ;; this `while' loop at last will run into the end of the next
679    ;; non-clear overlay or stop at bob.  Since POS may be right at the end
680    ;; of a previous non-clear location, we have to search at least one
681    ;; time from POS+1 backwards.
682    (while (not (or (singular-emacs-simple-sec-before previous-overlay-change)
683                    (eq previous-overlay-change (point-min))))
684      (setq previous-overlay-change
685            (previous-overlay-change previous-overlay-change)))
686    previous-overlay-change))
687
688(defun singular-emacs-simple-sec-end-at (pos)
689  "Return end of clear section at position POS.
690Assumes that no narrowing is in effect."
691  (let ((next-overlay-change (next-overlay-change (point))))
692    ;; this `while' loop at last will run into the beginning of the next
693    ;; non-clear overlay or stop at eob.  Since POS may not be at the
694    ;; beginning of a non-clear simple section we may start searching
695    ;; immediately.
696    (while (not (or (singular-emacs-simple-sec-at next-overlay-change)
697                    (eq next-overlay-change (point-max))))
698      (setq next-overlay-change
699            (next-overlay-change next-overlay-change)))
700    next-overlay-change))
701
702(defun singular-emacs-simple-sec-type (simple-sec)
703  "Return type of SIMPLE-SEC."
704  (if simple-sec
705      (overlay-get simple-sec 'singular-type)
706    singular-simple-sec-clear-type))
707
708(defun singular-emacs-simple-sec-at (pos)
709  "Return simple section at position POS."
710  (let ((overlays (overlays-at pos)) simple-sec)
711    ;; be careful, there may be other overlays!
712    (while (and overlays (not simple-sec))
713      (if (singular-emacs-simple-sec-type (car overlays))
714          (setq simple-sec (car overlays)))
715      (setq overlays (cdr overlays)))
716    simple-sec))
717
718(defun singular-emacs-simple-sec-before (pos)
719  "Return simple section before position POS.
720This is the same as `singular-simple-section-at' except if POS falls
721on a section border.  In this case `singular-simple-section-before'
722returns the previous simple section instead of the current one."
723  (singular-emacs-simple-sec-at (max 1 (1- pos))))
724
725(defun singular-emacs-simple-sec-in (beg end)
726  "Return a list of all simple sections intersecting with the region from BEG to END.
727A simple section intersects the region if the section and the region
728have at least one character in common.
729The result contains both clear and non-clear simple sections in the
730order in that the appear in the region."
731  ;; NOT READY
732  nil)
733;;}}}
734
735;;{{{ Simple section stuff for XEmacs
736(defun singular-xemacs-simple-sec-create (type end)
737  "Create a new simple section of type TYPE.
738Creates the section from end of previous simple section up to END.
739END should be larger than `singular-simple-sec-last-end'.
740Returns the new simple section or `empty' if no simple section has
741been created.
742Assumes that no narrowing is in effect.
743Updates `singular-simple-sec-last-end'."
744  (let ((last-end (marker-position singular-simple-sec-last-end))
745        ;; `simple-sec' is the new simple section or `empty'
746        simple-sec)
747
748    ;; get beginning of line before END.  At this point we need that there
749    ;; are no restrictions.
750    (setq end (let ((old-point (point)))
751                (goto-char end) (beginning-of-line)
752                (prog1 (point) (goto-char old-point))))
753
754    (cond
755     ;; do not create empty sections
756     ((eq end last-end) (setq simple-sec 'empty))
757     ;; create only non-clear simple sections
758     ((not (eq type singular-simple-sec-clear-type))
759      ;; if type has not changed we only have to extend the previous
760      ;; simple section
761      (setq simple-sec (singular-xemacs-simple-sec-before last-end))
762      (if (eq type (singular-xemacs-simple-sec-type simple-sec))
763          ;; move existing extent
764          (setq simple-sec (set-extent-endpoints simple-sec 
765                                                 (extent-start-position simple-sec) end))
766        ;; create new extent
767        (setq simple-sec (make-extent last-end end))
768        ;; set type property
769        (set-extent-property simple-sec 'singular-type type)
770        ;; set face
771        (set-extent-property simple-sec 'face (singular-lookup-face type)))))
772           
773    ;; update end of last simple section
774    (set-marker singular-simple-sec-last-end end)
775    simple-sec))
776
777(defun singular-xemacs-simple-sec-reset-last (pos)
778  "Reset end of last simple section to POS after accidental extension.
779Updates `singular-simple-sec-last-end', too."
780  (let ((simple-sec (singular-xemacs-simple-sec-at pos)))
781    (if simple-sec 
782        (set-extent-endpoints simple-sec (extent-start-position simple-sec) pos))
783    (set-marker singular-simple-sec-last-end pos)))
784
785(defun singular-xemacs-simple-sec-start (simple-sec)
786  "Return start of non-clear simple section SIMPLE-SEC."
787  (extent-start-position simple-sec))
788
789(defun singular-xemacs-simple-sec-end (simple-sec)
790  "Return end of non-clear simple section SIMPLE-SEC."
791  (extent-end-position simple-sec))
792
793(defun singular-xemacs-simple-sec-start-at (pos)
794  "Return start of clear section at position POS.
795Assumes that no narrowing is in effect."
796  (let ((previous-extent-change (1+ (point))))
797    ;; this `while' loop at last will run into the end of the next
798    ;; non-clear extent or stop at bob.  Since POS may be right at the end
799    ;; of a previous non-clear location, we have to search at least one
800    ;; time from POS+1 backwards.
801    (while (not (or (singular-xemacs-simple-sec-before previous-extent-change)
802                    (eq previous-extent-change (point-min))))
803      (setq previous-extent-change
804            (previous-extent-change previous-extent-change)))
805    previous-extent-change))
806
807(defun singular-xemacs-simple-sec-end-at (pos)
808  "Return end of clear section at position POS.
809Assumes that no narrowing is in effect."
810  (let ((next-extent-change (next-extent-change (point))))
811    ;; this `while' loop at last will run into the beginning of the next
812    ;; non-clear extent or stop at eob.  Since POS may not be at the
813    ;; beginning of a non-clear simple section we may start searching
814    ;; immediately.
815    (while (not (or (singular-xemacs-simple-sec-at next-extent-change)
816                    (eq next-extent-change (point-max))))
817      (setq next-extent-change
818            (next-extent-change next-extent-change)))
819    next-extent-change))
820
821(defun singular-xemacs-simple-sec-type (simple-sec)
822  "Return type of SIMPLE-SEC."
823  (if simple-sec
824      (extent-property simple-sec 'singular-type)
825    singular-simple-sec-clear-type))
826
827(defun singular-xemacs-simple-sec-at (pos)
828  "Return simple section at position POS."
829  (map-extents (function (lambda (ext args) ext))
830               ;; is this pos-pos-region OK? I think so.
831               (current-buffer) pos pos nil nil 'singular-type))
832
833(defun singular-xemacs-simple-sec-before (pos)
834  "Return simple section before position POS.
835This is the same as `singular-simple-section-at' except if POS falls
836on a section border.  In this case `singular-simple-section-before'
837returns the previous simple section instead of the current one."
838  (singular-xemacs-simple-sec-at (max 1 (1- pos))))
839
840(defun singular-xemacs-simple-sec-in (beg end)
841  "Return a list of all simple sections intersecting with the region from BEG to END.
842A simple section intersects the region if the section and the region
843have at least one character in common.
844The result contains both clear and non-clear simple sections in the
845order they appear in the region."
846  ;; NOT READY
847  nil)
848;;}}}
849
850;;{{{ Section stuff
851
852;; Note:
853;;
854;; Sections are built on simple sections.  Their purpose is to cover the
855;; difference between clear and non-clear simple sections.
856;;
857;; - Sections consist of a simple section, its type, and its start and end
858;;   points.  This is redundant information only in the case of non-clear
859;;   simple section.
860;; - Sections are read-only objects, neither are they modified nor are they
861;;   created.
862;; - Buffer narrowing does not restrict the extent of completely or
863;;   partially inaccessible sections.  In contrast to simple sections
864;;   the functions concerning sections do not assume that there is no
865;;   narrowing in effect.
866;; - Sections are independent from implementation dependencies.  There are
867;;   no different versions of the functions for Emacs and XEmacs.
868
869(defun singular-section-at (pos &optional restricted)
870  "Return section at position POS.
871Returns section intersected with current restriction if RESTRICTED is
872non-nil."
873  (let* ((simple-sec (singular-simple-sec-at pos))
874         (type (singular-simple-sec-type simple-sec))
875         start end)
876    (if simple-sec
877        (setq start (singular-simple-sec-start simple-sec)
878              end  (singular-simple-sec-end simple-sec))
879      (save-restriction
880        (widen)
881        (setq start (singular-simple-sec-start-at pos)
882              end (singular-simple-sec-end-at pos))))
883    (if restricted
884        (vector simple-sec type
885                (max start (point-min)) (min end (point-max)))
886      (vector simple-sec type start end))))
887
888(defun singular-section-before (pos &optional restricted)
889  "Return section before position POS.
890This is the same as `singular-section-at' except if POS falls on a
891section border.  In this case `singular-section-before' returns the
892previous section instead of the current one.
893Returns section intersected with current restriction if RESTRICTED is
894non-nil."
895  (singular-section-at (max 1 (1- pos)) restricted))
896
897(defmacro singular-section-simple-sec (section)
898  "Return underlying simple section of SECTION."
899  `(aref ,section 0))
900
901(defmacro singular-section-type (section)
902  "Return type of SECTION."
903  `(aref ,section 1))
904
905(defmacro singular-section-start (section)
906  "Return start of SECTION."
907  `(aref ,section 2))
908
909(defmacro singular-section-end (section)
910  "Return end of SECTION."
911  `(aref ,section 3))
912;;}}}
913
914;;{{{ Getting section contents
915(defun singular-input-section-to-string (section &optional end raw)
916  "Get content of SECTION as string.
917Returns text between start of SECTION and END if optional argument END
918is non-nil.  END should be a position inside SECTION.
919Strips leading prompts and trailing white space unless optional argument
920RAW is non-nil."
921  (save-restriction
922    (widen)
923    (let ((string (if end
924                      (buffer-substring (singular-section-start section) end)
925                    (buffer-substring (singular-section-start section)
926                                      (singular-section-end section)))))
927      (if raw string
928        (singular-strip-leading-prompt
929         (singular-strip-white-space string t))))))
930;;}}}
931
932;;{{{ Last input and output section
933(defun singular-last-input-section (&optional no-error)
934  "Return last input section.
935Returns nil if optional argument NO-ERROR is non-nil and there is no
936last input section defined, throws an error otherwise."
937  (let ((last-input-start (marker-position singular-last-input-section-start))
938        (last-input-end (marker-position singular-current-output-section-start)))
939    (cond ((and last-input-start last-input-end)
940           (vector (singular-simple-sec-at last-input-start) 'input
941                   last-input-start last-input-end))
942          (no-error nil)
943          (t (error "No last input section defined")))))
944
945(defun singular-current-output-section (&optional no-error)
946  "Return current output section.
947Returns nil if optional argument NO-ERROR is non-nil and there is no
948current output section defined, throws an error otherwise."
949  (let ((current-output-start (marker-position singular-current-output-section-start))
950        (current-output-end (save-excursion
951                              (goto-char (singular-process-mark))
952                              (singular-skip-prompt-backward)
953                              (and (bolp) (point)))))
954    (cond ((and current-output-start current-output-end)
955           (vector (singular-simple-sec-at current-output-start) 'output
956                   current-output-start current-output-end))
957          (no-error nil)
958          (t (error "No current output section defined")))))
959
960(defun singular-last-output-section (&optional no-error)
961  "Return last output section.
962Returns nil if optional argument NO-ERROR is non-nil and there is no
963last output section defined, throws an error otherwise."
964  (let ((last-output-start (marker-position singular-last-output-section-start))
965        (last-output-end (marker-position singular-last-input-section-start)))
966    (cond ((and last-output-start last-output-end)
967           (vector (singular-simple-sec-at last-output-start) 'output
968                   last-output-start last-output-end))
969          (no-error nil)
970          (t (error "No last output section defined")))))
971
972(defun singular-latest-output-section (&optional no-error)
973  "Return latest output section.
974This is the current output section if it is defined, otherwise the
975last output section.
976Returns nil if optional argument NO-ERROR is non-nil and there is no
977latest output section defined, throws an error otherwise."
978  (or (singular-current-output-section t)
979      (singular-last-output-section t)
980      (if no-error
981          nil
982        (error "No latest output section defined"))))
983;;}}}
984
985;;{{{ Folding sections
986(defvar singular-folding-ellipsis "Singular I/O ..."
987  "Ellipsis to show for folded input or output.")
988
989(defun singular-fold-internal (start end fold)
990  "(Un)fold region from START to END.
991Folds if FOLD is non-nil, otherwise unfolds.
992Folding affects undo information and buffer modified flag.
993Assumes that there is no narrowing in effect."
994  (save-excursion
995    (if fold
996        (progn
997          (goto-char start) (insert ?\r)
998          (subst-char-in-region start end ?\n ?\r t))
999      (subst-char-in-region start end ?\r ?\n t)
1000      (goto-char start) (delete-char 1))))
1001
1002(defun singular-section-foldedp (section)
1003  "Return t iff SECTION is folded.
1004Assumes that there is no narrowing in effect."
1005  (eq (char-after (singular-section-start section)) ?\r))
1006
1007(defun singular-fold-section (section)
1008  "\(Un)fold SECTION.
1009\(Un)folds section at point and goes to beginning of section if called
1010interactively.
1011Unfolds folded sections and folds unfolded sections."
1012  (interactive (list (singular-section-at (point))))
1013  (let ((start (singular-section-start section))
1014        ;; we have to save restrictions this way since we change text
1015        ;; outside the restriction.  Note that we do not use a marker for
1016        ;; `old-point-min'.  This way, even partial narrowed sections are
1017        ;; folded properly if they have been narrowed at bol.  Nice but
1018        ;; dirty trick: The insertion of a `?\r' at beginning of section
1019        ;; advances the beginning of the restriction such that it displays
1020        ;; the `?\r' immediately before bol.  Seems worth it.
1021        (old-point-min (point-min))
1022        (old-point-max (point-max-marker)))
1023    (unwind-protect
1024        (progn
1025          (widen)
1026          (singular-fold-internal start (singular-section-end section)
1027                                  (not (singular-section-foldedp section))))
1028      ;; this is unwide-protected
1029      (narrow-to-region old-point-min old-point-max)
1030      (set-marker old-point-max nil))
1031    (if (interactive-p) (goto-char (max start (point-min))))))
1032;;}}}
1033
1034;;{{{ Input and output filters
1035
1036;; debugging filters
1037(defun singular-debug-pre-input-filter (string)
1038  "Display STRING and some markers in mini-buffer."
1039  (singular-debug 'interactive-filter
1040                  (message "Pre-input filter: %s"
1041                           (singular-debug-format string)))
1042  (singular-debug 'interactive-filter
1043                  (message "Pre-input filter: (li %S ci %S lo %S co %S)"
1044                           (marker-position singular-last-input-section-start)
1045                           (marker-position singular-current-input-section-start)
1046                           (marker-position singular-last-output-section-start)
1047                           (marker-position singular-current-output-section-start)))
1048  nil)
1049
1050(defun singular-debug-post-input-filter (beg end)
1051  "Display BEG, END, and some markers in mini-buffer."
1052  (singular-debug 'interactive-filter
1053                  (message "Post-input filter: (beg %S end %S)" beg end))
1054  (singular-debug 'interactive-filter
1055                  (message "Post-input filter: (li %S ci %S lo %S co %S)"
1056                           (marker-position singular-last-input-section-start)
1057                           (marker-position singular-current-input-section-start)
1058                           (marker-position singular-last-output-section-start)
1059                           (marker-position singular-current-output-section-start))))
1060
1061(defun singular-debug-pre-output-filter (string)
1062  "Display STRING and some markers in mini-buffer."
1063  (singular-debug 'interactive-filter
1064                  (message "Pre-output filter: %s"
1065                           (singular-debug-format string)))
1066  (singular-debug 'interactive-filter
1067                  (message "Pre-output filter: (li %S ci %S lo %S co %S)"
1068                           (marker-position singular-last-input-section-start)
1069                           (marker-position singular-current-input-section-start)
1070                           (marker-position singular-last-output-section-start)
1071                           (marker-position singular-current-output-section-start)))
1072  nil)
1073
1074(defun singular-debug-post-output-filter (beg end simple-sec-start)
1075  "Display BEG, END, SIMPLE-SEC-START, and some markers in mini-buffer."
1076  (singular-debug 'interactive-filter
1077                  (message "Post-output filter: (beg %S end %S sss %S)"
1078                           beg end simple-sec-start))
1079  (singular-debug 'interactive-filter
1080                  (message "Post-output filter: (li %S ci %S lo %S co %S)"
1081                           (marker-position singular-last-input-section-start)
1082                           (marker-position singular-current-input-section-start)
1083                           (marker-position singular-last-output-section-start)
1084                           (marker-position singular-current-output-section-start))))
1085
1086;; stripping prompts
1087(defun singular-remove-prompt-filter (beg end simple-sec-start)
1088  "Strip prompts from last simple section."
1089  (if simple-sec-start (singular-remove-prompt simple-sec-start end)))
1090;;}}}
1091
1092;;{{{ Demo mode
1093(defvar singular-demo-chunk-regexp "\\(\n\n\\)"
1094  "Regular expressions to recognize chunks of a demo file.
1095If there is a subexpression specified its content is removed when the
1096chunk is displayed.")
1097
1098(defvar singular-demo-mode nil
1099  "Non-nil if Singular demo mode is on.
1100
1101This variable is buffer-local.")
1102
1103(defvar singular-demo-old-mode-name nil
1104  "Used to store previous `mode-name' before switching to demo mode.
1105
1106This variable is buffer-local.")
1107
1108(defvar singular-demo-end nil
1109  "Marker pointing to end of demo file.
1110
1111This variable is buffer-local.")
1112
1113(defvar singular-demo-command-on-enter nil
1114  "Singular command to send when entering demo mode or nil if no string to send.")
1115
1116(defvar singular-demo-command-on-leave nil
1117  "Singular command to send when leaving demo mode or nil if no string to send.")
1118 
1119(defun singular-demo-mode (mode)
1120  "Switch between demo mode states.
1121MODE may be either:
1122- `init' to initialize global variables;
1123- `exit' to clean up demo and leave Singular demo mode;
1124- `enter' to enter Singular demo mode;
1125- `leave' to leave Singular demo mode.
1126
1127Modifies the global variables `singular-demo-mode',
1128`singular-demo-end', and `singular-demo-old-mode-name' to reflect the
1129new state of Singular demo mode."
1130  (cond
1131   ;; initialization.  Should be called only once.
1132   ((eq mode 'init)
1133    (make-local-variable 'singular-demo-mode)
1134    (make-local-variable 'singular-demo-mode-old-name)
1135    (make-local-variable 'singular-demo-mode-end)
1136    (if (not (and (boundp 'singular-demo-end)
1137                  singular-demo-end))
1138        (setq singular-demo-end (make-marker))))
1139
1140   ;; final exit.  Clean up demo.
1141   ((and (eq mode 'exit)
1142         singular-demo-mode)
1143    (setq mode-name singular-demo-old-mode-name
1144          singular-demo-mode nil)
1145    ;; clean up hidden rest of demo file if existent
1146    (let ((old-point-min (point-min))
1147          (old-point-max (point-max)))
1148      (unwind-protect
1149          (progn
1150            (widen)
1151            (delete-region old-point-max singular-demo-end))
1152        ;; this is unwide-protected
1153        (narrow-to-region old-point-min old-point-max)))
1154    (if (and singular-demo-command-on-leave
1155             (singular-process))
1156        (send-string (singular-process) singular-demo-command-on-leave))
1157    (force-mode-line-update))
1158
1159   ;; enter demo mode
1160   ((and (eq mode 'enter)
1161         (not singular-demo-mode))
1162    (setq singular-demo-old-mode-name mode-name
1163          mode-name "Singular Demo"
1164          singular-demo-mode t)
1165    (if singular-demo-command-on-enter
1166        (send-string (singular-process) singular-demo-command-on-enter))
1167    (force-mode-line-update))
1168
1169   ;; leave demo mode
1170   ((and (eq mode 'leave)
1171         singular-demo-mode)
1172    (setq mode-name singular-demo-old-mode-name
1173          singular-demo-mode nil)
1174    (if singular-demo-command-on-leave
1175        (send-string (singular-process) singular-demo-command-on-leave))
1176    (force-mode-line-update))))
1177
1178(defun singular-demo-exit ()
1179  "Prematurely exit singular demo mode."
1180  (interactive)
1181  (singular-demo-mode 'exit))
1182
1183(defun singular-demo-show-next-chunk ()
1184  "Show next chunk of demo file at input prompt.
1185Moves point to end of buffer and widenes the buffer such that the next
1186chunk of the demo file becomes visible.
1187Finds and removes chunk separators as specified by
1188`singular-demo-chunk-regexp'.
1189Removing chunk separators affects undo information and buffer-modified
1190flag.
1191Leaves demo mode after showing last chunk."
1192  (let ((old-point-min (point-min)))
1193    (unwind-protect
1194        (progn
1195          (goto-char (point-max))
1196          (widen)
1197          (if (re-search-forward singular-demo-chunk-regexp singular-demo-end 'limit)
1198              (and (match-beginning 1)
1199                   (delete-region (match-beginning 1) (match-end 1)))
1200            ;; remove trailing white-space
1201            (skip-syntax-backward "-")
1202            (delete-region (point) singular-demo-end)
1203            (singular-demo-mode 'leave)))
1204
1205      ;; this is unwind-protected
1206      (narrow-to-region old-point-min (point)))))
1207
1208(defun singular-demo-load (demo-file)
1209  "Load demo file DEMO-FILE and enter Singular demo mode.
1210For a description of the Singular demo mode one should refer to the
1211doc-string of `singular-interactive-mode'.
1212Moves point to end of buffer and inserts contents of DEMO-FILE there."
1213  (interactive "fLoad demo file: ")
1214
1215  ;; check for running demo
1216  (and singular-demo-mode
1217       (error "Another demo is already running"))
1218
1219  (let ((old-point-min (point-min)))
1220    (unwind-protect
1221        (progn
1222          (goto-char (point-max))
1223          (widen)
1224          ;; load file and remember its end
1225          (set-marker singular-demo-end
1226                      (+ (point) (nth 1 (insert-file-contents demo-file)))))
1227
1228      ;; completely hide demo file.
1229      ;; This is unwide protected.
1230      (narrow-to-region old-point-min (point)))
1231
1232    ;; switch demo mode on
1233    (singular-demo-mode 'enter)))
1234;;}}}
1235     
1236;;{{{ Some lengthy notes on input and output
1237
1238;; NOT READY[so sorry]!
1239
1240;;}}}
1241
1242;;{{{ Sending input
1243(defvar singular-pre-input-filter-functions nil
1244  "Functions to call before input is sent to process.
1245These functions get one argument, a string containing the text which
1246is to be sent to process.  The functions should return either nil
1247or a string.  In the latter case the returned string replaces the
1248string to be sent to process.
1249
1250This is a buffer-local variable, not a buffer-local hook!
1251
1252`singular-run-hook-with-arg-and-value' is used to run the functions in
1253the list.")
1254
1255(defvar singular-post-input-filter-functions nil
1256  "Functions to call after input is sent to process.
1257These functions get two arguments BEG and END.
1258If `singular-input-filter' has been called with a string as argument
1259BEG and END gives the position of this string after insertion into the
1260buffer.
1261If `singular-input-filter' has been called with a position as argument
1262BEG and END equal process mark and that position, resp.
1263The functions may assume that no narrowing is in effect and may change
1264point at will.
1265
1266This hook is buffer-local.")
1267
1268(defvar singular-current-input-section-start nil
1269  "Marker to the start of the current input section.
1270This marker points nowhere on startup or if there is no current input
1271section.
1272
1273This variable is buffer-local.")
1274
1275(defvar singular-last-input-section-start nil
1276  "Marker to the start of the last input section.
1277This marker points nowhere on startup.
1278
1279This variable is buffer-local.")
1280
1281(defun singular-input-filter-init (pos)
1282  "Initialize all variables concerning input.
1283POS is the position of the process mark."
1284  ;; localize variables not yet localized in `singular-interactive-mode'
1285  (make-local-variable 'singular-current-input-section-start)
1286  (make-local-variable 'singular-last-input-section-start)
1287
1288  ;; initialize markers
1289  (if (not (markerp singular-current-input-section-start))
1290      (setq singular-current-input-section-start (make-marker)))
1291  (if (not (markerp singular-last-input-section-start))
1292      (setq singular-last-input-section-start (make-marker))))
1293
1294(defun singular-send-string (process string)
1295  "Send newline terminated STRING to to process PROCESS.
1296Runs the hooks on `singular-pre-input-filter-functions' in the buffer
1297associated to PROCESS.  The functions get the non-terminated string."
1298  (let ((process-buffer (process-buffer process)))
1299
1300    ;; check whether buffer is still alive
1301    (if (and process-buffer (buffer-name process-buffer))
1302        (save-excursion
1303          (set-buffer process-buffer)
1304          (send-string
1305           process
1306           (concat (singular-run-hook-with-arg-and-value
1307                    singular-pre-input-filter-functions string)
1308                   "\n"))))))
1309
1310(defun singular-input-filter (process string-or-pos)
1311  "Insert/update input from user in buffer associated to PROCESS.
1312Inserts STRING-OR-POS followed by a newline at process mark if it is a
1313string.
1314Assumes that the input is already inserted and that it is placed
1315between process mark and STRING-OR-POS if the latter is a position.
1316Inserts a newline after STRING-OR-POS.
1317
1318Takes care off:
1319- current buffer as well as point and restriction in buffer associated
1320  with process, even against non-local exits.
1321Updates:
1322- process mark;
1323- current and last sections;
1324- simple sections;
1325- mode line.
1326
1327Runs the hooks on `singular-pre-input-filter-functions' and
1328`singular-post-input-filter-functions'.
1329
1330For a more detailed descriptions of the input filter, the markers it
1331sets, and input filter functions refer to the section \"Some lengthy
1332notes on input and output\" in singular.el."
1333  (let ((process-buffer (process-buffer process)))
1334
1335    ;; check whether buffer is still alive
1336    (if (and process-buffer (buffer-name process-buffer))
1337        (let ((old-buffer (current-buffer))
1338              (old-pmark (marker-position (process-mark process)))
1339              old-point old-point-min old-point-max)
1340          (unwind-protect
1341              (let (simple-sec-start)
1342                (set-buffer process-buffer)
1343                ;; the following lines are not protected since the
1344                ;; unwind-forms refer the variables being set here
1345                (setq old-point (point-marker)
1346                      old-point-min (point-min-marker)
1347                      old-point-max (point-max-marker)
1348
1349                ;; get end of last simple section (equals start of
1350                ;; current)
1351                      simple-sec-start (marker-position singular-simple-sec-last-end))
1352
1353                ;; prepare for insertion
1354                (widen)
1355                (set-marker-insertion-type old-point t)
1356                (set-marker-insertion-type old-point-max t)
1357
1358                ;; insert string at process mark and advance process
1359                ;; mark after insertion.  If it not a string simply
1360                ;; jump to desired position and insrt a newline.
1361                (if (stringp string-or-pos)
1362                    (progn
1363                      (goto-char old-pmark)
1364                      (insert string-or-pos))
1365                  (goto-char string-or-pos))
1366                (insert ?\n)
1367                (set-marker (process-mark process) (point))
1368
1369                ;; create new simple section and update section markers
1370                (cond
1371                 ((eq (singular-simple-sec-create 'input (point)) 'empty)
1372                  nil)
1373                 ;; a new simple section has been created ...
1374                 ((null (marker-position singular-current-input-section-start))
1375                  ;; ... and even a new input section has been created!
1376                  (set-marker singular-current-input-section-start
1377                              simple-sec-start)
1378                  (set-marker singular-last-output-section-start
1379                              singular-current-output-section-start)
1380                  (set-marker singular-current-output-section-start nil)))
1381
1382                ;; run post-output hooks and force mode-line update
1383                (run-hook-with-args 'singular-post-input-filter-functions
1384                                    old-pmark (point)))
1385
1386            ;; restore buffer, restrictions and point
1387            (narrow-to-region old-point-min old-point-max)
1388            (set-marker old-point-min nil)
1389            (set-marker old-point-max nil)
1390            (goto-char old-point)
1391            (set-marker old-point nil)
1392            (set-buffer old-buffer))))))
1393           
1394(defun singular-get-old-input (get-section)
1395  "Retrieve old input.
1396Retrivies from beginning of current section to point if GET-SECTION is
1397non-nil, otherwise on a per-line base."
1398  (if get-section
1399      ;; get input from input section
1400      (let ((section (singular-section-at (point))))
1401        (if (eq (singular-section-type section) 'input)
1402            (setq old-input (singular-input-section-to-string section (point)))
1403          (error "Not on an input section")))
1404    ;; get input from line
1405    (save-excursion
1406      (beginning-of-line)
1407      (singular-skip-prompt-forward)
1408      (let ((old-point (point)))
1409        (end-of-line)
1410        (buffer-substring old-point (point))))))
1411
1412(defun singular-send-or-copy-input (send-full-section)
1413  "Send input from current buffer to associated process.
1414NOT READY[old input copying, demo mode,
1415          eol-on-send, history, SEND-FULL-SECTION]!"
1416  (interactive "P")
1417
1418  (let ((process (get-buffer-process (current-buffer)))
1419        pmark)
1420    ;; some checks and initializations
1421    (or process (error "Current buffer has no process"))
1422    (setq pmark (marker-position (process-mark process)))
1423
1424    (cond
1425     (;; check for demo mode and show next chunk if necessary
1426      (and singular-demo-mode
1427          (eq (point) pmark)
1428          (eq pmark (point-max)))
1429      (singular-demo-show-next-chunk))
1430
1431     (;; get old input
1432      (< (point) pmark)
1433      (let ((old-input (singular-get-old-input send-full-section)))
1434        (goto-char pmark)
1435        (insert old-input)))
1436
1437     (;; send input from pmark to point after doing history expansion
1438      t
1439      ;; go to desired position
1440      (if comint-eol-on-send (end-of-line))
1441      (if send-full-section (goto-char (point-max)))
1442
1443      ;; do history expansion
1444      (if (eq comint-input-autoexpand 'input)
1445          (comint-replace-by-expanded-history t))
1446      (let* ((input (buffer-substring pmark (point))))
1447
1448        ;; insert input into history
1449        (if (and (funcall comint-input-filter input)
1450                 (or (null comint-input-ignoredups)
1451                     (not (ring-p comint-input-ring))
1452                     (ring-empty-p comint-input-ring)
1453                     (not (string-equal (ring-ref comint-input-ring 0) input))))
1454            (ring-insert comint-input-ring input))
1455        (setq comint-input-ring-index nil)
1456
1457        ;; send string to process ...
1458        (singular-send-string process input)
1459        ;; ... and insert it into buffer ...
1460        (singular-input-filter process (point)))))))
1461;;}}}
1462
1463;;{{{ Receiving output
1464(defvar singular-pre-output-filter-functions nil
1465  "Functions to call before output is inserted into the buffer.
1466These functions get one argument, a string containing the text sent
1467from process.  The functions should return either nil or a string.
1468In the latter case the returned string replaces the string sent from
1469process.
1470
1471This is a buffer-local variable, not a buffer-local hook!
1472
1473`singular-run-hook-with-arg-and-value' is used to run the functions in
1474this list.")
1475
1476(defvar singular-post-output-filter-functions nil
1477  "Functions to call after output is inserted into the buffer.
1478These functions get three arguments BEG, END, and SIMPLE-SEC-START.
1479The region between BEG and END is what has been inserted into the
1480buffer.
1481SIMPLE-SEC-START is the start of the simple section which has been
1482created on insertion or nil if no simple section has been created.
1483The functions may assume that no narrowing is in effect and may change
1484point at will.
1485
1486This hook is buffer-local.")
1487
1488(defvar singular-current-output-section-start nil
1489  "Marker to the start of the current output section.
1490This marker points nowhere on startup or if there is no current output
1491section.
1492
1493This variable is buffer-local.")
1494
1495(defvar singular-last-output-section-start nil
1496  "Marker to the start of the last output section.
1497This marker points nowhere on startup.
1498
1499This variable is buffer-local.")
1500
1501(defun singular-output-filter-init (pos)
1502  "Initialize all variables concerning output including process mark.
1503Set process mark to POS."
1504
1505  ;; localize variables not yet localized in `singular-interactive-mode'
1506  (make-local-variable 'singular-current-output-section-start)
1507  (make-local-variable 'singular-last-output-section-start)
1508
1509  ;; initialize markers
1510  (if (not (markerp singular-current-output-section-start))
1511      (setq singular-current-output-section-start (make-marker)))
1512  (if (not (markerp singular-last-output-section-start))
1513      (setq singular-last-output-section-start (make-marker)))
1514  (set-marker (singular-process-mark) pos))
1515
1516(defun singular-output-filter (process string)
1517  "Insert STRING containing output from PROCESS into its associated buffer.
1518Takes care off:
1519- current buffer as well as point and restriction in buffer associated
1520  with process, even against non-local exits.
1521Updates:
1522- process mark;
1523- current and last sections;
1524- simple sections;
1525- mode line.
1526Runs the hooks on `singular-pre-output-filter-functions' and
1527`singular-post-output-filter-functions'.
1528
1529For a more detailed descriptions of the output filter, the markers it
1530sets, and output filter functions refer to the section \"Some lengthy
1531notes on input and output\" in singular.el."
1532  (let ((process-buffer (process-buffer process)))
1533
1534    ;; check whether buffer is still alive
1535    (if (and process-buffer (buffer-name process-buffer))
1536        (let ((old-buffer (current-buffer))
1537              (old-pmark (marker-position (process-mark process)))
1538              old-point old-point-min old-point-max)
1539          (unwind-protect
1540              (let (simple-sec-start)
1541                (set-buffer process-buffer)
1542                ;; the following lines are not protected since the
1543                ;; unwind-forms refer the variables being set here
1544                (setq old-point (point-marker)
1545                      old-point-min (point-min-marker)
1546                      old-point-max (point-max-marker)
1547
1548                ;; get end of last simple section (equals start of
1549                ;; current)
1550                      simple-sec-start (marker-position singular-simple-sec-last-end)
1551
1552                ;; get string to insert
1553                      string (singular-run-hook-with-arg-and-value
1554                              singular-pre-output-filter-functions
1555                              string))
1556
1557                ;; prepare for insertion
1558                (widen)
1559                (set-marker-insertion-type old-point t)
1560                (set-marker-insertion-type old-point-max t)
1561
1562                ;; insert string at process mark and advance process
1563                ;; mark after insertion
1564                (goto-char old-pmark)
1565                (insert string)
1566                (set-marker (process-mark process) (point))
1567
1568                ;; create new simple section and update section markers
1569                (cond
1570                 ((eq (singular-simple-sec-create 'output (point)) 'empty)
1571                  (setq simple-sec-start nil))
1572                 ;; a new simple section has been created ...
1573                 ((null (marker-position singular-current-output-section-start))
1574                  ;; ... and even a new output section has been created!
1575                  (set-marker singular-current-output-section-start
1576                              simple-sec-start)
1577                  (set-marker singular-last-input-section-start
1578                              singular-current-input-section-start)
1579                  (set-marker singular-current-input-section-start nil)))
1580
1581                ;; run post-output hooks and force mode-line update
1582                (run-hook-with-args 'singular-post-output-filter-functions
1583                                    old-pmark (point) simple-sec-start)
1584                (force-mode-line-update))
1585
1586            ;; restore buffer, restrictions and point
1587            (narrow-to-region old-point-min old-point-max)
1588            (set-marker old-point-min nil)
1589            (set-marker old-point-max nil)
1590            (goto-char old-point)
1591            (set-marker old-point nil)
1592            (set-buffer old-buffer))))))
1593;;}}}
1594
1595;;{{{ Singular interactive mode
1596(defun singular-interactive-mode ()
1597  "Major mode for interacting with Singular.
1598
1599NOT READY [how to send input]!
1600
1601NOT READY [multiple Singulars]!
1602
1603\\{singular-interactive-mode-map}
1604Customization: Entry to this mode runs the hooks on `comint-mode-hook'
1605and `singular-interactive-mode-hook' \(in that order).  Before each
1606input, the hooks on `comint-input-filter-functions' are run.  After
1607each Singular output, the hooks on `comint-output-filter-functions'
1608are run.
1609
1610NOT READY [much more to come.  See shell.el.]!"
1611  (interactive)
1612
1613  ;; run comint mode and do basic mode setup
1614  (comint-mode)
1615  (setq major-mode 'singular-interactive-mode)
1616  (setq mode-name "Singular Interaction")
1617  (use-local-map singular-interactive-mode-map)
1618  (set-syntax-table singular-interactive-mode-syntax-table)
1619  (if singular-interactive-mode-menu
1620      ()
1621    (easy-menu-define singular-interactive-mode-menu 
1622                      singular-interactive-mode-map ""
1623                      singular-interactive-mode-menu-1))
1624  (easy-menu-add singular-interactive-mode-menu)
1625  (setq comment-start "// ")
1626  (setq comment-start-skip "// *")
1627  (setq comment-end "")
1628
1629  ;; customize comint for Singular
1630  (setq comint-prompt-regexp singular-prompt-regexp)
1631  (setq comint-delimiter-argument-list singular-delimiter-argument-list)
1632  (setq comint-input-ignoredups singular-input-ignoredups)
1633  (make-local-variable 'comint-buffer-maximum-size)
1634  (setq comint-buffer-maximum-size singular-buffer-maximum-size)
1635  (setq comint-input-ring-size singular-input-ring-size)
1636  (setq comint-input-filter singular-history-filter)
1637
1638  ;; get name of history file (if any)
1639  (setq comint-input-ring-file-name (getenv "SINGULARHIST"))
1640  (if (or (not comint-input-ring-file-name)
1641          (equal comint-input-ring-file-name "")
1642          (equal (file-truename comint-input-ring-file-name) "/dev/null"))
1643      (setq comint-input-ring-file-name nil))
1644
1645  ;; initialize singular demo mode, input and output filters
1646  (singular-demo-mode 'init)
1647  (make-local-variable 'singular-pre-input-filter-functions)
1648  (make-local-hook 'singular-post-input-filter-functions)
1649  (make-local-variable 'singular-pre-output-filter-functions)
1650  (make-local-hook 'singular-post-output-filter-functions)
1651
1652  ;; selective display
1653  (setq selective-display t)
1654  (setq selective-display-ellipses t)
1655  (cond
1656   ;; Emacs
1657   ((eq singular-emacs-flavor 'emacs)
1658    (setq buffer-display-table (or (copy-sequence standard-display-table)
1659                                   (make-display-table)))
1660    (set-display-table-slot buffer-display-table
1661     'selective-display (vconcat singular-folding-ellipsis)))
1662    ;; XEmacs
1663   (t
1664    (set-glyph-image invisible-text-glyph singular-folding-ellipsis (current-buffer))))
1665
1666  ;; debugging filters
1667  (singular-debug 'interactive-filter
1668                  (add-hook 'singular-pre-input-filter-functions
1669                            'singular-debug-pre-input-filter nil t))
1670  (singular-debug 'interactive-filter
1671                  (add-hook 'singular-post-input-filter-functions
1672                            'singular-debug-post-input-filter nil t))
1673  (singular-debug 'interactive-filter
1674                  (add-hook 'singular-pre-output-filter-functions
1675                            'singular-debug-pre-output-filter nil t))
1676  (singular-debug 'interactive-filter
1677                  (add-hook 'singular-post-output-filter-functions
1678                            'singular-debug-post-output-filter nil t))
1679
1680  ;; other input or output filters
1681  (add-hook 'singular-post-output-filter-functions
1682            'singular-remove-prompt-filter nil t)
1683
1684  ;; font-locking
1685  (cond
1686   ;; Emacs
1687   ((eq singular-emacs-flavor 'emacs)
1688    (make-local-variable 'font-lock-defaults)
1689    (singular-debug 'interactive (message "Setting up font-lock for emacs"))
1690    (setq font-lock-defaults singular-font-lock-defaults)))
1691
1692  (run-hooks 'singular-interactive-mode-hook))
1693;;}}}
1694
1695;;{{{ Starting singular
1696(defvar singular-start-file "~/.emacs_singularrc"
1697  "Name of start-up file to pass to Singular.
1698If the file named by this variable exists it is given as initial input
1699to any Singular process being started.  Note that this may lose due to
1700a timing error if Singular discards input when it starts up.")
1701
1702(defvar singular-default-executable "Singular"
1703  "Default name of Singular executable.
1704Used by `singular' when new Singular processes are started.")
1705
1706(defvar singular-default-name "singular"
1707  "Default process name for Singular process.
1708Used by `singular' when new Singular processes are started.")
1709
1710(defvar singular-default-switches '("-t")
1711  "Default switches for Singular processes.
1712Used by `singular' when new Singular processes are started.")
1713
1714(defun singular-exit-sentinel (process message)
1715 "Clean up after termination of Singular.
1716Writes back input ring after regular termination of Singular if
1717process buffer is still alive."
1718  (save-excursion
1719    (singular-debug 'interactive
1720                    (message "Sentinel: %s" (substring message 0 -1)))
1721    ;; exit demo mode if necessary
1722    (singular-demo-mode 'exit)
1723    (if (string-match "finished\\|exited" message)
1724        (let ((process-buffer (process-buffer process)))
1725          (if (and process-buffer
1726                   (buffer-name process-buffer)
1727                   (set-buffer process-buffer))
1728              (progn
1729                (singular-debug 'interactive (message "Writing input ring back"))
1730                (comint-write-input-ring)))))))
1731
1732(defun singular-exec (buffer name executable start-file switches)
1733  "Start a new Singular process NAME in BUFFER, running EXECUTABLE.
1734EXECUTABLE should be a string denoting an executable program.
1735SWITCHES should be a list of strings that are passed as command line
1736switches.  START-FILE should be the name of a file which contents is
1737sent to the process.
1738
1739Deletes any old processes running in that buffer.
1740Moves point to the end of BUFFER.
1741Initializes all important markers and the simple sections.
1742Runs `comint-exec-hook' and `singular-exec-hook' (in that order).
1743Returns BUFFER."
1744  (let ((old-buffer (current-buffer)))
1745    (unwind-protect
1746        (progn
1747          (set-buffer buffer)
1748
1749          ;; delete any old processes
1750          (let ((process (get-buffer-process buffer)))
1751            (if process (delete-process process)))
1752
1753          ;; create new process
1754          (singular-debug 'interactive (message "Starting new Singular"))
1755          (let ((process (comint-exec-1 name buffer executable switches)))
1756
1757            ;; set process filter and sentinel
1758            (set-process-filter process 'singular-output-filter)
1759            (set-process-sentinel process 'singular-exit-sentinel)
1760            (make-local-variable 'comint-ptyp)
1761            (setq comint-ptyp process-connection-type) ; T if pty, NIL if pipe.
1762
1763            ;; go to the end of the buffer, initialize I/O and simple
1764            ;; sections
1765            (goto-char (point-max))
1766            (singular-input-filter-init (point))
1767            (singular-output-filter-init (point))
1768            (singular-simple-sec-init (point))
1769
1770            ;; feed process with start file and read input ring.  Take
1771            ;; care about the undo information.
1772            (if start-file
1773                (let ((buffer-undo-list t) start-string)
1774                  (singular-debug 'interactive (message "Feeding start file"))
1775                  (sleep-for 1)                 ; try to avoid timing errors
1776                  (insert-file-contents start-file)
1777                  (setq start-string (buffer-substring (point) (point-max)))
1778                  (delete-region (point) (point-max))
1779                  (send-string process start-string)))
1780            (singular-debug 'interactive (message "Reading input ring"))
1781            (comint-read-input-ring t)
1782
1783            ;; execute hooks
1784            (run-hooks 'comint-exec-hook)
1785            (run-hooks 'singular-exec-hook))
1786         
1787          buffer)
1788      ;; this code is unwide-protected
1789      (set-buffer old-buffer))))
1790
1791;; Note:
1792;;
1793;; In contrast to shell.el, `singular' does not run
1794;; `singular-interactive-mode' every time a new Singular process is
1795;; started, but only when a new buffer is created.  This behaviour seems
1796;; more intuitive w.r.t. local variables and hooks.
1797
1798(defun singular (&optional executable name switches)
1799  "Run an inferior Singular process, with I/O through an Emacs buffer.
1800
1801NOT READY [arguments, default values, and interactive use]!
1802
1803If buffer exists but Singular is not running, starts new Singular.
1804If buffer exists and Singular is running, just switches to buffer.
1805If a file `~/.emacs_singularrc' exists, it is given as initial input.
1806Note that this may lose due to a timing error if Singular discards
1807input when it starts up.
1808
1809If a new buffer is created it is put in Singular interactive mode,
1810giving commands for sending input and handling ouput of Singular.  See
1811`singular-interactive-mode'.
1812
1813Every time `singular' starts a new Singular process it runs the hooks
1814on `comint-exec-hook' and `singular-exec-hook' \(in that order).
1815
1816Type \\[describe-mode] in the Singular buffer for a list of commands."
1817  ;; handle interactive calls
1818  (interactive (list singular-default-executable
1819                     singular-default-name
1820                     singular-default-switches))
1821
1822  (let* (;; get default values for optional arguments
1823         (executable (or executable singular-default-executable))
1824         (name (or name singular-default-name))
1825         (switches (or switches singular-default-switches))
1826
1827         (buffer-name (singular-process-name-to-buffer-name name))
1828         ;; buffer associated with Singular, nil if there is none
1829         (buffer (get-buffer buffer-name)))
1830
1831    (if (not buffer)
1832        (progn
1833          ;; create new buffer and call `singular-interactive-mode'
1834          (singular-debug 'interactive (message "Creating new buffer"))
1835          (setq buffer (get-buffer-create buffer-name))
1836          (set-buffer buffer)
1837          (singular-debug 'interactive (message "Calling `singular-interactive-mode'"))
1838          (singular-interactive-mode)))
1839
1840    (if (not (comint-check-proc buffer))
1841        ;; create new process if there is none
1842        (singular-exec buffer name executable
1843                       (if (file-exists-p singular-start-file)
1844                           singular-start-file)
1845                       switches))
1846
1847    ;; pop to buffer
1848    (singular-debug 'interactive (message "Calling `pop-to-buffer'"))
1849    (pop-to-buffer buffer)))
1850
1851;; for convenience only
1852(defalias 'Singular 'singular)
1853;;}}}
1854;;}}}
1855
1856(provide 'singular)
1857
1858;;; singular.el ends here.
Note: See TracBrowser for help on using the repository browser.