source: git/emacs/singular.el @ eedab2

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