Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Providing a Language Server (LSP) #22

Open
17 of 30 tasks
mickael-menu opened this issue Apr 3, 2021 · 107 comments
Open
17 of 30 tasks

Providing a Language Server (LSP) #22

mickael-menu opened this issue Apr 3, 2021 · 107 comments
Labels
enhancement New feature or request

Comments

@mickael-menu
Copy link
Member

mickael-menu commented Apr 3, 2021

zk can provide a basic integration with any LSP-compatible text editor by shipping a Language Server.

PR #21 implements a proof of concept server showing promising results, but there's more to come:

  • Links

    • Auto-complete internal Markdown links with [[
    • Auto-complete the path portion of a Markdown link with [custom title]((
    • User configuration to specify the link format using a template
    • Open a note from an inline Markdown link or WikiLink (document link when supported and go to definition as an alternative)
    • Open a website from an inline external Markdown link (with textDocument/documentLink)
    • Open a reference link
    • Move the caret to the next link in the note with a code action, for quick navigation (with window.showDocument)
    • Preview the content of a linked note with a hover
    • Diagnostic (error) to show dead links
    • Diagnostic (hint) to show the title of a linked note next to the link
    • Browse backlinks with find references
    • Browse backlinks / outbound links with a tree-like call hierarchy (client support is still lacking)
    • Highlight WikiLinks using semantic tokens (client support is still lacking)
    • (Maybe?) Report DocumentLink for unlinked mentions, using aliases metadata
  • Tags

    • Auto-complete tags with # and : trigger characters
    • Rename a tag with a refactor code action
    • Highlight tags using semantic tokens (client support is still lacking)
    • View the list of all the notes for a particular tag when running the References action.
  • Notes

    • Code action to create (and link) a new note using the current selection as its title
    • Code action to create (and link) a new note using the current selection as its content (refactor.extract?)
    • Code action to rename the note title
    • User configuration to declare dynamic code actions to generate notes with custom group / template
    • Snippet support using zk templates
  • Expose zk commands as LSP commands, for easy client-side consumption

    • zk.init to create a new notebook
    • zk.index to index a notebook manually
    • zk.new to create a new note
    • zk.list to search for notes (returning a JSON)
    • zk.tag.list to return the list of tags
    • zk.info to provide detailed JSON metadata about a notebook (list of groups, templates, dirs, etc.)
  • Other

    • Optimizations for large (10k+) notebooks (esp. completion)

Feel free to share more ideas!

@mhanberg
Copy link
Contributor

mhanberg commented Apr 5, 2021

Tested this out, it's very cool!

I am seeing an issue using go to definition with neovim builtin lsp, but i'm not sure if that's a neovim problem or a zk problem. The error is Error executing vim.schedule lua callback: ...stalls/neovim/nightly/share/nvim/runtime/lua/vim/uri.lua:116: attempt to call method 'match' (a nil value)

Log
2021/04/04 21:00:24.323 DEBUG [zk.rpc] jsonrpc2: <-- result #26: textDocument/definit
ion: {"uri":{"start":{"line":7,"character":0},"end":{"line":7,"character":8}},"target
Uri":"file:///Users/mitchell/Dropbox/notes/zk/gznd.md","targetRange":{"start":{"line"
:0,"character":0},"end":{"line":0,"character":0}},"targetSelectionRage":{"start":{"li
ne":0,"character":0},"end":{"line":0,"character":0}}}

@mickael-menu
Copy link
Member Author

@mhanberg It was actually caused by a bug in a third-party dependency. I opened a PR but forgot to disable the faulty portion in zk. You can try again from the latest main or the following pre-built binaries:

Btw, I tried the builtin LSP but couldn't make the trigger characters work for the completion. Did you setup anything special for this?

@mhanberg
Copy link
Contributor

mhanberg commented Apr 5, 2021

I updated and go to def works now 👍.

Btw, I tried the builtin LSP but couldn't make the trigger characters work for the completion. Did you setup anything special for this?

I use nvim-compe for completion.

@megalithic
Copy link

@mickael-menu first off, this is so great!

I've been using it with nvim-compe along with @mhanberg, too.

A couple items I've noticed.

  1. When attempting to initiate auto-complete of files with [[ or [ depending on the mode you want, it doesn't complete the file extension, which then breaks go to definition, as soon as I manually add the the file extension, go to definition works as expected.
  2. This is more of a question/confirmation of expected behaviour (the above might also be that, too); when auto-completing tags, should the \ escaping of space be converted back to ?

Other than those two minor items, this has been fantastic to use!

@mickael-menu
Copy link
Member Author

mickael-menu commented Apr 6, 2021

@megalithic

  1. When attempting to initiate auto-complete of files with [[ or [ depending on the mode you want, it doesn't complete the file extension, which then breaks go to definition, as soon as I manually add the the file extension, go to definition works as expected.

I set up the links for my own usage and I usually prefer to omit the extension. But I'll let users configure the format of the generated links in the config later on.

However, the link href is matched as a prefix of the actual path, so it should work without the extension or if you have a unique ID prefix. For example, [[202005201056]] would match 202005201056 Interesting subject.md. Could you share an example of link + note path pair which doesn't work? And what do you see in the LSP logs?

  1. This is more of a question/confirmation of expected behaviour (the above might also be that, too); when auto-completing tags, should the \ escaping of space be converted back to ?

This is expected, because a normal #hashtag can't contain spaces, so I added this ad-hoc escape syntax if an existing tag contains spaces. But if you enable Bear's multi-word tags syntax in the config, instead of using \ escape character the completed tag will be like #multi-word tag#
What tag syntax did you use so far supporting spaces?

@megalithic
Copy link

Thanks @mickael-menu .. I'm still playing around with this to get a repro case for you. I think I might be using something wrong; meaning, I'm looking to be able to [[ then rely on "friendly" titles to complete on. I've also noticed that not all items in my main notebook are showing up in the completion menu. I'll get a loom or something going to demonstrate today.

@megalithic
Copy link

megalithic commented Apr 6, 2021

Ahh, I'm wondering if certain characters in a filename or title might be breaking completion? |, :, and & are command items in some of my note file names and titles (i auto-extract calendar titles and use those from meeting note generation and pass that along to zk.nvim to generate the note, or open an existing note with the same file name).

Will check the logs to verify this.

When searching for a note (using zk edit --interactive via cli, not abstracted out via zk.nvim) that does contain one of the above characters, these are throw into the preview area of fzf:

image

@mickael-menu
Copy link
Member Author

I think I might be using something wrong; meaning, I'm looking to be able to [[ then rely on "friendly" titles to complete on

Yes right now the completion is on the note title (heading or YAML frontmatter) and not on the file path. I intend to add both in the completion filter later.

I'll open a new issue for the special characters.

@mhanberg
Copy link
Contributor

mhanberg commented Apr 7, 2021

Go to definition seems to be working when the current note is in the root of the notebook, but not when the note is in a sub-directory. I noticed this when implementing the "daily journal" example from the docs.

@mickael-menu
Copy link
Member Author

@mhanberg Thanks for spotting that, should be fixed in main

zk-v0.3.0-5-gdd561be-macos-arm64.zip

@mickael-menu
Copy link
Member Author

I merged in a way to specify the completion link format from the user configuration: #32

[format.markdown]
# Format used to generate links between notes.
# Either "wiki", "markdown" or a custom template. Default is "markdown".
link-format = "wiki"
# Indicates whether a link's path will be percent-encoded.
link-encode-path = false
# Indicates whether a link's path file extension will be removed.
link-drop-extension = true

Incidentally, internal link completion is triggered only with [[ now, not a single [. You need to setup your preferred link syntax in the zk config.

@megalithic
Copy link

@mickael-menu .. one thing i've noticed recently (not sure if it's a zk lsp implementation or related to neovim's lspconfig and native LS client implementation), is that when trying to "select" a completion item that has spaces it will break the selection and you'll end up with just First\ instead of First\ Word or First Word. Is this something that a.) works on coc.nvim; and b.) works for you with nvim-lspconfig and nvim's native lsp client implementation, that you've seen?

Thanks!

@megalithic
Copy link

Oh man, update; turns out bullets.vim was the culprit :( that's a bummer.

Soooo.. please ignore my above question. <CR> selection on an autocomplete item that has spaces, works just fine. :)

@mickael-menu
Copy link
Member Author

Ha that's interesting because I actually had the same issue with coc.nvim, which was considering spaces as the end of the completion item for some reason. I had to set this in its settings to make it work:

{
  // Important, otherwise link completion containing spaces and other special characters won't work.
  "suggest.invalidInsertCharacters": []
}

@mickael-menu
Copy link
Member Author

I created a Visual Studio Code client extension for zk, available at https://github.com/mickael-menu/zk-vscode

@ghost
Copy link

ghost commented Apr 29, 2021

In case anyone else gets stuck when using Pandoc in Vim, just add get_language_id = function() return 'markdown' end to your config (for Neovim's built-in LSP). This forces the filetype that get passed to the LSP as markdown (otherwise it will send markdown.pandoc) so that the LSP can work.

E.g:

configs.zkls = {
  default_config = {
     cmd = {'zk', 'lsp', '--log', '/tmp/zk-lsp.log'},
     filetypes = {'markdown', 'markdown.pandoc'},
     get_language_id = function() return 'markdown' end,  -- force language ID to markdown so that LS will work
     root_dir = function() return vim.loop.cwd() end,
     settings = {},
  };
}

@ghost
Copy link

ghost commented Apr 29, 2021

On a related note, does filtering the tag/note auto-completion work? If I enter the trigger character (# or [[), I get a popup with all my tags/notes, but as soon as I start typing to "filter" through the list, I just get normal (non-LSP) auto-completion. I'm using the built-in Neovim LSP client and nvim-compe, so not sure if it's an issue with one of them?

@mickael-menu
Copy link
Member Author

@ZachariasLenz Thanks, I'll add this to the documentation!

On a related note, does filtering the tag/note auto-completion work?

It depends what you expect but with coc.nvim I can filter though the auto-completion list with fuzzy-matching. It matches the notes' title and their path (which is hidden from the completion items).

as soon as I start typing to "filter" through the list, I just get normal (non-LSP) auto-completion

I'm not sure what you mean by non-LSP auto-completion? Would you be able to record a screencast?

@ghost
Copy link

ghost commented Apr 29, 2021

@mickael-menu see the screencast for an example of what I mean:

scrrencast_lsp_completion.mp4

@mickael-menu
Copy link
Member Author

mickael-menu commented Apr 29, 2021

@ZachariasLenz Thanks, this definitely doesn't look right. I'm not sure what could be the problem as I'm not using NeoVim's built-in LSP yet. Do you have any idea @megalithic?

(Nice color scheme ;))

Here's how it looks like with coc.nvim
zk-autocomplete2.mov

@mickael-menu
Copy link
Member Author

mickael-menu commented May 11, 2021

I just merged some pretty exciting new LSP features.

I added two code actions to create a new note using the text selection as title. Either in the current directory, or the root/top directory. It will convert the selection to a link to the newly created note.

This feature builds on new custom LSP commands: zk.index and zk.new. Using your editor's API, you can directly call these commands which can be used to create keybindings around various note creation use cases. Here's a screencast:

lsp-actions.mov

You will need a bit of configuration to add user commands and shortcuts exposing zk.index and zk.new.

This is my coc.nvim config:

~/.config/nvim/init.vim
command! -nargs=0 ZkIndex :call CocAction("runCommand", "zk.index", expand("%:p"))
command! -nargs=? ZkNew :exec "edit ".CocAction("runCommand", "zk.new", expand("%:p"), <args>).path

nnoremap <leader>zi :ZkIndex<CR>
nnoremap <leader>zn :ZkNew {"title": input("Title: ")}<CR>
nnoremap <leader>zl :ZkNew {"dir": "log"}<CR>

(Note the cool keybinding which prompts for a title: nnoremap <leader>zn :ZkNew {"title": input("Title: ")}<CR>)

And here's with built-in Neovim LSP client:

~/.config/nvim/init.vim
command! -nargs=0 ZkIndex :lua require'lspconfig'.zk.index()
command! -nargs=? ZkNew :lua require'lspconfig'.zk.new(<args>)

lua << EOF

local lspconfig = require'lspconfig'
local configs = require'lspconfig/configs'

configs.zk = {
  default_config = {
    cmd = {'zk', 'lsp'};
    filetypes = {'markdown'};
    root_dir = lspconfig.util.root_pattern('.zk');
    settings = {};
  };
}

configs.zk.index = function()
  vim.lsp.buf.execute_command({
    command = "zk.index",
    arguments = {vim.api.nvim_buf_get_name(0)},
  })
end

configs.zk.new = function(...)
  vim.lsp.buf_request(0, 'workspace/executeCommand',
    {
        command = "zk.new",
        arguments = {
            vim.api.nvim_buf_get_name(0),
            ...
        },
    },
    function(_, _, result)
      if not (result and result.path) then return end
      vim.cmd("edit " .. result.path)
    end
  )
end

lspconfig.zk.setup({
  on_attach = function(client, bufnr)
    -- Key mappings
    local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end
    local opts = { noremap=true, silent=true }
    buf_set_keymap("n", "<CR>", "<cmd>lua vim.lsp.buf.definition()<CR>", opts)
    buf_set_keymap("n", "K", "<cmd>lua vim.lsp.buf.hover()<CR>", opts)
    buf_set_keymap("n", "<leader>zi", ":ZkIndex<CR>", opts)
    buf_set_keymap("v", "<leader>zn", ":'<,'>lua vim.lsp.buf.range_code_action()<CR>", opts)
    buf_set_keymap("n", "<leader>zn", ":ZkNew {title = vim.fn.input('Title: ')}<CR>", opts)
    buf_set_keymap("n", "<leader>zl", ":ZkNew {dir = 'log'}<CR>", opts)
  end
})
EOF

@mickael-menu mickael-menu pinned this issue May 11, 2021
@mickael-menu
Copy link
Member Author

zk now reports diagnostics for dead links and wiki-link titles.

The wiki-link titles are particularly useful with Neovim's 0.5 built-in LSP client, as they are displayed as virtual text. However I disabled this diagnostic by default as it's not so useful for other editors. You need to enable it in your zk config:

[lsp.diagnostics]
# Report titles of wiki-links as hints.
wiki-title = "hint"

Here's a screenshot after customizing the colors:

~/.config/nvim/init.vim
highlight LspDiagnosticsDefaultError ctermfg=red guifg=red
highlight LspDiagnosticsUnderlineError ctermfg=red guifg=red
highlight LspDiagnosticsDefaultHint ctermfg=yellow guifg=yellow
highlight LspDiagnosticsUnderlineHint cterm=none gui=none

Screenshot 2021-05-16 at 21 44 05

@mickael-menu
Copy link
Member Author

Considering that there are often issues with the additionalTextEdits, I merged in a PR that disable them by default. You can still opt-in manually for clients like VS Code1 requirint it for link completion to work properly.

Let me know if it works for you @stefanvanburen.

Footnotes

  1. For VS Code I detected the client name in the LSP server to enable additionalTextEdits automatically, but other clients might need it too.

@manolomartinez
Copy link

manolomartinez commented Feb 9, 2022

As regards pandoc integration, I don't know if this solves the same problem that @ZachariasLenz was having, but my markdown files were already subsumed into a pandoc filetype (which is managed by a different plugin). So I simply changed the relevant line in coc-settings.json to

"filetypes": ["markdown", "pandoc"]

And now the LSP works perfectly with my pandoc files.

@daephx

This comment was marked as duplicate.

@mickael-menu
Copy link
Member Author

From now on let's open a dedicated GitHub issue for LSP bug reports or LSP feature requests. This issue will be used to follow the evolution of the LSP implementation.

@haus20xx

This comment was marked as off-topic.

@chris-sanders

This comment was marked as off-topic.

@kkga
Copy link

kkga commented Aug 29, 2022

Anyone managed to make the LSP work with Kakoune (kak-lsp)?

@zalegrala
Copy link
Contributor

Wow, this project is awesome, nice work!

@cipharius
Copy link

Anyone managed to make the LSP work with Kakoune (kak-lsp)?

Yes, I managed to get it working with kak-lsp. Try adding this config to your kak-lsp.toml(by default ~/.config/kak-lsp/kak-lsp.toml):

[language.markdown]
filetypes = ["markdown"]
roots = [".zk"]
command = "zk"
args = ["lsp"]

Note that it's very important to call the config entry as language.markdown, using language.zk-markdown will break it. I had to find it out the hard way, zk checks file languageId, which kak-lsp takes from the configuration entries.

@zalegrala
Copy link
Contributor

The title issue mentions that Open a website from an inline external Markdown link has been implemented, but I didn't see docs on how to use it. Could someone point me the way? I assume this means that if I have [link](http://somewhere) that I can tell vim to open that link in my browser, or presumably with xdg-open.

@mickael-menu
Copy link
Member Author

The title issue mentions that Open a website from an inline external Markdown link has been implemented, but I didn't see docs on how to use it.

It was a mistake, but I implemented it quickly in #261. However, this uses LSP documentLink which is the only way AFAIK to report external URLs with LSP. Unfortunately I don't think NeoVim uses documentLink at the moment, but it work with VS Code for example.

Personally with NeoVim I've been using gx with a cursor on an URL.

@zalegrala
Copy link
Contributor

Ah, I had nvim-tree disabling the netrw functionality, but gx works for me on this. Thanks for the pointer.

@zalegrala
Copy link
Contributor

I seem to be experiencing the issue mentioned above where goto definition doesn't work outside of the root directory. I also notice that when running :LspInfo I see that the zk "root directory" is set as "Running in single file mode". Would this have something to do with that? What's the right way to set the root path to the notebook? ZK_NOTEBOOK_DIR is already present in the directory.

@mickael-menu
Copy link
Member Author

@zalegrala I think that "Running in single file mode" is expected, I have the same thing. I can't reproduce the issue though, opening a note from a sub directory or a directory outside the notebook.

I have this in my ~/.zshrc:

export ZK_NOTEBOOK_DIR=~/Notes

@huynle
Copy link

huynle commented Oct 4, 2022

just wanna stop by and say thanks for the continual work! im a big fan and with the current feature set, i'm able to clean all my markdown notes from 7+ years ago (written in markdown, hoping for zk to come around). very much appreciated, and hoping to contribute soon as i learn more Go

@zalegrala
Copy link
Contributor

@mickael-menu Yep, thanks for the note. I've got ZK_NOTEBOOK_DIR set in my environment also, but using gd in vim for go-to-definition doesn't seem to work reliably. I had a marksman plugin that would would, so I was a little confused about which plugin was doing what, but disabling that one and relying just on the zk plugin seems to disable the functionality.

@mickael-menu
Copy link
Member Author

@zalegrala Ha yes, I'm not sure gd is actually using the LSP's go to definition.

If you use zk-nvim (but this should partly work with just the zk LSP server) you need to declare a list of mappings.

The one you want is:

-- Open the link under the caret.
map("n", "<CR>", "<Cmd>lua vim.lsp.buf.definition()<CR>", opts)

@zalegrala
Copy link
Contributor

Ah yes, gd does seem to be defined locally. I've got this currently, so I think I'm in-line with the approach @mickael-menu.

local keymap = vim.api.nvim_buf_set_keymap
keymap(bufnr, "n", "gd", "<cmd>lua vim.lsp.buf.definition()<CR>", opts)

@WhiskeyJack96
Copy link

Any thoughts on adding TODO support for autocomplete and code actions for switching TODO to DONE? I'd be happy to put up a pr giving it a shot if you'd be interested!

@mickael-menu
Copy link
Member Author

mickael-menu commented Dec 11, 2022

@WhiskeyJack96 IMO this is out of scope for zk. It would be more suited for a task manager plugin.

This discussion could be relevant: zk-org/zk-nvim#88

@WhiskeyJack96
Copy link

@mickael-menu yeah I can see that, is there any plan for or existing/method to extend zk? my workflow currently revolves around using logseq for and then editting my journal notes with Helix in the terminal during work (but helix doesnt currently support plugins or multiple lsps per filetype).

@mickael-menu
Copy link
Member Author

@WhiskeyJack96 I don't think zk will have a plugin system for arbitrary code actions. Sounds like an issue with Helix, that it doesn't support plugins.

However, if you're willing to put in the work, you could just fork zk. Another alternative would be to create your own "composite" LSP server with custom actions and delegating the rest to the zk LSP server. zk is using https://github.com/tliron/glsp under the hood.

@pr4th4m
Copy link

pr4th4m commented Jan 5, 2024

Any plans to support textDocument/documentSymbol ?

@dotrosedotnet
Copy link

dotrosedotnet commented Apr 15, 2024

I'm getting

Error executing vim.schedule lua callback: ...w/Cellar/neovim/0.9.5/share/nvim/runtime/lua/vim/lsp.l
ua:1308: RPC[Error] code_name = InvalidParams, message = "Invalid params"
stack traceback:
        [C]: in function 'assert'
        ...w/Cellar/neovim/0.9.5/share/nvim/runtime/lua/vim/lsp.lua:1308: in function ''
        vim/_editor.lua: in function <vim/_editor.lua:0>

nvim --version gives me:

NVIM v0.9.5
Build type: Release
LuaJIT 2.1.1710088188

   system vimrc file: "$VIM/sysinit.vim"
  fall-back for $VIM: "/opt/homebrew/Cellar/neovim/0.9.5/share/nvim"

I installed via homebrew on an m3. Any ideas?

EDIT:

I think this was due to me using the markdown_oxide lsp server, which not only seemed buggy, but would try to run a bunch of html for a russian website in nvim, so...

I'm using marksman now as my lsp server, and everything seems alright.

@WhyNotHugo
Copy link
Contributor

Highlight tags using semantic tokens (client support is still lacking)

Neovim has client support for this and it would be pretty useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests