source: git/emacs/singular.el @ a70441f

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