zk-org / zk

A plain text note-taking assistant
https://zk-org.github.io/zk/
GNU General Public License v3.0
1.64k stars 119 forks source link

Providing a Language Server (LSP) #22

Open mickael-menu opened 3 years ago

mickael-menu commented 3 years ago

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:

Feel free to share more ideas!

cyruseuros commented 3 years ago

@mickael-menu fyi https://github.com/neovim/nvim-lspconfig/pull/1249

mickael-menu commented 3 years ago

@mickael-menu fyi neovim/nvim-lspconfig#1249

Sweet! From a previous conversation I had with mjlbach, I wasn't sure a zk config would be approved for nvim-lspconfig. Thanks for submitting it!

ahmedelgabri commented 2 years ago

For zk.new is there is a way to set the notes directory? Here is my use case, I have a single place where I store my notes inside it. And I want to be able to create a note from anywhere inside of it, currently, this doesn't work because the LSP always tries to look for the closest .zk folder & it zk.new will not work if that's not the case.

So I have to navigate to my notes directory to be able to use zk.new. I'm following the same exact setup mentioned here https://github.com/mickael-menu/zk/issues/22#issuecomment-839125677 (maybe I'm also missing how to make this work as I want, so any pointers are welcome)

mickael-menu commented 2 years ago

@ahmedelgabri The problem is that Neovim (by design) starts zk's LSP server only when the current directory and file is part of a notebook, because of this in the config:

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

So the zk.new command won't be available if you're not in a notebook. Maybe you could automate moving to your notebook before calling zk.new.

ahmedelgabri commented 2 years ago

@mickael-menu I changed root_dir to be vim.loop.cwd so that's always the current directory of the current file, which means zk.new will always be available inside markdown files. But still, it doesn't work & the logs show this error

2021/10/10 18:01:19.578 DEBUG [zk.server] zk: warning: open failed: stat /Users/ahmed/.dotfiles/README.md/.zk: not a directory
2021/10/10 18:01:27.062 DEBUG [zk.rpc] jsonrpc2: --> request #2: workspace/executeCommand: {"arguments":["/Users/ahmed/.dotfiles/README.md",{"dir":"","title":"fooooooooooooooooo"}],"command":"zk.new"}
2021/10/10 18:01:27.063 DEBUG [zk.rpc] jsonrpc2: <-- error #2: workspace/executeCommand: {"code":-32600,"message":"open failed: stat /Users/ahmed/.dotfiles/README.md/.zk: not a directory","data":null}
{
  cmd = { 'zk', 'lsp', '--log', '/tmp/zk-lsp.log' },
  root_dir = function(fname)
    return vim.loop.cwd()
  end,
}
mickael-menu commented 2 years ago

@ahmedelgabri I pushed a fix on main, hope it will help you out.

ahmedelgabri commented 2 years ago

@mickael-menu I think you forgot to push that commit :), nothing is on main after 0.7.0 release

mickael-menu commented 2 years ago

Damn you're right 😄

pwntester commented 2 years ago

is there anyway for the autocompletion feature to insert just the simple note filename (without the extension nor the path)?

Thanks!

pwntester commented 2 years ago

is there anyway for the autocompletion feature to insert just the simple note filename (without the extension nor the path)?

Thanks!

Scratch that, the following worked like a charm 🪄

[format.markdown]
link-format = "[[{{filename}}]]"
VolkovIlia commented 2 years ago

Got some problems with autocompletion. nvim: 5.1 completion: COQ.nvim

Completion works but there is few bugs:

  1. Completion triggers by [[ but the resulting output is [[Title](ref) expected [Title](ref)
  2. After deleting reference after [[ completion does not trigger till restart nvim.
  3. After any deletion completion breaks.

I saw you using nvim-compe but nvim-compe deprecated, nvim-complection (I used) is archived. Is this plugin hardcoded for proper behavior in compe? If no, can you give some suggestions for making proper bug report and fixing this behavior in other plugins (nvim-cmp, coq_nvim)

ahmedelgabri commented 2 years ago

I use nvim-cmp & I never had any issues with completion myself (also previously with nvim-compe). Just a guess here, do you have any brackets or snippets engine plugins or something like this that might be conflicting?

VolkovIlia commented 2 years ago

I use nvim-cmp & I never had any issues with completion myself (also previously with nvim-compe). Just a guess here, do you have any brackets or snippets engine plugins or something like this that might be conflicting?

I've deleted snippets engine but issue still here(. I will change autocomplete engine to nvim-cmp but it's sad because coq nvim is awesome.

cyruseuros commented 2 years ago

@mickael-menu not sure why but running the 0.7 release I can't find references to the current note when the cursor isn't hovering over another reference. regular references work just fine. This is with neovim's native lsp - is the issue reproducible with other clients?

mickael-menu commented 2 years ago

@wurosh

not sure why but running the 0.7 release I can't find references to the current note when the cursor isn't hovering over another reference

I'm not sure why, it still works for me with 0.7 and Neovim native LSP client. I'm using this binding:

    buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)

Maybe you have something conflicting with the binding?


@VolkovIlia

Is this plugin hardcoded for proper behavior in compe? If no, can you give some suggestions for making proper bug report and fixing this behavior in other plugins (nvim-cmp, coq_nvim)

Completion works but there is few bugs:

  1. Completion triggers by [[ but the resulting output is [[Title](ref) expected [Title](ref)

It should cater to any LSP client, although there are some workarounds specific to make things work with VSCode. Maybe coq.nvim is not implementing the whole spec? In particular, it needs support for AdditionalTextEdits.

  1. After deleting reference after [[ completion does not trigger till restart nvim.
  2. After any deletion completion breaks.

These should be bugs in coq.nvim or Neovim. The LSP server doesn't have any direct control over the UX.

Because auto-completion with [[ required some hacky workarounds, I'm considering adding an option to auto-complete with only [ and avoid doing any substitution/replacement. Maybe that would help.

VolkovIlia commented 2 years ago

@wurosh

not sure why but running the 0.7 release I can't find references to the current note when the cursor isn't hovering over another reference

I'm not sure why, it still works for me with 0.7 and Neovim native LSP client. I'm using this binding:

    buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)

Maybe you have something conflicting with the binding?

@VolkovIlia

Is this plugin hardcoded for proper behavior in compe? If no, can you give some suggestions for making proper bug report and fixing this behavior in other plugins (nvim-cmp, coq_nvim) Completion works but there is few bugs:

  1. Completion triggers by [[ but the resulting output is [[Title](ref) expected [Title](ref)

It should cater to any LSP client, although there are some workarounds specific to make things work with VSCode. Maybe coq.nvim is not implementing the whole spec? In particular, it needs support for AdditionalTextEdits.

  1. After deleting reference after [[ completion does not trigger till restart nvim.
  2. After any deletion completion breaks.

These should be bugs in coq.nvim or Neovim. The LSP server doesn't have any direct control over the UX.

Because auto-completion with [[ required some hacky workarounds, I'm considering adding an option to auto-complete with only [ and avoid doing any substitution/replacement. Maybe that would help.

Now I am using cmp-nvim and it working fine with [[ <CR>, but if I use [[ <key L> I got the result with [[title](ref). Mb [ is better trigger for compl plugins...

cyruseuros commented 2 years ago

@mickael-menu even when running the command directly (:lua vim.lsp.buf.references()) I get the following logs:

[ERROR][2021-11-09 16:40:30] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-09 16:51:15] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-09 16:51:23] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:26:12] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:29:01] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:29:05] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:29:20] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:29:24] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:29:36] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:32:45] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:32:52] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:33:23] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:33:49] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:34:12] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:34:33] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:35:53] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-10 23:36:12] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-11 08:59:13] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-11 08:59:22] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-11 09:13:58] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"
[ERROR][2021-11-11 11:13:24] .../vim/lsp/rpc.lua:401    "rpc"   "zk"    "stderr"    "\r\r"

No such problem when hovering over a reference

tmerse commented 2 years ago

@wurosh

I see that back-links through find references are implemented! Nice! Should they be checked off in the issue tracker? I was under the impression they weren't there which made me hesitate about adopting this otherwise awesome tool. One more thing - would it be possible to find references for the current zettel by default (if a link isn't under cursor)? Otherwise I need to find one back-link manually first to find the others.

That's actually why I didn't check this item yet, I had more in mind getting the backlinks of the current note. I'll take a look to see if this can be done when the caret is not over a link, I think there was a blocker last time I checked.

@wurosh + @nogweii

Also now that the LSP suppports creating/deleting/renaming files, can zk also support renaming/moving zettels to a different directory and all references to them?

Yes I would like to have this feature eventually. It won't be quick to implement though.

@VolkovIlia

Is it any opportunity to make the reference style exactly like Obsidian does. Out of the box [ref](file) works fine, but referring to sub-titles does not make any scenes. Also, did not find how to trigger completion for sub-titles: [reference#subtitle](file) ....

How is the link format exactly in Obsidian? Links with anchors (#subtitle) should work in zk.

Completion of subtitles is not implemented. I'll add it to the list but have no plan on implementing it myself soon.

Any Progress on this? Especially subtitle completion and Obsidian/Vimwiki link compatibility?

I put together a small video, demonstrating the differences of zk lsp gotoDefinition vs :VimWikiFollowLink

https://user-images.githubusercontent.com/2323181/145356905-797ec288-3517-4577-84d3-41d36a5295c1.mov

Jumping to exact subheadings of other files (or the current file, e.g. TOC listings) would be much appreciated features!

mickael-menu commented 2 years ago

@tmerse I keep my notes short and atomic, so I don't have much use for jumping to subheadings myself. But I'm open for contributions if you're up for it.

tmerse commented 2 years ago

@mickael-menu : Fair point on keeping the the files small. Unfortunately I'am not familiar with go, but I may look into the source when I find some free time, if not just for the learning experience :)

Thanks for providing this, I think lsp-backed markdown is a great idea!

mickael-menu commented 2 years ago

I added two new commands in https://github.com/mickael-menu/zk/pull/114:

smithbm2316 commented 2 years ago

I added two new commands in #114:

* [`zk.list`](https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zklist) to search for notes.

* [`zk.tag.list`](https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zktaglist) to retrieve the list of tags.

This is so exciting!! Thanks for the hard work @mickael-menu 😄

kabouzeid commented 2 years ago

Why do the LSP commands need "A path to any file or directory in the notebook, to locate it."? I think the language server should at least default to the rootUri.

cljoly commented 2 years ago

Why do the LSP commands need "A path to any file or directory in the notebook, to locate it."? I think the language server should at least default to the rootUri.

I think that would be helpful to run fuzzy finders like Telescope without having a note from the zettelkasten opened.

kabouzeid commented 2 years ago

Hmm I agree that this might be useful. After further looking at the source code, I guess it makes sense, since this server is designed to handle multiple notebooks, and does not use the rootUri at all.

This doesn't work so well with nvim-lspconfig, since it'll start one server instance per root directory anyways.

mickael-menu commented 2 years ago

Why do the LSP commands need "A path to any file or directory in the notebook, to locate it."? I think the language server should at least default to the rootUri.

Some commands have a different effect depending where you are located in the notebook (the links produced or the directory where a note will be created), like with the regular zk CLI commands.

However defaulting on the notebook root if not explicitly overridden sounds reasonable. I'm not sure if this would be best handled at the level of the LSP server or the zk-nvim Lua wrapper though?

This doesn't work so well with nvim-lspconfig, since it'll start one server instance per root directory anyways.

Yes that's a pickle... I circumvented this with a binding that goes to my notebook "index" file, but that would be nice to run the note picker from wherever.

kabouzeid commented 2 years ago

I've made some changes, this should now work in neovim with a single instance of the zk server. Please refer to the updated readme.

https://github.com/kabouzeid/zk-nvim/tree/telescope

tissieres commented 2 years ago

Hello! Is it planned to add auto-complete on tags in the YAML frontmatter? It works well when added with # but I use them only in the frontmatter.

mickael-menu commented 2 years ago

@tissieres I answered there https://github.com/mickael-menu/zk/issues/144

stefanvanburen commented 2 years ago

thought I'd add (since I hadn't seen this discussed above) that with default neovim lsp omnifunc used for completion, the additionalTextEdits aren't currently applied, so starting with [[ to trigger completion and then selecting an entry, say "Test", gives you [[[[Test]] 😞.

Currently trying out nvim-cmp, which as mentioned above fixes this issue, but hopefully that helps anyone else out with this problem (until it's implemented) in the future!

kabouzeid commented 2 years ago

@stefanvanburen there is also https://github.com/mfussenegger/nvim-lsp-compl if you find cmp too bloated, and want a more minimal completion plugin which supports additionalTextEdits. Personally I like cmp though.

stefanvanburen commented 2 years ago

@kabouzeid thanks! hadn't seen that one - was under the impression that cmp was the main neovim autocompletion plugin nowadays, will take a look!

mickael-menu commented 2 years ago

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 Code[^1] requirint it for link completion to work properly.

Let me know if it works for you @stefanvanburen.

[^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 commented 2 years ago

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 commented 2 years ago

I don't know if this was mentioned but I didn't see any other relevant issues. It appears the LSP attempts to check wikilink syntax within code blocks. Which is probably a rare issue but still doesn't strike me as appropriate behavior. I only noticed this due to the multi-line string syntax Lua uses.

image

mickael-menu commented 2 years ago

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 commented 2 years ago

I've been having issues getting autocomplete to work with Neovim 0.7 and coc.nvim. It works exactly as expected if the config has link-format: 'wiki' but in all other cases, the autocomplete never even shows up. I checked the logs from zk lsp and in the broken cases, it does seem like messaging is working as expected(though I am a total novice to the inner workings of the lsp, I mostly mean there seems to be messages and responses as expected, but it could be failing somewhere in the communication chain that I'm not looking), there is a message with the autocomplete options and can even verify that the newText value is of the correct link-format however it does not seem to propagate to the editor UI. Is this expected for coc.nvim? I'm not using the zk plugin that interfaces with the native lsp and have gotten this issue even when I take virtually everything out of my vim.init.

Any ideas?

chris-sanders commented 2 years ago

I'm using markdown link format, with nvim, and coc-nvim for auto-completion. I just upgraded form 0.5.0 to 0.7.0 and 0.7.2 and all 3 the autocomplete is working for me. However, I also have a Mac running 0.7.0 with the same setup and it's not working, I didn't notice until I tested today.

That's to say the setup should work, you should probably open a dedicated GitHub issue with your version details for further troubleshooting.

kkga commented 2 years ago

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

zalegrala commented 1 year ago

Wow, this project is awesome, nice work!

cipharius commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 https://github.com/mickael-menu/zk/pull/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 commented 1 year ago

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

zalegrala commented 1 year ago

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 commented 1 year ago

@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 commented 1 year ago

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 commented 1 year ago

@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 commented 1 year ago

@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 commented 1 year ago

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)