Add `zk.link` LSP command (#284)

pull/288/head
Patrick Anker 1 year ago committed by GitHub
parent 150c82fb22
commit f7d4db07d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,9 +6,11 @@ All notable changes to this project will be documented in this file.
### Added
* LSP: `zk.new` now returns the created note's content in its output (`content`), and has two new options:
* `dryRun` will prevent `zk.new` from creating the note on the file system.
* `insertContentAtLocation` can be used to insert the created note's content into an arbitrary location.
* LSP:
* `zk.new` now returns the created note's content in its output (`content`), and has two new options:
* `dryRun` will prevent `zk.new` from creating the note on the file system.
* `insertContentAtLocation` can be used to insert the created note's content into an arbitrary location.
* A new `zk.link` command to insert a link to a given note (contributed by [@psanker](https://github.com/mickael-menu/zk/pull/284)).
## 0.12.0

@ -182,6 +182,18 @@ This LSP command calls `zk new` to create a new note. It can be useful to quickl
* `path` containing the absolute path to the created note.
* `content` containing the raw content of the created note.
#### `zk.link`
This LSP command allows editors to tap into the note linking mechanism. It takes three arguments:
1. A `path` to any file in the notebook that will be linked to
2. An LSP `location` object that points to where the link will be inserted
3. An optional title of the link. If `title` is not provided, the title of the note will be inserted instead
`zk.link` returns a JSON object with the path to the linked note, if the linking was successful.
**Note**: This command is _not_ exposed in the command line. This command is targeted at editor / plugin authors to extend zk functionality.
#### `zk.list`
This LSP command calls `zk list` to search a notebook. It takes two arguments:

@ -0,0 +1,64 @@
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 {
return nil, err
}
if note == nil {
return nil, errors.New("Requested note to link to not found!")
}
info := &linkInfo{
note: note,
location: opts.Location,
title: opts.Title,
}
err = linkNote(notebook, documents, context, info)
if err != nil {
return nil, err
}
return map[string]interface{}{
"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,
}
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 err != nil {
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) error {
if info.location == nil {
return errors.New("'location' not provided")
}
// Get current document to edit
doc, ok := documents.Get(info.location.URI)
if !ok {
return fmt.Errorf("Cannot insert link in '%s'", info.location.URI)
}
formatter, err := notebook.NewLinkFormatter()
if err != nil {
return 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 err
}
link, err := formatter(formatterContext)
if err != nil {
return 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 nil
}

Loading…
Cancel
Save