From 16e5f55323d61f75922c34b6bb2cd1ceedd91eec Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Fri, 4 Jan 2019 21:48:19 -0500 Subject: [PATCH] YN callbacks and better multi cursor --- cmd/micro/action/actions.go | 71 ++++++++++++++++++++------------- cmd/micro/action/bufhandler.go | 3 ++ cmd/micro/action/infohandler.go | 18 +++++++-- cmd/micro/buffer/cursor.go | 2 +- cmd/micro/info/infobuffer.go | 28 +++++++++++-- 5 files changed, 86 insertions(+), 36 deletions(-) diff --git a/cmd/micro/action/actions.go b/cmd/micro/action/actions.go index 4aed393f..768d0090 100644 --- a/cmd/micro/action/actions.go +++ b/cmd/micro/action/actions.go @@ -138,14 +138,14 @@ func (h *BufHandler) CursorLeft() bool { // CursorRight moves the cursor right func (h *BufHandler) CursorRight() bool { - h.Cursor.Deselect(true) + h.Cursor.Deselect(false) h.Cursor.Right() return true } // WordRight moves the cursor one word to the right func (h *BufHandler) WordRight() bool { - h.Cursor.Deselect(true) + h.Cursor.Deselect(false) h.Cursor.WordRight() return true } @@ -987,8 +987,17 @@ func (h *BufHandler) Escape() bool { // Quit this will close the current tab or view that is open func (h *BufHandler) Quit() bool { - screen.Screen.Fini() - os.Exit(0) + if h.Buf.Modified() { + InfoBar.YNPrompt("Save changes to "+h.Buf.GetName()+" before closing? (y,n,esc)", func(yes, canceled bool) { + if !canceled && !yes { + screen.Screen.Fini() + os.Exit(0) + } + }) + } else { + screen.Screen.Fini() + os.Exit(0) + } return false } @@ -1055,31 +1064,36 @@ func (h *BufHandler) SpawnMultiCursor() bool { spawner := h.Buf.GetCursor(h.Buf.NumCursors() - 1) if !spawner.HasSelection() { spawner.SelectWord() - } else { - sel := spawner.GetSelection() - searchStart := spawner.CurSelection[1] - - match, found, err := h.Buf.FindNext(string(sel), searchStart, true) - if err != nil { - InfoBar.Error(err) - } - if found { - c := buffer.NewCursor(h.Buf, buffer.Loc{}) - c.SetSelectionStart(match[0]) - c.SetSelectionEnd(match[1]) - c.OrigSelection[0] = c.CurSelection[0] - c.OrigSelection[1] = c.CurSelection[1] - c.Loc = c.CurSelection[1] - - h.Buf.AddCursor(c) - h.Buf.MergeCursors() - h.Win.Relocate() - } else { - InfoBar.Message("No matches found") - } + h.multiWord = true + return true } - return false + sel := spawner.GetSelection() + searchStart := spawner.CurSelection[1] + + search := string(sel) + if h.multiWord { + search = "\\b" + search + "\\b" + } + match, found, err := h.Buf.FindNext(search, searchStart, true) + if err != nil { + InfoBar.Error(err) + } + if found { + c := buffer.NewCursor(h.Buf, buffer.Loc{}) + c.SetSelectionStart(match[0]) + c.SetSelectionEnd(match[1]) + c.OrigSelection[0] = c.CurSelection[0] + c.OrigSelection[1] = c.CurSelection[1] + c.Loc = c.CurSelection[1] + + h.Buf.AddCursor(c) + h.Buf.MergeCursors() + } else { + InfoBar.Message("No matches found") + } + + return true } // SpawnMultiCursorSelect adds a cursor at the beginning of each line of a selection @@ -1158,6 +1172,8 @@ func (h *BufHandler) RemoveMultiCursor() bool { if h.Buf.NumCursors() > 1 { h.Buf.RemoveCursor(h.Buf.NumCursors() - 1) h.Buf.UpdateCursors() + } else { + h.multiWord = false } return false } @@ -1165,5 +1181,6 @@ func (h *BufHandler) RemoveMultiCursor() bool { // RemoveAllMultiCursors removes all cursors except the base cursor func (h *BufHandler) RemoveAllMultiCursors() bool { h.Buf.ClearCursors() + h.multiWord = false return true } diff --git a/cmd/micro/action/bufhandler.go b/cmd/micro/action/bufhandler.go index 2c925fde..74436312 100644 --- a/cmd/micro/action/bufhandler.go +++ b/cmd/micro/action/bufhandler.go @@ -90,6 +90,9 @@ type BufHandler struct { // Last search stores the last successful search for FindNext and FindPrev lastSearch string + // Should the current multiple cursor selection search based on word or + // based on selection (false for selection, true for word) + multiWord bool splitID uint64 } diff --git a/cmd/micro/action/infohandler.go b/cmd/micro/action/infohandler.go index b2941ec7..bc0a5ad1 100644 --- a/cmd/micro/action/infohandler.go +++ b/cmd/micro/action/infohandler.go @@ -34,10 +34,18 @@ func (h *InfoHandler) HandleEvent(event tcell.Event) { done := h.DoKeyEvent(ke) if !done && e.Key() == tcell.KeyRune { - h.DoRuneInsert(e.Rune()) - done = true + if e.Rune() == 'y' && h.HasYN { + h.YNResp = true + h.DonePrompt(false) + } else if e.Rune() == 'n' && h.HasYN { + h.YNResp = false + h.DonePrompt(false) + } else if !h.HasYN { + h.DoRuneInsert(e.Rune()) + done = true + } } - if done && h.HasPrompt { + if done && h.HasPrompt && !h.HasYN { resp := strings.TrimSpace(string(h.LineBytes(0))) hist := h.History[h.PromptType] hist[h.HistoryNum] = resp @@ -144,7 +152,9 @@ func (h *InfoHandler) InsertTab() { // TODO: autocomplete } func (h *InfoHandler) InsertNewline() { - h.DonePrompt(false) + if !h.HasYN { + h.DonePrompt(false) + } } func (h *InfoHandler) Quit() { h.DonePrompt(true) diff --git a/cmd/micro/buffer/cursor.go b/cmd/micro/buffer/cursor.go index aded3ba2..3bb94c2c 100644 --- a/cmd/micro/buffer/cursor.go +++ b/cmd/micro/buffer/cursor.go @@ -167,7 +167,7 @@ func (c *Cursor) Deselect(start bool) { if start { c.Loc = c.CurSelection[0] } else { - c.Loc = c.CurSelection[1] + c.Loc = c.CurSelection[1].Move(-1, c.buf) } c.ResetSelection() c.StoreVisualX() diff --git a/cmd/micro/info/infobuffer.go b/cmd/micro/info/infobuffer.go index 91604e43..1a29cfc1 100644 --- a/cmd/micro/info/infobuffer.go +++ b/cmd/micro/info/infobuffer.go @@ -15,10 +15,12 @@ type InfoBuf struct { HasPrompt bool HasMessage bool HasError bool + HasYN bool PromptType string - Msg string + Msg string + YNResp bool // This map stores the history for all the different kinds of uses Prompt has // It's a map of history type -> history array @@ -30,6 +32,7 @@ type InfoBuf struct { PromptCallback func(resp string, canceled bool) EventCallback func(resp string) + YNCallback func(yes bool, canceled bool) } // NewBuffer returns a new infobuffer @@ -92,16 +95,27 @@ func (i *InfoBuf) Prompt(prompt string, msg string, ptype string, eventcb func(s i.PromptType = ptype i.Msg = prompt i.HasPrompt = true - i.HasMessage, i.HasError = false, false + i.HasMessage, i.HasError, i.HasYN = false, false, false i.PromptCallback = donecb i.EventCallback = eventcb i.Buffer.Insert(i.Buffer.Start(), msg) } +func (i *InfoBuf) YNPrompt(prompt string, donecb func(bool, bool)) { + if i.HasPrompt { + i.DonePrompt(true) + } + + i.Msg = prompt + i.HasPrompt = true + i.HasYN = true + i.HasMessage, i.HasError = false, false + i.YNCallback = donecb +} + // DonePrompt finishes the current prompt and indicates whether or not it was canceled func (i *InfoBuf) DonePrompt(canceled bool) { - i.HasPrompt = false - if i.PromptCallback != nil { + if i.PromptCallback != nil && !i.HasYN { if canceled { i.PromptCallback("", true) h := i.History[i.PromptType] @@ -113,8 +127,14 @@ func (i *InfoBuf) DonePrompt(canceled bool) { h[len(h)-1] = resp } } + if i.YNCallback != nil && i.HasYN { + i.YNCallback(i.YNResp, canceled) + } + i.HasPrompt = false + i.HasYN = false i.PromptCallback = nil i.EventCallback = nil + i.YNCallback = nil i.Replace(i.Start(), i.End(), "") }