Changes http://wiki.axiom-developer.org/AxiomEmacsMode/diff
--
??changed:
\begin{document}
-\title{\$SPAD/src/emacs/axiommode.el\\Version 01-10-2007}
+\title{\$SPAD/src/emacs/axiommode.el\\Version 04-10-2007}
\author{Jay Belanger, Fran\c{c}ois Maltey, Martin Rubey and Cliff Yapp}
??changed:
\item[C-return] writes the front item of the kill ring into a temporary file,
- and then [[)re]]ads that file.
+ and then [[)read]]s that file.
+\item[tab] completes file names after a system command like [[)read]].
\end{description}
++added:
+There remain a few things to do:
+\begin{itemize}
+\item clean up the initialisation code following the elisp reference manual
+ [[Major Mode Conventions]].
+\item enable [[)undo]], binding it to [[C-_]], possibly as follows:
+ \begin{itemize}
+ \item locally, undo input,
+ \item if at the last prompt, and there is no input, [[C-_]] should issue an
+ undo and delete the last input output combination. This probably means
+ that we need to track the prompt, doesn't it?
+ \end{itemize}
+\item use the [[)history]] facility to emulate worksheets.
+\end{itemize}
+
\section{Structuring the File}
--removed:
<<TerminalMode>>
-<<EAxiomMode>>
-<<PamphletMode>>
@
--removed:
;; to be defined before being set.
-(defvar axiom-localpaths) ;; Used when searching the system path
(defvar axiom-args "-noclef") ;; Ignored when AXIOMsys is used.
(defvar axiom-after-output-wait 100) ;; time to wait for axiom to repond prompt
++added:
+
+@
+
+<<axiom-mode-map>>=
(defvar axiom-mode-map (copy-keymap comint-mode-map)
"local key map for Axiom terminal mode")
++added:
+(define-key axiom-mode-map [(meta k)] 'axiom-copy-to-clipboard)
+(define-key axiom-mode-map [(ctrl return)] 'axiom-yank)
+(define-key axiom-mode-map [(meta up)] 'axiom-previous-input)
+(define-key axiom-mode-map [(meta down)] 'axiom-next-input)
+(define-key axiom-mode-map [left] 'axiom-backward-char)
+(define-key axiom-mode-map [right] 'axiom-forward-char)
+(define-key axiom-mode-map [(shift up)] 'axiom-paint-previous-line)
+(define-key axiom-mode-map [(shift down)] 'axiom-paint-next-line)
+(define-key axiom-mode-map [(shift left)] 'axiom-paint-previous-char)
+(define-key axiom-mode-map [(shift right)] 'axiom-paint-next-char)
+;; doesn't work in terminal
+;; (define-key axiom-mode-map [return] 'axiom-eval)
+(define-key axiom-mode-map "\C-m" 'axiom-eval)
+;; doesn't work in terminal
+;;(define-key axiom-mode-map [(meta return)] 'axiom-eval-append)
+(define-key axiom-mode-map "\M-\C-m" 'axiom-eval-append)
+(define-key axiom-mode-map "\t" 'axiom-dynamic-complete)
@
@
++added:
+
+I had [[(define-key axiom-mode-map [(tab)] 'axiom-dynamic-complete)]]
+here before, but that did not work entirely: hitting tab twice inserted a tab!
+
+In cases where we are overriding comint key bindings, we apparently need to do
+this after [[use-local-map]], which is issued in [[axiom-run]]. However, I do
+not quite understand why we do that, in fact.
??changed:
utility. The third line supplies the REGEXP syntax to identify the Axiom
-prompts. The forth line provides a command that will be used on startup to
-disable the rather large volumes of messages Axiom can generate when loading
-libraries.
+prompts.
??changed:
;; Utility variables
-(defvar axiom-mode-hook nil)
-(defvar axiom-process nil)
+(defcustom axiom-mode-hook '()
+ "*Hook for customising Axiom mode."
+ :type 'hook
+ :group 'axiom)
+
(defvar axiom-system-command nil)
??changed:
(defvar axiom-system-command nil)
-(defvar axiom-end-of-input 0)
+(defvar axiom-end-of-input 0)
+(defvar axiom-current-input "")
+(defvar axiom-paint-face 'axiom-paint-lightblue)
--removed:
-(defun axiom-yank ()
- "puts the front item of the kill ring into a temporary file and makes axiom
)read it"
- (interactive)
- (let* ((tmp-file (make-temp-file "axiom" nil ".input"))
- (end (progn (goto-char (point-max))
- (point))))
- (write-region (car kill-ring-yank-pointer) nil tmp-file)
- (delete-region (axiom-previous-prompt) end)
- (comint-set-process-mark)
- (insert (concat ")re " tmp-file))
- (axiom-eval)
- (delete-file tmp-file)))
-
-(define-key axiom-mode-map [(ctrl return)] 'axiom-yank)
-
(defun axiom-output? (position)
??changed:
(put-text-property begin end 'rear-nonsticky t)
- (put-text-property begin end 'read-only t)
-))
+ (put-text-property begin end 'read-only t)))
??changed:
(put-text-property begin end 'rear-nonsticky t); otherwise I cannot append
text
- (put-text-property begin end 'read-only t)
-))
+ (put-text-property begin end 'read-only t)))
+
@
--removed:
<<UtilityFunctions>>=
-
(defun axiom-output-filter (str)
(sit-for 0 axiom-after-output-wait))
++added:
+
@
??changed:
-<<get-axiom-command>>=
+<<StartingAxiom>>=
(defun axiom-get-command ()
??changed:
"Searches the local system PATH variable for the axiom binary"
- (setq axiom-localpaths exec-path)
- (while axiom-localpaths
- (when (file-executable-p (concat (car axiom-localpaths) "/axiom"))
- (setq axiom-localpaths nil)
- (setq axiom-command "axiom"))
- (setq axiom-localpaths (cdr axiom-localpaths)))
- ;; If we come up empty, default to AXIOMsys
- (unless (equal axiom-command "axiom")
- (setq axiom-command "AXIOMsys")))
+ (let ((axiom-localpaths exec-path))
+ (while axiom-localpaths
+ (when (file-executable-p (concat (car axiom-localpaths) "/axiom"))
+ (setq axiom-localpaths nil)
+ (setq axiom-command "axiom"))
+ (setq axiom-localpaths (cdr axiom-localpaths)))
+ ;; If we come up empty, default to AXIOMsys
+ (unless (equal axiom-command "axiom")
+ (setq axiom-command "AXIOMsys"))))
@
--removed:
-These are the two key functions for starting Axiom. We will assemble them in
-the proper order here:
-
-Unfortunately the default input prompt is a bit messy, and we must define a
-rather crude function to clean this up:
-
-<<initialprompthack>>=
-(defun axiom-clean-up-prompt ()
- (let ((inhibit-read-only t)
- (point-after-last-message nil)
- (point-before-current-marker nil))
- (goto-char (point-min))
- (search-forward "(1) ->")
- (search-backward "(1) ->")
- (setq point-after-last-message (point-marker))
- (goto-char (point-max))
- (re-search-backward "([0-9]+) -> ")
- (setq point-before-current-marker (point-marker))
- (if (not (eq point-after-last-message point-before-current-marker))
-[9 more lines...]
I really would like to have a behaviour like that of shell mode, i.e.,
--removed:
-<<core-startup-functions>>=
-
-<<initialprompthack>>
-<<axiom-cleanup>>
-
-(defun axiom-comint-run ()
- "Run PROGRAM in a comint buffer with ARGS and switch to it."
- (switch-to-buffer (make-comint "axiom" axiom-command nil axiom-args)))
-
-(defun axiom-run()
- "Run Axiom in a buffer."
- ;; Get the command to use
- (axiom-get-command)
- ;; Run that command and switch to the new buffer
- (axiom-comint-run)
- ;; and identify the process as well:
- (setq axiom-process (get-buffer-process (current-buffer)))
- ;; We need a custom wait condition for the first case, since two input
- ;; prompts appear when "axiom" is used to as the startup command.
-[22 more lines...]
\section{Terminal Mode}
??changed:
\subsection{Axiom-Mode}
-
-Now that we can start Axiom and communicate with it, we need to actually define
-our Emacs Terminal mode. We will call this mode ``axiom-mode'' since it is the
-most basic of the modes we will define, at least from a user interaction
-standpoint.
-
-Because of cases where we wish to start axiom while in another buffer, it is
-not practical to make axiom-mode an actual derived mode in and of itself. We
-will define a function which sets up the comint environment with Axiom running
-and a custom keymap. A second call of axiom switch to the main axiom buffer.
-This is the main axiom-mode fuction.
+This function is run only when issuing [[M-x axiom]]. Very likely, as soon as
+I allow visiting saved history, this needs to change.
+
+<<StartingAxiom>>=
+(defun axiom-run()
+ "Run Axiom in a buffer."
+ ;; Get the command to use
+ (axiom-get-command)
+ ;; Run that command and switch to the new buffer
+ (switch-to-buffer (make-comint "axiom" axiom-command nil axiom-args)))
+@
+
+<<StartingAxiom>>=
+(defun axiom-wait-start (proc before-banner delete-banner)
+ "Wait until the prompts have appeared. Find the end of the banner after
+ before-banner. If delete-banner is nil, make the banner write-protected,
+ otherwise delete it."
+ (comint-goto-process-mark)
+ ;; wait until the prompt(s) have appeared
+ (cond ((equal axiom-command "axiom")
+ (while (not (re-search-backward "(1) -> [^ ](1) ->" before-banner t))
+ (accept-process-output proc)))
+ ((equal axiom-command "AXIOMsys")
+ (while (not (re-search-backward "(1) -> " before-banner t))
+ (accept-process-output proc))))
+
+ (if delete-banner
+ (delete-region before-banner (point))
+ (progn
+ (remove-text-properties before-banner (point) '(face nil rear-nonsticky
nil))
+ (put-text-property before-banner (point) 'front-sticky t)
+ (put-text-property before-banner (point) 'read-only t)))
+
+ (goto-char before-banner)
+ (search-forward "(1) ->")
+ (search-backward "(1) ->")
+
+ (let ((inhibit-read-only t)
+ (point-after-last-message nil)
+ (point-before-current-marker nil))
+
+ (setq point-after-last-message (point-marker))
+ (goto-char (point-max))
+ (re-search-backward "([0-9]+) -> ")
+ (setq point-before-current-marker (point-marker))
+ (unless (eq point-after-last-message point-before-current-marker)
+ (delete-region point-after-last-message point-before-current-marker))
+ (goto-char (point-max))
+ ;; We need to explicitly write protect the first prompt, since it
+ ;; is outside the normal write protect mode used for subsequent
+ ;; output:
+ (axiom-make-prompt (- (point) 7) (point))))
+
+@
??changed:
<<defineaxiom-mode>>=
-(define-derived-mode axiom-mode comint-mode "AXIOM")
-
-(defun axiom-buffer ()
- "Return the buffer in which the Axiom process is running, nil
-otherwise"
- (and (processp axiom-process) (process-buffer axiom-process)))
-
-
-(defun axiom ()
- "Run axiom in a terminal environment"
- (interactive)
- (if (and (processp axiom-process)
- (eq (process-status axiom-process) 'run))
- (switch-to-buffer (axiom-buffer))
- (axiom-mode-new-axiom-process))
- (axiom-mode))
-
-;; If there is a running axiom process switch to this axiom buffer
-;; In the other case clean buffer and process variables and
-[25 more lines...]
+(put 'axiom-mode 'mode-class 'special)
+
+(define-derived-mode axiom-mode comint-mode "AXIOM"
+ ""
+ (let ((proc (get-buffer-process (current-buffer))))
+ (if proc
+ (axiom-wait-start proc (point-min) nil)
+ (let ((before-banner (point-max)))
+ ;; if proc is nil, we assume that we have visited a file.
+ ;; I guess I should make sure that axiom is running otherwise, but I do
+ ;; not know how.
+ (axiom-get-command)
+ ;; inserst a newline so that the banner is easier to find
+ (goto-char before-banner)
+ (insert "\n")
+ ;; Run that command
+ (make-comint-in-buffer "axiom"
+ (current-buffer)
+ axiom-command nil axiom-args)
+ (axiom-wait-start (get-buffer-process (current-buffer))
+ (1+ before-banner) t)))
+
+ (make-local-variable 'axiom-system-command)
+ (setq axiom-system-command nil)
+ (make-local-variable 'axiom-end-of-input)
+ (setq axiom-end-of-input 0)
+ (make-local-variable 'axiom-current-input)
+ (setq axiom-current-input "")
+ (make-local-variable 'axiom-paint-face)
+ (setq axiom-paint-face 'axiom-paint-lightblue)
+
+ ;; we want this hook to be local lest we conflict with other buffers in
comint
+ ;; or shell mode
+ (add-hook 'comint-output-filter-functions 'axiom-output-filter nil t)
+
+ (setq buffer-offer-save t)
+ (add-hook 'after-save-hook 'axiom-save-history nil t)
+ (add-hook 'kill-buffer-query-functions 'axiom-query-kill)
+
;; Next, we turn on some key bindings for our new mode:
??changed:
(insert ")se me au of")
- (axiom-eval)))
-
+ (axiom-normal-eval)
+ (unless proc
+ (axiom-wait-for-output)
+ (insert (concat ")history )restore " (buffer-file-name)))
+ (axiom-normal-eval))
+ (set-buffer-modified-p nil)))
+
+(defun axiom ()
+ "Run axiom in a terminal environment"
+ (interactive)
+ (if (not (comint-check-proc "*axiom*"))
+ (progn (axiom-run)
+ (axiom-mode))
+ (pop-to-buffer "*axiom*")))
+
+(provide 'axiom)
@
++added:
+\subsection{Saving a session}
+
+Axiom provides [[)history )save]] and [[)history )restore]] to save and restore
+a session. Additionally, we save the text of the buffer, so after reloading it
+looks and behaves really the same.
+
+<<axiom-maybe-save>>=
+(defun axiom-query-kill ()
+ (if (eq major-mode 'axiom-mode)
+ (or (not (buffer-modified-p))
+ (yes-or-no-p (format "Buffer %s modified; kill anyway? "
+ (buffer-name))))
+ t))
+@
+
+
+This is going to be called just before the buffer is saved into the filename
+supplied by the user. Thus, the following files will be created:
+\begin{itemize}
+\item [[foo]]
+\item [[foo.input]]
+\item [[foo.axh/index.KAF]]
+\end{itemize}
+I'd rather have everything in one directory, but maybe that's not worth the
+effort. Visiting [[foo]] should make emacs restore the history.
+
+<<axiom-maybe-save>>=
+(defun axiom-save-history ()
+ (comint-goto-process-mark)
+ (insert (concat ")history )save " (buffer-file-name)))
+ (axiom-normal-eval)
+ (set-buffer-modified-p nil))
+
+@
+
\subsection{Moving in Axiom Mode}
??changed:
-<<axiommodekeyboardmap-axiom-previous-input>>=
+<<axiom-previous-input>>=
(defun axiom-previous-prompt ()
??changed:
the regular expression to find a prompt, rather than the text-property, for
-axiom-reset."
+axiom-reset. ???It seems that this is not quite true???"
(let ((found?))
??changed:
-<<axiommodekeyboardmap-axiom-next-input>>=
+<<axiom-next-input>>=
(defun axiom-next-input ()
??changed:
-<<axiommodekeyboardmap-axiom-scroll-input>>=
+<<axiom-scroll-input>>=
(defun axiom-end-of-input ()
??changed:
(interactive "p")
- (when (axiom-prompt? (1- (process-mark axiom-process)))
+ (when (axiom-prompt? (1- (process-mark (get-buffer-process
(current-buffer)))))
(axiom-previous-prompt)
??changed:
(interactive "p")
- (when (axiom-prompt? (1- (process-mark axiom-process)))
+ (when (axiom-prompt? (1- (process-mark (get-buffer-process
(current-buffer)))))
(axiom-previous-prompt)
??changed:
-<<axiommodekeyboardmap-axiom-backward-char>>=
+<<axiom-backward-char>>=
(defun axiom-backward-char (&optional arg)
??changed:
-<<axiommodekeyboardmap-axiom-forward-char>>=
+<<axiom-forward-char>>=
(defun axiom-forward-char (&optional arg)
--removed:
-In order to use these functions, keymap bindings must be defined for Emacs:
-
-<<axiom-mode-map>>=
-(define-key axiom-mode-map [(meta up)] 'axiom-previous-input)
-(define-key axiom-mode-map [(meta down)] 'axiom-next-input)
-(define-key axiom-mode-map [left] 'axiom-backward-char)
-(define-key axiom-mode-map [right] 'axiom-forward-char)
-@
-
-In cases where we are overriding comint key bindings, we apparently need to do
-this after the use-local-map command. This code block appears after that
-command in the terminal mode definition.
-
-<<axiom-mode-map-redefine>>=
-(substitute-key-definition 'comint-previous-input 'axiom-scroll-previous-input
axiom-mode-map)
-(substitute-key-definition 'comint-next-input 'axiom-scroll-next-input
axiom-mode-map)
-@
-
\subsection{Reset}
??changed:
(defun axiom-resync-directory ()
- "Send )sys pwd to the axiom process, parse the result, cd to it and clean up
- output. Assumes that we are just after a prompt."
- (let* ((inhibit-read-only t)
- (begin (progn (forward-line 0) (point)))
- (end (progn (end-of-line) (point)))
- (dir))
- (delete-region begin end)
+ "Go to the process mark. Send )sys pwd to the axiom process, parse the
+ result, cd to it and clean up output. FIXME: Doesn't seem to work nicely
+ after undo..."
+ (interactive)
+ (comint-goto-process-mark)
+ (let ((inhibit-read-only t)
+ (begin (point))
+ end dir)
(process-send-string (get-buffer-process (current-buffer)) ")sys pwd\n")
--removed:
(end-of-line)
- (sit-for 0)
(setq end (point))
??changed:
(cd (buffer-substring-no-properties begin end))
- (delete-region (1- begin) end)
- (forward-line 1)
- (end-of-line)))
+ (forward-line 0)
+ (delete-region (1- (point)) end)
+ (comint-goto-process-mark)))
+
+
+; (let* ((inhibit-read-only t)
+; (begin (progn (forward-line 0) (point)))
+; (end (progn (end-of-line) (point)))
+; (dir))
+; (delete-region begin end)
+; (process-send-string (get-buffer-process (current-buffer)) ")sys pwd\n")
+; (axiom-wait-for-output)
+; (forward-line -1)
+; (end-of-line)
+; (sit-for 0)
+; (setq end (point))
+; (cd (buffer-substring-no-properties begin end))
+; (delete-region (1- begin) end)
+; (forward-line 1)
+; (end-of-line)))
??changed:
(comint-goto-process-mark)
- (axiom-resync-directory)))
+ ; do not use copy-marker here, because we don't want this position to
+ ; move. Otherwise axiom-resync-directory will fail in axiom-filter-output
+ (setq axiom-end-of-input (point)))
+ (axiom-resync-directory))
--removed:
-(define-key axiom-mode-map [(meta k)] 'axiom-copy-to-clipboard)
-
@
@
++added:
+
+\subsection{Modifying old input}
??changed:
-Next we create a convenience function to clear old overlays. This is mainly to
-avoid unanticipated conflicts between old overlays and new ones.
+Next we create a convenience function to clear old overlays. This is to avoid
+unanticipated conflicts between old overlays and new ones.
??changed:
-When a change actually occurs, matters are actually straightforward - we just
-change the face of the existing overlay to the one we defined earlier, and
-remove the modification hooks call to this function to prevent recursion.
+When a change actually occurs, we just change the face of the existing overlay
+to [[axiom-changed-input]], and remove the modification hooks call to this
+function to prevent recursion.
--removed:
<<axiom-changed-function>>=
-(defvar axiom-current-input "")
(defun axiom-flag-as-changed (overlay after-change begin end &optional len)
++added:
+painting is now (more or less) confined to the output region, and copying
+output into another buffer preserves it. In particular, using enriched-mode,
+one can save the painted stuff to a file. (Don't forget to switch off
+font-lock-mode!)
+
+
<<axiom-paint>>=
--removed:
("output" axiom-output)))
-
-(defvar axiom-paint-face 'axiom-paint-lightblue)
--removed:
-(define-key axiom-mode-map [(shift up)] 'axiom-paint-previous-line)
-(define-key axiom-mode-map [(shift down)] 'axiom-paint-next-line)
-(define-key axiom-mode-map [(shift left)] 'axiom-paint-previous-char)
-(define-key axiom-mode-map [(shift right)] 'axiom-paint-next-char)
-
@
??changed:
;; otherwise, we first check whether process-mark is at a prompt
- (when (axiom-prompt? (1- (process-mark axiom-process)))
+ (when (axiom-prompt? (1- (process-mark (get-buffer-process
(current-buffer)))))
--removed:
-;; Now that everything is defined, bind the return key to our new eval function
-;; doesn't work in terminal
-;; (define-key axiom-mode-map [return] 'axiom-eval)
-(define-key axiom-mode-map "\C-m" 'axiom-eval)
-
(defun axiom-eval-append ()
??changed:
(interactive)
- (when (axiom-prompt? (1- (process-mark axiom-process)))
+ (when (axiom-prompt? (1- (process-mark (get-buffer-process
(current-buffer)))))
(let* ((input (buffer-substring (axiom-previous-prompt)
(insert input)
++added:
+;; wouldn't axiom-normal-eval be sufficient here?
(axiom-eval))))
--removed:
-;; doesn't work in terminal
-;;(define-key axiom-mode-map [(meta return)] 'axiom-eval-append)
-(define-key axiom-mode-map "\M-\C-m" 'axiom-eval-append)
-
@
--removed:
[[.input]], [[.spad]] and [[.as]] filenames, but I do not know how.
-
-<<axiom-command-completion>>=
-(define-key axiom-mode-map "\t" 'axiom-dynamic-complete)
-@
-
-I had [[(define-key axiom-mode-map [(tab)] 'axiom-dynamic-complete)]]
-here before, but that did not work entirely: hitting tab twice inserted a tab!
++added:
+\subsection{Inserting definitions from the kill ring}
+
+We want to copy function definitions from an \verb{.input} file into the axiom
+session. Because of differences in the parsers concerning multi-line input,
+the only practical way seems to be to write the definition into a temporary
+file and then \verb{)read} it.
+
+<<axiom-yank>>=
+(defun axiom-yank ()
+ "puts the front item of the kill ring into a temporary file and makes axiom
)read it"
+ (interactive)
+ (let* ((tmp-file (make-temp-file "axiom" nil ".input"))
+ (end (progn (goto-char (point-max))
+ (point))))
+ (write-region (car kill-ring-yank-pointer) nil tmp-file)
+ (delete-region (axiom-previous-prompt) end)
+ (comint-set-process-mark)
+ (insert (concat ")read " tmp-file))
+;; wouldn't axiom-normal-eval be sufficient-here???
+ (axiom-eval)
+ (delete-file tmp-file)))
+
+@
+
\subsection{Restarting and Re-evaluating - Kill and Restart Axiom without
??changed:
<<axiom-command-completion>>
-<<axiommodekeyboardmap-axiom-previous-input>>
-<<axiommodekeyboardmap-axiom-next-input>>
-<<axiommodekeyboardmap-axiom-scroll-input>>
-<<axiommodekeyboardmap-axiom-backward-char>>
-<<axiommodekeyboardmap-axiom-forward-char>>
+<<axiom-yank>>
+<<axiom-previous-input>>
+<<axiom-next-input>>
+<<axiom-scroll-input>>
+<<axiom-backward-char>>
+<<axiom-forward-char>>
<<axiom-mode-map>>
<<axiom-eval-control>>
++added:
+<<axiom-maybe-save>>
<<defineaxiom-mode>>
--removed:
-
-
-
-\section{EAxiom}
-
-EAxiom, like EMaxima, provides something rather different from terminal mode,
-or even a conventional mathematical notebook. Instead of having a line by line
-mathematical interaction, the user inserts ``Cells'' into a LaTeX document
-which allow the user to submit expressions to a background Axiom process and
-recieve the answers in TeX format. Moreover, these answers are processed by a
-style file defined specificly for the task, allowing automatically and cleanly
-formatted Axiom mathematical expressions to be included directly in a LaTeX
-document. It is a step towards Literate Programming, in fact. However, as
-opposed to the pamphlet mode this mode is intended for documents or sections of
-documents where the intent is not to provide runnable Axiom code but use
-Axiom's results. For the former case, see the Noweb Mode further on in this
-document.
-
-There are a formidable number of options which can be defined for this type of
-[49 more lines...]
\end{document}
--
forwarded from http://wiki.axiom-developer.org/[EMAIL PROTECTED]