feat(lsp): add 'zk.link' LSP command

Allows users to take advantage of the linking mechanism in their editing
tools outside of the 'zk.new' context. For example, I made a
"ZkInsertLink" command that inserts a link at the current cursor location
after searching for the desired note. This improves on the current
behavior which only autocompletes based on note name, which users may or
may not remember.
pull/284/head
Patrick Anker 1 year ago
parent 6506198743
commit 9e378aa70a
No known key found for this signature in database
GPG Key ID: 66338E6C32588ED1

@ -0,0 +1,61 @@
package lsp
import (
"fmt"
"path/filepath"
"github.com/mickael-menu/zk/internal/core"
"github.com/mickael-menu/zk/internal/util/errors"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
const cmdLink = "zk.link"
type cmdLinkOpts struct {
Path *string `json:"path"`
Location *protocol.Location `json:"location"`
Title *string `json:"title"`
}
func executeCommandLink(notebook *core.Notebook, documents *documentStore, context *glsp.Context, args []interface{}) (interface{}, error) {
var opts cmdLinkOpts
if len(args) > 1 {
arg, ok := args[1].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("%s expects a dictionary of options as second argument, got: %v", cmdLink, args[1])
}
err := unmarshalJSON(arg, &opts)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse %s args, got: %v", cmdLink, arg)
}
}
if opts.Path == nil {
return nil, errors.New("'path' not provided")
}
note, err := notebook.FindByHref(*opts.Path, false)
if err != nil || note == nil {
return nil, errors.New("Requested note to link to not found!")
}
info := &linkInfo{
note: note,
location: opts.Location,
title: opts.Title,
}
ok, err := linkNote(notebook, documents, context, info)
if !ok {
return nil, err
}
return map[string]interface{}{
"ok": true,
"path": filepath.Join(notebook.Path, note.Path),
}, nil
}

@ -83,37 +83,18 @@ func executeCommandNew(notebook *core.Notebook, documents *documentStore, contex
}
if !opts.DryRun && opts.InsertLinkAtLocation != nil {
doc, ok := documents.Get(opts.InsertLinkAtLocation.URI)
if !ok {
return nil, fmt.Errorf("can't insert link in %s", opts.InsertLinkAtLocation.URI)
}
linkFormatter, err := notebook.NewLinkFormatter()
if err != nil {
return nil, err
}
minNote := note.AsMinimalNote()
path := core.NotebookPath{
Path: note.Path,
BasePath: notebook.Path,
WorkingDir: filepath.Dir(doc.Path),
}
linkFormatterContext, err := core.NewLinkFormatterContext(path, note.Title, note.Metadata)
if err != nil {
return nil, err
}
info := &linkInfo{
note: &minNote,
location: opts.InsertLinkAtLocation,
title: &opts.Title,
}
ok, err := linkNote(notebook, documents, context, info)
link, err := linkFormatter(linkFormatterContext)
if err != nil {
return nil, err
}
go context.Call(protocol.ServerWorkspaceApplyEdit, protocol.ApplyWorkspaceEditParams{
Edit: protocol.WorkspaceEdit{
Changes: map[string][]protocol.TextEdit{
opts.InsertLinkAtLocation.URI: {{Range: opts.InsertLinkAtLocation.Range, NewText: link}},
},
},
}, nil)
if !ok {
return nil, err
}
}
absPath := filepath.Join(notebook.Path, note.Path)

@ -369,6 +369,13 @@ func NewServer(opts ServerOpts) *Server {
}
return executeCommandNew(nb, server.documents, context, params.Arguments)
case cmdLink:
nb, err := openNotebook()
if err != nil {
return nil, err
}
return executeCommandLink(nb, server.documents, context, params.Arguments)
case cmdList:
nb, err := openNotebook()
if err != nil {

@ -3,10 +3,14 @@ package lsp
import (
"fmt"
"net/url"
"path/filepath"
"runtime"
"strings"
"github.com/mickael-menu/zk/internal/core"
"github.com/mickael-menu/zk/internal/util/errors"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func pathToURI(path string) string {
@ -56,3 +60,59 @@ func (b *jsonBoolean) UnmarshalJSON(data []byte) error {
}
return nil
}
type linkInfo struct {
note *core.MinimalNote
location *protocol.Location
title *string
}
func linkNote(notebook *core.Notebook, documents *documentStore, context *glsp.Context, info *linkInfo) (bool, error) {
if info.location == nil {
return false, errors.New("'location' not provided")
}
// Get current document to edit
doc, ok := documents.Get(info.location.URI)
if !ok {
return false, fmt.Errorf("Cannot insert link in '%s'", info.location.URI)
}
formatter, err := notebook.NewLinkFormatter()
if err != nil {
return false, err
}
path := core.NotebookPath{
Path: info.note.Path,
BasePath: notebook.Path,
WorkingDir: filepath.Dir(doc.Path),
}
var title *string
title = info.title
if title == nil {
title = &info.note.Title
}
formatterContext, err := core.NewLinkFormatterContext(path, *title, info.note.Metadata)
if err != nil {
return false, err
}
link, err := formatter(formatterContext)
if err != nil {
return false, err
}
go context.Call(protocol.ServerWorkspaceApplyEdit, protocol.ApplyWorkspaceEditParams{
Edit: protocol.WorkspaceEdit{
Changes: map[string][]protocol.TextEdit{
info.location.URI: {{Range: info.location.Range, NewText: link}},
},
},
}, nil)
return true, nil
}

Loading…
Cancel
Save