NoteClient/main.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()
}
}
}
}
}
}