The language server
pcpp --lsp is a Language Server Protocol server: the editor runs it, and so can any LSP client. Every answer comes from the compiler's own analysed tree.
The server
pcpp --lsp speaks LSP over stdin / stdout (Content-Length framing) and names itself PaskalCPP LSP; the editor runs one for its whole lifetime.
- Positions use the first encoding the client lists that the server knows (utf-16, utf-8, utf-32); a client that lists none gets UTF-16. Buffers sync incrementally.
- Every language feature is answered from the compiler's own frontend (lexer, parser, semantic pass) run in check mode on the open buffers: nothing is built, an open buffer wins over its file on disk, and an import that is open is read from its buffer.
- A request the server does not know is answered with an error (method not found); a request whose parameters it cannot read with an invalid-params error.
Diagnostics
Each changed buffer is checked and its reports are published as diagnostics, up to 100 errors per check. The parser recovers from a mistake and checks the rest of the file, so one broken line does not hide the others. The reports are the compiler's own: see Diagnostics.
A check remembers the files on disk its result depends on (directive paths, imported units). A client that can register file watchers gets them, and a change on disk checks every open buffer again.
Reading code
| Feature | What it gives |
|---|---|
| Colouring | keywords and builtin types as semantic tokens, from the lexer's own tables |
| Hover | the declaration of the name under the cursor in a PaskalCPP code block, and its /// doc comment as Markdown; a name from another file names that file |
| Go to definition | the name in the declaration, in another file too; on an overload set, every member |
| Inlay hints | the parameter name before each argument of a PaskalCPP routine call |
| Outline | the file's imports, declarations, initialize / finalize and tests, a type's members, a routine's local routines and types |
| Workspace symbols | the declarations of every open file, narrowed by a case-insensitive substring of the name |
| Call hierarchy | a routine's callers and the routines it calls, each with the places of the calls |
- A doc comment's tags are
@param,@returns,@raises,@see,@deprecated,@sinceand@example. - Inlay hints are left out for a C/C++ callee, an intrinsic, the variadic tail, and an argument already spelled as its parameter; for an overload set they show only where every member names that parameter alike.
- The outline leaves out a routine's local variables; selecting an entry selects its name.
- Call hierarchy works on a routine's name at its declaration or at a call (a forward stands for its full routine), across the modules the open files reach. A call outside every routine (a module's body, initialize / finalize, a test) comes from the module itself.
Writing code
- Completion, also on
.and->: the names in scope from the analysed tree (innermost first), then intrinsics, the names the module's imports export (bare; a name two units export is one entry per unit, insertingunit.Name), builtin types and keywords; after./->the members of the left side's type, then what its embeds promote. PaskalCPP names only. - Format Document and Format Selection rewrite only the space between tokens and keep every comment. A file with a syntax error is not formatted, and the client is shown why. The editor's format settings are on Settings.