aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjixiuf <jixiuf@qq.com>2020-03-21 13:42:47 +0800
committerjixiuf <jixiuf@qq.com>2020-03-27 22:47:55 +0800
commit996c535b9cc6aa70c3595413582d97abfab16edd (patch)
treeb73c0d944401cba5efb908eb1de1c4a6c2f1bf2f
parent7a247906c17f97f6edad6c478da0c34307db2b80 (diff)
reimplements vterm--get-prompt-point with text property and bind vterm-previous-prompt and vterm-next-prompt.
-rw-r--r--README.md20
-rw-r--r--elisp.h1
-rw-r--r--vterm-module.c73
-rw-r--r--vterm-module.h3
-rw-r--r--vterm.el80
5 files changed, 132 insertions, 45 deletions
diff --git a/README.md b/README.md
index 6f1e3f3..c167c31 100644
--- a/README.md
+++ b/README.md
@@ -128,8 +128,8 @@ The package can be installed with `guix package -i emacs-vterm`.
## Shell-side configuration
Some of the most useful features in `vterm` (e.g.,
-[directory-tracking](#directory-tracking) or [message
-passing](#message-passing)) require shell-side configurations. The main goal of
+[directory-tracking and prompt-tracking](#directory-tracking-and-prompt-tracking) or
+[message passing](#message-passing)) require shell-side configurations. The main goal of
these additional functions is to enable the shell to send information to `vterm`
via properly escaped sequences. A function that helps in this task,
`vterm_printf`, is defined below. This function is widely used throughout this
@@ -285,14 +285,26 @@ is ansi color 8-15.
- vterm-color-cyan
- vterm-color-white
-## Directory tracking
+## Directory tracking and Prompt tracking
`vterm` supports _directory tracking_. If this feature is enabled, the default
directory in Emacs and the current working directory in `vterm` are synced. As a
result, interactive functions that ask for a path or a file (e.g., `dired` or
`find-file`) will do so starting from the current location.
-Directory tracking requires some configuration, as the shell has to be
+And `vterm` supports _prompt tracking_. If this feature is enabled, Emacs knows
+where the prompt ends, you needn't customize `term-prompt-regexp` any more.
+Then you can use `vterm-next-prompt` and `vterm-previous-prompt`
+moving to end of next/previous prompt. The default keybinding is `C-c C-n` and `C-c C-p`.
+
+And `vterm-beginning-of-line` would move the point to the first character after the
+shell prompt on this line. If the point is already there, move to the beginning of the line.
+The default keybinding is `C-a` in `vterm-copy-mode`.
+
+And `vterm--at-prompt-p` would check whether the cursor is at the point just after
+the shell prompt.
+
+Directory tracking and Prompt tracking requires some configuration, as the shell has to be
instructed to share the relevant information with Emacs. The following pieces of
code assume that you have the function `vterm_printf` as defined in section
[shell-side configuration](#shell-side-configuration).
diff --git a/elisp.h b/elisp.h
index ab5cc98..ec597af 100644
--- a/elisp.h
+++ b/elisp.h
@@ -26,6 +26,7 @@ emacs_value Qcursor_type;
emacs_value Qemacs_major_version;
emacs_value Qvterm_line_wrap;
emacs_value Qrear_nonsticky;
+emacs_value Qvterm_prompt;
// Emacs functions
emacs_value Fsymbol_value;
diff --git a/vterm-module.c b/vterm-module.c
index a635427..ae3d330 100644
--- a/vterm-module.c
+++ b/vterm-module.c
@@ -216,7 +216,22 @@ static bool is_eol(Term *term, int end_col, int row, int col) {
}
return 1;
}
-
+static int is_end_of_prompt(Term *term, int end_col, int row, int col) {
+ LineInfo *info = get_lineinfo(term, row);
+ if (info == NULL) {
+ return 0;
+ }
+ if (info->prompt_col < 0) {
+ return 0;
+ }
+ if (info->prompt_col == col) {
+ return 1;
+ }
+ if (is_eol(term, end_col, row, col) && info->prompt_col >= col) {
+ return 1;
+ }
+ return 0;
+}
static size_t get_col_offset(Term *term, int row, int end_col) {
int col = 0;
size_t offset = 0;
@@ -255,12 +270,23 @@ static void refresh_lines(Term *term, emacs_env *env, int start_row,
VTermScreenCell lastCell;
fetch_cell(term, start_row, 0, &lastCell);
- int offset = 0;
for (i = start_row; i < end_row; i++) {
int newline = 0;
+ int isprompt = 0;
for (j = 0; j < end_col; j++) {
fetch_cell(term, i, j, &cell);
+ if (isprompt && length > 0) {
+ emacs_value text = render_text(env, term, buffer, length, &lastCell);
+ insert(env, render_prompt(env, text));
+ length = 0;
+ }
+
+ isprompt = is_end_of_prompt(term, end_col, i, j);
+ if (isprompt && length > 0) {
+ insert(env, render_text(env, term, buffer, length, &lastCell));
+ length = 0;
+ }
if (!compare_cells(&cell, &lastCell)) {
emacs_value text = render_text(env, term, buffer, length, &lastCell);
@@ -290,10 +316,15 @@ static void refresh_lines(Term *term, emacs_env *env, int start_row,
if (cell.width > 1) {
int w = cell.width - 1;
- offset += w;
j = j + w;
}
}
+ if (isprompt && length > 0) {
+ emacs_value text = render_text(env, term, buffer, length, &lastCell);
+ insert(env, render_prompt(env, text));
+ length = 0;
+ isprompt = 0;
+ }
if (!newline) {
emacs_value text = render_text(env, term, buffer, length, &lastCell);
@@ -666,6 +697,17 @@ static emacs_value render_text(emacs_env *env, Term *term, char *buffer,
return text;
}
+static emacs_value render_prompt(emacs_env *env, emacs_value text) {
+
+ emacs_value properties;
+
+ properties =
+ list(env, (emacs_value[]){Qvterm_prompt, Qt, Qrear_nonsticky, Qt}, 4);
+
+ add_text_properties(env, text, properties);
+
+ return text;
+}
static emacs_value render_fake_newline(emacs_env *env, Term *term) {
@@ -1104,27 +1146,6 @@ emacs_value Fvterm_get_icrnl(emacs_env *env, ptrdiff_t nargs,
return Qnil;
}
-emacs_value Fvterm_get_prompt_point(emacs_env *env, ptrdiff_t nargs,
- emacs_value args[], void *data) {
- Term *term = env->get_user_ptr(env, args[0]);
- int linenum = env->extract_integer(env, args[1]);
- if (linenum >= term->linenum) {
- linenum = term->linenum;
- }
- for (int l = linenum; l >= 1; l--) {
- int cur_row = linenr_to_row(term, l);
- LineInfo *info = get_lineinfo(term, cur_row);
- if (info != NULL && info->prompt_col >= 0) {
- goto_line(env, l);
- size_t offset = get_col_offset(term, cur_row, info->prompt_col);
- forward_char(env, env->make_integer(env, info->prompt_col - offset));
-
- return point(env);
- }
- }
- return Qnil;
-}
-
emacs_value Fvterm_reset_cursor_point(emacs_env *env, ptrdiff_t nargs,
emacs_value args[], void *data) {
Term *term = env->get_user_ptr(env, args[0]);
@@ -1158,6 +1179,7 @@ int emacs_module_init(struct emacs_runtime *ert) {
env->make_global_ref(env, env->intern(env, "vterm-line-wrap"));
Qrear_nonsticky =
env->make_global_ref(env, env->intern(env, "rear-nonsticky"));
+ Qvterm_prompt = env->make_global_ref(env, env->intern(env, "vterm-prompt"));
Qface = env->make_global_ref(env, env->intern(env, "font-lock-face"));
Qbox = env->make_global_ref(env, env->intern(env, "box"));
@@ -1236,9 +1258,6 @@ int emacs_module_init(struct emacs_runtime *ert) {
fun = env->make_function(env, 2, 2, Fvterm_get_pwd,
"Get the working directory of at line n.", NULL);
bind_function(env, "vterm--get-pwd-raw", fun);
- fun = env->make_function(env, 2, 2, Fvterm_get_prompt_point,
- "Get the end postion of current prompt.", NULL);
- bind_function(env, "vterm--get-prompt-point-internal", fun);
fun = env->make_function(env, 1, 1, Fvterm_reset_cursor_point,
"Reset cursor postion.", NULL);
bind_function(env, "vterm--reset-point", fun);
diff --git a/vterm-module.h b/vterm-module.h
index 752d744..068fb8f 100644
--- a/vterm-module.h
+++ b/vterm-module.h
@@ -90,7 +90,8 @@ static bool compare_cells(VTermScreenCell *a, VTermScreenCell *b);
static bool is_key(unsigned char *key, size_t len, char *key_description);
static emacs_value render_text(emacs_env *env, Term *term, char *string,
int len, VTermScreenCell *cell);
-static emacs_value render_fake_newline(emacs_env *env, Term *term);
+static emacs_value render_fake_newline(emacs_env *env, Term *term);
+static emacs_value render_prompt(emacs_env *env, emacs_value text);
static emacs_value color_to_rgb_string(emacs_env *env, Term *term,
VTermColor *color);
diff --git a/vterm.el b/vterm.el
index 0daaf42..25d212f 100644
--- a/vterm.el
+++ b/vterm.el
@@ -233,6 +233,7 @@ color is used as ansi color 15."
(defvar-local vterm--redraw-timer nil)
(defvar-local vterm--redraw-immididately nil)
(defvar-local vterm--linenum-remapping nil)
+(defvar-local vterm--prompt-tracking-enabled-p nil)
(defvar vterm-timer-delay 0.1
"Delay for refreshing the buffer after receiving updates from libvterm.
@@ -402,6 +403,8 @@ This is the value of `next-error-function' in Compilation buffers."
(define-key vterm-mode-map [remap self-insert-command] #'vterm--self-insert)
(define-key vterm-mode-map (kbd "C-c C-r") #'vterm-reset-cursor-point)
+(define-key vterm-mode-map (kbd "C-c C-n") #'vterm-next-prompt)
+(define-key vterm-mode-map (kbd "C-c C-p") #'vterm-previous-prompt)
(define-key vterm-mode-map (kbd "C-c C-t") #'vterm-copy-mode)
@@ -412,6 +415,8 @@ This is the value of `next-error-function' in Compilation buffers."
(define-key vterm-copy-mode-map (kbd "RET") #'vterm-copy-mode-done)
(define-key vterm-copy-mode-map (kbd "C-c C-r") #'vterm-reset-cursor-point)
(define-key vterm-copy-mode-map (kbd "C-a") #'vterm-beginning-of-line)
+(define-key vterm-copy-mode-map (kbd "C-c C-n") #'vterm-next-prompt)
+(define-key vterm-copy-mode-map (kbd "C-c C-p") #'vterm-previous-prompt)
(define-minor-mode vterm-copy-mode
@@ -831,12 +836,60 @@ the called functions."
(apply (cadr f) args)
(message "Failed to find command: %s" command))))
+
+(defun vterm--prompt-tracking-enabled-p ()
+ "Check whether feature of tracking the prompt is enabled or not.
+
+Prompt tracking need shell side configurations.
+
+For zsh user, this is done by PROMPT=$PROMPT'%{$(vterm_prompt_end)%}'.
+
+The shell send semantic information about where the prompt ends via properly
+escaped sequences to Emacs.
+
+More information see `Shell-side configuration' and `Directory tracking'
+in README."
+ (or vterm--prompt-tracking-enabled-p
+ (save-excursion
+ (setq vterm--prompt-tracking-enabled-p
+ (next-single-property-change (point-min) 'vterm-prompt)))))
+
+(defun vterm-next-prompt (n)
+ "Move to end of Nth next prompt in the buffer."
+ (interactive "p")
+ (if (vterm--prompt-tracking-enabled-p)
+ (let ((pt (point)) )
+ (goto-char (vterm--get-prompt-point))
+ (cl-loop repeat (or n 1) do
+ (setq pt (next-single-property-change (point-at-bol 2) 'vterm-prompt))
+ (when pt (goto-char pt))))
+ (term-next-prompt n)))
+
+(defun vterm-previous-prompt (n)
+ "Move to end of Nth previous prompt in the buffer."
+ (interactive "p")
+ (if (vterm--prompt-tracking-enabled-p)
+ (let ((pt (point)) )
+ (goto-char (vterm--get-prompt-point))
+ (cl-loop repeat (or n 1) do
+ (setq pt (previous-single-property-change (1- (point)) 'vterm-prompt))
+ (when pt (goto-char (1- pt)))))
+ (term-previous-prompt n)))
+
(defun vterm--get-prompt-point ()
- "Get the position of the end of current prompt."
+ "Get the position of the end of current prompt.
+More information see `vterm--prompt-tracking-enabled-p' and
+`Directory tracking and Prompt tracking'in README. "
(let (pt)
(save-excursion
- (setq pt (vterm--get-prompt-point-internal
- vterm--term (line-number-at-pos))))
+ (end-of-line)
+ (if (get-text-property (point) 'vterm-prompt)
+ (setq pt (point))
+ (setq pt (previous-single-property-change (point) 'vterm-prompt))
+ (when pt
+ (goto-char pt)
+ (backward-char)
+ (setq pt (point)))))
(unless pt
(save-excursion
(beginning-of-line)
@@ -844,16 +897,6 @@ the called functions."
(setq pt (point))))
pt))
-(defun vterm-reset-cursor-point ()
- "Make sure the cursor at the right postion."
- (interactive)
- (vterm--reset-point vterm--term))
-
-(defun vterm--get-cursor-point ()
- "Get term cursor position."
- (save-excursion
- (vterm-reset-cursor-point)))
-
(defun vterm--at-prompt-p ()
"Check whether the cursor postion is at shell prompt or not."
(let ((pt (point))
@@ -879,6 +922,17 @@ Effectively toggle between the two positions."
(goto-char prompt-pt)
(beginning-of-line)))))
+(defun vterm-reset-cursor-point ()
+ "Make sure the cursor at the right postion."
+ (interactive)
+ (vterm--reset-point vterm--term))
+
+(defun vterm--get-cursor-point ()
+ "Get term cursor position."
+ (save-excursion
+ (vterm-reset-cursor-point)))
+
+
(defun vterm--remove-fake-newlines ()
(goto-char (point-min))
(let (fake-newline)