mirror of
https://github.com/mickael-menu/zk
synced 2024-11-07 15:20:21 +00:00
Publish LSP diagnostics for dead links and wiki-link titles (#42)
This commit is contained in:
parent
e8cb1d8046
commit
aa68199df8
@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
|
||||
* [Editor integration through LSP](https://github.com/mickael-menu/zk/issues/22):
|
||||
* New code actions to create a note using the current selection as title.
|
||||
* Custom commands to [run `new` and `index` from your editor](docs/editors-integration.md#custom-commands).
|
||||
* Diagnostics to [report dead links or wiki-link titles](docs/config-lsp.md).
|
||||
* Customize the format of `fzf`'s lines [with your own template](docs/tool-fzf.md).
|
||||
```toml
|
||||
[tool]
|
||||
|
27
docs/config-lsp.md
Normal file
27
docs/config-lsp.md
Normal file
@ -0,0 +1,27 @@
|
||||
# LSP configuration
|
||||
|
||||
The `[lsp]` [configuration file](config.md) section provides settings to fine-tune the [LSP editors integration](editors-integration.md).
|
||||
|
||||
## Diagnostics
|
||||
|
||||
Use the `[lsp.diagnostics]` sub-section to configure how LSP diagnostics are reported to your editors. Each diagnostic setting can be:
|
||||
|
||||
* An empty string or `none` to ignore this diagnostic.
|
||||
* `hint`, `info`, `warning` or `error` to enable and set the severity of the diagnostic.
|
||||
|
||||
| Setting | Default | Description |
|
||||
|--------------|-----------|---------------------------------------------------------------------------|
|
||||
| `wiki-title` | `"none"` | Report titles of wiki-links, which is useful if you use IDs for filenames |
|
||||
| `dead-link` | `"error"` | Warn for dead links between notes |
|
||||
|
||||
## Complete example
|
||||
|
||||
```toml
|
||||
[lsp]
|
||||
|
||||
[lsp.diagnostics]
|
||||
# Report titles of wiki-links as hints.
|
||||
wiki-title = "hint"
|
||||
# Warn for dead links between notes.
|
||||
dead-link = "error"
|
||||
```
|
@ -5,11 +5,12 @@ Each [notebook](notebook.md) contains a configuration file used to customize you
|
||||
* `[note]` sets the [note creation rules](config-note.md)
|
||||
* `[extra]` contains free [user variables](config-extra.md) which can be expanded in templates
|
||||
* `[group]` defines [note groups](config-group.md) with custom rules
|
||||
* `[format]` configures the [note format settings](note-format.md), such as Markdown options.
|
||||
* `[format]` configures the [note format settings](note-format.md), such as Markdown options
|
||||
* `[tool]` customizes interaction with external programs such as:
|
||||
* [your default editor](tool-editor.md)
|
||||
* [your default pager](tool-pager.md)
|
||||
* [`fzf`](tool-fzf.md)
|
||||
* `[lsp]` setups the [Language Server Protocol settings](config-lsp.md) for [editors integration](editors-integration.md)
|
||||
* `[filter]` declares your [named filters](config-filter.md)
|
||||
* `[alias]` holds your [command aliases](config-alias.md)
|
||||
|
||||
@ -104,5 +105,13 @@ recent = "zk edit --sort created- --created-after 'last two weeks' --interactive
|
||||
|
||||
# Show a random note.
|
||||
lucky = "zk list --quiet --format full --sort random --limit 1"
|
||||
```
|
||||
|
||||
# LSP (EDITOR INTEGRATION)
|
||||
[lsp]
|
||||
|
||||
[lsp.diagnostics]
|
||||
# Report titles of wiki-links as hints.
|
||||
wiki-title = "hint"
|
||||
# Warn for dead links between notes.
|
||||
dead-link = "error"
|
||||
```
|
@ -14,7 +14,10 @@ There are several extensions available to integrate `zk` in your favorite editor
|
||||
* Preview the content of a note when hovering a link.
|
||||
* Navigate in your notes by following internal links.
|
||||
* Create a new note using the current selection as title.
|
||||
* Diagnostics for dead links and wiki-links titles.
|
||||
* [And more to come...](https://github.com/mickael-menu/zk/issues/22)
|
||||
|
||||
You can configure some of these features in your notebook's [configuration file](config-lsp.md).
|
||||
|
||||
### Editor LSP configurations
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/mickael-menu/zk/internal/core"
|
||||
"github.com/mickael-menu/zk/internal/util"
|
||||
"github.com/mickael-menu/zk/internal/util/errors"
|
||||
"github.com/tliron/glsp"
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
@ -26,22 +27,24 @@ func newDocumentStore(fs core.FileStorage, logger util.Logger) *documentStore {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *documentStore) DidOpen(params protocol.DidOpenTextDocumentParams) error {
|
||||
func (s *documentStore) DidOpen(params protocol.DidOpenTextDocumentParams, notify glsp.NotifyFunc) (*document, error) {
|
||||
langID := params.TextDocument.LanguageID
|
||||
if langID != "markdown" && langID != "vimwiki" {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
path, err := s.normalizePath(params.TextDocument.URI)
|
||||
uri := params.TextDocument.URI
|
||||
path, err := s.normalizePath(uri)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
s.documents[path] = &document{
|
||||
doc := &document{
|
||||
URI: uri,
|
||||
Path: path,
|
||||
Content: params.TextDocument.Text,
|
||||
}
|
||||
|
||||
return nil
|
||||
s.documents[path] = doc
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
func (s *documentStore) Close(uri protocol.DocumentUri) {
|
||||
@ -68,9 +71,11 @@ func (s *documentStore) normalizePath(pathOrUri string) (string, error) {
|
||||
|
||||
// document represents an opened file.
|
||||
type document struct {
|
||||
Path string
|
||||
Content string
|
||||
lines []string
|
||||
URI protocol.DocumentUri
|
||||
Path string
|
||||
NeedsRefreshDiagnostics bool
|
||||
Content string
|
||||
lines []string
|
||||
}
|
||||
|
||||
// ApplyChanges updates the content of the document from LSP textDocument/didChange events.
|
||||
@ -190,7 +195,7 @@ func (d *document) DocumentLinks() ([]documentLink, error) {
|
||||
lines := d.GetLines()
|
||||
for lineIndex, line := range lines {
|
||||
|
||||
appendLink := func(href string, start, end int) {
|
||||
appendLink := func(href string, start, end int, hasTitle bool) {
|
||||
if href == "" {
|
||||
return
|
||||
}
|
||||
@ -207,6 +212,7 @@ func (d *document) DocumentLinks() ([]documentLink, error) {
|
||||
Character: protocol.UInteger(end),
|
||||
},
|
||||
},
|
||||
HasTitle: hasTitle,
|
||||
})
|
||||
}
|
||||
|
||||
@ -216,12 +222,13 @@ func (d *document) DocumentLinks() ([]documentLink, error) {
|
||||
if decodedHref, err := url.PathUnescape(href); err == nil {
|
||||
href = decodedHref
|
||||
}
|
||||
appendLink(href, match[0], match[1])
|
||||
appendLink(href, match[0], match[1], true)
|
||||
}
|
||||
|
||||
for _, match := range wikiLinkRegex.FindAllStringSubmatchIndex(line, -1) {
|
||||
href := line[match[2]:match[3]]
|
||||
appendLink(href, match[0], match[1])
|
||||
hasTitle := match[4] != -1
|
||||
appendLink(href, match[0], match[1], hasTitle)
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,4 +238,7 @@ func (d *document) DocumentLinks() ([]documentLink, error) {
|
||||
type documentLink struct {
|
||||
Href string
|
||||
Range protocol.Range
|
||||
// HasTitle indicates whether this link has a title information. For
|
||||
// example [[filename]] doesn't but [[filename|title]] does.
|
||||
HasTitle bool
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mickael-menu/zk/internal/core"
|
||||
"github.com/mickael-menu/zk/internal/util"
|
||||
@ -127,7 +128,14 @@ func NewServer(opts ServerOpts) *Server {
|
||||
}
|
||||
|
||||
handler.TextDocumentDidOpen = func(context *glsp.Context, params *protocol.DidOpenTextDocumentParams) error {
|
||||
return server.documents.DidOpen(*params)
|
||||
doc, err := server.documents.DidOpen(*params, context.Notify)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if doc != nil {
|
||||
server.refreshDiagnosticsOfDocument(doc, context.Notify, false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
handler.TextDocumentDidChange = func(context *glsp.Context, params *protocol.DidChangeTextDocumentParams) error {
|
||||
@ -137,6 +145,7 @@ func NewServer(opts ServerOpts) *Server {
|
||||
}
|
||||
|
||||
doc.ApplyChanges(params.ContentChanges)
|
||||
server.refreshDiagnosticsOfDocument(doc, context.Notify, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -225,12 +234,12 @@ func NewServer(opts ServerOpts) *Server {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
target, err := server.targetForHref(link.Href, doc, notebook)
|
||||
if err != nil || target == "" || strutil.IsURL(target) {
|
||||
target, err := server.noteForHref(link.Href, doc, notebook)
|
||||
if err != nil || target == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path, err := uriToPath(target)
|
||||
path, err := uriToPath(target.URI)
|
||||
if err != nil {
|
||||
server.logger.Printf("unable to parse URI: %v", err)
|
||||
return nil, err
|
||||
@ -268,14 +277,14 @@ func NewServer(opts ServerOpts) *Server {
|
||||
|
||||
documentLinks := []protocol.DocumentLink{}
|
||||
for _, link := range links {
|
||||
target, err := server.targetForHref(link.Href, doc, notebook)
|
||||
if target == "" || err != nil {
|
||||
target, err := server.noteForHref(link.Href, doc, notebook)
|
||||
if target == nil || err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
documentLinks = append(documentLinks, protocol.DocumentLink{
|
||||
Range: link.Range,
|
||||
Target: &target,
|
||||
Target: &target.URI,
|
||||
})
|
||||
}
|
||||
|
||||
@ -298,8 +307,8 @@ func NewServer(opts ServerOpts) *Server {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
target, err := server.targetForHref(link.Href, doc, notebook)
|
||||
if link == nil || target == "" || err != nil {
|
||||
target, err := server.noteForHref(link.Href, doc, notebook)
|
||||
if link == nil || target == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -308,11 +317,11 @@ func NewServer(opts ServerOpts) *Server {
|
||||
if false && isTrue(clientCapabilities.TextDocument.Definition.LinkSupport) {
|
||||
return protocol.LocationLink{
|
||||
OriginSelectionRange: &link.Range,
|
||||
TargetURI: target,
|
||||
TargetURI: target.URI,
|
||||
}, nil
|
||||
} else {
|
||||
return protocol.Location{
|
||||
URI: target,
|
||||
URI: target.URI,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
@ -378,6 +387,11 @@ func NewServer(opts ServerOpts) *Server {
|
||||
return server
|
||||
}
|
||||
|
||||
// Run starts the Language Server in stdio mode.
|
||||
func (s *Server) Run() error {
|
||||
return errors.Wrap(s.server.RunStdio(), "lsp")
|
||||
}
|
||||
|
||||
const cmdIndex = "zk.index"
|
||||
|
||||
func (s *Server) executeCommandIndex(args []interface{}) (interface{}, error) {
|
||||
@ -516,32 +530,105 @@ func (s *Server) notebookOf(doc *document) (*core.Notebook, error) {
|
||||
return s.notebooks.Open(doc.Path)
|
||||
}
|
||||
|
||||
// targetForHref returns the LSP documentUri for the note at the given HREF.
|
||||
func (s *Server) targetForHref(href string, doc *document, notebook *core.Notebook) (string, error) {
|
||||
// noteForHref returns the LSP documentUri for the note at the given HREF.
|
||||
func (s *Server) noteForHref(href string, doc *document, notebook *core.Notebook) (*Note, error) {
|
||||
if strutil.IsURL(href) {
|
||||
return href, nil
|
||||
} else {
|
||||
path := filepath.Clean(filepath.Join(filepath.Dir(doc.Path), href))
|
||||
path, err := filepath.Rel(notebook.Path, path)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to resolve href: %s", href)
|
||||
}
|
||||
note, err := notebook.FindByHref(path)
|
||||
if err != nil {
|
||||
s.logger.Printf("findByHref(%s): %s", href, err.Error())
|
||||
return "", err
|
||||
}
|
||||
if note == nil {
|
||||
return "", nil
|
||||
}
|
||||
joined_path := filepath.Join(notebook.Path, note.Path)
|
||||
return pathToURI(joined_path), nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
path := filepath.Clean(filepath.Join(filepath.Dir(doc.Path), href))
|
||||
path, err := filepath.Rel(notebook.Path, path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to resolve href: %s", href)
|
||||
}
|
||||
note, err := notebook.FindByHref(path)
|
||||
if err != nil {
|
||||
s.logger.Printf("findByHref(%s): %s", href, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
if note == nil {
|
||||
return nil, nil
|
||||
}
|
||||
joined_path := filepath.Join(notebook.Path, note.Path)
|
||||
|
||||
return &Note{*note, pathToURI(joined_path)}, nil
|
||||
}
|
||||
|
||||
// Run starts the Language Server in stdio mode.
|
||||
func (s *Server) Run() error {
|
||||
return errors.Wrap(s.server.RunStdio(), "lsp")
|
||||
type Note struct {
|
||||
core.MinimalNote
|
||||
URI protocol.DocumentUri
|
||||
}
|
||||
|
||||
func (s *Server) refreshDiagnosticsOfDocument(doc *document, notify glsp.NotifyFunc, delay bool) {
|
||||
if doc.NeedsRefreshDiagnostics { // Already refreshing
|
||||
return
|
||||
}
|
||||
|
||||
notebook, err := s.notebookOf(doc)
|
||||
if err != nil {
|
||||
s.logger.Err(err)
|
||||
return
|
||||
}
|
||||
|
||||
diagConfig := notebook.Config.LSP.Diagnostics
|
||||
if diagConfig.WikiTitle == core.LSPDiagnosticNone && diagConfig.DeadLink == core.LSPDiagnosticNone {
|
||||
// No diagnostic enabled.
|
||||
return
|
||||
}
|
||||
|
||||
doc.NeedsRefreshDiagnostics = true
|
||||
go func() {
|
||||
if delay {
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
doc.NeedsRefreshDiagnostics = false
|
||||
|
||||
diagnostics := []protocol.Diagnostic{}
|
||||
links, err := doc.DocumentLinks()
|
||||
if err != nil {
|
||||
s.logger.Err(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, link := range links {
|
||||
if strutil.IsURL(link.Href) {
|
||||
continue
|
||||
}
|
||||
target, err := s.noteForHref(link.Href, doc, notebook)
|
||||
if err != nil {
|
||||
s.logger.Err(err)
|
||||
continue
|
||||
}
|
||||
|
||||
var severity protocol.DiagnosticSeverity
|
||||
var message string
|
||||
if target == nil {
|
||||
if diagConfig.DeadLink == core.LSPDiagnosticNone {
|
||||
continue
|
||||
}
|
||||
severity = protocol.DiagnosticSeverity(diagConfig.DeadLink)
|
||||
message = "not found"
|
||||
} else {
|
||||
if link.HasTitle || diagConfig.WikiTitle == core.LSPDiagnosticNone {
|
||||
continue
|
||||
}
|
||||
severity = protocol.DiagnosticSeverity(diagConfig.WikiTitle)
|
||||
message = target.Title
|
||||
}
|
||||
|
||||
diagnostics = append(diagnostics, protocol.Diagnostic{
|
||||
Range: link.Range,
|
||||
Severity: &severity,
|
||||
Source: stringPtr("zk"),
|
||||
Message: message,
|
||||
})
|
||||
}
|
||||
|
||||
go notify(protocol.ServerTextDocumentPublishDiagnostics, protocol.PublishDiagnosticsParams{
|
||||
URI: doc.URI,
|
||||
Diagnostics: diagnostics,
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *Server) buildTagCompletionList(notebook *core.Notebook, triggerChar string) ([]protocol.CompletionItem, error) {
|
||||
|
@ -161,9 +161,9 @@ func globalConfigDir() string {
|
||||
|
||||
// SetCurrentNotebook sets the first notebook found in the given search paths
|
||||
// as the current default one.
|
||||
func (c *Container) SetCurrentNotebook(searchDirs []Dirs) {
|
||||
func (c *Container) SetCurrentNotebook(searchDirs []Dirs) error {
|
||||
if len(searchDirs) == 0 {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, dirs := range searchDirs {
|
||||
@ -176,9 +176,14 @@ func (c *Container) SetCurrentNotebook(searchDirs []Dirs) {
|
||||
c.Config = c.currentNotebook.Config
|
||||
// FIXME: Is there something to do to support multiple notebooks here?
|
||||
os.Setenv("ZK_NOTEBOOK_DIR", c.currentNotebook.Path)
|
||||
return
|
||||
}
|
||||
// Report the error only if it's not the "notebook not found" one.
|
||||
var errNotFound core.ErrNotebookNotFound
|
||||
if !errors.As(c.currentNotebookErr, &errNotFound) {
|
||||
return c.currentNotebookErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWorkingDir resets the current working directory.
|
||||
|
@ -15,6 +15,7 @@ type Config struct {
|
||||
Groups map[string]GroupConfig
|
||||
Format FormatConfig
|
||||
Tool ToolConfig
|
||||
LSP LSPConfig
|
||||
Filters map[string]string
|
||||
Aliases map[string]string
|
||||
Extra map[string]string
|
||||
@ -46,6 +47,12 @@ func NewDefaultConfig() Config {
|
||||
LinkDropExtension: true,
|
||||
},
|
||||
},
|
||||
LSP: LSPConfig{
|
||||
Diagnostics: LSPDiagnosticConfig{
|
||||
WikiTitle: LSPDiagnosticNone,
|
||||
DeadLink: LSPDiagnosticError,
|
||||
},
|
||||
},
|
||||
Filters: map[string]string{},
|
||||
Aliases: map[string]string{},
|
||||
Extra: map[string]string{},
|
||||
@ -124,6 +131,27 @@ type ToolConfig struct {
|
||||
FzfLine opt.String
|
||||
}
|
||||
|
||||
// LSPConfig holds the Language Server Protocol configuration.
|
||||
type LSPConfig struct {
|
||||
Diagnostics LSPDiagnosticConfig
|
||||
}
|
||||
|
||||
// LSPDiagnosticConfig holds the LSP diagnostics configuration.
|
||||
type LSPDiagnosticConfig struct {
|
||||
WikiTitle LSPDiagnosticSeverity
|
||||
DeadLink LSPDiagnosticSeverity
|
||||
}
|
||||
|
||||
type LSPDiagnosticSeverity int
|
||||
|
||||
const (
|
||||
LSPDiagnosticNone LSPDiagnosticSeverity = 0
|
||||
LSPDiagnosticError LSPDiagnosticSeverity = 1
|
||||
LSPDiagnosticWarning LSPDiagnosticSeverity = 2
|
||||
LSPDiagnosticInfo LSPDiagnosticSeverity = 3
|
||||
LSPDiagnosticHint LSPDiagnosticSeverity = 4
|
||||
)
|
||||
|
||||
// NoteConfig holds the user configuration used when generating new notes.
|
||||
type NoteConfig struct {
|
||||
// Handlebars template used when generating a new filename.
|
||||
@ -184,12 +212,14 @@ func OpenConfig(path string, parentConfig Config, fs FileStorage) (Config, error
|
||||
//
|
||||
// The parentConfig will be used to inherit default config settings.
|
||||
func ParseConfig(content []byte, path string, parentConfig Config) (Config, error) {
|
||||
wrap := errors.Wrapperf("failed to read config")
|
||||
|
||||
config := parentConfig
|
||||
|
||||
var tomlConf tomlConfig
|
||||
err := toml.Unmarshal(content, &tomlConf)
|
||||
if err != nil {
|
||||
return config, errors.Wrap(err, "failed to read config")
|
||||
return config, wrap(err)
|
||||
}
|
||||
|
||||
// Note
|
||||
@ -275,6 +305,21 @@ func ParseConfig(content []byte, path string, parentConfig Config) (Config, erro
|
||||
config.Tool.FzfLine = opt.NewNotEmptyString(*tool.FzfLine)
|
||||
}
|
||||
|
||||
// LSP
|
||||
lspDiags := tomlConf.LSP.Diagnostics
|
||||
if lspDiags.WikiTitle != nil {
|
||||
config.LSP.Diagnostics.WikiTitle, err = lspDiagnosticSeverityFromString(*lspDiags.WikiTitle)
|
||||
if err != nil {
|
||||
return config, wrap(err)
|
||||
}
|
||||
}
|
||||
if lspDiags.DeadLink != nil {
|
||||
config.LSP.Diagnostics.DeadLink, err = lspDiagnosticSeverityFromString(*lspDiags.DeadLink)
|
||||
if err != nil {
|
||||
return config, wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Filters
|
||||
if tomlConf.Filters != nil {
|
||||
for k, v := range tomlConf.Filters {
|
||||
@ -345,6 +390,7 @@ type tomlConfig struct {
|
||||
Groups map[string]tomlGroupConfig `toml:"group"`
|
||||
Format tomlFormatConfig
|
||||
Tool tomlToolConfig
|
||||
LSP tomlLSPConfig
|
||||
Extra map[string]string
|
||||
Filters map[string]string `toml:"filter"`
|
||||
Aliases map[string]string `toml:"alias"`
|
||||
@ -387,6 +433,13 @@ type tomlToolConfig struct {
|
||||
FzfLine *string `toml:"fzf-line"`
|
||||
}
|
||||
|
||||
type tomlLSPConfig struct {
|
||||
Diagnostics struct {
|
||||
WikiTitle *string `toml:"wiki-title"`
|
||||
DeadLink *string `toml:"dead-link"`
|
||||
}
|
||||
}
|
||||
|
||||
func charsetFromString(charset string) Charset {
|
||||
switch charset {
|
||||
case "alphanum":
|
||||
@ -414,3 +467,20 @@ func caseFromString(c string) Case {
|
||||
return CaseLower
|
||||
}
|
||||
}
|
||||
|
||||
func lspDiagnosticSeverityFromString(s string) (LSPDiagnosticSeverity, error) {
|
||||
switch s {
|
||||
case "", "none":
|
||||
return LSPDiagnosticNone, nil
|
||||
case "error":
|
||||
return LSPDiagnosticError, nil
|
||||
case "warning":
|
||||
return LSPDiagnosticWarning, nil
|
||||
case "info":
|
||||
return LSPDiagnosticInfo, nil
|
||||
case "hint":
|
||||
return LSPDiagnosticHint, nil
|
||||
default:
|
||||
return LSPDiagnosticNone, fmt.Errorf("%s: unknown LSP diagnostic severity - may be none, hint, info, warning or error", s)
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,12 @@ func TestParseDefaultConfig(t *testing.T) {
|
||||
FzfPreview: opt.NullString,
|
||||
FzfLine: opt.NullString,
|
||||
},
|
||||
LSP: LSPConfig{
|
||||
Diagnostics: LSPDiagnosticConfig{
|
||||
WikiTitle: LSPDiagnosticNone,
|
||||
DeadLink: LSPDiagnosticError,
|
||||
},
|
||||
},
|
||||
Filters: make(map[string]string),
|
||||
Aliases: make(map[string]string),
|
||||
Extra: make(map[string]string),
|
||||
@ -115,6 +121,10 @@ func TestParseComplete(t *testing.T) {
|
||||
|
||||
[group."without path"]
|
||||
paths = []
|
||||
|
||||
[lsp.diagnostics]
|
||||
wiki-title = "hint"
|
||||
dead-link = "none"
|
||||
`), ".zk/config.toml", NewDefaultConfig())
|
||||
|
||||
assert.Nil(t, err)
|
||||
@ -207,6 +217,12 @@ func TestParseComplete(t *testing.T) {
|
||||
FzfPreview: opt.NewString("bat {1}"),
|
||||
FzfLine: opt.NewString("{{title}}"),
|
||||
},
|
||||
LSP: LSPConfig{
|
||||
Diagnostics: LSPDiagnosticConfig{
|
||||
WikiTitle: LSPDiagnosticHint,
|
||||
DeadLink: LSPDiagnosticNone,
|
||||
},
|
||||
},
|
||||
Filters: map[string]string{
|
||||
"recents": "--created-after '2 weeks ago'",
|
||||
"journal": "journal --sort created",
|
||||
@ -317,6 +333,12 @@ func TestParseMergesGroupConfig(t *testing.T) {
|
||||
LinkDropExtension: true,
|
||||
},
|
||||
},
|
||||
LSP: LSPConfig{
|
||||
Diagnostics: LSPDiagnosticConfig{
|
||||
WikiTitle: LSPDiagnosticNone,
|
||||
DeadLink: LSPDiagnosticError,
|
||||
},
|
||||
},
|
||||
Filters: make(map[string]string),
|
||||
Aliases: make(map[string]string),
|
||||
Extra: map[string]string{
|
||||
@ -401,6 +423,34 @@ func TestParseMarkdownLinkEncodePath(t *testing.T) {
|
||||
test("custom", false)
|
||||
}
|
||||
|
||||
func TestParseLSPDiagnosticsSeverity(t *testing.T) {
|
||||
test := func(value string, expected LSPDiagnosticSeverity) {
|
||||
toml := fmt.Sprintf(`
|
||||
[lsp.diagnostics]
|
||||
wiki-title = "%s"
|
||||
dead-link = "%s"
|
||||
`, value, value)
|
||||
conf, err := ParseConfig([]byte(toml), ".zk/config.toml", NewDefaultConfig())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, conf.LSP.Diagnostics.WikiTitle, expected)
|
||||
assert.Equal(t, conf.LSP.Diagnostics.DeadLink, expected)
|
||||
}
|
||||
|
||||
test("", LSPDiagnosticNone)
|
||||
test("none", LSPDiagnosticNone)
|
||||
test("error", LSPDiagnosticError)
|
||||
test("warning", LSPDiagnosticWarning)
|
||||
test("info", LSPDiagnosticInfo)
|
||||
test("hint", LSPDiagnosticHint)
|
||||
|
||||
toml := `
|
||||
[lsp.diagnostics]
|
||||
wiki-title = "foobar"
|
||||
`
|
||||
_, err := ParseConfig([]byte(toml), ".zk/config.toml", NewDefaultConfig())
|
||||
assert.Err(t, err, "foobar: unknown LSP diagnostic severity - may be none, hint, info, warning or error")
|
||||
}
|
||||
|
||||
func TestGroupConfigClone(t *testing.T) {
|
||||
original := GroupConfig{
|
||||
Paths: []string{"original"},
|
||||
|
@ -296,6 +296,22 @@ multiword-tags = false
|
||||
#fzf-preview = "bat -p --color always {-1}"
|
||||
|
||||
|
||||
# LSP
|
||||
#
|
||||
# Configure basic editor integration for LSP-compatible editors.
|
||||
# See https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md
|
||||
#
|
||||
[lsp]
|
||||
|
||||
[lsp.diagnostics]
|
||||
# Each diagnostic can have for value: none, hint, info, warning, error
|
||||
|
||||
# Report titles of wiki-links as hints.
|
||||
#wiki-title = "hint"
|
||||
# Warn for dead links between notes.
|
||||
dead-link = "error"
|
||||
|
||||
|
||||
# NAMED FILTERS
|
||||
#
|
||||
# A named filter is a set of note filtering options used frequently together.
|
||||
|
3
main.go
3
main.go
@ -68,7 +68,8 @@ func main() {
|
||||
fatalIfError(err)
|
||||
searchDirs, err := notebookSearchDirs(dirs)
|
||||
fatalIfError(err)
|
||||
container.SetCurrentNotebook(searchDirs)
|
||||
err = container.SetCurrentNotebook(searchDirs)
|
||||
fatalIfError(err)
|
||||
|
||||
// Run the alias or command.
|
||||
if isAlias, err := runAlias(container, os.Args[1:]); isAlias {
|
||||
|
Loading…
Reference in New Issue
Block a user