aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/change.txt1
-rw-r--r--runtime/doc/deprecated.txt1
-rw-r--r--runtime/doc/lsp.txt23
-rw-r--r--runtime/doc/news.txt6
-rw-r--r--runtime/doc/starting.txt33
-rw-r--r--runtime/doc/visual.txt1
-rw-r--r--runtime/lua/vim/lsp/handlers.lua15
-rw-r--r--runtime/lua/vim/lsp/util.lua178
-rw-r--r--runtime/queries/markdown/highlights.scm8
-rw-r--r--runtime/queries/markdown_inline/highlights.scm8
-rw-r--r--src/nvim/api/win_config.c3
-rw-r--r--src/nvim/normal.c2
-rw-r--r--src/nvim/option.c3
-rw-r--r--test/functional/plugin/lsp/utils_spec.lua34
-rw-r--r--test/functional/plugin/lsp_spec.lua2
-rw-r--r--test/functional/ui/float_spec.lua23
-rw-r--r--test/functional/ui/highlight_spec.lua14
-rw-r--r--test/old/testdir/test_visual.vim14
18 files changed, 280 insertions, 89 deletions
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index 894557818b..2c47421b02 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -196,6 +196,7 @@ gR Enter Virtual Replace mode: Each character you type
*v_r*
{Visual}r{char} Replace all selected characters by {char}.
+ CTRL-C will be inserted literally.
*v_C*
{Visual}["x]C Delete the highlighted lines [into register x] and
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index 2e9312cf74..407d7ae9fb 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -141,6 +141,7 @@ LSP FUNCTIONS
`progress` of |vim.lsp.client|
- *vim.lsp.get_active_clients()* Use |vim.lsp.get_clients()|
- *vim.lsp.for_each_buffer_client()* Use |vim.lsp.get_clients()|
+- *vim.lsp.util.trim_empty_lines()* Use |vim.split()| with `trimempty` instead.
TREESITTER FUNCTIONS
- *vim.treesitter.language.require_language()* Use |vim.treesitter.language.add()|
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 5103cc223f..15292cd7cf 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -1678,20 +1678,24 @@ convert_input_to_markdown_lines({input}, {contents})
window for `textDocument/hover`, for parsing the result of
`textDocument/signatureHelp`, and potentially others.
+ Note that if the input is of type `MarkupContent` and its kind is
+ `plaintext`, then the corresponding value is returned without further
+ modifications.
+
Parameters: ~
• {input} (`MarkedString` | `MarkedString[]` | `MarkupContent`)
• {contents} (table|nil) List of strings to extend with converted
lines. Defaults to {}.
Return: ~
- (table) {contents} extended with lines of converted markdown.
+ string[] extended with lines of converted markdown.
See also: ~
• https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
*vim.lsp.util.convert_signature_help_to_markdown_lines()*
convert_signature_help_to_markdown_lines({signature_help}, {ft}, {triggers})
- Converts `textDocument/SignatureHelp` response to markdown lines.
+ Converts `textDocument/signatureHelp` response to markdown lines.
Parameters: ~
• {signature_help} (table) Response of `textDocument/SignatureHelp`
@@ -1908,10 +1912,6 @@ open_floating_preview({contents}, {syntax}, {opts})
height when wrap is enabled
• max_width: (integer) maximal width of floating window
• max_height: (integer) maximal height of floating window
- • pad_top: (integer) number of lines to pad contents at
- top
- • pad_bottom: (integer) number of lines to pad contents at
- bottom
• focus_id: (string) if a popup with this id is opened,
then focus it
• close_events: (table) list of events that closes the
@@ -2005,8 +2005,6 @@ stylize_markdown({bufnr}, {contents}, {opts})
• wrap_at character to wrap at for computing height
• max_width maximal width of floating window
• max_height maximal height of floating window
- • pad_top number of lines to pad contents at top
- • pad_bottom number of lines to pad contents at bottom
• separator insert separator after code block
Return: ~
@@ -2035,15 +2033,6 @@ text_document_completion_list_to_complete_items({result}, {prefix})
See also: ~
• complete-items
-trim_empty_lines({lines}) *vim.lsp.util.trim_empty_lines()*
- Removes empty lines from the beginning and end.
-
- Parameters: ~
- • {lines} (table) list of lines to trim
-
- Return: ~
- (table) trimmed list of lines
-
*vim.lsp.util.try_trim_markdown_code_blocks()*
try_trim_markdown_code_blocks({lines})
Accepts markdown lines and tries to reduce them to a filetype if they
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 7c971097fb..240eb152d7 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -232,6 +232,11 @@ The following changes to existing APIs or features add new behavior.
In addition, |nvim_buf_get_extmarks()| has gained an "overlap" option to
return such ranges even if they started before the specified position.
+• LSP hover and signature help now use Treesitter for highlighting of Markdown
+ content.
+ Note that syntax highlighting of code examples requires a matching parser
+ and may be affected by custom queries.
+
==============================================================================
REMOVED FEATURES *news-removed*
@@ -267,6 +272,7 @@ release.
- |vim.lsp.util.get_progress_messages()| Use |vim.lsp.status()| instead.
- |vim.lsp.get_active_clients()| Use |vim.lsp.get_clients()| instead.
- |vim.lsp.for_each_buffer_client()| Use |vim.lsp.get_clients()| instead.
+ - |vim.lsp.util.trim_empty_lines()| Use |vim.split()| with `trimempty` instead.
• `vim.loop` has been renamed to `vim.uv`.
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index 5f6e932693..17d5b4668d 100644
--- a/runtime/doc/starting.txt
+++ b/runtime/doc/starting.txt
@@ -1362,9 +1362,9 @@ paths.
*base-directories* *xdg*
The "base" (root) directories conform to the XDG Base Directory Specification.
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
-The $XDG_CONFIG_HOME, $XDG_DATA_HOME, $XDG_RUNTIME_DIR, and $XDG_STATE_HOME
-environment variables are used if defined, else default values (listed below)
-are used.
+The $XDG_CONFIG_HOME, $XDG_DATA_HOME, $XDG_RUNTIME_DIR, $XDG_STATE_HOME,
+$XDG_CACHE_HOME, $XDG_CONFIG_DIRS and $XDG_DATA_DIRS environment variables
+are used if defined, else default values (listed below) are used.
CONFIG DIRECTORY (DEFAULT) ~
*$XDG_CONFIG_HOME* Nvim: stdpath("config")
@@ -1386,9 +1386,32 @@ STATE DIRECTORY (DEFAULT) ~
Unix: ~/.local/state ~/.local/state/nvim
Windows: ~/AppData/Local ~/AppData/Local/nvim-data
+CACHE DIRECTORY (DEFAULT) ~
+ *$XDG_CACHE_HOME* Nvim: stdpath("cache")
+ Unix: ~/.cache ~/.cache/nvim
+ Windows: ~/AppData/Local/Temp ~/AppData/Local/Temp/nvim-data
+
+LOG FILE (DEFAULT) ~
+ `$NVIM_LOG_FILE` Nvim: stdpath("log")
+ Unix: ~/.local/state/nvim ~/.local/state/nvim/log
+ Windows: ~/AppData/Local/nvim-data ~/AppData/Local/nvim-data/log
+
+ADDITIONAL CONFIGS DIRECTORY (DEFAULT) ~
+ *$XDG_CONFIG_DIRS* Nvim: stdpath("config_dirs")
+ Unix: /etc/xdg/ /etc/xdg/nvim
+ Windows: Not applicable Not applicable
+
+ADDITIONAL DATA DIRECTORY (DEFAULT) ~
+ *$XDG_DATA_DIRS* Nvim: stdpath("data_dirs")
+ Unix: /usr/local/share /usr/local/share/nvim
+ /usr/share /usr/share/nvim
+ Windows: Not applicable Not applicable
+
Note: Throughout the help pages these defaults are used as placeholders, e.g.
"~/.config" is understood to mean "$XDG_CONFIG_HOME or ~/.config".
+Note: The log file directory is controlled by `$XDG_STATE_HOME`.
+
NVIM_APPNAME *$NVIM_APPNAME*
The standard directories can be further configured by the `$NVIM_APPNAME`
environment variable. This variable controls the sub-directory that Nvim will
@@ -1408,8 +1431,8 @@ LOG FILE *log* *$NVIM_LOG_FILE* *E5430*
Besides 'debug' and 'verbose', Nvim keeps a general log file for internal
debugging, plugins and RPC clients. >
:echo $NVIM_LOG_FILE
-By default, the file is located at stdpath("log")/log unless that path
-is inaccessible or if $NVIM_LOG_FILE was set before |startup|.
+By default, the file is located at stdpath("log")/log ($XDG_STATE_HOME/nvim/log)
+unless that path is inaccessible or if $NVIM_LOG_FILE was set before |startup|.
vim:noet:tw=78:ts=8:ft=help:norl:
diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt
index 6ca486e8cf..0d1ea937c0 100644
--- a/runtime/doc/visual.txt
+++ b/runtime/doc/visual.txt
@@ -169,6 +169,7 @@ If you want to highlight exactly the same area as the last time, you can use
CTRL-C In Visual mode: Stop Visual mode. When insert mode is
pending (the mode message shows
"-- (insert) VISUAL --"), it is also stopped.
+ On MS-Windows, you may need to press CTRL-Break.
==============================================================================
3. Changing the Visual area *visual-change*
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 4ea3dde81c..d43d9a7cfa 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -371,15 +371,21 @@ function M.hover(_, result, ctx, config)
end
return
end
- local markdown_lines = util.convert_input_to_markdown_lines(result.contents)
- markdown_lines = util.trim_empty_lines(markdown_lines)
- if vim.tbl_isempty(markdown_lines) then
+ local format = 'markdown'
+ local contents ---@type string[]
+ if type(result.contents) == 'table' and result.contents.kind == 'plaintext' then
+ format = 'plaintext'
+ contents = vim.split(result.contents.value or '', '\n', { trimempty = true })
+ else
+ contents = util.convert_input_to_markdown_lines(result.contents)
+ end
+ if vim.tbl_isempty(contents) then
if config.silent ~= true then
vim.notify('No information available')
end
return
end
- return util.open_floating_preview(markdown_lines, 'markdown', config)
+ return util.open_floating_preview(contents, format, config)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
@@ -470,7 +476,6 @@ function M.signature_help(_, result, ctx, config)
vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters')
local ft = vim.bo[ctx.bufnr].filetype
local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers)
- lines = util.trim_empty_lines(lines)
if vim.tbl_isempty(lines) then
if config.silent ~= true then
print('No signature help available')
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 54721865b7..988057f5f9 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -102,7 +102,7 @@ end
local function split_lines(value)
value = string.gsub(value, '\r\n?', '\n')
- return split(value, '\n', { plain = true })
+ return split(value, '\n', { plain = true, trimempty = true })
end
local function create_window_without_focus()
@@ -877,9 +877,12 @@ end
--- window for `textDocument/hover`, for parsing the result of
--- `textDocument/signatureHelp`, and potentially others.
---
+--- Note that if the input is of type `MarkupContent` and its kind is `plaintext`,
+--- then the corresponding value is returned without further modifications.
+---
---@param input (`MarkedString` | `MarkedString[]` | `MarkupContent`)
---@param contents (table|nil) List of strings to extend with converted lines. Defaults to {}.
----@return table {contents} extended with lines of converted markdown.
+---@return string[] extended with lines of converted markdown.
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
function M.convert_input_to_markdown_lines(input, contents)
contents = contents or {}
@@ -887,27 +890,13 @@ function M.convert_input_to_markdown_lines(input, contents)
if type(input) == 'string' then
list_extend(contents, split_lines(input))
else
- assert(type(input) == 'table', 'Expected a table for Hover.contents')
+ assert(type(input) == 'table', 'Expected a table for LSP input')
-- MarkupContent
if input.kind then
- -- The kind can be either plaintext or markdown.
- -- If it's plaintext, then wrap it in a <text></text> block
-
- -- Some servers send input.value as empty, so let's ignore this :(
local value = input.value or ''
-
- if input.kind == 'plaintext' then
- -- wrap this in a <text></text> block so that stylize_markdown
- -- can properly process it as plaintext
- value = string.format('<text>\n%s\n</text>', value)
- end
-
- -- assert(type(value) == 'string')
list_extend(contents, split_lines(value))
-- MarkupString variation 2
elseif input.language then
- -- Some servers send input.value as empty, so let's ignore this :(
- -- assert(type(input.value) == 'string')
table.insert(contents, '```' .. input.language)
list_extend(contents, split_lines(input.value or ''))
table.insert(contents, '```')
@@ -925,7 +914,7 @@ function M.convert_input_to_markdown_lines(input, contents)
return contents
end
---- Converts `textDocument/SignatureHelp` response to markdown lines.
+--- Converts `textDocument/signatureHelp` response to markdown lines.
---
---@param signature_help table Response of `textDocument/SignatureHelp`
---@param ft string|nil filetype that will be use as the `lang` for the label markdown code block
@@ -955,10 +944,10 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
end
local label = signature.label
if ft then
- -- wrap inside a code block so stylize_markdown can render it properly
+ -- wrap inside a code block for proper rendering
label = ('```%s\n%s\n```'):format(ft, label)
end
- list_extend(contents, split(label, '\n', { plain = true }))
+ list_extend(contents, split(label, '\n', { plain = true, trimempty = true }))
if signature.documentation then
-- if LSP returns plain string, we treat it as plaintext. This avoids
-- special characters like underscore or similar from being interpreted
@@ -1223,7 +1212,7 @@ function M.preview_location(location, opts)
local syntax = vim.bo[bufnr].syntax
if syntax == '' then
-- When no syntax is set, we use filetype as fallback. This might not result
- -- in a valid syntax definition. See also ft detection in stylize_markdown.
+ -- in a valid syntax definition.
-- An empty syntax is more common now with TreeSitter, since TS disables syntax.
syntax = vim.bo[bufnr].filetype
end
@@ -1240,36 +1229,65 @@ local function find_window_by_var(name, value)
end
end
---- Trims empty lines from input and pad top and bottom with empty lines
----
----@param contents table of lines to trim and pad
----@param opts table with optional fields
---- - pad_top number of lines to pad contents at top (default 0)
---- - pad_bottom number of lines to pad contents at bottom (default 0)
----@return table table of trimmed and padded lines
-function M._trim(contents, opts)
- validate({
- contents = { contents, 't' },
- opts = { opts, 't', true },
- })
- opts = opts or {}
- contents = M.trim_empty_lines(contents)
- if opts.pad_top then
- for _ = 1, opts.pad_top do
- table.insert(contents, 1, '')
+---Returns true if the line is empty or only contains whitespace.
+---@param line string
+---@return boolean
+local function is_blank_line(line)
+ return line and line:match('^%s*$')
+end
+
+---Returns true if the line corresponds to a Markdown thematic break.
+---@param line string
+---@return boolean
+local function is_separator_line(line)
+ return line and line:match('^ ? ? ?%-%-%-+%s*$')
+end
+
+---Replaces separator lines by the given divider and removing surrounding blank lines.
+---@param contents string[]
+---@param divider string
+---@return string[]
+local function replace_separators(contents, divider)
+ local trimmed = {}
+ local l = 1
+ while l <= #contents do
+ local line = contents[l]
+ if is_separator_line(line) then
+ if l > 1 and is_blank_line(contents[l - 1]) then
+ table.remove(trimmed)
+ end
+ table.insert(trimmed, divider)
+ if is_blank_line(contents[l + 1]) then
+ l = l + 1
+ end
+ else
+ table.insert(trimmed, line)
end
+ l = l + 1
end
- if opts.pad_bottom then
- for _ = 1, opts.pad_bottom do
- table.insert(contents, '')
+
+ return trimmed
+end
+
+---Collapses successive blank lines in the input table into a single one.
+---@param contents string[]
+---@return string[]
+local function collapse_blank_lines(contents)
+ local collapsed = {}
+ local l = 1
+ while l <= #contents do
+ local line = contents[l]
+ if is_blank_line(line) then
+ while is_blank_line(contents[l + 1]) do
+ l = l + 1
+ end
end
+ table.insert(collapsed, line)
+ l = l + 1
end
- return contents
+ return collapsed
end
---- Generates a table mapping markdown code block lang to vim syntax,
---- based on g:markdown_fenced_languages
----@return table table of lang -> syntax mappings
local function get_markdown_fences()
local fences = {}
for _, fence in pairs(vim.g.markdown_fenced_languages or {}) do
@@ -1297,8 +1315,6 @@ end
--- - wrap_at character to wrap at for computing height
--- - max_width maximal width of floating window
--- - max_height maximal height of floating window
---- - pad_top number of lines to pad contents at top
---- - pad_bottom number of lines to pad contents at bottom
--- - separator insert separator after code block
---@return table stripped content
function M.stylize_markdown(bufnr, contents, opts)
@@ -1335,7 +1351,7 @@ function M.stylize_markdown(bufnr, contents, opts)
end
-- Clean up
- contents = M._trim(contents, opts)
+ contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true })
local stripped = {}
local highlights = {}
@@ -1484,6 +1500,45 @@ function M.stylize_markdown(bufnr, contents, opts)
return stripped
end
+--- @class lsp.util.NormalizeMarkdownOptions
+--- @field width integer Thematic breaks are expanded to this size. Defaults to 80.
+
+--- Normalizes Markdown input to a canonical form.
+---
+--- The returned Markdown adheres to the GitHub Flavored Markdown (GFM)
+--- specification.
+---
+--- The following transformations are made:
+---
+--- 1. Carriage returns ('\r') and empty lines at the beginning and end are removed
+--- 2. Successive empty lines are collapsed into a single empty line
+--- 3. Thematic breaks are expanded to the given width
+---
+---@private
+---@param contents string[]
+---@param opts? lsp.util.NormalizeMarkdownOptions
+---@return string[] table of lines containing normalized Markdown
+---@see https://github.github.com/gfm
+function M._normalize_markdown(contents, opts)
+ validate({
+ contents = { contents, 't' },
+ opts = { opts, 't', true },
+ })
+ opts = opts or {}
+
+ -- 1. Carriage returns are removed
+ contents = vim.split(table.concat(contents, '\n'):gsub('\r', ''), '\n', { trimempty = true })
+
+ -- 2. Successive empty lines are collapsed into a single empty line
+ contents = collapse_blank_lines(contents)
+
+ -- 3. Thematic breaks are expanded to the given width
+ local divider = string.rep('─', opts.width or 80)
+ contents = replace_separators(contents, divider)
+
+ return contents
+end
+
--- Closes the preview window
---
---@param winnr integer window id of preview window
@@ -1620,8 +1675,6 @@ end
--- - wrap_at: (integer) character to wrap at for computing height when wrap is enabled
--- - max_width: (integer) maximal width of floating window
--- - max_height: (integer) maximal height of floating window
---- - pad_top: (integer) number of lines to pad contents at top
---- - pad_bottom: (integer) number of lines to pad contents at bottom
--- - focus_id: (string) if a popup with this id is opened, then focus it
--- - close_events: (table) list of events that closes the floating window
--- - focusable: (boolean, default true) Make float focusable
@@ -1629,8 +1682,7 @@ end
--- is also `true`, focus an existing floating window with the same
--- {focus_id}
---@return integer bufnr of newly created float window
----@return integer winid of newly created float window
----preview window
+---@return integer winid of newly created float window preview window
function M.open_floating_preview(contents, syntax, opts)
validate({
contents = { contents, 't' },
@@ -1639,7 +1691,6 @@ function M.open_floating_preview(contents, syntax, opts)
})
opts = opts or {}
opts.wrap = opts.wrap ~= false -- wrapping by default
- opts.stylize_markdown = opts.stylize_markdown ~= false and vim.g.syntax_on ~= nil
opts.focus = opts.focus ~= false
opts.close_events = opts.close_events or { 'CursorMoved', 'CursorMovedI', 'InsertCharPre' }
@@ -1671,16 +1722,21 @@ function M.open_floating_preview(contents, syntax, opts)
api.nvim_win_close(existing_float, true)
end
+ -- Create the buffer
local floating_bufnr = api.nvim_create_buf(false, true)
- local do_stylize = syntax == 'markdown' and opts.stylize_markdown
-
- -- Clean up input: trim empty lines from the end, pad
- contents = M._trim(contents, opts)
+ -- Set up the contents, using treesitter for markdown
+ local do_stylize = syntax == 'markdown' and vim.g.syntax_on ~= nil
if do_stylize then
- -- applies the syntax and sets the lines to the buffer
- contents = M.stylize_markdown(floating_bufnr, contents, opts)
+ local width = M._make_floating_popup_size(contents, opts)
+ contents = M._normalize_markdown(contents, { width = width })
+ vim.bo[floating_bufnr].filetype = 'markdown'
+ vim.treesitter.start(floating_bufnr)
+ api.nvim_buf_set_lines(floating_bufnr, 0, -1, false, contents)
else
+ -- Clean up input: trim empty lines
+ contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true })
+
if syntax then
vim.bo[floating_bufnr].syntax = syntax
end
@@ -1697,9 +1753,9 @@ function M.open_floating_preview(contents, syntax, opts)
local float_option = M.make_floating_popup_options(width, height, opts)
local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option)
+
if do_stylize then
vim.wo[floating_winnr].conceallevel = 2
- vim.wo[floating_winnr].concealcursor = 'n'
end
-- disable folding
vim.wo[floating_winnr].foldenable = false
@@ -1708,6 +1764,7 @@ function M.open_floating_preview(contents, syntax, opts)
vim.bo[floating_bufnr].modifiable = false
vim.bo[floating_bufnr].bufhidden = 'wipe'
+
api.nvim_buf_set_keymap(
floating_bufnr,
'n',
@@ -1908,6 +1965,7 @@ function M.symbols_to_items(symbols, bufnr)
end
--- Removes empty lines from the beginning and end.
+---@deprecated use `vim.split()` with `trimempty` instead
---@param lines table list of lines to trim
---@return table trimmed list of lines
function M.trim_empty_lines(lines)
diff --git a/runtime/queries/markdown/highlights.scm b/runtime/queries/markdown/highlights.scm
index e78d233cc6..2cc5546bac 100644
--- a/runtime/queries/markdown/highlights.scm
+++ b/runtime/queries/markdown/highlights.scm
@@ -61,3 +61,11 @@
] @string.escape
(inline) @spell
+
+;; Conceal backticks
+(fenced_code_block
+ (fenced_code_block_delimiter) @conceal
+ (#set! conceal ""))
+(fenced_code_block
+ (info_string (language) @conceal
+ (#set! conceal "")))
diff --git a/runtime/queries/markdown_inline/highlights.scm b/runtime/queries/markdown_inline/highlights.scm
index cd5da530d7..c75da478af 100644
--- a/runtime/queries/markdown_inline/highlights.scm
+++ b/runtime/queries/markdown_inline/highlights.scm
@@ -92,3 +92,11 @@
"]"
] @conceal
(#set! conceal ""))
+
+;; Replace common HTML entities.
+((entity_reference) @conceal (#eq? @conceal "&nbsp;") (#set! conceal ""))
+((entity_reference) @conceal (#eq? @conceal "&lt;") (#set! conceal "<"))
+((entity_reference) @conceal (#eq? @conceal "&gt;") (#set! conceal ">"))
+((entity_reference) @conceal (#eq? @conceal "&amp;") (#set! conceal "&"))
+((entity_reference) @conceal (#eq? @conceal "&quot;") (#set! conceal "\""))
+((entity_reference) @conceal (#any-of? @conceal "&ensp;" "&emsp;") (#set! conceal " "))
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 3d77d65fbc..ebdbd896a5 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -197,6 +197,9 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E
if (win_valid(wp) && buffer > 0) {
Boolean noautocmd = !enter || fconfig.noautocmd;
win_set_buf(wp, buf, noautocmd, err);
+ if (!fconfig.noautocmd) {
+ apply_autocmds(EVENT_WINNEW, NULL, NULL, false, buf);
+ }
}
if (!win_valid(wp)) {
api_set_error(err, kErrorTypeException, "Window was closed immediately");
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 4280d62a95..d730f247a9 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -4513,7 +4513,7 @@ static void nv_replace(cmdarg_T *cap)
// Visual mode "r"
if (VIsual_active) {
if (got_int) {
- reset_VIsual();
+ got_int = false;
}
if (had_ctrl_v) {
// Use a special (negative) number to make a difference between a
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 803c5173ea..c34999ed32 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1915,6 +1915,9 @@ bool parse_winhl_opt(win_T *wp)
char *commap = xstrchrnul(hi, ',');
size_t len = (size_t)(commap - hi);
int hl_id = len ? syn_check_group(hi, len) : -1;
+ if (hl_id == 0) {
+ return false;
+ }
int hl_id_link = nlen ? syn_check_group(p, nlen) : 0;
HlAttrs attrs = HLATTRS_INIT;
diff --git a/test/functional/plugin/lsp/utils_spec.lua b/test/functional/plugin/lsp/utils_spec.lua
index f544255d81..12763cfef5 100644
--- a/test/functional/plugin/lsp/utils_spec.lua
+++ b/test/functional/plugin/lsp/utils_spec.lua
@@ -88,6 +88,40 @@ describe('vim.lsp.util', function()
end)
end)
+ describe('normalize_markdown', function ()
+ it('collapses consecutive blank lines', function ()
+ local result = exec_lua [[
+ local lines = {
+ 'foo',
+ '',
+ '',
+ '',
+ 'bar',
+ '',
+ 'baz'
+ }
+ return vim.lsp.util._normalize_markdown(lines)
+ ]]
+ local expected = {'foo', '', 'bar', '', 'baz'}
+ eq(expected, result)
+ end)
+
+ it('removes preceding and trailing empty lines', function ()
+ local result = exec_lua [[
+ local lines = {
+ '',
+ 'foo',
+ 'bar',
+ '',
+ ''
+ }
+ return vim.lsp.util._normalize_markdown(lines)
+ ]]
+ local expected = {'foo', 'bar'}
+ eq(expected, result)
+ end)
+ end)
+
describe("make_floating_popup_options", function ()
local function assert_anchor(anchor_bias, expected_anchor)
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index e0a8badb67..7e30af5058 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -3032,7 +3032,7 @@ describe('LSP', function()
}
return vim.lsp.util.convert_signature_help_to_markdown_lines(signature_help, 'cs', {','})
]]
- local expected = {'```cs', 'TestEntity.TestEntity()', '```', '<text>', 'some doc', '</text>'}
+ local expected = {'```cs', 'TestEntity.TestEntity()', '```', 'some doc'}
eq(expected, result)
end)
end)
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index e1d5f6b965..f75fb52108 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -103,6 +103,29 @@ describe('float window', function()
assert_alive()
end)
+ it('open with WinNew autocmd', function()
+ local res = exec_lua([[
+ local triggerd = false
+ local buf = vim.api.nvim_create_buf(true, true)
+ vim.api.nvim_create_autocmd('WinNew', {
+ callback = function(opt)
+ if opt.buf == buf then
+ triggerd = true
+ end
+ end
+ })
+ local opts = {
+ relative = "win",
+ row = 0, col = 0,
+ width = 1, height = 1,
+ noautocmd = false,
+ }
+ vim.api.nvim_open_win(buf, true, opts)
+ return triggerd
+ ]])
+ eq(true, res)
+ end)
+
it('opened with correct height', function()
local height = exec_lua([[
vim.go.winheight = 20
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index c68f4cf34c..931e1f9985 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -8,6 +8,7 @@ local feed_command, eq = helpers.feed_command, helpers.eq
local curbufmeths = helpers.curbufmeths
local funcs = helpers.funcs
local meths = helpers.meths
+local exec_lua = helpers.exec_lua
describe('colorscheme compatibility', function()
before_each(function()
@@ -2641,4 +2642,17 @@ describe('highlight namespaces', function()
|
]]}
end)
+
+ it('winhl does not accept invalid value #24586', function()
+ local res = exec_lua([[
+ local curwin = vim.api.nvim_get_current_win()
+ vim.api.nvim_command("set winhl=Normal:Visual")
+ local _, msg = pcall(vim.api.nvim_command,"set winhl='Normal:Wrong'")
+ return { msg, vim.wo[curwin].winhl }
+ ]])
+ eq({
+ "Vim(set):E5248: Invalid character in group name",
+ "Normal:Visual",
+ },res)
+ end)
end)
diff --git a/test/old/testdir/test_visual.vim b/test/old/testdir/test_visual.vim
index a12ecefc73..5d70492451 100644
--- a/test/old/testdir/test_visual.vim
+++ b/test/old/testdir/test_visual.vim
@@ -1577,4 +1577,18 @@ func Test_visual_hl_with_showbreak()
call StopVimInTerminal(buf)
endfunc
+func Test_Visual_r_CTRL_C()
+ new
+ " visual r_cmd
+ call setline(1, [' '])
+ call feedkeys("\<c-v>$r\<c-c>", 'tx')
+ call assert_equal([''], getline(1, 1))
+
+ " visual gr_cmd
+ call setline(1, [' '])
+ call feedkeys("\<c-v>$gr\<c-c>", 'tx')
+ call assert_equal([''], getline(1, 1))
+ bw!
+endfu
+
" vim: shiftwidth=2 sts=2 expandtab