diff options
| author | jixiuf <jixiuf@qq.com> | 2019-10-20 23:11:35 +0800 |
|---|---|---|
| committer | jixiuf <jixiuf@qq.com> | 2019-11-17 14:58:31 +0800 |
| commit | 0df4686645a9783e49cffbf4b79a55afab27753b (patch) | |
| tree | a00dd3f41f6fdfcfee336fe82687ba169f988b7a | |
| parent | 19a6c027e7e29a0b16ca4ca9ae6991abe27ef856 (diff) | |
support compilation-shell-minior-mode
| -rw-r--r-- | vterm-module.c | 152 | ||||
| -rw-r--r-- | vterm-module.h | 15 | ||||
| -rw-r--r-- | vterm.el | 73 |
3 files changed, 225 insertions, 15 deletions
diff --git a/vterm-module.c b/vterm-module.c index 74782ea..eae80f3 100644 --- a/vterm-module.c +++ b/vterm-module.c @@ -10,6 +10,21 @@ #include <unistd.h> #include <vterm.h> +static LineInfo *alloc_lineinfo() { + LineInfo *info = malloc(sizeof(LineInfo)); + info->directory = NULL; + return info; +} +void free_lineinfo(LineInfo *line) { + if (line == NULL) { + return; + } + if (line->directory != NULL) { + free(line->directory); + line->directory = NULL; + } + free(line); +} static int term_sb_push(int cols, const VTermScreenCell *cells, void *data) { Term *term = (Term *)data; @@ -25,6 +40,10 @@ static int term_sb_push(int cols, const VTermScreenCell *cells, void *data) { // Recycle old row if it's the right size sbrow = term->sb_buffer[term->sb_current - 1]; } else { + if (term->sb_buffer[term->sb_current - 1]->info != NULL) { + free_lineinfo(term->sb_buffer[term->sb_current - 1]->info); + term->sb_buffer[term->sb_current - 1]->info = NULL; + } free(term->sb_buffer[term->sb_current - 1]); } @@ -41,6 +60,31 @@ static int term_sb_push(int cols, const VTermScreenCell *cells, void *data) { if (!sbrow) { sbrow = malloc(sizeof(ScrollbackLine) + c * sizeof(sbrow->cells[0])); sbrow->cols = c; + sbrow->info = NULL; + } + if (sbrow->info != NULL) { + free_lineinfo(sbrow->info); + } + sbrow->info = term->lines[0]; + memmove(term->lines, term->lines + 1, + sizeof(term->lines[0]) * (term->lines_len - 1)); + if (term->resizing) { + /* pushed by window height decr */ + if (term->lines[term->lines_len - 1] != NULL) { + /* do not need free here ,it is reused ,we just need set null */ + term->lines[term->lines_len - 1] = NULL; + } + term->lines_len--; + } else { + LineInfo *lastline = term->lines[term->lines_len - 1]; + if (lastline != NULL) { + LineInfo *line = alloc_lineinfo(); + if (lastline->directory != NULL) { + line->directory = malloc(1 + strlen(lastline->directory)); + strcpy(line->directory, lastline->directory); + } + term->lines[term->lines_len - 1] = line; + } } // New row is added at the start of the storage buffer. @@ -97,7 +141,14 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) { cells[col].width = 1; } + LineInfo **lines = malloc(sizeof(LineInfo *) * (term->lines_len + 1)); + + memmove(lines + 1, term->lines, sizeof(term->lines[0]) * term->lines_len); + lines[0] = sbrow->info; free(sbrow); + term->lines_len += 1; + free(term->lines); + term->lines = lines; return 1; } @@ -128,6 +179,24 @@ static void fetch_cell(Term *term, int row, int col, VTermScreenCell *cell) { } } +static char *get_row_directory(Term *term, int row) { + if (row < 0) { + ScrollbackLine *sbrow = term->sb_buffer[-row - 1]; + return sbrow->info->directory; + /* return term->dirs[0]; */ + } else { + return term->lines[row]->directory; + } +} +static LineInfo *get_lineinfo(Term *term, int row) { + if (row < 0) { + ScrollbackLine *sbrow = term->sb_buffer[-row - 1]; + return sbrow->info; + /* return term->dirs[0]; */ + } else { + return term->lines[row]; + } +} static bool is_eol(Term *term, int end_col, int row, int col) { /* This cell is EOL if this and every cell to the right is black */ if (row >= 0) { @@ -227,7 +296,6 @@ static void refresh_lines(Term *term, emacs_env *env, int start_row, return; } - // Refresh the screen (visible part of the buffer when the terminal is // focused) of a invalidated terminal static void refresh_screen(Term *term, emacs_env *env) { @@ -260,9 +328,43 @@ static int term_resize(int rows, int cols, void *user_data) { Term *term = (Term *)user_data; term->invalid_start = 0; term->invalid_end = rows; + + /* if rows=term->lines_len, that means term_sb_pop already resize term->lines + */ + /* if rows<term->lines_len, term_sb_push would resize term->lines there */ + /* we noly need to take care of rows>term->height */ + + if (rows > term->height) { + if (rows > term->lines_len) { + LineInfo **infos = term->lines; + term->lines = malloc(sizeof(LineInfo *) * rows); + memmove(term->lines, infos, sizeof(infos[0]) * term->lines_len); + + LineInfo *lastline = term->lines[term->lines_len - 1]; + for (int i = term->lines_len; i < rows; i++) { + if (lastline != NULL) { + LineInfo *line = alloc_lineinfo(); + if (lastline->directory != NULL) { + line->directory = + malloc(1 + strlen(term->lines[term->lines_len - 1]->directory)); + strcpy(line->directory, + term->lines[term->lines_len - 1]->directory); + } + term->lines[i] = line; + } else { + term->lines[i] = NULL; + } + } + term->lines_len = rows; + free(infos); + } + } + term->width = cols; term->height = rows; + invalidate_terminal(term, -1, -1); + term->resizing = false; return 1; } @@ -582,6 +684,10 @@ static void term_clear_scrollback(Term *term, emacs_env *env) { return; } for (int i = 0; i < term->sb_current; i++) { + if (term->sb_buffer[i]->info != NULL) { + free_lineinfo(term->sb_buffer[i]->info); + term->sb_buffer[i]->info = NULL; + } free(term->sb_buffer[i]); } free(term->sb_buffer); @@ -672,6 +778,10 @@ static void term_process_key(Term *term, emacs_env *env, unsigned char *key, void term_finalize(void *object) { Term *term = (Term *)object; for (int i = 0; i < term->sb_current; i++) { + if (term->sb_buffer[i]->info != NULL) { + free_lineinfo(term->sb_buffer[i]->info); + term->sb_buffer[i]->info = NULL; + } free(term->sb_buffer[i]); } if (term->title) { @@ -687,6 +797,12 @@ void term_finalize(void *object) { free(term->elisp_code); term->elisp_code = NULL; } + for (int i = 0; i < term->lines_len; i++) { + if (term->lines[i] != NULL) { + free_lineinfo(term->lines[i]); + term->lines[i] = NULL; + } + } if (term->pty_fd > 0) { close(term->pty_fd); @@ -713,6 +829,18 @@ static int osc_callback(const char *command, size_t cmdlen, void *user) { term->directory = malloc(cmdlen - 4 + 1); strcpy(term->directory, &buffer[4]); term->directory_changed = true; + + for (int i = term->cursor.row; i < term->lines_len; i++) { + if (term->lines[i] == NULL) { + term->lines[i] = alloc_lineinfo(); + } + + if (term->lines[i]->directory != NULL) { + free(term->lines[i]->directory); + } + term->lines[i]->directory = malloc(cmdlen - 4 + 1); + strcpy(term->lines[i]->directory, &buffer[4]); + } return 1; } else if (cmdlen > 4 && buffer[0] == '5' && buffer[1] == '1' && buffer[2] == ';' && buffer[3] == 'E') { @@ -769,17 +897,26 @@ emacs_value Fvterm_new(emacs_env *env, ptrdiff_t nargs, emacs_value args[], } term->linenum = term->height; term->linenum_added = 0; + term->resizing = false; term->pty_fd = -1; term->title = NULL; term->title_changed = false; + term->cursor.row = 0; + term->cursor.col = 0; term->directory = NULL; term->directory_changed = false; term->elisp_code = NULL; term->elisp_code_changed = false; + term->lines = malloc(sizeof(LineInfo *) * rows); + term->lines_len = rows; + for (int i = 0; i < rows; i++) { + term->lines[i] = NULL; + } + return env->make_user_ptr(env, term_finalize, term); } @@ -847,6 +984,7 @@ emacs_value Fvterm_set_size(emacs_env *env, ptrdiff_t nargs, emacs_value args[], term->linenum_added = rows - term->height - term->sb_current; } } + term->resizing = true; vterm_set_size(term->vt, rows, cols); vterm_screen_flush_damage(term->vts); @@ -870,6 +1008,15 @@ emacs_value Fvterm_set_pty_name(emacs_env *env, ptrdiff_t nargs, } return Qnil; } +emacs_value Fvterm_get_pwd(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]); + int row = linenr_to_row(term, linenum); + char *dir = get_row_directory(term, row); + + return env->make_string(env, dir, strlen(dir)); +} emacs_value Fvterm_get_icrnl(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) { @@ -973,6 +1120,9 @@ int emacs_module_init(struct emacs_runtime *ert) { fun = env->make_function(env, 2, 2, Fvterm_set_pty_name, "Sets the name of the pty.", NULL); bind_function(env, "vterm--set-pty-name", fun); + 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, 1, 1, Fvterm_get_icrnl, "Gets the icrnl state of the pty", NULL); diff --git a/vterm-module.h b/vterm-module.h index 005d5b5..93c657e 100644 --- a/vterm-module.h +++ b/vterm-module.h @@ -17,11 +17,18 @@ int plugin_is_GPL_compatible; #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif +typedef struct LineInfo { + char *directory; /* working directory */ + +} LineInfo; + typedef struct ScrollbackLine { size_t cols; + LineInfo *info; VTermScreenCell cells[]; } ScrollbackLine; + enum { VTERM_PROP_CURSOR_BLOCK = VTERM_PROP_CURSORSHAPE_BLOCK, VTERM_PROP_CURSOR_UNDERLINE = VTERM_PROP_CURSORSHAPE_UNDERLINE, @@ -67,8 +74,13 @@ typedef struct Term { char *elisp_code; bool elisp_code_changed; + /* the size of dirs almost = window height,value = directory of that line */ + LineInfo **lines; + int lines_len; + int width, height; int height_resize; + bool resizing; int pty_fd; } Term; @@ -105,6 +117,9 @@ emacs_value Fvterm_set_pty_name(emacs_env *env, ptrdiff_t nargs, emacs_value Fvterm_get_icrnl(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data); +emacs_value Fvterm_get_pwd(emacs_env *env, ptrdiff_t nargs, + emacs_value args[], void *data); + int emacs_module_init(struct emacs_runtime *ert); #endif /* VTERM_MODULE_H */ @@ -76,6 +76,7 @@ (require 'subr-x) (require 'cl-lib) (require 'color) +(require 'compile) (defcustom vterm-shell shell-file-name "The shell that gets run in the vterm." @@ -261,7 +262,35 @@ If nil, never delay") :sentinel (when vterm-exit-functions #'vterm--sentinel)))) (vterm--set-pty-name vterm--term (process-tty-name vterm--process)) (process-put vterm--process 'adjust-window-size-function - #'vterm--window-adjust-process-window-size)) + #'vterm--window-adjust-process-window-size) + (setq next-error-function 'vterm-next-error-function)) + +(defun vterm--compilation-setup () + "function to setup `compilation-shell-minor-mode' for vterm. +`'compilation-shell-minor-mode' would change the value of +local variable `next-error-function',so we should call this function in +`compilation-shell-minor-mode-hook'." + (when (eq major-mode 'vterm-mode) + (setq next-error-function 'vterm-next-error-function))) + +(add-hook 'compilation-shell-minor-mode-hook #'vterm--compilation-setup) + +;;;###autoload +(defun vterm-next-error-function (n &optional reset) + "Advance to the next error message and visit the file where the error was. +This is the value of `next-error-function' in Compilation buffers." + (interactive "p") + (let* ((pt (point)) + (msg (compilation-next-error (or n 1) nil + (or compilation-current-error + compilation-messages-start + (point-min)))) + (default-directory default-directory) + (pwd (vterm--get-pwd))) + (when pwd + (setq default-directory pwd)) + (goto-char pt) + (compilation-next-error-function n reset))) ;; Function keys and most of C- and M- bindings (defun vterm--exclude-keys (exceptions) @@ -618,19 +647,35 @@ if N is negative backward-line from end of buffer." (defun vterm--set-directory (path) "Set `default-directory' to PATH." - (if (string-match "^\\(.*?\\)@\\(.*?\\):\\(.*?\\)$" path) - (progn - (let ((user (match-string 1 path)) - (host (match-string 2 path)) - (dir (match-string 3 path))) - (if (and (string-equal user user-login-name) - (string-equal host (system-name))) - (progn - (when (file-directory-p dir) - (setq default-directory (file-name-as-directory dir)))) - (setq default-directory (file-name-as-directory (concat "/-:" path)))))) - (when (file-directory-p path) - (setq default-directory (file-name-as-directory path))))) + (let ((dir (vterm--get-directory path))) + (when dir (setq default-directory dir)))) + +(defun vterm--get-directory (path) + "Get normalized directory to PATH." + (when path + (let (directory) + (if (string-match "^\\(.*?\\)@\\(.*?\\):\\(.*?\\)$" path) + (progn + (let ((user (match-string 1 path)) + (host (match-string 2 path)) + (dir (match-string 3 path))) + (if (and (string-equal user user-login-name) + (string-equal host (system-name))) + (progn + (when (file-directory-p dir) + (setq directory (file-name-as-directory dir)))) + (setq directory (file-name-as-directory (concat "/-:" path)))))) + (when (file-directory-p path) + (setq directory (file-name-as-directory path)))) + directory))) + +(defun vterm--get-pwd (&optional linenum) + "Get working directory at LINENUM." + (let ((raw-pwd (vterm--get-pwd-raw + vterm--term + (or linenum (line-number-at-pos))))) + (when raw-pwd + (vterm--get-directory raw-pwd)))) (defun vterm--get-color(index) "Get color by index from `vterm-color-palette'. |
