aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/autoload/health/provider.vim10
-rw-r--r--runtime/doc/channel.txt77
-rw-r--r--runtime/doc/provider.txt2
-rw-r--r--runtime/lua/vim/lsp/util.lua1
-rw-r--r--src/nvim/api/buffer.c14
-rw-r--r--src/nvim/decoration.c57
-rw-r--r--src/nvim/decoration.h17
-rw-r--r--src/nvim/screen.c15
-rw-r--r--test/functional/autoread/focus_spec.lua7
-rw-r--r--test/functional/plugin/lsp_spec.lua16
-rw-r--r--test/functional/ui/decorations_spec.lua32
11 files changed, 185 insertions, 63 deletions
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
index 112dd4354f..de540405e6 100644
--- a/runtime/autoload/health/provider.vim
+++ b/runtime/autoload/health/provider.vim
@@ -400,8 +400,6 @@ function! s:check_python(version) abort
endfor
endif
- let pip = 'pip' . (a:version == 2 ? '' : '3')
-
if empty(python_exe)
" No Python executable can import 'neovim'. Check if any Python executable
" can import 'pynvim'. If so, that Python failed to import 'neovim' as
@@ -413,9 +411,9 @@ function! s:check_python(version) abort
\ 'Detected pip upgrade failure: Python executable can import "pynvim" but '
\ . 'not "neovim": '. pynvim_exe,
\ "Use that Python version to reinstall \"pynvim\" and optionally \"neovim\".\n"
- \ . pip ." uninstall pynvim neovim\n"
- \ . pip ." install pynvim\n"
- \ . pip ." install neovim # only if needed by third-party software")
+ \ . pynvim_exe ." -m pip uninstall pynvim neovim\n"
+ \ . pynvim_exe ." -m pip install pynvim\n"
+ \ . pynvim_exe ." -m pip install neovim # only if needed by third-party software")
endif
else
let [pyversion, current, latest, status] = s:version_info(python_exe)
@@ -440,7 +438,7 @@ function! s:check_python(version) abort
if s:is_bad_response(current)
call health#report_error(
\ "pynvim is not installed.\nError: ".current,
- \ ['Run in shell: '. pip .' install pynvim'])
+ \ ['Run in shell: '. python_exe .' -m pip install pynvim'])
endif
if s:is_bad_response(latest)
diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt
index 967f4b26f2..656bb10c45 100644
--- a/runtime/doc/channel.txt
+++ b/runtime/doc/channel.txt
@@ -174,4 +174,81 @@ Put this in `uppercase.vim` and run: >
nvim --headless --cmd "source uppercase.vim"
==============================================================================
+5. Using a prompt buffer *prompt-buffer*
+
+If you want to type input for the job in a Vim window you have a few options:
+- Use a normal buffer and handle all possible commands yourself.
+ This will be complicated, since there are so many possible commands.
+- Use a terminal window. This works well if what you type goes directly to
+ the job and the job output is directly displayed in the window.
+ See |terminal|.
+- Use a window with a prompt buffer. This works well when entering a line for
+ the job in Vim while displaying (possibly filtered) output from the job.
+
+A prompt buffer is created by setting 'buftype' to "prompt". You would
+normally only do that in a newly created buffer.
+
+The user can edit and enter one line of text at the very last line of the
+buffer. When pressing Enter in the prompt line the callback set with
+|prompt_setcallback()| is invoked. It would normally send the line to a job.
+Another callback would receive the output from the job and display it in the
+buffer, below the prompt (and above the next prompt).
+
+Only the text in the last line, after the prompt, is editable. The rest of the
+buffer is not modifiable with Normal mode commands. It can be modified by
+calling functions, such as |append()|. Using other commands may mess up the
+buffer.
+
+After setting 'buftype' to "prompt" Vim does not automatically start Insert
+mode, use `:startinsert` if you want to enter Insert mode, so that the user
+can start typing a line.
+
+The text of the prompt can be set with the |prompt_setprompt()| function. If
+no prompt is set with |prompt_setprompt()|, "% " is used. You can get the
+effective prompt text for a buffer, with |prompt_getprompt()|.
+
+The user can go to Normal mode and navigate through the buffer. This can be
+useful to see older output or copy text.
+
+Any command that starts Insert mode, such as "a", "i", "A" and "I", will move
+the cursor to the last line. "A" will move to the end of the line, "I" to the
+start of the line.
+
+Here is an example for Unix. It starts a shell in the background and prompts
+for the next shell command. Output from the shell is displayed above the
+prompt. >
+
+ " Function handling a line of text that has been typed.
+ func TextEntered(text)
+ " Send the text to a shell with Enter appended.
+ call chansend(g:shell_job, [a:text, ''])
+ endfunc
+
+ " Function handling output from the shell: Added above the prompt.
+ func GotOutput(channel, msg, name)
+ call append(line("$") - 1, a:msg)
+ endfunc
+
+ " Function handling the shell exit: close the window.
+ func JobExit(job, status, event)
+ quit!
+ endfunc
+
+ " Start a shell in the background.
+ let shell_job = jobstart(["/bin/sh"], #{
+ \ on_stdout: function('GotOutput'),
+ \ on_stderr: function('GotOutput'),
+ \ on_exit: function('JobExit'),
+ \ })
+
+ new
+ set buftype=prompt
+ let buf = bufnr('')
+ call prompt_setcallback(buf, function("TextEntered"))
+ call prompt_setprompt(buf, "shell command: ")
+
+ " start accepting shell commands
+ startinsert
+<
+
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt
index f944689d0b..be895f9e4e 100644
--- a/runtime/doc/provider.txt
+++ b/runtime/doc/provider.txt
@@ -88,7 +88,7 @@ Example using pyenv: >
pyenv install 3.4.4
pyenv virtualenv 3.4.4 py3nvim
pyenv activate py3nvim
- pip install pynvim
+ python3 -m pip install pynvim
pyenv which python # Note the path
The last command reports the interpreter path, add it to your init.vim: >
let g:python3_host_prog = '/path/to/py3nvim/bin/python'
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 28a669778a..71ec85381b 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -470,6 +470,7 @@ function M.apply_text_document_edit(text_document_edit, index)
-- `VersionedTextDocumentIdentifier`s version may be null
-- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier
if should_check_version and (text_document.version
+ and text_document.version > 0
and M.buf_versions[bufnr]
and M.buf_versions[bufnr] > text_document.version) then
print("Buffer ", text_document.uri, " newer than edits.")
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index c55dc39605..e79a7a2de2 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -1437,6 +1437,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
/// default
/// - "combine": combine with background text color
/// - "blend": blend with background text color.
+/// - hl_eol : when true, for a multiline highlight covering the
+/// EOL of a line, continue the highlight for the rest
+/// of the screen line (just like for diff and
+/// cursorline highlight).
///
/// - ephemeral : for use with |nvim_set_decoration_provider|
/// callbacks. The mark will only be used for the current
@@ -1581,6 +1585,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
if (ERROR_SET(err)) {
goto error;
}
+ } else if (strequal("hl_eol", k.data)) {
+ decor.hl_eol = api_object_to_bool(*v, "hl_eol", false, err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
} else if (strequal("hl_mode", k.data)) {
if (v->type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
@@ -1669,7 +1678,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
if (ephemeral) {
d = &decor;
} else if (kv_size(decor.virt_text)
- || decor.priority != DECOR_PRIORITY_BASE) {
+ || decor.priority != DECOR_PRIORITY_BASE
+ || decor.hl_eol) {
// TODO(bfredl): this is a bit sketchy. eventually we should
// have predefined decorations for both marks/ephemerals
d = xcalloc(1, sizeof(*d));
@@ -1680,7 +1690,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
// TODO(bfredl): synergize these two branches even more
if (ephemeral && decor_state.buf == buf) {
- decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, 0);
+ decor_add_ephemeral((int)line, (int)col, line2, col2, &decor);
} else {
if (ephemeral) {
api_set_error(err, kErrorTypeException, "not yet implemented");
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 52a48ae6fb..e39d2328f5 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -144,9 +144,9 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state)
state->row = -1;
state->buf = buf;
for (size_t i = 0; i < kv_size(state->active); i++) {
- HlRange item = kv_A(state->active, i);
+ DecorRange item = kv_A(state->active, i);
if (item.virt_text_owned) {
- clear_virttext(&item.virt_text);
+ clear_virttext(&item.decor.virt_text);
}
}
kv_size(state->active) = 0;
@@ -190,14 +190,14 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state)
if (mark.id&MARKTREE_END_FLAG) {
decor_add(state, altpos.row, altpos.col, mark.row, mark.col,
- decor, false, 0);
+ decor, false);
} else {
if (altpos.row == -1) {
altpos.row = mark.row;
altpos.col = mark.col;
}
decor_add(state, mark.row, mark.col, altpos.row, altpos.col,
- decor, false, 0);
+ decor, false);
}
next_mark:
@@ -222,22 +222,19 @@ bool decor_redraw_line(buf_T *buf, int row, DecorState *state)
}
static void decor_add(DecorState *state, int start_row, int start_col,
- int end_row, int end_col, Decoration *decor, bool owned,
- DecorPriority priority)
+ int end_row, int end_col, Decoration *decor, bool owned)
{
int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0;
- HlRange range = { start_row, start_col, end_row, end_col,
- attr_id, MAX(priority, decor->priority),
- decor->virt_text,
- decor->virt_text_pos, decor->virt_text_hide, decor->hl_mode,
+ DecorRange range = { start_row, start_col, end_row, end_col,
+ *decor, attr_id,
kv_size(decor->virt_text) && owned, -1 };
kv_pushp(state->active);
size_t index;
for (index = kv_size(state->active)-1; index > 0; index--) {
- HlRange item = kv_A(state->active, index-1);
- if (item.priority <= range.priority) {
+ DecorRange item = kv_A(state->active, index-1);
+ if (item.decor.priority <= range.decor.priority) {
break;
}
kv_A(state->active, index) = kv_A(state->active, index-1);
@@ -291,7 +288,7 @@ int decor_redraw_col(buf_T *buf, int col, int virt_col, bool hidden,
}
decor_add(state, mark.row, mark.col, endpos.row, endpos.col,
- decor, false, 0);
+ decor, false);
next_mark:
marktree_itr_next(buf->b_marktree, state->itr);
@@ -300,11 +297,11 @@ next_mark:
int attr = 0;
size_t j = 0;
for (size_t i = 0; i < kv_size(state->active); i++) {
- HlRange item = kv_A(state->active, i);
+ DecorRange item = kv_A(state->active, i);
bool active = false, keep = true;
if (item.end_row < state->row
|| (item.end_row == state->row && item.end_col <= col)) {
- if (!(item.start_row >= state->row && kv_size(item.virt_text))) {
+ if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))) {
keep = false;
}
} else {
@@ -324,13 +321,13 @@ next_mark:
attr = hl_combine_attr(attr, item.attr_id);
}
if ((item.start_row == state->row && item.start_col <= col)
- && kv_size(item.virt_text) && item.virt_col == -1) {
- item.virt_col = (item.virt_text_hide && hidden) ? -2 : virt_col;
+ && kv_size(item.decor.virt_text) && item.virt_col == -1) {
+ item.virt_col = (item.decor.virt_text_hide && hidden) ? -2 : virt_col;
}
if (keep) {
kv_A(state->active, j++) = item;
} else if (item.virt_text_owned) {
- clear_virttext(&item.virt_text);
+ clear_virttext(&item.decor.virt_text);
}
}
kv_size(state->active) = j;
@@ -343,28 +340,34 @@ void decor_redraw_end(DecorState *state)
state->buf = NULL;
}
-VirtText decor_redraw_virt_text(buf_T *buf, DecorState *state)
+VirtText decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr)
{
decor_redraw_col(buf, MAXCOL, MAXCOL, false, state);
+ VirtText text = VIRTTEXT_EMPTY;
for (size_t i = 0; i < kv_size(state->active); i++) {
- HlRange item = kv_A(state->active, i);
- if (item.start_row == state->row && kv_size(item.virt_text)
- && item.virt_text_pos == kVTEndOfLine) {
- return item.virt_text;
+ DecorRange item = kv_A(state->active, i);
+ if (!kv_size(text)
+ && item.start_row == state->row && kv_size(item.decor.virt_text)
+ && item.decor.virt_text_pos == kVTEndOfLine) {
+ text = item.decor.virt_text;
+ }
+
+ if (item.decor.hl_eol && item.start_row <= state->row) {
+ *eol_attr = hl_combine_attr(*eol_attr, item.attr_id);
}
}
- return VIRTTEXT_EMPTY;
+
+ return text;
}
void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col,
- Decoration *decor, DecorPriority priority)
+ Decoration *decor)
{
if (end_row == -1) {
end_row = start_row;
end_col = start_col;
}
- decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true,
- priority);
+ decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true);
}
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index c5424a1642..08d69060f0 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -37,33 +37,28 @@ struct Decoration
VirtTextPos virt_text_pos;
bool virt_text_hide;
HlMode hl_mode;
+ bool hl_eol;
// TODO(bfredl): style, signs, etc
DecorPriority priority;
bool shared; // shared decoration, don't free
};
#define DECORATION_INIT { 0, KV_INITIAL_VALUE, kVTEndOfLine, false, \
- kHlModeUnknown, DECOR_PRIORITY_BASE, false }
+ kHlModeUnknown, false, DECOR_PRIORITY_BASE, false }
typedef struct {
int start_row;
int start_col;
int end_row;
int end_col;
- int attr_id;
- // TODO(bfredl): embed decoration instead, perhaps using an arena
- // for ephemerals?
- DecorPriority priority;
- VirtText virt_text;
- VirtTextPos virt_text_pos;
- bool virt_text_hide;
- HlMode hl_mode;
+ Decoration decor;
+ int attr_id; // cached lookup of decor.hl_id
bool virt_text_owned;
int virt_col;
-} HlRange;
+} DecorRange;
typedef struct {
MarkTreeIter itr[1];
- kvec_t(HlRange) active;
+ kvec_t(DecorRange) active;
buf_T *buf;
int top_row;
int row;
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 9fb2eb2772..9f11bae16c 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -3951,7 +3951,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
.hl_id = hl_err }));
do_virttext = true;
} else if (has_decor) {
- virt_text = decor_redraw_virt_text(wp->w_buffer, &decor_state);
+ virt_text = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr);
if (kv_size(virt_text)) {
do_virttext = true;
}
@@ -4381,11 +4381,12 @@ void draw_virt_text(buf_T *buf, int *end_col, int max_col)
{
DecorState *state = &decor_state;
for (size_t i = 0; i < kv_size(state->active); i++) {
- HlRange *item = &kv_A(state->active, i);
- if (item->start_row == state->row && kv_size(item->virt_text)
- && item->virt_text_pos == kVTOverlay
+ DecorRange *item = &kv_A(state->active, i);
+ if (item->start_row == state->row && kv_size(item->decor.virt_text)
+ && item->decor.virt_text_pos == kVTOverlay
&& item->virt_col >= 0) {
- VirtText vt = item->virt_text;
+ VirtText vt = item->decor.virt_text;
+ HlMode hl_mode = item->decor.hl_mode;
LineState s = LINE_STATE("");
int virt_attr = 0;
int col = item->virt_col;
@@ -4405,9 +4406,9 @@ void draw_virt_text(buf_T *buf, int *end_col, int max_col)
}
int attr;
bool through = false;
- if (item->hl_mode == kHlModeCombine) {
+ if (hl_mode == kHlModeCombine) {
attr = hl_combine_attr(linebuf_attr[col], virt_attr);
- } else if (item->hl_mode == kHlModeBlend) {
+ } else if (hl_mode == kHlModeBlend) {
through = (*s.p == ' ');
attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through);
} else {
diff --git a/test/functional/autoread/focus_spec.lua b/test/functional/autoread/focus_spec.lua
index 1d52e9948f..3f9a0ad09b 100644
--- a/test/functional/autoread/focus_spec.lua
+++ b/test/functional/autoread/focus_spec.lua
@@ -9,6 +9,7 @@ local feed_data = thelpers.feed_data
if helpers.pending_win32(pending) then return end
describe('autoread TUI FocusGained/FocusLost', function()
+ local f1 = 'xtest-foo'
local screen
before_each(function()
@@ -17,8 +18,12 @@ describe('autoread TUI FocusGained/FocusLost', function()
..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler"]')
end)
+ teardown(function()
+ os.remove(f1)
+ end)
+
it('external file change', function()
- local path = 'xtest-foo'
+ local path = f1
local expected_addition = [[
line 1
line 2
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 66b33cc9e1..557f8a206f 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -1124,14 +1124,14 @@ describe('LSP', function()
'2nd line of 语text';
}, buf_lines(target_bufnr))
end)
- it('correctly goes ahead with the edit if the version is vim.NIL', function()
- -- we get vim.NIL when we decode json null value.
- local json = exec_lua[[
- return vim.fn.json_decode("{ \"a\": 1, \"b\": null }")
- ]]
- eq(json.b, exec_lua("return vim.NIL"))
-
- exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit(exec_lua("return vim.NIL")))
+ it('always accepts edit with version = 0', function()
+ exec_lua([[
+ local args = {...}
+ local bufnr = select(1, ...)
+ local text_edit = select(2, ...)
+ vim.lsp.util.buf_versions[bufnr] = 10
+ vim.lsp.util.apply_text_document_edit(text_edit)
+ ]], target_bufnr, text_document_edit(0))
eq({
'First ↥ 🤦 🦄 line of text';
'2nd line of 语text';
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 295a54aec8..82d3075be2 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -29,6 +29,7 @@ describe('decorations providers', function()
[10] = {italic = true, background = Screen.colors.Magenta};
[11] = {foreground = Screen.colors.Red, background = tonumber('0x005028')};
[12] = {foreground = tonumber('0x990000')};
+ [13] = {background = Screen.colors.LightBlue};
}
end)
@@ -331,6 +332,37 @@ describe('decorations providers', function()
|
]]}
end)
+
+ it('can highlight beyond EOL', function()
+ insert(mulholland)
+ setup_provider [[
+ local test_ns = a.nvim_create_namespace "veberod"
+ function on_do(event, ...)
+ if event == "line" then
+ local win, buf, line = ...
+ if string.find(a.nvim_buf_get_lines(buf, line, line+1, true)[1], "buf") then
+ a.nvim_buf_set_extmark(buf, test_ns, line, 0, {
+ end_line = line+1;
+ hl_group = 'DiffAdd';
+ hl_eol = true;
+ ephemeral = true;
+ })
+ end
+ end
+ end
+ ]]
+
+ screen:expect{grid=[[
+ // just to see if there was an accident |
+ // on Mulholland Drive |
+ try_start(); |
+ {13:bufref_T save_buf; }|
+ {13:switch_buffer(&save_buf, buf); }|
+ posp = getmark(mark, false); |
+ {13:restore_buffer(&save_buf);^ }|
+ |
+ ]]}
+ end)
end)
describe('extmark decorations', function()