aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim/lsp')
-rw-r--r--runtime/lua/vim/lsp/buf.lua9
-rw-r--r--runtime/lua/vim/lsp/codelens.lua231
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua20
-rw-r--r--runtime/lua/vim/lsp/handlers.lua8
-rw-r--r--runtime/lua/vim/lsp/rpc.lua13
-rw-r--r--runtime/lua/vim/lsp/util.lua158
6 files changed, 340 insertions, 99 deletions
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 5dd7109bb0..b13d662ccb 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -156,7 +156,7 @@ function M.formatting(options)
if client == nil then return end
local params = util.make_formatting_params(options)
- return client.request("textDocument/formatting", params)
+ return client.request("textDocument/formatting", params, nil, vim.api.nvim_get_current_buf())
end
--- Performs |vim.lsp.buf.formatting()| synchronously.
@@ -176,7 +176,7 @@ function M.formatting_sync(options, timeout_ms)
if client == nil then return end
local params = util.make_formatting_params(options)
- local result, err = client.request_sync("textDocument/formatting", params, timeout_ms)
+ local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf())
if result and result.result then
util.apply_text_edits(result.result)
elseif err then
@@ -218,7 +218,7 @@ function M.formatting_seq_sync(options, timeout_ms, order)
for _, client in ipairs(clients) do
if client.resolved_capabilities.document_formatting then
local params = util.make_formatting_params(options)
- local result, err = client.request_sync("textDocument/formatting", params, timeout_ms)
+ local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf())
if result and result.result then
util.apply_text_edits(result.result)
elseif err then
@@ -297,6 +297,7 @@ local function pick_call_hierarchy_item(call_hierarchy_items)
return choice
end
+--@private
local function call_hierarchy(method)
local params = util.make_position_params()
request('textDocument/prepareCallHierarchy', params, function(err, _, result)
@@ -338,7 +339,7 @@ end
--- Add the folder at path to the workspace folders. If {path} is
--- not provided, the user will be prompted for a path using |input()|.
function M.add_workspace_folder(workspace_folder)
- workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'))
+ workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'), 'dir')
vim.api.nvim_command("redraw")
if not (workspace_folder and #workspace_folder > 0) then return end
if vim.fn.isdirectory(workspace_folder) == 0 then
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
new file mode 100644
index 0000000000..fbd37e3830
--- /dev/null
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -0,0 +1,231 @@
+local util = require('vim.lsp.util')
+local api = vim.api
+local M = {}
+
+--- bufnr → true|nil
+--- to throttle refreshes to at most one at a time
+local active_refreshes = {}
+
+--- bufnr -> client_id -> lenses
+local lens_cache_by_buf = setmetatable({}, {
+ __index = function(t, b)
+ local key = b > 0 and b or api.nvim_get_current_buf()
+ return rawget(t, key)
+ end
+})
+
+local namespaces = setmetatable({}, {
+ __index = function(t, key)
+ local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key)
+ rawset(t, key, value)
+ return value
+ end;
+})
+
+--@private
+M.__namespaces = namespaces
+
+
+--@private
+local function execute_lens(lens, bufnr, client_id)
+ local line = lens.range.start.line
+ api.nvim_buf_clear_namespace(bufnr, namespaces[client_id], line, line + 1)
+
+ -- Need to use the client that returned the lens → must not use buf_request
+ local client = vim.lsp.get_client_by_id(client_id)
+ assert(client, 'Client is required to execute lens, client_id=' .. client_id)
+ client.request('workspace/executeCommand', lens.command, function(...)
+ local result = vim.lsp.handlers['workspace/executeCommand'](...)
+ M.refresh()
+ return result
+ end, bufnr)
+end
+
+
+--- Return all lenses for the given buffer
+---
+---@return table (`CodeLens[]`)
+function M.get(bufnr)
+ local lenses_by_client = lens_cache_by_buf[bufnr]
+ if not lenses_by_client then return {} end
+ local lenses = {}
+ for _, client_lenses in pairs(lenses_by_client) do
+ vim.list_extend(lenses, client_lenses)
+ end
+ return lenses
+end
+
+
+--- Run the code lens in the current line
+---
+function M.run()
+ local line = api.nvim_win_get_cursor(0)[1]
+ local bufnr = api.nvim_get_current_buf()
+ local options = {}
+ local lenses_by_client = lens_cache_by_buf[bufnr] or {}
+ for client, lenses in pairs(lenses_by_client) do
+ for _, lens in pairs(lenses) do
+ if lens.range.start.line == (line - 1) then
+ table.insert(options, {client=client, lens=lens})
+ end
+ end
+ end
+ if #options == 0 then
+ vim.notify('No executable codelens found at current line')
+ elseif #options == 1 then
+ local option = options[1]
+ execute_lens(option.lens, bufnr, option.client)
+ else
+ local options_strings = {"Code lenses:"}
+ for i, option in ipairs(options) do
+ table.insert(options_strings, string.format('%d. %s', i, option.lens.command.title))
+ end
+ local choice = vim.fn.inputlist(options_strings)
+ if choice < 1 or choice > #options then
+ return
+ end
+ local option = options[choice]
+ execute_lens(option.lens, bufnr, option.client)
+ end
+end
+
+
+--- Display the lenses using virtual text
+---
+---@param lenses table of lenses to display (`CodeLens[] | null`)
+---@param bufnr number
+---@param client_id number
+function M.display(lenses, bufnr, client_id)
+ if not lenses or not next(lenses) then
+ return
+ end
+ local lenses_by_lnum = {}
+ for _, lens in pairs(lenses) do
+ local line_lenses = lenses_by_lnum[lens.range.start.line]
+ if not line_lenses then
+ line_lenses = {}
+ lenses_by_lnum[lens.range.start.line] = line_lenses
+ end
+ table.insert(line_lenses, lens)
+ end
+ local ns = namespaces[client_id]
+ local num_lines = api.nvim_buf_line_count(bufnr)
+ for i = 0, num_lines do
+ local line_lenses = lenses_by_lnum[i]
+ api.nvim_buf_clear_namespace(bufnr, ns, i, i + 1)
+ local chunks = {}
+ for _, lens in pairs(line_lenses or {}) do
+ local text = lens.command and lens.command.title or 'Unresolved lens ...'
+ table.insert(chunks, {text, 'LspCodeLens' })
+ end
+ if #chunks > 0 then
+ api.nvim_buf_set_virtual_text(bufnr, ns, i, chunks, {})
+ end
+ end
+end
+
+
+--- Store lenses for a specific buffer and client
+---
+---@param lenses table of lenses to store (`CodeLens[] | null`)
+---@param bufnr number
+---@param client_id number
+function M.save(lenses, bufnr, client_id)
+ local lenses_by_client = lens_cache_by_buf[bufnr]
+ if not lenses_by_client then
+ lenses_by_client = {}
+ lens_cache_by_buf[bufnr] = lenses_by_client
+ local ns = namespaces[client_id]
+ api.nvim_buf_attach(bufnr, false, {
+ on_detach = function(b) lens_cache_by_buf[b] = nil end,
+ on_lines = function(_, b, _, first_lnum, last_lnum)
+ api.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum)
+ end
+ })
+ end
+ lenses_by_client[client_id] = lenses
+end
+
+
+--@private
+local function resolve_lenses(lenses, bufnr, client_id, callback)
+ lenses = lenses or {}
+ local num_lens = vim.tbl_count(lenses)
+ if num_lens == 0 then
+ callback()
+ return
+ end
+
+ --@private
+ local function countdown()
+ num_lens = num_lens - 1
+ if num_lens == 0 then
+ callback()
+ end
+ end
+ local ns = namespaces[client_id]
+ local client = vim.lsp.get_client_by_id(client_id)
+ for _, lens in pairs(lenses or {}) do
+ if lens.command then
+ countdown()
+ else
+ client.request('codeLens/resolve', lens, function(_, _, result)
+ if result and result.command then
+ lens.command = result.command
+ -- Eager display to have some sort of incremental feedback
+ -- Once all lenses got resolved there will be a full redraw for all lenses
+ -- So that multiple lens per line are properly displayed
+ api.nvim_buf_set_virtual_text(
+ bufnr,
+ ns,
+ lens.range.start.line,
+ {{ lens.command.title, 'LspCodeLens' },},
+ {}
+ )
+ end
+ countdown()
+ end, bufnr)
+ end
+ end
+end
+
+
+--- |lsp-handler| for the method `textDocument/codeLens`
+---
+function M.on_codelens(err, _, result, client_id, bufnr)
+ assert(not err, vim.inspect(err))
+
+ M.save(result, bufnr, client_id)
+
+ -- Eager display for any resolved (and unresolved) lenses and refresh them
+ -- once resolved.
+ M.display(result, bufnr, client_id)
+ resolve_lenses(result, bufnr, client_id, function()
+ M.display(result, bufnr, client_id)
+ active_refreshes[bufnr] = nil
+ end)
+end
+
+
+--- Refresh the codelens for the current buffer
+---
+--- It is recommended to trigger this using an autocmd or via keymap.
+---
+--- <pre>
+--- autocmd BufEnter,CursorHold,InsertLeave <buffer> lua vim.lsp.codelens.refresh()
+--- </pre>
+---
+function M.refresh()
+ local params = {
+ textDocument = util.make_text_document_params()
+ }
+ local bufnr = api.nvim_get_current_buf()
+ if active_refreshes[bufnr] then
+ return
+ end
+ active_refreshes[bufnr] = true
+ vim.lsp.buf_request(0, 'textDocument/codeLens', params)
+end
+
+
+return M
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index dabe400e0d..64dde78f17 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -271,8 +271,12 @@ local function set_diagnostic_cache(diagnostics, bufnr, client_id)
end
-- Account for servers that place diagnostics on terminating newline
if buf_line_count > 0 then
- local start = diagnostic.range.start
- start.line = math.min(start.line, buf_line_count - 1)
+ diagnostic.range.start.line = math.max(math.min(
+ diagnostic.range.start.line, buf_line_count - 1
+ ), 0)
+ diagnostic.range["end"].line = math.max(math.min(
+ diagnostic.range["end"].line, buf_line_count - 1
+ ), 0)
end
end
@@ -317,9 +321,9 @@ function M.save(diagnostics, bufnr, client_id)
-- Clean up our data when the buffer unloads.
api.nvim_buf_attach(bufnr, false, {
- on_detach = function(b)
+ on_detach = function(_, b)
clear_diagnostic_cache(b, client_id)
- _diagnostic_cleanup[bufnr][client_id] = nil
+ _diagnostic_cleanup[b][client_id] = nil
end
})
end
@@ -1119,6 +1123,8 @@ end
--- </pre>
---@param opts table Configuration table
--- - show_header (boolean, default true): Show "Diagnostics:" header.
+--- - Plus all the opts for |vim.lsp.diagnostic.get_line_diagnostics()|
+--- and |vim.lsp.util.open_floating_preview()| can be used here.
---@param bufnr number The buffer number
---@param line_nr number The line number
---@param client_id number|nil the client id
@@ -1208,9 +1214,9 @@ function M.set_loclist(opts)
if severity then
return d.severity == severity
end
- severity = to_severity(opts.severity_limit)
- if severity then
- return d.severity == severity
+ local severity_limit = to_severity(opts.severity_limit)
+ if severity_limit then
+ return d.severity <= severity_limit
end
return true
end
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 6ae54ea253..41852b9d88 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -187,12 +187,15 @@ M['textDocument/publishDiagnostics'] = function(...)
return require('vim.lsp.diagnostic').on_publish_diagnostics(...)
end
+M['textDocument/codeLens'] = function(...)
+ return require('vim.lsp.codelens').on_codelens(...)
+end
+
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
M['textDocument/references'] = function(_, _, result)
if not result then return end
util.set_qflist(util.locations_to_items(result))
api.nvim_command("copen")
- api.nvim_command("wincmd p")
end
--@private
@@ -207,7 +210,6 @@ local symbol_handler = function(_, _, result, _, bufnr)
util.set_qflist(util.symbols_to_items(result, bufnr))
api.nvim_command("copen")
- api.nvim_command("wincmd p")
end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
M['textDocument/documentSymbol'] = symbol_handler
@@ -298,7 +300,6 @@ local function location_handler(_, method, result)
if #result > 1 then
util.set_qflist(util.locations_to_items(result))
api.nvim_command("copen")
- api.nvim_command("wincmd p")
end
else
util.jump_to_location(result)
@@ -379,7 +380,6 @@ local make_call_hierarchy_handler = function(direction)
end
util.set_qflist(items)
api.nvim_command("copen")
- api.nvim_command("wincmd p")
end
end
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 98835d6708..4c5f02af9d 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -50,8 +50,13 @@ recursive_convert_NIL = function(v, tbl_processed)
return nil
elseif not tbl_processed[v] and type(v) == 'table' then
tbl_processed[v] = true
+ local inside_list = vim.tbl_islist(v)
return vim.tbl_map(function(x)
- return recursive_convert_NIL(x, tbl_processed)
+ if not inside_list or (inside_list and type(x) == "table") then
+ return recursive_convert_NIL(x, tbl_processed)
+ else
+ return x
+ end
end, v)
end
@@ -444,7 +449,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
method = method;
params = params;
}
- if result then
+ if result and message_callbacks then
message_callbacks[message_id] = schedule_wrap(callback)
return result, message_id
else
@@ -543,14 +548,14 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
-- - The server will not send a result callback after this cancellation.
-- - If the server sent this cancellation ACK after sending the result, the user of this RPC
-- client will ignore the result themselves.
- if result_id then
+ if result_id and message_callbacks then
message_callbacks[result_id] = nil
end
return
end
end
- local callback = message_callbacks[result_id]
+ local callback = message_callbacks and message_callbacks[result_id]
if callback then
message_callbacks[result_id] = nil
validate {
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index cb9a7cbed5..195e3a0e65 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -50,7 +50,7 @@ local function get_border_size(opts)
local width = 0
if type(border) == 'string' then
- local border_size = {none = {0, 0}, single = {2, 2}, double = {2, 2}, shadow = {1, 1}}
+ local border_size = {none = {0, 0}, single = {2, 2}, double = {2, 2}, rounded = {2, 2}, solid = {2, 2}, shadow = {1, 1}}
if border_size[border] == nil then
error("floating preview border is not correct. Please refer to the docs |vim.api.nvim_open_win()|"
.. vim.inspect(border))
@@ -806,14 +806,20 @@ function M.convert_input_to_markdown_lines(input, contents)
assert(type(input) == 'table', "Expected a table for Hover.contents")
-- MarkupContent
if input.kind then
- -- The kind can be either plaintext or markdown. However, either way we
- -- will just be rendering markdown, so we handle them both the same way.
- -- TODO these can have escaped/sanitized html codes in markdown. We
- -- should make sure we handle this correctly.
+ -- 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 :(
+ input.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
+ input.value = string.format("<text>\n%s\n</text>", input.value or "")
+ end
+
-- assert(type(input.value) == 'string')
- list_extend(contents, split_lines(input.value or ''))
+ list_extend(contents, split_lines(input.value))
-- MarkupString variation 2
elseif input.language then
-- Some servers send input.value as empty, so let's ignore this :(
@@ -861,7 +867,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft)
end
local label = signature.label
if ft then
- -- wrap inside a code block so fancy_markdown can render it properly
+ -- wrap inside a code block so stylize_markdown can render it properly
label = ("```%s\n%s\n```"):format(ft, label)
end
vim.list_extend(contents, vim.split(label, '\n', true))
@@ -1005,7 +1011,7 @@ function M.preview_location(location, opts)
local syntax = api.nvim_buf_get_option(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 fancy_floating_win.
+ -- in a valid syntax definition. See also ft detection in stylize_markdown.
-- An empty syntax is more common now with TreeSitter, since TS disables syntax.
syntax = api.nvim_buf_get_option(bufnr, 'filetype')
end
@@ -1023,53 +1029,6 @@ local function find_window_by_var(name, value)
end
end
---- Enters/leaves the focusable window associated with the current buffer via the
---window - variable `unique_name`. If no such window exists, run the function
---{fn}.
----
---@param unique_name (string) Window variable
---@param fn (function) should return create a new window and return a tuple of
----({focusable_buffer_id}, {window_id}). if {focusable_buffer_id} is a valid
----buffer id, the newly created window will be the new focus associated with
----the current buffer via the tag `unique_name`.
---@returns (pbufnr, pwinnr) if `fn()` has created a new window; nil otherwise
----@deprecated please use open_floating_preview directly
-function M.focusable_float(unique_name, fn)
- vim.notify("focusable_float is deprecated. Please use open_floating_preview and pass focus_id = [unique_name] instead", vim.log.levels.WARN)
- -- Go back to previous window if we are in a focusable one
- if npcall(api.nvim_win_get_var, 0, unique_name) then
- return api.nvim_command("wincmd p")
- end
- local bufnr = api.nvim_get_current_buf()
- do
- local win = find_window_by_var(unique_name, bufnr)
- if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then
- api.nvim_set_current_win(win)
- api.nvim_command("stopinsert")
- return
- end
- end
- local pbufnr, pwinnr = fn()
- if pbufnr then
- api.nvim_win_set_var(pwinnr, unique_name, bufnr)
- return pbufnr, pwinnr
- end
-end
-
---- Focuses/unfocuses the floating preview window associated with the current
---- buffer via the window variable `unique_name`. If no such preview window
---- exists, makes a new one.
----
---@param unique_name (string) Window variable
---@param fn (function) The return values of this function will be passed
----directly to |vim.lsp.util.open_floating_preview()|, in the case that a new
----floating window should be created
----@deprecated please use open_floating_preview directly
-function M.focusable_preview(unique_name, fn)
- vim.notify("focusable_preview is deprecated. Please use open_floating_preview and pass focus_id = [unique_name] instead", vim.log.levels.WARN)
- return M.open_floating_preview(fn(), {focus_id = unique_name})
-end
-
--- Trims empty lines from input and pad top and bottom with empty lines
---
---@param contents table of lines to trim and pad
@@ -1097,12 +1056,19 @@ function M._trim(contents, opts)
return contents
end
-
-
---- @deprecated please use open_floating_preview directly
-function M.fancy_floating_markdown(contents, opts)
- vim.notify("fancy_floating_markdown is deprecated. Please use open_floating_preview and pass focus_id = [unique_name] instead", vim.log.levels.WARN)
- return M.open_floating_preview(contents, "markdown", opts)
+-- Generates a table mapping markdown code block lang to vim syntax,
+-- based on g:markdown_fenced_languages
+-- @return a table of lang -> syntax mappings
+-- @private
+local function get_markdown_fences()
+ local fences = {}
+ for _, fence in pairs(vim.g.markdown_fenced_languages or {}) do
+ local lang, syntax = fence:match("^(.*)=(.*)$")
+ if lang then
+ fences[lang] = syntax
+ end
+ end
+ return fences
end
--- Converts markdown into syntax highlighted regions by stripping the code
@@ -1134,26 +1100,50 @@ function M.stylize_markdown(bufnr, contents, opts)
}
opts = opts or {}
+ -- table of fence types to {ft, begin, end}
+ -- when ft is nil, we get the ft from the regex match
+ local matchers = {
+ block = {nil, "```+([a-zA-Z0-9_]*)", "```+"},
+ pre = {"", "<pre>", "</pre>"},
+ code = {"", "<code>", "</code>"},
+ text = {"plaintex", "<text>", "</text>"},
+ }
+
+ local match_begin = function(line)
+ for type, pattern in pairs(matchers) do
+ local ret = line:match(string.format("^%%s*%s%%s*$", pattern[2]))
+ if ret then
+ return {
+ type = type,
+ ft = pattern[1] or ret
+ }
+ end
+ end
+ end
+
+ local match_end = function(line, match)
+ local pattern = matchers[match.type]
+ return line:match(string.format("^%%s*%s%%s*$", pattern[3]))
+ end
+
+ -- Clean up
+ contents = M._trim(contents, opts)
+
local stripped = {}
local highlights = {}
+ -- keep track of lnums that contain markdown
+ local markdown_lines = {}
do
local i = 1
while i <= #contents do
local line = contents[i]
- -- TODO(ashkan): use a more strict regex for filetype?
- local ft = line:match("^```([a-zA-Z0-9_]*)$")
- -- local ft = line:match("^```(.*)$")
- -- TODO(ashkan): validate the filetype here.
- local is_pre = line:match("^%s*<pre>%s*$")
- if is_pre then
- ft = ""
- end
- if ft then
+ local match = match_begin(line)
+ if match then
local start = #stripped
i = i + 1
while i <= #contents do
line = contents[i]
- if line == "```" or (is_pre and line:match("^%s*</pre>%s*$")) then
+ if match_end(line, match) then
i = i + 1
break
end
@@ -1161,23 +1151,30 @@ function M.stylize_markdown(bufnr, contents, opts)
i = i + 1
end
table.insert(highlights, {
- ft = ft;
+ ft = match.ft;
start = start + 1;
- finish = #stripped + 1 - 1;
+ finish = #stripped;
})
else
table.insert(stripped, line)
+ markdown_lines[#stripped] = true
i = i + 1
end
end
end
- -- Clean up
- stripped = M._trim(stripped, opts)
-- Compute size of float needed to show (wrapped) lines
opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0))
local width, height = M._make_floating_popup_size(stripped, opts)
+ local sep_line = string.rep("─", math.min(width, opts.wrap_at or width))
+
+ for l in pairs(markdown_lines) do
+ if stripped[l]:match("^---+$") then
+ stripped[l] = sep_line
+ end
+ end
+
-- Insert blank line separator after code block
local insert_separator = opts.separator
if insert_separator == nil then insert_separator = true end
@@ -1188,10 +1185,8 @@ function M.stylize_markdown(bufnr, contents, opts)
h.finish = h.finish + offset
-- check if a seperator already exists and use that one instead of creating a new one
if h.finish + 1 <= #stripped then
- if stripped[h.finish + 1]:match("^---+$") then
- stripped[h.finish + 1] = string.rep("─", math.min(width, opts.wrap_at or width))
- else
- table.insert(stripped, h.finish + 1, string.rep("─", math.min(width, opts.wrap_at or width)))
+ if stripped[h.finish + 1] ~= sep_line then
+ table.insert(stripped, h.finish + 1, sep_line)
offset = offset + 1
height = height + 1
end
@@ -1199,6 +1194,7 @@ function M.stylize_markdown(bufnr, contents, opts)
end
end
+
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped)
local idx = 1
@@ -1206,11 +1202,13 @@ function M.stylize_markdown(bufnr, contents, opts)
-- keep track of syntaxes we already inlcuded.
-- no need to include the same syntax more than once
local langs = {}
+ local fences = get_markdown_fences()
local function apply_syntax_to_region(ft, start, finish)
if ft == "" then
vim.cmd(string.format("syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend", start, finish + 1))
return
end
+ ft = fences[ft] or ft
local name = ft..idx
idx = idx + 1
local lang = "@"..ft:upper()
@@ -1239,7 +1237,7 @@ function M.stylize_markdown(bufnr, contents, opts)
apply_syntax_to_region(h.ft, h.start, h.finish)
last = h.finish + 1
end
- if last < #stripped then
+ if last <= #stripped then
apply_syntax_to_region("lsp_markdown", last, #stripped)
end
end)