Evil/Term: Fix term-char-mode and sync points
parent
2df7c1e276
commit
d7df4f8bc2
|
@ -3,66 +3,18 @@
|
||||||
(evil-set-initial-state 'term-mode 'insert)
|
(evil-set-initial-state 'term-mode 'insert)
|
||||||
|
|
||||||
;; TODO: Set prompt regexp.
|
;; TODO: Set prompt regexp.
|
||||||
;; TODO: Can the prompt be read-only?
|
;; TODO: Can prompt be read-only?
|
||||||
|
|
||||||
;; TODO: Rebinding ESC has the drawback that programs like vi cannot use it anymore.
|
;; TODO: Rebinding ESC has the drawback that programs like vi cannot use it anymore.
|
||||||
;; Workaround: switch to Emacs mode and double-press ESC.
|
;; Workaround: switch to Emacs mode and double-press ESC.
|
||||||
;; Otherwise leave it ESC to C-cC-j.
|
;; Otherwise leave ESC to C-cC-j.
|
||||||
;; Or bind char-mode ESC to C-cC-x?
|
;; Or bind char-mode ESC to C-cC-x?
|
||||||
|
|
||||||
;; TODO: Move this out of Evil? No, it depends on modal editing... Actually yes,
|
|
||||||
;; the cursor movement part can be moved to a separate function so that normal
|
|
||||||
;; Emacs bindings can use it too.
|
|
||||||
;; TODO: A new line gets inserted when calling M-b, M-f in char-mode
|
|
||||||
;; and then switching to line-mode. Seems like it happens on the first prompt
|
|
||||||
;; only.
|
|
||||||
;; TODO: More generally, prompt gets messed up when going to char mode and
|
|
||||||
;; current point is after last char-mode's point. See `term-char-mode'.
|
|
||||||
(defun evil-term-char-mode-goto-point ()
|
|
||||||
"Switch to char mode.
|
|
||||||
To switch to char mode, just swith to insert mode somewhere on the last line of the last prompt."
|
|
||||||
(interactive) ; No need for intertice if set in a hook.
|
|
||||||
(when (get-buffer-process (current-buffer))
|
|
||||||
;; (term-char-mode)
|
|
||||||
;; (evil-insert-state)
|
|
||||||
;; TODO: When going from char->line mode, point is not necessarily at the
|
|
||||||
;; end. To come back, Insert and delete char. `term-send-backspace'.
|
|
||||||
;; Can this break text-mode programs? Or anything reading input without waiting for EOL?
|
|
||||||
;; TODO: Even better: if point in line mode is after last prompt on last line, find it in char mode by call enough `term-send-left' and right. Then no need for C-cC-k.
|
|
||||||
;; It's important that point must be on last line, because when point is on a multi-line command, it cannot go back to the previous lines.
|
|
||||||
(let ((last-prompt (save-excursion (goto-char (point-max)) (when (= (line-beginning-position) (line-end-position)) (backward-char)) (term-bol nil)))
|
|
||||||
(last-bol (save-excursion (goto-char (point-max)) (when (= (line-beginning-position) (line-end-position)) (backward-char)) (line-beginning-position)))
|
|
||||||
(ref-point (point))
|
|
||||||
;; TODO: Refactor code without last-point if not needed. Test when
|
|
||||||
;; background program outputs beyond the char-mode commandline.
|
|
||||||
(last-point (point)))
|
|
||||||
;; TODO: Optimize by setting left/right func to var.
|
|
||||||
(when (and (>= (point) last-prompt) (>= (point) last-bol))
|
|
||||||
(term-char-mode)
|
|
||||||
;; Send a cursor move so that Emacs' point gets updated to the last
|
|
||||||
;; char-mode position, off-by-one.
|
|
||||||
(term-send-left)
|
|
||||||
;; If moving left did not move the cursor, we are at term-bol, so move right.
|
|
||||||
(when (= (point) last-point)
|
|
||||||
(term-send-right))
|
|
||||||
;; If (point) is still last-point, it means there is no room for moving,
|
|
||||||
;; i.e. commandline is empty in char-mode. We don't need to move any
|
|
||||||
;; further.
|
|
||||||
;; Otherwise, move to ref-point.
|
|
||||||
(while (and (/= (point) last-point) (/= (point) ref-point))
|
|
||||||
(setq last-point (point))
|
|
||||||
(if (> (point) ref-point) (term-send-left) (term-send-right)))))
|
|
||||||
;; TODO: Add this to insert state entry hook and remove this line.
|
|
||||||
(evil-insert-state)))
|
|
||||||
|
|
||||||
(defun evil-term-char-mode-insert ()
|
(defun evil-term-char-mode-insert ()
|
||||||
(interactive)
|
(interactive)
|
||||||
(when (term-in-line-mode)
|
(term-char-mode)
|
||||||
(term-char-mode))
|
(evil-insert-state))
|
||||||
(unless (eq evil-state 'insert)
|
|
||||||
(evil-insert-state)))
|
|
||||||
|
|
||||||
;; TODO: Test these.
|
|
||||||
(evil-define-key 'normal term-mode-map
|
(evil-define-key 'normal term-mode-map
|
||||||
"\C-c\C-k" 'evil-term-char-mode-insert
|
"\C-c\C-k" 'evil-term-char-mode-insert
|
||||||
(kbd "RET") 'term-send-input)
|
(kbd "RET") 'term-send-input)
|
||||||
|
@ -80,16 +32,65 @@ To switch to char mode, just swith to insert mode somewhere on the last line of
|
||||||
"0" 'term-bol
|
"0" 'term-bol
|
||||||
"$" 'term-show-maximum-output)
|
"$" 'term-show-maximum-output)
|
||||||
|
|
||||||
;; Bind something to it? C-cC-c is used by Eshell for that, but it is taken here, no? Keep C-cC-k.
|
(defun evil-term-char-mode-entry-function ()
|
||||||
;; (defun evil-term-char-mode-entry-function ()
|
(when (get-buffer-process (current-buffer))
|
||||||
;; ;; Neet to check if we have a process, this is not the case when term-mode-hook is run.
|
(let (last-prompt last-bol)
|
||||||
;; (when (and (= (point) (point-max)) (get-buffer-process (current-buffer)))
|
(save-excursion
|
||||||
;; (term-char-mode)))
|
(goto-char (point-max))
|
||||||
|
(when (= (line-beginning-position) (line-end-position))
|
||||||
|
(ignore-errors (backward-char)))
|
||||||
|
(setq last-prompt (term-bol nil)
|
||||||
|
last-bol (line-beginning-position)))
|
||||||
|
;; We check if point is after both last-prompt and last-bol to handle multi-line prompts.
|
||||||
|
(when (and (>= (point) last-prompt) (>= (point) last-bol))
|
||||||
|
(term-char-mode)))))
|
||||||
|
|
||||||
(defun evil-term-setup ()
|
(defun evil-term-setup ()
|
||||||
;; (add-hook 'evil-insert-state-entry-hook 'evil-term-char-mode-entry-function nil t)
|
(add-hook 'evil-insert-state-entry-hook 'evil-term-char-mode-entry-function)
|
||||||
;; (add-hook 'evil-insert-state-entry-hook 'evil-term-char-mode-goto-point nil t)
|
(add-hook 'evil-insert-state-exit-hook 'term-line-mode))
|
||||||
(add-hook 'evil-insert-state-exit-hook 'term-line-mode nil t))
|
|
||||||
(add-hook 'term-mode-hook 'evil-term-setup)
|
(add-hook 'term-mode-hook 'evil-term-setup)
|
||||||
|
|
||||||
|
;;; TODO: Report `term-char-mode' fix upstream.
|
||||||
|
;;; Originally, `term-char-mode' check if point is after `pmark' and if it is,
|
||||||
|
;;; it cuts the characters between pmark and point and sends them to the
|
||||||
|
;;; terminal.
|
||||||
|
;;; The idea is that when you write a commandline in char mode, then switch to
|
||||||
|
;;; line mode and keep on writing _without moving the point_, you can go back to
|
||||||
|
;;; char mode and keep the modifications.
|
||||||
|
;;; I'd say this is rather useless as the point of line mode is to _move
|
||||||
|
;;; around_, not to do the same thing you can do in char mode.
|
||||||
|
;;; The more sensical thing to do: erase process content, replace it with
|
||||||
|
;;; line-mode's commandline and move char-mode point to where line-mode point
|
||||||
|
;;; is.
|
||||||
|
(defun term-char-mode ()
|
||||||
|
"Switch to char (\"raw\") sub-mode of term mode.
|
||||||
|
Each character you type is sent directly to the inferior without
|
||||||
|
intervention from Emacs, except for the escape character (usually C-c)."
|
||||||
|
(interactive)
|
||||||
|
;; FIXME: Emit message? Cfr ilisp-raw-message
|
||||||
|
(when (term-in-line-mode)
|
||||||
|
(setq term-old-mode-map (current-local-map))
|
||||||
|
(use-local-map term-raw-map)
|
||||||
|
(easy-menu-add term-terminal-menu)
|
||||||
|
(easy-menu-add term-signals-menu)
|
||||||
|
(let (last-prompt
|
||||||
|
last-bol
|
||||||
|
(pmark (process-mark (get-buffer-process (current-buffer)))))
|
||||||
|
(save-excursion
|
||||||
|
(goto-char (point-max))
|
||||||
|
(when (= (line-beginning-position) (line-end-position))
|
||||||
|
(ignore-errors (backward-char)))
|
||||||
|
(setq last-prompt (term-bol nil)
|
||||||
|
last-bol (line-beginning-position)))
|
||||||
|
;; Move char-mode point to line-mode point.
|
||||||
|
;; We check if point is after both last-prompt and last-bol to handle multi-line prompts.
|
||||||
|
(when (and
|
||||||
|
(>= (point) last-prompt)
|
||||||
|
(>= (point) last-bol)
|
||||||
|
(/= (point) pmark))
|
||||||
|
(let ((term-move (if (> (point) pmark) 'term-send-right 'term-send-left)))
|
||||||
|
(dotimes (_ (abs (- (point) pmark)))
|
||||||
|
(funcall term-move)))))
|
||||||
|
(term-update-mode-line)))
|
||||||
|
|
||||||
(provide 'init-evil-term)
|
(provide 'init-evil-term)
|
||||||
|
|
Loading…
Reference in New Issue