Skip to content

Commit b56fa1b

Browse files
committedMar 5, 2025
feat: provide in-process lsp completion source
## Details Rather than requiring a source to be added to completion engine by user create an in-process LSP that provides completion capabilities to whatever engine the user happens to be running. That way the setup is the same for both `nvim-cmp` & `blink.cmp` and more minimal since LSPs are enabled by default by completion engines. Idea for this and the implementation came from [saecki/crates.nvim](https://github.com/saecki/crates.nvim) and seems to have no downside. Can add more capabilities in the future though I don't think there's a strong use case for this yet. To avoid breaking existing `nvim-cmp` users we add opt-in configurations for `lsp` & `coq`, and if neither of these are set we continue to register the `nvim-cmp` source, keeping default behavior effectively unchanged. To enable this feature set: ```lua require('render-markdown').setup({ completions = { lsp = { enabled = true } }, }) ``` Since having multiple of these sources running at once would result in duplicate suggestions we only setup the first one that is enabled in the order `lsp` -> `coq` -> `cmp`.
1 parent 059f503 commit b56fa1b

File tree

13 files changed

+231
-28
lines changed

13 files changed

+231
-28
lines changed
 

Diff for: ‎README.md

+28-1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@ use({
123123
This plugin provides completions for both checkboxes and callouts provided you follow
124124
the relevant setup.
125125

126+
## in-process lsp
127+
128+
The recommended way of getting completions from this plugin. Only requires being
129+
enabled with no additional configuration, assuming you have general LSP completions.
130+
131+
```lua
132+
require('render-markdown').setup({
133+
completions = { lsp = { enabled = true } },
134+
})
135+
```
136+
126137
## nvim-cmp
127138

128139
```lua
@@ -154,7 +165,9 @@ require('blink.cmp').setup({
154165
## coq_nvim
155166

156167
```lua
157-
require('render-markdown.integ.coq').setup()
168+
require('render-markdown').setup({
169+
completions = { coq = { enabled = true } },
170+
})
158171
```
159172

160173
# Setup
@@ -263,6 +276,12 @@ require('render-markdown').setup({
263276
-- Called after plugin clears a buffer.
264277
clear = function() end,
265278
},
279+
completions = {
280+
-- Settings for coq_nvim completions source
281+
coq = { enabled = false },
282+
-- Settings for in-process language server completions
283+
lsp = { enabled = false },
284+
},
266285
-- Useful context to have when evaluating values.
267286
-- | level | the number of '#' in the heading marker |
268287
-- | sections | for each level how deeply nested the heading is |
@@ -1489,6 +1508,14 @@ The table below shows all the highlight groups with their default link
14891508
- [Markdown Ecosystem](doc/markdown-ecosystem.md): Information about other `markdown`
14901509
related plugins and how they co-exist
14911510
1511+
# Acknowledgments
1512+
1513+
- [headlines.nvim](https://github.com/lukas-reineke/headlines.nvim): The plugin that
1514+
inspired me to create this one and whose implementation I used as a reference for
1515+
the original version
1516+
- [crates.nvim](https://github.com/Saecki/crates.nvim): Used the in-process lsp implementation
1517+
as an awesome reference [lsp.lua](https://github.com/saecki/crates.nvim/blob/main/lua/crates/lsp.lua)
1518+
14921519
<!-- panvimdoc-ignore-start -->
14931520
14941521
# Donate

Diff for: ‎doc/render-markdown.txt

+35-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*render-markdown.txt* For 0.10.0 Last change: 2025 March 03
1+
*render-markdown.txt* For 0.10.0 Last change: 2025 March 04
22

33
==============================================================================
44
Table of Contents *render-markdown-table-of-contents*
@@ -12,6 +12,7 @@ Table of Contents *render-markdown-table-of-contents*
1212
- packer.nvim |render-markdown-install-packer.nvim|
1313
5. Commands |render-markdown-commands|
1414
6. Completions |render-markdown-completions|
15+
- in-process lsp |render-markdown-completions-in-process-lsp|
1516
- nvim-cmp |render-markdown-completions-nvim-cmp|
1617
- blink.cmp |render-markdown-completions-blink.cmp|
1718
- coq_nvim |render-markdown-completions-coq_nvim|
@@ -34,6 +35,7 @@ Table of Contents *render-markdown-table-of-contents*
3435
- obsidian.nvim |render-markdown-info-obsidian.nvim|
3536
- Images |render-markdown-info-images|
3637
- Additional |render-markdown-info-additional|
38+
10. Acknowledgments |render-markdown-acknowledgments|
3739

3840
==============================================================================
3941
1. render-markdown.nvim *render-markdown-render-markdown.nvim*
@@ -180,6 +182,19 @@ This plugin provides completions for both checkboxes and callouts provided you
180182
follow the relevant setup.
181183

182184

185+
IN-PROCESS LSP *render-markdown-completions-in-process-lsp*
186+
187+
The recommended way of getting completions from this plugin. Only requires
188+
being enabled with no additional configuration, assuming you have general LSP
189+
completions.
190+
191+
>lua
192+
require('render-markdown').setup({
193+
completions = { lsp = { enabled = true } },
194+
})
195+
<
196+
197+
183198
NVIM-CMP *render-markdown-completions-nvim-cmp*
184199

185200
>lua
@@ -213,7 +228,9 @@ BLINK.CMP *render-markdown-completions-blink.cmp*
213228
COQ_NVIM *render-markdown-completions-coq_nvim*
214229

215230
>lua
216-
require('render-markdown.integ.coq').setup()
231+
require('render-markdown').setup({
232+
completions = { coq = { enabled = true } },
233+
})
217234
<
218235

219236

@@ -324,6 +341,12 @@ Default Configuration ~
324341
-- Called after plugin clears a buffer.
325342
clear = function() end,
326343
},
344+
completions = {
345+
-- Settings for coq_nvim completions source
346+
coq = { enabled = false },
347+
-- Settings for in-process language server completions
348+
lsp = { enabled = false },
349+
},
327350
-- Useful context to have when evaluating values.
328351
-- | level | the number of '#' in the heading marker |
329352
-- | sections | for each level how deeply nested the heading is |
@@ -1573,6 +1596,16 @@ ADDITIONAL *render-markdown-info-additional*
15731596
- Markdown Ecosystem <doc/markdown-ecosystem.md>: Information about other `markdown`
15741597
related plugins and how they co-exist
15751598

1599+
1600+
==============================================================================
1601+
10. Acknowledgments *render-markdown-acknowledgments*
1602+
1603+
- headlines.nvim <https://github.com/lukas-reineke/headlines.nvim>: The plugin that
1604+
inspired me to create this one and whose implementation I used as a reference for
1605+
the original version
1606+
- crates.nvim <https://github.com/Saecki/crates.nvim>: Used the in-process lsp implementation
1607+
as an awesome reference lsp.lua <https://github.com/saecki/crates.nvim/blob/main/lua/crates/lsp.lua>
1608+
15761609
Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>
15771610

15781611
vim:tw=78:ts=8:noet:ft=help:norl:

Diff for: ‎lua/render-markdown/health.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ local state = require('render-markdown.state')
55
local M = {}
66

77
---@private
8-
M.version = '8.0.19'
8+
M.version = '8.0.20'
99

1010
function M.check()
1111
M.start('version')

Diff for: ‎lua/render-markdown/init.lua

+14
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ local M = {}
1919
---@field [1] string text
2020
---@field [2] string|string[] highlights
2121

22+
---@class (exact) render.md.UserCompletion
23+
---@field public enabled? boolean
24+
25+
---@class (exact) render.md.UserCompletions
26+
---@field public coq? render.md.UserCompletion
27+
---@field public lsp? render.md.UserCompletion
28+
2229
---@class (exact) render.md.CallbackContext
2330
---@field public buf integer
2431

@@ -318,6 +325,7 @@ local M = {}
318325
---@field public change_events? string[]
319326
---@field public injections? table<string, render.md.UserInjection>
320327
---@field public on? render.md.UserCallback
328+
---@field public completions? render.md.UserCompletions
321329
---@field public overrides? render.md.UserConfigOverrides
322330
---@field public custom_handlers? table<string, render.md.Handler>
323331

@@ -412,6 +420,12 @@ M.default_config = {
412420
-- Called after plugin clears a buffer.
413421
clear = function() end,
414422
},
423+
completions = {
424+
-- Settings for coq_nvim completions source
425+
coq = { enabled = false },
426+
-- Settings for in-process language server completions
427+
lsp = { enabled = false },
428+
},
415429
-- Useful context to have when evaluating values.
416430
-- | level | the number of '#' in the heading marker |
417431
-- | sections | for each level how deeply nested the heading is |

Diff for: ‎lua/render-markdown/integ/blink.lua

-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ function Source:get_completions(context, callback)
3333
callback({
3434
is_incomplete_forward = false,
3535
is_incomplete_backward = false,
36-
context = context,
3736
items = items,
3837
})
3938
end

Diff for: ‎lua/render-markdown/integ/cmp.lua

+9-2
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,17 @@ function Source:complete(params, callback)
3030
end
3131

3232
---@class render.md.integ.Cmp
33-
local M = {}
33+
---@field private registered boolean
34+
local M = {
35+
registered = false,
36+
}
3437

35-
---Should only be called from plugin directory
38+
---Should only be called from manager on initial buffer attach
3639
function M.setup()
40+
if M.registered then
41+
return
42+
end
43+
M.registered = true
3744
local has_cmp, cmp = pcall(require, 'cmp')
3845
if not has_cmp then
3946
return

Diff for: ‎lua/render-markdown/integ/coq.lua

+29-19
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
11
local source = require('render-markdown.integ.source')
22

3+
---@class render.md.cmp.Coq
4+
---@field private registered boolean
5+
local M = {
6+
registered = false,
7+
}
8+
9+
---Should only be called from manager on initial buffer attach
10+
---or by a user to enable the integration
11+
function M.setup()
12+
if M.registered then
13+
return
14+
end
15+
M.registered = true
16+
local has_coq = pcall(require, 'coq')
17+
if not has_coq then
18+
return
19+
end
20+
---@type table<integer, table>
21+
COQsources = COQsources or {}
22+
COQsources[M.new_uid(COQsources)] = {
23+
name = 'markdown',
24+
fn = M.complete,
25+
}
26+
end
27+
28+
---@private
329
---@param map table<integer, table>
430
---@return integer
5-
local function new_uid(map)
31+
function M.new_uid(map)
632
---@type integer|nil
733
local key = nil
834
while true do
@@ -14,9 +40,10 @@ local function new_uid(map)
1440
end
1541
end
1642

43+
---@private
1744
---@param args { line: string, pos: { [1]: integer, [2]: integer } }
1845
---@param callback fun(response?: lsp.CompletionItem[])
19-
local function complete(args, callback)
46+
function M.complete(args, callback)
2047
---@return lsp.CompletionItem[]?
2148
local function get_items()
2249
if not source.enabled() then
@@ -31,21 +58,4 @@ local function complete(args, callback)
3158
callback(get_items())
3259
end
3360

34-
---@class render.md.cmp.Coq
35-
local M = {}
36-
37-
---Should only be called by a user to enable the integration
38-
function M.setup()
39-
local has_coq = pcall(require, 'coq')
40-
if not has_coq then
41-
return
42-
end
43-
---@type table<integer, table>
44-
COQsources = COQsources or {}
45-
COQsources[new_uid(COQsources)] = {
46-
name = 'markdown',
47-
fn = complete,
48-
}
49-
end
50-
5161
return M

Diff for: ‎lua/render-markdown/integ/lsp.lua

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
local source = require('render-markdown.integ.source')
2+
3+
---@class render.md.integ.Lsp
4+
local M = {}
5+
6+
---Should only be called from manager on initial buffer attach
7+
function M.setup()
8+
local name = 'render-markdown'
9+
---@type vim.lsp.ClientConfig
10+
local config = {
11+
name = name,
12+
cmd = M.server,
13+
}
14+
---@type vim.lsp.start.Opts
15+
local opts = {
16+
bufnr = 0,
17+
reuse_client = function(lsp_client, lsp_config)
18+
return lsp_client.name == lsp_config.name
19+
end,
20+
}
21+
vim.lsp.start(config, opts)
22+
end
23+
24+
---@private
25+
---@param dispatchers vim.lsp.rpc.Dispatchers
26+
---@return vim.lsp.rpc.PublicClient
27+
function M.server(dispatchers)
28+
local id = 0
29+
local closing = false
30+
31+
---@type vim.lsp.rpc.PublicClient
32+
return {
33+
request = function(method, params, callback)
34+
if method == 'initialize' then
35+
callback(nil, {
36+
capabilities = {
37+
completionProvider = {
38+
triggerCharacters = source.trigger_characters(),
39+
},
40+
},
41+
})
42+
elseif method == 'textDocument/completion' then
43+
callback(nil, M.completions(params))
44+
elseif method == 'shutdown' then
45+
callback(nil, nil)
46+
end
47+
id = id + 1
48+
return true, id
49+
end,
50+
notify = function(method)
51+
if method == 'exit' then
52+
-- code 0 (success), signal 15 (SIGTERM)
53+
dispatchers.on_exit(0, 15)
54+
end
55+
return true
56+
end,
57+
is_closing = function()
58+
return closing
59+
end,
60+
terminate = function()
61+
closing = true
62+
end,
63+
}
64+
end
65+
66+
---@private
67+
---@param params lsp.CompletionParams
68+
---@return lsp.CompletionList?
69+
function M.completions(params)
70+
-- lsp position: (0,0)-indexed
71+
local cursor = params.position
72+
local items = source.items(0, cursor.line, cursor.character)
73+
if items == nil then
74+
return nil
75+
end
76+
---@type lsp.CompletionList
77+
return {
78+
isIncomplete = false,
79+
items = items,
80+
}
81+
end
82+
83+
return M

Diff for: ‎lua/render-markdown/integ/source.lua

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@ function M.trigger_characters()
2222
return { '-', '*', '+', '>', ' ' }
2323
end
2424

25-
---@param buf integer
25+
---@param buf integer 0 for current buffer
2626
---@param row integer 0-indexed
2727
---@param col integer 0-indexed
2828
---@return lsp.CompletionItem[]?
2929
function M.items(buf, row, col)
30+
if buf == 0 then
31+
buf = util.current('buf')
32+
end
33+
3034
local has_parser, parser = pcall(vim.treesitter.get_parser, buf)
3135
if not has_parser or parser == nil then
3236
return nil

Diff for: ‎lua/render-markdown/manager.lua

+7
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ function M.attach(buf)
8787

8888
local config = state.get(buf)
8989
state.on.attach({ buf = buf })
90+
if state.completions.lsp.enabled then
91+
require('render-markdown.integ.lsp').setup()
92+
elseif state.completions.coq.enabled then
93+
require('render-markdown.integ.coq').setup()
94+
else
95+
require('render-markdown.integ.cmp').setup()
96+
end
9097

9198
local events = { 'BufWinEnter', 'BufLeave', 'CmdlineChanged', 'CursorHold', 'CursorMoved', 'WinScrolled' }
9299
local change_events = { 'DiffUpdated', 'ModeChanged', 'TextChanged' }

0 commit comments

Comments
 (0)
Please sign in to comment.