api/ui: win_viewport event for visible range and cursor position in window

This commit is contained in:
Björn Linse 2020-01-23 18:05:04 +01:00
parent 4139678f97
commit 1fe0b329fe
11 changed files with 305 additions and 21 deletions

View File

@ -592,6 +592,12 @@ tabs.
When |ext_messages| is active, no message grid is used, and this event
will not be sent.
["win_viewport", grid, win, topline, botline, curline, curcol]
Indicates the range of buffer text displayed in the window, as well
as the cursor position in the buffer. All positions are zero-based.
`botline` is set to one more than the line count of the buffer, if
there are filler lines past the end.
==============================================================================
Popupmenu Events *ui-popupmenu*

View File

@ -115,6 +115,10 @@ void win_close(Integer grid)
void msg_set_pos(Integer grid, Integer row, Boolean scrolled, String sep_char)
FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
void win_viewport(Integer grid, Window win, Integer topline,
Integer botline, Integer curline, Integer curcol)
FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY;
void popupmenu_show(Array items, Integer selected,
Integer row, Integer col, Integer grid)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;

View File

@ -1190,6 +1190,8 @@ struct window_S {
to adjust w_valid */
colnr_T w_valid_leftcol; /* last known w_leftcol */
bool w_viewport_invalid;
/*
* w_cline_height is the number of physical lines taken by the buffer line
* that the cursor is on. We use this to avoid extra calls to plines().

View File

@ -86,6 +86,7 @@ static void comp_botline(win_T *wp)
/* wp->w_botline is the line that is just below the window */
wp->w_botline = lnum;
wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
wp->w_viewport_invalid = true;
set_empty_rows(wp, done);
@ -151,6 +152,7 @@ void update_topline(void)
curwin->w_topline = curwin->w_cursor.lnum;
curwin->w_botline = curwin->w_topline;
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
curwin->w_viewport_invalid = true;
curwin->w_scbind_pos = 1;
return;
}
@ -175,6 +177,7 @@ void update_topline(void)
curwin->w_topline = 1;
curwin->w_botline = 2;
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
curwin->w_viewport_invalid = true;
curwin->w_scbind_pos = 1;
}
/*
@ -311,6 +314,7 @@ void update_topline(void)
}
}
curwin->w_valid |= VALID_TOPLINE;
curwin->w_viewport_invalid = true;
win_check_anchored_floats(curwin);
/*
@ -407,6 +411,7 @@ void check_cursor_moved(win_T *wp)
|VALID_CHEIGHT|VALID_CROW|VALID_TOPLINE);
wp->w_valid_cursor = wp->w_cursor;
wp->w_valid_leftcol = wp->w_leftcol;
wp->w_viewport_invalid = true;
} else if (wp->w_cursor.col != wp->w_valid_cursor.col
|| wp->w_leftcol != wp->w_valid_leftcol
|| wp->w_cursor.coladd != wp->w_valid_cursor.coladd
@ -415,6 +420,7 @@ void check_cursor_moved(win_T *wp)
wp->w_valid_cursor.col = wp->w_cursor.col;
wp->w_valid_leftcol = wp->w_leftcol;
wp->w_valid_cursor.coladd = wp->w_cursor.coladd;
wp->w_viewport_invalid = true;
}
}
@ -1458,6 +1464,7 @@ void scroll_cursor_top(int min_scroll, int always)
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
curwin->w_valid |= VALID_TOPLINE;
curwin->w_viewport_invalid = true;
}
}
@ -1662,6 +1669,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
curwin->w_valid = old_valid;
}
curwin->w_valid |= VALID_TOPLINE;
curwin->w_viewport_invalid = true;
}
/// Recompute topline to put the cursor halfway across the window
@ -1818,6 +1826,7 @@ void cursor_correct(void)
}
}
curwin->w_valid |= VALID_TOPLINE;
curwin->w_viewport_invalid = true;
}

View File

@ -4121,6 +4121,7 @@ void scroll_redraw(int up, long count)
}
if (curwin->w_cursor.lnum != prev_lnum)
coladvance(curwin->w_curswant);
curwin->w_viewport_invalid = true;
redraw_later(VALID);
}

View File

@ -417,7 +417,7 @@ int update_screen(int type)
need_wait_return = false;
}
win_ui_flush_positions();
win_ui_flush();
msg_ext_check_clear();
/* reset cmdline_row now (may have been changed temporarily) */
@ -1629,6 +1629,7 @@ static void win_update(win_T *wp)
* changes are relevant).
*/
wp->w_valid |= VALID_BOTLINE;
wp->w_viewport_invalid = true;
if (wp == curwin && wp->w_botline != old_botline && !recursive) {
recursive = TRUE;
curwin->w_valid &= ~VALID_TOPLINE;
@ -1648,7 +1649,7 @@ static void win_update(win_T *wp)
/* restore got_int, unless CTRL-C was hit while redrawing */
if (!got_int)
got_int = save_got_int;
}
} // NOLINT(readability/fn_size)
/// Returns width of the signcolumn that should be used for the whole window
///

View File

@ -424,7 +424,7 @@ int ui_current_col(void)
void ui_flush(void)
{
cmdline_ui_flush();
win_ui_flush_positions();
win_ui_flush();
msg_ext_ui_flush();
msg_scroll_flush();

View File

@ -773,6 +773,21 @@ static void ui_ext_win_position(win_T *wp)
}
void ui_ext_win_viewport(win_T *wp)
{
if ((wp == curwin || ui_has(kUIMultigrid)) && wp->w_viewport_invalid) {
int botline = wp->w_botline;
if (botline == wp->w_buffer->b_ml.ml_line_count+1
&& wp->w_empty_rows == 0) {
// TODO(bfredl): The might be more cases to consider, like how does this
// interact with incomplete final line? Diff filler lines?
botline = wp->w_buffer->b_ml.ml_line_count;
}
ui_call_win_viewport(wp->w_grid.handle, wp->handle, wp->w_topline-1,
botline, wp->w_cursor.lnum-1, wp->w_cursor.col);
wp->w_viewport_invalid = false;
}
}
static bool parse_float_anchor(String anchor, FloatAnchor *out)
{
@ -4688,6 +4703,7 @@ static win_T *win_alloc(win_T *after, int hidden)
new_wp->w_scbind_pos = 1;
new_wp->w_floating = 0;
new_wp->w_float_config = FLOAT_CONFIG_INIT;
new_wp->w_viewport_invalid = true;
// use global option for global-local options
new_wp->w_p_so = -1;
@ -6992,7 +7008,7 @@ void get_framelayout(const frame_T *fr, list_T *l, bool outer)
}
}
void win_ui_flush_positions(void)
void win_ui_flush(void)
{
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_pos_changed && wp->w_grid.chars != NULL) {
@ -7003,6 +7019,9 @@ void win_ui_flush_positions(void)
}
wp->w_pos_changed = false;
}
if (tp == curtab) {
ui_ext_win_viewport(wp);
}
}
}

View File

@ -976,6 +976,28 @@ describe('floatwin', function()
{2:~ }|
]], float_pos={
[5] = {{id = 1002}, "NE", 4, 0, 50, true}
}, win_viewport = {
[2] = {
topline = 0,
botline = 3,
curline = 0,
curcol = 3,
win = { id = 1000 }
},
[4] = {
topline = 0,
botline = 3,
curline = 0,
curcol = 3,
win = { id = 1001 }
},
[5] = {
topline = 0,
botline = 2,
curline = 0,
curcol = 0,
win = { id = 1002 }
}
}}
else
screen:expect([[

View File

@ -1962,4 +1962,191 @@ describe('ext_multigrid', function()
{1:~ }|
]]}
end)
it('has viewport information', function()
screen:try_resize(48, 8)
screen:expect{grid=[[
## grid 1
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
{11:[No Name] }|
[3:------------------------------------------------]|
## grid 2
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
## grid 3
|
]], win_viewport={
[2] = {win = { id = 1000 }, topline = 0, botline = 2, curline = 0, curcol = 0}
}}
insert([[
Lorem ipsum dolor sit amet, consectetur
adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa
qui officia deserunt mollit anim id est
laborum.]])
screen:expect{grid=[[
## grid 1
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
{11:[No Name] [+] }|
[3:------------------------------------------------]|
## grid 2
ea commodo consequat. Duis aute irure dolor in |
reprehenderit in voluptate velit esse cillum |
dolore eu fugiat nulla pariatur. Excepteur sint |
occaecat cupidatat non proident, sunt in culpa |
qui officia deserunt mollit anim id est |
laborum^. |
## grid 3
|
]], win_viewport={
[2] = {win = {id = 1000}, topline = 5, botline = 11, curline = 10, curcol = 7},
}}
feed('<c-u>')
screen:expect{grid=[[
## grid 1
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
{11:[No Name] [+] }|
[3:------------------------------------------------]|
## grid 2
incididunt ut labore et dolore magna aliqua. |
Ut enim ad minim veniam, quis nostrud |
exercitation ullamco laboris nisi ut aliquip ex |
ea commodo consequat. Duis aute irure dolor in |
reprehenderit in voluptate velit esse cillum |
^dolore eu fugiat nulla pariatur. Excepteur sint |
## grid 3
|
]], win_viewport={
[2] = {win = {id = 1000}, topline = 2, botline = 9, curline = 7, curcol = 0},
}}
command("split")
screen:expect{grid=[[
## grid 1
[4:------------------------------------------------]|
[4:------------------------------------------------]|
[4:------------------------------------------------]|
{11:[No Name] [+] }|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
{12:[No Name] [+] }|
[3:------------------------------------------------]|
## grid 2
reprehenderit in voluptate velit esse cillum |
dolore eu fugiat nulla pariatur. Excepteur sint |
## grid 3
|
## grid 4
ea commodo consequat. Duis aute irure dolor in |
reprehenderit in voluptate velit esse cillum |
^dolore eu fugiat nulla pariatur. Excepteur sint |
]], win_viewport={
[2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0},
[4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 7, curcol = 0},
}}
feed("b")
screen:expect{grid=[[
## grid 1
[4:------------------------------------------------]|
[4:------------------------------------------------]|
[4:------------------------------------------------]|
{11:[No Name] [+] }|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
{12:[No Name] [+] }|
[3:------------------------------------------------]|
## grid 2
reprehenderit in voluptate velit esse cillum |
dolore eu fugiat nulla pariatur. Excepteur sint |
## grid 3
|
## grid 4
ea commodo consequat. Duis aute irure dolor in |
reprehenderit in voluptate velit esse ^cillum |
dolore eu fugiat nulla pariatur. Excepteur sint |
]], win_viewport={
[2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0},
[4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 6, curcol = 38},
}}
feed("2k")
screen:expect{grid=[[
## grid 1
[4:------------------------------------------------]|
[4:------------------------------------------------]|
[4:------------------------------------------------]|
{11:[No Name] [+] }|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
{12:[No Name] [+] }|
[3:------------------------------------------------]|
## grid 2
reprehenderit in voluptate velit esse cillum |
dolore eu fugiat nulla pariatur. Excepteur sint |
## grid 3
|
## grid 4
exercitation ullamco laboris nisi ut a^liquip ex |
ea commodo consequat. Duis aute irure dolor in |
reprehenderit in voluptate velit esse cillum |
]], win_viewport={
[2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0},
[4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38},
}}
-- handles non-current window
meths.win_set_cursor(1000, {1, 10})
screen:expect{grid=[[
## grid 1
[4:------------------------------------------------]|
[4:------------------------------------------------]|
[4:------------------------------------------------]|
{11:[No Name] [+] }|
[2:------------------------------------------------]|
[2:------------------------------------------------]|
{12:[No Name] [+] }|
[3:------------------------------------------------]|
## grid 2
Lorem ipsum dolor sit amet, consectetur |
adipisicing elit, sed do eiusmod tempor |
## grid 3
|
## grid 4
exercitation ullamco laboris nisi ut a^liquip ex |
ea commodo consequat. Duis aute irure dolor in |
reprehenderit in voluptate velit esse cillum |
]], win_viewport={
[2] = {win = {id = 1000}, topline = 0, botline = 3, curline = 0, curcol = 10},
[4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38},
}}
end)
end)

View File

@ -158,6 +158,7 @@ function Screen.new(width, height)
wildmenu_items = nil,
wildmenu_selected = nil,
win_position = {},
win_viewport = {},
float_pos = {},
msg_grid = nil,
msg_grid_pos = nil,
@ -254,7 +255,7 @@ end
-- canonical order of ext keys, used to generate asserts
local ext_keys = {
'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos',
'messages', 'showmode', 'showcmd', 'ruler', 'float_pos',
'messages', 'showmode', 'showcmd', 'ruler', 'float_pos', 'win_viewport'
}
-- Asserts that the screen state eventually matches an expected state.
@ -421,6 +422,9 @@ screen:redraw_debug() to show all intermediate screen states. ]])
if expected.mode ~= nil then
extstate.mode = self.mode
end
if expected.win_viewport == nil then
extstate.win_viewport = nil
end
-- Convert assertion errors into invalid screen state descriptions.
for _, k in ipairs(concat_tables(ext_keys, {'mode'})) do
@ -726,6 +730,7 @@ function Screen:_handle_grid_destroy(grid)
self._grids[grid] = nil
if self._options.ext_multigrid then
self.win_position[grid] = nil
self.win_viewport[grid] = nil
end
end
@ -746,14 +751,24 @@ function Screen:_handle_grid_cursor_goto(grid, row, col)
end
function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height)
self.win_position[grid] = {
win = win,
startrow = startrow,
startcol = startcol,
width = width,
height = height
}
self.float_pos[grid] = nil
self.win_position[grid] = {
win = win,
startrow = startrow,
startcol = startcol,
width = width,
height = height
}
self.float_pos[grid] = nil
end
function Screen:_handle_win_viewport(grid, win, topline, botline, curline, curcol)
self.win_viewport[grid] = {
win = win,
topline = topline,
botline = botline,
curline = curline,
curcol = curcol
}
end
function Screen:_handle_win_float_pos(grid, ...)
@ -1130,6 +1145,8 @@ function Screen:_extstate_repr(attr_state)
messages[i] = {kind=entry[1], content=self:_chunks_repr(entry[2], attr_state)}
end
local win_viewport = (next(self.win_viewport) and self.win_viewport) or nil
return {
popupmenu=self.popupmenu,
cmdline=cmdline,
@ -1141,7 +1158,8 @@ function Screen:_extstate_repr(attr_state)
showcmd=self:_chunks_repr(self.showcmd, attr_state),
ruler=self:_chunks_repr(self.ruler, attr_state),
msg_history=msg_history,
float_pos=self.float_pos
float_pos=self.float_pos,
win_viewport=win_viewport,
}
end
@ -1216,10 +1234,6 @@ function Screen:render(headers, attr_state, preview)
return rv
end
local remove_all_metatables = function(item, path)
if path[#path] ~= inspect.METATABLE then return item end
end
-- Returns the current screen state in the form of a screen:expect()
-- keyword-args map.
function Screen:get_snapshot(attrs, ignore)
@ -1269,6 +1283,26 @@ function Screen:get_snapshot(attrs, ignore)
return kwargs, ext_state, attr_state
end
local function fmt_ext_state(name, state)
if name == "win_viewport" then
local str = "{\n"
for k,v in pairs(state) do
str = (str.." ["..k.."] = {win = {id = "..v.win.id.."}, topline = "
..v.topline..", botline = "..v.botline..", curline = "..v.curline
..", curcol = "..v.curcol.."},\n")
end
return str .. "}"
else
-- TODO(bfredl): improve formatting of more states
local function remove_all_metatables(item, path)
if path[#path] ~= inspect.METATABLE then
return item
end
end
return inspect(state,{process=remove_all_metatables})
end
end
function Screen:print_snapshot(attrs, ignore)
local kwargs, ext_state, attr_state = self:get_snapshot(attrs, ignore)
local attrstr = ""
@ -1291,9 +1325,8 @@ function Screen:print_snapshot(attrs, ignore)
print(kwargs.grid)
io.stdout:write( "]]"..attrstr)
for _, k in ipairs(ext_keys) do
if ext_state[k] ~= nil then
-- TODO(bfredl): improve formatting
io.stdout:write(", "..k.."="..inspect(ext_state[k],{process=remove_all_metatables}))
if ext_state[k] ~= nil and not (k == "win_viewport" and not self.options.ext_multigrid) then
io.stdout:write(", "..k.."="..fmt_ext_state(k, ext_state[k]))
end
end
print("}\n")