I have a language server implementation for my programming language. Since it has been developed much later than its compiler, no stuff like query-based redesign was viable to implement. So, I just adopted the compiler codebase for language server needs in a tricky way: on each document change I just redo parsing, but not other compilation steps and use parsing results to perform fetches from name tables built previously - for some older document state. This state is costly to update and it's done only once in a while and only if input document is syntactically-correct. This approach works surprisingly well (it's fast and completion is mostly correct) and doesn't require throwing the whole compiler code away.
Even more, I can't imagine how people write language servers with full reprocessing on each document change (even with query-based approach). How do they deal with broken syntax? isn't it generally impossible to recovery syntactic errors, so that the end result is as good as expected?
You need a fault tolerant parser, tree sitter is a good example on how it works in practice, your compiler should have the ability to run a few steps (parsing, symbol resolution, type checking, etc) with an incomplete/invalid AST.
> isn't it generally impossible to recovery syntactic errors
AFAIK Zig was designed in such way that, in most cases, you can parse each line independently, like their strings by default are single line, you need a special syntax for multiline strings that still are per line basis.
I doubt it's possible to perform proper syntax recovery without messing the result. My approach with usage of some previous valid program state is more reliable and easier to implement.
> AFAIK Zig was designed in such way that, in most cases, you can parse each line independently
It can't be true. Basic stuff like opening/closing braces introduces some global syntax state, so that line-by-line parsing isn't possible. Do you mean something like tokenization instead? It is in many cases possible. But for true LSP it's almost useless.
I know Haskell’s LSP years ago just stopped on broken syntax, so the only diagnostic would be the syntax error and you would lose code actions. That was annoying, especially because the LSP was slow.
JetBrains (not an LSP) works up to the syntax error, then recovers and works after. In some cases that are not hard to trigger (e.g. after a closing delimiter), the recovery is wrong and everything after is messed up. But in practice that’s rarely an issue; if I want those later diagnostics and code actions, I fix the delimiters, and the analysis updates quickly.
Even more, I can't imagine how people write language servers with full reprocessing on each document change (even with query-based approach). How do they deal with broken syntax? isn't it generally impossible to recovery syntactic errors, so that the end result is as good as expected?