source: git/emacs/singular.el @ fffd22

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