source: git/emacs/singular.el @ 4bde6b

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