source: git/emacs/singular.el @ e5e79a

spielwiese
Last change on this file since e5e79a was e5e79a, checked in by Tim Wichmann <wichmann@…>, 25 years ago
1999-08-11 T. Wichmann <wichmann@arboretum.itwm.uni-kl.de> * singular.el: code for menu support rewritten scanning of singular header implemented dynamic completion implemented git-svn-id: file:///usr/local/Singular/svn/trunk@3421 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100644
File size: 136.3 KB
Line 
1;;; singular.el --- Emacs support for Computer Algebra System Singular
2
3;; $Id: singular.el,v 1.31 1999-08-11 12:51:59 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;; - When referring to the Singular interactive mode, do it in that
18;;   wording.  Use the notation `singular-interactive-mode' only when
19;;   really referring to the lisp object.
20;; - use a `fill-column' of 75 for doc strings and comments
21;; - mark incomplete doc strings or code with `NOT READY' optionally
22;;   followed by an explanation what exactly is missing
23;;
24;; - use foldings to structure the source code but try not to exceed a
25;;   maximum depth of two foldings
26;; - use lowercase folding titles except for first word
27;; - folding-marks are `;;{{{' and `;;}}}' resp., for sake of standard
28;;   conformity
29;; - use the foldings to modularize code.  That is, each folding should be,
30;;   as far as possible, self-content.  Define a function `singular-*-init'
31;;   in the folding to do the initialization of the module contained in
32;;   that folding.  Call that function from `singular-interactive-mode',
33;;   for example, instead of initializing the module directly from
34;;   `singular-interactive-mode'.  Look at the code how it is done for the
35;;   simple section or for the folding stuff.
36;;
37;; - use `singular' as prefix for all global symbols
38;; - use `singular-debug' as prefix for all global symbols concerning
39;;   debugging.
40;; - use, whenever possible without names becoming too clumsy, some unique
41;;   prefix inside a folding
42;;
43;; - mark dependencies on Emacs flavor/version with a comment of the form
44;;   `;; Emacs[ <version> ]'     resp.
45;;   `;; XEmacs[ <version> ][ <nasty comment> ]'
46;;   specified in that order, if possible
47;; - use a `cond' statement to execute Emacs flavor/version-dependent code,
48;;   not `if'.  This is to make such checks more extensible.
49;; - try to define different functions for different flavors/version and
50;;   use `singular-fset' at library-loading time to set the function you
51;;   really need.  If the function is named `singular-<basename>', the
52;;   flavor/version-dependent functions should be named
53;;   `singular-<flavor>[-<version>]-<basename>'.
54;;
55;; - use `singular-debug' for debugging output/actions
56;; - to switch between buffer and process names, use the functions
57;;   `singular-process-name-to-buffer-name' and
58;;   `singular-buffer-name-to-process-name'
59;; - call the function `singular-keep-region-active' as last statement in
60;;   an interactive function that should keep the region active (for
61;;   example, in functions that move the point).  This is necessary to keep
62;;   XEmacs' zmacs regions active.
63;; - to get the process of the current buffer, use `singular-process'.  To
64;;   get the current process mark, use `singular-process-mark'.  Both
65;;   functions check whether Singular is alive and throw an error if not,
66;;   so you do not have to care about that yourself.  If you do not want an
67;;   error specify non-nil argument NO-ERROR.  But use them anyway.
68;; - we assume that the buffer is *not* read-only
69
70;;}}}
71
72;;{{{ Code common to both modes
73;;{{{ Customizing
74(defgroup singular-faces nil
75  "Faces in Singular mode and Singular interactive mode."
76  :group 'faces
77  :group 'singular-interactive)
78;;}}}
79
80;;{{{ Debugging stuff
81(defvar singular-debug nil
82  "List of modes to debug or t to debug all modes.
83Currently, the following modes are supported:
84  `interactive',
85  `interactive-filter'.")
86
87(defun singular-debug-format (string)
88  "Return STRING in a nicer format."
89  (save-match-data
90    (while (string-match "\n" string)
91      (setq string (replace-match "^J" nil nil string)))
92
93    (if (> (length string) 16)
94        (concat "<" (substring string 0 7) ">...<" (substring string -8) ">")
95      (concat "<" string ">"))))
96
97(defmacro singular-debug (mode form &optional else-form)
98  "Major debugging hook for singular.el.
99Evaluates FORM if `singular-debug' equals t or if MODE is an element
100of `singular-debug', othwerwise ELSE-FORM."
101  `(if (or (eq singular-debug t)
102           (memq ,mode singular-debug))
103       ,form
104     ,else-form))
105;;}}}
106
107;;{{{ Determining version
108(defvar singular-emacs-flavor nil
109  "A symbol describing the current Emacs.
110Currently, only Emacs \(`emacs') and XEmacs \(`xemacs') are supported.")
111
112(defvar singular-emacs-major-version nil
113  "An integer describing the major version of the current emacs.")
114
115(defvar singular-emacs-minor-version nil
116  "An integer describing the minor version of the current emacs.")
117
118(defun singular-fset (real-function emacs-function xemacs-function)
119  "Set REAL-FUNCTION to one of the functions, in dependency on Emacs flavor and version.
120Sets REAL-FUNCTION to XEMACS-FUNCTION if `singular-emacs-flavor' is
121`xemacs', otherwise sets REAL-FUNCTION to EMACS-FUNCTION.
122
123This is not as common as it would be desirable.  But it is sufficient so
124far."
125  (cond
126   ;; XEmacs
127   ((eq singular-emacs-flavor 'xemacs)
128    (fset real-function xemacs-function))
129   ;; Emacs
130   (t
131    (fset real-function emacs-function))))
132
133(defun singular-set-version ()
134  "Determine flavor, major version, and minor version of current emacs.
135singular.el is guaranteed to run on Emacs 20.3 and XEmacs 20.3.
136It should run on newer version and on slightly older ones, too.
137
138This function is called exactly once when singular.el is loaded."
139  ;; get major and minor versions first
140  (if (and (boundp 'emacs-major-version)
141           (boundp 'emacs-minor-version))
142      (setq singular-emacs-major-version emacs-major-version
143            singular-emacs-minor-version emacs-minor-version)
144    (with-output-to-temp-buffer "*singular warnings*"
145      (princ
146"You seem to have quite an old Emacs or XEmacs version.  Some of the
147features from singular.el will not work properly.  Consider upgrading to a
148more recent version of Emacs or XEmacs.  singular.el is guaranteed to run
149on Emacs 20.3 and XEmacs 20.3."))
150    ;; assume the oldest version we support
151    (setq singular-emacs-major-version 20
152          singular-emacs-minor-version 3))
153
154  ;; get flavor
155  (if (string-match "XEmacs\\|Lucid" emacs-version)
156      (setq singular-emacs-flavor 'xemacs)
157    (setq singular-emacs-flavor 'emacs)))
158
159(singular-set-version)
160;;}}}
161
162;;{{{ Syntax table
163(defvar singular-mode-syntax-table nil
164  "Syntax table for `singular-interactive-mode' resp. `singular-mode'.")
165
166(if singular-mode-syntax-table
167    ()
168  (setq singular-mode-syntax-table (make-syntax-table))
169  ;; stolen from cc-mode.el except for back-tics which are special to Singular
170  (modify-syntax-entry ?_  "_"          singular-mode-syntax-table)
171  (modify-syntax-entry ?\\ "\\"         singular-mode-syntax-table)
172  (modify-syntax-entry ?+  "."          singular-mode-syntax-table)
173  (modify-syntax-entry ?-  "."          singular-mode-syntax-table)
174  (modify-syntax-entry ?=  "."          singular-mode-syntax-table)
175  (modify-syntax-entry ?%  "."          singular-mode-syntax-table)
176  (modify-syntax-entry ?<  "."          singular-mode-syntax-table)
177  (modify-syntax-entry ?>  "."          singular-mode-syntax-table)
178  (modify-syntax-entry ?&  "."          singular-mode-syntax-table)
179  (modify-syntax-entry ?|  "."          singular-mode-syntax-table)
180  (modify-syntax-entry ?\' "\""         singular-mode-syntax-table)
181  (modify-syntax-entry ?\` "\""         singular-mode-syntax-table)
182  ;; block and line-oriented comments
183  (cond
184   ;; Emacs
185   ((eq singular-emacs-flavor 'emacs)
186    (modify-syntax-entry ?/  ". 124b"   singular-mode-syntax-table)
187    (modify-syntax-entry ?*  ". 23"     singular-mode-syntax-table))
188   ;; XEmacs
189   (t
190    (modify-syntax-entry ?/  ". 1456"   singular-mode-syntax-table)
191    (modify-syntax-entry ?*  ". 23"     singular-mode-syntax-table)))
192  (modify-syntax-entry ?\n "> b"        singular-mode-syntax-table)
193  (modify-syntax-entry ?\^m "> b"       singular-mode-syntax-table))
194
195(defun singular-mode-syntax-table-init ()
196  "Initialize syntax table of current buffer.
197
198This function is called at mode initialization time."
199  (set-syntax-table singular-mode-syntax-table))
200;;}}}
201
202;;{{{ Miscellaneous
203(defsubst singular-keep-region-active ()
204  "Do whatever is necessary to keep the region active in XEmacs.
205Ignore byte-compiler warnings you might see.  This is not needed for
206Emacs."
207  ;; XEmacs.  We do not use the standard way here to test for flavor
208  ;; because it is presumably faster with that test on `boundp'.
209  (and (boundp 'zmacs-region-stays)
210       (setq zmacs-region-stays t)))
211;;}}}
212;;}}}
213
214;;{{{ Singular interactive mode
215;;{{{ Customizing
216
217;; Note:
218;;
219;; Some notes on Customize:
220;;
221;; - The documentation states that for the `:initialize' option of
222;;   `defcustom' the default value is `custom-initialize-set'.  However, in
223;;   the source code of Customize `custom-initialize-reset' is used.  So
224;;   better always specify the `:initialize' option explicitly.
225;; - Customize is bad at setting buffer-local variables or properties.
226;;   This is quite natural since Customize itself uses its own buffer.  So
227;;   changing buffer-local variables and properties with Customize is
228;;   possible only at a "Singular-global" level.  That is, for all buffers
229;;   currently having Singular interactive mode as major mode.  The function
230;;   `singular-map-buffer' helps to do such customization.
231;;
232;; Some common customizing patterns:
233;;
234;; - How to customize buffer-local properties?
235;;   First, the `defcustom' itself must not set anything buffer-local since
236;;   at time of its definition (most likely) no Singular buffers will be
237;;   around.  If there are Singular buffers we do not care about them.  But
238;;   anyhow, at definition of the `defcustom' the global default has to be
239;;   set.  Hence, the `:initialize' option should be set to
240;;   `custom-initialize-default'.
241;;   The buffer-local initialization has to be done at mode initialization
242;;   time.  The global default value should then be used to set the local
243;;   properties.
244;;   At last, the function specified with the `:set' option should set the
245;;   local properties in all Singular buffers to the new, customized value.
246;;   Most likely, the function `singular-map-buffer' may be used for that.
247;;   In addition, the function should, of course, set the global value via
248;;   `set-default'.
249;;   For an example, see `singular-folding-line-move-ignore-folding'.
250;;
251;; - How to encapsulate other mode's global variables into Singular
252;;   interactive mode variables?
253;;   Set them always.  That is, set them if the `defcustom' is evaluated
254;;   (use `custom-initialize-reset' as `:initial' function) and set them
255;;   when the Singular interactive mode variable is customized (by means
256;;   of an appropriate `:set' function).
257;;   For an example, see `singular-section-face-alist' (which does not
258;;   encapsulate another mode's variable, but Singular interactive mode's
259;;   own variable `singular-simple-sec-clear-type').
260
261(defgroup singular-interactive nil
262  "Running Singular with Emacs or XEmacs as front end."
263  :group 'processes)
264
265(defgroup singular-sections-and-foldings nil
266  "Sections and foldings in Singular interactive mode."
267  :group 'singular-interactive)
268
269(defgroup singular-interactive-miscellaneous nil
270  "Miscellaneous settings for Singular interactive mode."
271  :group 'singular-interactive)
272
273(defgroup singular-demo-mode nil
274  "Settings concerning Singular demo mode."
275  :group 'singular-interactive)
276
277(defun singular-map-buffer (func &rest args)
278  "Apply FUNC to ARGS in all existing Singular buffers.
279That is, in all buffers having Singular interactive major mode.  The
280function is executed in the context of the buffer.  This is a must-have for
281the customizing stuff to change buffer-local properties."
282  (save-excursion
283    (mapcar (function
284             (lambda (buffer)
285               (set-buffer buffer)
286               (if (eq major-mode 'singular-interactive-mode)
287                   (apply func args))))
288            (buffer-list))))
289;;}}}
290
291;;{{{ Comint
292
293;; Note:
294;;
295;; We require Comint, but we really do not use it too much.  One may argue
296;; that this is bad since Comint is a standardized way to communicate with
297;; external processes.  One may argue further that many experienced Emacs
298;; users are forced now to re-do their Comint customization for Singular
299;; interactive mode.  However, we believe that the intersection between
300;; experienced Emacs users and users of Singular interactive mode is almost
301;; empty.
302;;
303;; In fact, we used Comint really much in the beginning of this project.
304;; Later during development it turned at that using Comint's input and
305;; output processing is to inflexible and not appropriate for Singular
306;; interactive mode with its input and output sections.  So we begun to
307;; rewrite large portions of Comint to adapt it to our needs.  At some
308;; point it came clear that it would be best to throw out Comint
309;; alltogether, would not have been there some auxilliary functions which
310;; are really useful but annoying to rewrite.  These are, for example, the
311;; command line history functions or the completion stuff offered by
312;; Comint.
313;;
314;; Our policy with regard to these remainders of Comint is: Use the
315;; functions to bind them to keys, but do not use them internally.
316;; Encapsulate Comint customization into Singular interactive mode
317;; customization.  In particular, do not take care about Comint settings
318;; which already may be present, overwrite them.  Hide Comint from the
319;; user.
320;;
321;; Here is how exactly we use Comint:
322;;
323;; - All variables necessary to use Comint's input ring are properly
324;;   initialized.  One may find this in the `History' folding.
325;; - `comint-prompt-regexp' is initialized since it is used in some
326;;   of the functions regarding input ring handling.  Furthermore, its
327;;   initialization enables us to use functions as `comint-bol', etc.
328;;   Initialization is done in the `Skipping and stripping prompts ...'
329;;   folding.
330;; - We call `comint-mode' as first step in `singular-interactive-mode'.
331;;   Most of the work done there is to initialize the local variables as
332;;   necessary.  Besides that, the function does nothing that interferes
333;;   with Singular interactive mode.  To be consequent we set
334;;   `comint-mode-hook' temporarily to nil when calling `comint-mode'.
335;; - In `singular-exec', we use `comint-exec-1' to fire up the process.
336;;   Furthermore, we set `comint-ptyp' there as it is used in the signal
337;;   sending commands of Comint.  All that `comint-exec-1' does is that it
338;;   sets up the process environment (it adds or modifies the setting of
339;;   the 'TERM' variable), sets the execution directory, and does some
340;;   magic with the process coding stuff.
341;; - One more time the most important point: we do *not* use Comint's
342;;   output and input processing.  In particular, we do not run any of
343;;   Comint's hooks on input or output.  Anyway, we do better, don't we?
344
345(require 'comint)
346;;}}}
347
348;;{{{ Font-locking
349(defvar singular-font-lock-error-face 'singular-font-lock-error-face
350  "Face name to use for Singular errors.")
351
352(defvar singular-font-lock-warning-face 'singular-font-lock-warning-face
353  "Face name to use for Singular warnings.")
354
355(defvar singular-font-lock-prompt-face 'singular-font-lock-prompt-face
356  "Face name to use for Singular prompts.")
357
358(defface singular-font-lock-error-face
359  '((((class color)) (:foreground "Red" :bold t))
360    (t (:inverse-video t :bold t)))
361  "*Font Lock mode face used to highlight Singular errors."
362  :group 'singular-faces)
363
364(defface singular-font-lock-warning-face
365  '((((class color)) (:foreground "OrangeRed" :bold nil))
366    (t (:inverse-video t :bold t)))
367  "*Font Lock mode face used to highlight Singular warnings."
368  :group 'singular-faces)
369
370(defface singular-font-lock-prompt-face
371  '((((class color) (background light)) (:foreground "Blue" :bold t))
372    (((class color) (background dark)) (:foreground "LightSkyBlue" :bold t))
373    (t (:inverse-video t :bold t)))
374  "*Font Lock mode face used to highlight Singular prompts."
375  :group 'singular-faces)
376
377(defconst singular-font-lock-singular-types nil
378  "List of Singular types.")
379
380(eval-when-compile
381  (setq singular-font-lock-singular-types
382        '("def" "ideal" "int" "intmat" "intvec" "link" "list" "map" "matrix"
383          "module" "number" "poly" "proc" "qring" "resolution" "ring" "string"
384          "vector")))
385
386(defconst singular-interactive-font-lock-keywords-1
387  '(
388    ("^\\([>.]\\) " 1 singular-font-lock-prompt-face t)
389    ("^   [\\?].*" 0 singular-font-lock-error-face t)
390    ("^// \\*\\*.*" 0 singular-font-lock-warning-face t)
391    )
392  "Subdued level highlighting for Singular interactive mode")
393
394(defconst singular-interactive-font-lock-keywords-2
395  (append
396   singular-interactive-font-lock-keywords-1
397   (eval-when-compile
398     (list
399      (cons
400       (concat "\\<" (regexp-opt singular-font-lock-singular-types t) "\\>")
401       'font-lock-type-face))))
402  "Medium level highlighting for Singular interactive mode")
403
404(defconst singular-interactive-font-lock-keywords-3
405  (append
406   singular-interactive-font-lock-keywords-2
407   '(
408     ("^   [\\?].*`\\(\\sw\\sw+\\)`" 1 font-lock-reference-name-face t)
409     ))
410  "Gaudy level highlighting for Singular interactive mode.")
411
412(defconst singular-interactive-font-lock-keywords singular-interactive-font-lock-keywords-1
413  "Default highlighting for Singular interactive mode.")
414
415(defconst singular-interactive-font-lock-defaults
416  '((singular-interactive-font-lock-keywords
417     singular-interactive-font-lock-keywords-1
418     singular-interactive-font-lock-keywords-2
419     singular-interactive-font-lock-keywords-3)
420    ;; KEYWORDS-ONLY (do not fontify strings & comments if non-nil)
421    nil
422    ;; CASE-FOLD (ignore case if non-nil)
423    nil
424    ;; SYNTAX-ALIST (add this to Font Lock's syntax table)
425    ((?_ . "w"))
426    ;; SYNTAX-BEGIN
427    singular-section-goto-beginning)
428  "Default expressions to highlight in Singular interactive mode.")
429
430(defun singular-interactive-font-lock-init ()
431  "Initialize Font Lock mode for Singular interactive mode.
432
433For XEmacs, this function is called exactly once when singular.el is
434loaded.
435For Emacs, this function is called  at mode initialization time."
436  (cond 
437   ;; Emacs
438   ((eq singular-emacs-flavor 'emacs)
439    (singular-debug 'interactive (message "Setting up Font Lock mode for Emacs"))
440    (set (make-local-variable 'font-lock-defaults)
441         singular-interactive-font-lock-defaults))
442   ;; XEmacs
443   ((eq singular-emacs-flavor 'xemacs)
444    (singular-debug 'interactive (message "Setting up Font Lock mode for XEmacs"))
445    (put 'singular-interactive-mode
446         'font-lock-defaults singular-interactive-font-lock-defaults))))
447
448;; XEmacs Font Lock mode initialization
449(cond
450 ;; XEmacs
451 ((eq singular-emacs-flavor 'xemacs)
452  (singular-interactive-font-lock-init)))
453;;}}}
454
455;;{{{ Key map
456(defvar singular-interactive-mode-map nil
457  "Key map to use in Singular interactive mode.")
458
459(if singular-interactive-mode-map
460    ()
461  ;; create empty keymap first
462  (cond
463   ;; Emacs
464   ((eq singular-emacs-flavor 'emacs)
465    (setq singular-interactive-mode-map (make-sparse-keymap)))
466   ;; XEmacs
467   (t
468    (setq singular-interactive-mode-map (make-keymap))
469    (set-keymap-name singular-interactive-mode-map
470                     'singular-interactive-mode-map)))
471
472  ;; define keys
473  (define-key singular-interactive-mode-map "\t"        'singular-dynamic-complete)
474  (define-key singular-interactive-mode-map [?\C-m]     'singular-send-or-copy-input)
475  (define-key singular-interactive-mode-map [?\M-r]     'comint-previous-matching-input)
476  (define-key singular-interactive-mode-map [?\M-s]     'comint-next-matching-input)
477
478  ;; C-c prefix
479  (define-key singular-interactive-mode-map [?\C-c ?\C-f] 'singular-folding-toggle-fold-at-point-or-all)
480  (define-key singular-interactive-mode-map [?\C-c ?\C-o] 'singular-folding-toggle-fold-latest-output)
481  (define-key singular-interactive-mode-map [?\C-c ?\C-l] 'singular-recenter))
482
483(defcustom singular-history-keys '(meta)
484  "Keys to use for history access.
485Should be a list describing which keys or key combinations to use for
486history access in Singular interactive mode.  Valid entries are `control',
487`cursor', and `meta'.
488
489For more information one should refer to the documentation of
490`singular-history-keys'.
491
492Changing this variable has an immediate effect only if one uses
493\\[customize] to do so."
494  :type '(set (const :tag "Cursor keys" cursor)
495              (const :tag "C-p, C-n" control)
496              (const :tag "M-p, M-n" meta))
497  :initialize 'custom-initialize-default
498  :set (function
499        (lambda (var value)
500          (singular-history-cursor-keys-set value singular-cursor-keys)
501          (set-default var value)))
502  :group 'singular-interactive-miscellaneous)
503
504(defcustom singular-cursor-keys '(control cursor)
505  "Keys to use for cursor movement.
506Should be a list describing which keys or key combinations to use for
507cursor movement in Singular interactive mode.  Valid entries are `control',
508`cursor', and `meta'.
509
510An experienced Emacs user would prefer setting `singular-cursor-keys' to
511`(control cursor)' and `singular-history-keys' to `(meta)'.  This means
512that C-p, C-n, and the cursor keys move the cursor, whereas M-p and M-n
513scroll through the history of Singular commands.
514
515On the other hand, an user used to running Singular in a, say, xterm, would
516prefer the other way round: Setting the variable `singular-history-keys' to
517`(control cursor)' and `singular-cursor-keys' to `(meta)'.
518
519Keys which are not mentioned in both lists are not modified from their
520standard settings.  Naturally, the lists `singular-cursor-keys' and
521`singular-history-keys' should be disjunct.
522
523Changing this variable has an immediate effect only if one uses
524\\[customize] to do so."
525  :type '(set (const :tag "Cursor keys" cursor)
526              (const :tag "C-p, C-n" control)
527              (const :tag "M-p, M-n" meta))
528  :initialize 'custom-initialize-default
529  :set (function
530        (lambda (var value)
531          (singular-history-cursor-keys-set singular-history-keys value)
532          (set-default var value)))
533  :group 'singular-interactive-miscellaneous)
534
535(defun singular-history-cursor-key-set (key function-spec)
536  "Set keys corresponding to KEY and according to FUNCTION-SPEC.
537FUNCTION-SPEC should be a cons cell of the format (PREV-FUNC . NEXT-FUNC)."
538  (cond
539   ((eq key 'control)
540    (define-key singular-interactive-mode-map [?\C-p]   (car function-spec))
541    (define-key singular-interactive-mode-map [?\C-n]   (cdr function-spec)))
542   ((eq key 'meta)
543    (define-key singular-interactive-mode-map [?\M-p]   (car function-spec))
544    (define-key singular-interactive-mode-map [?\M-n]   (cdr function-spec)))
545   ((eq key 'cursor)
546    (define-key singular-interactive-mode-map [up]      (car function-spec))
547    (define-key singular-interactive-mode-map [down]    (cdr function-spec)))))
548
549(defun singular-history-cursor-keys-set (history-keys cursor-keys)
550  "Set the keys according to HISTORY-KEYS and CURSOR-KEYS.
551Checks whether HISTORY-KEYS and CURSOR-KEYS are disjunct.  Throws an error
552if not."
553  ;; do the check first
554  (if (memq nil (mapcar (function (lambda (elt) (not (memq elt history-keys))))
555                        cursor-keys))
556      (error "History keys and cursor keys are not disjunct (see `singular-cursor-keys')"))
557
558  ;; remove old bindings first
559  (singular-history-cursor-key-set 'cursor '(nil . nil))
560  (singular-history-cursor-key-set 'control '(nil . nil))
561  (singular-history-cursor-key-set 'meta '(nil . nil))
562
563  ;; set new bindings
564  (mapcar (function
565           (lambda (key)
566             (singular-history-cursor-key-set key '(comint-previous-input . comint-next-input))))
567          history-keys)
568  (mapcar (function
569           (lambda (key)
570             (singular-history-cursor-key-set key '(previous-line . next-line))))
571          cursor-keys))
572
573;; static initialization.  Deferred to this point since at the time where
574;; the defcustoms are defined not all necessary functions and variables are
575;; available.
576(singular-history-cursor-keys-set singular-history-keys singular-cursor-keys)
577
578(defun singular-interactive-mode-map-init ()
579  "Initialize key map for Singular interactive mode.
580
581This function is called  at mode initialization time."
582  (use-local-map singular-interactive-mode-map))
583;;}}}
584
585;;{{{ Menus and logos
586(defvar singular-interactive-mode-menu-1 nil
587  "NOT READY [docu]")
588
589(defvar singular-interactive-mode-menu-2 nil
590  "NOT READY [docu]")
591
592(or singular-interactive-mode-menu-1
593    (easy-menu-define singular-interactive-mode-menu-1
594                      singular-interactive-mode-map ""
595                      '("Singular"
596                        ["start default" singular t]
597                        ["start..." singular-other t]
598                        ["exit" singular-exit-singular t])))
599
600;; XEmacs does not provide the function easy-menu-add-item, so define
601;; the new function singular-menu-add-item.
602(singular-fset 'singular-menu-add-item
603               (function (lambda (map path-head path-sub element before)
604                           (easy-menu-add-item map (list path-sub) 
605                                               element before)))
606               (function (lambda (map path-head path-sub element before)
607                           (add-menu-button (list path-head path-sub)
608                                            element before))))
609
610(defun singular-menu-install-libraries ()
611  "Updates the singular command menu with libraries.
612Go through the alist `singular-completion-library-list' and for
613each entry add a new menu element in the submenu
614(\"Commands\" \"libraries\")."          ;" font-lock trick.
615  (let ((libs (sort singular-completion-library-list 
616                    (function (lambda (a b)
617                                (string< (car b) (car a))))))
618        (last "other...")
619        current)
620    (while libs
621      (setq current (car (car libs)))
622      (singular-menu-add-item singular-interactive-mode-menu-2
623                              "Commands" "libraries" 
624                              (vector current
625                                      (list 'singular-load-library current t)
626                                      t)
627                              last)
628
629      (setq last current)
630      (setq libs (cdr libs)))
631    (singular-menu-add-item singular-interactive-mode-menu-2
632                            "Commands" "libraries"
633                            "--:singleLine" "other...")))
634;     (easy-menu-add-item singular-interactive-mode-menu-2
635;                       '("libraries") "---" "other...")))
636
637(or singular-interactive-mode-menu-2
638    (easy-menu-define 
639     singular-interactive-mode-menu-2
640     singular-interactive-mode-map ""
641     (list 
642      "Commands"
643      ["load file..." singular-load-file t]
644      (list
645       "libraries"
646       ["other..." singular-load-library t])
647      "---"
648      ["load demo" singular-demo-load (not singular-demo-mode)]
649      ["exit demo" singular-demo-exit singular-demo-mode]
650      "---"
651      ["truncate lines" singular-toggle-truncate-lines
652       :style toggle :selected truncate-lines]
653      "---"
654      ["fold last output" singular-fold-last-output t]
655      ["fold all output" singular-fold-all-output t]
656      ["fold at point" singular-fold-at-point t]
657      "---"
658      ["unfold last output" singular-unfold-last-output t]
659      ["unfold all output" singular-unfold-all-output t]
660      ["unfold at point" singular-unfold-at-point t]
661      )))
662
663;; NOT READY
664;; This is just a temporary hack for XEmacs demo.
665(defvar singular-install-in-main-menu nil
666  "NOT READY [docu]")
667
668(if singular-install-in-main-menu
669    (cond
670     ;; XEmacs
671     ((eq singular-emacs-flavor 'xemacs)
672      (add-submenu nil 
673                   singular-start-menu-definition))))
674
675(defun singular-interactive-mode-menu-init ()
676  "Initialize menus for Singular interactive mode.
677
678This function is called  at mode initialization time."
679  ;; Note: easy-menu-add is not necessary in emacs, since the menu
680  ;; is added automatically with the keymap.
681  ;; See help on `easy-menu-add'
682  (easy-menu-add singular-interactive-mode-menu-1)
683  (easy-menu-add singular-interactive-mode-menu-2))
684;;}}}
685
686;;{{{ Skipping and stripping prompts and newlines and other things
687
688;; Note:
689;;
690;; All of these functions modify the match data!
691
692(defun singular-strip-white-space (string &optional trailing leading)
693  "Strip off trailing or leading white-space from STRING.
694Strips off trailing white-space if optional argument TRAILING is
695non-nil.
696Strips off leading white-space if optional argument LEADING is
697non-nil."
698  (let ((beg 0)
699        (end (length string)))
700    (and leading
701         (string-match "\\`\\s-*" string)
702         (setq beg (match-end 0)))
703    (and trailing
704         (string-match "\\s-*\\'" string beg)
705         (setq end (match-beginning 0)))
706    (substring string beg end)))
707
708(defconst singular-extended-prompt-regexp "\\([?>.] \\)"
709  "Matches one Singular prompt.
710Should not be anchored neither to start nor to end!")
711
712(defconst singular-strip-leading-prompt-regexp
713  (concat "\\`" singular-extended-prompt-regexp "+")
714  "Matches Singular prompt anchored to string start.")
715
716(defun singular-strip-leading-prompt (string)
717  "Strip leading prompts from STRING.
718May or may not return STRING or a modified copy of it."
719  (if (string-match singular-strip-leading-prompt-regexp string)
720      (substring string (match-end 0))
721    string))
722
723(defconst singular-remove-prompt-regexp
724  (concat "^" singular-extended-prompt-regexp
725          "*" singular-extended-prompt-regexp)
726  "Matches a non-empty sequence of prompts at start of a line.")
727
728(defun singular-remove-prompt (beg end)
729  "Remove all superfluous prompts from region between BEG and END.
730Removes all but the last prompt of a sequence if that sequence ends at
731END.
732The region between BEG and END should be accessible.
733Leaves point after the last prompt found."
734  (let ((end (copy-marker end))
735        prompt-end)
736    (goto-char beg)
737    (while (and (setq prompt-end
738                      (re-search-forward singular-remove-prompt-regexp end t))
739                (not (= end prompt-end)))
740      (delete-region (match-beginning 0) prompt-end))
741
742    ;; check for trailing prompt
743    (if prompt-end
744        (delete-region (match-beginning 0)  (match-beginning 2)))
745    (set-marker end nil)))
746
747(defconst singular-skip-prompt-forward-regexp
748  (concat singular-extended-prompt-regexp "*")
749  "Matches an arbitary sequence of Singular prompts.")
750
751(defun singular-prompt-skip-forward ()
752  "Skip forward over prompts."
753  (looking-at singular-skip-prompt-forward-regexp)
754  (goto-char (match-end 0)))
755
756(defun singular-skip-prompt-backward ()
757  "Skip backward over prompts."
758  (while (re-search-backward singular-extended-prompt-regexp (- (point) 2) t)))
759
760(defun singular-remove-prompt-filter (beg end simple-sec-start)
761  "Strip prompts from last simple section."
762  (if simple-sec-start (singular-remove-prompt simple-sec-start end)))
763
764(defvar singular-prompt-regexp "^> "
765  "Regexp to match prompt patterns in Singular.
766Should not match the continuation prompt \(`.'), only the regular
767prompt \(`>').
768
769This variable is used to initialize `comint-prompt-regexp' when
770Singular interactive mode starts up.")
771;;}}}
772
773;;{{{ Miscellaneous
774
775;; Note:
776;;
777;; We assume a one-to-one correspondence between Singular buffers and
778;; Singular processes.  We always have (equal buffer-name (concat "*"
779;; process-name "*")).
780
781(defsubst singular-buffer-name-to-process-name (buffer-name)
782  "Create the process name for BUFFER-NAME.
783The process name is the buffer name with surrounding `*' stripped off."
784  (substring buffer-name 1 -1))
785
786(defsubst singular-process-name-to-buffer-name (process-name)
787  "Create the buffer name for PROCESS-NAME.
788The buffer name is the process name with surrounding `*'."
789  (concat "*" process-name "*"))
790
791(defsubst singular-run-hook-with-arg-and-value (hook value)
792  "Call functions on HOOK.
793Provides argument VALUE to the functions.  If a function returns a non-nil
794value it replaces VALUE as new argument to the remaining functions.
795Returns final VALUE."
796  (while hook
797    (setq value (or (funcall (car hook) value) value)
798          hook (cdr hook)))
799  value)
800
801(defsubst singular-process (&optional no-error)
802  "Return process of current buffer.
803If no process is active this function silently returns nil if optional
804argument NO-ERROR is non-nil, otherwise it throws an error."
805  (cond ((get-buffer-process (current-buffer)))
806        (no-error nil)
807        (t (error "No Singular running in this buffer"))))
808
809(defsubst singular-process-mark (&optional no-error)
810  "Return process mark of current buffer.
811If no process is active this function silently returns nil if optional
812argument NO-ERROR is non-nil, otherwise it throws an error."
813  (let ((process (singular-process no-error)))
814    (and process
815         (process-mark process))))
816
817(defun singular-time-stamp-difference (new-time-stamp old-time-stamp)
818  "Return the number of seconds between NEW-TIME-STAMP and OLD-TIME-STAMP.
819Both NEW-TIME-STAMP and OLD-TIME-STAMP should be in the format
820that is returned, for example, by `current-time'.
821Does not return a difference larger than 2^17 seconds."
822  (let ((high-difference (min 1 (- (car new-time-stamp) (car old-time-stamp))))
823        (low-difference (- (cadr new-time-stamp) (cadr old-time-stamp))))
824    (+ (* high-difference 131072) low-difference)))
825;;}}}
826
827;;{{{ Miscellaneous interactive
828(defun singular-recenter (&optional arg)
829  "Center point in window and redisplay frame.  With ARG, put point on line ARG.
830The desired position of point is always relative to the current window.
831Just C-u as prefix means put point in the center of the window.
832If ARG is omitted or nil, erases the entire frame and then redraws with
833point in the center of the current window.
834Scrolls window to the left margin and moves point to beginning of line."
835  (interactive "P")
836  (singular-reposition-point-and-window)
837  (recenter arg))
838
839(defun singular-reposition-point-and-window ()
840  "Scroll window to the left margin and move point to beginning of line."
841  (interactive)
842  (set-window-hscroll (selected-window) 0)
843  (move-to-column 0)
844  ;; be careful where to place point
845  (singular-prompt-skip-forward))
846
847(defun singular-toggle-truncate-lines ()
848  "Toggle `truncate-lines'.
849A non-nil value of `truncate-lines' means do not display continuation
850lines\; give each line of text one screen line.
851Repositions window and point after toggling `truncate-lines'."
852  (interactive)
853  (setq truncate-lines (not truncate-lines))
854  ;; reposition so that user does not get confused
855  (singular-reposition-point-and-window))
856
857;; this is not a buffer-local variable even if at first glance it seems
858;; that it should be one.  But if one changes buffer the contents of this
859;; variable becomes irrelevant since the last command is no longer a
860;; horizontal scroll command.  The same is true for the initial value, so
861;; we set it to nil.
862(defvar singular-scroll-previous-amount nil
863  "Amount of previous horizontal scroll command.")
864
865(defun singular-scroll-right (&optional scroll-amount)
866  "Scroll selected window SCROLL-AMOUNT columns right.
867SCROLL-AMOUNT defaults to amount of previous horizontal scroll command.  If
868the command immediately preceding this command has not been a horizontal
869scroll command SCROLL-AMOUNT defaults to window width minus 2.
870Moves point to leftmost visible column."
871  (interactive "P")
872
873  ;; get amount to scroll
874  (setq singular-scroll-previous-amount
875        (cond (scroll-amount (prefix-numeric-value scroll-amount))
876              ((eq last-command 'singular-scroll-horizontal)
877               singular-scroll-previous-amount)
878              (t (- (frame-width) 2)))
879        this-command 'singular-scroll-horizontal)
880
881  ;; scroll
882  (scroll-right singular-scroll-previous-amount)
883  (move-to-column (window-hscroll))
884  ;; be careful where to place point.  But what if `(current-column)'
885  ;; equals, say, one?  Well, we simply do not care about that case.
886  ;; Should not happen to often.
887  (if (eq (current-column) 0)
888      (singular-prompt-skip-forward)))
889
890(defun singular-scroll-left (&optional scroll-amount)
891  "Scroll selected window SCROLL-AMOUNT columns left.
892SCROLL-AMOUNT defaults to amount of previous horizontal scroll command.  If
893the command immediately preceding this command has not been a horizontal
894scroll command SCROLL-AMOUNT defaults to window width minus 2.
895Moves point to leftmost visible column."
896  (interactive "P")
897
898  ;; get amount to scroll
899  (setq singular-scroll-previous-amount
900        (cond (scroll-amount (prefix-numeric-value scroll-amount))
901              ((eq last-command 'singular-scroll-horizontal)
902               singular-scroll-previous-amount)
903              (t (- (frame-width) 2)))
904        this-command 'singular-scroll-horizontal)
905
906  ;; scroll
907  (scroll-left singular-scroll-previous-amount)
908  (move-to-column (window-hscroll))
909  ;; be careful where to place point.  But what if `(current-column)'
910  ;; equals, say, one?  Well, we simply do not care about that case.
911  ;; Should not happen to often.
912  (if (eq (current-column) 0)
913      (singular-prompt-skip-forward)))
914
915(defun singular-load-file (file &optional noexpand)
916  "Read a file into Singular (via '< \"FILE\";').
917If optional argument NOEXPAND is non-nil, FILE is left as it is entered by
918the user, otherwise it is expanded using `expand-file-name'."
919  (interactive "fLoad file: ")
920  (let* ((filename (if noexpand file (expand-file-name file)))
921         (string (concat "< \"" filename "\";"))
922         (process (singular-process)))
923    (singular-input-filter process string)
924    (singular-send-string process string)))
925
926(defun singular-load-library (file &optional noexpand)
927  "Read a Singular library (via 'LIB \"FILE\";').
928If optional argument NOEXPAND is non-nil, FILE is left as it is entered by
929the user, otherwise it is expanded using `expand-file-name'."
930  (interactive "fLoad Library: ")
931  (let* ((filename (if noexpand file (expand-file-name file)))
932         (string (concat "LIB \"" filename "\";"))
933         (process (singular-process)))
934    (singular-input-filter process string)
935    (singular-send-string process string)))
936
937(defun singular-exit-singular ()
938  "Exit Singular and kill Singular buffer.
939Sends string \"quit;\" to Singular process."
940  (interactive)
941  (let ((string "quit;")
942        (process (singular-process)))
943    (singular-input-filter process string)
944    (singular-send-string process string))
945  (kill-buffer (current-buffer)))
946;;}}}
947
948;;{{{ History
949(defcustom singular-history-ignoredups t
950  "If non-nil, do not add input matching the last on the input history."
951  :type 'boolean
952  :initialize 'custom-initialize-default
953  :group 'singular-interactive-miscellaneous)
954
955;; this variable is used to set Comint's `comint-input-ring-size'
956(defcustom singular-history-size 64
957  "Size of the input history.
958
959Changing this variable has no immediate effect even if one uses
960\\[customize] to do so.  The new value will be used only in new Singular
961interactive mode buffers."
962  :type 'integer
963  :initialize 'custom-initialize-default
964  :group 'singular-interactive-miscellaneous)
965
966(defcustom singular-history-filter-regexp "\\`\\(..?\\|\\s *\\)\\'"
967  "Regular expression to filter strings *not* to insert in the input history.
968By default, input consisting of less than three characters and input
969consisting of white-space only is not inserted into the input history."
970  :type 'regexp
971  :initialize 'custom-initialize-default
972  :group 'singular-interactive-miscellaneous)
973
974(defcustom singular-history-explicit-file-name nil
975  "If non-nil, use this as file name to load and save the input history.
976If this variable equals nil, the `SINGULARHIST' environment variable is
977used to determine the file name.
978One should note that the input history is saved to file only on regular
979termination of Singular; that is, if one leaves Singular using the commands
980`quit\;' or `exit\;'."
981  :type '(choice (const nil) file)
982  :initialize 'custom-initialize-default
983  :group 'singular-interactive-miscellaneous)
984
985(defun singular-history-read ()
986  "Read the input history from file.
987If `singular-history-explicit-file-name' is non-nil, uses that as file
988name, otherwise tries environment variable `SINGULARHIST'.
989This function is called from `singular-exec' every time a new Singular
990process is started."
991  (singular-debug 'interactive (message "Reading input ring"))
992  (let ((comint-input-ring-file-name (or singular-history-explicit-file-name
993                                         (getenv "SINGULARHIST"))))
994    ;; `comint-read-input-ring' does nothing if
995    ;; `comint-input-ring-file-name' equals nil
996    (comint-read-input-ring t)))
997
998(defun singular-history-write ()
999  "Write back the input history to file.
1000If `singular-history-explicit-file-name' is non-nil, uses that as file
1001name, otherwise tries environment variable `SINGULARHIST'.
1002This function is called from `singular-exit-sentinel' every time a Singular
1003process terminates regularly."
1004  (singular-debug 'interactive (message "Writing input ring back"))
1005  (let ((comint-input-ring-file-name (or singular-history-explicit-file-name
1006                                         (getenv "SINGULARHIST"))))
1007    ;; `comint-write-input-ring' does nothing if
1008    ;; `comint-input-ring-file-name' equals nil
1009    (comint-write-input-ring)))
1010
1011(defun singular-history-insert (input)
1012  "Insert string INPUT into the input history if necessary."
1013  (if (and (not (string-match singular-history-filter-regexp input))
1014           (or singular-demo-insert-into-history
1015               (not singular-demo-mode))
1016           (or (not singular-history-ignoredups)
1017               (not (ring-p comint-input-ring))
1018               (ring-empty-p comint-input-ring)
1019               (not (string-equal (ring-ref comint-input-ring 0) input))))
1020      (ring-insert comint-input-ring input))
1021  (setq comint-input-ring-index nil))
1022
1023(defun singular-history-init ()
1024  "Initialize variables concerning the input history.
1025
1026This function is called at mode initialization time."
1027  (setq comint-input-ring-size singular-history-size))
1028;;}}}
1029
1030;;{{{ Simple section API for both Emacs and XEmacs
1031
1032;; Note:
1033;;
1034;; Sections and simple sections are used to mark Singular's input and
1035;; output for further access.  Here are some general notes on simple
1036;; sections.  Sections are explained in the respective folding.
1037;;
1038;; In general, simple sections are more or less Emacs' overlays or XEmacs
1039;; extents, resp.  But they are more than simply an interface to overlays
1040;; or extents.
1041;;
1042;; - Simple sections are non-empty portions of text.  They are interpreted
1043;;   as left-closed, right-opened intervals, i.e., the start point of a
1044;;   simple sections belongs to it whereas the end point does not.
1045;; - Simple sections start and end at line borders only.
1046;; - Simple sections do not overlap.  Thus, any point in the buffer may be
1047;;   covered by at most one simple section.
1048;; - Besides from their start and their end, simple sections have some type
1049;;   associated.
1050;; - Simple sections are realized using overlays (extents for XEmacs)
1051;;   which define the start and, end, and type (via properties) of the
1052;;   simple section.  Actually, as a lisp object a simple section is
1053;;   nothing else but the underlying overlay.
1054;; - There may be so-called clear simple sections.  Clear simple sections
1055;;   have not an underlying overlay.  Instead, they start at the end of the
1056;;   preceding non-clear simple section, end at the beginning of the next
1057;;   non-clear simple section, and have the type defined by
1058;;   `singular-simple-sec-clear-type'.  Clear simple sections are
1059;;   represented by nil.
1060;; - Buffer narrowing does not restrict the extent of completely or
1061;;   partially inaccessible simple sections.  But one should note that
1062;;   some of the functions assume that there is no narrowing in
1063;;   effect.
1064;; - After creation, simple sections are not modified any further.
1065;; - There is one nasty little corner case: what if a non-clear simple
1066;;   section spans up to end of buffer?  By definition, eob is not included
1067;;   in that section since they are right-opened intervals.  Most of the
1068;;   functions react as if there is an imagenary empty clear simple section
1069;;   at eob.
1070;; - Even though by now there are only two types of different simple
1071;;   sections there may be an arbitrary number of them.  Furthermore,
1072;;   simple sections of different types may appear in arbitrary order.
1073;;
1074;; - In `singular-interactive-mode', the whole buffer is covered with
1075;;   simple sections from the very beginning of the file up to the
1076;;   beginning of the line containing the last input or output.  The
1077;;   remaining text up to `(point-max)' may be interpreted as covered by
1078;;   one clear simple section.  Thus, it is most reasonable to define
1079;;   `input' to be the type of clear simple sections.
1080
1081(defvar singular-simple-sec-clear-type 'input
1082  "Type of clear simple sections.
1083If nil no clear simple sections are used.
1084
1085One should not set this variable directly.  Rather, one should customize
1086`singular-section-face-alist'.")
1087
1088(defvar singular-simple-sec-last-end nil
1089  "Marker at the end of the last simple section.
1090Should be initialized by `singular-simple-sec-init' before any calls to
1091`singular-simple-sec-create' are done.  Instead of accessing this variable
1092directly one should use the macro `singular-simple-sec-last-end-position'.
1093
1094This variable is buffer-local.")
1095
1096(defun singular-simple-sec-init (pos)
1097  "Initialize variables belonging to simple section management.
1098Creates the buffer-local marker `singular-simple-sec-last-end' and
1099initializes it to POS.  POS should be at beginning of a line.
1100
1101This function is called every time a new Singular session is started."
1102  (make-local-variable 'singular-simple-sec-last-end)
1103  (if (not (markerp singular-simple-sec-last-end))
1104      (setq singular-simple-sec-last-end (make-marker)))
1105  (set-marker singular-simple-sec-last-end pos))
1106
1107(defmacro singular-simple-sec-last-end-position ()
1108  "Return the marker position of `singular-simple-sec-last-end'.
1109This macro exists more or less for purposes of information hiding only."
1110  '(marker-position singular-simple-sec-last-end))
1111
1112(defsubst singular-simple-sec-lookup-face (type)
1113  "Return the face to use for simple sections of type TYPE.
1114This accesses the `singular-section-type-alist'.  It does not harm if nil
1115is associated with TYPE in that alist: In this case, this function will
1116never be called for that TYPE."
1117  (cdr (assq type singular-section-face-alist)))
1118
1119;; Note:
1120;;
1121;; The rest of the folding is either marked as
1122;; Emacs
1123;; or
1124;; XEmacs
1125
1126(singular-fset 'singular-simple-sec-create
1127               'singular-emacs-simple-sec-create
1128               'singular-xemacs-simple-sec-create)
1129
1130(singular-fset 'singular-simple-sec-at
1131               'singular-emacs-simple-sec-at
1132               'singular-xemacs-simple-sec-at)
1133
1134(singular-fset 'singular-simple-sec-start
1135               'singular-emacs-simple-sec-start
1136               'singular-xemacs-simple-sec-start)
1137
1138(singular-fset 'singular-simple-sec-end
1139               'singular-emacs-simple-sec-end
1140               'singular-xemacs-simple-sec-end)
1141
1142(singular-fset 'singular-simple-sec-type
1143               'singular-emacs-simple-sec-type
1144               'singular-xemacs-simple-sec-type)
1145
1146(singular-fset 'singular-simple-sec-before
1147               'singular-emacs-simple-sec-before
1148               'singular-xemacs-simple-sec-before)
1149
1150(singular-fset 'singular-simple-sec-start-at
1151               'singular-emacs-simple-sec-start-at
1152               'singular-xemacs-simple-sec-start-at)
1153
1154(singular-fset 'singular-simple-sec-end-at
1155               'singular-emacs-simple-sec-end-at
1156               'singular-xemacs-simple-sec-end-at)
1157
1158(singular-fset 'singular-simple-sec-in
1159               'singular-emacs-simple-sec-in
1160               'singular-xemacs-simple-sec-in)
1161;;}}}
1162
1163;;{{{ Simple section API for Emacs
1164(defsubst singular-emacs-simple-sec-start (simple-sec)
1165  "Return start of non-clear simple section SIMPLE-SEC.
1166Narrowing has no effect on this function."
1167  (overlay-start simple-sec))
1168
1169(defsubst singular-emacs-simple-sec-end (simple-sec)
1170  "Return end of non-clear simple section SIMPLE-SEC.
1171Narrowing has no effect on this function."
1172  (overlay-end simple-sec))
1173
1174(defsubst singular-emacs-simple-sec-type (simple-sec)
1175  "Return type of SIMPLE-SEC.
1176Returns nil if SIMPLE-SEC happens to be an overlay but not a simple
1177section.
1178Narrowing has no effect on this function."
1179  (if simple-sec
1180      (overlay-get simple-sec 'singular-type)
1181    singular-simple-sec-clear-type))
1182
1183(defsubst singular-emacs-simple-sec-before (pos)
1184  "Return simple section before buffer position POS.
1185This is the same as `singular-simple-sec-at' except if POS falls on a
1186section border.  In this case `singular-simple-section-before' returns the
1187previous simple section instead of the current one.  If POS falls on
1188beginning of buffer, the simple section at beginning of buffer is returned.
1189Narrowing has no effect on this function."
1190  (singular-emacs-simple-sec-at (max 1 (1- pos))))
1191
1192(defun singular-emacs-simple-sec-create (type end)
1193  "Create a new simple section of type TYPE.
1194Creates the section from end of previous simple section up to the first
1195beginning of line before END.  That position should be larger than or equal
1196to `singular-simple-sec-last-end'.  Updates `singular-simple-sec-last-end'.
1197Returns the new simple section or `empty' if no simple section has been
1198created.
1199Assumes that no narrowing is in effect."
1200  (let ((last-end (singular-simple-sec-last-end-position))
1201        ;; `simple-sec' is the new simple section or `empty'
1202        simple-sec)
1203
1204    ;; get beginning of line before END.  At this point we need that there
1205    ;; are no restrictions.
1206    (setq end (let ((old-point (point)))
1207                (goto-char end) (beginning-of-line)
1208                (prog1 (point) (goto-char old-point))))
1209
1210    (cond
1211     ;; do not create empty sections
1212     ((eq end last-end)
1213      'empty)
1214     ;; non-clear simple sections
1215     ((not (eq type singular-simple-sec-clear-type))
1216      ;; if type has not changed we only have to extend the previous simple
1217      ;; section.  If `last-end' happens to be 1 (meaning that we are
1218      ;; creating the first non-clear simple section in the buffer), then
1219      ;; `singular-simple-sec-before' returns nil,
1220      ;; `singular-simple-sec-type' returns the type of clear simple
1221      ;; sections that definitely does not equal TYPE, and a new simple
1222      ;; section is created as necessary.
1223      (setq simple-sec (singular-emacs-simple-sec-before last-end))
1224      (if (eq type (singular-emacs-simple-sec-type simple-sec))
1225          ;; move existing overlay
1226          (setq simple-sec (move-overlay simple-sec (overlay-start simple-sec) end))
1227        ;; create new overlay
1228        (setq simple-sec (make-overlay last-end end))
1229        ;; set type property
1230        (overlay-put simple-sec 'singular-type type)
1231        ;; set face
1232        (overlay-put simple-sec 'face (singular-simple-sec-lookup-face type))
1233        ;; evaporate empty sections
1234        (overlay-put simple-sec 'evaporate t))
1235      ;; update `singular-simple-sec-last-end' and return new simple
1236      ;; section
1237      (set-marker singular-simple-sec-last-end end)
1238      simple-sec)
1239     ;; clear simple sections
1240     (t
1241      ;; update `singular-simple-sec-last-end' and return nil
1242      (set-marker singular-simple-sec-last-end end)
1243      nil))))
1244
1245(defun singular-emacs-simple-sec-start-at (pos)
1246  "Return start of clear simple section at position POS.
1247Assumes the existence of an imagenary empty clear simple section if POS is
1248at end of buffer and there is non-clear simple section immediately ending
1249at POS.
1250Assumes that no narrowing is in effect (since `previous-overlay-change'
1251imlicitly does so)."
1252  ;; yes, this `(1+ pos)' is OK at eob for
1253  ;; `singular-emacs-simple-sec-before' as well as
1254  ;; `previous-overlay-change'
1255  (let ((previous-overlay-change-pos (1+ pos)))
1256    ;; this `while' loop at last will run into the end of the next
1257    ;; non-clear simple section or stop at bob.  Since POS may be right at
1258    ;; the end of a previous non-clear location, we have to search at least
1259    ;; one time from POS+1 backwards.
1260    (while (not (or (singular-emacs-simple-sec-before previous-overlay-change-pos)
1261                    (eq previous-overlay-change-pos 1)))
1262      (setq previous-overlay-change-pos
1263            (previous-overlay-change previous-overlay-change-pos)))
1264    previous-overlay-change-pos))
1265
1266(defun singular-emacs-simple-sec-end-at (pos)
1267  "Return end of clear simple section at position POS.
1268Assumes the existence of an imagenary empty clear simple section if POS is
1269at end of buffer and there is non-clear simple section immediately ending
1270at POS.
1271Assumes that no narrowing is in effect (since `next-overlay-change'
1272imlicitly does so)."
1273  (let ((next-overlay-change-pos (next-overlay-change pos)))
1274    ;; this `while' loop at last will run into the beginning of the next
1275    ;; non-clear simple section or stop at eob.  Since POS may not be at
1276    ;; the beginning of a non-clear simple section we may start searching
1277    ;; immediately.
1278    (while (not (or (singular-emacs-simple-sec-at next-overlay-change-pos)
1279                    (eq next-overlay-change-pos (point-max))))
1280      (setq next-overlay-change-pos
1281            (next-overlay-change next-overlay-change-pos)))
1282    next-overlay-change-pos))
1283
1284(defun singular-emacs-simple-sec-at (pos)
1285  "Return simple section at buffer position POS.
1286Assumes the existence of an imagenary empty clear simple section if POS is
1287at end of buffer and there is non-clear simple section immediately ending
1288at POS.
1289Narrowing has no effect on this function."
1290  ;; at eob, `overlays-at' always returns nil so everything is OK for this
1291  ;; case, too
1292  (let ((overlays (overlays-at pos)) simple-sec)
1293    ;; be careful, there may be other overlays!
1294    (while (and overlays (not simple-sec))
1295      (if (singular-emacs-simple-sec-type (car overlays))
1296          (setq simple-sec (car overlays)))
1297      (setq overlays (cdr overlays)))
1298    simple-sec))
1299
1300(defun singular-emacs-simple-sec-in (beg end)
1301  "Return a list of all simple sections intersecting with the region from BEG to END.
1302A simple section intersects the region if the section and the region have
1303at least one character in common.  The sections are returned with
1304startpoints in increasing order and clear simple sections (that is, nil's)
1305inserted as necessary.  BEG is assumed to be less than or equal to END.
1306The imagenary empty clear simple section at end of buffer is never included
1307in the result.
1308Narrowing has no effect on this function."
1309  (let (overlays overlay-cursor)
1310    (if (= beg end)
1311        ;; `overlays-in' seems not be correct with respect to this case
1312        nil
1313      ;; go to END since chances are good that the overlays come in correct
1314      ;; order, then
1315      (setq overlays (let ((old-point (point)))
1316                       (goto-char end)
1317                       (prog1 (overlays-in beg end)
1318                         (goto-char old-point)))
1319
1320      ;; now, turn overlays that are not simple sections into nils
1321            overlays (mapcar (function
1322                              (lambda (overlay)
1323                                (and (singular-emacs-simple-sec-type overlay)
1324                                     overlay)))
1325                             overlays)
1326      ;; then, remove nils from list
1327            overlays (delq nil overlays)
1328      ;; now, we have to sort the list since documentation of `overlays-in'
1329      ;; does not state anything about the order the overlays are returned in
1330            overlays
1331            (sort overlays
1332                  (function
1333                   (lambda (a b)
1334                     (< (overlay-start a) (overlay-start b))))))
1335
1336      ;; at last, we have the list of non-clear simple sections.  Now, go and
1337      ;; insert clear simple sections as necessary.
1338      (if (null overlays)
1339          ;; if there are no non-clear simple sections at all there can be
1340          ;; only one large clear simple section
1341          '(nil)
1342        ;; we care about inside clear simple section first
1343        (setq overlay-cursor overlays)
1344        (while (cdr overlay-cursor)
1345          (if (eq (overlay-end (car overlay-cursor))
1346                  (overlay-start (cadr overlay-cursor)))
1347              (setq overlay-cursor (cdr overlay-cursor))
1348            ;; insert nil
1349            (setcdr overlay-cursor
1350                    (cons nil (cdr overlay-cursor)))
1351            (setq overlay-cursor (cddr overlay-cursor))))
1352        ;; now, check BEG and END for clear simple sections
1353        (if (> (overlay-start (car overlays)) beg)
1354            (setq overlays (cons nil overlays)))
1355        ;; `overlay-cursor' still points to the end
1356        (if (< (overlay-end (car overlay-cursor)) end)
1357            (setcdr overlay-cursor (cons nil nil)))
1358        overlays))))
1359;;}}}
1360
1361;;{{{ Simple section API for XEmacs
1362(defsubst singular-xemacs-simple-sec-start (simple-sec)
1363  "Return start of non-clear simple section SIMPLE-SEC.
1364Narrowing has no effect on this function."
1365  (extent-start-position simple-sec))
1366
1367(defsubst singular-xemacs-simple-sec-end (simple-sec)
1368  "Return end of non-clear simple section SIMPLE-SEC.
1369Narrowing has no effect on this function."
1370  (extent-end-position simple-sec))
1371
1372(defsubst singular-xemacs-simple-sec-type (simple-sec)
1373  "Return type of SIMPLE-SEC.
1374Returns nil if SIMPLE-SEC happens to be an extent but not a simple
1375section.
1376Narrowing has no effect on this function."
1377  (if simple-sec
1378      (extent-property simple-sec 'singular-type)
1379    singular-simple-sec-clear-type))
1380
1381(defsubst singular-xemacs-simple-sec-before (pos)
1382  "Return simple section before buffer position POS.
1383This is the same as `singular-simple-sec-at' except if POS falls on a
1384section border.  In this case `singular-simple-section-before' returns the
1385previous simple section instead of the current one.  If POS falls on
1386beginning of buffer, the simple section at beginning of buffer is returned.
1387Narrowing has no effect on this function."
1388  (singular-xemacs-simple-sec-at (max 1 (1- pos))))
1389
1390(defun singular-xemacs-simple-sec-create (type end)
1391  "Create a new simple section of type TYPE.
1392Creates the section from end of previous simple section up to the first
1393beginning of line before END.  That position should be larger than or equal
1394to `singular-simple-sec-last-end'.  Updates `singular-simple-sec-last-end'.
1395Returns the new simple section or `empty' if no simple section has been
1396created.
1397Assumes that no narrowing is in effect."
1398  (let ((last-end (singular-simple-sec-last-end-position))
1399        ;; `simple-sec' is the new simple section or `empty'
1400        simple-sec)
1401
1402    ;; get beginning of line before END.  At this point we need that there
1403    ;; are no restrictions.
1404    (setq end (let ((old-point (point)))
1405                (goto-char end) (beginning-of-line)
1406                (prog1 (point) (goto-char old-point))))
1407
1408    (cond
1409     ;; do not create empty sections
1410     ((eq end last-end)
1411      'empty)
1412     ;; non-clear simple sections
1413     ((not (eq type singular-simple-sec-clear-type))
1414      ;; if type has not changed we only have to extend the previous simple
1415      ;; section.  If `last-end' happens to be 1 (meaning that we are
1416      ;; creating the first non-clear simple section in the buffer), then
1417      ;; `singular-simple-sec-before' returns nil,
1418      ;; `singular-simple-sec-type' returns the type of clear simple
1419      ;; sections that definitely does not equal TYPE, and a new simple
1420      ;; section is created as necessary.
1421      (setq simple-sec (singular-xemacs-simple-sec-before last-end))
1422      (if (eq type (singular-xemacs-simple-sec-type simple-sec))
1423          ;; move existing extent
1424          (setq simple-sec (set-extent-endpoints simple-sec 
1425                                                 (extent-start-position simple-sec) end))
1426        ;; create new extent
1427        (setq simple-sec (make-extent last-end end))
1428        ;; set type property
1429        (set-extent-property simple-sec 'singular-type type)
1430        ;; set face.  In contrast to Emacs, we do not need to set somethin
1431        ;; like `evaporate'.  `detachable' is set by XEmacs by default.
1432        (set-extent-property simple-sec 'face (singular-simple-sec-lookup-face type)))
1433      ;; update `singular-simple-sec-last-end' and return new simple
1434      ;; section
1435      (set-marker singular-simple-sec-last-end end)
1436      simple-sec)
1437     ;; clear simple sections
1438     (t
1439      ;; update `singular-simple-sec-last-end' and return nil
1440      (set-marker singular-simple-sec-last-end end)
1441      nil))))
1442
1443(defun singular-xemacs-simple-sec-start-at (pos)
1444  "Return start of clear simple section at position POS.
1445Assumes the existence of an imagenary empty clear simple section if POS is
1446at end of buffer and there is non-clear simple section immediately ending
1447at POS.
1448Assumes that no narrowing is in effect (since `previous-extent-change'
1449imlicitly does so)."
1450  ;; get into some hairy details at end of buffer.  Look if there is a
1451  ;; non-clear simple section immediately ending at end of buffer and
1452  ;; return the start of the imagenary empty clear simple section in that
1453  ;; case.  If buffer is empty this test fails since
1454  ;; `singular-xemacs-simple-sec-before' (corretly) returns nil.  But in
1455  ;; that case the following loop returns the correct result.
1456  (if (and (eq pos (point-max))
1457           (singular-xemacs-simple-sec-before pos))
1458      pos
1459    (let ((previous-extent-change-pos (min (1+ pos) (point-max))))
1460      ;; this `while' loop at last will run into the end of the next
1461      ;; non-clear simple section or stop at bob.  Since POS may be right at
1462      ;; the end of a previous non-clear location, we have to search at least
1463      ;; one time from POS+1 backwards.
1464      (while (not (or (singular-xemacs-simple-sec-before previous-extent-change-pos)
1465                      (eq previous-extent-change-pos 1)))
1466        (setq previous-extent-change-pos
1467              (previous-extent-change previous-extent-change-pos)))
1468      previous-extent-change-pos)))
1469
1470(defun singular-xemacs-simple-sec-end-at (pos)
1471  "Return end of clear simple section at position POS.
1472Assumes the existence of an imagenary empty clear simple section if POS is
1473at end of buffer and there is non-clear simple section immediately ending
1474at POS.
1475Assumes that no narrowing is in effect (since `next-extent-change'
1476imlicitly does so)."
1477  (let ((next-extent-change-pos (next-extent-change pos)))
1478    ;; this `while' loop at last will run into the beginning of the next
1479    ;; non-clear simple section or stop at eob.  Since POS may not be at
1480    ;; the beginning of a non-clear simple section we may start searching
1481    ;; immediately.
1482    (while (not (or (singular-xemacs-simple-sec-at next-extent-change-pos)
1483                    (eq next-extent-change-pos (point-max))))
1484      (setq next-extent-change-pos
1485            (next-extent-change next-extent-change-pos)))
1486    next-extent-change-pos))
1487
1488(defun singular-xemacs-simple-sec-at (pos)
1489  "Return simple section at buffer position POS.
1490Assumes the existence of an imagenary empty clear simple section if POS is
1491at end of buffer and there is non-clear simple section immediately ending
1492at POS.
1493Narrowing has no effect on this function."
1494  ;; at eob, `map-extent' always returns nil so everything is OK for this
1495  ;; case, too.  Do not try to use `extent-at' at this point.  `extent-at'
1496  ;; does not return extents outside narrowed text.
1497  (map-extents (function (lambda (ext args) ext))
1498               nil pos pos nil nil 'singular-type))
1499
1500(defun singular-xemacs-simple-sec-in (beg end)
1501  "Return a list of all simple sections intersecting with the region from BEG to END.
1502A simple section intersects the region if the section and the region have
1503at least one character in common.  The sections are returned with
1504startpoints in increasing order and clear simple sections (that is, nil's)
1505inserted as necessary.  BEG is assumed to be less than or equal to END.
1506The imagenary empty clear simple section at end of buffer is never included
1507in the result.
1508Narrowing has no effect on this function."
1509  (let (extents extent-cursor)
1510    (if (= beg end)
1511        ;; `mapcar-extents' may return some extents in this case, so
1512        ;; exclude it
1513        nil
1514      ;; OK, that's a little bit easier than for Emacs ...
1515      (setq extents (mapcar-extents 'identity nil nil beg end nil 'singular-type))
1516
1517      ;; now we have the list of non-clear simple sections.  Go and
1518      ;; insert clear simple sections as necessary.
1519      (if (null extents)
1520          ;; if there are no non-clear simple sections at all there can be
1521          ;; only one large clear simple section
1522          '(nil)
1523        ;; we care about inside clear simple section first
1524        (setq extent-cursor extents)
1525        (while (cdr extent-cursor)
1526          (if (eq (extent-end-position (car extent-cursor))
1527                  (extent-start-position (cadr extent-cursor)))
1528              (setq extent-cursor (cdr extent-cursor))
1529            ;; insert nil
1530            (setcdr extent-cursor
1531                    (cons nil (cdr extent-cursor)))
1532            (setq extent-cursor (cddr extent-cursor))))
1533        ;; now, check BEG and END for clear simple sections
1534        (if (> (extent-start-position (car extents)) beg)
1535            (setq extents (cons nil extents)))
1536        ;; `extent-cursor' still points to the end
1537        (if (< (extent-end-position (car extent-cursor)) end)
1538            (setcdr extent-cursor (cons nil nil)))
1539        extents))))
1540;;}}}
1541
1542;;{{{ Section API
1543
1544;; Note:
1545;;
1546;; Sections are built on simple sections.  Their purpose is to cover the
1547;; difference between clear and non-clear simple sections.
1548;;
1549;; - Sections consist of a simple section, its type, and its start and end
1550;;   points.  This is redundant information only in the case of non-clear
1551;;   simple section.
1552;; - Sections are read-only objects, neither are they modified nor are they
1553;;   created.
1554;; - Buffer narrowing does not restrict the extent of completely or
1555;;   partially inaccessible sections.  In contrast to simple sections the
1556;;   functions concerning sections do not assume that there is no narrowing
1557;;   in effect.  However, most functions provide an optional argument
1558;;   RESTRICTED that restricts the start and end point of the returned
1559;;   sections to the currently active restrictions.  Of course, that does
1560;;   not affect the range of the underlying simple sections, only the
1561;;   additional start and end points being returned.  One should note that
1562;;   by restricting sections one may get empty sections, that is, sections
1563;;   for which the additional start and end point are equal.
1564;; - Sections are independent from implementation dependencies.  There are
1565;;   no different versions of the functions for Emacs and XEmacs.
1566;; - Whenever possible, one should not access simple section directly.
1567;;   Instead, one should use the section API.
1568
1569(defcustom singular-section-face-alist '((input . nil)
1570                                         (output . singular-section-output-face))
1571  "*Alist that maps section types to faces.
1572Should be a list consisting of elements (SECTION-TYPE . FACE-OR-NIL), where
1573SECTION-TYPE is either `input' or `output'.
1574
1575At any time, the Singular interactive mode buffer is completely covered by
1576sections of two different types: input sections and output sections.  This
1577variable determines which faces are used to display the different sections.
1578
1579If for type SECTION-TYPE the value FACE-OR-NIL is a face it is used to
1580display the contents of all sections of that particular type.
1581If instead FACE-OR-NIL equals nil sections of that type become so-called
1582clear sections.  The content of clear sections is displayed as regular
1583text, with no faces at all attached to them.
1584
1585Some notes and restrictions on this variable (believe them or not):
1586o Changing this variable during a Singular session may cause unexpected
1587  results (but not too serious ones, though).
1588o There may be only one clear section type defined at a time.
1589o Choosing clear input sections is a good idea.
1590o Choosing clear output sections is a bad idea.
1591o Consequence: Not to change this variable is a good idea."
1592  ;; to add new section types, simply extend the `list' widget.
1593  ;; The rest should work unchanged.  Do not forget to update docu.
1594  :type '(list (cons :tag "Input sections"
1595                     (const :format "" input)
1596                     (choice :format
1597"Choose either clear or non-clear input sections.  For non-clear sections,
1598select or modify a face (preferably `singular-section-input-face') used to
1599display the sections.
1600%[Choice%]
1601%v
1602"
1603                             (const :tag "Clear sections" nil)
1604                             (face :tag "Non-clear sections")))
1605               (cons :tag "Output sections"
1606                     (const :format "" output)
1607                     (choice :format
1608"Choose either clear or non-clear ouput sections.  For non-clear sections,
1609select or modify a face (preferably `singular-section-output-face') used to
1610display the sections.
1611%[Choice%]
1612%v
1613"
1614                             (const :tag "Clear sections" nil)
1615                             (face :tag "Non-clear sections"))))
1616  :initialize 'custom-initialize-reset
1617  ;; this function checks for validity (only one clear section
1618  ;; type) and sets `singular-simple-sec-clear-type' accordingly.
1619  ;; In case of an error, nothing is set or modified.
1620  :set (function (lambda (var value)
1621                   (let* ((cdrs-with-nils (mapcar 'cdr value))
1622                          (cdrs-without-nils (delq nil (copy-sequence cdrs-with-nils))))
1623                     (if (> (- (length cdrs-with-nils) (length cdrs-without-nils)) 1)
1624                         (error "Only one clear section type allowed (see `singular-section-face-alist')")
1625                       (set-default var value)
1626                       (setq singular-simple-sec-clear-type (car (rassq nil value)))))))
1627  :group 'singular-faces
1628  :group 'singular-sections-and-foldings)
1629
1630(defface singular-section-input-face '((t nil))
1631  "*Face to use for input sections.
1632It may be not sufficient to modify this face to change the appearance of
1633input sections.  See `singular-section-face-alist' for more information."
1634  :group 'singular-faces
1635  :group 'singular-sections-and-foldings)
1636
1637(defface singular-section-output-face '((t (:bold t)))
1638  "*Face to use for output sections.
1639It may be not sufficient to modify this face to change the appearance of
1640output sections.  See `singular-section-face-alist' for more information."
1641  :group 'singular-faces
1642  :group 'singular-sections-and-foldings)
1643
1644(defsubst singular-section-create (simple-sec type start end)
1645  "Create and return a new section."
1646  (vector simple-sec type start end))
1647
1648(defsubst singular-section-simple-sec (section)
1649  "Return underlying simple section of SECTION."
1650  (aref section 0))
1651
1652(defsubst singular-section-type (section)
1653  "Return type of SECTION."
1654  (aref section 1))
1655
1656(defsubst singular-section-start (section)
1657  "Return start of SECTION."
1658  (aref section 2))
1659
1660(defsubst singular-section-end (section)
1661  "Return end of SECTION."
1662  (aref section 3))
1663
1664(defun singular-section-at (pos &optional restricted)
1665  "Return section at position POS.
1666Returns section intersected with current restriction if RESTRICTED is
1667non-nil."
1668  (let* ((simple-sec (singular-simple-sec-at pos))
1669         (type (singular-simple-sec-type simple-sec))
1670         start end)
1671    (if simple-sec
1672        (setq start (singular-simple-sec-start simple-sec)
1673              end  (singular-simple-sec-end simple-sec))
1674      (save-restriction
1675        (widen)
1676        (setq start (singular-simple-sec-start-at pos)
1677              end (singular-simple-sec-end-at pos))))
1678    (cond
1679     ;; not restricted first
1680     ((not restricted)
1681      (singular-section-create simple-sec type start end))
1682     ;; restricted and degenerated
1683     ((and restricted
1684           (< end (point-min)))
1685      (singular-section-create simple-sec type (point-min) (point-min)))
1686     ;; restricted and degenerated
1687     ((and restricted
1688           (> start (point-max)))
1689      (singular-section-create simple-sec type (point-max) (point-max)))
1690     ;; restricted but not degenrated
1691     (t
1692      (singular-section-create simple-sec type
1693                               (max start (point-min))
1694                               (min end (point-max)))))))
1695
1696(defun singular-section-before (pos &optional restricted)
1697  "Return section before position POS.
1698This is the same as `singular-section-at' except if POS falls on a section
1699border.  In this case `singular-section-before' returns the previous
1700section instead of the current one.  If POS falls on beginning of buffer,
1701the section at beginning of buffer is returned.
1702Returns section intersected with current restriction if RESTRICTED is
1703non-nil."
1704  (singular-section-at (max 1 (1- pos)) restricted))
1705
1706(defun singular-section-in (beg end &optional restricted)
1707  "Return a list of all sections intersecting with the region from BEG to END.
1708A section intersects with the region if the section and the region have at
1709least one character in common.  The sections are returned in increasing
1710order.
1711If optional argument RESTRICTED is non-nil only sections which are
1712completely in the intersection of the region and the current restriction
1713are returned."
1714  ;; exchange BEG and END if necessary as a special service to our users
1715  (let* ((reg-beg (min beg end))
1716         (reg-end (max beg end))
1717         ;; we need these since we widen the buffer later on
1718         (point-min (point-min))
1719         (point-max (point-max))
1720         simple-sections)
1721    (if (and restricted
1722             (or (> reg-beg point-max) (< reg-end point-min)))
1723        ;; degenerate restrictions
1724        nil
1725      ;; do the intersection if necessary and get simple sections
1726      (setq reg-beg (if restricted (max reg-beg point-min) reg-beg)
1727            reg-end (if restricted (min reg-end point-max) reg-end)
1728            simple-sections (singular-simple-sec-in reg-beg reg-end))
1729      ;; we still have REG-BEG <= REG-END in any case.  SIMPLE-SECTIONS
1730      ;; contains the list of simple sections intersecting with the region
1731      ;; from REG-BEG and REG-END.
1732
1733      (if (null simple-sections)
1734          nil
1735        ;; and here we even have REG-BEG < REG-END
1736        (save-restriction
1737          (widen)
1738          ;; get sections intersecting with the region from REG-BEG to
1739          ;; REG-END
1740          (let* ((sections (singular-section-in-internal simple-sections
1741                                                         reg-beg reg-end))
1742                 first-section-start last-section-end)
1743            (if (not restricted)
1744                sections
1745              (setq first-section-start (singular-section-start (car sections))
1746                    last-section-end (singular-section-end (car (last sections))))
1747              ;; popping off first element is easy ...
1748              (if (< first-section-start point-min)
1749                  (setq sections (cdr sections)))
1750              ;; ... but last element is harder to pop off
1751              (cond
1752               (;; no elements left
1753                (null sections)
1754                nil)
1755               (;; one element left
1756                (null (cdr sections))
1757                (if (> last-section-end point-max)
1758                    nil
1759                  sections))
1760               (;; more than one element left
1761                t
1762                (if (> last-section-end point-max)
1763                    (setcdr (last sections 2) nil))
1764                sections)))))))))
1765
1766(defun singular-section-in-internal (simple-sections reg-beg reg-end)
1767  "Create a list of sections from SIMPLE-SECTIONS.
1768This is the back-end for `singular-section-in'.
1769First simple section should be such that it contains REG-BEG, last simple
1770section should be such that it contains or ends at REG-END.  These
1771arguments are used to find the start resp. end of clear simple sections of
1772terminal clear simple sections in SIMPLE-SECTIONS.
1773Assumes that REG-BEG < REG-END.
1774Assumes that SIMPLE-SECTIONS is not empty.
1775Assumes that no narrowing is in effect."
1776  (let* (;; we pop off the extra nil at the end of the loop
1777         (sections (cons nil nil))
1778         (sections-end sections)
1779         (simple-section (car simple-sections))
1780         type start end)
1781
1782    ;; first, get unrestricted start
1783    (setq start (if simple-section
1784                    (singular-simple-sec-start simple-section)
1785                  ;; here we need that no narrowing is in effect
1786                  (singular-simple-sec-start-at reg-beg)))
1787
1788    ;; loop through all simple sections but last
1789    (while (cdr simple-sections)
1790      (setq simple-section (car simple-sections)
1791            type (singular-simple-sec-type simple-section)
1792            end (if simple-section
1793                    (singular-simple-sec-end simple-section)
1794                  (singular-simple-sec-start (cadr simple-sections)))
1795
1796            ;; append the new section to `sections-end'
1797            sections-end
1798            (setcdr sections-end
1799                    (cons (singular-section-create simple-section type start end) nil))
1800
1801            ;; get next simple section and its start
1802            simple-sections (cdr simple-sections)
1803            start end))
1804
1805    ;; care about last simple section
1806    (setq simple-section (car simple-sections)
1807          type (singular-simple-sec-type simple-section)
1808          end (if simple-section
1809                  (singular-simple-sec-end simple-section)
1810                ;; the `1-' is OK since REG-BEG < REG-END.
1811                ;; here we need that no narrowing is in effect
1812                (singular-simple-sec-end-at (1- reg-end))))
1813    (setcdr sections-end
1814            (cons (singular-section-create simple-section type start end) nil))
1815
1816    ;; we should not forget to pop off our auxilliary cons-cell
1817    (cdr sections)))
1818
1819(defun singular-section-mapsection (func sections &optional type-filter negate-filter)
1820  "Apply FUNC to each section in SECTIONS, and make a list of the results.
1821If optional argument TYPE-FILTER is non-nil it should be a list of section
1822types.  FUNC is then applied only to those sections with type occuring in
1823TYPE-FILTER.  If in addition optional argument NEGATE-FILTER is non-nil
1824FUNC is applied only to those sections with type not occuring in
1825TYPE-FILTER.
1826
1827In any case the length of the list this function returns equals the
1828number of sections actually processed."
1829  (if (not type-filter)
1830      (mapcar func sections)
1831    ;; copy the list first
1832    (let ((sections (copy-sequence sections)))
1833      ;; filter elements and turn them to t's
1834      (setq sections
1835            (mapcar (function
1836                     (lambda (section)
1837                       ;; that strange expression evaluates to t iff the
1838                       ;; section should be removed.  The `not' is to
1839                       ;; canonize boolean values to t or nil, resp.
1840                       (or (eq (not (memq (singular-section-type section) type-filter))
1841                               (not negate-filter))
1842                           section)))
1843                    sections)
1844
1845      ;; remove t's now
1846            sections (delq t sections))
1847
1848      ;; call function for remaining sections
1849      (mapcar func sections))))
1850;;}}}
1851
1852;;{{{ Section miscellaneous
1853(defun singular-input-section-to-string (section &optional end raw)
1854  "Get content of input section SECTION as string.
1855Returns text between start of SECTION and END if optional argument END is
1856non-nil, otherwise text between start and end of SECTION.  END should be a
1857position inside SECTION.
1858Strips leading prompts and trailing white space unless optional argument
1859RAW is non-nil."
1860  (save-restriction
1861    (widen)
1862    (let ((string (buffer-substring (singular-section-start section)
1863                                    (or end (singular-section-end section)))))
1864      (if raw
1865          string
1866        (singular-strip-leading-prompt (singular-strip-white-space string t))))))
1867;;}}}
1868
1869;;{{{ Section miscellaneous interactive
1870(defun singular-section-goto-beginning ()
1871  "Move point to beginning of current section."
1872  (interactive)
1873  (goto-char (singular-section-start (singular-section-at (point))))
1874  (singular-keep-region-active))
1875
1876(defun singular-section-goto-end ()
1877  "Move point to end of current section."
1878  (interactive)
1879  (goto-char (singular-section-end (singular-section-at (point))))
1880  (singular-keep-region-active))
1881
1882(defun singular-section-backward (n)
1883  "Move backward until encountering the beginning of a section.
1884With argument, do this that many times.  With N less than zero, call
1885`singular-section-forward' with argument -N."
1886  (interactive "p")
1887  (while (> n 0)
1888    (goto-char (singular-section-start (singular-section-before (point))))
1889    (setq n (1- n)))
1890  (if (< n 0)
1891      (singular-section-forward (- n))
1892    (singular-keep-region-active)))
1893
1894(defun singular-section-forward (n)
1895  "Move forward until encountering the end of a section.
1896With argument, do this that many times.  With N less than zero, call
1897`singular-section-backward' with argument -N."
1898  (interactive "p")
1899  (while (> n 0)
1900    (goto-char (singular-section-end (singular-section-at (point))))
1901    (setq n (1- n)))
1902  (if (< n 0)
1903      (singular-section-backward (- n))
1904    (singular-keep-region-active)))
1905;;}}}
1906
1907;;{{{ Folding sections for both Emacs and XEmacs
1908(defcustom singular-folding-ellipsis "Singular I/O ..."
1909  "*Ellipsis to show for folded input or output.
1910Changing this variable has an immediate effect only if one uses
1911\\[customize] to do so.
1912However, even then it may be necessary to refresh display completely (using
1913\\[recenter], for example) for the new settings to be visible."
1914  :type 'string
1915  :initialize 'custom-initialize-default
1916  :set (function
1917        (lambda (var value)
1918          ;; set in all singular buffers
1919          (singular-map-buffer 'singular-folding-set-ellipsis value)
1920          (set-default var value)))
1921  :group 'singular-sections-and-foldings)
1922
1923(defcustom singular-folding-line-move-ignore-folding t
1924  "*If non-nil, ignore folded sections when moving point up or down.
1925This variable is used to initialize `line-move-ignore-invisible'.  However,
1926documentation states that setting `line-move-ignore-invisible' to a non-nil
1927value may result in a slow-down when moving the point up or down.  One
1928should try to set this variable to nil if point motion seems too slow.
1929
1930Changing this variable has an immediate effect only if one uses
1931\\[customize] to do so."
1932  :type 'boolean
1933  :initialize 'custom-initialize-default
1934  :set (function
1935        (lambda (var value)
1936          ;; set in all singular buffers
1937          (singular-map-buffer 'set 'line-move-ignore-invisible value)
1938          (set-default var value)))
1939  :group 'singular-sections-and-foldings)
1940
1941(defun singular-folding-set-ellipsis (ellipsis)
1942  "Set ellipsis to show for folded input or output in current buffer."
1943  (cond
1944   ;; Emacs
1945   ((eq singular-emacs-flavor 'emacs)
1946    (setq buffer-display-table (or (copy-sequence standard-display-table)
1947                                   (make-display-table)))
1948    (set-display-table-slot buffer-display-table
1949                            'selective-display (vconcat ellipsis)))
1950   ;; XEmacs
1951   (t
1952    (set-glyph-image invisible-text-glyph ellipsis (current-buffer)))))
1953
1954(defun singular-folding-init ()
1955  "Initializes folding of sections for the current buffer.
1956That includes setting `buffer-invisibility-spec' and the ellipsis to show
1957for hidden text.
1958
1959This function is called at mode initialization time."
1960  ;; initialize `buffer-invisibility-spec' first
1961  (let ((singular-invisibility-spec (cons 'singular-interactive-mode t)))
1962    (if (and (listp buffer-invisibility-spec)
1963             (not (member singular-invisibility-spec buffer-invisibility-spec)))
1964        (setq buffer-invisibility-spec
1965              (cons singular-invisibility-spec buffer-invisibility-spec))
1966      (setq buffer-invisibility-spec (list singular-invisibility-spec))))
1967  ;; ignore invisible lines on movements
1968  (set (make-local-variable 'line-move-ignore-invisible)
1969       singular-folding-line-move-ignore-folding)
1970  ;; now for the ellipsis
1971  (singular-folding-set-ellipsis singular-folding-ellipsis))
1972
1973(defun singular-folding-fold (section &optional no-error)
1974  "Fold section SECTION if it is not already folded.
1975Does not fold sections that do not end in a newline or that are restricted
1976either in part or as a whole.  Rather fails with an error in such cases
1977or silently fails if optional argument NO-ERROR is non-nil.
1978This is for safety only: In both cases the result may be confusing to the
1979user."
1980  (let* ((start (singular-section-start section))
1981         (end (singular-section-end section)))
1982    (cond ((or (< start (point-min))
1983               (> end (point-max)))
1984           (unless no-error
1985             (error "Folding not possible: section is restricted in part or as a whole")))
1986          ((not (eq (char-before end) ?\n))
1987           (unless no-error
1988             (error "Folding not possible: section does not end in newline")))
1989          ((not (singular-folding-foldedp section))
1990           ;; fold but only if not already folded
1991           (singular-folding-fold-internal section)))))
1992
1993(defun singular-folding-unfold (section &optional no-error invisibility-overlay-or-extent)
1994  "Unfold section SECTION if it is not already unfolded.
1995Does not unfold sections that are restricted either in part or as a whole.
1996Rather fails with an error in such cases or silently fails if optional
1997argument NO-ERROR is non-nil.  This is for safety only: The result may be
1998confusing to the user.
1999If optional argument INVISIBILITY-OVERLAY-OR_EXTENT is non-nil it should be
2000the invisibility overlay or extent, respectively, of the section to
2001unfold."
2002  (let* ((start (singular-section-start section))
2003         (end (singular-section-end section)))
2004    (cond ((or (< start (point-min))
2005               (> end (point-max)))
2006           (unless no-error
2007             (error "Unfolding not possible: section is restricted in part or as a whole")))
2008          ((or invisibility-overlay-or-extent
2009               (setq invisibility-overlay-or-extent (singular-folding-foldedp section)))
2010           ;; unfold but only if not already unfolded
2011           (singular-folding-unfold-internal section invisibility-overlay-or-extent)))))
2012
2013(defun singular-folding-fold-at-point ()
2014  "Fold section point currently is in.
2015Does not fold sections that do not end in a newline or that are restricted
2016either in part or as a whole.  Rather fails with an error in such cases."
2017  (interactive)
2018  (singular-folding-fold (singular-section-at (point))))
2019
2020(defun singular-folding-unfold-at-point ()
2021  "Unfold section point currently is in.
2022Does not unfold sections that are restricted either in part or as a whole.
2023Rather fails with an error in such cases."
2024  (interactive)
2025  (singular-folding-unfold (singular-section-at (point))))
2026
2027(defun singular-folding-fold-latest-output ()
2028  "Fold latest output section.
2029Does not fold sections that do not end in a newline or that are restricted
2030either in part or as a whole.  Rather fails with an error in such cases."
2031  (interactive)
2032  (singular-folding-fold (singular-latest-output-section)))
2033
2034(defun singular-folding-unfold-latest-output ()
2035  "Unfolds latest output section.
2036Does not unfold sections that are restricted either in part or as a whole.
2037Rather fails with an error in such cases."
2038  (interactive)
2039  (singular-folding-unfold (singular-latest-output-section)))
2040
2041(defun singular-folding-fold-all-output ()
2042  "Fold all complete, unfolded output sections.
2043That is, all output sections that are not restricted in part or as a whole
2044and that end in a newline."
2045  (interactive)
2046  (singular-section-mapsection (function (lambda (section) (singular-folding-fold section t)))
2047                               (singular-section-in (point-min) (point-max) t)
2048                               '(output)))
2049
2050(defun singular-folding-unfold-all-output ()
2051  "Unfold all complete, folded output sections.
2052That is, all output sections that are not restricted in part or as a whole."
2053  (interactive)
2054  (singular-section-mapsection (function (lambda (section) (singular-folding-unfold section t)))
2055                               (singular-section-in (point-min) (point-max) t)
2056                               '(output)))
2057
2058(defun singular-folding-toggle-fold-at-point-or-all (&optional arg)
2059  "Fold or unfold section point currently is in or all output sections.
2060Without prefix argument, folds unfolded sections and unfolds folded
2061sections.  With prefix argument, folds all output sections if argument is
2062positive, otherwise unfolds all output sections.
2063Does neither fold nor unfold sections that do not end in a newline or that
2064are restricted either in part or as a whole.  Rather fails with an error in
2065such cases."
2066  (interactive "P")
2067    (cond ((not arg)
2068           ;; fold or unfold section at point
2069           (let* ((section (singular-section-at (point)))
2070                  (invisibility-overlay-or-extent (singular-folding-foldedp section)))
2071             (if invisibility-overlay-or-extent
2072                 (singular-folding-unfold section nil invisibility-overlay-or-extent)
2073               (singular-folding-fold section))))
2074          ((> (prefix-numeric-value arg) 0)
2075           (singular-folding-fold-all-output))
2076          (t
2077           (singular-folding-unfold-all-output))))
2078
2079(defun singular-folding-toggle-fold-latest-output (&optional arg)
2080  "Fold or unfold latest output section.
2081Folds unfolded sections and unfolds folded sections.
2082Does neither fold nor unfold sections that do not end in a newline or that
2083are restricted either in part or as a whole.  Rather fails with an error in
2084such cases."
2085  (interactive)
2086  (let* ((section (singular-latest-output-section))
2087         (invisibility-overlay-or-extent (singular-folding-foldedp section)))
2088    (if invisibility-overlay-or-extent
2089        (singular-folding-unfold section nil invisibility-overlay-or-extent)
2090      (singular-folding-fold section))))
2091
2092;; Note:
2093;;
2094;; The rest of the folding is either marked as
2095;; Emacs
2096;; or
2097;; XEmacs
2098
2099(singular-fset 'singular-folding-fold-internal
2100               'singular-emacs-folding-fold-internal
2101               'singular-xemacs-folding-fold-internal)
2102
2103(singular-fset 'singular-folding-unfold-internal
2104               'singular-emacs-folding-unfold-internal
2105               'singular-xemacs-folding-unfold-internal)
2106
2107(singular-fset 'singular-folding-foldedp
2108               'singular-emacs-folding-foldedp-internal
2109               'singular-xemacs-folding-foldedp-internal)
2110;;}}}
2111
2112;;{{{ Folding sections for Emacs
2113
2114;; Note:
2115;;
2116;; For Emacs, we use overlays to hide text (so-called "invisibility
2117;; overlays").  In addition to their `invisible' property, they have the
2118;; `singular-invisible' property set.  Setting the intangible property does
2119;; not work very well for Emacs.  We use the variable
2120;; `line-move-ignore-invisible' which works quite well.
2121
2122(defun singular-emacs-folding-fold-internal (section)
2123  "Fold section SECTION.
2124SECTION should end in a newline.  That terminal newline is not
2125folded or otherwise ellipsis does not appear.
2126SECTION should be unfolded."
2127  (let* ((start (singular-section-start section))
2128         ;; do not make trailing newline invisible
2129         (end (1- (singular-section-end section)))
2130         invisibility-overlay)
2131    ;; create new overlay and add properties
2132    (setq invisibility-overlay (make-overlay start end))
2133    ;; mark them as invisibility overlays
2134    (overlay-put invisibility-overlay 'singular-invisible t)
2135    ;; set invisible properties
2136    (overlay-put invisibility-overlay 'invisible 'singular-interactive-mode)
2137    ;; evaporate empty invisibility overlays
2138    (overlay-put invisibility-overlay 'evaporate t)))
2139
2140(defun singular-emacs-folding-unfold-internal (section &optional invisibility-overlay)
2141  "Unfold section SECTION.
2142SECTION should be folded.
2143If optional argument INVISIBILITY-OVERLAY is non-nil it should be the
2144invisibility overlay of the section to unfold."
2145  (let ((invisibility-overlay
2146         (or invisibility-overlay
2147             (singular-emacs-folding-foldedp-internal section))))
2148    ;; to keep number of overlays low we delete it
2149    (delete-overlay invisibility-overlay)))
2150
2151(defun singular-emacs-folding-foldedp-internal (section)
2152  "Returns non-nil iff SECTION is folded.
2153More specifically, returns the invisibility overlay if there is one.
2154Narrowing has no effect on this function."
2155  (let* ((start (singular-section-start section))
2156         (overlays (overlays-at start))
2157         invisibility-overlay)
2158    ;; check for invisibility overlay
2159    (while (and overlays (not invisibility-overlay))
2160      (if (overlay-get (car overlays) 'singular-invisible)
2161          (setq invisibility-overlay (car overlays))
2162        (setq overlays (cdr overlays))))
2163    invisibility-overlay))
2164;;}}}
2165
2166;;{{{ Folding sections for XEmacs
2167
2168;; Note:
2169;;
2170;; For XEmacs, we use extents to hide text (so-called "invisibility
2171;; extents").  In addition to their `invisible' property, they have the
2172;; `singular-invisible' property set.  To ignore invisible text we use the
2173;; variable `line-move-ignore-invisible' which works quite well.
2174
2175(defun singular-xemacs-folding-fold-internal (section)
2176  "Fold section SECTION.
2177SECTION should end in a newline.  That terminal newline is not
2178folded or otherwise ellipsis does not appear.
2179SECTION should be unfolded."
2180  (let* ((start (singular-section-start section))
2181         ;; do not make trailing newline invisible
2182         (end (1- (singular-section-end section)))
2183         invisibility-extent)
2184    ;; create new extent and add properties
2185    (setq invisibility-extent (make-extent start end))
2186    ;; mark them as invisibility extents
2187    (set-extent-property invisibility-extent 'singular-invisible t)
2188    ;; set invisible properties
2189    (set-extent-property invisibility-extent 'invisible 'singular-interactive-mode)))
2190
2191(defun singular-xemacs-folding-unfold-internal (section &optional invisibility-extent)
2192  "Unfold section SECTION.
2193SECTION should be folded.
2194If optional argument INVISIBILITY-EXTENT is non-nil it should be the
2195invisibility extent of the section to unfold."
2196  (let ((invisibility-extent
2197         (or invisibility-extent
2198             (singular-xemacs-folding-foldedp-internal section))))
2199    ;; to keep number of extents low we delete it
2200    (delete-extent invisibility-extent)))
2201
2202(defun singular-xemacs-folding-foldedp-internal (section)
2203  "Returns non-nil iff SECTION is folded.
2204More specifically, returns the invisibility extent if there is one.
2205Narrowing has no effect on this function."
2206  ;; do not try to use `extent-at' at this point.  `extent-at' does not
2207  ;; return extents outside narrowed text.
2208  (let* ((start (singular-section-start section))
2209         (invisibility-extent (map-extents
2210                            (function (lambda (ext args) ext))
2211                            nil start start nil nil 'singular-invisible)))
2212    invisibility-extent))
2213;;}}}
2214
2215;;{{{ Online help
2216
2217;; Note:
2218;;
2219;; Catching user's help commands to Singular and translating them to calls
2220;; to `info' is quite a difficult task due to the asynchronous
2221;; communication with Singular.  We use an heuristic approach which should
2222;; work in most cases:
2223
2224(require 'info)
2225
2226(defcustom singular-help-same-window 'default
2227  "Specifies how to open the window for Singular online help.
2228If this variable equals `default', the standard Emacs behaviour to open the
2229Info buffer is adopted (which very much depends on the settings of
2230`same-window-buffer-names').
2231If this variable is non-nil, Singular online help comes up in the selected
2232window.
2233If this variable equals nil, Singular online help comes up in another
2234window."
2235  :initialize 'custom-initialize-default
2236  :type '(choice (const :tag "This window" t)
2237                 (const :tag "Other window" nil)
2238                 (const :tag "Default" default))
2239  :group 'singular-interactive-miscellaneous)
2240
2241(defcustom singular-help-explicit-file-name nil
2242  "Specifies the file name of the Singular online manual.
2243If non-nil, this variable overrides all other possible ways to determine
2244the file name of the Singular online manual.
2245For more information one should refer to the `singular-help' function."
2246  :initialize 'custom-initialize-default
2247  :type 'file
2248  :group 'singular-interactive-miscellaneous)
2249
2250(defvar singular-help-time-stamp 0
2251  "A time stamp set by `singular-help-pre-input-hook'.
2252This time stamp is set to `(current-time)' when the user issues a help
2253command.  To be true, not the whole time stamp is stored, only the less
2254significant half.
2255
2256This variable is buffer-local.")
2257
2258(defvar singular-help-response-pending nil
2259  "If non-nil, Singulars response has not been completely received.
2260
2261This variable is buffer-local.")
2262
2263(defvar singular-help-topic nil
2264  "If non-nil, contains help topic to dhow in post output filter.
2265
2266This variable is buffer-local.")
2267
2268(defconst singular-help-command-regexp "^\\s-*shelp\\>"
2269  "Regular expression to match Singular help commands.")
2270
2271(defconst singular-help-response-line-1
2272  "^Your help command could not be executed.  Use\n"
2273  "Regular expression that matches the first line of Singulars response.")
2274
2275(defconst singular-help-response-line-2
2276  "^C-h C-s \\(.*\\)\n")
2277
2278(defconst singular-help-response-line-3
2279  "^to enter the Singular online help\.  For general\n"
2280  "Regular expression that matches the first line of Singulars response.")
2281
2282(defconst singular-help-response-line-4
2283  "^information on Singular running on Emacs, type C-h m\.\n"
2284  "Regular expression that matches the first line of Singulars response.")
2285
2286(defun singular-help-pre-input-filter (input)
2287  "Check user's input for help commands.
2288Sets time stamp if one is found."
2289  (if (string-match singular-help-command-regexp input)
2290      (setq singular-help-time-stamp (cadr (current-time))))
2291  ;; return nil so that input passes unchanged
2292  nil)
2293
2294(defun singular-help-pre-output-filter (output)
2295  "Check for Singular's response on a help command.
2296Removes it and fires up `(info)' to handle the help command."
2297  ;; check first
2298  ;; - whether a help statement has been issued less than one second ago, or
2299  ;; - whether there is a pending response.
2300  ;;
2301  ;; Only if one of these conditions is met we go on and check text for a
2302  ;; response on a help command.  Checking uncoditionally every piece of
2303  ;; output would be far too expensive.
2304  ;;
2305  ;; If check fails nil is returned, what is exactly what we need for the
2306  ;; filter.
2307  (if (or (= (cadr (current-time)) singular-help-time-stamp)
2308          singular-help-response-pending)
2309      ;; if response is pending for more than five seconds, give up
2310      (if (and singular-help-response-pending
2311               (> (singular-time-stamp-difference (current-time) singular-help-time-stamp) 5))
2312          ;; this command returns nil, what is exactly what we need for the filter
2313          (setq singular-help-response-pending nil)
2314          ;; go through output, removing the response.  If there is a
2315          ;; pending response we nevertheless check for all lines, not only
2316          ;; for the pending one.  At last, pending responses should not
2317          ;; occur to often.
2318          (when (string-match singular-help-response-line-1 output)
2319            (setq output (replace-match "" t t output))
2320            (setq singular-help-response-pending t))
2321          (when (string-match singular-help-response-line-2 output)
2322            ;; after all, we found what we are looking for
2323            (setq singular-help-topic (substring output (match-beginning 1) (match-end 1)))
2324            (setq output (replace-match "" t t output))
2325            (setq singular-help-response-pending t))
2326          (when (string-match singular-help-response-line-3 output)
2327            (setq output (replace-match "" t t output))
2328            (setq singular-help-response-pending t))
2329          (when (string-match singular-help-response-line-4 output)
2330            (setq output (replace-match "" t t output))
2331            ;; we completely removed the help from output!
2332            (setq singular-help-response-pending nil))
2333
2334          ;; return modified OUTPUT
2335          output)))
2336
2337(defun singular-help-post-output-filter (&rest ignore)
2338  (when singular-help-topic
2339    (save-excursion (singular-help singular-help-topic))
2340    (setq singular-help-topic nil)))
2341
2342(defun singular-help (&optional help-topic)
2343  "Show help on HELP-TOPIC in Singular online manual."
2344 
2345  (interactive "s")
2346
2347  ;; check for empty help topic and convert it to top node
2348  (if (or (null help-topic) (string= help-topic ""))
2349      (setq help-topic "Top"))
2350
2351  (let ((same-window-buffer-names
2352         (cond
2353          ((null singular-help-same-window)
2354           nil)
2355          ((eq singular-help-same-window 'default)
2356           same-window-buffer-names)
2357          (t
2358           '("*info*"))))
2359        (node-name (concat "(" (or singular-help-explicit-file-name
2360                                   singular-help-file-name)
2361                           ")" help-topic)))
2362    (pop-to-buffer "*info*")
2363    (Info-goto-node node-name)))
2364   
2365
2366(defun singular-help-init ()
2367  "Initialize online help support for Singular interactive mode.
2368
2369This function is called at mode initialization time."
2370  (make-local-variable 'singular-help-time-stamp)
2371  (make-local-variable 'singular-help-response-pending)
2372  (make-local-variable 'singular-help-topic)
2373  (add-hook 'singular-pre-input-filter-functions 'singular-help-pre-input-filter)
2374  (add-hook 'singular-pre-output-filter-functions 'singular-help-pre-output-filter)
2375  (add-hook 'singular-post-output-filter-functions 'singular-help-post-output-filter))
2376;;}}}
2377
2378;;{{{ Scanning of header and handling of emacs home directory
2379;;
2380;; Scanning of header
2381;;
2382(defvar singular-scan-header-emacs-home-regexp "^// \\*\\* EmacsDir: \\(.+\\)\n"
2383  "Regular expression matching the location of emacs home in Singular
2384header.")
2385
2386(defvar singular-scan-header-info-file-regexp "^// \\*\\* InfoFile: \\(.+\\)\n"
2387  "Regular expression matching the location of Singular info file in
2388Singular header.")
2389
2390(defvar singular-scan-header-time-stamp 0
2391  "A time stamp set by singular-scan-header.
2392
2393This variable is buffer-local.")
2394
2395(defvar singular-scan-header-scan-for '(emacs-home info-file)
2396  "List of things to scan for in Singular header.
2397If `singular-scan-header-pre-output-filter' finds one thing in the current
2398output, it removes the corresponding value from the list.
2399If this variable gets nil, `singular-scan-header-pre-output-filter' is
2400removed from the pre-output-filter.
2401
2402This variable is buffer-local.")
2403
2404(defun singular-scan-header-init ()
2405  "NOT READY: docu"
2406  (make-local-variable 'singular-scan-header-time-stamp)
2407  (setq singular-scan-header-time-stamp (current-time))
2408  (make-local-variable 'singular-emacs-home-directory)
2409  (setq singular-emacs-home-directory nil)
2410  (make-local-variable 'singular-scan-header-scan-for)
2411  (setq singular-scan-header-scan-for '(emacs-home info-file))
2412  (add-hook 'singular-pre-output-filter-functions 'singular-scan-header-pre-output-filter))
2413
2414(defun singular-scan-header-pre-output-filter (output)
2415  "NOT READY: docu"
2416  (let ((changed nil))
2417
2418    ;; Search for emacs home directory
2419    (when (string-match singular-scan-header-emacs-home-regexp output)
2420      (setq singular-scan-header-scan-for (delq 'emacs-home singular-scan-header-scan-for))
2421      (setq singular-emacs-home-directory (substring output (match-beginning 1) (match-end 1)))
2422      (setq output (replace-match "" t t output))
2423      (setq changed t)
2424
2425      (or (load (singular-expand-emacs-file-name "cmd-cmpl.el" t) t t t)
2426          (message "Can't find command completion file! Command completion disabled."))
2427      (or (load (singular-expand-emacs-file-name "hlp-cmpl.el" t) t t t)
2428          (message "Can't find help topic completion file! Help completion disabled."))
2429      (if (load (singular-expand-emacs-file-name "lib-cmpl.el" t) t t t)
2430          (singular-menu-install-libraries)
2431        (message "Can't find library index file!")))
2432
2433    ;; Search for Singular info file
2434    (when (string-match singular-scan-header-info-file-regexp output)
2435      (setq singular-scan-header-scan-for (delq 'info-file singular-scan-header-scan-for))
2436      (setq singular-help-file-name (substring output (match-beginning 1) (match-end 1)))
2437      (setq output (replace-match "" t t output))
2438      (setq changed t))
2439
2440    ;; Remove from hook if everything is found or if we already waited
2441    ;; too long.
2442    (if (or (eq singular-scan-header-scan-for nil) 
2443            (> (singular-time-stamp-difference (current-time) singular-scan-header-time-stamp) 10))
2444        (remove-hook 'singular-pre-output-filter-functions 'singular-scan-header-pre-output-filter))
2445
2446    ;; Return new output string if we changed it, nil otherwise
2447    (and changed output)))
2448
2449;;
2450;; handling of emacs home directory
2451;;
2452(defvar singular-emacs-home-directory nil
2453  "Path to the emacs sub-directory of Singular as string.
2454`singular-scan-header' searches the Singular header for the path and sets
2455this variable to the corresponding value.
2456Is initialized by `singular-scan-header-pre-output-filter'.
2457
2458This variable is buffer-local.")
2459
2460(defun singular-expand-emacs-file-name (file &optional noerror)
2461  "Adds absolute path of emacs home directory.
2462Adds the content of `singular-emacs-home-directory' to the string FILE.
2463If `singular-emacs-home-directory' is nil, return nil and signal
2464an error unless optional argument NOERROR is not nil."
2465  (if singular-emacs-home-directory
2466      (concat singular-emacs-home-directory "/" file)
2467    (if noerror
2468        nil
2469      (error "Variable singular-emacs-home-directory not set"))))
2470;;}}}
2471
2472;;{{{ Filename, Command, and Help Completion
2473(defvar singular-completion-cmd-list nil
2474  "An alist containing all Singular commands to complete.
2475
2476This variable is buffer-local.")
2477
2478(defvar singular-completion-hlp-list nil
2479  "An alist containg all Singular help topics to complete.
2480
2481This variable is buffer-local.")
2482
2483(defun singular-completion-init ()
2484  "Initialize completion of file names, commands and help topics
2485for Singular interactive mode.
2486
2487This function is called at mode initialization time."
2488  (make-local-variable 'singular-completion-cmd-list)
2489  (setq singular-completion-cmd-list nil)
2490  (make-local-variable 'singular-completion-hlp-list)
2491  (setq singular-completion-hlp-list nil))
2492
2493(defun singular-completion-do (pattern beg end completion-alist)
2494  "Try completion on string PATTERN using alist COMPLETION-ALIST.
2495Insert completed version of PATTERN as new text between BEG and END.
2496Assumes the COMPLETION-ALIST is not nil."
2497  (let ((completion (try-completion pattern completion-alist)))
2498    (cond ((eq completion t)
2499           (message "[Sole completion]"))  ;; nothing to complete
2500          ((null completion)               ;; no completion found
2501           (message "Can't find completion for \"%s\"" pattern)
2502           (ding))
2503          ((not (string= pattern completion))
2504           (delete-region beg end)
2505           (insert completion))
2506          (t
2507           (message "Making completion list...")
2508           (let ((list (all-completions pattern 
2509                                        completion-alist)))
2510             (with-output-to-temp-buffer "*Completions*"
2511               (display-completion-list list)))
2512           (message "Making completion list...%s" "done")))))
2513
2514(defun singular-dynamic-complete ()
2515  "Dynamic complete word before point.
2516Perform file name completion if point is inside a string.
2517Perform completion of Singular help topics if point is at the end of a
2518help command (\"help\" or \"?\").
2519Otherwise perform completion of Singular commands."
2520  (interactive)
2521  ;; Check if we are inside a string. The search is done back to the
2522  ;; process-mark which should be the beginning of the current input.
2523  ;; No check at this point whether there is a process!
2524  (if (save-excursion
2525        (nth 3 (parse-partial-sexp (singular-process-mark) (point))))
2526      ;; then: inside string, thus expand filename
2527      (comint-dynamic-complete-as-filename)
2528    ;; else: expand command or help
2529    (let ((end (point))
2530          beg)
2531      (if (save-excursion
2532            (beginning-of-line)
2533            (re-search-forward (concat singular-prompt-regexp
2534                                       "[ \t]*\\([\\?]\\|help \\)[ \t]*\\(.*\\)")
2535                               end t))
2536          ;; then: help completion
2537          (if singular-completion-hlp-list
2538              (singular-completion-do (match-string 2) (match-beginning 2)
2539                                      end singular-completion-hlp-list)
2540            (message "Completion of Singular help topics disabled.")
2541            (ding))
2542        ;; else: command completion
2543        (save-excursion
2544          (skip-chars-backward "a-zA-Z0-9")
2545          (setq beg (point)))
2546        (if singular-completion-cmd-list
2547            (singular-completion-do (buffer-substring beg end) beg
2548                                    end singular-completion-cmd-list)
2549          (message "Completion of Singular commands disabled.")
2550          (ding))))))
2551;;}}}
2552
2553;;{{{ Debugging filters
2554(defun singular-debug-pre-input-filter (string)
2555  "Display STRING and some markers in mini-buffer."
2556  (singular-debug 'interactive-filter
2557                  (message "Pre-input filter: %s (li %S ci %S lo %S co %S)"
2558                           (singular-debug-format string)
2559                           (marker-position singular-last-input-section-start)
2560                           (marker-position singular-current-input-section-start)
2561                           (marker-position singular-last-output-section-start)
2562                           (marker-position singular-current-output-section-start)))
2563  nil)
2564
2565(defun singular-debug-post-input-filter (beg end)
2566  "Display BEG, END, and some markers in mini-buffer."
2567  (singular-debug 'interactive-filter
2568                  (message "Post-input filter: (beg %S end %S) (li %S ci %S lo %S co %S)"
2569                           beg end
2570                           (marker-position singular-last-input-section-start)
2571                           (marker-position singular-current-input-section-start)
2572                           (marker-position singular-last-output-section-start)
2573                           (marker-position singular-current-output-section-start))))
2574
2575(defun singular-debug-pre-output-filter (string)
2576  "Display STRING and some markers in mini-buffer."
2577  (singular-debug 'interactive-filter
2578                  (message "Pre-output filter: %s (li %S ci %S lo %S co %S)"
2579                           (singular-debug-format string)
2580                           (marker-position singular-last-input-section-start)
2581                           (marker-position singular-current-input-section-start)
2582                           (marker-position singular-last-output-section-start)
2583                           (marker-position singular-current-output-section-start)))
2584  nil)
2585
2586(defun singular-debug-post-output-filter (beg end simple-sec-start)
2587  "Display BEG, END, SIMPLE-SEC-START, and some markers in mini-buffer."
2588  (singular-debug 'interactive-filter
2589                  (message "Post-output filter: (beg %S end %S sss %S) (li %S ci %S lo %S co %S)"
2590                           beg end simple-sec-start
2591                           (marker-position singular-last-input-section-start)
2592                           (marker-position singular-current-input-section-start)
2593                           (marker-position singular-last-output-section-start)
2594                           (marker-position singular-current-output-section-start))))
2595
2596(defun singular-debug-filter-init ()
2597  "Add debug filters to the necessary hooks.
2598
2599This function is called at mode initialization time."
2600  (add-hook 'singular-pre-input-filter-functions
2601            'singular-debug-pre-input-filter nil t)
2602  (add-hook 'singular-post-input-filter-functions
2603            'singular-debug-post-input-filter nil t)
2604  (add-hook 'singular-pre-output-filter-functions
2605            'singular-debug-pre-output-filter nil t)
2606  (add-hook 'singular-post-output-filter-functions
2607            'singular-debug-post-output-filter nil t))
2608;;}}}
2609
2610;;{{{ Demo mode
2611(defcustom singular-demo-chunk-regexp "\\(\n\\s *\n\\)"
2612  "Regular expressions to recognize separate chunks of a demo file.
2613If there is a subexpression specified its contents is removed when the
2614chunk is displayed.
2615The default value is \"\\\\(\\n\\\\s *\\n\\\\)\" which means that chunks are
2616separated by a blank line which is removed when the chunks are displayed."
2617  :type 'regexp
2618  :group 'singular-demo-mode)
2619
2620(defcustom singular-demo-insert-into-history nil
2621  "If non-nil, insert input into history even while demo mode is on.
2622Otherwise, demo chunks and other commands executed during demo mode are not
2623inserted into the history."
2624  :type 'boolean
2625  :group 'singular-demo-mode)
2626
2627(defcustom singular-demo-print-messages nil
2628  "If non-nil, print message on how to continue demo mode."
2629  :type 'boolean
2630  :group 'singular-demo-mode)
2631
2632(defcustom singular-demo-exit-on-load nil
2633  "If non-nil, a running demo is automatically discarded when a new one is loaded.
2634Otherwise, the load is aborted with an error."
2635  :type 'boolean
2636  :group 'singular-demo-mode)
2637
2638(defcustom singular-demo-load-directory nil
2639  "Directory where demo files reside.
2640If non-nil, this directory is offered as a starting point to search for
2641demo files when `singular-demo-load' is called interactively.
2642If this variable equals nil whatever Emacs offers is used as starting
2643point.  In general, this is the directory where Singular has been started
2644in."
2645  :type '(choice (const nil) (file))
2646  :group 'singular-demo-mode)
2647
2648(defvar singular-demo-mode nil
2649  "Non-nil if Singular demo mode is on.
2650
2651This variable is buffer-local.")
2652
2653(defvar singular-demo-old-mode-name nil
2654  "Used to store previous `mode-name' before switching to demo mode.
2655
2656This variable is buffer-local.")
2657
2658(defvar singular-demo-end nil
2659  "Marker pointing to end of demo file.
2660
2661This variable is buffer-local.")
2662
2663(defun singular-demo-load (demo-file)
2664  "Load demo file DEMO-FILE and enter Singular demo mode.
2665NOT READY."
2666  (interactive
2667   (list
2668    (cond
2669     ;; Emacs
2670     ((eq singular-emacs-flavor 'emacs)
2671      (read-file-name "Load demo file: "
2672                      singular-demo-load-directory
2673                      nil t))
2674     ;; XEmacs
2675     (t
2676      ;; there are some problems with the window being popped up when this
2677      ;; function is called from a menu.  It does not display the contents
2678      ;; of `singular-demo-load-directory' but of `default-directory'.
2679      (let ((default-directory (or singular-demo-load-directory
2680                                   default-directory)))
2681        (read-file-name "Load demo file: "
2682                        singular-demo-load-directory
2683                        nil t))))))
2684
2685  ;; check for running demo
2686  (if singular-demo-mode
2687      (if singular-demo-exit-on-load
2688          ;; silently exit running demo
2689          (singular-demo-exit)
2690        (error "There already is a demo running, exit with `singular-demo-exit' first")))
2691
2692  ;; load new demo
2693  (let ((old-point-min (point-min)))
2694    (unwind-protect
2695        (progn
2696          (goto-char (point-max))
2697          (widen)
2698          (cond
2699           ;; XEmacs
2700           ((eq singular-emacs-flavor 'xemacs)
2701            ;; load file and remember its end
2702            (set-marker singular-demo-end
2703                        (+ (point) (nth 1 (insert-file-contents-literally demo-file)))))
2704           ;; Emacs
2705           (t
2706            ;; Emacs does something like an `insert-before-markers' so
2707            ;; save all essential markers
2708            (let ((pmark-pos (marker-position (singular-process-mark)))
2709                  (sliss-pos (marker-position singular-last-input-section-start))
2710                  (sciss-pos (marker-position singular-current-input-section-start))
2711                  (sloss-pos (marker-position singular-last-output-section-start))
2712                  (scoss-pos (marker-position singular-current-output-section-start)))
2713
2714              (unwind-protect
2715                  ;; load file and remember its end
2716                  (set-marker singular-demo-end
2717                              (+ (point) (nth 1 (insert-file-contents-literally demo-file))))
2718
2719                ;; restore markers.
2720                ;; This is unwind-protected.
2721                (set-marker (singular-process-mark) pmark-pos)
2722                (set-marker singular-last-input-section-start sliss-pos)
2723                (set-marker singular-current-input-section-start sciss-pos)
2724                (set-marker singular-last-output-section-start sloss-pos)
2725                (set-marker singular-current-output-section-start scoss-pos))))))
2726
2727      ;; completely hide demo file.
2728      ;; This is unwind-protected.
2729      (narrow-to-region old-point-min (point))))
2730
2731  ;; switch demo mode on
2732  (setq singular-demo-old-mode-name mode-name
2733        mode-name "Singular Demo"
2734        singular-demo-mode t)
2735  (run-hooks 'singular-demo-mode-enter-hook)
2736  (if singular-demo-print-messages (message "Hit RET to start demo"))
2737  (force-mode-line-update))
2738
2739(defun singular-demo-exit-internal ()
2740  "Exit Singular demo mode.
2741Recovers the old mode name, sets `singular-demo-mode' to nil, runs
2742the hooks on `singular-demo-mode-exit-hook'."
2743  (setq mode-name singular-demo-old-mode-name
2744        singular-demo-mode nil)
2745  (run-hooks 'singular-demo-mode-exit-hook)
2746  (force-mode-line-update))
2747
2748(defun singular-demo-exit ()
2749  "Prematurely exit Singular demo mode.
2750Cleans up everything that is left from the demo.
2751Runs the hooks on `singular-demo-mode-exit-hook'.
2752Does nothing when Singular demo mode is turned off."
2753  (interactive)
2754  (when singular-demo-mode
2755    ;; clean up hidden rest of demo file
2756    (let ((old-point-min (point-min))
2757          (old-point-max (point-max)))
2758      (unwind-protect
2759          (progn
2760            (widen)
2761            (delete-region old-point-max singular-demo-end))
2762        ;; this is unwind-protected
2763        (narrow-to-region old-point-min old-point-max)))
2764    (singular-demo-exit-internal)))
2765
2766(defun singular-demo-show-next-chunk ()
2767  "Show next chunk of demo file at input prompt.
2768Assumes that Singular demo mode is on.
2769Moves point to end of buffer and widenes the buffer such that the next
2770chunk of the demo file becomes visible.
2771Finds and removes chunk separators as specified by
2772`singular-demo-chunk-regexp'.
2773Leaves demo mode after showing last chunk.  In that case runs hooks on
2774`singular-demo-mode-exit-hook'."
2775  (let ((old-point-min (point-min)))
2776    (unwind-protect
2777        (progn
2778          (goto-char (point-max))
2779          (widen)
2780          (if (re-search-forward singular-demo-chunk-regexp singular-demo-end 'limit)
2781              (if (match-beginning 1)
2782                  (delete-region (match-beginning 1) (match-end 1)))
2783            ;; remove trailing white-space.  We may not use
2784            ;; `(skip-syntax-backward "-")' since newline is has no white
2785            ;; space syntax.  The solution down below should suffice in
2786            ;; almost all cases ...
2787            (skip-chars-backward " \t\n\r")
2788            (delete-region (point) singular-demo-end)
2789            (singular-demo-exit-internal)))
2790
2791      ;; this is unwind-protected
2792      (narrow-to-region old-point-min (point)))))
2793
2794(defun singular-demo-mode-init ()
2795  "Initialize variables belonging to Singular demo mode.
2796Creates some buffer-local variables and the buffer-local marker
2797`singular-demo-end'.
2798
2799This function is called  at mode initialization time."
2800  (make-local-variable 'singular-demo-mode)
2801  (make-local-variable 'singular-demo-mode-old-name)
2802  (make-local-variable 'singular-demo-mode-end)
2803  (if (not (and (boundp 'singular-demo-end)
2804                singular-demo-end))
2805      (setq singular-demo-end (make-marker))))
2806;;}}}
2807     
2808;;{{{ Some lengthy notes on input and output
2809
2810;; NOT READY[so sorry]!
2811
2812;;}}}
2813
2814;;{{{ Last input and output section
2815(defun singular-last-input-section (&optional no-error)
2816  "Return last input section.
2817Returns nil if optional argument NO-ERROR is non-nil and there is no
2818last input section defined, throws an error otherwise."
2819  (let ((last-input-start (marker-position singular-last-input-section-start))
2820        (last-input-end (marker-position singular-current-output-section-start)))
2821    (cond ((and last-input-start last-input-end)
2822           (singular-section-create (singular-simple-sec-at last-input-start) 'input
2823                                    last-input-start last-input-end))
2824          (no-error nil)
2825          (t (error "No last input section defined")))))
2826
2827(defun singular-current-output-section (&optional no-error)
2828  "Return current output section.
2829Returns nil if optional argument NO-ERROR is non-nil and there is no
2830current output section defined, throws an error otherwise."
2831  (let ((current-output-start (marker-position singular-current-output-section-start))
2832        (current-output-end (save-excursion
2833                              (save-restriction
2834                                (widen)
2835                                (goto-char (singular-process-mark))
2836                                (singular-skip-prompt-backward)
2837                                (and (bolp) (point))))))
2838    (cond ((and current-output-start current-output-end)
2839           (singular-section-create (singular-simple-sec-at current-output-start) 'output
2840                                    current-output-start current-output-end))
2841          (no-error nil)
2842          (t (error "No current output section defined")))))
2843
2844(defun singular-last-output-section (&optional no-error)
2845  "Return last output section.
2846Returns nil if optional argument NO-ERROR is non-nil and there is no
2847last output section defined, throws an error otherwise."
2848  (let ((last-output-start (marker-position singular-last-output-section-start))
2849        (last-output-end (marker-position singular-last-input-section-start)))
2850    (cond ((and last-output-start last-output-end)
2851           (singular-section-create (singular-simple-sec-at last-output-start) 'output
2852                                    last-output-start last-output-end))
2853          (no-error nil)
2854          (t (error "No last output section defined")))))
2855
2856(defun singular-latest-output-section (&optional no-error)
2857  "Return latest output section.
2858This is the current output section if it is defined, otherwise the
2859last output section.
2860Returns nil if optional argument NO-ERROR is non-nil and there is no
2861latest output section defined, throws an error otherwise."
2862  (or (singular-current-output-section t)
2863      (singular-last-output-section t)
2864      (if no-error
2865          nil
2866        (error "No latest output section defined"))))
2867;;}}}
2868
2869;;{{{ Sending input
2870(defvar singular-pre-input-filter-functions nil
2871  "Functions to call before input is sent to process.
2872These functions get one argument, a string containing the text which
2873is to be sent to process.  The functions should return either nil
2874or a string.  In the latter case the returned string replaces the
2875string to be sent to process.
2876
2877This is a buffer-local variable, not a buffer-local hook!
2878
2879`singular-run-hook-with-arg-and-value' is used to run the functions in
2880the list.")
2881
2882(defvar singular-post-input-filter-functions nil
2883  "Functions to call after input is sent to process.
2884These functions get two arguments BEG and END.
2885If `singular-input-filter' has been called with a string as argument
2886BEG and END gives the position of this string after insertion into the
2887buffer.
2888If `singular-input-filter' has been called with a position as argument
2889BEG and END equal process mark and that position, resp.
2890The functions may assume that no narrowing is in effect and may change
2891point at will.
2892
2893This hook is buffer-local.")
2894
2895(defvar singular-current-input-section-start nil
2896  "Marker to the start of the current input section.
2897This marker points nowhere on startup or if there is no current input
2898section.
2899
2900This variable is buffer-local.")
2901
2902(defvar singular-last-input-section-start nil
2903  "Marker to the start of the last input section.
2904This marker points nowhere on startup.
2905
2906This variable is buffer-local.")
2907
2908(defun singular-input-filter-init (pos)
2909  "Initialize all variables concerning input.
2910POS is the position of the process mark."
2911  ;; localize variables not yet localized in `singular-interactive-mode'
2912  (make-local-variable 'singular-current-input-section-start)
2913  (make-local-variable 'singular-last-input-section-start)
2914
2915  ;; initialize markers
2916  (if (not (markerp singular-current-input-section-start))
2917      (setq singular-current-input-section-start (make-marker)))
2918  (if (not (markerp singular-last-input-section-start))
2919      (setq singular-last-input-section-start (make-marker))))
2920
2921(defun singular-send-string (process string)
2922  "Send newline terminated STRING to to process PROCESS.
2923Runs the hooks on `singular-pre-input-filter-functions' in the buffer
2924associated to PROCESS.  The functions get the non-terminated string."
2925  (let ((process-buffer (process-buffer process)))
2926
2927    ;; check whether buffer is still alive
2928    (if (and process-buffer (buffer-name process-buffer))
2929        (save-excursion
2930          (set-buffer process-buffer)
2931          (send-string
2932           process
2933           (concat (singular-run-hook-with-arg-and-value
2934                    singular-pre-input-filter-functions string)
2935                   "\n"))))))
2936
2937(defun singular-input-filter (process string-or-pos)
2938  "Insert/update input from user in buffer associated to PROCESS.
2939Inserts STRING-OR-POS followed by a newline at process mark if it is a
2940string.
2941Assumes that the input is already inserted and that it is placed
2942between process mark and STRING-OR-POS if the latter is a position.
2943Inserts a newline after STRING-OR-POS.
2944
2945Takes care off:
2946- current buffer as well as point and restriction in buffer associated
2947  with process, even against non-local exits.
2948Updates:
2949- process mark;
2950- current and last sections;
2951- simple sections;
2952- mode line.
2953
2954Runs the hooks on `singular-pre-input-filter-functions' and
2955`singular-post-input-filter-functions'.
2956
2957For a more detailed descriptions of the input filter, the markers it
2958sets, and input filter functions refer to the section \"Some lengthy
2959notes on input and output\" in singular.el."
2960  (let ((process-buffer (process-buffer process)))
2961
2962    ;; check whether buffer is still alive
2963    (if (and process-buffer (buffer-name process-buffer))
2964        (let ((old-buffer (current-buffer))
2965              (old-pmark (marker-position (process-mark process)))
2966              old-point old-point-min old-point-max)
2967          (unwind-protect
2968              (let (simple-sec-start)
2969                (set-buffer process-buffer)
2970                ;; the following lines are not protected since the
2971                ;; unwind-forms refer the variables being set here
2972                (setq old-point (point-marker)
2973                      old-point-min (point-min-marker)
2974                      old-point-max (point-max-marker)
2975
2976                ;; get end of last simple section (equals start of
2977                ;; current)
2978                      simple-sec-start (singular-simple-sec-last-end-position))
2979
2980                ;; prepare for insertion
2981                (widen)
2982                (set-marker-insertion-type old-point t)
2983                (set-marker-insertion-type old-point-max t)
2984
2985                ;; insert string at process mark and advance process
2986                ;; mark after insertion.  If it not a string simply
2987                ;; jump to desired position and insrt a newline.
2988                (if (stringp string-or-pos)
2989                    (progn
2990                      (goto-char old-pmark)
2991                      (insert string-or-pos))
2992                  (goto-char string-or-pos))
2993                (insert ?\n)
2994                (set-marker (process-mark process) (point))
2995
2996                ;; create new simple section and update section markers
2997                (cond
2998                 ((eq (singular-simple-sec-create 'input (point)) 'empty)
2999                  nil)
3000                 ;; a new simple section has been created ...
3001                 ((null (marker-position singular-current-input-section-start))
3002                  ;; ... and even a new input section has been created!
3003                  (set-marker singular-current-input-section-start
3004                              simple-sec-start)
3005                  (set-marker singular-last-output-section-start
3006                              singular-current-output-section-start)
3007                  (set-marker singular-current-output-section-start nil)))
3008
3009                ;; run post-output hooks and force mode-line update
3010                (run-hook-with-args 'singular-post-input-filter-functions
3011                                    old-pmark (point)))
3012
3013            ;; restore buffer, restrictions and point
3014            (narrow-to-region old-point-min old-point-max)
3015            (set-marker old-point-min nil)
3016            (set-marker old-point-max nil)
3017            (goto-char old-point)
3018            (set-marker old-point nil)
3019            (set-buffer old-buffer))))))
3020           
3021(defun singular-get-old-input (get-section)
3022  "Retrieve old input.
3023Retrivies from beginning of current section to point if GET-SECTION is
3024non-nil, otherwise on a per-line base."
3025  (if get-section
3026      ;; get input from input section
3027      (let ((section (singular-section-at (point))))
3028        (if (eq (singular-section-type section) 'input)
3029            (setq old-input (singular-input-section-to-string section (point)))
3030          (error "Not on an input section")))
3031    ;; get input from line
3032    (save-excursion
3033      (beginning-of-line)
3034      (singular-prompt-skip-forward)
3035      (let ((old-point (point)))
3036        (end-of-line)
3037        (buffer-substring old-point (point))))))
3038
3039(defun singular-send-or-copy-input (send-full-section)
3040  "Send input from current buffer to associated process.
3041NOT READY[old input copying, demo mode,
3042          eol-on-send, history, SEND-FULL-SECTION]!"
3043  (interactive "P")
3044
3045  (let ((process (get-buffer-process (current-buffer)))
3046        pmark)
3047    ;; some checks and initializations
3048    (or process (error "Current buffer has no process"))
3049    (setq pmark (marker-position (process-mark process)))
3050
3051    (cond
3052     (;; check for demo mode and show next chunk if necessary
3053      (and singular-demo-mode
3054          (eq (point) pmark)
3055          (eq pmark (point-max)))
3056      (singular-demo-show-next-chunk))
3057
3058     (;; get old input
3059      (< (point) pmark)
3060      (let ((old-input (singular-get-old-input send-full-section)))
3061        (goto-char pmark)
3062        (insert old-input)))
3063
3064     (;; send input from pmark to point after doing history expansion
3065      t
3066      ;; I don't know if this is the right point to insert the message
3067      ;; print message if demo mode is active
3068      (and singular-demo-mode
3069           singular-demo-print-messages
3070           (message "Hit RET to continue demo"))
3071
3072      ;; go to desired position.  NOT READY.
3073      ;(if singular-eol-on-send (end-of-line))
3074      ;(if send-full-section (goto-char (point-max)))
3075
3076      (let* ((input (buffer-substring pmark (point))))
3077        ;; insert string into history
3078        (singular-history-insert input)
3079        ;; send string to process
3080        (singular-send-string process input)
3081        ;; "insert" it into buffer
3082        (singular-input-filter process (point)))))))
3083;;}}}
3084
3085;;{{{ Receiving output
3086(defvar singular-pre-output-filter-functions nil
3087  "Functions to call before output is inserted into the buffer.
3088These functions get one argument, a string containing the text sent
3089from process.  The functions should return either nil or a string.
3090In the latter case the returned string replaces the string sent from
3091process.
3092
3093This is a buffer-local variable, not a buffer-local hook!
3094
3095`singular-run-hook-with-arg-and-value' is used to run the functions in
3096this list.")
3097
3098(defvar singular-post-output-filter-functions nil
3099  "Functions to call after output is inserted into the buffer.
3100These functions get three arguments BEG, END, and SIMPLE-SEC-START.
3101The region between BEG and END is what has been inserted into the
3102buffer.
3103SIMPLE-SEC-START is the start of the simple section which has been
3104created on insertion or nil if no simple section has been created.
3105The functions may assume that no narrowing is in effect and may change
3106point at will.
3107
3108This hook is buffer-local.")
3109
3110(defvar singular-current-output-section-start nil
3111  "Marker to the start of the current output section.
3112This marker points nowhere on startup or if there is no current output
3113section.
3114
3115This variable is buffer-local.")
3116
3117(defvar singular-last-output-section-start nil
3118  "Marker to the start of the last output section.
3119This marker points nowhere on startup.
3120
3121This variable is buffer-local.")
3122
3123(defun singular-output-filter-init (pos)
3124  "Initialize all variables concerning output including process mark.
3125Set process mark to POS."
3126
3127  ;; localize variables not yet localized in `singular-interactive-mode'
3128  (make-local-variable 'singular-current-output-section-start)
3129  (make-local-variable 'singular-last-output-section-start)
3130
3131  ;; initialize markers
3132  (if (not (markerp singular-current-output-section-start))
3133      (setq singular-current-output-section-start (make-marker)))
3134  (if (not (markerp singular-last-output-section-start))
3135      (setq singular-last-output-section-start (make-marker)))
3136  (set-marker (singular-process-mark) pos))
3137
3138(defun singular-output-filter (process string)
3139  "Insert STRING containing output from PROCESS into its associated buffer.
3140Takes care off:
3141- current buffer as well as point and restriction in buffer associated
3142  with process, even against non-local exits.
3143Updates:
3144- process mark;
3145- current and last sections;
3146- simple sections;
3147- mode line.
3148Runs the hooks on `singular-pre-output-filter-functions' and
3149`singular-post-output-filter-functions'.
3150
3151For a more detailed descriptions of the output filter, the markers it
3152sets, and output filter functions refer to the section \"Some lengthy
3153notes on input and output\" in singular.el."
3154  (let ((process-buffer (process-buffer process)))
3155
3156    ;; check whether buffer is still alive
3157    (if (and process-buffer (buffer-name process-buffer))
3158        (let ((old-buffer (current-buffer))
3159              (old-pmark (marker-position (process-mark process)))
3160              old-point old-point-min old-point-max)
3161          (unwind-protect
3162              (let (simple-sec-start)
3163                (set-buffer process-buffer)
3164                ;; the following lines are not protected since the
3165                ;; unwind-forms refer the variables being set here
3166                (setq old-point (point-marker)
3167                      old-point-min (point-min-marker)
3168                      old-point-max (point-max-marker)
3169
3170                ;; get end of last simple section (equals start of
3171                ;; current)
3172                      simple-sec-start (singular-simple-sec-last-end-position)
3173
3174                ;; get string to insert
3175                      string (singular-run-hook-with-arg-and-value
3176                              singular-pre-output-filter-functions
3177                              string))
3178
3179                ;; prepare for insertion
3180                (widen)
3181                (set-marker-insertion-type old-point t)
3182                (set-marker-insertion-type old-point-max t)
3183
3184                ;; insert string at process mark and advance process
3185                ;; mark after insertion
3186                (goto-char old-pmark)
3187                (insert string)
3188                (set-marker (process-mark process) (point))
3189
3190                ;; create new simple section and update section markers
3191                (cond
3192                 ((eq (singular-simple-sec-create 'output (point)) 'empty)
3193                  (setq simple-sec-start nil))
3194                 ;; a new simple section has been created ...
3195                 ((null (marker-position singular-current-output-section-start))
3196                  ;; ... and even a new output section has been created!
3197                  (set-marker singular-current-output-section-start
3198                              simple-sec-start)
3199                  (set-marker singular-last-input-section-start
3200                              singular-current-input-section-start)
3201                  (set-marker singular-current-input-section-start nil)))
3202
3203                ;; run post-output hooks and force mode-line update
3204                (run-hook-with-args 'singular-post-output-filter-functions
3205                                    old-pmark (point) simple-sec-start)
3206                (force-mode-line-update))
3207
3208            ;; restore buffer, restrictions and point
3209            (narrow-to-region old-point-min old-point-max)
3210            (set-marker old-point-min nil)
3211            (set-marker old-point-max nil)
3212            (goto-char old-point)
3213            (set-marker old-point nil)
3214            (set-buffer old-buffer))))))
3215;;}}}
3216
3217;;{{{ Singular interactive mode
3218(defun singular-interactive-mode ()
3219  "Major mode for interacting with Singular.
3220
3221NOT READY [how to send input]!
3222
3223NOT READY [multiple Singulars]!
3224
3225\\{singular-interactive-mode-map}
3226Customization: Entry to this mode runs the hooks on `comint-mode-hook'
3227and `singular-interactive-mode-hook' \(in that order).
3228
3229NOT READY [much more to come.  See shell.el.]!"
3230  (interactive)
3231
3232  ;; uh-oh, we have to set `comint-input-ring-size' before we call
3233  ;; `comint-mode'
3234  (singular-history-init)
3235
3236  ;; run comint mode and do basic mode setup
3237  (let (comint-mode-hook)
3238    (comint-mode))
3239  (setq major-mode 'singular-interactive-mode)
3240  (setq mode-name "Singular Interaction")
3241
3242  ;; key bindings, syntax tables and menus
3243  (singular-interactive-mode-map-init)
3244  (singular-mode-syntax-table-init)
3245  (singular-interactive-mode-menu-init)
3246
3247  (setq comment-start "// ")
3248  (setq comment-start-skip "// *")
3249  (setq comment-end "")
3250
3251;  (singular-prompt-init)
3252
3253  ;; initialize singular demo mode, input and output filters
3254  (singular-demo-mode-init)
3255  (make-local-variable 'singular-pre-input-filter-functions)
3256  (make-local-hook 'singular-post-input-filter-functions)
3257  (make-local-variable 'singular-pre-output-filter-functions)
3258  (make-local-hook 'singular-post-output-filter-functions)
3259
3260  ;; folding sections
3261  (singular-folding-init)
3262
3263  ;; debugging filters
3264  (singular-debug 'interactive-filter (singular-debug-filter-init))
3265
3266  (singular-scan-header-init)
3267  (singular-help-init)
3268  (singular-completion-init)
3269
3270  ;; other input or output filters
3271  (add-hook 'singular-post-output-filter-functions
3272            'singular-remove-prompt-filter nil t)
3273
3274  ;; Emacs Font Lock mode initialization
3275  (cond
3276   ;; Emacs
3277   ((eq singular-emacs-flavor 'emacs)
3278    (singular-interactive-font-lock-init)))
3279
3280  (run-hooks 'singular-interactive-mode-hook))
3281;;}}}
3282
3283;;{{{ Starting singular
3284(defvar singular-start-file "~/.emacs_singularrc"
3285  "Name of start-up file to pass to Singular.
3286If the file named by this variable exists it is given as initial input
3287to any Singular process being started.  Note that this may lose due to
3288a timing error if Singular discards input when it starts up.")
3289
3290(defvar singular-default-executable "Singular"
3291  "Default name of Singular executable.
3292Used by `singular' when new Singular processes are started.")
3293
3294(defvar singular-default-name "singular"
3295  "Default process name for Singular process.
3296Used by `singular' when new Singular processes are started.")
3297
3298(defvar singular-default-switches '("--emacs")
3299  "Default switches for Singular processes.
3300Used by `singular' when new Singular processes are started.")
3301
3302(defun singular-exit-sentinel (process message)
3303 "Clean up after termination of Singular.
3304Writes back input ring after regular termination of Singular if
3305process buffer is still alive."
3306  (save-excursion
3307    (singular-debug 'interactive
3308                    (message "Sentinel: %s" (substring message 0 -1)))
3309    ;; exit demo mode if necessary
3310    (singular-demo-exit)
3311    (if (string-match "finished\\|exited" message)
3312        (let ((process-buffer (process-buffer process)))
3313          (if (and process-buffer
3314                   (buffer-name process-buffer)
3315                   (set-buffer process-buffer))
3316              ;; write back history
3317              (singular-history-write))))))
3318
3319(defun singular-exec (buffer name executable start-file switches)
3320  "Start a new Singular process NAME in BUFFER, running EXECUTABLE.
3321EXECUTABLE should be a string denoting an executable program.
3322SWITCHES should be a list of strings that are passed as command line
3323switches.  START-FILE should be the name of a file which contents is
3324sent to the process.
3325
3326Deletes any old processes running in that buffer.
3327Moves point to the end of BUFFER.
3328Initializes all important markers and the simple sections.
3329Runs the hooks on `singular-exec-hook'.
3330Returns BUFFER."
3331  (let ((old-buffer (current-buffer)))
3332    (unwind-protect
3333        (progn
3334          (set-buffer buffer)
3335
3336          ;; delete any old processes
3337          (let ((process (get-buffer-process buffer)))
3338            (if process (delete-process process)))
3339
3340          ;; create new process
3341          (singular-debug 'interactive (message "Starting new Singular"))
3342          (let ((process (comint-exec-1 name buffer executable switches)))
3343
3344            ;; set process filter and sentinel
3345            (set-process-filter process 'singular-output-filter)
3346            (set-process-sentinel process 'singular-exit-sentinel)
3347            (make-local-variable 'comint-ptyp)
3348            (setq comint-ptyp process-connection-type) ; T if pty, NIL if pipe.
3349
3350            ;; go to the end of the buffer, initialize I/O and simple
3351            ;; sections
3352            (goto-char (point-max))
3353            (singular-input-filter-init (point))
3354            (singular-output-filter-init (point))
3355            (singular-simple-sec-init (point))
3356
3357            ;; feed process with start file and read input ring.  Take
3358            ;; care about the undo information.
3359            (if start-file
3360                (let ((buffer-undo-list t) start-string)
3361                  (singular-debug 'interactive (message "Feeding start file"))
3362                  (sleep-for 1)                 ; try to avoid timing errors
3363                  (insert-file-contents start-file)
3364                  (setq start-string (buffer-substring (point) (point-max)))
3365                  (delete-region (point) (point-max))
3366                  (send-string process start-string)))
3367
3368            ;; read history if present
3369            (singular-history-read)
3370
3371            ;; execute hooks
3372            (run-hooks 'singular-exec-hook))
3373         
3374          buffer)
3375      ;; this code is unwide-protected
3376      (set-buffer old-buffer))))
3377
3378;; Note:
3379;;
3380;; In contrast to shell.el, `singular' does not run
3381;; `singular-interactive-mode' every time a new Singular process is
3382;; started, but only when a new buffer is created.  This behaviour seems
3383;; more intuitive w.r.t. local variables and hooks.
3384
3385(defun singular (&optional executable name switches)
3386  "Run an inferior Singular process, with I/O through an Emacs buffer.
3387
3388NOT READY [arguments, default values, and interactive use]!
3389
3390If buffer exists but Singular is not running, starts new Singular.
3391If buffer exists and Singular is running, just switches to buffer.
3392If a file `~/.emacs_singularrc' exists, it is given as initial input.
3393Note that this may lose due to a timing error if Singular discards
3394input when it starts up.
3395
3396If a new buffer is created it is put in Singular interactive mode,
3397giving commands for sending input and handling ouput of Singular.  See
3398`singular-interactive-mode'.
3399
3400Every time `singular' starts a new Singular process it runs the hooks
3401on `singular-exec-hook'.
3402
3403Type \\[describe-mode] in the Singular buffer for a list of commands."
3404  ;; handle interactive calls
3405  (interactive (list singular-default-executable
3406                     singular-default-name
3407                     singular-default-switches))
3408
3409  (let* (;; get default values for optional arguments
3410         (executable (or executable singular-default-executable))
3411         (name (or name singular-default-name))
3412         (switches (or switches singular-default-switches))
3413
3414         (buffer-name (singular-process-name-to-buffer-name name))
3415         ;; buffer associated with Singular, nil if there is none
3416         (buffer (get-buffer buffer-name)))
3417
3418    (if (not buffer)
3419        (progn
3420          ;; create new buffer and call `singular-interactive-mode'
3421          (singular-debug 'interactive (message "Creating new buffer"))
3422          (setq buffer (get-buffer-create buffer-name))
3423          (set-buffer buffer)
3424
3425          (singular-debug 'interactive (message "Calling `singular-interactive-mode'"))
3426          (singular-interactive-mode)))
3427
3428    (if (not (comint-check-proc buffer))
3429        ;; create new process if there is none
3430        (singular-exec buffer name executable
3431                       (if (file-exists-p singular-start-file)
3432                           singular-start-file)
3433                       switches))
3434
3435    ;; pop to buffer
3436    (singular-debug 'interactive (message "Calling `pop-to-buffer'"))
3437    (pop-to-buffer buffer t)))
3438
3439;; for convenience only
3440(defalias 'Singular 'singular)
3441
3442(defun singular-generate-new-buffer-name (name)
3443  "Generate a unique buffer name for a singular interactive buffer.
3444The string NAME is the desired name for the singular interactive
3445buffer, without surrounding stars.
3446The string returned is surrounded by stars.
3447
3448If no buffer with name \"*NAME*\" exists, return \"*NAME*\".
3449Otherwise check for buffer called \"*NAME<n>*\" where n is a
3450increasing number and return \"*NAME<n>*\" if no such buffer
3451exists."
3452  (let ((new-name (singular-process-name-to-buffer-name name)) 
3453        (count 2))
3454    (while (get-buffer new-name)
3455      (setq new-name (singular-process-name-to-buffer-name
3456                      (concat name "<" (format "%d" count) ">")))
3457      (setq count (1+ count)))
3458    new-name))
3459 
3460(defun singular-other (file) 
3461  "Start a new Singular, different to the default Singular.
3462FILE is a Singular executable.
3463
3464Asks in the minibuffer for a buffer-name and for Singular options.
3465Calls `singular' with the appropriate arguments."
3466  (interactive "fSingular executable: ")
3467  ;; NOT READY [code]
3468  (let ((name (singular-generate-new-buffer-name 
3469               (downcase (file-name-nondirectory file))))
3470        (switches "")
3471        temp)
3472
3473    ;; Read buffer name from minibuffer at strip surrounding stars
3474    ;; NOT READY: This code is not very beautyful.
3475    (let ((buffer-exists t)
3476          (new-name name))
3477      (while buffer-exists
3478        (setq new-name (read-from-minibuffer "Singular buffer name: " name))
3479        (if (get-buffer new-name)
3480            (progn 
3481              (message "This buffer already exists.")
3482              (sleep-for 1))
3483          (setq buffer-exists nil)
3484          (setq name new-name))))
3485       
3486   
3487    (if (string-match "^\\*\\(.*\\)\\*$" name)
3488        (setq name (substring name (match-beginning 1) (match-end 1))))
3489
3490    ;; make one string of options from list of default options
3491    (setq temp singular-default-switches)
3492    (while temp
3493      (setq switches (concat switches (car temp) " "))
3494      (setq temp (cdr temp)))
3495    ;; in minibuffer omit display of option "-t "
3496    (setq switches (read-from-minibuffer "Singular options: " 
3497                                         (replace-in-string switches "-t " "")))
3498
3499    ;; make list of strings of switch-string
3500    (setq temp '("-t"))
3501    (while (string-match "-[^ ]*" switches)
3502      (setq temp (append temp (list (substring switches (match-beginning 0) 
3503                                               (match-end 0)))))
3504      (setq switches (substring switches (match-end 0) nil)))
3505    (setq switches temp)
3506
3507    (singular file name switches)))
3508;;}}}
3509;;}}}
3510
3511(provide 'singular)
3512
3513;;; Local Variables:
3514;;; fill-column: 75
3515;;; End:
3516
3517;;; singular.el ends here.
Note: See TracBrowser for help on using the repository browser.