289 lines
4.7 KiB
Go
289 lines
4.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/gdamore/tcell/v2"
|
|
_ "github.com/gdamore/tcell/v2/encoding"
|
|
"github.com/mattn/go-runewidth"
|
|
)
|
|
|
|
const lineHeight = 2
|
|
const editor = "vim"
|
|
|
|
const (
|
|
uiMain = 0
|
|
uiDelete = 1
|
|
)
|
|
|
|
var uiState = uiMain
|
|
var selectedNote = 0
|
|
var notes []note
|
|
|
|
func edit(s tcell.Screen, text string, ext string) (string, error) {
|
|
cmd := exec.Command("vipe", "--suffix", ext)
|
|
cmd.Env = append(cmd.Env, "EDITOR="+editor)
|
|
|
|
pipe, _ := cmd.StdinPipe()
|
|
io.WriteString(pipe, text)
|
|
pipe.Close()
|
|
|
|
err := s.Suspend()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
text = string(output)
|
|
|
|
err = s.Resume()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return text, nil
|
|
}
|
|
|
|
func editNote(s tcell.Screen, n note) (note, error) {
|
|
newContent, err := edit(s, n.Content, "md")
|
|
if err != nil {
|
|
return note{}, err
|
|
}
|
|
|
|
n.Content = newContent
|
|
|
|
err = putNote(n)
|
|
if err != nil {
|
|
return note{}, nil
|
|
}
|
|
|
|
showNotes(s)
|
|
s.Sync()
|
|
|
|
return n, nil
|
|
}
|
|
|
|
func editNoteTitle(s tcell.Screen, n note) (note, error) {
|
|
newTitle, err := edit(s, n.Title, "txt")
|
|
if err != nil {
|
|
return note{}, err
|
|
}
|
|
|
|
idx := strings.Index(newTitle, "\n")
|
|
if idx != -1 {
|
|
newTitle = newTitle[:idx]
|
|
}
|
|
|
|
if len(newTitle) == 0 {
|
|
return n, nil
|
|
}
|
|
|
|
n.Title = newTitle
|
|
|
|
err = putNote(n)
|
|
if err != nil {
|
|
return note{}, nil
|
|
}
|
|
|
|
showNotes(s)
|
|
s.Sync()
|
|
|
|
return n, nil
|
|
}
|
|
|
|
func emitStr(s tcell.Screen, x int, y int, style tcell.Style, str string) {
|
|
for _, c := range str {
|
|
var comb []rune
|
|
w := runewidth.RuneWidth(c)
|
|
|
|
if w == 0 {
|
|
comb = []rune{c}
|
|
c = ' '
|
|
w = 1
|
|
}
|
|
|
|
s.SetContent(x, y, c, comb, style)
|
|
x += w
|
|
}
|
|
}
|
|
|
|
func showNotes(s tcell.Screen) {
|
|
uiState = uiMain
|
|
|
|
s.Fill(' ', tcell.StyleDefault)
|
|
|
|
for idx, note := range notes {
|
|
style := tcell.StyleDefault
|
|
|
|
if idx == selectedNote {
|
|
style = style.Foreground(tcell.ColorBlack).Background(tcell.ColorWhite)
|
|
}
|
|
|
|
text := fmt.Sprint(idx) + ". " + note.Title
|
|
emitStr(s, 1, 1+idx*lineHeight, style, text)
|
|
}
|
|
|
|
_, h := s.Size()
|
|
emitStr(s, 1, h-1, tcell.StyleDefault.Foreground(tcell.ColorBlue), "n - new, r - rename, d - delete, Enter - edit, Esc - exit")
|
|
}
|
|
|
|
func showDelete(s tcell.Screen) {
|
|
uiState = uiDelete
|
|
|
|
s.Fill(' ', tcell.StyleDefault)
|
|
emitStr(s, 1, 1, tcell.StyleDefault, "Do you really want to delete the note '"+notes[selectedNote].Title+"'?")
|
|
emitStr(s, 1, 3, tcell.StyleDefault.Foreground(tcell.ColorBlue), "y - yes, n - no")
|
|
}
|
|
|
|
func showUI(s tcell.Screen) {
|
|
switch uiState {
|
|
case uiMain:
|
|
showNotes(s)
|
|
case uiDelete:
|
|
showDelete(s)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
var err error
|
|
notes, err = loadNotes()
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
s, err := tcell.NewScreen()
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
if err := s.Init(); err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
defStyle := tcell.StyleDefault.Background(tcell.ColorBlack).Foreground(tcell.ColorWhite)
|
|
s.SetStyle(defStyle)
|
|
|
|
showNotes(s)
|
|
|
|
for {
|
|
switch ev := s.PollEvent().(type) {
|
|
case *tcell.EventResize:
|
|
showUI(s)
|
|
s.Sync()
|
|
case *tcell.EventKey:
|
|
switch uiState {
|
|
case uiMain:
|
|
switch ev.Key() {
|
|
case tcell.KeyUp:
|
|
if selectedNote > 0 {
|
|
selectedNote--
|
|
}
|
|
|
|
showNotes(s)
|
|
s.Show()
|
|
|
|
case tcell.KeyDown:
|
|
if selectedNote < len(notes)-1 {
|
|
selectedNote++
|
|
}
|
|
|
|
showNotes(s)
|
|
s.Show()
|
|
|
|
case tcell.KeyEnter:
|
|
n, err := editNote(s, notes[selectedNote])
|
|
if err != nil {
|
|
s.Fini()
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
notes[selectedNote] = n
|
|
|
|
case tcell.KeyEscape:
|
|
s.Fini()
|
|
os.Exit(1)
|
|
|
|
case tcell.KeyRune:
|
|
switch ev.Rune() {
|
|
case 'r':
|
|
n, err := editNoteTitle(s, notes[selectedNote])
|
|
if err != nil {
|
|
s.Fini()
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
notes[selectedNote] = n
|
|
|
|
showNotes(s)
|
|
s.Sync()
|
|
|
|
case 'n':
|
|
title, err := edit(s, "", "txt")
|
|
if err != nil {
|
|
s.Fini()
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
if len(title) == 0 {
|
|
showNotes(s)
|
|
s.Sync()
|
|
break
|
|
}
|
|
|
|
n2, err := postNote(note{Title: title})
|
|
if err != nil {
|
|
s.Fini()
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
notes = append(notes, n2)
|
|
|
|
selectedNote = len(notes) - 1
|
|
|
|
showNotes(s)
|
|
s.Sync()
|
|
|
|
case 'd':
|
|
showDelete(s)
|
|
s.Show()
|
|
}
|
|
}
|
|
|
|
case uiDelete:
|
|
switch ev.Key() {
|
|
case tcell.KeyRune:
|
|
switch ev.Rune() {
|
|
case 'y':
|
|
err := deleteNote(notes[selectedNote])
|
|
if err != nil {
|
|
s.Fini()
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
notes = append(notes[:selectedNote], notes[selectedNote+1:]...)
|
|
|
|
if selectedNote >= len(notes) {
|
|
selectedNote = len(notes) - 1
|
|
}
|
|
|
|
showNotes(s)
|
|
s.Show()
|
|
case 'n':
|
|
showNotes(s)
|
|
s.Show()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|