Skip to content

Commit

Permalink
Merge pull request #437 from noborus/save-buffer
Browse files Browse the repository at this point in the history
Add save buffer
  • Loading branch information
noborus authored Sep 6, 2023
2 parents 5409ad6 + 01a7a29 commit 5b00d4b
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 23 deletions.
14 changes: 6 additions & 8 deletions oviewer/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func (root *Root) toggleWrapMode() {
// Move cursor to correct position
x, err := root.Doc.optimalX(m.columnCursor)
if err != nil {
log.Println(err)
root.setMessageLog(err.Error())
}
// Move if off screen
if x < m.x || x > m.x+(root.scr.vWidth-root.scr.startX) {
Expand Down Expand Up @@ -112,14 +112,13 @@ func (root *Root) closeFile() {
return
}
root.Doc.requestClose()
root.setMessagef("close file %s", root.Doc.FileName)
log.Printf("close file %s", root.Doc.FileName)
root.setMessageLogf("close file %s", root.Doc.FileName)
}

// reload performs a reload of the current document.
func (root *Root) reload(m *Document) {
if err := m.reload(); err != nil {
root.setMessagef("cannot reload: %s", err)
root.setMessageLogf("cannot reload: %s", err)
return
}
root.releaseEventBuffer()
Expand Down Expand Up @@ -347,11 +346,11 @@ func (root *Root) suspend() {
c.Stdout = os.Stdout
c.Stderr = os.Stderr
if err := c.Run(); err != nil {
log.Println(err)
root.setMessageLog(err.Error())
}
fmt.Println("resume ov")
if err := root.Screen.Resume(); err != nil {
log.Println(err)
root.setMessageLog(err.Error())
}
log.Println("Resume")
}
Expand Down Expand Up @@ -428,8 +427,7 @@ func (root *Root) setWatchInterval(input string) {
root.Doc.watchMode()
}
atomic.StoreInt32(&root.Doc.watchRestart, 1)
log.Printf("Set watch interval %d", interval)
root.setMessagef("Set watch interval %d", interval)
root.setMessageLogf("Set watch interval %d", interval)
}

// setWriteBA sets the number before and after the line
Expand Down
6 changes: 2 additions & 4 deletions oviewer/doclist.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package oviewer

import (
"log"
"sync/atomic"
)

Expand All @@ -27,7 +26,7 @@ func (root *Root) hasDocChanged() bool {

// addDocument adds a document and displays it.
func (root *Root) addDocument(m *Document) {
root.setMessagef("add %s", m.FileName)
root.setMessageLogf("add %s", m.FileName)
m.general = root.Config.General
m.regexpCompile()

Expand All @@ -47,8 +46,7 @@ func (root *Root) closeDocument() {
return
}

root.setMessagef("close [%d]%s", root.CurrentDoc, root.Doc.FileName)
log.Printf("close [%d]%s", root.CurrentDoc, root.Doc.FileName)
root.setMessageLogf("close [%d]%s", root.CurrentDoc, root.Doc.FileName)
root.mu.Lock()
root.DocList[root.CurrentDoc].requestClose()
root.DocList = append(root.DocList[:root.CurrentDoc], root.DocList[root.CurrentDoc+1:]...)
Expand Down
7 changes: 5 additions & 2 deletions oviewer/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ func (m *Document) CurrentLN() int {
}

// Export exports the document in the specified range.
func (m *Document) Export(w io.Writer, start int, end int) {
func (m *Document) Export(w io.Writer, start int, end int) error {
end = min(end, m.BufEndNum()-1)
startChunk, startCn := chunkLineNum(start)
endChunk, endCn := chunkLineNum(end)
Expand All @@ -336,9 +336,12 @@ func (m *Document) Export(w io.Writer, start int, end int) {
ecn = endCn + 1
}
chunk := m.store.chunks[chunkNum]
m.store.export(w, chunk, scn, ecn)
if err := m.store.export(w, chunk, scn, ecn); err != nil {
return err
}
scn = 0
}
return nil
}

// BufStartNum return start line number.
Expand Down
4 changes: 3 additions & 1 deletion oviewer/document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ func TestDocument_Export(t *testing.T) {
for !m.BufEOF() {
}
m.bottomLN = m.BufEndNum()
m.Export(w, tt.args.start, tt.args.end)
if err := m.Export(w, tt.args.start, tt.args.end); err != nil {
t.Fatal(err)
}
if gotW := w.String(); gotW != tt.wantW {
t.Errorf("Document.Export() = %v, want %v", gotW, tt.wantW)
}
Expand Down
2 changes: 2 additions & 0 deletions oviewer/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ func (root *Root) eventLoop(ctx context.Context, quitChan chan<- struct{}) {
root.setMultiColor(ev.value)
case *eventJumpTarget:
root.setJumpTarget(ev.value)
case *eventSaveBuffer:
root.saveBuffer(ev.value)

// tcell events
case *tcell.EventResize:
Expand Down
3 changes: 3 additions & 0 deletions oviewer/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
SectionStart // SectionStart is a section start position input mode.
MultiColor // MultiColor is multi-word coloring.
JumpTarget // JumpTarget is the position to display the search results.
SaveBuffer // SaveBuffer is the save buffer.
)

// Input represents the status of various inputs.
Expand All @@ -47,6 +48,7 @@ type Input struct {
SectionStartCandidate *candidate
MultiColorCandidate *candidate
JumpTargetCandidate *candidate
SaveBufferCandidate *candidate

value string
cursorX int
Expand All @@ -67,6 +69,7 @@ func NewInput() *Input {
i.SectionStartCandidate = sectionStartCandidate()
i.MultiColorCandidate = multiColorCandidate()
i.JumpTargetCandidate = jumpTargetCandidate()
i.SaveBufferCandidate = saveBufferCandidate()

i.Event = &eventNormal{}
return &i
Expand Down
66 changes: 66 additions & 0 deletions oviewer/input_savebuffer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package oviewer

import "github.com/gdamore/tcell/v2"

// setSaveBuffer is a wrapper to move to setSaveBufferMode.
func (root *Root) setSaveBuffer() {
if root.Doc.seekable {
root.setMessage("Saving regular files is not supported")
return
}
root.setSaveBufferMode()
}

// setSaveBufferMode sets the inputMode to SaveBuffer.
func (root *Root) setSaveBufferMode() {
input := root.input
input.value = ""
input.cursorX = 0
input.Event = newSaveBufferEvent(input.SaveBufferCandidate)
}

// saveBufferCandidate returns the candidate to set to default.
func saveBufferCandidate() *candidate {
return &candidate{
list: []string{},
}
}

// eventSaveBuffer represents the mode input mode.
type eventSaveBuffer struct {
tcell.EventTime
clist *candidate
value string
}

// newSaveBufferEvent returns SaveBufferModeEvent.
func newSaveBufferEvent(clist *candidate) *eventSaveBuffer {
return &eventSaveBuffer{clist: clist}
}

// Mode returns InputMode.
func (e *eventSaveBuffer) Mode() InputMode {
return SaveBuffer
}

// Prompt returns the prompt string in the input field.
func (e *eventSaveBuffer) Prompt() string {
return "(Save)file:"
}

// Confirm returns the event when the input is confirmed.
func (e *eventSaveBuffer) Confirm(str string) tcell.Event {
e.value = str
e.SetEventNow()
return e
}

// Up returns strings when the up key is pressed during input.
func (e *eventSaveBuffer) Up(str string) string {
return e.clist.up()
}

// Down returns strings when the down key is pressed during input.
func (e *eventSaveBuffer) Down(str string) string {
return e.clist.down()
}
4 changes: 4 additions & 0 deletions oviewer/keybind.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const (
actionToggleMouse = "toggle_mouse"
actionMultiColor = "multi_color"
actionJumpTarget = "jump_target"
actionSaveBuffer = "save_buffer"

inputCaseSensitive = "input_casesensitive"
inputSmartCaseSensitive = "input_smart_casesensitive"
Expand Down Expand Up @@ -150,6 +151,7 @@ func (root *Root) handlers() map[string]func() {
actionToggleMouse: root.toggleMouse,
actionMultiColor: root.setMultiColorMode,
actionJumpTarget: root.setJumpTargetMode,
actionSaveBuffer: root.setSaveBuffer,

inputCaseSensitive: root.inputCaseSensitive,
inputSmartCaseSensitive: root.inputSmartCaseSensitive,
Expand Down Expand Up @@ -230,6 +232,7 @@ func defaultKeyBinds() KeyBind {
actionSuspend: {"ctrl+z"},
actionMultiColor: {"."},
actionJumpTarget: {"j"},
actionSaveBuffer: {"S"},

inputCaseSensitive: {"alt+c"},
inputSmartCaseSensitive: {"alt+s"},
Expand Down Expand Up @@ -258,6 +261,7 @@ func (k KeyBind) String() string {
k.writeKeyBind(&b, actionFollow, "follow mode toggle")
k.writeKeyBind(&b, actionFollowAll, "follow all mode toggle")
k.writeKeyBind(&b, actionToggleMouse, "enable/disable mouse")
k.writeKeyBind(&b, actionSaveBuffer, "save buffer to file")

fmt.Fprint(&b, "\n\tMoving\n")
fmt.Fprint(&b, "\n")
Expand Down
34 changes: 29 additions & 5 deletions oviewer/oviewer.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,12 @@ func (root *Root) Close() {
root.Screen.Fini()
}

// setMessagef displays a formatted message in status.
func (root *Root) setMessagef(format string, a ...any) {
msg := fmt.Sprintf(format, a...)
root.setMessage(msg)
}

// setMessage displays a message in status.
func (root *Root) setMessage(msg string) {
if root.message == msg {
Expand All @@ -697,19 +703,32 @@ func (root *Root) setMessage(msg string) {
root.Show()
}

func (root *Root) setMessagef(format string, a ...any) {
// setMessageLogf displays a formatted message in the status and outputs it to the log.
func (root *Root) setMessageLogf(format string, a ...any) {
msg := fmt.Sprintf(format, a...)
root.setMessage(msg)
root.setMessageLog(msg)
}

// setMessageLog displays a message in the status and outputs it to the log.
func (root *Root) setMessageLog(msg string) {
if root.message == msg {
return
}
root.message = msg
log.Print(msg)
root.drawStatus()
root.Show()
}

// debugMessage outputs a debug message.
func (root *Root) debugMessage(msg string) {
if !root.Debug {
return
}
if root.Doc == root.logDoc {
return
}
root.message = msg

if len(msg) == 0 {
return
}
Expand Down Expand Up @@ -899,15 +918,19 @@ func (root *Root) WriteOriginal() {
end = m.topLN + root.AfterWriteOriginal - 1
}

m.Export(os.Stdout, start, end)
if err := m.Export(os.Stdout, start, end); err != nil {
log.Println(err)
}
}

// WriteLog write to the log terminal.
func (root *Root) WriteLog() {
m := root.logDoc
start := max(0, m.BufEndNum()-MaxWriteLog)
end := m.BufEndNum()
m.Export(os.Stdout, start, end)
if err := m.Export(os.Stdout, start, end); err != nil {
log.Println(err)
}
}

// lineNumber returns the line information from y on the screen.
Expand All @@ -918,6 +941,7 @@ func (scr SCR) lineNumber(y int) LineNumber {
return scr.numbers[0]
}

// debugNumOfChunk outputs the number of chunks.
func (root *Root) debugNumOfChunk() {
if !root.Debug {
return
Expand Down
73 changes: 73 additions & 0 deletions oviewer/save.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package oviewer

import (
"os"
"strings"

"github.com/gdamore/tcell/v2"
)

type saveSelection string

const (
saveCancel saveSelection = "cancel"
saveOverWrite saveSelection = "overwrite"
saveAppend saveSelection = "append"
)

// saveBuffer saves the buffer to the specified file.
func (root *Root) saveBuffer(input string) {
fileName := strings.TrimSpace(input)

perm := os.FileMode(0644)
flag := os.O_WRONLY | os.O_CREATE
_, err := os.Stat(fileName)
if err == nil {
root.setMessagef("overwrite? (O)overwrite, (A)Append, (N)cancel:")
switch root.saveConfirm() {
case saveOverWrite:
flag = os.O_WRONLY | os.O_TRUNC
case saveAppend:
flag |= os.O_APPEND
case saveCancel:
root.setMessage("save cancel")
return
}
}

file, err := os.OpenFile(fileName, flag, perm)
if err != nil {
root.setMessageLogf("cannot save: %s:%s", fileName, err)
return
}
defer file.Close()

if err := root.Doc.Export(file, root.Doc.BufStartNum(), root.Doc.BufEndNum()); err != nil {
root.setMessageLogf("cannot save: %s:%s", fileName, err)
return
}

root.setMessageLogf("saved %s", fileName)
}

// saveConfirm waits for the user to confirm the save.
func (root *Root) saveConfirm() saveSelection {
for {
ev := root.Screen.PollEvent()
switch ev := ev.(type) {
case *tcell.EventKey:
if ev.Key() == tcell.KeyRune {
switch ev.Rune() {
case 'o', 'O':
return saveOverWrite
case 'a', 'A':
return saveAppend
case 'n', 'N', 'q', 'Q':
return saveCancel
}
} else if ev.Key() == tcell.KeyEscape {
return saveCancel
}
}
}
}
Loading

0 comments on commit 5b00d4b

Please sign in to comment.