source: git/emacs/singular.el @ 2690cd

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