mirror of https://github.com/mickael-menu/zk
Add LSP commands `zk.list` and `zk.tag.list` (#114)
parent
0e88685140
commit
9c06068cce
@ -0,0 +1,27 @@
|
||||
package lsp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mickael-menu/zk/internal/core"
|
||||
)
|
||||
|
||||
const cmdIndex = "zk.index"
|
||||
|
||||
func executeCommandIndex(notebook *core.Notebook, args []interface{}) (interface{}, error) {
|
||||
opts := core.NoteIndexOpts{}
|
||||
if len(args) == 2 {
|
||||
options, ok := args[1].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("zk.index expects a dictionary of options as second argument, got: %v", args[1])
|
||||
}
|
||||
if forceOption, ok := options["force"]; ok {
|
||||
opts.Force = toBool(forceOption)
|
||||
}
|
||||
if verboseOption, ok := options["verbose"]; ok {
|
||||
opts.Verbose = toBool(verboseOption)
|
||||
}
|
||||
}
|
||||
|
||||
return notebook.Index(opts)
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
package lsp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/mickael-menu/zk/internal/cli"
|
||||
"github.com/mickael-menu/zk/internal/core"
|
||||
"github.com/mickael-menu/zk/internal/util"
|
||||
"github.com/mickael-menu/zk/internal/util/errors"
|
||||
strutil "github.com/mickael-menu/zk/internal/util/strings"
|
||||
)
|
||||
|
||||
const cmdList = "zk.list"
|
||||
|
||||
type cmdListOpts struct {
|
||||
Select []string `json:"select"`
|
||||
cli.Filtering
|
||||
}
|
||||
|
||||
func executeCommandList(logger util.Logger, notebook *core.Notebook, args []interface{}) (interface{}, error) {
|
||||
var opts cmdListOpts
|
||||
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", cmdTagList, args[1])
|
||||
}
|
||||
err := unmarshalJSON(arg, &opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse %s args, got: %v", cmdTagList, arg)
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts.Select) == 0 {
|
||||
return nil, fmt.Errorf("%s expects a `select` option with the list of fields to return", cmdTagList)
|
||||
}
|
||||
var selection = newListSelection(opts.Select)
|
||||
|
||||
findOpts, err := opts.NewNoteFindOpts(notebook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
notes, err := notebook.FindNotes(findOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listNotes := []listNote{}
|
||||
for _, note := range notes {
|
||||
listNotes = append(listNotes, newListNote(note, selection, notebook.Path))
|
||||
}
|
||||
|
||||
return listNotes, nil
|
||||
}
|
||||
|
||||
type listSelection struct {
|
||||
Filename bool
|
||||
FilenameStem bool
|
||||
Path bool
|
||||
AbsPath bool
|
||||
Title bool
|
||||
Lead bool
|
||||
Body bool
|
||||
Snippets bool
|
||||
RawContent bool
|
||||
WordCount bool
|
||||
Tags bool
|
||||
Metadata bool
|
||||
Created bool
|
||||
Modified bool
|
||||
Checksum bool
|
||||
}
|
||||
|
||||
func newListSelection(fields []string) listSelection {
|
||||
return listSelection{
|
||||
Filename: strutil.Contains(fields, "filename"),
|
||||
FilenameStem: strutil.Contains(fields, "filenameStem"),
|
||||
Path: strutil.Contains(fields, "path"),
|
||||
AbsPath: strutil.Contains(fields, "absPath"),
|
||||
Title: strutil.Contains(fields, "title"),
|
||||
Lead: strutil.Contains(fields, "lead"),
|
||||
Body: strutil.Contains(fields, "body"),
|
||||
Snippets: strutil.Contains(fields, "snippets"),
|
||||
RawContent: strutil.Contains(fields, "rawContent"),
|
||||
WordCount: strutil.Contains(fields, "wordCount"),
|
||||
Tags: strutil.Contains(fields, "tags"),
|
||||
Metadata: strutil.Contains(fields, "metadata"),
|
||||
Created: strutil.Contains(fields, "created"),
|
||||
Modified: strutil.Contains(fields, "modified"),
|
||||
Checksum: strutil.Contains(fields, "checksum"),
|
||||
}
|
||||
}
|
||||
|
||||
func newListNote(note core.ContextualNote, selection listSelection, basePath string) listNote {
|
||||
var res listNote
|
||||
if selection.Filename {
|
||||
res.Filename = note.Filename()
|
||||
}
|
||||
if selection.FilenameStem {
|
||||
res.FilenameStem = note.FilenameStem()
|
||||
}
|
||||
if selection.Path {
|
||||
res.Path = note.Path
|
||||
}
|
||||
if selection.AbsPath {
|
||||
res.AbsPath = filepath.Join(basePath, note.Path)
|
||||
}
|
||||
if selection.Title {
|
||||
res.Title = note.Title
|
||||
}
|
||||
if selection.Lead {
|
||||
res.Lead = note.Lead
|
||||
}
|
||||
if selection.Body {
|
||||
res.Body = note.Body
|
||||
}
|
||||
if selection.Snippets {
|
||||
res.Snippets = note.Snippets
|
||||
}
|
||||
if selection.RawContent {
|
||||
res.RawContent = note.RawContent
|
||||
}
|
||||
if selection.WordCount {
|
||||
res.WordCount = note.WordCount
|
||||
}
|
||||
if selection.Tags {
|
||||
res.Tags = note.Tags
|
||||
}
|
||||
if selection.Metadata {
|
||||
res.Metadata = note.Metadata
|
||||
}
|
||||
if selection.Created {
|
||||
res.Created = ¬e.Created
|
||||
}
|
||||
if selection.Modified {
|
||||
res.Modified = ¬e.Modified
|
||||
}
|
||||
if selection.Checksum {
|
||||
res.Checksum = note.Checksum
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type listNote struct {
|
||||
Filename string `json:"filename,omitempty"`
|
||||
FilenameStem string `json:"filenameStem,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
AbsPath string `json:"absPath,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Lead string `json:"lead,omitempty"`
|
||||
Body string `json:"body,omitempty"`
|
||||
Snippets []string `json:"snippets,omitempty"`
|
||||
RawContent string `json:"rawContent,omitempty"`
|
||||
WordCount int `json:"wordCount,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
Created *time.Time `json:"created,omitempty"`
|
||||
Modified *time.Time `json:"modified,omitempty"`
|
||||
Checksum string `json:"checksum,omitempty"`
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package lsp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mickael-menu/zk/internal/core"
|
||||
dateutil "github.com/mickael-menu/zk/internal/util/date"
|
||||
"github.com/mickael-menu/zk/internal/util/errors"
|
||||
"github.com/mickael-menu/zk/internal/util/opt"
|
||||
"github.com/tliron/glsp"
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
const cmdNew = "zk.new"
|
||||
|
||||
type cmdNewOpts struct {
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Dir string `json:"dir"`
|
||||
Group string `json:"group"`
|
||||
Template string `json:"template"`
|
||||
Extra map[string]string `json:"extra"`
|
||||
Date string `json:"date"`
|
||||
Edit jsonBoolean `json:"edit"`
|
||||
InsertLinkAtLocation *protocol.Location `json:"insertLinkAtLocation"`
|
||||
}
|
||||
|
||||
func executeCommandNew(notebook *core.Notebook, documents *documentStore, context *glsp.Context, args []interface{}) (interface{}, error) {
|
||||
var opts cmdNewOpts
|
||||
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", cmdNew, args[1])
|
||||
}
|
||||
err := unmarshalJSON(arg, &opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse %s args, got: %v", cmdNew, arg)
|
||||
}
|
||||
}
|
||||
|
||||
date, err := dateutil.TimeFromNatural(opts.Date)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "%s, failed to parse the `date` option", opts.Date)
|
||||
}
|
||||
|
||||
note, err := notebook.NewNote(core.NewNoteOpts{
|
||||
Title: opt.NewNotEmptyString(opts.Title),
|
||||
Content: opts.Content,
|
||||
Directory: opt.NewNotEmptyString(opts.Dir),
|
||||
Group: opt.NewNotEmptyString(opts.Group),
|
||||
Template: opt.NewNotEmptyString(opts.Template),
|
||||
Extra: opts.Extra,
|
||||
Date: date,
|
||||
})
|
||||
if err != nil {
|
||||
var noteExists core.ErrNoteExists
|
||||
if !errors.As(err, ¬eExists) {
|
||||
return nil, err
|
||||
}
|
||||
note, err = notebook.FindNote(core.NoteFindOpts{
|
||||
IncludeHrefs: []string{noteExists.Name},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if note == nil {
|
||||
return nil, errors.New("zk.new could not generate a new note")
|
||||
}
|
||||
|
||||
if 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
|
||||
}
|
||||
|
||||
currentDir := filepath.Dir(doc.Path)
|
||||
linkFormatterContext, err := core.NewLinkFormatterContext(note.AsMinimalNote(), notebook.Path, currentDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
absPath := filepath.Join(notebook.Path, note.Path)
|
||||
if opts.Edit {
|
||||
go context.Call(protocol.ServerWindowShowDocument, protocol.ShowDocumentParams{
|
||||
URI: pathToURI(absPath),
|
||||
TakeFocus: boolPtr(true),
|
||||
}, nil)
|
||||
}
|
||||
|
||||
return map[string]interface{}{"path": absPath}, nil
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package lsp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mickael-menu/zk/internal/core"
|
||||
"github.com/mickael-menu/zk/internal/util"
|
||||
"github.com/mickael-menu/zk/internal/util/errors"
|
||||
)
|
||||
|
||||
const cmdTagList = "zk.tag.list"
|
||||
|
||||
type cmdTagListOpts struct {
|
||||
Sort []string `json:"sort"`
|
||||
}
|
||||
|
||||
func executeCommandTagList(logger util.Logger, notebook *core.Notebook, args []interface{}) (interface{}, error) {
|
||||
var opts cmdTagListOpts
|
||||
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", cmdTagList, args[1])
|
||||
}
|
||||
err := unmarshalJSON(arg, &opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse %s args, got: %v", cmdTagList, arg)
|
||||
}
|
||||
}
|
||||
|
||||
var sorters []core.CollectionSorter
|
||||
var err error
|
||||
if opts.Sort != nil {
|
||||
sorters, err = core.CollectionSortersFromStrings(opts.Sort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return notebook.FindCollections(core.CollectionKindTag, sorters)
|
||||
}
|
Loading…
Reference in New Issue