Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
105d65e29b | ||
|
|
86ae6858ab | ||
|
|
0802e00f2a | ||
|
|
8fd37e42a6 | ||
|
|
01be43047f | ||
|
|
e29f33ef51 | ||
|
|
71c10dcd58 | ||
|
|
dd40b1af5b |
1
Filelist
1
Filelist
@@ -440,6 +440,7 @@ SRC_ALL = \
|
||||
src/libvterm/t/66screen_extent.test \
|
||||
src/libvterm/t/67screen_dbl_wh.test \
|
||||
src/libvterm/t/68screen_termprops.test \
|
||||
src/libvterm/t/69screen_pushline.test \
|
||||
src/libvterm/t/69screen_reflow.test \
|
||||
src/libvterm/t/90vttest_01-movement-1.test \
|
||||
src/libvterm/t/90vttest_01-movement-2.test \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*filetype.txt* For Vim version 9.2. Last change: 2026 Apr 09
|
||||
*filetype.txt* For Vim version 9.2. Last change: 2026 Apr 11
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -806,6 +806,14 @@ To enable the recognition of Markdown comments each time after removing
|
||||
re-source "javaformat.vim" for Vim versions greater than `8.2.1397`: >
|
||||
runtime autoload/javaformat.vim
|
||||
<
|
||||
JUJUTSU DESCRIPTION *ft-jjdescription-plugin*
|
||||
|
||||
The length of the first line of the commit message used for syntax
|
||||
highlighting can be configured via `g:jjcommit_summary_length`, and will
|
||||
fallback to `g:gitcommit_summary_length`. The default is 50. Example: >
|
||||
|
||||
let g:jjcommit_summary_length = 70
|
||||
<
|
||||
JSON-FORMAT *ft-json-plugin*
|
||||
|
||||
JSON filetype can be extended to use 'formatexpr' and "json.FormatExpr()"
|
||||
@@ -816,7 +824,7 @@ Add following lines to $HOME/.vim/ftplugin/json.vim: >
|
||||
vim9script
|
||||
import autoload 'dist/json.vim'
|
||||
setl formatexpr=json.FormatExpr()
|
||||
|
||||
<
|
||||
LUA *ft-lua-plugin* *g:lua_folding*
|
||||
|
||||
You can enable folding of Lua functions using |fold-expr| by: >
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*options.txt* For Vim version 9.2. Last change: 2026 Apr 09
|
||||
*options.txt* For Vim version 9.2. Last change: 2026 Apr 11
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -8626,7 +8626,8 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
|
||||
*stl-%[FuncName]*
|
||||
%[ defines clickable regions in the statusline. When the user clicks
|
||||
on a region with the mouse, the specified function is called.
|
||||
on a region with the mouse, the specified function is called. The
|
||||
same syntax can also be used in 'tabline'.
|
||||
|
||||
%[FuncName] Start of a clickable region. "FuncName" is the name
|
||||
of a Vim function to call when the region is clicked.
|
||||
@@ -8644,11 +8645,17 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
"button" Mouse button: "l" (left), "m" (middle), "r" (right).
|
||||
"mods" Modifier keys: combination of "s" (shift), "c" (ctrl),
|
||||
"a" (alt). Empty string if no modifiers.
|
||||
"winid" |window-ID| of the window whose statusline was clicked.
|
||||
"winid" |window-ID| of the window whose statusline was clicked,
|
||||
or 0 when the click was in 'tabline'.
|
||||
"area" "statusline" or "tabline". Indicates which option the
|
||||
clicked region belongs to. Useful when a single
|
||||
callback is shared between 'statusline' and 'tabline'.
|
||||
|
||||
If the function returns non-zero, the statusline is redrawn.
|
||||
Dragging the statusline to resize the window still works even when
|
||||
click handlers are defined.
|
||||
click handlers are defined. When used in 'tabline', clicks in
|
||||
%[FuncName] regions are dispatched to the callback instead of the
|
||||
default tab-selection behavior.
|
||||
|
||||
Example: >
|
||||
func! ClickFile(info)
|
||||
|
||||
@@ -7601,6 +7601,7 @@ ft-inform-syntax syntax.txt /*ft-inform-syntax*
|
||||
ft-java-plugin filetype.txt /*ft-java-plugin*
|
||||
ft-java-syntax syntax.txt /*ft-java-syntax*
|
||||
ft-javascript-omni insert.txt /*ft-javascript-omni*
|
||||
ft-jjdescription-plugin filetype.txt /*ft-jjdescription-plugin*
|
||||
ft-jq-syntax syntax.txt /*ft-jq-syntax*
|
||||
ft-json-plugin filetype.txt /*ft-json-plugin*
|
||||
ft-json-syntax syntax.txt /*ft-json-syntax*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*version9.txt* For Vim version 9.2. Last change: 2026 Apr 09
|
||||
*version9.txt* For Vim version 9.2. Last change: 2026 Apr 10
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -52615,6 +52615,7 @@ Other ~
|
||||
bypassing the shell completely.
|
||||
- Allow mouse clickable regions in the |status-line| using the
|
||||
|stl-%[FuncName]| atom.
|
||||
- Enable reflow support in the |:terminal|.
|
||||
|
||||
Platform specific ~
|
||||
-----------------
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
vim9script
|
||||
|
||||
# Highlight Yank plugin
|
||||
# Last Change: 2025 Mar 22
|
||||
# Last Change: 2026 Apr 11
|
||||
|
||||
def HighlightedYank()
|
||||
|
||||
@@ -28,7 +28,11 @@ def HighlightedYank()
|
||||
return [v[0][1], col_beg, col_end - col_beg]
|
||||
}))
|
||||
var winid = win_getid()
|
||||
timer_start(duration, (_) => m->matchdelete(winid))
|
||||
timer_start(duration, (_) => {
|
||||
if winbufnr(winid) != -1
|
||||
m->matchdelete(winid)
|
||||
endif
|
||||
})
|
||||
endif
|
||||
enddef
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
" 2025 Apr 17 by Vim Project (don't require space to start comments, #17130)
|
||||
" 2026 Apr 09 by Vim Project (anchor status regex to beginning of line, #19879)
|
||||
" 2026 Apr 09 by Vim Project (detect renames of files, #19879)
|
||||
" 2026 Apr 11 by Vim Project (configure summary length, #19905)
|
||||
|
||||
if exists('b:current_syntax')
|
||||
finish
|
||||
@@ -20,10 +21,21 @@ syn region jjComment start="^JJ:" end="$" contains=jjAdded,jjRemoved,jjChanged,j
|
||||
syn include @jjCommitDiff syntax/diff.vim
|
||||
syn region jjCommitDiff start=/\%(^diff --\%(git\|cc\|combined\) \)\@=/ end=/^\%(diff --\|$\|@@\@!\|[^[:alnum:]\ +-]\S\@!\)\@=/ fold contains=@jjCommitDiff
|
||||
|
||||
hi def link jjComment Comment
|
||||
hi def link jjAdded Added
|
||||
hi def link jjRemoved Removed
|
||||
hi def link jjChanged Changed
|
||||
hi def link jjRenamed Changed
|
||||
if get(g:, 'jjcommit_summary_length', get(g:, 'gitcommit_summary_length', 0)) < 0
|
||||
syn match jjdescriptionSummary "^.*$" contained containedin=jjcommitFirstLine nextgroup=jjcommitOverflow contains=@Spell
|
||||
elseif get(g:, 'jjcommit_summary_length', get(g:, 'gitcommit_summary_length', 1)) > 0
|
||||
exe 'syn match jjdescriptionSummary "^.*\%<' . (get(g:, 'jjcommit_summary_length', get(:g, 'gitcommit_summary_length', 50) + 1) . 'v." contained containedin=jjcommitFirstLine nextgroup=jjcommitOverflow contains=@Spell'
|
||||
endif
|
||||
syn match jjcommitOverflow ".*" contained contains=@Spell
|
||||
syn match jjcommitBlank "^.\+" contained contains=@Spell
|
||||
syn match jjcommitFirstLine "\%^.*" nextgroup=jjcommitBlank,jjComment skipnl
|
||||
|
||||
hi def link jjcommitSummary Keyword
|
||||
hi def link jjComment Comment
|
||||
hi def link jjAdded Added
|
||||
hi def link jjRemove Removed
|
||||
hi def link jjChange Changed
|
||||
hi def link jjRenamed Changed
|
||||
hi def link jjcommitBlank Error
|
||||
|
||||
let b:current_syntax = 'jjdescription'
|
||||
|
||||
@@ -104,6 +104,10 @@ EXTERN int redrawing_for_callback INIT(= 0);
|
||||
*/
|
||||
EXTERN short *TabPageIdxs INIT(= NULL);
|
||||
|
||||
// Click regions for 'tabline' (%[FuncName]).
|
||||
EXTERN stl_click_region_T *tabline_stl_click INIT(= NULL);
|
||||
EXTERN int tabline_stl_click_count INIT(= 0);
|
||||
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
// Array with size Rows x Columns containing zindex of popups.
|
||||
EXTERN short *popup_mask INIT(= NULL);
|
||||
|
||||
51
src/gui.c
51
src/gui.c
@@ -1659,9 +1659,11 @@ again:
|
||||
gui_may_resize_shell(void)
|
||||
{
|
||||
if (new_pixel_height)
|
||||
{
|
||||
// careful: gui_resize_shell() may postpone the resize again if we
|
||||
// were called indirectly by it
|
||||
gui_resize_shell(new_pixel_width, new_pixel_height);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
@@ -3737,12 +3739,24 @@ gui_init_which_components(char_u *oldval UNUSED)
|
||||
// Don't do this while starting up though.
|
||||
// Don't change Rows when adding menu/toolbar/tabline.
|
||||
// Don't change Columns when adding vertical toolbar.
|
||||
if (!gui.starting && need_set_size != (RESIZE_VERT | RESIZE_HOR))
|
||||
(void)char_avail();
|
||||
if ((need_set_size & RESIZE_VERT) == 0)
|
||||
Rows = prev_Rows;
|
||||
if ((need_set_size & RESIZE_HOR) == 0)
|
||||
Columns = prev_Columns;
|
||||
{
|
||||
// Save the size gui_set_shellsize() determined before
|
||||
// char_avail() processes spurious configure events that may
|
||||
// corrupt Rows/Columns.
|
||||
long post_Rows = Rows;
|
||||
long post_Columns = Columns;
|
||||
|
||||
if (!gui.starting && need_set_size != (RESIZE_VERT | RESIZE_HOR))
|
||||
(void)char_avail();
|
||||
if ((need_set_size & RESIZE_VERT) == 0)
|
||||
Rows = prev_Rows;
|
||||
else
|
||||
Rows = post_Rows;
|
||||
if ((need_set_size & RESIZE_HOR) == 0)
|
||||
Columns = prev_Columns;
|
||||
else
|
||||
Columns = post_Columns;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// When the console tabline appears or disappears the window positions
|
||||
@@ -3793,14 +3807,29 @@ gui_update_tabline(void)
|
||||
out_flush();
|
||||
|
||||
if (!showit != !shown)
|
||||
{
|
||||
// Save Rows/Columns before showing/hiding the tabline.
|
||||
// gui_mch_show_tabline() processes GTK events (via
|
||||
// gui_mch_update) which may trigger a spurious configure event
|
||||
// that temporarily corrupts Rows. Restore the original values so
|
||||
// that gui_set_shellsize() uses the correct row/column count.
|
||||
int save_Rows = Rows;
|
||||
int save_Columns = Columns;
|
||||
|
||||
gui_mch_show_tabline(showit);
|
||||
Rows = save_Rows;
|
||||
Columns = save_Columns;
|
||||
|
||||
// Resize the outer window to compensate for the tabline height
|
||||
// change. This is also needed when called from draw_tabline()
|
||||
// (e.g. after :tabclose), where no caller will do the resize.
|
||||
// When called from gui_init_which_components() the subsequent
|
||||
// gui_set_shellsize() call will redo this to the same dimensions,
|
||||
// which is harmless.
|
||||
gui_set_shellsize(FALSE, showit, RESIZE_VERT);
|
||||
}
|
||||
if (showit != 0)
|
||||
gui_mch_update_tabline();
|
||||
|
||||
// When the tabs change from hidden to shown or from shown to
|
||||
// hidden the size of the text area should remain the same.
|
||||
if (!showit != !shown)
|
||||
gui_set_shellsize(FALSE, showit, RESIZE_VERT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -408,11 +408,13 @@ static int using_gnome = 0;
|
||||
* Width and height are of gui.mainwin.
|
||||
*/
|
||||
typedef struct resize_history {
|
||||
int used; // If true, can't match for discard. Only matches once.
|
||||
int used; // If true, can't match for discard. Only matches once.
|
||||
int width;
|
||||
int height;
|
||||
int from_shellsize; // TRUE if recorded by gui_mch_set_shellsize (not
|
||||
// a pre-record from gui_mch_show_tabline).
|
||||
# ifdef ENABLE_RESIZE_HISTORY_LOG
|
||||
int seq; // for ch_log messages
|
||||
int seq; // for ch_log messages
|
||||
# endif
|
||||
struct resize_history *next;
|
||||
} resize_hist_T;
|
||||
@@ -422,13 +424,28 @@ static resize_hist_T *latest_resize_hist;
|
||||
// list of stale resize requests
|
||||
static resize_hist_T *old_resize_hists;
|
||||
|
||||
// On Wayland (and GTK3 with CSD), gtk_window_resize(w, h) results in
|
||||
// gtk_window_get_size() returning (w - csd_w, h - csd_h). These offsets are
|
||||
// computed once from the first confirmed resize response and applied to all
|
||||
// subsequent gtk_window_resize() calls so the form gets the intended size.
|
||||
static int mch_csd_width = 0;
|
||||
static int mch_csd_height = 0;
|
||||
|
||||
// Expected form widget width for the most recent gui_mch_set_shellsize()
|
||||
// call (set only after mch_csd_width is known). Used in
|
||||
// form_configure_event() to clamp slightly-narrow configure responses that
|
||||
// result from CSD under-compensation, preventing column loss.
|
||||
static int mch_pending_form_w = 0;
|
||||
|
||||
/*
|
||||
* Used when calling gtk_window_resize().
|
||||
* Create a resize request history item, put previous request on stale list.
|
||||
* Width/height are the size of the request for the gui.mainwin.
|
||||
* from_shellsize is TRUE when called from gui_mch_set_shellsize (vs a
|
||||
* stale pre-record from gui_mch_show_tabline).
|
||||
*/
|
||||
static void
|
||||
alloc_resize_hist(int width, int height)
|
||||
alloc_resize_hist(int width, int height, int from_shellsize)
|
||||
{
|
||||
// alloc a new resize hist, save current in list of old history
|
||||
resize_hist_T *prev_hist = latest_resize_hist;
|
||||
@@ -436,6 +453,7 @@ alloc_resize_hist(int width, int height)
|
||||
|
||||
new_hist->width = width;
|
||||
new_hist->height = height;
|
||||
new_hist->from_shellsize = from_shellsize;
|
||||
latest_resize_hist = new_hist;
|
||||
|
||||
// previous hist item becomes head of list
|
||||
@@ -3102,7 +3120,25 @@ get_item_dimensions(GtkWidget *widget, GtkOrientation orientation)
|
||||
GtkAllocation allocation;
|
||||
|
||||
gtk_widget_get_allocation(widget, &allocation);
|
||||
if (allocation.height > 1)
|
||||
return allocation.height;
|
||||
|
||||
// Allocation hasn't been updated yet (widget just became visible,
|
||||
// e.g. tab bar shown asynchronously on Wayland). Query the preferred
|
||||
// height so the caller gets a valid value before the layout pass
|
||||
// runs. Use the maximum of minimum and natural height: GTK may
|
||||
// allocate min_h even when natural_h is smaller (e.g. GtkNotebook
|
||||
// tab bar has min_h > natural_h due to CSS).
|
||||
# if GTK_CHECK_VERSION(3,0,0)
|
||||
{
|
||||
gint min_h = 0, natural_h = 0;
|
||||
|
||||
gtk_widget_get_preferred_height(widget, &min_h, &natural_h);
|
||||
return MAX(min_h, natural_h);
|
||||
}
|
||||
# else
|
||||
return allocation.height;
|
||||
# endif
|
||||
# else
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
return widget->allocation.height;
|
||||
@@ -3147,7 +3183,11 @@ get_menu_tool_height(void)
|
||||
height += get_item_dimensions(gui.toolbar, GTK_ORIENTATION_HORIZONTAL);
|
||||
#endif
|
||||
#ifdef FEAT_GUI_TABLINE
|
||||
if (gui.tabline != NULL)
|
||||
// Only include the tabline height when tabs are actually shown. After
|
||||
// gtk_notebook_set_show_tabs(FALSE) the widget allocation is not updated
|
||||
// until the GTK main loop runs, so reading it would give a stale value.
|
||||
if (gui.tabline != NULL
|
||||
&& gtk_notebook_get_show_tabs(GTK_NOTEBOOK(gui.tabline)))
|
||||
height += get_item_dimensions(gui.tabline, GTK_ORIENTATION_HORIZONTAL);
|
||||
#endif
|
||||
|
||||
@@ -3513,6 +3553,19 @@ gui_mch_show_tabline(int showit)
|
||||
|
||||
if (!showit != !gtk_notebook_get_show_tabs(GTK_NOTEBOOK(gui.tabline)))
|
||||
{
|
||||
# ifdef TRACK_RESIZE_HISTORY
|
||||
// When the tabline visibility changes, GTK defers the formwin
|
||||
// relayout. The resulting configure event fires while the mainwin is
|
||||
// still at its current size. Pre-record that size so it becomes a
|
||||
// stale (discardable) entry once gui_mch_set_shellsize() records the
|
||||
// new target size.
|
||||
{
|
||||
int w, h;
|
||||
|
||||
gtk_window_get_size(GTK_WINDOW(gui.mainwin), &w, &h);
|
||||
alloc_resize_hist(w, h, FALSE);
|
||||
}
|
||||
# endif
|
||||
// Note: this may cause a resize event
|
||||
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), showit);
|
||||
update_window_manager_hints(0, 0);
|
||||
@@ -4284,6 +4337,22 @@ gui_mch_new_colors(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* One-shot idle callback to issue a corrective gtk_window_resize() after the
|
||||
* startup configure event has been processed. At startup
|
||||
* gui_mch_set_shellsize() runs before the CSD offsets are known (they are
|
||||
* measured from the first configure response), so the window ends up
|
||||
* mch_csd_height pixels too short and mch_csd_width pixels too narrow. This
|
||||
* callback re-issues the shellsize with the now-known offsets so the physical
|
||||
* window matches Vim's model.
|
||||
*/
|
||||
static gboolean
|
||||
startup_resize_correction_cb(gpointer data UNUSED)
|
||||
{
|
||||
gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH);
|
||||
return FALSE; // one-shot
|
||||
}
|
||||
|
||||
/*
|
||||
* This signal informs us about the need to rearrange our sub-widgets.
|
||||
*/
|
||||
@@ -4321,6 +4390,41 @@ form_configure_event(GtkWidget *widget UNUSED,
|
||||
&& match_stale_width_height(w, h))
|
||||
// discard stale event
|
||||
return TRUE;
|
||||
|
||||
// On Wayland (GTK3 CSD), gtk_window_resize(w, req_h) results in
|
||||
// gtk_window_get_size() returning (req_w - csd_w, req_h - csd_h).
|
||||
// Compute the offsets once from the first confirmed resize response.
|
||||
if ((mch_csd_height == 0 || mch_csd_width == 0)
|
||||
&& latest_resize_hist != NULL
|
||||
&& !latest_resize_hist->used
|
||||
&& latest_resize_hist->from_shellsize
|
||||
&& gui.char_height > 0)
|
||||
{
|
||||
int pot_csd_w = latest_resize_hist->width - w;
|
||||
int pot_csd_h = latest_resize_hist->height - h;
|
||||
|
||||
if (pot_csd_w > 0 && pot_csd_w < gui.char_width)
|
||||
{
|
||||
mch_csd_width = pot_csd_w;
|
||||
// The resize that triggered this startup event was issued without
|
||||
// CSD compensation; retroactively set the expected form width so
|
||||
// that the clamp below corrects Columns for this same event.
|
||||
mch_pending_form_w = (int)Columns * gui.char_width
|
||||
+ gui_get_base_width();
|
||||
}
|
||||
if (pot_csd_h > 0 && pot_csd_h < gui.char_height)
|
||||
{
|
||||
mch_csd_height = pot_csd_h;
|
||||
// Similarly, correct the form height for this event so that Rows
|
||||
// is computed correctly despite the missing CSD compensation.
|
||||
usable_height += mch_csd_height;
|
||||
}
|
||||
// The window was resized without CSD compensation and is physically
|
||||
// too small. Schedule a corrective resize (now that offsets are
|
||||
// known) so the window actually fits the geometry Vim has just set.
|
||||
if ((mch_csd_height > 0 || mch_csd_width > 0) && gtk_socket_id == 0)
|
||||
g_idle_add(startup_resize_correction_cb, NULL);
|
||||
}
|
||||
clear_resize_hists();
|
||||
#endif
|
||||
|
||||
@@ -4354,9 +4458,22 @@ form_configure_event(GtkWidget *widget UNUSED,
|
||||
if (gtk_socket_id != 0)
|
||||
usable_height -= (gui.char_height - (gui.char_height/2)); // sic.
|
||||
|
||||
gui_gtk_form_freeze(GTK_FORM(gui.formwin));
|
||||
gui_resize_shell(event->width, usable_height);
|
||||
gui_gtk_form_thaw(GTK_FORM(gui.formwin));
|
||||
// If the configure event delivers a form width that is slightly less than
|
||||
// the width we intended (mch_pending_form_w), the difference is within
|
||||
// one char_width and is due to CSD under-compensation. Clamp to the
|
||||
// intended width so that column count does not drift downward.
|
||||
{
|
||||
int use_width = event->width;
|
||||
|
||||
#ifdef TRACK_RESIZE_HISTORY
|
||||
if (mch_pending_form_w > event->width
|
||||
&& mch_pending_form_w - event->width < gui.char_width)
|
||||
use_width = mch_pending_form_w;
|
||||
#endif
|
||||
gui_gtk_form_freeze(GTK_FORM(gui.formwin));
|
||||
gui_resize_shell(use_width, usable_height);
|
||||
gui_gtk_form_thaw(GTK_FORM(gui.formwin));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -4855,8 +4972,22 @@ gui_mch_set_shellsize(int width, int height,
|
||||
width += get_menu_tool_width();
|
||||
height += get_menu_tool_height();
|
||||
|
||||
// Compensate for CSD frame: on Wayland (GTK3 with CSD), the compositor
|
||||
// subtracts the decoration margin from the requested size. mch_csd_width
|
||||
// and mch_csd_height are computed from the first startup resize response
|
||||
// and are 0 on X11.
|
||||
width += mch_csd_width;
|
||||
height += mch_csd_height;
|
||||
|
||||
// Record the form width we expect the compositor to deliver. Used in
|
||||
// form_configure_event() to clamp configure responses that are slightly
|
||||
// narrower than intended (due to CSD startup underestimate), preventing
|
||||
// column loss. Only meaningful once mch_csd_width has been measured.
|
||||
mch_pending_form_w = (mch_csd_width > 0)
|
||||
? width - get_menu_tool_width() - mch_csd_width : 0;
|
||||
|
||||
#ifdef TRACK_RESIZE_HISTORY
|
||||
alloc_resize_hist(width, height); // track the resize request
|
||||
alloc_resize_hist(width, height, TRUE);
|
||||
#endif
|
||||
if (gtk_socket_id == 0)
|
||||
gtk_window_resize(GTK_WINDOW(gui.mainwin), width, height);
|
||||
|
||||
364
src/json.c
364
src/json.c
@@ -18,7 +18,7 @@
|
||||
|
||||
#if defined(FEAT_EVAL)
|
||||
|
||||
static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int depth);
|
||||
static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int options);
|
||||
|
||||
/*
|
||||
* Encode "val" into a JSON format string.
|
||||
@@ -28,7 +28,7 @@ static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int option
|
||||
static int
|
||||
json_encode_gap(garray_T *gap, typval_T *val, int options)
|
||||
{
|
||||
if (json_encode_item(gap, val, get_copyID(), options, 0) == FAIL)
|
||||
if (json_encode_item(gap, val, get_copyID(), options) == FAIL)
|
||||
{
|
||||
ga_clear(gap);
|
||||
gap->ga_data = vim_strsave((char_u *)"");
|
||||
@@ -264,31 +264,61 @@ is_simple_key(char_u *key)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
ENC_LIST,
|
||||
ENC_TUPLE,
|
||||
ENC_DICT,
|
||||
} json_enc_type_T;
|
||||
|
||||
typedef struct {
|
||||
json_enc_type_T je_type;
|
||||
int je_options; // options when this container was entered
|
||||
union {
|
||||
struct {
|
||||
list_T *list;
|
||||
listitem_T *li; // current item
|
||||
} l;
|
||||
struct {
|
||||
tuple_T *tuple;
|
||||
int idx; // current index
|
||||
int len;
|
||||
} t;
|
||||
struct {
|
||||
dict_T *dict;
|
||||
hashitem_T *hi; // current entry
|
||||
int todo; // remaining entries
|
||||
} d;
|
||||
} je_u;
|
||||
} json_enc_frame_T;
|
||||
|
||||
/*
|
||||
* Encode "val" into "gap".
|
||||
* Uses an explicit stack to avoid deep recursion on nested structures.
|
||||
* Return FAIL or OK.
|
||||
*/
|
||||
static int
|
||||
json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int depth)
|
||||
json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
|
||||
{
|
||||
char_u numbuf[NUMBUFLEN];
|
||||
char_u *res;
|
||||
blob_T *b;
|
||||
list_T *l;
|
||||
tuple_T *tuple;
|
||||
dict_T *d;
|
||||
int i;
|
||||
char_u numbuf[NUMBUFLEN];
|
||||
char_u *res;
|
||||
blob_T *b;
|
||||
list_T *l;
|
||||
tuple_T *tuple;
|
||||
dict_T *d;
|
||||
int i;
|
||||
garray_T stack;
|
||||
typval_T *cur_val;
|
||||
json_enc_frame_T *frame;
|
||||
|
||||
if (depth > p_mfd)
|
||||
ga_init2(&stack, sizeof(json_enc_frame_T), 100);
|
||||
cur_val = val;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
emsg(_(e_function_call_depth_is_higher_than_maxfuncdepth));
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
switch (val->v_type)
|
||||
switch (cur_val->v_type)
|
||||
{
|
||||
case VAR_BOOL:
|
||||
switch ((long)val->vval.v_number)
|
||||
switch ((long)cur_val->vval.v_number)
|
||||
{
|
||||
case VVAL_FALSE: GA_CONCAT_LITERAL(gap, "false"); break;
|
||||
case VVAL_TRUE: GA_CONCAT_LITERAL(gap, "true"); break;
|
||||
@@ -296,7 +326,7 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept
|
||||
break;
|
||||
|
||||
case VAR_SPECIAL:
|
||||
switch ((long)val->vval.v_number)
|
||||
switch ((long)cur_val->vval.v_number)
|
||||
{
|
||||
case VVAL_NONE: if ((options & JSON_JS) != 0
|
||||
&& (options & JSON_NO_NONE) == 0)
|
||||
@@ -312,13 +342,13 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept
|
||||
size_t numbuflen;
|
||||
|
||||
numbuflen = vim_snprintf_safelen((char *)numbuf, sizeof(numbuf),
|
||||
"%lld", (varnumber_T)val->vval.v_number);
|
||||
"%lld", (varnumber_T)cur_val->vval.v_number);
|
||||
ga_concat_len(gap, numbuf, numbuflen);
|
||||
}
|
||||
break;
|
||||
|
||||
case VAR_STRING:
|
||||
res = val->vval.v_string;
|
||||
res = cur_val->vval.v_string;
|
||||
write_string(gap, res);
|
||||
break;
|
||||
|
||||
@@ -330,11 +360,11 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
case VAR_TYPEALIAS:
|
||||
semsg(_(e_cannot_json_encode_str), vartype_name(val->v_type));
|
||||
return FAIL;
|
||||
semsg(_(e_cannot_json_encode_str), vartype_name(cur_val->v_type));
|
||||
goto theend;
|
||||
|
||||
case VAR_BLOB:
|
||||
b = val->vval.v_blob;
|
||||
b = cur_val->vval.v_blob;
|
||||
if (b == NULL || b->bv_ga.ga_len == 0)
|
||||
GA_CONCAT_LITERAL(gap, "[]");
|
||||
else
|
||||
@@ -366,7 +396,7 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept
|
||||
break;
|
||||
|
||||
case VAR_LIST:
|
||||
l = val->vval.v_list;
|
||||
l = cur_val->vval.v_list;
|
||||
if (l == NULL)
|
||||
GA_CONCAT_LITERAL(gap, "[]");
|
||||
else
|
||||
@@ -375,26 +405,28 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept
|
||||
GA_CONCAT_LITERAL(gap, "[]");
|
||||
else
|
||||
{
|
||||
listitem_T *li;
|
||||
|
||||
l->lv_copyID = copyID;
|
||||
ga_append(gap, '[');
|
||||
CHECK_LIST_MATERIALIZE(l);
|
||||
for (li = l->lv_first; li != NULL && !got_int; )
|
||||
if (l->lv_first != NULL)
|
||||
{
|
||||
if (json_encode_item(gap, &li->li_tv, copyID,
|
||||
options & JSON_JS,
|
||||
depth + 1) == FAIL)
|
||||
return FAIL;
|
||||
if ((options & JSON_JS)
|
||||
&& li->li_next == NULL
|
||||
&& li->li_tv.v_type == VAR_SPECIAL
|
||||
&& li->li_tv.vval.v_number == VVAL_NONE)
|
||||
// add an extra comma if the last item is v:none
|
||||
ga_append(gap, ',');
|
||||
li = li->li_next;
|
||||
if (li != NULL)
|
||||
ga_append(gap, ',');
|
||||
if (stack.ga_len >= p_mfd)
|
||||
{
|
||||
emsg(_(e_function_call_depth_is_higher_than_maxfuncdepth));
|
||||
goto theend;
|
||||
}
|
||||
if (ga_grow(&stack, 1) == FAIL)
|
||||
goto theend;
|
||||
frame = ((json_enc_frame_T *)stack.ga_data)
|
||||
+ stack.ga_len;
|
||||
frame->je_type = ENC_LIST;
|
||||
frame->je_options = options;
|
||||
frame->je_u.l.list = l;
|
||||
frame->je_u.l.li = l->lv_first;
|
||||
++stack.ga_len;
|
||||
options = options & JSON_JS;
|
||||
cur_val = &l->lv_first->li_tv;
|
||||
continue;
|
||||
}
|
||||
ga_append(gap, ']');
|
||||
l->lv_copyID = 0;
|
||||
@@ -403,7 +435,7 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept
|
||||
break;
|
||||
|
||||
case VAR_TUPLE:
|
||||
tuple = val->vval.v_tuple;
|
||||
tuple = cur_val->vval.v_tuple;
|
||||
if (tuple == NULL)
|
||||
GA_CONCAT_LITERAL(gap, "[]");
|
||||
else
|
||||
@@ -414,33 +446,37 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept
|
||||
{
|
||||
int len = TUPLE_LEN(tuple);
|
||||
|
||||
tuple->tv_copyID = copyID;
|
||||
ga_append(gap, '[');
|
||||
for (i = 0; i < len && !got_int; i++)
|
||||
if (len > 0)
|
||||
{
|
||||
typval_T *t_item = TUPLE_ITEM(tuple, i);
|
||||
if (json_encode_item(gap, t_item, copyID,
|
||||
options & JSON_JS,
|
||||
depth + 1) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if ((options & JSON_JS)
|
||||
&& i == len - 1
|
||||
&& t_item->v_type == VAR_SPECIAL
|
||||
&& t_item->vval.v_number == VVAL_NONE)
|
||||
// add an extra comma if the last item is v:none
|
||||
ga_append(gap, ',');
|
||||
if (i <= len - 2)
|
||||
ga_append(gap, ',');
|
||||
tuple->tv_copyID = copyID;
|
||||
ga_append(gap, '[');
|
||||
if (stack.ga_len >= p_mfd)
|
||||
{
|
||||
emsg(_(e_function_call_depth_is_higher_than_maxfuncdepth));
|
||||
goto theend;
|
||||
}
|
||||
if (ga_grow(&stack, 1) == FAIL)
|
||||
goto theend;
|
||||
frame = ((json_enc_frame_T *)stack.ga_data)
|
||||
+ stack.ga_len;
|
||||
frame->je_type = ENC_TUPLE;
|
||||
frame->je_options = options;
|
||||
frame->je_u.t.tuple = tuple;
|
||||
frame->je_u.t.idx = 0;
|
||||
frame->je_u.t.len = len;
|
||||
++stack.ga_len;
|
||||
options = options & JSON_JS;
|
||||
cur_val = TUPLE_ITEM(tuple, 0);
|
||||
continue;
|
||||
}
|
||||
ga_append(gap, ']');
|
||||
tuple->tv_copyID = 0;
|
||||
else
|
||||
GA_CONCAT_LITERAL(gap, "[]");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VAR_DICT:
|
||||
d = val->vval.v_dict;
|
||||
d = cur_val->vval.v_dict;
|
||||
if (d == NULL)
|
||||
GA_CONCAT_LITERAL(gap, "{}");
|
||||
else
|
||||
@@ -449,46 +485,60 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept
|
||||
GA_CONCAT_LITERAL(gap, "{}");
|
||||
else
|
||||
{
|
||||
int first = TRUE;
|
||||
int todo = (int)d->dv_hashtab.ht_used;
|
||||
hashitem_T *hi;
|
||||
|
||||
d->dv_copyID = copyID;
|
||||
ga_append(gap, '{');
|
||||
if (todo > 0)
|
||||
{
|
||||
d->dv_copyID = copyID;
|
||||
ga_append(gap, '{');
|
||||
|
||||
for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int;
|
||||
++hi)
|
||||
if (!HASHITEM_EMPTY(hi))
|
||||
{
|
||||
--todo;
|
||||
if (first)
|
||||
first = FALSE;
|
||||
else
|
||||
ga_append(gap, ',');
|
||||
if ((options & JSON_JS)
|
||||
// Find first non-empty hash entry
|
||||
for (hi = d->dv_hashtab.ht_array;
|
||||
HASHITEM_EMPTY(hi); ++hi)
|
||||
;
|
||||
--todo;
|
||||
|
||||
// Write first key
|
||||
if ((options & JSON_JS)
|
||||
&& is_simple_key(hi->hi_key))
|
||||
ga_concat(gap, hi->hi_key);
|
||||
else
|
||||
write_string(gap, hi->hi_key);
|
||||
ga_append(gap, ':');
|
||||
if (json_encode_item(gap, &dict_lookup(hi)->di_tv,
|
||||
copyID, options | JSON_NO_NONE,
|
||||
depth + 1) == FAIL)
|
||||
return FAIL;
|
||||
ga_concat(gap, hi->hi_key);
|
||||
else
|
||||
write_string(gap, hi->hi_key);
|
||||
ga_append(gap, ':');
|
||||
|
||||
if (stack.ga_len >= p_mfd)
|
||||
{
|
||||
emsg(_(e_function_call_depth_is_higher_than_maxfuncdepth));
|
||||
goto theend;
|
||||
}
|
||||
ga_append(gap, '}');
|
||||
d->dv_copyID = 0;
|
||||
if (ga_grow(&stack, 1) == FAIL)
|
||||
goto theend;
|
||||
frame = ((json_enc_frame_T *)stack.ga_data)
|
||||
+ stack.ga_len;
|
||||
frame->je_type = ENC_DICT;
|
||||
frame->je_options = options;
|
||||
frame->je_u.d.dict = d;
|
||||
frame->je_u.d.hi = hi;
|
||||
frame->je_u.d.todo = todo;
|
||||
++stack.ga_len;
|
||||
options = options | JSON_NO_NONE;
|
||||
cur_val = &dict_lookup(hi)->di_tv;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
GA_CONCAT_LITERAL(gap, "{}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VAR_FLOAT:
|
||||
#if defined(HAVE_MATH_H)
|
||||
if (isnan(val->vval.v_float))
|
||||
if (isnan(cur_val->vval.v_float))
|
||||
GA_CONCAT_LITERAL(gap, "NaN");
|
||||
else if (isinf(val->vval.v_float))
|
||||
else if (isinf(cur_val->vval.v_float))
|
||||
{
|
||||
if (val->vval.v_float < 0.0)
|
||||
if (cur_val->vval.v_float < 0.0)
|
||||
GA_CONCAT_LITERAL(gap, "-Infinity");
|
||||
else
|
||||
GA_CONCAT_LITERAL(gap, "Infinity");
|
||||
@@ -499,17 +549,151 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept
|
||||
size_t numbuflen;
|
||||
|
||||
numbuflen = vim_snprintf_safelen((char *)numbuf, sizeof(numbuf),
|
||||
"%g", val->vval.v_float);
|
||||
"%g", cur_val->vval.v_float);
|
||||
ga_concat_len(gap, numbuf, numbuflen);
|
||||
}
|
||||
break;
|
||||
|
||||
case VAR_UNKNOWN:
|
||||
case VAR_ANY:
|
||||
case VAR_VOID:
|
||||
internal_error_no_abort("json_encode_item()");
|
||||
return FAIL;
|
||||
goto theend;
|
||||
}
|
||||
return OK;
|
||||
|
||||
// Process the completed item by advancing containers or
|
||||
// returning when the stack is empty.
|
||||
for (;;)
|
||||
{
|
||||
int advance = FALSE;
|
||||
|
||||
if (stack.ga_len == 0)
|
||||
{
|
||||
ga_clear(&stack);
|
||||
return OK;
|
||||
}
|
||||
|
||||
frame = ((json_enc_frame_T *)stack.ga_data)
|
||||
+ stack.ga_len - 1;
|
||||
switch (frame->je_type)
|
||||
{
|
||||
case ENC_LIST:
|
||||
{
|
||||
listitem_T *li = frame->je_u.l.li;
|
||||
|
||||
// JSON_JS: add trailing comma if last item is v:none
|
||||
if ((frame->je_options & JSON_JS)
|
||||
&& li->li_next == NULL
|
||||
&& li->li_tv.v_type == VAR_SPECIAL
|
||||
&& li->li_tv.vval.v_number == VVAL_NONE)
|
||||
ga_append(gap, ',');
|
||||
|
||||
li = li->li_next;
|
||||
frame->je_u.l.li = li;
|
||||
if (li != NULL && !got_int)
|
||||
{
|
||||
ga_append(gap, ',');
|
||||
options = frame->je_options & JSON_JS;
|
||||
cur_val = &li->li_tv;
|
||||
advance = TRUE;
|
||||
break;
|
||||
}
|
||||
ga_append(gap, ']');
|
||||
frame->je_u.l.list->lv_copyID = 0;
|
||||
--stack.ga_len;
|
||||
break;
|
||||
}
|
||||
|
||||
case ENC_TUPLE:
|
||||
{
|
||||
int idx = frame->je_u.t.idx;
|
||||
int len = frame->je_u.t.len;
|
||||
|
||||
// JSON_JS: add trailing comma if last item is v:none
|
||||
if ((frame->je_options & JSON_JS) && idx == len - 1)
|
||||
{
|
||||
typval_T *t_item =
|
||||
TUPLE_ITEM(frame->je_u.t.tuple, idx);
|
||||
|
||||
if (t_item->v_type == VAR_SPECIAL
|
||||
&& t_item->vval.v_number == VVAL_NONE)
|
||||
ga_append(gap, ',');
|
||||
}
|
||||
|
||||
++idx;
|
||||
frame->je_u.t.idx = idx;
|
||||
if (idx < len && !got_int)
|
||||
{
|
||||
ga_append(gap, ',');
|
||||
options = frame->je_options & JSON_JS;
|
||||
cur_val = TUPLE_ITEM(frame->je_u.t.tuple, idx);
|
||||
advance = TRUE;
|
||||
break;
|
||||
}
|
||||
ga_append(gap, ']');
|
||||
frame->je_u.t.tuple->tv_copyID = 0;
|
||||
--stack.ga_len;
|
||||
break;
|
||||
}
|
||||
|
||||
case ENC_DICT:
|
||||
{
|
||||
hashitem_T *hi = frame->je_u.d.hi;
|
||||
int todo = frame->je_u.d.todo;
|
||||
|
||||
if (todo > 0 && !got_int)
|
||||
{
|
||||
// Advance to next non-empty entry
|
||||
for (++hi; HASHITEM_EMPTY(hi); ++hi)
|
||||
;
|
||||
--todo;
|
||||
frame->je_u.d.hi = hi;
|
||||
frame->je_u.d.todo = todo;
|
||||
|
||||
ga_append(gap, ',');
|
||||
if ((frame->je_options & JSON_JS)
|
||||
&& is_simple_key(hi->hi_key))
|
||||
ga_concat(gap, hi->hi_key);
|
||||
else
|
||||
write_string(gap, hi->hi_key);
|
||||
ga_append(gap, ':');
|
||||
|
||||
options = frame->je_options | JSON_NO_NONE;
|
||||
cur_val = &dict_lookup(hi)->di_tv;
|
||||
advance = TRUE;
|
||||
break;
|
||||
}
|
||||
ga_append(gap, '}');
|
||||
frame->je_u.d.dict->dv_copyID = 0;
|
||||
--stack.ga_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (advance)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
theend:
|
||||
// Clean up copyIDs for remaining frames on error/failure
|
||||
for (i = 0; i < stack.ga_len; i++)
|
||||
{
|
||||
frame = ((json_enc_frame_T *)stack.ga_data) + i;
|
||||
switch (frame->je_type)
|
||||
{
|
||||
case ENC_LIST:
|
||||
frame->je_u.l.list->lv_copyID = 0;
|
||||
break;
|
||||
case ENC_TUPLE:
|
||||
frame->je_u.t.tuple->tv_copyID = 0;
|
||||
break;
|
||||
case ENC_DICT:
|
||||
frame->je_u.d.dict->dv_copyID = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ga_clear(&stack);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -7,7 +7,7 @@ The original can be found:
|
||||
https://github.com/neovim/libvterm
|
||||
|
||||
Modifications:
|
||||
- revisions up to 839 have been included
|
||||
- revisions up to 843 have been included
|
||||
- Added a .gitignore file.
|
||||
- use TRUE and FALSE instead of true and false
|
||||
- use int or unsigned int instead of bool
|
||||
|
||||
@@ -452,6 +452,8 @@ typedef struct {
|
||||
int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
|
||||
int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
|
||||
int (*sb_clear)(void *user);
|
||||
// ABI-compat only enabled if vterm_state_callbacks_has_premove() is invoked
|
||||
int (*premove)(VTermRect dest, void *user);
|
||||
} VTermStateCallbacks;
|
||||
|
||||
// VIM: added
|
||||
@@ -488,6 +490,8 @@ VTermState *vterm_obtain_state(VTerm *vt);
|
||||
void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user);
|
||||
void *vterm_state_get_cbdata(VTermState *state);
|
||||
|
||||
void vterm_state_callbacks_has_premove(VTermState *state);
|
||||
|
||||
void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user);
|
||||
void *vterm_state_get_unrecognised_fbdata(VTermState *state);
|
||||
|
||||
@@ -578,6 +582,8 @@ typedef struct {
|
||||
int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
|
||||
int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
|
||||
int (*sb_clear)(void* user);
|
||||
/* ABI-compat this is only used if vterm_screen_callbacks_has_pushline4() is called */
|
||||
int (*sb_pushline4)(int cols, const VTermScreenCell *cells, int continuation, void *user);
|
||||
} VTermScreenCallbacks;
|
||||
|
||||
// Return the screen of the vterm.
|
||||
@@ -590,6 +596,8 @@ VTermScreen *vterm_obtain_screen(VTerm *vt);
|
||||
void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user);
|
||||
void *vterm_screen_get_cbdata(VTermScreen *screen);
|
||||
|
||||
void vterm_screen_callbacks_has_pushline4(VTermScreen *screen);
|
||||
|
||||
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user);
|
||||
void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen);
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ struct VTermScreen
|
||||
|
||||
const VTermScreenCallbacks *callbacks;
|
||||
void *cbdata;
|
||||
int callbacks_has_pushline4;
|
||||
|
||||
VTermDamageSize damage_merge;
|
||||
/* start_row == -1 => no damage */
|
||||
@@ -209,28 +210,41 @@ static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void sb_pushline_from_row(VTermScreen *screen, int row)
|
||||
static void sb_pushline_from_row(VTermScreen *screen, int row, int continuation)
|
||||
{
|
||||
VTermPos pos;
|
||||
pos.row = row;
|
||||
for(pos.col = 0; pos.col < screen->cols; pos.col++)
|
||||
vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col);
|
||||
|
||||
(screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
|
||||
if(screen->callbacks_has_pushline4 && screen->callbacks->sb_pushline4)
|
||||
(screen->callbacks->sb_pushline4)(screen->cols, screen->sb_buffer, continuation, screen->cbdata);
|
||||
else
|
||||
(screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
|
||||
}
|
||||
|
||||
static int premove(VTermRect rect, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
|
||||
if(((screen->callbacks && screen->callbacks->sb_pushline) ||
|
||||
(screen->callbacks_has_pushline4 && screen->callbacks && screen->callbacks->sb_pushline4)) &&
|
||||
rect.start_row == 0 && rect.start_col == 0 && // starts top-left corner
|
||||
rect.end_col == screen->cols && // full width
|
||||
screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen
|
||||
for(int row = 0; row < rect.end_row; row++) {
|
||||
const VTermLineInfo *lineinfo = vterm_state_get_lineinfo(screen->state, row);
|
||||
sb_pushline_from_row(screen, row, lineinfo->continuation);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int moverect_internal(VTermRect dest, VTermRect src, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
|
||||
if(screen->callbacks && screen->callbacks->sb_pushline &&
|
||||
dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner
|
||||
dest.end_col == screen->cols && // full width
|
||||
screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen
|
||||
for(int row = 0; row < src.start_row; row++)
|
||||
sb_pushline_from_row(screen, row);
|
||||
}
|
||||
|
||||
int cols = src.end_col - src.start_col;
|
||||
int downward = src.start_row - dest.start_row;
|
||||
|
||||
@@ -685,9 +699,12 @@ static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new
|
||||
|
||||
if(old_row >= 0 && bufidx == BUFIDX_PRIMARY) {
|
||||
/* Push spare lines to scrollback buffer */
|
||||
if(screen->callbacks && screen->callbacks->sb_pushline)
|
||||
for(int row = 0; row <= old_row; row++)
|
||||
sb_pushline_from_row(screen, row);
|
||||
if((screen->callbacks && screen->callbacks->sb_pushline) ||
|
||||
(screen->callbacks_has_pushline4 && screen->callbacks && screen->callbacks->sb_pushline4))
|
||||
for(int row = 0; row <= old_row; row++) {
|
||||
const VTermLineInfo *lineinfo = old_lineinfo + row;
|
||||
sb_pushline_from_row(screen, row, lineinfo->continuation);
|
||||
}
|
||||
if(active)
|
||||
statefields->pos.row -= (old_row + 1);
|
||||
}
|
||||
@@ -880,6 +897,7 @@ static VTermStateCallbacks state_cbs = {
|
||||
&resize, // resize
|
||||
&setlineinfo, // setlineinfo
|
||||
&sb_clear, //sb_clear
|
||||
&premove, // premove
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -914,6 +932,7 @@ static VTermScreen *screen_new(VTerm *vt)
|
||||
|
||||
screen->callbacks = NULL;
|
||||
screen->cbdata = NULL;
|
||||
screen->callbacks_has_pushline4 = 0;
|
||||
|
||||
screen->buffers[BUFIDX_PRIMARY] = alloc_buffer(screen, rows, cols);
|
||||
|
||||
@@ -927,6 +946,7 @@ static VTermScreen *screen_new(VTerm *vt)
|
||||
}
|
||||
|
||||
vterm_state_set_callbacks(screen->state, &state_cbs, screen);
|
||||
vterm_state_callbacks_has_premove(screen->state);
|
||||
|
||||
return screen;
|
||||
}
|
||||
@@ -1127,6 +1147,11 @@ void *vterm_screen_get_cbdata(VTermScreen *screen)
|
||||
return screen->cbdata;
|
||||
}
|
||||
|
||||
void vterm_screen_callbacks_has_pushline4(VTermScreen *screen)
|
||||
{
|
||||
screen->callbacks_has_pushline4 = 1;
|
||||
}
|
||||
|
||||
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user)
|
||||
{
|
||||
vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user);
|
||||
|
||||
@@ -78,6 +78,7 @@ static VTermState *vterm_state_new(VTerm *vt)
|
||||
|
||||
state->callbacks = NULL;
|
||||
state->cbdata = NULL;
|
||||
state->callbacks_has_premove = 0;
|
||||
|
||||
state->selection.callbacks = NULL;
|
||||
state->selection.user = NULL;
|
||||
@@ -133,6 +134,33 @@ static void scroll(VTermState *state, VTermRect rect, int downward, int rightwar
|
||||
else if(rightward < -cols)
|
||||
rightward = -cols;
|
||||
|
||||
if(state->callbacks_has_premove && state->callbacks && state->callbacks->premove) {
|
||||
// TODO: technically this logic is wrong if both downward != 0 and rightward != 0
|
||||
|
||||
/* Work out what subsection of the destination area is about to be destroyed */
|
||||
if(downward > 0)
|
||||
/* about to destroy the top */
|
||||
(*state->callbacks->premove)((VTermRect){
|
||||
.start_row = rect.start_row, .end_row = rect.start_row + downward,
|
||||
.start_col = rect.start_col, .end_col = rect.end_col}, state->cbdata);
|
||||
else if(downward < 0)
|
||||
/* about to destroy the bottom */
|
||||
(*state->callbacks->premove)((VTermRect){
|
||||
.start_row = rect.end_row + downward, .end_row = rect.end_row,
|
||||
.start_col = rect.start_col, .end_col = rect.end_col}, state->cbdata);
|
||||
|
||||
if(rightward > 0)
|
||||
/* about to destroy the left */
|
||||
(*state->callbacks->premove)((VTermRect){
|
||||
.start_row = rect.start_row, .end_row = rect.end_row,
|
||||
.start_col = rect.start_col, .end_col = rect.start_col + rightward}, state->cbdata);
|
||||
else if(rightward < 0)
|
||||
/* about to destroy the right */
|
||||
(*state->callbacks->premove)((VTermRect){
|
||||
.start_row = rect.start_row, .end_row = rect.end_row,
|
||||
.start_col = rect.end_col + rightward, .end_col = rect.end_col}, state->cbdata);
|
||||
}
|
||||
|
||||
// Update lineinfo if full line
|
||||
if(rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) {
|
||||
int height = rect.end_row - rect.start_row - abs(downward);
|
||||
@@ -2319,6 +2347,11 @@ void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *cal
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_state_callbacks_has_premove(VTermState *state)
|
||||
{
|
||||
state->callbacks_has_premove = 1;
|
||||
}
|
||||
|
||||
void *vterm_state_get_cbdata(VTermState *state)
|
||||
{
|
||||
return state->cbdata;
|
||||
|
||||
@@ -70,6 +70,7 @@ struct VTermState
|
||||
|
||||
const VTermStateCallbacks *callbacks;
|
||||
void *cbdata;
|
||||
int callbacks_has_premove;
|
||||
|
||||
const VTermStateFallbacks *fallbacks;
|
||||
void *fbdata;
|
||||
|
||||
@@ -127,24 +127,28 @@ PUSH "\e[100;105r\eD"
|
||||
PUSH "\e[5;2r\eD"
|
||||
|
||||
RESET
|
||||
WANTSTATE -s+me
|
||||
WANTSTATE -s+Pme
|
||||
|
||||
!Scroll Down move+erase emulation
|
||||
PUSH "\e[S"
|
||||
premove 0..1,0..80
|
||||
moverect 1..25,0..80 -> 0..24,0..80
|
||||
erase 24..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "\e[2S"
|
||||
premove 0..2,0..80
|
||||
moverect 2..25,0..80 -> 0..23,0..80
|
||||
erase 23..25,0..80
|
||||
?cursor = 0,0
|
||||
|
||||
!Scroll Up move+erase emulation
|
||||
PUSH "\e[T"
|
||||
premove 24..25,0..80
|
||||
moverect 0..24,0..80 -> 1..25,0..80
|
||||
erase 0..1,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "\e[2T"
|
||||
premove 23..25,0..80
|
||||
moverect 0..23,0..80 -> 2..25,0..80
|
||||
erase 0..2,0..80
|
||||
?cursor = 0,0
|
||||
|
||||
@@ -268,7 +268,7 @@ PUSH "\e[2\"q"
|
||||
PUSH "\eP\$q\"q\e\\"
|
||||
output "\eP1\$r2\"q\e\\"
|
||||
|
||||
WANTSTATE -s+m
|
||||
WANTSTATE -s+Pm
|
||||
|
||||
!ICH move+erase emuation
|
||||
RESET
|
||||
@@ -278,12 +278,14 @@ PUSH "ACD"
|
||||
PUSH "\e[2D"
|
||||
?cursor = 0,1
|
||||
PUSH "\e[@"
|
||||
premove 0..1,79..80
|
||||
moverect 0..1,1..79 -> 0..1,2..80
|
||||
erase 0..1,1..2
|
||||
?cursor = 0,1
|
||||
PUSH "B"
|
||||
?cursor = 0,2
|
||||
PUSH "\e[3@"
|
||||
premove 0..1,77..80
|
||||
moverect 0..1,2..77 -> 0..1,5..80
|
||||
erase 0..1,2..5
|
||||
|
||||
@@ -295,10 +297,12 @@ PUSH "ABBC"
|
||||
PUSH "\e[3D"
|
||||
?cursor = 0,1
|
||||
PUSH "\e[P"
|
||||
premove 0..1,1..2
|
||||
moverect 0..1,2..80 -> 0..1,1..79
|
||||
erase 0..1,79..80
|
||||
?cursor = 0,1
|
||||
PUSH "\e[3P"
|
||||
premove 0..1,1..4
|
||||
moverect 0..1,4..80 -> 0..1,1..77
|
||||
erase 0..1,77..80
|
||||
?cursor = 0,1
|
||||
|
||||
@@ -146,9 +146,9 @@ PUSH "\e[25H\r\nABCDE\b\b\b\e[2P\r\n"
|
||||
sb_pushline 80 =
|
||||
moverect 1..25,0..80 -> 0..24,0..80
|
||||
damage 24..25,0..80 = 24<41 42 43 44 45>
|
||||
sb_pushline 80 =
|
||||
moverect 24..25,4..80 -> 24..25,2..78
|
||||
damage 24..25,78..80
|
||||
sb_pushline 80 =
|
||||
DAMAGEFLUSH
|
||||
moverect 1..25,0..80 -> 0..24,0..80
|
||||
damage 24..25,0..80
|
||||
|
||||
17
src/libvterm/t/69screen_pushline.test
Normal file
17
src/libvterm/t/69screen_pushline.test
Normal file
@@ -0,0 +1,17 @@
|
||||
INIT
|
||||
WANTSTATE
|
||||
WANTSCREEN b
|
||||
|
||||
RESET
|
||||
|
||||
!Spillover text marks continuation on second line
|
||||
PUSH "A"x85
|
||||
PUSH "\r\n"
|
||||
?lineinfo 0 =
|
||||
?lineinfo 1 = cont
|
||||
|
||||
!Continuation mark sent to sb_pushline
|
||||
PUSH "\n"x23
|
||||
sb_pushline 80 = 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
|
||||
PUSH "\n"
|
||||
sb_pushline 80 cont = 41 41 41 41 41
|
||||
@@ -326,6 +326,18 @@ static int movecursor(VTermPos pos, VTermPos oldpos UNUSED, int visible UNUSED,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int want_premove = 0;
|
||||
static int premove(VTermRect rect, void *user UNUSED)
|
||||
{
|
||||
if(!want_premove)
|
||||
return 0;
|
||||
|
||||
printf("premove %d..%d,%d..%d\n",
|
||||
rect.start_row, rect.end_row, rect.start_col, rect.end_col);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int want_scrollrect = 0;
|
||||
static int scrollrect(VTermRect rect, int downward, int rightward, void *user UNUSED)
|
||||
{
|
||||
@@ -509,6 +521,7 @@ VTermStateCallbacks state_cbs = {
|
||||
NULL, // resize
|
||||
state_setlineinfo, // setlineinfo
|
||||
state_sb_clear, // sb_clear
|
||||
premove, // premove
|
||||
};
|
||||
|
||||
static int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user UNUSED)
|
||||
@@ -590,7 +603,7 @@ static int screen_damage(VTermRect rect, void *user UNUSED)
|
||||
}
|
||||
|
||||
static int want_screen_scrollback = 0;
|
||||
static int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user UNUSED)
|
||||
static int screen_sb_pushline4(int cols, const VTermScreenCell *cells, int continuation, void *user UNUSED)
|
||||
{
|
||||
int eol;
|
||||
int c;
|
||||
@@ -602,7 +615,7 @@ static int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user
|
||||
while(eol && !cells[eol-1].chars[0])
|
||||
eol--;
|
||||
|
||||
printf("sb_pushline %d =", cols);
|
||||
printf("sb_pushline %d%s =", cols, continuation ? " cont" : "");
|
||||
for(c = 0; c < eol; c++)
|
||||
printf(" %02X", cells[c].chars[0]);
|
||||
printf("\n");
|
||||
@@ -647,9 +660,10 @@ VTermScreenCallbacks screen_cbs = {
|
||||
settermprop, // settermprop
|
||||
NULL, // bell
|
||||
NULL, // resize
|
||||
screen_sb_pushline, // sb_pushline
|
||||
NULL, // sb_pushline
|
||||
screen_sb_popline, // sb_popline
|
||||
screen_sb_clear, // sb_clear
|
||||
screen_sb_pushline4, // sb_pushline4
|
||||
};
|
||||
|
||||
int main(int argc UNUSED, char **argv UNUSED)
|
||||
@@ -689,6 +703,7 @@ int main(int argc UNUSED, char **argv UNUSED)
|
||||
if(!state) {
|
||||
state = vterm_obtain_state(vt);
|
||||
vterm_state_set_callbacks(state, &state_cbs, NULL);
|
||||
vterm_state_callbacks_has_premove(state);
|
||||
/* In some tests we want to check the behaviour of overflowing the
|
||||
* buffer, so make it nicely small
|
||||
*/
|
||||
@@ -713,6 +728,9 @@ int main(int argc UNUSED, char **argv UNUSED)
|
||||
case 's':
|
||||
want_scrollrect = sense;
|
||||
break;
|
||||
case 'P':
|
||||
want_premove = sense;
|
||||
break;
|
||||
case 'm':
|
||||
want_moverect = sense;
|
||||
break;
|
||||
@@ -740,6 +758,7 @@ int main(int argc UNUSED, char **argv UNUSED)
|
||||
if(!screen)
|
||||
screen = vterm_obtain_screen(vt);
|
||||
vterm_screen_set_callbacks(screen, &screen_cbs, NULL);
|
||||
vterm_screen_callbacks_has_pushline4(screen);
|
||||
|
||||
while(line[i] == ' ')
|
||||
i++;
|
||||
|
||||
@@ -124,7 +124,7 @@ sub do_line
|
||||
elsif( $line =~ m/^putglyph (\S+) (.*)$/ ) {
|
||||
$line = "putglyph " . join( ",", map sprintf("%x", $_), eval($1) ) . " $2";
|
||||
}
|
||||
elsif( $line =~ m/^(?:movecursor|scrollrect|moverect|erase|damage|sb_pushline|sb_popline|sb_clear|settermprop|setmousefunc|selection-query) ?/ ) {
|
||||
elsif( $line =~ m/^(?:movecursor|scrollrect|premove|moverect|erase|damage|sb_pushline|sb_popline|sb_clear|settermprop|setmousefunc|selection-query) ?/ ) {
|
||||
# no conversion
|
||||
}
|
||||
elsif( $line =~ m/^(selection-set) (.*?) (\[?)(.*?)(\]?)$/ ) {
|
||||
|
||||
@@ -1368,7 +1368,8 @@ list_slice_or_index(
|
||||
{
|
||||
// copy the item to "var1" to avoid that freeing the list makes it
|
||||
// invalid.
|
||||
listitem_T *li = check_range_index_one(list, (long *)&n1, TRUE, TRUE);
|
||||
long index = n1;
|
||||
listitem_T *li = check_range_index_one(list, &index, TRUE, TRUE);
|
||||
if (li == NULL)
|
||||
return FAIL;
|
||||
copy_tv(&li->li_tv, &var1);
|
||||
|
||||
70
src/mouse.c
70
src/mouse.c
@@ -21,6 +21,9 @@ static long mouse_hor_step = 6;
|
||||
static long mouse_vert_step = 3;
|
||||
static win_T *dragwin = NULL; // window being dragged
|
||||
static int stl_click_handler(win_T *wp, int mcol, int which_button, int mods);
|
||||
static int stl_click_handler_regions(stl_click_region_T *regions,
|
||||
int region_count, int winid, int mcol,
|
||||
int which_button, int mods);
|
||||
|
||||
void
|
||||
mouse_set_vert_scroll_step(long step)
|
||||
@@ -502,6 +505,16 @@ do_mouse(
|
||||
// Check for clicking in the tab page line.
|
||||
if (TabPageIdxs != NULL && mouse_row == 0 && firstwin->w_winrow > 0)
|
||||
{
|
||||
// Dispatch 'tabline' %[FuncName] click regions before falling through
|
||||
// to tab-page selection. On drag events fall through to the normal
|
||||
// tab-drag handling.
|
||||
if (is_click && !is_drag
|
||||
&& stl_click_handler_regions(tabline_stl_click,
|
||||
tabline_stl_click_count,
|
||||
0, mouse_col, which_button,
|
||||
mod_mask))
|
||||
return FALSE;
|
||||
|
||||
tp_label.just_in = true;
|
||||
tp_label.nr = TabPageIdxs[mouse_col];
|
||||
|
||||
@@ -1640,11 +1653,19 @@ mouse_model_popup(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Call a statusline click handler function.
|
||||
* Call a click-region callback function.
|
||||
* "regions"/"region_count" describe the resolved click regions,
|
||||
* "winid" is stored as the "winid" key in the info dict (0 for tabline).
|
||||
* Returns TRUE if the function was called and handled the click.
|
||||
*/
|
||||
static int
|
||||
stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
stl_click_handler_regions(
|
||||
stl_click_region_T *regions,
|
||||
int region_count,
|
||||
int winid,
|
||||
int mcol,
|
||||
int which_button,
|
||||
int mods)
|
||||
{
|
||||
#ifdef FEAT_EVAL
|
||||
int n;
|
||||
@@ -1658,17 +1679,16 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
funcexe_T funcexe;
|
||||
int col = mcol;
|
||||
|
||||
if (wp == NULL || wp->w_stl_click == NULL || wp->w_stl_click_count == 0)
|
||||
if (regions == NULL || region_count == 0)
|
||||
return FALSE;
|
||||
|
||||
// Find the click region at the given column.
|
||||
for (n = 0; n < wp->w_stl_click_count; n++)
|
||||
for (n = 0; n < region_count; n++)
|
||||
{
|
||||
if (col >= wp->w_stl_click[n].col_start
|
||||
&& col < wp->w_stl_click[n].col_end)
|
||||
if (col >= regions[n].col_start && col < regions[n].col_end)
|
||||
break;
|
||||
}
|
||||
if (n >= wp->w_stl_click_count || wp->w_stl_click[n].funcname == NULL)
|
||||
if (n >= region_count || regions[n].funcname == NULL)
|
||||
return FALSE;
|
||||
|
||||
// Build the info dictionary.
|
||||
@@ -1676,7 +1696,7 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
if (info == NULL)
|
||||
return FALSE;
|
||||
|
||||
dict_add_number(info, "minwid", wp->w_stl_click[n].minwid);
|
||||
dict_add_number(info, "minwid", regions[n].minwid);
|
||||
|
||||
// Determine number of clicks.
|
||||
// MOD_MASK_2CLICK=0x20, MOD_MASK_3CLICK=0x40, MOD_MASK_4CLICK=0x60
|
||||
@@ -1705,7 +1725,13 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
mods_str[mi] = NUL;
|
||||
dict_add_string(info, "mods", mods_str);
|
||||
|
||||
dict_add_number(info, "winid", wp->w_id);
|
||||
dict_add_number(info, "winid", winid);
|
||||
|
||||
// "area": which option the clicked region belongs to. Lets a shared
|
||||
// dispatcher distinguish 'statusline' from 'tabline' (and future areas)
|
||||
// without having to overload winid == 0.
|
||||
dict_add_string(info, "area",
|
||||
winid == 0 ? (char_u *)"tabline" : (char_u *)"statusline");
|
||||
|
||||
// Call the function with the info dict as argument.
|
||||
argvars[0].v_type = VAR_DICT;
|
||||
@@ -1718,7 +1744,7 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
|
||||
CLEAR_FIELD(funcexe);
|
||||
funcexe.fe_evaluate = TRUE;
|
||||
(void)call_func(wp->w_stl_click[n].funcname, -1,
|
||||
(void)call_func(regions[n].funcname, -1,
|
||||
&rettv, 1, argvars, &funcexe);
|
||||
|
||||
n = (int)rettv.vval.v_number;
|
||||
@@ -1726,11 +1752,20 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
dict_unref(info);
|
||||
|
||||
if (n != 0)
|
||||
{
|
||||
// Make sure the tabline gets redrawn too when the callback asks for
|
||||
// a redraw (redraw_statuslines() only redraws the tabline when
|
||||
// redraw_tabline is set).
|
||||
if (winid == 0)
|
||||
redraw_tabline = TRUE;
|
||||
redraw_statuslines();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
(void)wp;
|
||||
(void)regions;
|
||||
(void)region_count;
|
||||
(void)winid;
|
||||
(void)mcol;
|
||||
(void)which_button;
|
||||
(void)mods;
|
||||
@@ -1738,6 +1773,19 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Call a statusline click handler function for window "wp".
|
||||
* Returns TRUE if the function was called and handled the click.
|
||||
*/
|
||||
static int
|
||||
stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
{
|
||||
if (wp == NULL)
|
||||
return FALSE;
|
||||
return stl_click_handler_regions(wp->w_stl_click, wp->w_stl_click_count,
|
||||
wp->w_id, mcol, which_button, mods);
|
||||
}
|
||||
|
||||
// dragwin is declared near the top of the file
|
||||
|
||||
/*
|
||||
|
||||
@@ -123,6 +123,8 @@ typedef struct
|
||||
char_u *pattern;
|
||||
int nsubexp; // number of ()
|
||||
int nstate;
|
||||
void *listbuf[2]; // cached list buffers for
|
||||
// nfa_regmatch()
|
||||
nfa_state_T state[1]; // actually longer..
|
||||
} nfa_regprog_T;
|
||||
|
||||
|
||||
@@ -5797,9 +5797,21 @@ nfa_regmatch(
|
||||
// Allocate memory for the lists of nodes.
|
||||
size = (prog->nstate + 1) * sizeof(nfa_thread_T);
|
||||
|
||||
list[0].t = alloc(size);
|
||||
// Reuse cached list buffers from prog when available (top-level call).
|
||||
// Recursive calls must allocate their own buffers.
|
||||
if (toplevel && prog->listbuf[0] != NULL)
|
||||
{
|
||||
list[0].t = (nfa_thread_T *)prog->listbuf[0];
|
||||
list[1].t = (nfa_thread_T *)prog->listbuf[1];
|
||||
prog->listbuf[0] = NULL;
|
||||
prog->listbuf[1] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
list[0].t = alloc(size);
|
||||
list[1].t = alloc(size);
|
||||
}
|
||||
list[0].len = prog->nstate + 1;
|
||||
list[1].t = alloc(size);
|
||||
list[1].len = prog->nstate + 1;
|
||||
if (list[0].t == NULL || list[1].t == NULL)
|
||||
goto theend;
|
||||
@@ -7287,9 +7299,18 @@ nextchar:
|
||||
#endif
|
||||
|
||||
theend:
|
||||
// Free memory
|
||||
vim_free(list[0].t);
|
||||
vim_free(list[1].t);
|
||||
// Cache list buffers in prog for reuse, or free if prog already has
|
||||
// cached buffers (recursive call case).
|
||||
if (prog->listbuf[0] == NULL && list[0].t != NULL && list[1].t != NULL)
|
||||
{
|
||||
prog->listbuf[0] = list[0].t;
|
||||
prog->listbuf[1] = list[1].t;
|
||||
}
|
||||
else
|
||||
{
|
||||
vim_free(list[0].t);
|
||||
vim_free(list[1].t);
|
||||
}
|
||||
vim_free(listids);
|
||||
#undef ADD_STATE_IF_MATCH
|
||||
#ifdef NFA_REGEXP_DEBUG_LOG
|
||||
@@ -7644,6 +7665,8 @@ nfa_regcomp(char_u *expr, int re_flags)
|
||||
goto fail;
|
||||
state_ptr = prog->state;
|
||||
prog->re_in_use = FALSE;
|
||||
prog->listbuf[0] = NULL;
|
||||
prog->listbuf[1] = NULL;
|
||||
|
||||
/*
|
||||
* PASS 2
|
||||
@@ -7707,6 +7730,8 @@ nfa_regfree(regprog_T *prog)
|
||||
|
||||
vim_free(((nfa_regprog_T *)prog)->match_text);
|
||||
vim_free(((nfa_regprog_T *)prog)->pattern);
|
||||
vim_free(((nfa_regprog_T *)prog)->listbuf[0]);
|
||||
vim_free(((nfa_regprog_T *)prog)->listbuf[1]);
|
||||
vim_free(prog);
|
||||
}
|
||||
|
||||
|
||||
46
src/screen.c
46
src/screen.c
@@ -1486,23 +1486,41 @@ win_redr_custom(
|
||||
TabPageIdxs[col++] = fillchar;
|
||||
}
|
||||
|
||||
// Resolve click function regions for statusline.
|
||||
if (wp != NULL && !draw_ruler)
|
||||
// Resolve click function regions for statusline or tabline.
|
||||
if (!draw_ruler)
|
||||
{
|
||||
int click_count = 0;
|
||||
stl_click_region_T **out_regions;
|
||||
int *out_count;
|
||||
int base_col;
|
||||
int click_count = 0;
|
||||
|
||||
if (wp != NULL)
|
||||
{
|
||||
out_regions = &wp->w_stl_click;
|
||||
out_count = &wp->w_stl_click_count;
|
||||
base_col = wp->w_wincol;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 'tabline': store regions in global state since there is no
|
||||
// associated window.
|
||||
out_regions = &tabline_stl_click;
|
||||
out_count = &tabline_stl_click_count;
|
||||
base_col = firstwin->w_wincol;
|
||||
}
|
||||
|
||||
// Count the click regions.
|
||||
for (n = 0; clicktab[n].start != NULL; n++)
|
||||
click_count++;
|
||||
|
||||
// Free old click regions.
|
||||
if (wp->w_stl_click != NULL)
|
||||
if (*out_regions != NULL)
|
||||
{
|
||||
for (n = 0; n < wp->w_stl_click_count; n++)
|
||||
vim_free(wp->w_stl_click[n].funcname);
|
||||
VIM_CLEAR(wp->w_stl_click);
|
||||
for (n = 0; n < *out_count; n++)
|
||||
vim_free((*out_regions)[n].funcname);
|
||||
VIM_CLEAR(*out_regions);
|
||||
}
|
||||
wp->w_stl_click_count = 0;
|
||||
*out_count = 0;
|
||||
|
||||
if (click_count > 0)
|
||||
{
|
||||
@@ -1514,7 +1532,7 @@ win_redr_custom(
|
||||
{
|
||||
char_u *cur_funcname = NULL;
|
||||
int cur_minwid = 0;
|
||||
int region_start = wp->w_wincol;
|
||||
int region_start = base_col;
|
||||
|
||||
// Walk through click records converting buffer positions
|
||||
// to screen columns.
|
||||
@@ -1530,7 +1548,7 @@ win_redr_custom(
|
||||
if (cur_funcname != NULL)
|
||||
{
|
||||
regions[rcount].col_start = region_start;
|
||||
regions[rcount].col_end = wp->w_wincol + len;
|
||||
regions[rcount].col_end = base_col + len;
|
||||
regions[rcount].funcname =
|
||||
vim_strsave(cur_funcname);
|
||||
regions[rcount].minwid = cur_minwid;
|
||||
@@ -1539,22 +1557,22 @@ win_redr_custom(
|
||||
|
||||
cur_funcname = clicktab[n].funcname;
|
||||
cur_minwid = clicktab[n].minwid;
|
||||
region_start = wp->w_wincol + len;
|
||||
region_start = base_col + len;
|
||||
}
|
||||
|
||||
// Close final region if it extends to the end.
|
||||
if (cur_funcname != NULL)
|
||||
{
|
||||
regions[rcount].col_start = region_start;
|
||||
regions[rcount].col_end = wp->w_wincol + maxwidth;
|
||||
regions[rcount].col_end = base_col + maxwidth;
|
||||
regions[rcount].funcname =
|
||||
vim_strsave(cur_funcname);
|
||||
regions[rcount].minwid = cur_minwid;
|
||||
rcount++;
|
||||
}
|
||||
|
||||
wp->w_stl_click = regions;
|
||||
wp->w_stl_click_count = rcount;
|
||||
*out_regions = regions;
|
||||
*out_count = rcount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
292
src/terminal.c
292
src/terminal.c
@@ -61,9 +61,11 @@ typedef struct {
|
||||
|
||||
typedef struct sb_line_S {
|
||||
int sb_cols; // can differ per line
|
||||
int sb_bytes; // length in bytes of text
|
||||
cellattr_T *sb_cells; // allocated
|
||||
cellattr_T sb_fill_attr; // for short line
|
||||
char_u *sb_text; // for tl_scrollback_postponed
|
||||
char_u continuation;
|
||||
} sb_line_T;
|
||||
|
||||
#ifdef MSWIN
|
||||
@@ -148,6 +150,8 @@ struct terminal_S {
|
||||
garray_T tl_scrollback;
|
||||
int tl_scrollback_scrolled;
|
||||
garray_T tl_scrollback_postponed;
|
||||
int tl_scrollback_snapshot;
|
||||
int tl_buffer_scrolled;
|
||||
|
||||
char_u *tl_highlight_name; // replaces "Terminal"; allocated
|
||||
|
||||
@@ -1386,6 +1390,112 @@ update_cursor(term_T *term, int redraw)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the location of a scrollbackline in the buffer
|
||||
*/
|
||||
static void
|
||||
scrollbackline_pos_in_buf(term_T *term, int row, linenr_T *lnum, int *start_col, size_t *start_pos)
|
||||
{
|
||||
sb_line_T *lines = (sb_line_T *)term->tl_scrollback.ga_data;
|
||||
linenr_T calc_lnum = term->tl_buffer_scrolled;
|
||||
size_t calc_pos = 0;
|
||||
int calc_col = 0;
|
||||
int i;
|
||||
|
||||
if (row < 0 || row >= term->tl_scrollback.ga_len)
|
||||
return;
|
||||
|
||||
if (row > term->tl_scrollback_scrolled)
|
||||
{
|
||||
// Lookback how far along in the top line we are
|
||||
for (i = term->tl_scrollback_scrolled + 1; i > 0 && lines[i].continuation; --i)
|
||||
{
|
||||
calc_pos += lines[i - 1].sb_bytes;
|
||||
calc_col += lines[i - 1].sb_cols;
|
||||
}
|
||||
i = term->tl_scrollback_scrolled + 1;
|
||||
calc_lnum = term->tl_buffer_scrolled + 1;
|
||||
// Do not count this line's bytes/cols twice
|
||||
if (lines[i].continuation)
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = 1;
|
||||
calc_lnum = 1;
|
||||
}
|
||||
|
||||
for (; i <= row; ++i)
|
||||
{
|
||||
if (!lines[i].continuation)
|
||||
{
|
||||
++calc_lnum;
|
||||
calc_pos = 0;
|
||||
calc_col = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
calc_pos += lines[i - 1].sb_bytes;
|
||||
calc_col += lines[i - 1].sb_cols;
|
||||
}
|
||||
}
|
||||
|
||||
*lnum = calc_lnum;
|
||||
if (start_col)
|
||||
*start_col = calc_col;
|
||||
if (start_pos)
|
||||
*start_pos = calc_pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the location of a buffer line in the scrollback
|
||||
*/
|
||||
static void
|
||||
bufline_pos_in_scrollback(term_T *term, linenr_T lnum, int col, int *row, int *wrapped_col)
|
||||
{
|
||||
buf_T *buf = term->tl_buffer;
|
||||
sb_line_T *lines = (sb_line_T *)term->tl_scrollback.ga_data;
|
||||
linenr_T calc_row = term->tl_scrollback_scrolled;
|
||||
int calc_col = col;
|
||||
linenr_T l;
|
||||
|
||||
if (lnum > buf->b_ml.ml_line_count)
|
||||
return;
|
||||
|
||||
if (lnum > term->tl_buffer_scrolled)
|
||||
{
|
||||
calc_row = term->tl_scrollback_scrolled;
|
||||
l = term->tl_buffer_scrolled + 1;
|
||||
|
||||
while (calc_row < term->tl_scrollback.ga_len && lines[calc_row].continuation)
|
||||
++calc_row;
|
||||
}
|
||||
else
|
||||
{
|
||||
calc_row = 0;
|
||||
l = 1;
|
||||
}
|
||||
|
||||
while (calc_row < term->tl_scrollback.ga_len && l < lnum)
|
||||
{
|
||||
++calc_row;
|
||||
if (!lines[calc_row].continuation)
|
||||
++l;
|
||||
}
|
||||
|
||||
while (calc_row + 1 < term->tl_scrollback.ga_len && lines[calc_row + 1].continuation
|
||||
&& calc_col >= lines[calc_row].sb_cols)
|
||||
{
|
||||
calc_col -= lines[calc_row].sb_cols;
|
||||
++calc_row;
|
||||
}
|
||||
|
||||
if (row)
|
||||
*row = calc_row;
|
||||
if (wrapped_col)
|
||||
*wrapped_col = calc_col;
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoked when "msg" output from a job was received. Write it to the terminal
|
||||
* of "buffer".
|
||||
@@ -1908,13 +2018,14 @@ term_try_stop_job(buf_T *buf)
|
||||
* Add the last line of the scrollback buffer to the buffer in the window.
|
||||
*/
|
||||
static void
|
||||
add_scrollback_line_to_buffer(term_T *term, char_u *text, int len)
|
||||
add_scrollback_line_to_buffer(term_T *term, char_u *text, int len, int append)
|
||||
{
|
||||
buf_T *buf = term->tl_buffer;
|
||||
int empty = (buf->b_ml.ml_flags & ML_EMPTY);
|
||||
linenr_T lnum = buf->b_ml.ml_line_count;
|
||||
|
||||
#ifdef MSWIN
|
||||
char_u *tmp = text;
|
||||
if (!enc_utf8 && enc_codepage > 0)
|
||||
{
|
||||
WCHAR *ret = NULL;
|
||||
@@ -1927,13 +2038,32 @@ add_scrollback_line_to_buffer(term_T *term, char_u *text, int len)
|
||||
WideCharToMultiByte_alloc(enc_codepage, 0,
|
||||
ret, length, (char **)&text, &len, 0, 0);
|
||||
vim_free(ret);
|
||||
ml_append_buf(term->tl_buffer, lnum, text, len, FALSE);
|
||||
vim_free(text);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
ml_append_buf(term->tl_buffer, lnum, text, len + 1, FALSE);
|
||||
|
||||
if (append)
|
||||
{
|
||||
char_u *prev_text = ml_get_buf(buf, lnum, FALSE);
|
||||
size_t prev_len = STRLEN(prev_text);
|
||||
|
||||
char_u *both = alloc(len + prev_len + 2);
|
||||
if (both == NULL)
|
||||
return;
|
||||
vim_strncpy(both, prev_text, prev_len + 1);
|
||||
vim_strncpy(both + prev_len, text, len + 1);
|
||||
|
||||
curbuf = buf;
|
||||
ml_replace(lnum, both, FALSE);
|
||||
curbuf = curwin->w_buffer;
|
||||
}
|
||||
else
|
||||
ml_append_buf(buf, lnum, text, len + 1, FALSE);
|
||||
|
||||
#ifdef MSWIN
|
||||
if (tmp != text)
|
||||
vim_free(text);
|
||||
#endif
|
||||
if (empty)
|
||||
{
|
||||
// Delete the empty line that was in the empty buffer.
|
||||
@@ -1989,6 +2119,7 @@ add_empty_scrollback(term_T *term, cellattr_T *fill_attr, int lnum)
|
||||
}
|
||||
}
|
||||
line->sb_cols = 0;
|
||||
line->sb_bytes = 0;
|
||||
line->sb_cells = NULL;
|
||||
line->sb_fill_attr = *fill_attr;
|
||||
++term->tl_scrollback.ga_len;
|
||||
@@ -2005,16 +2136,33 @@ cleanup_scrollback(term_T *term)
|
||||
{
|
||||
sb_line_T *line;
|
||||
garray_T *gap;
|
||||
char_u *bufline;
|
||||
size_t bufline_length;
|
||||
|
||||
curbuf = term->tl_buffer;
|
||||
gap = &term->tl_scrollback;
|
||||
while (curbuf->b_ml.ml_line_count > term->tl_scrollback_scrolled
|
||||
&& gap->ga_len > 0)
|
||||
bufline = ml_get_buf(curbuf, curbuf->b_ml.ml_line_count, FALSE);
|
||||
bufline_length = STRLEN(bufline);
|
||||
while (term->tl_scrollback_snapshot && gap->ga_len > 0)
|
||||
{
|
||||
ml_delete(curbuf->b_ml.ml_line_count);
|
||||
line = (sb_line_T *)gap->ga_data + gap->ga_len - 1;
|
||||
if (line->sb_bytes < 0 || (size_t)line->sb_bytes > bufline_length)
|
||||
break;
|
||||
bufline_length -= line->sb_bytes;
|
||||
if (!bufline_length)
|
||||
{
|
||||
ml_delete(curbuf->b_ml.ml_line_count);
|
||||
bufline = ml_get_buf(curbuf, curbuf->b_ml.ml_line_count, FALSE);
|
||||
bufline_length = STRLEN(bufline);
|
||||
}
|
||||
vim_free(line->sb_cells);
|
||||
--gap->ga_len;
|
||||
--term->tl_scrollback_snapshot;
|
||||
}
|
||||
if (bufline_length < STRLEN(bufline))
|
||||
{
|
||||
char_u *shortened = vim_strnsave(bufline, bufline_length);
|
||||
ml_replace(curbuf->b_ml.ml_line_count, shortened, FALSE);
|
||||
}
|
||||
curbuf = curwin->w_buffer;
|
||||
if (curbuf == term->tl_buffer)
|
||||
@@ -2028,6 +2176,7 @@ cleanup_scrollback(term_T *term)
|
||||
update_snapshot(term_T *term)
|
||||
{
|
||||
VTermScreen *screen;
|
||||
VTermState *state;
|
||||
int len;
|
||||
int lines_skipped = 0;
|
||||
VTermPos pos;
|
||||
@@ -2043,6 +2192,7 @@ update_snapshot(term_T *term)
|
||||
cleanup_scrollback(term);
|
||||
|
||||
screen = vterm_obtain_screen(term->tl_vterm);
|
||||
state = vterm_obtain_state(term->tl_vterm);
|
||||
fill_attr = new_fill_attr = term->tl_default_color;
|
||||
for (pos.row = 0; pos.row < term->tl_rows; ++pos.row)
|
||||
{
|
||||
@@ -2067,7 +2217,10 @@ update_snapshot(term_T *term)
|
||||
// Line was skipped, add an empty line.
|
||||
--lines_skipped;
|
||||
if (add_empty_scrollback(term, &fill_attr, 0) == OK)
|
||||
add_scrollback_line_to_buffer(term, (char_u *)"", 0);
|
||||
{
|
||||
add_scrollback_line_to_buffer(term, (char_u *)"", 0, 0);
|
||||
++term->tl_scrollback_snapshot;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 0)
|
||||
@@ -2079,9 +2232,12 @@ update_snapshot(term_T *term)
|
||||
{
|
||||
garray_T ga;
|
||||
int width;
|
||||
const VTermLineInfo *lineinfo;
|
||||
sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
|
||||
+ term->tl_scrollback.ga_len;
|
||||
|
||||
lineinfo = vterm_state_get_lineinfo(state, pos.row);
|
||||
|
||||
ga_init2(&ga, 1, 100);
|
||||
for (pos.col = 0; pos.col < len; pos.col += width)
|
||||
{
|
||||
@@ -2116,17 +2272,21 @@ update_snapshot(term_T *term)
|
||||
}
|
||||
}
|
||||
line->sb_cols = len;
|
||||
line->sb_bytes = ga.ga_len;
|
||||
line->sb_cells = p;
|
||||
line->sb_fill_attr = new_fill_attr;
|
||||
line->continuation = (char_u)lineinfo->continuation;
|
||||
fill_attr = new_fill_attr;
|
||||
++term->tl_scrollback.ga_len;
|
||||
++term->tl_scrollback_snapshot;
|
||||
|
||||
if (ga_grow(&ga, 1) == FAIL)
|
||||
add_scrollback_line_to_buffer(term, (char_u *)"", 0);
|
||||
add_scrollback_line_to_buffer(term, (char_u *)"", 0, 0);
|
||||
else
|
||||
{
|
||||
*((char_u *)ga.ga_data + ga.ga_len) = NUL;
|
||||
add_scrollback_line_to_buffer(term, ga.ga_data, ga.ga_len);
|
||||
add_scrollback_line_to_buffer(term, ga.ga_data, ga.ga_len,
|
||||
lineinfo->continuation);
|
||||
}
|
||||
ga_clear(&ga);
|
||||
}
|
||||
@@ -2141,7 +2301,10 @@ update_snapshot(term_T *term)
|
||||
++pos.row)
|
||||
{
|
||||
if (add_empty_scrollback(term, &fill_attr, 0) == OK)
|
||||
add_scrollback_line_to_buffer(term, (char_u *)"", 0);
|
||||
{
|
||||
add_scrollback_line_to_buffer(term, (char_u *)"", 0, 0);
|
||||
++term->tl_scrollback_snapshot;
|
||||
}
|
||||
}
|
||||
|
||||
term->tl_dirty_snapshot = FALSE;
|
||||
@@ -2186,7 +2349,7 @@ may_move_terminal_to_buffer(term_T *term, int redraw)
|
||||
// Update the snapshot only if something changes or the buffer does not
|
||||
// have all the lines.
|
||||
if (term->tl_dirty_snapshot || term->tl_buffer->b_ml.ml_line_count
|
||||
<= term->tl_scrollback_scrolled)
|
||||
<= term->tl_buffer_scrolled)
|
||||
update_snapshot(term);
|
||||
|
||||
if (redraw)
|
||||
@@ -2282,7 +2445,9 @@ cleanup_vterm(term_T *term)
|
||||
static void
|
||||
term_enter_normal_mode(void)
|
||||
{
|
||||
term_T *term = curbuf->b_term;
|
||||
term_T *term = curbuf->b_term;
|
||||
linenr_T lnum;
|
||||
int col;
|
||||
|
||||
set_terminal_mode(term, TRUE);
|
||||
|
||||
@@ -2291,15 +2456,16 @@ term_enter_normal_mode(void)
|
||||
|
||||
// Move the window cursor to the position of the cursor in the
|
||||
// terminal.
|
||||
curwin->w_cursor.lnum = term->tl_scrollback_scrolled
|
||||
+ term->tl_cursor_pos.row + 1;
|
||||
lnum = term->tl_buffer_scrolled + 1 + term->tl_cursor_pos.row;
|
||||
col = term->tl_cursor_pos.col;
|
||||
scrollbackline_pos_in_buf(term, term->tl_cursor_pos.row + term->tl_scrollback_scrolled, &lnum, &col, NULL);
|
||||
|
||||
curwin->w_cursor.lnum = lnum;
|
||||
check_cursor();
|
||||
if (coladvance(term->tl_cursor_pos.col) == FAIL)
|
||||
if (coladvance(col) == FAIL)
|
||||
coladvance(MAXCOL);
|
||||
curwin->w_set_curswant = TRUE;
|
||||
|
||||
// Display the same lines as in the terminal.
|
||||
curwin->w_topline = term->tl_scrollback_scrolled + 1;
|
||||
curwin->w_topline = term->tl_buffer_scrolled + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3483,20 +3649,27 @@ limit_scrollback(term_T *term, garray_T *gap, int update_buffer)
|
||||
int todo = MAX(term->tl_buffer->b_p_twsl / 10,
|
||||
gap->ga_len - term->tl_buffer->b_p_twsl);
|
||||
int i;
|
||||
sb_line_T *sb_lines = (sb_line_T *)gap->ga_data;
|
||||
|
||||
curbuf = term->tl_buffer;
|
||||
for (i = 0; i < todo; ++i)
|
||||
{
|
||||
vim_free(((sb_line_T *)gap->ga_data + i)->sb_cells);
|
||||
if (update_buffer)
|
||||
if (update_buffer && (!sb_lines[i].continuation || !i))
|
||||
{
|
||||
ml_delete(1);
|
||||
--term->tl_buffer_scrolled;
|
||||
}
|
||||
vim_free(sb_lines[i].sb_cells);
|
||||
}
|
||||
// Continue until end of wrapped line
|
||||
for (; todo < gap->ga_len && sb_lines[todo].continuation; ++todo)
|
||||
vim_free(sb_lines[todo].sb_cells);
|
||||
curbuf = curwin->w_buffer;
|
||||
|
||||
gap->ga_len -= todo;
|
||||
mch_memmove(gap->ga_data,
|
||||
(sb_line_T *)gap->ga_data + todo,
|
||||
sizeof(sb_line_T) * gap->ga_len);
|
||||
(sb_line_T *)gap->ga_data + todo,
|
||||
sizeof(sb_line_T) * gap->ga_len);
|
||||
if (update_buffer)
|
||||
{
|
||||
win_T *curwin_save = curwin;
|
||||
@@ -3521,7 +3694,7 @@ limit_scrollback(term_T *term, garray_T *gap, int update_buffer)
|
||||
* Handle a line that is pushed off the top of the screen.
|
||||
*/
|
||||
static int
|
||||
handle_pushline(int cols, const VTermScreenCell *cells, void *user)
|
||||
handle_pushline(int cols, const VTermScreenCell *cells, int continuation, void *user)
|
||||
{
|
||||
term_T *term = (term_T *)user;
|
||||
garray_T *gap;
|
||||
@@ -3600,16 +3773,20 @@ handle_pushline(int cols, const VTermScreenCell *cells, void *user)
|
||||
*(text + text_len) = NUL;
|
||||
}
|
||||
if (update_buffer)
|
||||
add_scrollback_line_to_buffer(term, text, text_len);
|
||||
add_scrollback_line_to_buffer(term, text, text_len, continuation);
|
||||
|
||||
line = (sb_line_T *)gap->ga_data + gap->ga_len;
|
||||
line->sb_cols = len;
|
||||
line->sb_bytes = text_len;
|
||||
line->sb_cells = p;
|
||||
line->sb_fill_attr = fill_attr;
|
||||
line->continuation = (char_u)continuation;
|
||||
if (update_buffer)
|
||||
{
|
||||
line->sb_text = NULL;
|
||||
++term->tl_scrollback_scrolled;
|
||||
if (!continuation)
|
||||
++term->tl_buffer_scrolled;
|
||||
ga_clear(&ga); // free the text
|
||||
}
|
||||
else
|
||||
@@ -3651,17 +3828,20 @@ handle_postponed_scrollback(term_T *term)
|
||||
text = pp_line->sb_text;
|
||||
if (text == NULL)
|
||||
text = (char_u *)"";
|
||||
add_scrollback_line_to_buffer(term, text, (int)STRLEN(text));
|
||||
add_scrollback_line_to_buffer(term, text, (int)STRLEN(text), pp_line->continuation);
|
||||
vim_free(pp_line->sb_text);
|
||||
|
||||
line = (sb_line_T *)term->tl_scrollback.ga_data
|
||||
+ term->tl_scrollback.ga_len;
|
||||
line->sb_cols = pp_line->sb_cols;
|
||||
line->sb_bytes = pp_line->sb_bytes;
|
||||
line->sb_cells = pp_line->sb_cells;
|
||||
line->sb_fill_attr = pp_line->sb_fill_attr;
|
||||
line->sb_text = NULL;
|
||||
++term->tl_scrollback_scrolled;
|
||||
++term->tl_scrollback.ga_len;
|
||||
if (!pp_line->continuation)
|
||||
++term->tl_buffer_scrolled;
|
||||
}
|
||||
|
||||
ga_clear(&term->tl_scrollback_postponed);
|
||||
@@ -3685,9 +3865,10 @@ static VTermScreenCallbacks screen_callbacks = {
|
||||
handle_settermprop, // settermprop
|
||||
handle_bell, // bell
|
||||
handle_resize, // resize
|
||||
handle_pushline, // sb_pushline
|
||||
NULL, // sb_pushline
|
||||
NULL, // sb_popline
|
||||
NULL // sb_clear
|
||||
NULL, // sb_clear
|
||||
handle_pushline // sb_pushline4
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -4243,16 +4424,21 @@ term_get_attr(win_T *wp, linenr_T lnum, int col)
|
||||
term_T *term = buf->b_term;
|
||||
sb_line_T *line;
|
||||
cellattr_T *cellattr;
|
||||
int sb_line = -1;
|
||||
int sb_col = col;
|
||||
|
||||
if (lnum > term->tl_scrollback.ga_len)
|
||||
if (term->tl_scrollback.ga_len)
|
||||
bufline_pos_in_scrollback(term, lnum, col, &sb_line, &sb_col);
|
||||
|
||||
if (sb_line < 0)
|
||||
cellattr = &term->tl_default_color;
|
||||
else
|
||||
{
|
||||
line = (sb_line_T *)term->tl_scrollback.ga_data + lnum - 1;
|
||||
if (col < 0 || col >= line->sb_cols)
|
||||
line = (sb_line_T *)term->tl_scrollback.ga_data + sb_line;
|
||||
if (sb_col < 0 || sb_col >= line->sb_cols)
|
||||
cellattr = &line->sb_fill_attr;
|
||||
else
|
||||
cellattr = line->sb_cells + col;
|
||||
cellattr = line->sb_cells + sb_col;
|
||||
}
|
||||
return cell2attr(term, wp, &cellattr->attrs, &cellattr->fg, &cellattr->bg);
|
||||
}
|
||||
@@ -4964,6 +5150,7 @@ create_vterm(term_T *term, int rows, int cols)
|
||||
|
||||
vterm_screen_set_callbacks(screen, &screen_callbacks, term);
|
||||
vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_SCROLL);
|
||||
vterm_screen_callbacks_has_pushline4(screen);
|
||||
// TODO: depends on 'encoding'.
|
||||
vterm_set_utf8(vterm, 1);
|
||||
|
||||
@@ -5478,8 +5665,10 @@ read_dump_file(FILE *fd, VTermPos *cursor_pos)
|
||||
if (max_cells < ga_cell.ga_len)
|
||||
max_cells = ga_cell.ga_len;
|
||||
line->sb_cols = ga_cell.ga_len;
|
||||
line->sb_bytes = ga_text.ga_len;
|
||||
line->sb_cells = ga_cell.ga_data;
|
||||
line->sb_fill_attr = term->tl_default_color;
|
||||
line->continuation = 0;
|
||||
++term->tl_scrollback.ga_len;
|
||||
ga_init(&ga_cell);
|
||||
|
||||
@@ -6300,11 +6489,24 @@ f_term_getline(typval_T *argvars, typval_T *rettv)
|
||||
|
||||
if (term->tl_vterm == NULL)
|
||||
{
|
||||
linenr_T lnum = row + term->tl_scrollback_scrolled + 1;
|
||||
linenr_T lnum = 0;
|
||||
size_t offset = 0;
|
||||
int sb_row = term->tl_scrollback_scrolled + row;
|
||||
sb_line_T *line;
|
||||
|
||||
if (sb_row < 0 || sb_row >= term->tl_scrollback.ga_len)
|
||||
return;
|
||||
line = (sb_line_T *)term->tl_scrollback.ga_data + sb_row;
|
||||
|
||||
scrollbackline_pos_in_buf(term, sb_row, &lnum, NULL, &offset);
|
||||
|
||||
// vterm is finished, get the text from the buffer
|
||||
if (lnum > 0 && lnum <= buf->b_ml.ml_line_count)
|
||||
rettv->vval.v_string = vim_strsave(ml_get_buf(buf, lnum, FALSE));
|
||||
{
|
||||
char_u *p = ml_get_buf(buf, lnum, FALSE);
|
||||
if (STRLEN(p) >= offset + line->sb_bytes)
|
||||
rettv->vval.v_string = vim_strnsave(p + offset, line->sb_bytes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -6343,7 +6545,7 @@ f_term_getscrolled(typval_T *argvars, typval_T *rettv)
|
||||
buf = term_get_buf(argvars, "term_getscrolled()");
|
||||
if (buf == NULL)
|
||||
return;
|
||||
rettv->vval.v_number = buf->b_term->tl_scrollback_scrolled;
|
||||
rettv->vval.v_number = buf->b_term->tl_buffer_scrolled;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -6557,12 +6759,22 @@ f_term_scrape(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
else
|
||||
{
|
||||
linenr_T lnum = pos.row + term->tl_scrollback_scrolled;
|
||||
int sb_row = term->tl_scrollback_scrolled + pos.row;
|
||||
linenr_T lnum = 0;
|
||||
size_t offset = 0;
|
||||
|
||||
if (lnum < 0 || lnum >= term->tl_scrollback.ga_len)
|
||||
scrollbackline_pos_in_buf(term, sb_row, &lnum, NULL, &offset);
|
||||
|
||||
if (sb_row >= term->tl_scrollback.ga_len || lnum <= 0 || lnum > buf->b_ml.ml_line_count)
|
||||
return;
|
||||
p = ml_get_buf(buf, lnum + 1, FALSE);
|
||||
line = (sb_line_T *)term->tl_scrollback.ga_data + lnum;
|
||||
|
||||
line = (sb_line_T *)term->tl_scrollback.ga_data + sb_row;
|
||||
p = ml_get_buf(buf, lnum, FALSE);
|
||||
|
||||
if (STRLEN(p) < offset + line->sb_bytes)
|
||||
return;
|
||||
|
||||
p += offset;
|
||||
}
|
||||
|
||||
for (pos.col = 0; pos.col < term->tl_cols; )
|
||||
|
||||
@@ -1889,4 +1889,82 @@ func Test_guioptions_clipboard()
|
||||
let &guioptions = save_guioptions
|
||||
endfunc
|
||||
|
||||
" Tests for GUI window geometry: initial size from -geometry option and
|
||||
" size stability after :tabnew / :tabclose.
|
||||
"
|
||||
" Background: on GTK3 with client-side decorations (Wayland), the window
|
||||
" compositor subtracts the CSD frame from the requested size, causing the
|
||||
" window to open a few pixels too small (wrong &columns/&lines) and to shrink
|
||||
" further with each :tabnew/:tabclose cycle.
|
||||
|
||||
" Test that a GUI window opened with -geometry=WxH has exactly W columns
|
||||
" and H lines.
|
||||
"
|
||||
" Without the CSD fix, on GTK3/Wayland the compositor subtracts the frame
|
||||
" margin from the requested pixel size, so the window is a character cell too
|
||||
" narrow and too short.
|
||||
func Test_geometry_exact_size()
|
||||
CheckCanRunGui
|
||||
CheckFeature gui_gtk
|
||||
|
||||
let after =<< trim [CODE]
|
||||
call writefile([string(&columns), string(&lines)], 'Xtest_geomsize')
|
||||
qall
|
||||
[CODE]
|
||||
|
||||
" Hide the menu bar so it does not widen the minimum window size.
|
||||
if RunVim(['set guioptions-=m'], after, '-f -g -geometry 40x15')
|
||||
let result = readfile('Xtest_geomsize')
|
||||
call assert_equal('40', result[0], 'columns should match -geometry width')
|
||||
call assert_equal('15', result[1], 'lines should match -geometry height')
|
||||
endif
|
||||
|
||||
call delete('Xtest_geomsize')
|
||||
endfunc
|
||||
|
||||
" Test that the window size is unchanged after opening and closing a tab.
|
||||
"
|
||||
" Each :tabnew/:tabclose cycle triggers a tabline show/hide, which causes
|
||||
" asynchronous GTK layout events. Without the fix, stale configure events
|
||||
" from these layout passes are mis-interpreted as user resizes, reducing
|
||||
" &columns and &lines with every cycle. Three cycles are performed to
|
||||
" amplify any drift.
|
||||
func Test_tabnew_tabclose_size_stable()
|
||||
CheckCanRunGui
|
||||
CheckFeature gui_gtk
|
||||
|
||||
let after =<< trim [CODE]
|
||||
let cols0 = &columns
|
||||
let rows0 = &lines
|
||||
tabnew
|
||||
sleep 300m
|
||||
tabclose
|
||||
sleep 300m
|
||||
tabnew
|
||||
sleep 300m
|
||||
tabclose
|
||||
sleep 300m
|
||||
tabnew
|
||||
sleep 300m
|
||||
tabclose
|
||||
sleep 300m
|
||||
call writefile([string(cols0), string(rows0), string(&columns), string(&lines)], 'Xtest_tabsize')
|
||||
qall
|
||||
[CODE]
|
||||
|
||||
if RunVim(['set guioptions-=m'], after, '-f -g -geometry 40x15')
|
||||
let result = readfile('Xtest_tabsize')
|
||||
call assert_equal('40', result[0], 'initial columns should match -geometry width')
|
||||
call assert_equal('15', result[1], 'initial lines should match -geometry height')
|
||||
call assert_equal(result[0], result[2],
|
||||
\ 'columns changed after 3x tabnew/tabclose: '
|
||||
\ .. result[0] .. ' -> ' .. result[2])
|
||||
call assert_equal(result[1], result[3],
|
||||
\ 'lines changed after 3x tabnew/tabclose: '
|
||||
\ .. result[1] .. ' -> ' .. result[3])
|
||||
endif
|
||||
|
||||
call delete('Xtest_tabsize')
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
@@ -752,6 +752,7 @@ func Test_statusline_click_handler()
|
||||
call assert_equal(1, g:stl_click_info.nclicks)
|
||||
call assert_equal(0, g:stl_click_info.minwid)
|
||||
call assert_equal(win_getid(), g:stl_click_info.winid)
|
||||
call assert_equal('statusline', g:stl_click_info.area)
|
||||
unlet! g:stl_click_info
|
||||
|
||||
" Click outside click region (on the filename part)
|
||||
@@ -873,4 +874,60 @@ func Test_statusline_click_linebreak_still_works()
|
||||
let &laststatus = save_ls
|
||||
endfunc
|
||||
|
||||
func Test_tabline_click_handler()
|
||||
let save_mouse = &mouse
|
||||
let save_tal = &tabline
|
||||
let save_stal = &showtabline
|
||||
if has('gui')
|
||||
let save_go = &guioptions
|
||||
set guioptions-=e
|
||||
endif
|
||||
set mouse=a
|
||||
set showtabline=2
|
||||
|
||||
" Two adjacent click regions in 'tabline' with different minwid.
|
||||
set tabline=%1[StlClickTestFunc][AAA]%[]%2[StlClickTestFunc][BBB]%[]
|
||||
redraw!
|
||||
|
||||
" Click on [AAA] region (tabline is row 1).
|
||||
call test_setmouse(1, 2)
|
||||
call feedkeys("\<LeftMouse>\<LeftRelease>", 'xt')
|
||||
call assert_true(exists('g:stl_click_info'))
|
||||
call assert_equal('l', g:stl_click_info.button)
|
||||
call assert_equal(1, g:stl_click_info.nclicks)
|
||||
call assert_equal(1, g:stl_click_info.minwid)
|
||||
" winid is 0 for tabline clicks (no associated window).
|
||||
call assert_equal(0, g:stl_click_info.winid)
|
||||
call assert_equal('tabline', g:stl_click_info.area)
|
||||
unlet! g:stl_click_info
|
||||
|
||||
" Click on [BBB] region.
|
||||
call test_setmouse(1, 7)
|
||||
call feedkeys("\<LeftMouse>\<LeftRelease>", 'xt')
|
||||
call assert_true(exists('g:stl_click_info'))
|
||||
call assert_equal(2, g:stl_click_info.minwid)
|
||||
unlet! g:stl_click_info
|
||||
|
||||
" Middle click on [AAA].
|
||||
call test_setmouse(1, 2)
|
||||
call feedkeys("\<MiddleMouse>\<MiddleRelease>", 'xt')
|
||||
call assert_true(exists('g:stl_click_info'))
|
||||
call assert_equal('m', g:stl_click_info.button)
|
||||
unlet! g:stl_click_info
|
||||
|
||||
" Click outside any %[...] region: no callback, no error.
|
||||
set tabline=xxx%1[StlClickTestFunc][YYY]%[]
|
||||
redraw!
|
||||
call test_setmouse(1, 1)
|
||||
call feedkeys("\<LeftMouse>\<LeftRelease>", 'xt')
|
||||
call assert_false(exists('g:stl_click_info'))
|
||||
|
||||
let &mouse = save_mouse
|
||||
let &tabline = save_tal
|
||||
let &showtabline = save_stal
|
||||
if has('gui')
|
||||
let &guioptions = save_go
|
||||
endif
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
@@ -2451,4 +2451,29 @@ func Test_terminal_disable_kitty_keyboard()
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_terminal_unwraps()
|
||||
CheckNotMSWindows
|
||||
|
||||
30vnew
|
||||
|
||||
redraw
|
||||
let buf = term_start("echo 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15")
|
||||
" Wait until both wrapped lines have appeared in the terminal
|
||||
call WaitForAssert({-> assert_equal('14+15', term_getline(buf, 2))})
|
||||
|
||||
" A long wrapped line appears as 2 lines in libvterm
|
||||
let l = term_getline(buf, 1)
|
||||
call assert_equal('1+2+3+4+5+6+7+8+9+10+11+12+13+', l)
|
||||
|
||||
let l = term_getline(buf, 2)
|
||||
call assert_equal('14+15', l)
|
||||
|
||||
call TermWait(buf)
|
||||
" It should appear as a single buffer line in vim
|
||||
let lastline = getline('$')
|
||||
call assert_equal('1+2+3+4+5+6+7+8+9+10+11+12+13+14+15', lastline)
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
@@ -734,6 +734,18 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
339,
|
||||
/**/
|
||||
338,
|
||||
/**/
|
||||
337,
|
||||
/**/
|
||||
336,
|
||||
/**/
|
||||
335,
|
||||
/**/
|
||||
334,
|
||||
/**/
|
||||
333,
|
||||
/**/
|
||||
|
||||
Reference in New Issue
Block a user