source: git/emacs/singular.el @ caa8e6

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