From 6c9b0f13c8d72e41ed8ae1895d7bba91d92d5003 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Tue, 21 Jan 2025 15:35:35 +0200 Subject: [PATCH 01/44] finder: import fzf/src for native integration --- go.mod | 17 +++++++++++- go.sum | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 go.sum diff --git a/go.mod b/go.mod index a2aa4a2..eb63b8d 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,18 @@ module github.com/alonswartz/notesium -go 1.16 +go 1.20 + +require ( + github.com/charlievieth/fastwalk v1.0.9 // indirect + github.com/gdamore/encoding v1.0.1 // indirect + github.com/gdamore/tcell/v2 v2.8.1 // indirect + github.com/junegunn/fzf v0.58.0 // indirect + github.com/junegunn/go-shellwords v0.0.0-20240813092932-a62c48c52e97 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/term v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b1edd03 --- /dev/null +++ b/go.sum @@ -0,0 +1,87 @@ +github.com/charlievieth/fastwalk v1.0.9 h1:Odb92AfoReO3oFBfDGT5J+nwgzQPF/gWAw6E6/lkor0= +github.com/charlievieth/fastwalk v1.0.9/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI= +github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= +github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= +github.com/gdamore/tcell/v2 v2.8.1 h1:KPNxyqclpWpWQlPLx6Xui1pMk8S+7+R37h3g07997NU= +github.com/gdamore/tcell/v2 v2.8.1/go.mod h1:bj8ori1BG3OYMjmb3IklZVWfZUJ1UBQt9JXrOCOhGWw= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/junegunn/fzf v0.58.0 h1:sT6lO4OTkHpEHpr8E1iZz6bvxZ6URHjTYl8/yhS8s1U= +github.com/junegunn/fzf v0.58.0/go.mod h1:IsDYaa3WFbMYYi8yp92fQFTqN10hs3nH4OMBiz/kJXo= +github.com/junegunn/go-shellwords v0.0.0-20240813092932-a62c48c52e97 h1:rqzLixVo1c/GQW6px9j1xQmlvQIn+lf/V6M1UQ7IFzw= +github.com/junegunn/go-shellwords v0.0.0-20240813092932-a62c48c52e97/go.mod h1:6EILKtGpo5t+KLb85LNZLAF6P9LKp78hJI80PXMcn3c= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From fd5683ba244d1398c657c90df2262df20c54b31a Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 22 Jan 2025 12:38:44 +0200 Subject: [PATCH 02/44] finder: initial implementation with hardcoded values --- finder.go | 35 ++++++++++++++++++++++++++++++++ notesium.go | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++ options.go | 7 +++++++ 3 files changed, 99 insertions(+) create mode 100644 finder.go diff --git a/finder.go b/finder.go new file mode 100644 index 0000000..2f1c4d8 --- /dev/null +++ b/finder.go @@ -0,0 +1,35 @@ +package main + +import ( + "fmt" + + fzf "github.com/junegunn/fzf/src" +) + +type channelWriter struct { + ch chan string +} + +func (cw *channelWriter) Write(p []byte) (n int, err error) { + str := string(p) // Convert bytes to string + cw.ch <- str // Send to channel + return len(p), nil +} + +func runFinder(inputChan chan string, outputChan chan string, opts []string) (int, error) { + options, err := fzf.ParseOptions(false, opts) + if err != nil { + return 2, fmt.Errorf("fzf error: %w", err) + } + + options.Input = inputChan + options.Output = outputChan + + code, err := fzf.Run(options) + if err != nil { + return code, fmt.Errorf("error running fzf: %w", err) + } + + return code, nil +} + diff --git a/notesium.go b/notesium.go index cee4334..80a49d6 100644 --- a/notesium.go +++ b/notesium.go @@ -58,6 +58,8 @@ func main() { notesiumLines(notesiumDir, cmd.Options.(linesOptions), os.Stdout) case "stats": notesiumStats(notesiumDir, cmd.Options.(statsOptions), os.Stdout) + case "finder": + notesiumFinder(notesiumDir, cmd.Options.(finderOptions)) case "web": notesiumWeb(notesiumDir, cmd.Options.(webOptions)) case "extract": @@ -331,6 +333,61 @@ func notesiumStats(dir string, opts statsOptions, w io.Writer) { fmt.Fprintf(w, keyFormat+" %d\n", "chars", chars) } +func notesiumFinder(dir string, opts finderOptions) { + optsInput := []string{ + "list", + "--color", + "--prefix=label", + "--sort=alpha", + } + + optsFzf := []string{ + "--ansi", + "--exact", + "--no-sort", + "--delimiter=:", + "--with-nth=3..", + "--reverse", + "--border", + "--no-height", + "--no-scrollbar", + "--no-separator", + } + + inputCmd, err := parseOptions(optsInput) + if err != nil { + log.Fatal(err) + } + + inputChan := make(chan string) + outputChan := make(chan string) + + go func() { + defer close(inputChan) + writer := &channelWriter{ch: inputChan} + + switch inputCmd.Name { + case "list": + notesiumList(dir, inputCmd.Options.(listOptions), writer) + default: + log.Fatal("input command not supported: ", inputCmd.Name) + } + }() + + go func() { + defer close(outputChan) + for output := range outputChan { + fmt.Printf(output) + } + }() + + code, err := runFinder(inputChan, outputChan, optsFzf) + if code != 0 && code != 130 && err != nil { + fmt.Fprintf(os.Stderr, "Error running fzf: %v\n", err) + } + os.Exit(code) +} + func notesiumWeb(dir string, opts webOptions) { populateCache(dir) diff --git a/options.go b/options.go index b65c695..ea927be 100644 --- a/options.go +++ b/options.go @@ -82,6 +82,9 @@ type linesOptions struct { filter string } +type finderOptions struct { +} + type statsOptions struct { color Color table bool @@ -231,6 +234,10 @@ func parseOptions(args []string) (Command, error) { cmd.Options = opts return cmd, nil + case "finder": + cmd.Options = finderOptions{} + return cmd, nil + case "stats": opts := statsOptions{} for _, opt := range args[1:] { From d7699cfd41da7c196ceb18d8b844643984b1ed3e Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 22 Jan 2025 13:43:24 +0200 Subject: [PATCH 03/44] finder: support custom input using end-of-options unix convention --- notesium.go | 17 +++++++---------- options.go | 20 +++++++++++++++++++- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/notesium.go b/notesium.go index 80a49d6..152fe6d 100644 --- a/notesium.go +++ b/notesium.go @@ -334,11 +334,9 @@ func notesiumStats(dir string, opts statsOptions, w io.Writer) { } func notesiumFinder(dir string, opts finderOptions) { - optsInput := []string{ - "list", - "--color", - "--prefix=label", - "--sort=alpha", + inputCmd, err := parseOptions(opts.input) + if err != nil { + log.Fatal(err) } optsFzf := []string{ @@ -354,11 +352,6 @@ func notesiumFinder(dir string, opts finderOptions) { "--no-separator", } - inputCmd, err := parseOptions(optsInput) - if err != nil { - log.Fatal(err) - } - inputChan := make(chan string) outputChan := make(chan string) @@ -369,6 +362,10 @@ func notesiumFinder(dir string, opts finderOptions) { switch inputCmd.Name { case "list": notesiumList(dir, inputCmd.Options.(listOptions), writer) + case "links": + notesiumLinks(dir, inputCmd.Options.(linksOptions), writer) + case "lines": + notesiumLines(dir, inputCmd.Options.(linesOptions), writer) default: log.Fatal("input command not supported: ", inputCmd.Name) } diff --git a/options.go b/options.go index ea927be..000163d 100644 --- a/options.go +++ b/options.go @@ -35,6 +35,8 @@ Commands: stats Print statistics --color Color code using ansi escape sequences --table Format as table with whitespace delimited columns + finder Start finder (interactive filter selection TUI) + -- CMD [OPTS] Input (default: list --color --prefix=label --sort=alpha) web Start web server --webroot=PATH Path to web root to serve (default: embedded webroot) --mount=DIR:URI Additional directory to serve under webroot (experimental) @@ -83,6 +85,7 @@ type linesOptions struct { } type finderOptions struct { + input []string } type statsOptions struct { @@ -235,7 +238,22 @@ func parseOptions(args []string) (Command, error) { return cmd, nil case "finder": - cmd.Options = finderOptions{} + opts := finderOptions{} + opts.input = []string{"list", "--color", "--prefix=label", "--sort=alpha"} + for i, opt := range args[1:] { + if opt == "--" { + opts.input = args[i+2:] + if len(opts.input) == 0 { + return Command{}, fmt.Errorf("input command not specified") + } + break + } + switch { + default: + return Command{}, fmt.Errorf("unrecognized option: %s", opt) + } + } + cmd.Options = opts return cmd, nil case "stats": From eec71b7e2197cae0ab119d16c7c9d80b0f106ee8 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 22 Jan 2025 15:59:53 +0200 Subject: [PATCH 04/44] finder: support setting custom prompt text --- notesium.go | 1 + options.go | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/notesium.go b/notesium.go index 152fe6d..d8f1a7a 100644 --- a/notesium.go +++ b/notesium.go @@ -350,6 +350,7 @@ func notesiumFinder(dir string, opts finderOptions) { "--no-height", "--no-scrollbar", "--no-separator", + fmt.Sprintf("--prompt=%s> ", opts.prompt), } inputChan := make(chan string) diff --git a/options.go b/options.go index 000163d..1fb54ba 100644 --- a/options.go +++ b/options.go @@ -36,6 +36,7 @@ Commands: --color Color code using ansi escape sequences --table Format as table with whitespace delimited columns finder Start finder (interactive filter selection TUI) + --prompt=STR Set custom prompt text -- CMD [OPTS] Input (default: list --color --prefix=label --sort=alpha) web Start web server --webroot=PATH Path to web root to serve (default: embedded webroot) @@ -85,7 +86,8 @@ type linesOptions struct { } type finderOptions struct { - input []string + input []string + prompt string } type statsOptions struct { @@ -249,6 +251,8 @@ func parseOptions(args []string) (Command, error) { break } switch { + case strings.HasPrefix(opt, "--prompt="): + opts.prompt = strings.TrimPrefix(opt, "--prompt=") default: return Command{}, fmt.Errorf("unrecognized option: %s", opt) } From 657015e99f0a36b9f244603d56343231ae33b435 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 22 Jan 2025 17:51:41 +0200 Subject: [PATCH 05/44] finder: support note preview --- notesium.go | 29 +++++++++++++++++++++++++++++ options.go | 24 ++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/notesium.go b/notesium.go index d8f1a7a..4203f24 100644 --- a/notesium.go +++ b/notesium.go @@ -60,6 +60,8 @@ func main() { notesiumStats(notesiumDir, cmd.Options.(statsOptions), os.Stdout) case "finder": notesiumFinder(notesiumDir, cmd.Options.(finderOptions)) + case "cat": + notesiumCat(notesiumDir, cmd.Options.(catOptions)) case "web": notesiumWeb(notesiumDir, cmd.Options.(webOptions)) case "extract": @@ -353,6 +355,24 @@ func notesiumFinder(dir string, opts finderOptions) { fmt.Sprintf("--prompt=%s> ", opts.prompt), } + if opts.preview { + executablePath, err := os.Executable() + if err != nil { + log.Fatalf("error resolving executable path: %v", err) + } + + executablePath, err = filepath.EvalSymlinks(executablePath) + if err != nil { + log.Fatalf("error resolving symlinked executable path: %v", err) + } + + optsFzf = append(optsFzf, + "--bind=ctrl-/:toggle-preview", + "--preview-window=+{2}-/2", + fmt.Sprintf("--preview=%s cat {}", executablePath), + ) + } + inputChan := make(chan string) outputChan := make(chan string) @@ -386,6 +406,15 @@ func notesiumFinder(dir string, opts finderOptions) { os.Exit(code) } +func notesiumCat(dir string, opts catOptions) { + path := filepath.Join(dir, opts.filename) + content, err := os.ReadFile(path) + if err != nil { + log.Fatalf("%v\n", err) + } + fmt.Print(string(content)) +} + func notesiumWeb(dir string, opts webOptions) { populateCache(dir) diff --git a/options.go b/options.go index 1fb54ba..a61a937 100644 --- a/options.go +++ b/options.go @@ -36,6 +36,7 @@ Commands: --color Color code using ansi escape sequences --table Format as table with whitespace delimited columns finder Start finder (interactive filter selection TUI) + --preview Display note preview (toggle with ctrl-/) --prompt=STR Set custom prompt text -- CMD [OPTS] Input (default: list --color --prefix=label --sort=alpha) web Start web server @@ -86,8 +87,13 @@ type linesOptions struct { } type finderOptions struct { - input []string - prompt string + input []string + prompt string + preview bool +} + +type catOptions struct { + filename string } type statsOptions struct { @@ -251,6 +257,8 @@ func parseOptions(args []string) (Command, error) { break } switch { + case opt == "--preview": + opts.preview = true case strings.HasPrefix(opt, "--prompt="): opts.prompt = strings.TrimPrefix(opt, "--prompt=") default: @@ -326,6 +334,18 @@ func parseOptions(args []string) (Command, error) { cmd.Options = opts return cmd, nil + case "cat": + if len(args) != 2 { + return Command{}, fmt.Errorf("filename not specified or too many arguments") + } + filename := args[1] + if colonIndex := strings.Index(filename, ":"); colonIndex != -1 { + filename = filename[:colonIndex] + } + opts := catOptions{filename: filename} + cmd.Options = opts + return cmd, nil + case "version": opts := versionOptions{} for _, opt := range args[1:] { From 28637a4dae71a789fb8c3e8385d3678905674e06 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Fri, 24 Jan 2025 14:14:58 +0200 Subject: [PATCH 06/44] finder: support note preview syntax highlighting --- highlight.go | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++ notesium.go | 7 ++-- 2 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 highlight.go diff --git a/highlight.go b/highlight.go new file mode 100644 index 0000000..2c5b708 --- /dev/null +++ b/highlight.go @@ -0,0 +1,102 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "regexp" + "strings" +) + +const ( + ansiHeading = "\033[1m" + ansiBold = "\033[1m" + ansiItalic = "\033[3m" + ansiLink = "\033[34m" + ansiCodeBlock = "\033[33m" + ansiBlockQuote = "\033[36m" + ansiListMarker = "\033[36m" + ansiReset = "\033[0m" +) +var ( + reBold = regexp.MustCompile(`\*\*(.*?)\*\*`) + reBoldAlt = regexp.MustCompile(`__(.*?)__`) + reItalic = regexp.MustCompile(`\*(.*?)\*`) + reItalicAlt = regexp.MustCompile(`_(.*?)_`) + reUnorderedList = regexp.MustCompile(`^(\s*[-+*]) `) + reOrderedList = regexp.MustCompile(`^(\s*\d+\.) `) + reLinkPlain = regexp.MustCompile(`(?:https?://|www\.)[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(/[^\s]*)?`) + reLinkMarkdown = regexp.MustCompile(`\[(.[^]]*?)\]\((.[^)]*?)\)`) +) + +func renderMarkdown(reader io.Reader, writer io.Writer) { + inCodeBlock := false + scanner := bufio.NewScanner(reader) + + for scanner.Scan() { + line := scanner.Text() + highlightedLine := highlightLine(line, &inCodeBlock) + fmt.Fprintln(writer, highlightedLine) + } + + if err := scanner.Err(); err != nil { + fmt.Fprintf(writer, "Error reading content: %v\n", err) + } +} + +func highlightLine(line string, inCodeBlock *bool) string { + // Code blocks + if strings.HasPrefix(line, "```") { + *inCodeBlock = !*inCodeBlock + return ansiCodeBlock + line + ansiReset + } + if *inCodeBlock { + return ansiCodeBlock + line + ansiReset + } + + // Headers + if strings.HasPrefix(line, "#") { + return ansiHeading + line + ansiReset + } + + // Blockquotes + if strings.HasPrefix(line, "> ") { + return ansiBlockQuote + line + ansiReset + } + + // Links + line = highlightLink(line, reLinkMarkdown, ansiLink) + line = highlightRegex(line, reLinkPlain, ansiLink, 0) + + // Bold (**text** or __text__) + line = highlightRegex(line, reBold, ansiBold, 2) + line = highlightRegex(line, reBoldAlt, ansiBold, 2) + + // Italic (*text* or _text_) + line = highlightRegex(line, reItalic, ansiItalic, 1) + line = highlightRegex(line, reItalicAlt, ansiItalic, 1) + + // List markers + line = highlightRegex(line, reUnorderedList, ansiListMarker, 0) + line = highlightRegex(line, reOrderedList, ansiListMarker, 0) + + return line +} + +func highlightRegex(line string, re *regexp.Regexp, ansiCode string, markerLength int) string { + return re.ReplaceAllStringFunc(line, func(match string) string { + inner := match[markerLength : len(match)-markerLength] + return ansiCode + inner + ansiReset + }) +} + +func highlightLink(line string, re *regexp.Regexp, ansiCode string) string { + return re.ReplaceAllStringFunc(line, func(match string) string { + matches := re.FindStringSubmatch(match) + if len(matches) >= 2 { + title := matches[1] + return ansiCode + title + ansiReset + } + return match + }) +} diff --git a/notesium.go b/notesium.go index 4203f24..8aa9292 100644 --- a/notesium.go +++ b/notesium.go @@ -408,11 +408,12 @@ func notesiumFinder(dir string, opts finderOptions) { func notesiumCat(dir string, opts catOptions) { path := filepath.Join(dir, opts.filename) - content, err := os.ReadFile(path) + file, err := os.Open(path) if err != nil { - log.Fatalf("%v\n", err) + log.Fatalf("Error opening file: %v\n", err) } - fmt.Print(string(content)) + defer file.Close() + renderMarkdown(file, os.Stdout) } func notesiumWeb(dir string, opts webOptions) { From f5984e674a0fcf24ef8660661b5c67e804118176 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Fri, 24 Jan 2025 15:13:35 +0200 Subject: [PATCH 07/44] finder: support note preview inlineCode syntax highlighting --- highlight.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/highlight.go b/highlight.go index 2c5b708..61bcdc5 100644 --- a/highlight.go +++ b/highlight.go @@ -14,6 +14,7 @@ const ( ansiItalic = "\033[3m" ansiLink = "\033[34m" ansiCodeBlock = "\033[33m" + ansiInlineCode = "\033[33m" ansiBlockQuote = "\033[36m" ansiListMarker = "\033[36m" ansiReset = "\033[0m" @@ -25,6 +26,7 @@ var ( reItalicAlt = regexp.MustCompile(`_(.*?)_`) reUnorderedList = regexp.MustCompile(`^(\s*[-+*]) `) reOrderedList = regexp.MustCompile(`^(\s*\d+\.) `) + reInlineCode = regexp.MustCompile("`(.*?)`") reLinkPlain = regexp.MustCompile(`(?:https?://|www\.)[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(/[^\s]*)?`) reLinkMarkdown = regexp.MustCompile(`\[(.[^]]*?)\]\((.[^)]*?)\)`) ) @@ -64,6 +66,16 @@ func highlightLine(line string, inCodeBlock *bool) string { return ansiBlockQuote + line + ansiReset } + // Inline code + matches := reInlineCode.FindAllStringSubmatchIndex(line, -1) + if len(matches) > 0 { + return highlightLineWithInlineCode(line, matches) + } + + return highlightString(line) +} + +func highlightString(line string) string { // Links line = highlightLink(line, reLinkMarkdown, ansiLink) line = highlightRegex(line, reLinkPlain, ansiLink, 0) @@ -100,3 +112,31 @@ func highlightLink(line string, re *regexp.Regexp, ansiCode string) string { return match }) } + +func highlightLineWithInlineCode(line string, matches [][]int) string { + var builder strings.Builder + prevIndex := 0 + + for _, match := range matches { + start, end := match[0], match[1] + groupStart, groupEnd := match[2], match[3] + + // Handle text before inline code + if start > prevIndex { + builder.WriteString(highlightString(line[prevIndex:start])) + } + + builder.WriteString(ansiInlineCode) + builder.WriteString(line[groupStart:groupEnd]) + builder.WriteString(ansiReset) + + prevIndex = end + } + + // Handle text after inline code + if prevIndex < len(line) { + builder.WriteString(highlightString(line[prevIndex:])) + } + + return builder.String() +} From 241ef8dcc16d274d7c30c9f2fbceef77029cf9ee Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Sun, 26 Jan 2025 13:28:47 +0200 Subject: [PATCH 08/44] finder: support note preview with lineNumber highlight --- highlight.go | 29 +++++++++++++++++++++++++++-- notesium.go | 2 +- options.go | 17 ++++++++++++----- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/highlight.go b/highlight.go index 61bcdc5..91f52fb 100644 --- a/highlight.go +++ b/highlight.go @@ -17,6 +17,7 @@ const ( ansiInlineCode = "\033[33m" ansiBlockQuote = "\033[36m" ansiListMarker = "\033[36m" + ansiLineBg = "\033[40m" ansiReset = "\033[0m" ) var ( @@ -29,15 +30,20 @@ var ( reInlineCode = regexp.MustCompile("`(.*?)`") reLinkPlain = regexp.MustCompile(`(?:https?://|www\.)[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(/[^\s]*)?`) reLinkMarkdown = regexp.MustCompile(`\[(.[^]]*?)\]\((.[^)]*?)\)`) + reAnsi = regexp.MustCompile(`\x1b\[[0-9;]*m`) + reReset = regexp.MustCompile(`\x1b\[0m`) ) -func renderMarkdown(reader io.Reader, writer io.Writer) { +func renderMarkdown(reader io.Reader, writer io.Writer, lineNumber int) { inCodeBlock := false scanner := bufio.NewScanner(reader) - for scanner.Scan() { + for lineIndex := 1; scanner.Scan(); lineIndex++ { line := scanner.Text() highlightedLine := highlightLine(line, &inCodeBlock) + if lineNumber > 0 && lineNumber == lineIndex { + highlightedLine = highlightLineWithBackground(highlightedLine) + } fmt.Fprintln(writer, highlightedLine) } @@ -140,3 +146,22 @@ func highlightLineWithInlineCode(line string, matches [][]int) string { return builder.String() } + + +func highlightLineWithBackground(highlightedLine string) string { + // apply bg after resets to handle segments + highlightedLine = reReset.ReplaceAllStringFunc(highlightedLine, func(reset string) string { + return reset + ansiLineBg + }) + + // apply padding + termWidth := 79 + visibleChars := len(reAnsi.ReplaceAllString(highlightedLine, "")) + requiredPadding := termWidth - visibleChars + if requiredPadding > 0 { + padding := strings.Repeat(" ", requiredPadding) + highlightedLine += ansiLineBg + padding + } + + return ansiLineBg + highlightedLine + ansiReset +} diff --git a/notesium.go b/notesium.go index 8aa9292..10fe33d 100644 --- a/notesium.go +++ b/notesium.go @@ -413,7 +413,7 @@ func notesiumCat(dir string, opts catOptions) { log.Fatalf("Error opening file: %v\n", err) } defer file.Close() - renderMarkdown(file, os.Stdout) + renderMarkdown(file, os.Stdout, opts.lineNumber) } func notesiumWeb(dir string, opts webOptions) { diff --git a/options.go b/options.go index a61a937..5edca00 100644 --- a/options.go +++ b/options.go @@ -93,7 +93,8 @@ type finderOptions struct { } type catOptions struct { - filename string + filename string + lineNumber int } type statsOptions struct { @@ -335,14 +336,20 @@ func parseOptions(args []string) (Command, error) { return cmd, nil case "cat": + opts := catOptions{} + opts.lineNumber = 0 if len(args) != 2 { return Command{}, fmt.Errorf("filename not specified or too many arguments") } - filename := args[1] - if colonIndex := strings.Index(filename, ":"); colonIndex != -1 { - filename = filename[:colonIndex] + + parts := strings.Split(args[1], ":") + opts.filename = parts[0] + if len(parts) > 1 { + if num, err := strconv.Atoi(parts[1]); err == nil && num > 0 { + opts.lineNumber = num + } } - opts := catOptions{filename: filename} + cmd.Options = opts return cmd, nil From 028ecaeb4344df3f98c64ad52c2a6a2aa1b4b18b Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Sun, 26 Jan 2025 14:51:24 +0200 Subject: [PATCH 09/44] finder: refine colors for dark and light mode compatibility --- notesium.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/notesium.go b/notesium.go index 10fe33d..d9c0c2c 100644 --- a/notesium.go +++ b/notesium.go @@ -344,14 +344,16 @@ func notesiumFinder(dir string, opts finderOptions) { optsFzf := []string{ "--ansi", "--exact", - "--no-sort", - "--delimiter=:", - "--with-nth=3..", - "--reverse", "--border", + "--reverse", + "--no-sort", "--no-height", "--no-scrollbar", "--no-separator", + "--pointer=>", + "--delimiter=:", + "--with-nth=3..", + "--color=bg:8,bg+:0,fg:12,fg+:12,hl:11,hl+:3,pointer:9,info:3", fmt.Sprintf("--prompt=%s> ", opts.prompt), } From 82e5bc20ae7f2dae2df1087602810cd60a8ca735 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 12:48:15 +0200 Subject: [PATCH 10/44] vim: initial notesium.vim with fzf-based integration from readme --- vim/notesium.vim | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 vim/notesium.vim diff --git a/vim/notesium.vim b/vim/notesium.vim new file mode 100644 index 0000000..3814553 --- /dev/null +++ b/vim/notesium.vim @@ -0,0 +1,80 @@ +let $NOTESIUM_DIR = trim(system("notesium home")) +let $NOTESIUM_WEEKSTART = 1 "0 Sunday, 1 Monday, ... + +autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ fzf#vim#complete({ + \ 'source': 'notesium list --sort=mtime', + \ 'options': '+s -d : --with-nth 3.. --prompt "NotesiumInsertLink> "', + \ 'reducer': {l->"[". split(l[0],':1: ')[1] ."](".split(l[0],':')[0].")"}, + \ 'window': {'width': 0.5, 'height': 0.5}}) + +command! -bang NotesiumNew + \ execute ":e" system("notesium new") + +command! -bang NotesiumWeb + \ let s:options = "--stop-on-idle --open-browser" | + \ execute ":silent !nohup notesium web ".s:options." > /dev/null 2>&1 &" + +command! -bang -nargs=* NotesiumList + \ let s:spec = {'dir': $NOTESIUM_DIR, 'options': '+s -d : --with-nth 3..'} | + \ call fzf#vim#grep( + \ 'notesium list '.join(map(split(), 'shellescape(v:val)'), ' '), 0, + \ &columns > 79 ? fzf#vim#with_preview(s:spec, 'right', 'ctrl-/') : s:spec, 0) + +command! -bang -nargs=* NotesiumLinks + \ let s:spec = {'dir': $NOTESIUM_DIR, 'options': '-d : --with-nth 3..'} | + \ call fzf#vim#grep( + \ 'notesium links '.join(map(split(), 'shellescape(v:val)'), ' '), 0, + \ &columns > 79 ? fzf#vim#with_preview(s:spec, 'right', 'ctrl-/') : s:spec, 0) + +command! -bang -nargs=* NotesiumSearch + \ let s:spec = {'dir': $NOTESIUM_DIR, 'options': '-d : --with-nth 3..'} | + \ call fzf#vim#grep( + \ 'notesium lines '.join(map(split(), 'shellescape(v:val)'), ' '), 0, + \ &columns > 79 ? fzf#vim#with_preview(s:spec, 'right', 'ctrl-/') : s:spec, 0) + +command! -bang -nargs=* NotesiumDaily + \ let s:cdate = empty() ? strftime('%Y-%m-%d') : | + \ let s:output = system('notesium new --verbose --ctime='.s:cdate.'T00:00:00') | + \ let s:filepath = matchstr(s:output, 'path:\zs[^\n]*') | + \ execute 'edit ' . s:filepath | + \ if getline(1) =~ '^\s*$' | + \ let s:epoch = matchstr(s:output, 'epoch:\zs[^\n]*') | + \ call setline(1, '# ' . strftime('%b %d, %Y (%A)', s:epoch)) | + \ endif + +command! -bang -nargs=* NotesiumWeekly + \ let s:date = empty() ? strftime('%Y-%m-%d') : | + \ let s:output = system('notesium new --verbose --ctime='.s:date.'T00:00:01') | + \ let s:epoch = str2nr(matchstr(s:output, 'epoch:\zs[^\n]*')) | + \ let s:day = strftime('%u', s:epoch) | + \ let s:startOfWeek = empty($NOTESIUM_WEEKSTART) ? 1 : $NOTESIUM_WEEKSTART | + \ let s:diff = (s:day - s:startOfWeek + 7) % 7 | + \ let s:weekBegEpoch = s:epoch - (s:diff * 86400) | + \ let s:weekBegDate = strftime('%Y-%m-%d', s:weekBegEpoch) | + \ let s:output = system('notesium new --verbose --ctime='.s:weekBegDate.'T00:00:01') | + \ let s:filepath = matchstr(s:output, 'path:\zs[^\n]*') | + \ execute 'edit ' . s:filepath | + \ if getline(1) =~ '^\s*$' | + \ let s:weekFmt = s:startOfWeek == 0 ? '%U' : '%V' | + \ let s:yearWeekStr = strftime('%G: Week' . s:weekFmt, s:weekBegEpoch) | + \ let s:weekBegStr = strftime('%a %b %d', s:weekBegEpoch) | + \ let s:weekEndStr = strftime('%a %b %d', s:weekBegEpoch + (6 * 86400)) | + \ let s:title = printf('# %s (%s - %s)', s:yearWeekStr, s:weekBegStr, s:weekEndStr) | + \ call setline(1, s:title) | + \ endif + +nnoremap nn :NotesiumNew +nnoremap nd :NotesiumDaily +nnoremap nw :NotesiumWeekly +nnoremap nl :NotesiumList --prefix=label --sort=alpha --color +nnoremap nm :NotesiumList --prefix=mtime --sort=mtime --color +nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006-01 +nnoremap nb :NotesiumLinks --incoming =expand("%:t") +nnoremap nk :NotesiumLinks --color =expand("%:t") +nnoremap ns :NotesiumSearch --prefix=title --color +nnoremap nW :NotesiumWeb + +" overrides for journal +if $NOTESIUM_DIR =~ '**/journal/*' + nnoremap nl :NotesiumList --prefix=label --sort=mtime --color +endif From 42cb624496809231340985ecaa9b4ab11cf845a4 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 12:53:30 +0200 Subject: [PATCH 11/44] vim: initial finder and supporting callback functions --- vim/notesium.vim | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/vim/notesium.vim b/vim/notesium.vim index 3814553..31b29c7 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -1,6 +1,84 @@ +" vim:foldmethod=marker + let $NOTESIUM_DIR = trim(system("notesium home")) let $NOTESIUM_WEEKSTART = 1 "0 Sunday, 1 Monday, ... +" Notesium finder {{{1 +" ---------------------------------------------------------------------------- + +function! notesium#finder(config) abort + " Prepare command + let l:cmd = 'notesium finder ' . get(a:config, 'options', '') + let l:cmd .= ' -- ' . get(a:config, 'input', '') + + " Set window dimensions + let l:width = float2nr(&columns * get(a:config['window'], 'width', 0.85)) + let l:height = float2nr(&lines * get(a:config['window'], 'height', 0.85)) + let l:opts = { + \ 'relative': 'editor', + \ 'style': 'minimal', + \ 'row': (&lines - l:height) / 2, + \ 'col': (&columns - l:width) / 2, + \ 'width': l:width, + \ 'height': l:height } + + " Create buffer and floating window + highlight link NormalFloat Normal + let l:buf = nvim_create_buf(v:false, v:true) + let l:win = nvim_open_win(l:buf, v:true, l:opts) + + " Make sure we're in normal mode and start finder + call feedkeys("\", 'n') + call termopen(l:cmd, { + \ 'on_exit': { + \ job_id, exit_code, _ -> + \ notesium#finder_finalize(exit_code, l:buf, a:config['callback']) }}) + + " Focus the terminal and switch to insert mode + call nvim_set_current_win(l:win) + call feedkeys('i', 'n') +endfunction + +function! notesium#finder_finalize(exit_code, buf, callback) abort + " Capture buffer output, cleanup and validate + let l:output = trim(join(getbufline(a:buf, 1, '$'), "\n")) + if bufexists(a:buf) + execute 'bwipeout!' a:buf + endif + if empty(l:output) || a:exit_code == 130 + return + endif + if a:exit_code != 0 + echoerr printf("Finder error (%d): %s", a:exit_code, l:output) + return + endif + + " Parse output (filename:linenumber:text) and pass to callback + let l:parts = split(l:output, ':', 3) + if len(l:parts) < 3 + echoerr "Invalid finder output: " . l:output + return + endif + call a:callback(l:parts[0], l:parts[1], trim(l:parts[2])) +endfunction + +" Notesium finder callbacks {{{1 +" ---------------------------------------------------------------------------- + +function! notesium#finder_callback_editfile(filename, linenumber, text) abort + let l:file_path = fnamemodify($NOTESIUM_DIR, ':p') . a:filename + execute 'edit' fnameescape(l:file_path) + execute a:linenumber . 'normal! zz' +endfunction + +function! notesium#finder_callback_insertlink(filename, linenumber, text) abort + let l:link = printf("[%s](%s)", a:text, a:filename) + call feedkeys("a" . l:link, 'n') +endfunction + +" Notesium commands {{{1 +" ---------------------------------------------------------------------------- + autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ fzf#vim#complete({ \ 'source': 'notesium list --sort=mtime', \ 'options': '+s -d : --with-nth 3.. --prompt "NotesiumInsertLink> "', @@ -63,6 +141,9 @@ command! -bang -nargs=* NotesiumWeekly \ call setline(1, s:title) | \ endif +" Notesium mappings {{{1 +" ---------------------------------------------------------------------------- + nnoremap nn :NotesiumNew nnoremap nd :NotesiumDaily nnoremap nw :NotesiumWeekly From 597e4cf857d8e212057c8662649ef50631fe3b3e Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 12:58:58 +0200 Subject: [PATCH 12/44] vim: list - update to use finder --- vim/notesium.vim | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index 31b29c7..8152701 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -92,11 +92,12 @@ command! -bang NotesiumWeb \ let s:options = "--stop-on-idle --open-browser" | \ execute ":silent !nohup notesium web ".s:options." > /dev/null 2>&1 &" -command! -bang -nargs=* NotesiumList - \ let s:spec = {'dir': $NOTESIUM_DIR, 'options': '+s -d : --with-nth 3..'} | - \ call fzf#vim#grep( - \ 'notesium list '.join(map(split(), 'shellescape(v:val)'), ' '), 0, - \ &columns > 79 ? fzf#vim#with_preview(s:spec, 'right', 'ctrl-/') : s:spec, 0) +command! -nargs=* NotesiumList + \ call notesium#finder({ + \ 'input': 'list ' . join(map(split(), 'shellescape(v:val)'), ' '), + \ 'options': '--prompt=NotesiumList' . (&columns > 79 ? ' --preview' : ''), + \ 'callback': function('notesium#finder_callback_editfile'), + \ 'window': {'width': 0.85, 'height': 0.85} }) command! -bang -nargs=* NotesiumLinks \ let s:spec = {'dir': $NOTESIUM_DIR, 'options': '-d : --with-nth 3..'} | From db08b531174dce7d5b0e0612816833563fa79b55 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 13:08:16 +0200 Subject: [PATCH 13/44] vim: insertlink - update to use finder --- vim/notesium.vim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index 8152701..11ebaee 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -79,11 +79,11 @@ endfunction " Notesium commands {{{1 " ---------------------------------------------------------------------------- -autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ fzf#vim#complete({ - \ 'source': 'notesium list --sort=mtime', - \ 'options': '+s -d : --with-nth 3.. --prompt "NotesiumInsertLink> "', - \ 'reducer': {l->"[". split(l[0],':1: ')[1] ."](".split(l[0],':')[0].")"}, - \ 'window': {'width': 0.5, 'height': 0.5}}) +autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ notesium#finder({ + \ 'input': 'list --sort=mtime', + \ 'options': '--prompt=NotesiumInsertLink', + \ 'callback': function('notesium#finder_callback_insertlink'), + \ 'window': {'width': 0.5, 'height': 0.5} }) command! -bang NotesiumNew \ execute ":e" system("notesium new") From d78789b1f1061e5d1f722495a31294bd3b59c0f8 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 13:11:01 +0200 Subject: [PATCH 14/44] vim: insertlink - localize autocmd to buffer --- vim/notesium.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index 11ebaee..34fddcc 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -79,7 +79,7 @@ endfunction " Notesium commands {{{1 " ---------------------------------------------------------------------------- -autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ notesium#finder({ +autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ notesium#finder({ \ 'input': 'list --sort=mtime', \ 'options': '--prompt=NotesiumInsertLink', \ 'callback': function('notesium#finder_callback_insertlink'), From 8af687e04230c76573a085a0f514c4072e83aba7 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 13:15:33 +0200 Subject: [PATCH 15/44] vim: insertlink - split out command for customizability --- vim/notesium.vim | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index 34fddcc..a6b99c0 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -79,12 +79,6 @@ endfunction " Notesium commands {{{1 " ---------------------------------------------------------------------------- -autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ notesium#finder({ - \ 'input': 'list --sort=mtime', - \ 'options': '--prompt=NotesiumInsertLink', - \ 'callback': function('notesium#finder_callback_insertlink'), - \ 'window': {'width': 0.5, 'height': 0.5} }) - command! -bang NotesiumNew \ execute ":e" system("notesium new") @@ -92,6 +86,13 @@ command! -bang NotesiumWeb \ let s:options = "--stop-on-idle --open-browser" | \ execute ":silent !nohup notesium web ".s:options." > /dev/null 2>&1 &" +command! -nargs=* NotesiumInsertLink + \ call notesium#finder({ + \ 'input': 'list ' . join(map(split(), 'shellescape(v:val)'), ' '), + \ 'options': '--prompt=NotesiumInsertLink', + \ 'callback': function('notesium#finder_callback_insertlink'), + \ 'window': {'width': 0.5, 'height': 0.5} }) + command! -nargs=* NotesiumList \ call notesium#finder({ \ 'input': 'list ' . join(map(split(), 'shellescape(v:val)'), ' '), @@ -145,6 +146,7 @@ command! -bang -nargs=* NotesiumWeekly " Notesium mappings {{{1 " ---------------------------------------------------------------------------- +autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ execute(':NotesiumInsertLink --sort=mtime') nnoremap nn :NotesiumNew nnoremap nd :NotesiumDaily nnoremap nw :NotesiumWeekly From b130c77f099f6bbc7317d5adbc07c3de8f0422fb Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 13:19:35 +0200 Subject: [PATCH 16/44] vim: search - update to use finder --- vim/notesium.vim | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index a6b99c0..bfd0f0a 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -106,11 +106,12 @@ command! -bang -nargs=* NotesiumLinks \ 'notesium links '.join(map(split(), 'shellescape(v:val)'), ' '), 0, \ &columns > 79 ? fzf#vim#with_preview(s:spec, 'right', 'ctrl-/') : s:spec, 0) -command! -bang -nargs=* NotesiumSearch - \ let s:spec = {'dir': $NOTESIUM_DIR, 'options': '-d : --with-nth 3..'} | - \ call fzf#vim#grep( - \ 'notesium lines '.join(map(split(), 'shellescape(v:val)'), ' '), 0, - \ &columns > 79 ? fzf#vim#with_preview(s:spec, 'right', 'ctrl-/') : s:spec, 0) +command! -nargs=* NotesiumSearch + \ call notesium#finder({ + \ 'input': 'lines ' . join(map(split(), 'shellescape(v:val)'), ' '), + \ 'options': '--prompt=NotesiumSearch' . (&columns > 79 ? ' --preview' : ''), + \ 'callback': function('notesium#finder_callback_editfile'), + \ 'window': {'width': 0.85, 'height': 0.85} }) command! -bang -nargs=* NotesiumDaily \ let s:cdate = empty() ? strftime('%Y-%m-%d') : | From 53f136c31913816d398fa52db9919d257b91e8f0 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 13:21:22 +0200 Subject: [PATCH 17/44] vim: search - rename to lines for consistency --- vim/notesium.vim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index bfd0f0a..08e5568 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -106,10 +106,10 @@ command! -bang -nargs=* NotesiumLinks \ 'notesium links '.join(map(split(), 'shellescape(v:val)'), ' '), 0, \ &columns > 79 ? fzf#vim#with_preview(s:spec, 'right', 'ctrl-/') : s:spec, 0) -command! -nargs=* NotesiumSearch +command! -nargs=* NotesiumLines \ call notesium#finder({ \ 'input': 'lines ' . join(map(split(), 'shellescape(v:val)'), ' '), - \ 'options': '--prompt=NotesiumSearch' . (&columns > 79 ? ' --preview' : ''), + \ 'options': '--prompt=NotesiumLines' . (&columns > 79 ? ' --preview' : ''), \ 'callback': function('notesium#finder_callback_editfile'), \ 'window': {'width': 0.85, 'height': 0.85} }) @@ -156,7 +156,7 @@ nnoremap nm :NotesiumList --prefix=mtime --sort=mtime --color nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006-01 nnoremap nb :NotesiumLinks --incoming =expand("%:t") nnoremap nk :NotesiumLinks --color =expand("%:t") -nnoremap ns :NotesiumSearch --prefix=title --color +nnoremap ns :NotesiumLines --prefix=title --color nnoremap nW :NotesiumWeb " overrides for journal From feed14ac7dc66e2a1c0864b46b5b61f75f7074a4 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 13:24:53 +0200 Subject: [PATCH 18/44] vim: links - update to use finder --- vim/notesium.vim | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index 08e5568..2a2874b 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -100,11 +100,12 @@ command! -nargs=* NotesiumList \ 'callback': function('notesium#finder_callback_editfile'), \ 'window': {'width': 0.85, 'height': 0.85} }) -command! -bang -nargs=* NotesiumLinks - \ let s:spec = {'dir': $NOTESIUM_DIR, 'options': '-d : --with-nth 3..'} | - \ call fzf#vim#grep( - \ 'notesium links '.join(map(split(), 'shellescape(v:val)'), ' '), 0, - \ &columns > 79 ? fzf#vim#with_preview(s:spec, 'right', 'ctrl-/') : s:spec, 0) +command! -nargs=* NotesiumLinks + \ call notesium#finder({ + \ 'input': 'links ' . join(map(split(), 'shellescape(v:val)'), ' '), + \ 'options': '--prompt=NotesiumLinks' . (&columns > 79 ? ' --preview' : ''), + \ 'callback': function('notesium#finder_callback_editfile'), + \ 'window': {'width': 0.85, 'height': 0.85} }) command! -nargs=* NotesiumLines \ call notesium#finder({ From 3c8df4fb6ba66f2a96eae71ba0103adf0c67b831 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 13:36:48 +0200 Subject: [PATCH 19/44] vim: links - move filename enumeration to command, append if note --- vim/notesium.vim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index 2a2874b..db6c857 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -101,8 +101,10 @@ command! -nargs=* NotesiumList \ 'window': {'width': 0.85, 'height': 0.85} }) command! -nargs=* NotesiumLinks + \ let s:is_note = expand("%:t") =~# '^[0-9a-f]\{8\}\.md$' | + \ let s:args = . (s:is_note ? ' ' . expand("%:t") : '') | \ call notesium#finder({ - \ 'input': 'links ' . join(map(split(), 'shellescape(v:val)'), ' '), + \ 'input': 'links ' . join(map(split(s:args), 'shellescape(v:val)'), ' '), \ 'options': '--prompt=NotesiumLinks' . (&columns > 79 ? ' --preview' : ''), \ 'callback': function('notesium#finder_callback_editfile'), \ 'window': {'width': 0.85, 'height': 0.85} }) @@ -155,8 +157,7 @@ nnoremap nw :NotesiumWeekly nnoremap nl :NotesiumList --prefix=label --sort=alpha --color nnoremap nm :NotesiumList --prefix=mtime --sort=mtime --color nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006-01 -nnoremap nb :NotesiumLinks --incoming =expand("%:t") -nnoremap nk :NotesiumLinks --color =expand("%:t") +nnoremap nk :NotesiumLinks --color nnoremap ns :NotesiumLines --prefix=title --color nnoremap nW :NotesiumWeb From 841f0ef0c4fe21559b1a85fb6403225f6304ad33 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 13:41:20 +0200 Subject: [PATCH 20/44] vim: links - only append note filename if bang specified and if note --- vim/notesium.vim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index db6c857..bffebd9 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -100,9 +100,10 @@ command! -nargs=* NotesiumList \ 'callback': function('notesium#finder_callback_editfile'), \ 'window': {'width': 0.85, 'height': 0.85} }) -command! -nargs=* NotesiumLinks +command! -bang -nargs=* NotesiumLinks \ let s:is_note = expand("%:t") =~# '^[0-9a-f]\{8\}\.md$' | - \ let s:args = . (s:is_note ? ' ' . expand("%:t") : '') | + \ let s:filename = ("" == "!" && s:is_note) ? expand("%:t") : '' | + \ let s:args = . (!empty(s:filename) ? ' ' . s:filename : '') | \ call notesium#finder({ \ 'input': 'links ' . join(map(split(s:args), 'shellescape(v:val)'), ' '), \ 'options': '--prompt=NotesiumLinks' . (&columns > 79 ? ' --preview' : ''), @@ -157,7 +158,7 @@ nnoremap nw :NotesiumWeekly nnoremap nl :NotesiumList --prefix=label --sort=alpha --color nnoremap nm :NotesiumList --prefix=mtime --sort=mtime --color nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006-01 -nnoremap nk :NotesiumLinks --color +nnoremap nk :NotesiumLinks! --color nnoremap ns :NotesiumLines --prefix=title --color nnoremap nW :NotesiumWeb From 783f0da49ec00bc4bfba2d2f4dbad7e6aad5ea9d Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 13:52:21 +0200 Subject: [PATCH 21/44] vim: config - replace NOTESIUM_WEEKSTART int with g:notesium_weekstart string --- vim/notesium.vim | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index bffebd9..f83d0bb 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -1,7 +1,10 @@ " vim:foldmethod=marker let $NOTESIUM_DIR = trim(system("notesium home")) -let $NOTESIUM_WEEKSTART = 1 "0 Sunday, 1 Monday, ... + +if !exists('g:notesium_weekstart') + let g:notesium_weekstart = 'monday' +endif " Notesium finder {{{1 " ---------------------------------------------------------------------------- @@ -127,12 +130,16 @@ command! -bang -nargs=* NotesiumDaily \ call setline(1, '# ' . strftime('%b %d, %Y (%A)', s:epoch)) | \ endif -command! -bang -nargs=* NotesiumWeekly +command! -nargs=* NotesiumWeekly + \ let s:daysMap = {'sunday': 0, 'monday': 1, 'tuesday': 2,'wednesday': 3, 'thursday': 4, 'friday': 5, 'saturday': 6} | + \ let s:startOfWeek = get(s:daysMap, g:notesium_weekstart, -1) | + \ if s:startOfWeek == -1 | + \ throw "Invalid g:notesium_weekstart: " . g:notesium_weekstart | + \ endif | \ let s:date = empty() ? strftime('%Y-%m-%d') : | \ let s:output = system('notesium new --verbose --ctime='.s:date.'T00:00:01') | \ let s:epoch = str2nr(matchstr(s:output, 'epoch:\zs[^\n]*')) | \ let s:day = strftime('%u', s:epoch) | - \ let s:startOfWeek = empty($NOTESIUM_WEEKSTART) ? 1 : $NOTESIUM_WEEKSTART | \ let s:diff = (s:day - s:startOfWeek + 7) % 7 | \ let s:weekBegEpoch = s:epoch - (s:diff * 86400) | \ let s:weekBegDate = strftime('%Y-%m-%d', s:weekBegEpoch) | From 00299a107ef7f83001cb716d21691857435b9f50 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 13:57:39 +0200 Subject: [PATCH 22/44] vim: config - adhere to g:notesium_weekstart in list ctime prefix date --- vim/notesium.vim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index f83d0bb..b96572b 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -164,12 +164,16 @@ nnoremap nd :NotesiumDaily nnoremap nw :NotesiumWeekly nnoremap nl :NotesiumList --prefix=label --sort=alpha --color nnoremap nm :NotesiumList --prefix=mtime --sort=mtime --color -nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006-01 +nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006/Week%V nnoremap nk :NotesiumLinks! --color nnoremap ns :NotesiumLines --prefix=title --color nnoremap nW :NotesiumWeb -" overrides for journal +" overrides +if g:notesium_weekstart ==# 'sunday' + nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006/Week%U +endif + if $NOTESIUM_DIR =~ '**/journal/*' nnoremap nl :NotesiumList --prefix=label --sort=mtime --color endif From b6239f0fcf21f275239f3042b265591334e440e8 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 14:05:50 +0200 Subject: [PATCH 23/44] vim: config - add g:notesium_mapping bool to disable default mappings --- vim/notesium.vim | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index b96572b..a457eb2 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -2,6 +2,10 @@ let $NOTESIUM_DIR = trim(system("notesium home")) +if !exists('g:notesium_mappings') + let g:notesium_mappings = 1 +endif + if !exists('g:notesium_weekstart') let g:notesium_weekstart = 'monday' endif @@ -158,22 +162,24 @@ command! -nargs=* NotesiumWeekly " Notesium mappings {{{1 " ---------------------------------------------------------------------------- -autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ execute(':NotesiumInsertLink --sort=mtime') -nnoremap nn :NotesiumNew -nnoremap nd :NotesiumDaily -nnoremap nw :NotesiumWeekly -nnoremap nl :NotesiumList --prefix=label --sort=alpha --color -nnoremap nm :NotesiumList --prefix=mtime --sort=mtime --color -nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006/Week%V -nnoremap nk :NotesiumLinks! --color -nnoremap ns :NotesiumLines --prefix=title --color -nnoremap nW :NotesiumWeb - -" overrides -if g:notesium_weekstart ==# 'sunday' - nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006/Week%U -endif +if g:notesium_mappings + autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ execute(':NotesiumInsertLink --sort=mtime') + nnoremap nn :NotesiumNew + nnoremap nd :NotesiumDaily + nnoremap nw :NotesiumWeekly + nnoremap nl :NotesiumList --prefix=label --sort=alpha --color + nnoremap nm :NotesiumList --prefix=mtime --sort=mtime --color + nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006/Week%V + nnoremap nk :NotesiumLinks! --color + nnoremap ns :NotesiumLines --prefix=title --color + nnoremap nW :NotesiumWeb + + " overrides + if g:notesium_weekstart ==# 'sunday' + nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006/Week%U + endif -if $NOTESIUM_DIR =~ '**/journal/*' - nnoremap nl :NotesiumList --prefix=label --sort=mtime --color + if $NOTESIUM_DIR =~ '**/journal/*' + nnoremap nl :NotesiumList --prefix=label --sort=mtime --color + endif endif From 56975765dfadeb43731d5f77a2fdfceab3ebdfef Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 14:08:46 +0200 Subject: [PATCH 24/44] vim: removed unneeded command bangs --- vim/notesium.vim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index a457eb2..9ac096b 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -86,10 +86,10 @@ endfunction " Notesium commands {{{1 " ---------------------------------------------------------------------------- -command! -bang NotesiumNew +command! NotesiumNew \ execute ":e" system("notesium new") -command! -bang NotesiumWeb +command! NotesiumWeb \ let s:options = "--stop-on-idle --open-browser" | \ execute ":silent !nohup notesium web ".s:options." > /dev/null 2>&1 &" @@ -124,7 +124,7 @@ command! -nargs=* NotesiumLines \ 'callback': function('notesium#finder_callback_editfile'), \ 'window': {'width': 0.85, 'height': 0.85} }) -command! -bang -nargs=* NotesiumDaily +command! -nargs=* NotesiumDaily \ let s:cdate = empty() ? strftime('%Y-%m-%d') : | \ let s:output = system('notesium new --verbose --ctime='.s:cdate.'T00:00:00') | \ let s:filepath = matchstr(s:output, 'path:\zs[^\n]*') | From 284908cde922b138c65bf61a528c94c6a170b8ed Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 16:09:36 +0200 Subject: [PATCH 25/44] vim: web - add windows support via powershell --- vim/notesium.vim | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index 9ac096b..3f6f688 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -89,10 +89,6 @@ endfunction command! NotesiumNew \ execute ":e" system("notesium new") -command! NotesiumWeb - \ let s:options = "--stop-on-idle --open-browser" | - \ execute ":silent !nohup notesium web ".s:options." > /dev/null 2>&1 &" - command! -nargs=* NotesiumInsertLink \ call notesium#finder({ \ 'input': 'list ' . join(map(split(), 'shellescape(v:val)'), ' '), @@ -159,6 +155,16 @@ command! -nargs=* NotesiumWeekly \ call setline(1, s:title) | \ endif +command! NotesiumWeb + \ let s:args = "--stop-on-idle --open-browser" | + \ if has('unix') | + \ execute ":silent !nohup notesium web ".s:args." > /dev/null 2>&1 &" | + \ elseif has('win32') || has('win64') | + \ execute ":silent !powershell -Command \"Start-Process -NoNewWindow notesium -ArgumentList 'web ".s:args."'\"" | + \ else | + \ throw "Unsupported platform" | + \ endif + " Notesium mappings {{{1 " ---------------------------------------------------------------------------- From b23fef765126f0e7abdb4a92a19a78a174c07ba0 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 16:16:16 +0200 Subject: [PATCH 26/44] vim: web - support q-args, append required options if missing --- vim/notesium.vim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index 3f6f688..c44c04f 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -155,8 +155,10 @@ command! -nargs=* NotesiumWeekly \ call setline(1, s:title) | \ endif -command! NotesiumWeb - \ let s:args = "--stop-on-idle --open-browser" | +command! -nargs=* NotesiumWeb + \ let s:r_args = ["--stop-on-idle", "--open-browser"] | + \ let s:q_args = filter(split(), 'index(s:r_args, v:val) == -1') + s:r_args | + \ let s:args = join(map(s:q_args, 'shellescape(v:val)'), ' ') | \ if has('unix') | \ execute ":silent !nohup notesium web ".s:args." > /dev/null 2>&1 &" | \ elseif has('win32') || has('win64') | From a136246846e9d3b29b0e935e779918fc351c74f9 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 29 Jan 2025 16:49:22 +0200 Subject: [PATCH 27/44] vim: config - add g:notesium_bin to override default --- vim/notesium.vim | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index c44c04f..f4a23de 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -1,21 +1,28 @@ " vim:foldmethod=marker -let $NOTESIUM_DIR = trim(system("notesium home")) +" Notesium configuration {{{1 +" ---------------------------------------------------------------------------- -if !exists('g:notesium_mappings') +if !exists('g:notesium_mappings') || empty(g:notesium_mappings) let g:notesium_mappings = 1 endif -if !exists('g:notesium_weekstart') +if !exists('g:notesium_weekstart') || empty(g:notesium_weekstart) let g:notesium_weekstart = 'monday' endif +if !exists('g:notesium_bin') || empty(g:notesium_bin) + let g:notesium_bin = 'notesium' +endif + +let $NOTESIUM_DIR = trim(system(g:notesium_bin . ' home')) + " Notesium finder {{{1 " ---------------------------------------------------------------------------- function! notesium#finder(config) abort " Prepare command - let l:cmd = 'notesium finder ' . get(a:config, 'options', '') + let l:cmd = g:notesium_bin . ' finder ' . get(a:config, 'options', '') let l:cmd .= ' -- ' . get(a:config, 'input', '') " Set window dimensions @@ -87,7 +94,7 @@ endfunction " ---------------------------------------------------------------------------- command! NotesiumNew - \ execute ":e" system("notesium new") + \ execute ":e" system(g:notesium_bin . ' new') command! -nargs=* NotesiumInsertLink \ call notesium#finder({ @@ -122,7 +129,7 @@ command! -nargs=* NotesiumLines command! -nargs=* NotesiumDaily \ let s:cdate = empty() ? strftime('%Y-%m-%d') : | - \ let s:output = system('notesium new --verbose --ctime='.s:cdate.'T00:00:00') | + \ let s:output = system(g:notesium_bin.' new --verbose --ctime='.s:cdate.'T00:00:00') | \ let s:filepath = matchstr(s:output, 'path:\zs[^\n]*') | \ execute 'edit ' . s:filepath | \ if getline(1) =~ '^\s*$' | @@ -137,7 +144,7 @@ command! -nargs=* NotesiumWeekly \ throw "Invalid g:notesium_weekstart: " . g:notesium_weekstart | \ endif | \ let s:date = empty() ? strftime('%Y-%m-%d') : | - \ let s:output = system('notesium new --verbose --ctime='.s:date.'T00:00:01') | + \ let s:output = system(g:notesium_bin.' new --verbose --ctime='.s:date.'T00:00:01') | \ let s:epoch = str2nr(matchstr(s:output, 'epoch:\zs[^\n]*')) | \ let s:day = strftime('%u', s:epoch) | \ let s:diff = (s:day - s:startOfWeek + 7) % 7 | @@ -160,9 +167,9 @@ command! -nargs=* NotesiumWeb \ let s:q_args = filter(split(), 'index(s:r_args, v:val) == -1') + s:r_args | \ let s:args = join(map(s:q_args, 'shellescape(v:val)'), ' ') | \ if has('unix') | - \ execute ":silent !nohup notesium web ".s:args." > /dev/null 2>&1 &" | + \ execute ":silent !nohup ".g:notesium_bin." web ".s:args." > /dev/null 2>&1 &" | \ elseif has('win32') || has('win64') | - \ execute ":silent !powershell -Command \"Start-Process -NoNewWindow notesium -ArgumentList 'web ".s:args."'\"" | + \ execute ":silent !powershell -Command \"Start-Process -NoNewWindow ".g:notesium_bin." -ArgumentList 'web ".s:args."'\"" | \ else | \ throw "Unsupported platform" | \ endif From f633d81294db135d67fa9bec91fca610ebf2dc62 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Thu, 30 Jan 2025 15:07:21 +0200 Subject: [PATCH 28/44] finder: refactor output channel handling to prevent race condition --- finder.go | 31 +++++++++++++++++++++++++------ notesium.go | 14 ++++---------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/finder.go b/finder.go index 2f1c4d8..cf69924 100644 --- a/finder.go +++ b/finder.go @@ -16,20 +16,39 @@ func (cw *channelWriter) Write(p []byte) (n int, err error) { return len(p), nil } -func runFinder(inputChan chan string, outputChan chan string, opts []string) (int, error) { +func runFinder(inputChan chan string, opts []string) ([]string, int, error) { options, err := fzf.ParseOptions(false, opts) if err != nil { - return 2, fmt.Errorf("fzf error: %w", err) + return nil, 2, fmt.Errorf("fzf error: %w", err) } + outputChan := make(chan string) + resultChan := make(chan struct { + code int + err error + }, 1) + options.Input = inputChan options.Output = outputChan - code, err := fzf.Run(options) - if err != nil { - return code, fmt.Errorf("error running fzf: %w", err) + go func() { + code, runErr := fzf.Run(options) + close(outputChan) + + resultChan <- struct { + code int + err error + }{code, runErr} + + close(resultChan) + }() + + var lines []string + for line := range outputChan { + lines = append(lines, line) } - return code, nil + result := <-resultChan + return lines, result.code, result.err } diff --git a/notesium.go b/notesium.go index d9c0c2c..9bc46d2 100644 --- a/notesium.go +++ b/notesium.go @@ -376,8 +376,6 @@ func notesiumFinder(dir string, opts finderOptions) { } inputChan := make(chan string) - outputChan := make(chan string) - go func() { defer close(inputChan) writer := &channelWriter{ch: inputChan} @@ -394,17 +392,13 @@ func notesiumFinder(dir string, opts finderOptions) { } }() - go func() { - defer close(outputChan) - for output := range outputChan { - fmt.Printf(output) - } - }() - - code, err := runFinder(inputChan, outputChan, optsFzf) + results, code, err := runFinder(inputChan, optsFzf) if code != 0 && code != 130 && err != nil { fmt.Fprintf(os.Stderr, "Error running fzf: %v\n", err) } + for _, line := range results { + fmt.Print(line) + } os.Exit(code) } From 07aa3ca047238cd298b9cee9700c7f8d829c33d2 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Sun, 2 Feb 2025 12:08:19 +0200 Subject: [PATCH 29/44] vim: insertlink - ensure correct mode transition --- vim/notesium.vim | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index f4a23de..eb79037 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -41,8 +41,7 @@ function! notesium#finder(config) abort let l:buf = nvim_create_buf(v:false, v:true) let l:win = nvim_open_win(l:buf, v:true, l:opts) - " Make sure we're in normal mode and start finder - call feedkeys("\", 'n') + " Start the finder call termopen(l:cmd, { \ 'on_exit': { \ job_id, exit_code, _ -> @@ -87,7 +86,7 @@ endfunction function! notesium#finder_callback_insertlink(filename, linenumber, text) abort let l:link = printf("[%s](%s)", a:text, a:filename) - call feedkeys("a" . l:link, 'n') + call feedkeys((mode() == 'i' ? '' : 'a') . l:link, 'n') endfunction " Notesium commands {{{1 @@ -178,7 +177,7 @@ command! -nargs=* NotesiumWeb " ---------------------------------------------------------------------------- if g:notesium_mappings - autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ execute(':NotesiumInsertLink --sort=mtime') + autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ :NotesiumInsertLink --sort=mtime nnoremap nn :NotesiumNew nnoremap nd :NotesiumDaily nnoremap nw :NotesiumWeekly From 2fd11ba1e46a7eecad4bad6a22c5437b9e843cf0 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Sun, 2 Feb 2025 12:16:34 +0200 Subject: [PATCH 30/44] vim: config - refactor NOTESIUM_DIR retrieval into function --- vim/notesium.vim | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index eb79037..7ac514f 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -15,7 +15,16 @@ if !exists('g:notesium_bin') || empty(g:notesium_bin) let g:notesium_bin = 'notesium' endif -let $NOTESIUM_DIR = trim(system(g:notesium_bin . ' home')) +function! notesium#get_notesium_dir() abort + let l:output = systemlist(g:notesium_bin . ' home') + if empty(l:output) || v:shell_error + echoerr "Failed to get NOTESIUM_DIR - " . join(l:output, "\n") + return '' + endif + return l:output[0] +endfunction + +let $NOTESIUM_DIR = notesium#get_notesium_dir() " Notesium finder {{{1 " ---------------------------------------------------------------------------- From 885f65904d09ccf42e89ae349d3759005ed28257 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Sun, 2 Feb 2025 12:36:47 +0200 Subject: [PATCH 31/44] vim: finder - wrap finder functions in neovim check --- vim/notesium.vim | 115 +++++++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 53 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index 7ac514f..c625147 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -29,60 +29,69 @@ let $NOTESIUM_DIR = notesium#get_notesium_dir() " Notesium finder {{{1 " ---------------------------------------------------------------------------- -function! notesium#finder(config) abort - " Prepare command - let l:cmd = g:notesium_bin . ' finder ' . get(a:config, 'options', '') - let l:cmd .= ' -- ' . get(a:config, 'input', '') - - " Set window dimensions - let l:width = float2nr(&columns * get(a:config['window'], 'width', 0.85)) - let l:height = float2nr(&lines * get(a:config['window'], 'height', 0.85)) - let l:opts = { - \ 'relative': 'editor', - \ 'style': 'minimal', - \ 'row': (&lines - l:height) / 2, - \ 'col': (&columns - l:width) / 2, - \ 'width': l:width, - \ 'height': l:height } - - " Create buffer and floating window - highlight link NormalFloat Normal - let l:buf = nvim_create_buf(v:false, v:true) - let l:win = nvim_open_win(l:buf, v:true, l:opts) - - " Start the finder - call termopen(l:cmd, { - \ 'on_exit': { - \ job_id, exit_code, _ -> - \ notesium#finder_finalize(exit_code, l:buf, a:config['callback']) }}) - - " Focus the terminal and switch to insert mode - call nvim_set_current_win(l:win) - call feedkeys('i', 'n') -endfunction - -function! notesium#finder_finalize(exit_code, buf, callback) abort - " Capture buffer output, cleanup and validate - let l:output = trim(join(getbufline(a:buf, 1, '$'), "\n")) - if bufexists(a:buf) - execute 'bwipeout!' a:buf - endif - if empty(l:output) || a:exit_code == 130 - return - endif - if a:exit_code != 0 - echoerr printf("Finder error (%d): %s", a:exit_code, l:output) - return - endif +if has('nvim') + + function! notesium#finder(config) abort + " Prepare command + let l:cmd = g:notesium_bin . ' finder ' . get(a:config, 'options', '') + let l:cmd .= ' -- ' . get(a:config, 'input', '') + + " Set window dimensions + let l:width = float2nr(&columns * get(a:config['window'], 'width', 0.85)) + let l:height = float2nr(&lines * get(a:config['window'], 'height', 0.85)) + let l:opts = { + \ 'relative': 'editor', + \ 'style': 'minimal', + \ 'row': (&lines - l:height) / 2, + \ 'col': (&columns - l:width) / 2, + \ 'width': l:width, + \ 'height': l:height } + + " Create buffer and floating window + highlight link NormalFloat Normal + let l:buf = nvim_create_buf(v:false, v:true) + let l:win = nvim_open_win(l:buf, v:true, l:opts) + + " Start the finder + call termopen(l:cmd, { + \ 'on_exit': { + \ job_id, exit_code, _ -> + \ notesium#finder_finalize(exit_code, l:buf, a:config['callback']) }}) + + " Focus the terminal and switch to insert mode + call nvim_set_current_win(l:win) + call feedkeys('i', 'n') + endfunction + + function! notesium#finder_finalize(exit_code, buf, callback) abort + " Capture buffer output, cleanup and validate + let l:output = trim(join(getbufline(a:buf, 1, '$'), "\n")) + if bufexists(a:buf) + execute 'bwipeout!' a:buf + endif + if empty(l:output) || a:exit_code == 130 + return + endif + if a:exit_code != 0 + echoerr printf("Finder error (%d): %s", a:exit_code, l:output) + return + endif + + " Parse output (filename:linenumber:text) and pass to callback + let l:parts = split(l:output, ':', 3) + if len(l:parts) < 3 + echoerr "Invalid finder output: " . l:output + return + endif + call a:callback(l:parts[0], l:parts[1], trim(l:parts[2])) + endfunction + +else + + echoerr "Error: Notesium currently requires Neovim" + finish - " Parse output (filename:linenumber:text) and pass to callback - let l:parts = split(l:output, ':', 3) - if len(l:parts) < 3 - echoerr "Invalid finder output: " . l:output - return - endif - call a:callback(l:parts[0], l:parts[1], trim(l:parts[2])) -endfunction +endif " Notesium finder callbacks {{{1 " ---------------------------------------------------------------------------- From a49a2eeec2b0b9e89b272dc896ee72172e81eb46 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Sun, 2 Feb 2025 12:38:43 +0200 Subject: [PATCH 32/44] vim: finder - add fallback implementation for vim --- vim/notesium.vim | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index c625147..9080b10 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -88,8 +88,28 @@ if has('nvim') else - echoerr "Error: Notesium currently requires Neovim" - finish + function! notesium#finder(config) abort + " Prepare the command + let l:cmd = g:notesium_bin . ' finder ' . get(a:config, 'options', '') + let l:cmd .= ' -- ' . get(a:config, 'input', '') + + " Start the finder + let l:output = system(l:cmd) + redraw! + if empty(l:output) || v:shell_error + return + endif + + " Parse output (filename:linenumber:text) and pass to callback + let l:parts = split(l:output, ':', 3) + if len(l:parts) < 3 + echoerr "Invalid finder output: " . l:output + return + endif + + let l:text = substitute(l:parts[2], '^\s*\(.\{-}\)\s*\n*$', '\1', '') + silent! call a:config['callback'](l:parts[0], l:parts[1], l:text) + endfunction endif From 41dacd0b7f449a665d75a3f93f00c95e3870e4dd Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Sun, 2 Feb 2025 12:51:10 +0200 Subject: [PATCH 33/44] vim: insertlink - adjust finder window size based on terminal width --- vim/notesium.vim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index 9080b10..4bbf3ae 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -138,7 +138,10 @@ command! -nargs=* NotesiumInsertLink \ 'input': 'list ' . join(map(split(), 'shellescape(v:val)'), ' '), \ 'options': '--prompt=NotesiumInsertLink', \ 'callback': function('notesium#finder_callback_insertlink'), - \ 'window': {'width': 0.5, 'height': 0.5} }) + \ 'window': { + \ 'width': (&columns > 79 ? 0.5 : 0.85), + \ 'height': (&columns > 79 ? 0.5 : 0.85) + \ } }) command! -nargs=* NotesiumList \ call notesium#finder({ From 935a6c30e36b50c9930d346df8f8d09c1c1dcca8 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Sun, 2 Feb 2025 13:09:22 +0200 Subject: [PATCH 34/44] vim: config - replace hardcoded window sizes with g:notesium_window --- vim/notesium.vim | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/vim/notesium.vim b/vim/notesium.vim index 4bbf3ae..70c49d0 100644 --- a/vim/notesium.vim +++ b/vim/notesium.vim @@ -15,6 +15,14 @@ if !exists('g:notesium_bin') || empty(g:notesium_bin) let g:notesium_bin = 'notesium' endif +if !exists('g:notesium_window') || empty(g:notesium_window) + let g:notesium_window = {'width': 0.85, 'height': 0.85} +endif + +if !exists('g:notesium_window_small') || empty(g:notesium_window_small) + let g:notesium_window_small = {'width': 0.5, 'height': 0.5} +endif + function! notesium#get_notesium_dir() abort let l:output = systemlist(g:notesium_bin . ' home') if empty(l:output) || v:shell_error @@ -37,8 +45,8 @@ if has('nvim') let l:cmd .= ' -- ' . get(a:config, 'input', '') " Set window dimensions - let l:width = float2nr(&columns * get(a:config['window'], 'width', 0.85)) - let l:height = float2nr(&lines * get(a:config['window'], 'height', 0.85)) + let l:width = float2nr(&columns * get(a:config['window'], 'width', 1)) + let l:height = float2nr(&lines * get(a:config['window'], 'height', 1)) let l:opts = { \ 'relative': 'editor', \ 'style': 'minimal', @@ -138,17 +146,14 @@ command! -nargs=* NotesiumInsertLink \ 'input': 'list ' . join(map(split(), 'shellescape(v:val)'), ' '), \ 'options': '--prompt=NotesiumInsertLink', \ 'callback': function('notesium#finder_callback_insertlink'), - \ 'window': { - \ 'width': (&columns > 79 ? 0.5 : 0.85), - \ 'height': (&columns > 79 ? 0.5 : 0.85) - \ } }) + \ 'window': (&columns > 79 ? g:notesium_window_small : g:notesium_window) }) command! -nargs=* NotesiumList \ call notesium#finder({ \ 'input': 'list ' . join(map(split(), 'shellescape(v:val)'), ' '), \ 'options': '--prompt=NotesiumList' . (&columns > 79 ? ' --preview' : ''), \ 'callback': function('notesium#finder_callback_editfile'), - \ 'window': {'width': 0.85, 'height': 0.85} }) + \ 'window': g:notesium_window }) command! -bang -nargs=* NotesiumLinks \ let s:is_note = expand("%:t") =~# '^[0-9a-f]\{8\}\.md$' | @@ -158,14 +163,14 @@ command! -bang -nargs=* NotesiumLinks \ 'input': 'links ' . join(map(split(s:args), 'shellescape(v:val)'), ' '), \ 'options': '--prompt=NotesiumLinks' . (&columns > 79 ? ' --preview' : ''), \ 'callback': function('notesium#finder_callback_editfile'), - \ 'window': {'width': 0.85, 'height': 0.85} }) + \ 'window': g:notesium_window }) command! -nargs=* NotesiumLines \ call notesium#finder({ \ 'input': 'lines ' . join(map(split(), 'shellescape(v:val)'), ' '), \ 'options': '--prompt=NotesiumLines' . (&columns > 79 ? ' --preview' : ''), \ 'callback': function('notesium#finder_callback_editfile'), - \ 'window': {'width': 0.85, 'height': 0.85} }) + \ 'window': g:notesium_window }) command! -nargs=* NotesiumDaily \ let s:cdate = empty() ? strftime('%Y-%m-%d') : | From 02127c97f9fdc46f8ad05f90ab947eecbfd365f0 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Tue, 4 Feb 2025 15:18:21 +0200 Subject: [PATCH 35/44] vim: restructure for conventional plugin layout --- vim/{ => plugin}/notesium.vim | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename vim/{ => plugin}/notesium.vim (100%) diff --git a/vim/notesium.vim b/vim/plugin/notesium.vim similarity index 100% rename from vim/notesium.vim rename to vim/plugin/notesium.vim From 915dc3443765f2a7571d7db82a7c0ea446f8cda4 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Tue, 4 Feb 2025 15:55:31 +0200 Subject: [PATCH 36/44] readme: replace vim example integration with vim plugin --- README.md | 202 +++++++++++++++++++----------------------------------- 1 file changed, 71 insertions(+), 131 deletions(-) diff --git a/README.md b/README.md index dc8417b..b8d1e37 100644 --- a/README.md +++ b/README.md @@ -55,12 +55,14 @@ It aspires and is designed to: - [Section folding](#section-folding) - [Syntax highlighting and concealment](#syntax-highlighting-and-concealment) - [Vim](#vim) - - [Example integration](#example-integration) + - [Setup](#setup) + - [Configuration](#configuration) + - [Commands](#commands) - [Keybindings](#keybindings-1) - - [Fzf search syntax](#fzf-search-syntax) + - [Finder search syntax](#finder-search-syntax) - [Related Vim settings](#related-vim-settings) - [Custom URI protocol](#custom-uri-protocol) - - [Example integration](#example-integration-1) + - [Example integration](#example-integration) - [Handler registration](#handler-registration) - [Design assumptions and rationale](#design-assumptions-and-rationale) - [Filenames are 8 hexidecimal digits](#filenames-are-8-hexidecimal-digits) @@ -511,130 +513,84 @@ of the formatting characters is supported. ## Vim -Notesium does not supply a Vim plugin, it is up to the user to write -their own Vim commands and configure keybindings. That said, below are -some fairly generic commands, with preferences configured in the -keybindings. +Notesium provides a Vim/Neovim plugin that integrates with the Notesium +CLI, particularily the `finder` (interactive filter selection TUI with +syntax highlighted preview). -- Dependencies: [fzf](https://github.com/junegunn/fzf) and [fzf.vim](https://github.com/junegunn/fzf.vim). -- Recommended: [bat](https://github.com/sharkdp/bat) for syntax highlighting in the preview. +- Depends: [Notesium CLI](#cli) (0.6.4 or above) - Recommended: [vim-markdown](https://github.com/preservim/vim-markdown) for general markdown goodness. - Recommended: [goyo.vim](https://github.com/junegunn/goyo.vim) and [lightlight.vim](https://github.com/junegunn/limelight.vim) for distraction free writing. -### Example integration +### Setup + +To install the plugin, add the repository to your plugin manager and +point its runtime path to the `vim` directory. For example: ```vim -let $NOTESIUM_DIR = trim(system("notesium home")) -let $NOTESIUM_WEEKSTART = 1 "0 Sunday, 1 Monday, ... - -autocmd BufRead,BufNewFile $NOTESIUM_DIR/*.md inoremap [[ fzf#vim#complete({ - \ 'source': 'notesium list --sort=mtime', - \ 'options': '+s -d : --with-nth 3.. --prompt "NotesiumInsertLink> "', - \ 'reducer': {l->"[". split(l[0],':1: ')[1] ."](".split(l[0],':')[0].")"}, - \ 'window': {'width': 0.5, 'height': 0.5}}) - -command! -bang NotesiumNew - \ execute ":e" system("notesium new") - -command! -bang NotesiumWeb - \ let s:options = "--stop-on-idle --open-browser" | - \ execute ":silent !nohup notesium web ".s:options." > /dev/null 2>&1 &" - -command! -bang -nargs=* NotesiumList - \ let s:spec = {'dir': $NOTESIUM_DIR, 'options': '+s -d : --with-nth 3..'} | - \ call fzf#vim#grep( - \ 'notesium list '.join(map(split(), 'shellescape(v:val)'), ' '), 0, - \ &columns > 79 ? fzf#vim#with_preview(s:spec, 'right', 'ctrl-/') : s:spec, 0) - -command! -bang -nargs=* NotesiumLinks - \ let s:spec = {'dir': $NOTESIUM_DIR, 'options': '-d : --with-nth 3..'} | - \ call fzf#vim#grep( - \ 'notesium links '.join(map(split(), 'shellescape(v:val)'), ' '), 0, - \ &columns > 79 ? fzf#vim#with_preview(s:spec, 'right', 'ctrl-/') : s:spec, 0) - -command! -bang -nargs=* NotesiumSearch - \ let s:spec = {'dir': $NOTESIUM_DIR, 'options': '-d : --with-nth 3..'} | - \ call fzf#vim#grep( - \ 'notesium lines '.join(map(split(), 'shellescape(v:val)'), ' '), 0, - \ &columns > 79 ? fzf#vim#with_preview(s:spec, 'right', 'ctrl-/') : s:spec, 0) - -command! -bang -nargs=* NotesiumDaily - \ let s:cdate = empty() ? strftime('%Y-%m-%d') : | - \ let s:output = system('notesium new --verbose --ctime='.s:cdate.'T00:00:00') | - \ let s:filepath = matchstr(s:output, 'path:\zs[^\n]*') | - \ execute 'edit ' . s:filepath | - \ if getline(1) =~ '^\s*$' | - \ let s:epoch = matchstr(s:output, 'epoch:\zs[^\n]*') | - \ call setline(1, '# ' . strftime('%b %d, %Y (%A)', s:epoch)) | - \ endif - -command! -bang -nargs=* NotesiumWeekly - \ let s:date = empty() ? strftime('%Y-%m-%d') : | - \ let s:output = system('notesium new --verbose --ctime='.s:date.'T00:00:01') | - \ let s:epoch = str2nr(matchstr(s:output, 'epoch:\zs[^\n]*')) | - \ let s:day = strftime('%u', s:epoch) | - \ let s:startOfWeek = empty($NOTESIUM_WEEKSTART) ? 1 : $NOTESIUM_WEEKSTART | - \ let s:diff = (s:day - s:startOfWeek + 7) % 7 | - \ let s:weekBegEpoch = s:epoch - (s:diff * 86400) | - \ let s:weekBegDate = strftime('%Y-%m-%d', s:weekBegEpoch) | - \ let s:output = system('notesium new --verbose --ctime='.s:weekBegDate.'T00:00:01') | - \ let s:filepath = matchstr(s:output, 'path:\zs[^\n]*') | - \ execute 'edit ' . s:filepath | - \ if getline(1) =~ '^\s*$' | - \ let s:weekFmt = s:startOfWeek == 0 ? '%U' : '%V' | - \ let s:yearWeekStr = strftime('%G: Week' . s:weekFmt, s:weekBegEpoch) | - \ let s:weekBegStr = strftime('%a %b %d', s:weekBegEpoch) | - \ let s:weekEndStr = strftime('%a %b %d', s:weekBegEpoch + (6 * 86400)) | - \ let s:title = printf('# %s (%s - %s)', s:yearWeekStr, s:weekBegStr, s:weekEndStr) | - \ call setline(1, s:title) | - \ endif - -nnoremap nn :NotesiumNew -nnoremap nd :NotesiumDaily -nnoremap nw :NotesiumWeekly -nnoremap nl :NotesiumList --prefix=label --sort=alpha --color -nnoremap nm :NotesiumList --prefix=mtime --sort=mtime --color -nnoremap nc :NotesiumList --prefix=ctime --sort=ctime --color --date=2006-01 -nnoremap nb :NotesiumLinks --incoming =expand("%:t") -nnoremap nk :NotesiumLinks --color =expand("%:t") -nnoremap ns :NotesiumSearch --prefix=title --color -nnoremap nW :NotesiumWeb - -" overrides for journal -if $NOTESIUM_DIR =~ '**/journal/*' - nnoremap nl :NotesiumList --prefix=label --sort=mtime --color -endif +" init.vim or .vimrc +Plug 'alonswartz/notesium', { 'rtp': 'vim' } ``` +```lua +-- init.lua +Plug('alonswartz/notesium', { ['rtp'] = 'vim' }) +``` + +### Configuration + +| Setting | Type | Comment +| ------- | ---- | ------- +| `g:notesium_bin` | `string` | Binary name or path (default: `notesium`) +| `g:notesium_mappings` | `boolean` | Enable(1) or disable(0) mappings (default: `1`) +| `g:notesium_weekstart` | `string` | First day of the week (default: `monday`) +| `g:notesium_window` | `dict` | Finder (`{'width': 0.85, 'height': 0.85}`) +| `g:notesium_window_small` | `dict` | InsertLink Finder (`{'width': 0.5, 'height': 0.5}`) + +Note: `g:notesium_bin` must be set prior to the plugin being sourced. + +### Commands + +| Command | Arguments | Comment +| ------- | --------- | ------- +| `:NotesiumNew` | None | Open new note for editing +| `:NotesiumDaily` | `YYYY-MM-DD` | Open new or existing daily note +| `:NotesiumWeekly` | `YYYY-MM-DD` | Open new or existing weekly note +| `:NotesiumList` | list --opts | Finder: Notes list +| `:NotesiumLinks!` | links --opts | Finder: Links related to active note +| `:NotesiumLinks` | links --opts | Finder: All notes links +| `:NotesiumLines` | lines --opts | Finder: All notes lines +| `:NotesiumInsertLink` | list --opts | Finder: Insert selected note link +| `:NotesiumWeb` | web --opts | Starts web server + +Note: `NotesiumWeekly` depends on `g:notesium_weekstart`. + ### Keybindings -| Mode | Binding | Comment -| ---- | ------- | ------- -| insert | `[[` | Opens note list, insert selection as markdown formatted link -| normal | `nn` | Opens new note for editing -| normal | `nd` | Opens new or existing daily note -| normal | `nw` | Opens new or existing weekly note -| normal | `nl` | List with prefixed label, sorted alphabetically (mtime if journal) -| normal | `nm` | List with prefixed date modified, sorted by mtime -| normal | `nc` | List with prefixed date created in custom format, sorted by ctime -| normal | `nb` | List all notes linking to this note (backlinks) -| normal | `nk` | List all links related to this note -| normal | `ns` | Full text search -| normal | `nW` | Opens browser with web view (auto stop webserver on idle) -| fzf | `C-k` `C-j` | Move up and down in fzf window -| fzf | `Enter` | Open selection -| fzf | `C-t` `C-x` `C-v` | Open selection in new tab, split, vertical split -| fzf | `C-/` | Toggle preview -| fzf | `Shift-Tab` | Multiple selection -| normal | `ge` | Open the link under the cursor (vim-markdown) -| normal | `[[` `]]` | Jump back and forward between headings (vim-markdown) - -### Fzf search syntax +| Mode | Binding | Comment +| ---- | ------- | ------- +| insert | `[[` | Opens note list, insert selection as markdown formatted link +| normal | `nn` | Opens new note for editing +| normal | `nd` | Opens new or existing daily note +| normal | `nw` | Opens new or existing weekly note +| normal | `nl` | List with prefixed label, sorted alphabetically (mtime if journal) +| normal | `nm` | List with prefixed date modified, sorted by mtime +| normal | `nc` | List with prefixed date created (YYYY/WeekXX), sorted by ctime +| normal | `nk` | List all links related to active note (or all if none) +| normal | `ns` | Full text search with prefixed note title +| normal | `nW` | Opens browser with embedded web/app (auto stop webserver on idle) +| finder | `C-j` `↓` | Select next entry (down) +| finder | `C-k` `↑` | Select previous entry (up) +| finder | `C-/` | Toggle preview +| finder | `Enter` | Submit selected entry +| finder | `Esc` | Dismiss finder +| normal | `ge` | Open the link under the cursor (vim-markdown) +| normal | `[[` `]]` | Jump back and forward between headings (vim-markdown) + +### Finder search syntax | Token | Match Type | Comment | ----- | ---------- | ------- -| `sbtrkt` | fuzzy-match | Items that fuzzy match `sbtrkt` -| `'word` | exact-match | Items that include `word` +| `word` | exact-match | Items that include `word` | `^word` | prefix exact-match | Items that start with `word` | `word$` | suffix exact-match | Items that end with `word` | `!word` | inverse exact-match | Items that do not include `word` @@ -642,27 +598,11 @@ endif | `!word$` | inverse suffix exact-match | Items that do not end with `word` | `foo bar` | multiple exact match (AND) | Items that include both `foo` AND `bar` | `foo \| bar` | multiple exact match (OR) | Items that include either `foo` OR `bar` +| `'sbtrkt` | fuzzy-match | Items that fuzzy match `sbtrkt` ### Related Vim settings ```vim -" junegunn/fzf.vim -let $FZF_DEFAULT_OPTS="--reverse --filepath-word --no-separator --no-scrollbar " -let g:fzf_buffers_jump = 1 -let g:fzf_layout = { 'window': { 'width': 0.85, 'height': 0.85 } } -let g:fzf_colors = { - \ 'fg': ['fg', 'Normal'], - \ 'bg': ['bg', 'Normal'], - \ 'hl': ['fg', 'Comment'], - \ 'fg+': ['fg', 'CursorLine', 'CursorColumn', 'Normal'], - \ 'bg+': ['bg', 'CursorLine', 'CursorColumn'], - \ 'hl+': ['fg', 'Statement'], - \ 'info': ['fg', 'PreProc'], - \ 'pointer': ['fg', 'Exception'], - \ 'marker': ['fg', 'Keyword'], - \ 'spinner': ['fg', 'Label'], - \ 'header': ['fg', 'Comment'] } - " preservim/vim-markdown let g:vim_markdown_folding_style_pythonic = 1 let g:vim_markdown_folding_level = 2 From 33077c5034aa41385b76096e13768f8d787873d5 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Tue, 4 Feb 2025 15:57:40 +0200 Subject: [PATCH 37/44] completion: finder end-of-options support --- completion.bash | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/completion.bash b/completion.bash index ad53820..dd2ed52 100644 --- a/completion.bash +++ b/completion.bash @@ -19,6 +19,20 @@ __notesium_complete() { # handle options with equals. COMP_WORDBREAKS is global. _get_comp_words_by_ref -n = cur prev + + if [[ "${COMP_WORDS[1]}" == "finder" ]]; then + if [[ "${prev}" == "--" ]]; then + words="$(echo -e "list\nlinks\nlines")" + else + for ((i = 1; i < ${#COMP_WORDS[@]} - 1; i++)); do + if [[ "${COMP_WORDS[i]}" == "--" ]]; then + words="$(__notesium_opts "${COMP_WORDS[i+1]}")" + break + fi + done + fi + fi + case ${cur} in --prefix=*|--sort=*) prev="${cur%%=*}=" From e9adf7562302c7cb5523dc23afbfc1639eaf7042 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Tue, 4 Feb 2025 15:58:09 +0200 Subject: [PATCH 38/44] readme: finder command --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b8d1e37..9d0e91e 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,10 @@ It aspires and is designed to: - Supports line filtering queries with AND, OR, and NOT conditionals. - **Stats** - View counts of notes, labels, orphans, links, lines, words, etc. +- **Finder** + - Interactive filter selection TUI. + - Supports List, Links and Lines commands and their options as input. + - Optional preview window with syntax highlighting and concealment. - **Web** - Completely self-contained and runs locally. - Create and edit notes with web based editor (optional vim-mode). @@ -291,6 +295,10 @@ Commands: stats Print statistics --color Color code using ansi escape sequences --table Format as table with whitespace delimited columns + finder Start finder (interactive filter selection TUI) + --preview Display note preview (toggle with ctrl-/) + --prompt=STR Set custom prompt text + -- CMD [OPTS] Input (default: list --color --prefix=label --sort=alpha) web Start web server --webroot=PATH Path to web root to serve (default: embedded webroot) --mount=DIR:URI Additional directory to serve under webroot (experimental) @@ -814,4 +822,4 @@ PAUSE=y bats tests/list.bats --tap The MIT License (MIT) -Copyright (c) 2023-2024 Alon Swartz +Copyright (c) 2023-2025 Alon Swartz From 4b5b9491b2eb464a9be05158b02fcc98a818a19c Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Wed, 5 Feb 2025 13:11:55 +0200 Subject: [PATCH 39/44] vim: workaround vimscript max split quirk --- vim/plugin/notesium.vim | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/vim/plugin/notesium.vim b/vim/plugin/notesium.vim index 70c49d0..317ee5d 100644 --- a/vim/plugin/notesium.vim +++ b/vim/plugin/notesium.vim @@ -85,13 +85,14 @@ if has('nvim') return endif - " Parse output (filename:linenumber:text) and pass to callback - let l:parts = split(l:output, ':', 3) + " Parse output (filename:linenumber: text) and pass to callback + let l:parts = split(l:output, ':') if len(l:parts) < 3 echoerr "Invalid finder output: " . l:output return endif - call a:callback(l:parts[0], l:parts[1], trim(l:parts[2])) + let l:text = trim(join(l:parts[2:], ':')) + call a:callback(l:parts[0], l:parts[1], l:text) endfunction else @@ -108,14 +109,14 @@ else return endif - " Parse output (filename:linenumber:text) and pass to callback - let l:parts = split(l:output, ':', 3) + " Parse output (filename:linenumber: text) and pass to callback + let l:parts = split(l:output, ':') if len(l:parts) < 3 echoerr "Invalid finder output: " . l:output return endif - - let l:text = substitute(l:parts[2], '^\s*\(.\{-}\)\s*\n*$', '\1', '') + let l:text = join(l:parts[2:], ':') + let l:text = substitute(l:text, '^\_s\+\|\_s\+$', '', 'g') silent! call a:config['callback'](l:parts[0], l:parts[1], l:text) endfunction From 6ee78402c8d76c90dcbc226d4569d1f3c7428b52 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Thu, 6 Feb 2025 14:14:52 +0200 Subject: [PATCH 40/44] vim: added plugin help documentation --- .gitignore | 1 + vim/doc/notesium.txt | 123 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 vim/doc/notesium.txt diff --git a/.gitignore b/.gitignore index ba01f71..34a3924 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build/ +vim/doc/tags notesium diff --git a/vim/doc/notesium.txt b/vim/doc/notesium.txt new file mode 100644 index 0000000..5221ef7 --- /dev/null +++ b/vim/doc/notesium.txt @@ -0,0 +1,123 @@ +notesium.txt Notesium Vim Plugin Last change: Feb 06 2025 +TABLE OF CONTENTS *notesium* *notesium-toc* +============================================================================== + + Notesium |notesium| + Setup |notesium-setup| + Configuration |notesium-config| + Commands |notesium-commands| + Mappings |notesium-mappings| + Finder |notesium-finder| + License |notesium-license| + +NOTESIUM *notesium* +============================================================================== + +Notesium - A simple yet powerful system for networked thought. +> + Writing does not make intellectual endeavours easier, it makes them + possible. Deepen understanding, insight, and allow for structure to emerge + organically by linking notes. +< +See the {Website}{1} and {GitHub}{2} repository for more details. + + {1} https://www.notesium.com + {2} https://github.com/alonswartz/notesium + +SETUP *notesium-setup* +============================================================================== + +The Notesium Vim plugin provides an interface for interacting with Notesium +from within Vim/NeoVim, and therefore requires the `notesium` binary to be +installed. + +To install the plugin, add the repository to your plugin manager and +point its runtime path to the `'vim'` directory. For example: +> + " init.vim or .vimrc + Plug 'alonswartz/notesium', { 'rtp': 'vim' } + + -- init.lua + Plug('alonswartz/notesium', { ['rtp'] = 'vim' }) +< +Note: The plugin depends on notesium 0.6.4 or above. + +CONFIGURATION *notesium-config* +============================================================================== + +`g:notesium_bin` Binary name or path `notesium` +`g:notesium_mappings` Enable(1) or disable(0) mappings `1` +`g:notesium_weekstart` First day of the week `monday` +`g:notesium_window` Finder Default `{'width': 0.85, 'height': 0.85}` +`g:notesium_window_small` Finder InsertLink `{'width': 0.50, 'height': 0.50}` + +Note: These settings should be set prior to the plugin being sourced. + +COMMANDS *notesium-commands* +============================================================================== + +`:NotesiumNew` Open new `note` for editing +`:NotesiumDaily [YYYY-MM-DD]` Open new or existing daily `note` +`:NotesiumWeekly [YYYY-MM-DD]` Open new or existing weekly `note` +`:NotesiumList [LIST_OPTS]` Open finder: list of notes +`:NotesiumLines [LINES_OPTS]` Open finder: lines of all notes +`:NotesiumLinks [LINKS_OPTS]` Open finder: links of all notes +`:NotesiumLinks! [LINKS_OPTS]` Open finder: links of the active `note` +`:NotesiumInsertLink [LIST_OPTS]` Open finder: insert selection as markdown link +`:NotesiumWeb [WEB_OPTS]` Start web server, open browser (stop on idle) + +Note: `NotesiumWeekly` depends on `g:notesium_weekstart`. + +MAPPINGS *notesium-mappings* +============================================================================== + +INSERT MODE + +`[[` Opens `note` list, insert selection as markdown formatted link + +NORMAL MODE + +`nn` Opens new `note` for editing +`nd` Opens new or existing daily `note` +`nw` Opens new or existing weekly `note` +`nl` List with prefixed label, sorted alphabetically; mtime if journal +`nm` List with prefixed date modified, sorted by mtime +`nc` List with prefixed date created `(YYYY/WeekXX)`, sorted by ctime +`nk` List all links related to active `note` (or all if none) +`ns` Full text search with prefixed `note` title +`nW` Opens browser with embedded web/app (auto stop webserver on idle) + +Note: The mappings can be enabled/disabled via `g:notesium_mappings`. + +FINDER *notesium-finder* +============================================================================== + +KEYBINDINGS + +`C-j` Select next entry (down) +`C-k` Select previous entry (up) +`C-/` Toggle preview +`Enter` Submit selected entry +`Esc` Dismiss finder + +SEARCH SYNTAX + +`word` Exact-match Items that include `word` +`^word` Prefix exact-match Items that start with `word` +`word$` Suffix exact-match Items that end with `word` +`!word` Inverse exact-match Items that do not include `word` +`!^word` Inverse prefix exact-match Items that do not start with `word` +`!word$` Inverse suffix exact-match Items that do not end with `word` +`foo bar` Multiple exact match (AND) Items that include both `foo` AND `bar` +`foo | bar` Multiple exact match (OR) Items that include either `foo` OR `bar` +`'sbtrkt` Fuzzy-match Items that fuzzy match `sbtrkt` + +LICENSE *notesium-license* +============================================================================== + +The MIT License (MIT) + +Copyright (c) 2023-2025 Alon Swartz + +============================================================================== +vim:tw=78:sw=2:ts=2:ft=help:nowrap: From bff54f39c71398ed8b7c9d7dc5b072917c5ad647 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Thu, 6 Feb 2025 14:15:29 +0200 Subject: [PATCH 41/44] readme: update vim section for consistency with plugin help doc --- README.md | 58 +++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 9d0e91e..7e0b762 100644 --- a/README.md +++ b/README.md @@ -522,53 +522,51 @@ of the formatting characters is supported. ## Vim Notesium provides a Vim/Neovim plugin that integrates with the Notesium -CLI, particularily the `finder` (interactive filter selection TUI with -syntax highlighted preview). +CLI, particularily the `finder` command providing and interactive filter +selection TUI with syntax highlighted preview. -- Depends: [Notesium CLI](#cli) (0.6.4 or above) +- Depends: [notesium](#cli) (0.6.4 or above) - Recommended: [vim-markdown](https://github.com/preservim/vim-markdown) for general markdown goodness. -- Recommended: [goyo.vim](https://github.com/junegunn/goyo.vim) and [lightlight.vim](https://github.com/junegunn/limelight.vim) for distraction free writing. +- Recommended: [goyo.vim](https://github.com/junegunn/goyo.vim) and [limelight.vim](https://github.com/junegunn/limelight.vim) for distraction free writing. ### Setup To install the plugin, add the repository to your plugin manager and -point its runtime path to the `vim` directory. For example: +point its runtime path to the `'vim'` directory. For example: -```vim +``` " init.vim or .vimrc Plug 'alonswartz/notesium', { 'rtp': 'vim' } -``` -```lua -- init.lua Plug('alonswartz/notesium', { ['rtp'] = 'vim' }) ``` ### Configuration -| Setting | Type | Comment -| ------- | ---- | ------- -| `g:notesium_bin` | `string` | Binary name or path (default: `notesium`) -| `g:notesium_mappings` | `boolean` | Enable(1) or disable(0) mappings (default: `1`) -| `g:notesium_weekstart` | `string` | First day of the week (default: `monday`) -| `g:notesium_window` | `dict` | Finder (`{'width': 0.85, 'height': 0.85}`) -| `g:notesium_window_small` | `dict` | InsertLink Finder (`{'width': 0.5, 'height': 0.5}`) +| Setting | Comment | Default +| ------- | ------- | ------- +| `g:notesium_bin` | Binary name or path | `notesium` +| `g:notesium_mappings` | Enable(1) or disable(0) mappings | `1` +| `g:notesium_weekstart` | First day of the week | `monday` +| `g:notesium_window` | Finder Default | `{'width': 0.85, 'height': 0.85}` +| `g:notesium_window_small` | Finder InsertLink | `{'width': 0.50, 'height': 0.50}` -Note: `g:notesium_bin` must be set prior to the plugin being sourced. +Note: These settings should be set prior to the plugin being sourced. ### Commands -| Command | Arguments | Comment -| ------- | --------- | ------- -| `:NotesiumNew` | None | Open new note for editing -| `:NotesiumDaily` | `YYYY-MM-DD` | Open new or existing daily note -| `:NotesiumWeekly` | `YYYY-MM-DD` | Open new or existing weekly note -| `:NotesiumList` | list --opts | Finder: Notes list -| `:NotesiumLinks!` | links --opts | Finder: Links related to active note -| `:NotesiumLinks` | links --opts | Finder: All notes links -| `:NotesiumLines` | lines --opts | Finder: All notes lines -| `:NotesiumInsertLink` | list --opts | Finder: Insert selected note link -| `:NotesiumWeb` | web --opts | Starts web server +| Command | Comment +| ------- | ------- +| `:NotesiumNew` | Open new `note` for editing +| `:NotesiumDaily [YYYY-MM-DD]` | Open new or existing daily `note` +| `:NotesiumWeekly [YYYY-MM-DD]` | Open new or existing weekly `note` +| `:NotesiumList [LIST_OPTS]` | Open finder: list of notes +| `:NotesiumLines [LINES_OPTS]` | Open finder: lines of all notes +| `:NotesiumLinks [LINKS_OPTS]` | Open finder: links of all notes +| `:NotesiumLinks! [LINKS_OPTS]` | Open finder: links of the active `note` +| `:NotesiumInsertLink [LIST_OPTS]` | Open finder: insert selection as markdown link +| `:NotesiumWeb [WEB_OPTS]` | Start web server, open browser (stop on idle) Note: `NotesiumWeekly` depends on `g:notesium_weekstart`. @@ -580,9 +578,9 @@ Note: `NotesiumWeekly` depends on `g:notesium_weekstart`. | normal | `nn` | Opens new note for editing | normal | `nd` | Opens new or existing daily note | normal | `nw` | Opens new or existing weekly note -| normal | `nl` | List with prefixed label, sorted alphabetically (mtime if journal) +| normal | `nl` | List with prefixed label, sorted alphabetically; mtime if journal | normal | `nm` | List with prefixed date modified, sorted by mtime -| normal | `nc` | List with prefixed date created (YYYY/WeekXX), sorted by ctime +| normal | `nc` | List with prefixed date created `(YYYY/WeekXX)`, sorted by ctime | normal | `nk` | List all links related to active note (or all if none) | normal | `ns` | Full text search with prefixed note title | normal | `nW` | Opens browser with embedded web/app (auto stop webserver on idle) @@ -619,9 +617,9 @@ let g:vim_markdown_auto_insert_bullets = 0 let g:vim_markdown_new_list_item_indent = 0 let g:vim_markdown_toc_autofit = 1 let g:vim_markdown_conceal_code_blocks = 0 - let g:markdown_fenced_languages = ['json', 'sh', 'shell=bash'] hi def link mkdHeading htmlH1 + autocmd FileType markdown setlocal conceallevel=2 ``` From 12a318badff49d757ade306d14dae3f85afe951f Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Thu, 6 Feb 2025 16:39:57 +0200 Subject: [PATCH 42/44] vim: ensure notesium_bin is executable, escape file paths --- vim/plugin/notesium.vim | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/vim/plugin/notesium.vim b/vim/plugin/notesium.vim index 317ee5d..5fcf97a 100644 --- a/vim/plugin/notesium.vim +++ b/vim/plugin/notesium.vim @@ -3,6 +3,10 @@ " Notesium configuration {{{1 " ---------------------------------------------------------------------------- +if !exists('g:notesium_bin') || empty(g:notesium_bin) + let g:notesium_bin = 'notesium' +endif + if !exists('g:notesium_mappings') || empty(g:notesium_mappings) let g:notesium_mappings = 1 endif @@ -11,10 +15,6 @@ if !exists('g:notesium_weekstart') || empty(g:notesium_weekstart) let g:notesium_weekstart = 'monday' endif -if !exists('g:notesium_bin') || empty(g:notesium_bin) - let g:notesium_bin = 'notesium' -endif - if !exists('g:notesium_window') || empty(g:notesium_window) let g:notesium_window = {'width': 0.85, 'height': 0.85} endif @@ -23,6 +23,11 @@ if !exists('g:notesium_window_small') || empty(g:notesium_window_small) let g:notesium_window_small = {'width': 0.5, 'height': 0.5} endif +if !executable(g:notesium_bin) + echoerr "notesium_bin not found: " . g:notesium_bin + finish +endif + function! notesium#get_notesium_dir() abort let l:output = systemlist(g:notesium_bin . ' home') if empty(l:output) || v:shell_error @@ -177,7 +182,7 @@ command! -nargs=* NotesiumDaily \ let s:cdate = empty() ? strftime('%Y-%m-%d') : | \ let s:output = system(g:notesium_bin.' new --verbose --ctime='.s:cdate.'T00:00:00') | \ let s:filepath = matchstr(s:output, 'path:\zs[^\n]*') | - \ execute 'edit ' . s:filepath | + \ execute 'edit' fnameescape(s:filepath) | \ if getline(1) =~ '^\s*$' | \ let s:epoch = matchstr(s:output, 'epoch:\zs[^\n]*') | \ call setline(1, '# ' . strftime('%b %d, %Y (%A)', s:epoch)) | @@ -198,7 +203,7 @@ command! -nargs=* NotesiumWeekly \ let s:weekBegDate = strftime('%Y-%m-%d', s:weekBegEpoch) | \ let s:output = system('notesium new --verbose --ctime='.s:weekBegDate.'T00:00:01') | \ let s:filepath = matchstr(s:output, 'path:\zs[^\n]*') | - \ execute 'edit ' . s:filepath | + \ execute 'edit' fnameescape(s:filepath) | \ if getline(1) =~ '^\s*$' | \ let s:weekFmt = s:startOfWeek == 0 ? '%U' : '%V' | \ let s:yearWeekStr = strftime('%G: Week' . s:weekFmt, s:weekBegEpoch) | From 62fe5e77a48213c3cd992386baf8e4ebd5b79b4c Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Thu, 6 Feb 2025 17:49:50 +0200 Subject: [PATCH 43/44] readme: update vim screenshots using new plugin --- README.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7e0b762..3d3b8ca 100644 --- a/README.md +++ b/README.md @@ -179,29 +179,30 @@ It aspires and is designed to:
*[Vim] List: prefixed with associated labels and sorted alphabetically* -![image: vim prefixed label](https://www.notesium.com/images/screenshot-1681733208.png) +![image: vim prefixed label](https://www.notesium.com/images/screenshot-1738853845.png)
*[Vim] List: prefixed with modification date and sorted per modification time* -![image: vim prefixed mtime](https://www.notesium.com/images/screenshot-1681733355.png) +![image: vim prefixed mtime](https://www.notesium.com/images/screenshot-1738854202.png)
*[Vim] Links: display all links* -![image: vim links all](https://www.notesium.com/images/screenshot-1681733482.png) +![image: vim links all](https://www.notesium.com/images/screenshot-1738854280.png)
*[Vim] Links: display links related to the current note* -![image: vim links related](https://www.notesium.com/images/screenshot-1681733712.png) +![image: vim links related](https://www.notesium.com/images/screenshot-1738854409.png)
*[Vim] Links: link insertion triggered by `[[`* -![image: vim link insertion](https://www.notesium.com/images/screenshot-1681734183.png) +![image: vim link insertion](https://www.notesium.com/images/screenshot-1738854615.png)
*[Vim] Lines: full text search (light theme)* -![image: vim full text search light](https://www.notesium.com/images/screenshot-1681734555.png) +![image: vim full text search light](https://www.notesium.com/images/screenshot-1738854816.png)
+ ## CLI Notesium is primarily tested and supported on Linux, with only @@ -558,13 +559,13 @@ Note: These settings should be set prior to the plugin being sourced. | Command | Comment | ------- | ------- -| `:NotesiumNew` | Open new `note` for editing -| `:NotesiumDaily [YYYY-MM-DD]` | Open new or existing daily `note` -| `:NotesiumWeekly [YYYY-MM-DD]` | Open new or existing weekly `note` +| `:NotesiumNew` | Open new note for editing +| `:NotesiumDaily [YYYY-MM-DD]` | Open new or existing daily note +| `:NotesiumWeekly [YYYY-MM-DD]` | Open new or existing weekly note | `:NotesiumList [LIST_OPTS]` | Open finder: list of notes | `:NotesiumLines [LINES_OPTS]` | Open finder: lines of all notes | `:NotesiumLinks [LINKS_OPTS]` | Open finder: links of all notes -| `:NotesiumLinks! [LINKS_OPTS]` | Open finder: links of the active `note` +| `:NotesiumLinks! [LINKS_OPTS]` | Open finder: links of the active note | `:NotesiumInsertLink [LIST_OPTS]` | Open finder: insert selection as markdown link | `:NotesiumWeb [WEB_OPTS]` | Start web server, open browser (stop on idle) From 768123acdb3b42f03d7a449087038374ca857f22 Mon Sep 17 00:00:00 2001 From: Alon Swartz Date: Sun, 9 Feb 2025 12:32:12 +0200 Subject: [PATCH 44/44] changelog: 0.6.4 --- CHANGELOG.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5face2..d225c44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,70 @@ +## 0.6.4 + +This release introduces the **finder** command, a built-in interactive +filtering and selection tool powered by the excellent **fzf engine**. It +enhances the CLI with flexible input handling and syntax-highlighted +previews, making it easier to search and navigate notes efficiently. The +finder works great as a standalone tool but becomes even more powerful +when integrated with other tools, such as Vim. + +Regarding Vim, Notesium now has a **Vim/Neovim plugin**, replacing the +previous example integration. The new plugin is easier to set up, +removes dependencies on `fzf`, `fzf.vim`, and `bat`, and seamlessly +integrates with the `finder` command. It is more robust, offers greater +flexibility, and enhances compatibility with both Vim and Neovim. + +This release **is backwards compatible**, but users transitioning from +the example Vim integration should review the notable changes below and +the updated documentation. + +Added: + +- Finder: Native interactive filter and selection TUI (embedded `fzf`). +- Finder: Input specified via end-of-options unix convention (`-- CMD [OPTS]`). +- Finder: Input supports List, Links, Links commands and their options. +- Finder: Input default `list --color --prefix=label --sort=alpha`. +- Finder: Preview with syntax highlighting and lineno support (`cat` command). +- Finder: Preview toggle with `Ctrl-/`. +- Finder: Configurable custom prompt. +- Completion: Updated to support Finder end-of-options. + +- Cat: Markdown syntax highlighting and concealment. +- Cat: Highlight - `header codeblock code blockquote plainlink listmarker`. +- Cat: Highlight and Concealment - `bold italic markdownlink`. +- Cat: Line number highlight. + +- Vim: Brand new Vim/Neovim plugin, installable via plugin managers. +- Vim: Integrates with Finder (removing dependency on `fzf`, `fzf.vim`, `bat`). +- Vim: Supports Neovim floating windows, with fallback for Vim without `term`. +- Vim: Auto enable/disable preview based on terminal width. +- Vim: Auto adjust window size for link-insertion based on terminal width. +- Vim: Documentation accessible via `:help notesium[-section]`. +- Vim: Settings - `g:notesium_(bin|mappings|weekstart|window|window_small)`. +- Vim: Commands - `Notesium(New|Daily|Weekly|List|Links|Lines|Web|InsertLink)`. +- Vim: Mappings - `(nn|nd|nw|nl|nm|nc|nk|ns|nW)`, `[[`. + +Changed: + +- Readme: Updated to include Finder command. +- Readme: Vim example integration replaced with new Vim/Neovim Plugin. +- Readme: Vim screenshots updated using new Vim/Neovim Plugin. + +**Vim plugin vs. Vim example integration**: + +- Fixed: Links filename enumeration, bang support. +- Fixed: InsertLink autocmd localized to buffer. +- Fixed: InsertLink auto adjust window size based on terminal width. +- Fixed: `nc` adheres to g:notesium_weekstart. +- Fixed: NotesiumWeb now supports custom and required arguments. +- Added: NotesiumWeb windows support via powershell. +- Added: g:notesium_bin to override hardcoded assumed `notesium`. +- Added: Callbacks for `editfile` and `insertlink`. +- Added: Plugin documentation accessible via `:help notesium[-section]`. +- Changed: NOTESIUM_WEEKSTART (int) replaced with g:notesium_weekstart (str). +- Changed: NotesiumSearch has been renamed to NotesiumLines. +- Removed: `nb` is not included in the plugin mappings. +- Removed: Dependencies on `fzf`, `fzf.vim` or a highlight tool such as `bat`. + ## 0.6.3 This release introduces a new **lines** `--filter` option with support