source: git/emacs/singular.el @ 6a9b5a

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