ambevar-dotfiles/.emacs.d/lisp/init-term.el

83 lines
3.8 KiB
EmacsLisp

;;; Term
;;; 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: replace char-mode's commandline 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? Cf. elisp-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
commandline
commandline-end-position
(line-mode-point (point))
(proc (get-buffer-process (current-buffer)))
(pmark (process-mark proc)))
(save-excursion
(goto-char (point-max))
(when (= (line-beginning-position) (line-end-position))
;; Sometimes a spurious newline gets inserted.
;; Work around it by skipping back past it.
(ignore-errors (backward-char)))
(setq
;; If the prompt regexp is wrong or if we are on a multiline prompt, get the line-beginning-position.
last-prompt (max (term-bol nil) (line-beginning-position))
;; Yank last commandline. If prompt is not properly recognized, it yanks the whole line.
commandline (buffer-substring-no-properties last-prompt (line-end-position))
;; We store the end-position here so that we don't have to wait for the
;; process when we send the commandline.
commandline-end-position (line-end-position)))
(when (and (>= (point) last-prompt)
;; commandline can be empty (e.g. term is still initializing),
;; then no need to continue.
(not (string-empty-p commandline)))
;; Clear line.
(dotimes (_ (abs (- last-prompt pmark)))
(term-send-backspace))
;; Point could be before line-end-position, so we need to delete the trailing characters.
(dotimes (_ (abs (- (point-max) pmark)))
(term-send-del))
;; Get prompt position with `process-mark'.
;; TODO: We need to wait a bit to make sure the previously-sent deletions have been processed.
;; This is brittle and it makes the prompt flicker once.
;; It is possible to work-around this: either use `term-prompt-regexp'
;; or send `term-left' enough times, wait and get it. That latter
;; solution has the advantage that it does not flickers, but it won't
;; work on dash.
(sleep-for 0 100)
(setq pmark (process-mark proc))
;; Remove actual prompt length from the commandline.
(setq commandline (substring commandline (- pmark last-prompt)))
;; Send commandline to term.
(term-send-raw-string commandline)
;; Move char-mode point to line-mode point. TODO: Don't do this if shell does not support cursor moves.
;; Underlying shell can be retrieved with:
;; (car (last (process-command (get-buffer-process (current-buffer)))))
(dotimes (_ (abs (- line-mode-point commandline-end-position)))
(term-send-left)))
;; Finish up.
(term-update-mode-line))))
(provide 'init-term)