Emacs Factor listener: new help mode; better run-factor/switch-to-factor behaviour.
							parent
							
								
									a87fb04098
								
							
						
					
					
						commit
						70645e0d3a
					
				
							
								
								
									
										196
									
								
								misc/factor.el
								
								
								
								
							
							
						
						
									
										196
									
								
								misc/factor.el
								
								
								
								
							| 
						 | 
				
			
			@ -35,6 +35,7 @@
 | 
			
		|||
 | 
			
		||||
(require 'font-lock)
 | 
			
		||||
(require 'comint)
 | 
			
		||||
(require 'view)
 | 
			
		||||
 | 
			
		||||
;;; Customization:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -64,6 +65,30 @@ value from the existing code in the buffer."
 | 
			
		|||
  :type '(file :must-match t)
 | 
			
		||||
  :group 'factor)
 | 
			
		||||
 | 
			
		||||
(defcustom factor-use-doc-window t
 | 
			
		||||
  "When on, use a separate window to display help information.
 | 
			
		||||
Disable to see that information in the factor-listener comint
 | 
			
		||||
window."
 | 
			
		||||
  :type 'boolean
 | 
			
		||||
  :group 'factor)
 | 
			
		||||
 | 
			
		||||
(defcustom factor-listener-use-other-window t
 | 
			
		||||
  "Use a window other than the current buffer's when switching to
 | 
			
		||||
the factor-listener buffer."
 | 
			
		||||
  :type 'boolean
 | 
			
		||||
  :group 'factor)
 | 
			
		||||
 | 
			
		||||
(defcustom factor-listener-window-allow-split t
 | 
			
		||||
  "Allow window splitting when switching to the factor-listener
 | 
			
		||||
buffer."
 | 
			
		||||
  :type 'boolean
 | 
			
		||||
  :group 'factor)
 | 
			
		||||
 | 
			
		||||
(defcustom factor-help-always-ask t
 | 
			
		||||
  "When enabled, always ask for confirmation in help prompts."
 | 
			
		||||
  :type 'boolean
 | 
			
		||||
  :group 'factor)
 | 
			
		||||
 | 
			
		||||
(defcustom factor-display-compilation-output t
 | 
			
		||||
  "Display the REPL buffer before compiling files."
 | 
			
		||||
  :type 'boolean
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +99,11 @@ value from the existing code in the buffer."
 | 
			
		|||
  :type 'hook
 | 
			
		||||
  :group 'factor)
 | 
			
		||||
 | 
			
		||||
(defcustom factor-help-mode-hook nil
 | 
			
		||||
  "Hook run by `factor-help-mode'."
 | 
			
		||||
  :type 'hook
 | 
			
		||||
  :group 'factor)
 | 
			
		||||
 | 
			
		||||
(defgroup factor-faces nil
 | 
			
		||||
  "Faces used in Factor mode"
 | 
			
		||||
  :group 'factor
 | 
			
		||||
| 
						 | 
				
			
			@ -125,6 +155,10 @@ value from the existing code in the buffer."
 | 
			
		|||
  "Face for parsing words."
 | 
			
		||||
  :group 'factor-faces)
 | 
			
		||||
 | 
			
		||||
(defface factor-font-lock-help-mode-headlines '((t (:bold t :weight bold)))
 | 
			
		||||
  "Face for headlines in help buffers."
 | 
			
		||||
  :group 'factor-faces)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
;;; Factor mode font lock:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -429,18 +463,6 @@ value from the existing code in the buffer."
 | 
			
		|||
  (factor-send-region (search-backward ":")
 | 
			
		||||
                      (search-forward  ";")))
 | 
			
		||||
 | 
			
		||||
(defun factor-see ()
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (comint-send-string "*factor*" "\\ ")
 | 
			
		||||
  (comint-send-string "*factor*" (thing-at-point 'sexp))
 | 
			
		||||
  (comint-send-string "*factor*" " see\n"))
 | 
			
		||||
 | 
			
		||||
(defun factor-help ()
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (comint-send-string "*factor*" "\\ ")
 | 
			
		||||
  (comint-send-string "*factor*" (thing-at-point 'sexp))
 | 
			
		||||
  (comint-send-string "*factor*" " help\n"))
 | 
			
		||||
 | 
			
		||||
(defun factor-edit ()
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (comint-send-string "*factor*" "\\ ")
 | 
			
		||||
| 
						 | 
				
			
			@ -459,17 +481,6 @@ value from the existing code in the buffer."
 | 
			
		|||
(defvar factor-mode-map (make-sparse-keymap)
 | 
			
		||||
  "Key map used by Factor mode.")
 | 
			
		||||
 | 
			
		||||
(define-key factor-mode-map "\C-c\C-f" 'factor-run-file)
 | 
			
		||||
(define-key factor-mode-map "\C-c\C-r" 'factor-send-region)
 | 
			
		||||
(define-key factor-mode-map "\C-c\C-d" 'factor-send-definition)
 | 
			
		||||
(define-key factor-mode-map "\C-c\C-s" 'factor-see)
 | 
			
		||||
(define-key factor-mode-map "\C-ce"    'factor-edit)
 | 
			
		||||
(define-key factor-mode-map "\C-c\C-h" 'factor-help)
 | 
			
		||||
(define-key factor-mode-map "\C-cc"    'comment-region)
 | 
			
		||||
(define-key factor-mode-map [return]   'newline-and-indent)
 | 
			
		||||
(define-key factor-mode-map [tab]      'indent-for-tab-command)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
;; Factor mode:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -494,23 +505,118 @@ value from the existing code in the buffer."
 | 
			
		|||
(add-to-list 'auto-mode-alist '("\\.factor\\'" . factor-mode))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
;;; Factor listener mode
 | 
			
		||||
;;; Factor listener mode:
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(define-derived-mode factor-listener-mode comint-mode "Factor Listener")
 | 
			
		||||
(define-derived-mode factor-listener-mode comint-mode "Factor Listener"
 | 
			
		||||
  "Major mode for interacting with an inferior Factor listener process.
 | 
			
		||||
\\{factor-listener-mode-map}"
 | 
			
		||||
  (set (make-local-variable 'comint-prompt-regexp) "^( [^)]+ ) "))
 | 
			
		||||
 | 
			
		||||
(define-key factor-listener-mode-map [f8] 'factor-refresh-all)
 | 
			
		||||
(defvar factor--listener-buffer nil
 | 
			
		||||
  "The buffer in which the Factor listener is running.")
 | 
			
		||||
 | 
			
		||||
(defun factor--listener-start-process ()
 | 
			
		||||
  "Start an inferior Factor listener process, using
 | 
			
		||||
`factor-binary' and `factor-image'."
 | 
			
		||||
  (setq factor--listener-buffer
 | 
			
		||||
        (apply 'make-comint "factor" (expand-file-name factor-binary) nil
 | 
			
		||||
               `("-run=listener" ,(format "-i=%s" (expand-file-name factor-image)))))
 | 
			
		||||
  (with-current-buffer factor--listener-buffer
 | 
			
		||||
    (factor-listener-mode)))
 | 
			
		||||
 | 
			
		||||
(defun factor--listener-process ()
 | 
			
		||||
  (or (and (buffer-live-p factor--listener-buffer)
 | 
			
		||||
           (get-buffer-process factor--listener-buffer))
 | 
			
		||||
      (progn (factor--listener-start-process)
 | 
			
		||||
             (factor--listener-process))))
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(defun run-factor ()
 | 
			
		||||
  "Start a factor listener inside emacs, or switch to it if it
 | 
			
		||||
already exists."
 | 
			
		||||
(defalias 'switch-to-factor 'run-factor)
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(defun run-factor (&optional arg)
 | 
			
		||||
  "Show the factor-listener buffer, starting the process if needed."
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (switch-to-buffer
 | 
			
		||||
   (make-comint-in-buffer "factor" nil (expand-file-name factor-binary) nil
 | 
			
		||||
			  (concat "-i=" (expand-file-name factor-image))
 | 
			
		||||
			  "-run=listener"))
 | 
			
		||||
  (factor-listener-mode))
 | 
			
		||||
  (let ((buf (process-buffer (factor--listener-process)))
 | 
			
		||||
        (pop-up-windows factor-listener-window-allow-split))
 | 
			
		||||
    (if factor-listener-use-other-window
 | 
			
		||||
        (pop-to-buffer buf)
 | 
			
		||||
      (switch-to-buffer buf))))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
;;;; Factor help mode:
 | 
			
		||||
 | 
			
		||||
(defvar factor-help-mode-map (make-sparse-keymap)
 | 
			
		||||
  "Keymap for Factor help mode.")
 | 
			
		||||
 | 
			
		||||
(defconst factor--help-headlines
 | 
			
		||||
  (regexp-opt '("Parent topics:"
 | 
			
		||||
                "Inputs and outputs"
 | 
			
		||||
                "Word description"
 | 
			
		||||
                "Generic word contract"
 | 
			
		||||
                "Vocabulary"
 | 
			
		||||
                "Definition")
 | 
			
		||||
              t))
 | 
			
		||||
 | 
			
		||||
(defconst factor--help-headlines-regexp (format "^%s" factor--help-headlines))
 | 
			
		||||
 | 
			
		||||
(defconst factor--help-font-lock-keywords
 | 
			
		||||
  `((,factor--help-headlines-regexp . 'factor-font-lock-help-mode-headlines)
 | 
			
		||||
    ,@factor-font-lock-keywords))
 | 
			
		||||
 | 
			
		||||
(defun factor-help-mode ()
 | 
			
		||||
  "Major mode for displaying Factor help messages.
 | 
			
		||||
\\{factor-help-mode-map}"
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (kill-all-local-variables)
 | 
			
		||||
  (use-local-map factor-help-mode-map)
 | 
			
		||||
  (setq mode-name "Factor Help")
 | 
			
		||||
  (setq major-mode 'factor-help-mode)
 | 
			
		||||
  (set (make-local-variable 'font-lock-defaults)
 | 
			
		||||
       '(factor--help-font-lock-keywords t nil nil nil))
 | 
			
		||||
  (set (make-local-variable 'comint-redirect-subvert-readonly) t)
 | 
			
		||||
  (set (make-local-variable 'view-no-disable-on-exit) t)
 | 
			
		||||
  (view-mode)
 | 
			
		||||
  (setq view-exit-action
 | 
			
		||||
        (lambda (buffer)
 | 
			
		||||
          ;; Use `with-current-buffer' to make sure that `bury-buffer'
 | 
			
		||||
          ;; also removes BUFFER from the selected window.
 | 
			
		||||
          (with-current-buffer buffer
 | 
			
		||||
            (bury-buffer))))
 | 
			
		||||
  (run-mode-hooks 'factor-help-mode-hook))
 | 
			
		||||
 | 
			
		||||
(defun factor--listener-help-buffer ()
 | 
			
		||||
  (set-buffer (get-buffer-create "*factor-help*"))
 | 
			
		||||
  (let ((inhibit-read-only t))
 | 
			
		||||
    (delete-region (point-min) (point-max)))
 | 
			
		||||
  (factor-help-mode)
 | 
			
		||||
  (current-buffer))
 | 
			
		||||
 | 
			
		||||
(defvar factor--help-history nil)
 | 
			
		||||
 | 
			
		||||
(defun factor--listener-show-help (&optional see)
 | 
			
		||||
  (let* ((def (thing-at-point 'sexp))
 | 
			
		||||
         (prompt (format "%s (%s): " (if see "See" "Help") def))
 | 
			
		||||
         (ask (or (not (eq major-mode 'factor-mode))
 | 
			
		||||
                  (not def)
 | 
			
		||||
                  factor-help-always-ask))
 | 
			
		||||
         (cmd (format "\\ %s %s"
 | 
			
		||||
                      (if ask (read-string prompt nil 'factor--help-history def) def)
 | 
			
		||||
                      (if see "see" "help")))
 | 
			
		||||
         (hb (factor--listener-help-buffer))
 | 
			
		||||
         (proc (factor--listener-process)))
 | 
			
		||||
    (comint-redirect-send-command-to-process cmd hb proc nil)
 | 
			
		||||
    (pop-to-buffer hb)))
 | 
			
		||||
 | 
			
		||||
(defun factor-see ()
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (factor--listener-show-help t))
 | 
			
		||||
 | 
			
		||||
(defun factor-help ()
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (factor--listener-show-help))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
(defun factor-refresh-all ()
 | 
			
		||||
  "Reload source files and documentation for all loaded
 | 
			
		||||
| 
						 | 
				
			
			@ -519,6 +625,28 @@ vocabularies which have been modified on disk."
 | 
			
		|||
  (comint-send-string "*factor*" "refresh-all\n"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
;;; Key bindings:
 | 
			
		||||
 | 
			
		||||
(defmacro factor--define-key (key cmd)
 | 
			
		||||
  `(progn
 | 
			
		||||
     (define-key factor-mode-map [(control ?c) ,key] ,cmd)
 | 
			
		||||
     (define-key factor-mode-map [(control ?c) (control ,key)] ,cmd)))
 | 
			
		||||
 | 
			
		||||
(factor--define-key ?f 'factor-run-file)
 | 
			
		||||
(factor--define-key ?r 'factor-send-region)
 | 
			
		||||
(factor--define-key ?d 'factor-send-definition)
 | 
			
		||||
(factor--define-key ?s 'factor-see)
 | 
			
		||||
(factor--define-key ?e 'factor-edit)
 | 
			
		||||
(factor--define-key ?z 'switch-to-factor)
 | 
			
		||||
(factor--define-key ?c 'comment-region)
 | 
			
		||||
 | 
			
		||||
(define-key factor-mode-map "\C-ch" 'factor-help)
 | 
			
		||||
(define-key factor-mode-map "\C-m" 'newline-and-indent)
 | 
			
		||||
(define-key factor-mode-map [tab] 'indent-for-tab-command)
 | 
			
		||||
 | 
			
		||||
(define-key factor-listener-mode-map [f8] 'factor-refresh-all)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
(provide 'factor)
 | 
			
		||||
;;; factor.el ends here
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue