Skip to content

Commit

Permalink
Saving & Creating Notes ; UI tweaks (#2)
Browse files Browse the repository at this point in the history
- removes prefix/suffix from filename in list box
- changes mapping of <enter> from search box
- saves content box when modified
- adds 'scrolltoview' behavior
- up/down keys in search box navigate through list
- allows creation of new notes
- shows snippets in list box
- updates readme
  • Loading branch information
ivan3bx authored Jan 22, 2023
1 parent 5dab45f commit a5fe8fc
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 43 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.vscode
dist/
*.db
*.log
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ The goal is to point 'nve' to a directory of plain-text files, and quickly searc

## Current Status

- 2023/01 - Navigation, search and viewing.
- 2023/01/16 - Navigation, search and viewing.
- 2023/02/22 - Saving, creating & displaying snippets

## TODO

- Saving edits
- Creating new notes from search box
- Monitor FS changes to incrementally update DB
- Display snippet in search results
- Support renaming of notes (modal)
- Colorize matching search term in content
- Syntax highlighting for Markdown files
- [x]Saving edits
- [x]Creating new notes from search box
- [x] ✅ Display snippet in search results
- [ ] Monitor FS changes to incrementally update DB
- [ ] Support renaming of notes (modal)
- [ ] Colorize matching search term in content
- [ ] Syntax highlighting for Markdown files

<image src="https://user-images.githubusercontent.com/179345/212459798-29c7c2e1-71fc-4323-9da4-6cdcff09f598.png" width="620"/>
7 changes: 4 additions & 3 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ func main() {
// View hierarchy
contentBox = nve.NewContentBox()
listBox = nve.NewListBox(contentBox, notes)
searchBox = nve.NewSearchBox(listBox, notes)
searchBox = nve.NewSearchBox(listBox, contentBox, notes)
)

notes.RegisterObservers(contentBox, listBox)
notes.RegisterObservers(listBox)
notes.Notify()

// global input events
Expand All @@ -35,7 +35,8 @@ func main() {
}
return &tcell.EventKey{}
case tcell.KeyEscape:
if contentBox.HasFocus() {
if !contentBox.HasFocus() {
listBox.SetCurrentItem(0)
app.SetFocus(searchBox)
return &tcell.EventKey{}
}
Expand Down
77 changes: 74 additions & 3 deletions content_box.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
package nve

import (
"log"
"time"
"unicode"
"unicode/utf8"

"github.com/bep/debounce"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)

type ContentBox struct {
*tview.TextArea
debounce func(func())
currentFile *FileRef
}

func NewContentBox() *ContentBox {
textArea := ContentBox{TextArea: tview.NewTextArea()}
textArea := ContentBox{
TextArea: tview.NewTextArea(),
debounce: debounce.New(300 * time.Millisecond),
}

textArea.SetBorder(true).
SetTitle("Content").
Expand All @@ -20,6 +30,12 @@ func NewContentBox() *ContentBox {
SetBorderPadding(1, 0, 1, 1).
SetTitleAlign(tview.AlignLeft)

textArea.SetFocusFunc(func() {
// ignore edits if there is no current file
if textArea.currentFile == nil {
textArea.Blur()
}
})
return &textArea
}

Expand All @@ -33,6 +49,61 @@ func (b *ContentBox) SetFile(f *FileRef) {
b.SetText(GetContent(f.Filename), false)
}

func (b *ContentBox) SearchResultsUpdate(_ *Notes) {
// TODO: if selected note changes, update content.
// InputHandler overrides default handling to switch focus away from search box when necessary.
func (b *ContentBox) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return b.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
event = b.mapSpecialKeys(event)

before := b.GetText()

if handler := b.TextArea.InputHandler(); handler != nil {
handler(event, setFocus)
}

if after := b.GetText(); before != after {
b.queueSave(after)
}
})
}

func (b *ContentBox) mapSpecialKeys(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
// navigate up
case tcell.KeyCtrlP:
event = tcell.NewEventKey(tcell.KeyUp, event.Rune(), event.Modifiers())

// navigate down
case tcell.KeyCtrlN:
event = tcell.NewEventKey(tcell.KeyDown, event.Rune(), event.Modifiers())

// navigate forward
case tcell.KeyCtrlF:
event = tcell.NewEventKey(tcell.KeyRight, event.Rune(), event.Modifiers())

// delete empty line
case tcell.KeyCtrlK:
fromRow, fromCol, toRow, toCol := b.GetCursor()

if fromRow == toRow && fromCol == toCol && fromCol == 0 {
if _, start, end := b.GetSelection(); start == end {
r, _ := utf8.DecodeRuneInString(b.GetText()[start:])
if !unicode.IsLetter(r) {
event = tcell.NewEventKey(tcell.KeyDelete, event.Rune(), event.Modifiers())
}
}
}

}

return event
}

func (b *ContentBox) queueSave(content string) {
b.debounce(func() {
err := SaveContent(b.currentFile.Filename, content)

if err != nil {
log.Println("Error saving content:", err)
}
})
}
2 changes: 1 addition & 1 deletion database.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func (db *DB) Recent(limit int) ([]*SearchResult, error) {
err = db.Select(&res, `
SELECT
docs.id, docs.filename, docs.md5, docs.modified_at,
substr(cti.text, 0, 10) as snippet
substr(cti.text, 0, 120) as snippet
FROM
documents docs
INNER JOIN
Expand Down
9 changes: 9 additions & 0 deletions files.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io/fs"
"os"
"path/filepath"
"strings"
"time"
)

Expand All @@ -24,6 +25,10 @@ type FileRef struct {
ModifiedAt time.Time `db:"modified_at"`
}

func (f *FileRef) DisplayName() string {
return strings.TrimSuffix(filepath.Base(f.Filename), filepath.Ext(f.Filename))
}

func GetContent(filename string) string {
bytes, err := os.ReadFile(filename)

Expand All @@ -34,6 +39,10 @@ func GetContent(filename string) string {
return string(bytes)
}

func SaveContent(filename string, content string) error {
return os.WriteFile(filename, []byte(content), 0644)
}

func scanDirectory(dirname string) ([]string, error) {
var files []string

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/ivan3bx/nve
go 1.19

require (
github.com/bep/debounce v1.2.1
github.com/gdamore/tcell/v2 v2.5.4
github.com/jmoiron/sqlx v1.3.5
github.com/mattn/go-sqlite3 v1.14.16
Expand Down
14 changes: 2 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0=
github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/k=
github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
Expand All @@ -15,8 +15,6 @@ github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
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-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
Expand All @@ -29,8 +27,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/rivo/tview v0.0.0-20230104153304-892d1a2eb0da h1:3Mh+tcC2KqetuHpWMurDeF+yOgyt4w4qtLIpwSQ3uqo=
github.com/rivo/tview v0.0.0-20230104153304-892d1a2eb0da/go.mod h1:lBUy/T5kyMudFzWUH/C2moN+NlU5qF505vzOyINXuUQ=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand All @@ -52,22 +48,16 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
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-20220318055525-2edf467146b5 h1:saXMvIOKvRFwbOMicHXr0B1uwoxq9dGmLe5ExMES6c4=
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/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.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
Expand Down
37 changes: 35 additions & 2 deletions list_box.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package nve

import (
"fmt"
"math"
"strings"

"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
Expand Down Expand Up @@ -65,8 +69,37 @@ func (b *ListBox) SearchResultsUpdate(notes *Notes) {
b.contentView.Clear()
}

for _, result := range lastResult {
b.AddItem(result.Filename, "", 0, nil)
selectedIndex := -1

for index, result := range lastResult {
displayName := result.DisplayName()
var formattedName string
if len(displayName) > 14 {
formattedName = fmt.Sprintf("%-20.20s..", displayName)
} else {
formattedName = fmt.Sprintf("%-22.22s", displayName)
}

b.AddItem(strings.Join([]string{formattedName, result.Snippet}, " : "), "", 0, nil)

if selectedIndex == -1 && strings.HasPrefix(displayName, notes.LastQuery) {
selectedIndex = index
}
}

_, _, _, height := b.GetInnerRect()

if selectedIndex >= 0 {
// highlights row with exact prefix match to search query.
b.SetCurrentItem(selectedIndex)

// scroll to view; use height of list box
b.SetOffset(int(math.Max(float64(selectedIndex-height+1), 0)), 0)
} else {
// highlight any selected row if not in visible rect
if !b.InRect(b.GetCurrentItem(), 0) {
b.SetOffset(b.GetCurrentItem(), 0)
}
}
}

Expand Down
37 changes: 36 additions & 1 deletion notes.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package nve

import (
"fmt"
"log"
"os"
"path/filepath"

_ "github.com/mattn/go-sqlite3" // sqlite driver
)
Expand Down Expand Up @@ -56,7 +58,7 @@ func (n *Notes) Search(text string) ([]string, error) {
n.LastQuery = text

if text == "" {
searchResults, err = n.db.Recent(10)
searchResults, err = n.db.Recent(20)
} else {
searchResults, err = n.db.Search(text)
}
Expand All @@ -81,6 +83,39 @@ func (n *Notes) Search(text string) ([]string, error) {
return res, nil
}

func (n *Notes) CreateNote(name string) (*FileRef, error) {
path := filepath.Join(n.config.Filepath, fmt.Sprintf("%s.%s", name, "md"))
newFile, err := os.OpenFile(path, os.O_CREATE, 0644)

if err != nil {
return nil, err
}

md5, err := calculateMD5(path)

if err != nil {
return nil, err
}

stat, err := newFile.Stat()

if err != nil {
return nil, err
}

fileRef := FileRef{
Filename: newFile.Name(),
MD5: md5,
ModifiedAt: stat.ModTime(),
}

if err := n.db.Insert(&fileRef, []byte{}); err != nil {
return nil, err
}

return &fileRef, nil
}

func (n *Notes) RegisterObservers(obs ...Observer) {
if n.observers != nil {
n.observers = obs
Expand Down
Loading

0 comments on commit a5fe8fc

Please sign in to comment.