mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-16 05:47:06 +09:00
Implement searching
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package action
|
package action
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -560,6 +561,37 @@ func (h *BufHandler) SaveAs() bool {
|
|||||||
|
|
||||||
// Find opens a prompt and searches forward for the input
|
// Find opens a prompt and searches forward for the input
|
||||||
func (h *BufHandler) Find() bool {
|
func (h *BufHandler) Find() bool {
|
||||||
|
InfoBar.Prompt("Find: ", "", func(resp string) {
|
||||||
|
match, found, _ := h.Buf.FindNext(resp, h.Cursor.Loc, true)
|
||||||
|
if found {
|
||||||
|
h.Cursor.SetSelectionStart(match[0])
|
||||||
|
h.Cursor.SetSelectionEnd(match[1])
|
||||||
|
h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0]
|
||||||
|
h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1]
|
||||||
|
} else {
|
||||||
|
log.Println("RESET")
|
||||||
|
h.Cursor.ResetSelection()
|
||||||
|
}
|
||||||
|
}, func(resp string, canceled bool) {
|
||||||
|
if !canceled {
|
||||||
|
match, found, err := h.Buf.FindNext(resp, h.Cursor.Loc, true)
|
||||||
|
if err != nil {
|
||||||
|
InfoBar.Error(err)
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
h.Cursor.SetSelectionStart(match[0])
|
||||||
|
h.Cursor.SetSelectionEnd(match[1])
|
||||||
|
h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0]
|
||||||
|
h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1]
|
||||||
|
h.Cursor.Loc = h.Cursor.CurSelection[1]
|
||||||
|
} else {
|
||||||
|
h.Cursor.ResetSelection()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
h.Cursor.ResetSelection()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -725,7 +757,7 @@ func (h *BufHandler) SelectAll() bool {
|
|||||||
|
|
||||||
// OpenFile opens a new file in the buffer
|
// OpenFile opens a new file in the buffer
|
||||||
func (h *BufHandler) OpenFile() bool {
|
func (h *BufHandler) OpenFile() bool {
|
||||||
InfoBar.Prompt("> ", "open ", func(resp string, canceled bool) {
|
InfoBar.Prompt("> ", "open ", nil, func(resp string, canceled bool) {
|
||||||
if !canceled {
|
if !canceled {
|
||||||
HandleCommand(resp)
|
HandleCommand(resp)
|
||||||
}
|
}
|
||||||
@@ -889,7 +921,7 @@ func (h *BufHandler) ShellMode() bool {
|
|||||||
|
|
||||||
// CommandMode lets the user enter a command
|
// CommandMode lets the user enter a command
|
||||||
func (h *BufHandler) CommandMode() bool {
|
func (h *BufHandler) CommandMode() bool {
|
||||||
InfoBar.Prompt("> ", "", func(resp string, canceled bool) {
|
InfoBar.Prompt("> ", "", nil, func(resp string, canceled bool) {
|
||||||
if !canceled {
|
if !canceled {
|
||||||
HandleCommand(resp)
|
HandleCommand(resp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,8 @@ modSearch:
|
|||||||
k = string(unicode.ToUpper(rune(k[0]))) + k[1:]
|
k = string(unicode.ToUpper(rune(k[0]))) + k[1:]
|
||||||
if code, ok := keyEvents["Ctrl"+k]; ok {
|
if code, ok := keyEvents["Ctrl"+k]; ok {
|
||||||
var r tcell.Key
|
var r tcell.Key
|
||||||
if code < 256 {
|
// Special case for escape, for some reason tcell doesn't send it with the esc character
|
||||||
|
if code < 256 && code != 27 {
|
||||||
r = code
|
r = code
|
||||||
}
|
}
|
||||||
// It is, we're done.
|
// It is, we're done.
|
||||||
@@ -118,7 +119,8 @@ modSearch:
|
|||||||
// See if we can find the key in bindingKeys
|
// See if we can find the key in bindingKeys
|
||||||
if code, ok := keyEvents[k]; ok {
|
if code, ok := keyEvents[k]; ok {
|
||||||
var r tcell.Key
|
var r tcell.Key
|
||||||
if code < 256 {
|
// Special case for escape, for some reason tcell doesn't send it with the esc character
|
||||||
|
if code < 256 && code != 27 {
|
||||||
r = code
|
r = code
|
||||||
}
|
}
|
||||||
return KeyEvent{
|
return KeyEvent{
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ func init() {
|
|||||||
BufMouseBindings = make(map[MouseEvent]BufMouseAction)
|
BufMouseBindings = make(map[MouseEvent]BufMouseAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BufMapKey maps a key event to an action
|
||||||
func BufMapKey(k KeyEvent, action string) {
|
func BufMapKey(k KeyEvent, action string) {
|
||||||
if f, ok := BufKeyActions[action]; ok {
|
if f, ok := BufKeyActions[action]; ok {
|
||||||
BufKeyStrings[k] = action
|
BufKeyStrings[k] = action
|
||||||
@@ -30,6 +31,8 @@ func BufMapKey(k KeyEvent, action string) {
|
|||||||
util.TermMessage("Error:", action, "does not exist")
|
util.TermMessage("Error:", action, "does not exist")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BufMapMouse maps a mouse event to an action
|
||||||
func BufMapMouse(k MouseEvent, action string) {
|
func BufMapMouse(k MouseEvent, action string) {
|
||||||
if f, ok := BufMouseActions[action]; ok {
|
if f, ok := BufMouseActions[action]; ok {
|
||||||
BufMouseBindings[k] = f
|
BufMouseBindings[k] = f
|
||||||
@@ -147,10 +150,13 @@ func (h *BufHandler) HandleEvent(event tcell.Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DoKeyEvent executes a key event by finding the action it is bound
|
||||||
|
// to and executing it (possibly multiple times for multiple cursors)
|
||||||
func (h *BufHandler) DoKeyEvent(e KeyEvent) bool {
|
func (h *BufHandler) DoKeyEvent(e KeyEvent) bool {
|
||||||
if action, ok := BufKeyBindings[e]; ok {
|
if action, ok := BufKeyBindings[e]; ok {
|
||||||
for _, a := range MultiActions {
|
estr := BufKeyStrings[e]
|
||||||
if a == BufKeyStrings[e] {
|
for _, s := range MultiActions {
|
||||||
|
if s == estr {
|
||||||
cursors := h.Buf.GetCursors()
|
cursors := h.Buf.GetCursors()
|
||||||
for _, c := range cursors {
|
for _, c := range cursors {
|
||||||
h.Buf.SetCurCursor(c.Num)
|
h.Buf.SetCurCursor(c.Num)
|
||||||
@@ -170,6 +176,8 @@ func (h *BufHandler) DoKeyEvent(e KeyEvent) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DoMouseEvent executes a mouse event by finding the action it is bound
|
||||||
|
// to and executing it
|
||||||
func (h *BufHandler) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
|
func (h *BufHandler) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
|
||||||
if action, ok := BufMouseBindings[e]; ok {
|
if action, ok := BufMouseBindings[e]; ok {
|
||||||
if action(h, te) {
|
if action(h, te) {
|
||||||
@@ -180,6 +188,8 @@ func (h *BufHandler) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DoRuneInsert inserts a given rune into the current buffer
|
||||||
|
// (possibly multiple times for multiple cursors)
|
||||||
func (h *BufHandler) DoRuneInsert(r rune) {
|
func (h *BufHandler) DoRuneInsert(r rune) {
|
||||||
cursors := h.Buf.GetCursors()
|
cursors := h.Buf.GetCursors()
|
||||||
for _, c := range cursors {
|
for _, c := range cursors {
|
||||||
@@ -199,6 +209,7 @@ func (h *BufHandler) DoRuneInsert(r rune) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BufKeyActions contains the list of all possible key actions the bufhandler could execute
|
||||||
var BufKeyActions = map[string]BufKeyAction{
|
var BufKeyActions = map[string]BufKeyAction{
|
||||||
"CursorUp": (*BufHandler).CursorUp,
|
"CursorUp": (*BufHandler).CursorUp,
|
||||||
"CursorDown": (*BufHandler).CursorDown,
|
"CursorDown": (*BufHandler).CursorDown,
|
||||||
@@ -297,13 +308,13 @@ var BufKeyActions = map[string]BufKeyAction{
|
|||||||
// This was changed to InsertNewline but I don't want to break backwards compatibility
|
// This was changed to InsertNewline but I don't want to break backwards compatibility
|
||||||
"InsertEnter": (*BufHandler).InsertNewline,
|
"InsertEnter": (*BufHandler).InsertNewline,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BufMouseActions contains the list of all possible mouse actions the bufhandler could execute
|
||||||
var BufMouseActions = map[string]BufMouseAction{
|
var BufMouseActions = map[string]BufMouseAction{
|
||||||
"MousePress": (*BufHandler).MousePress,
|
"MousePress": (*BufHandler).MousePress,
|
||||||
"MouseMultiCursor": (*BufHandler).MouseMultiCursor,
|
"MouseMultiCursor": (*BufHandler).MouseMultiCursor,
|
||||||
}
|
}
|
||||||
|
|
||||||
const funcPrefixLen = 21 // length of "action.(*BufHandler)."
|
|
||||||
|
|
||||||
// MultiActions is a list of actions that should be executed multiple
|
// MultiActions is a list of actions that should be executed multiple
|
||||||
// times if there are multiple cursors (one per cursor)
|
// times if there are multiple cursors (one per cursor)
|
||||||
// Generally actions that modify global editor state like quitting or
|
// Generally actions that modify global editor state like quitting or
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ func DefaultCommands() map[string]StrCommand {
|
|||||||
// enter
|
// enter
|
||||||
func CommandEditAction(prompt string) BufKeyAction {
|
func CommandEditAction(prompt string) BufKeyAction {
|
||||||
return func(h *BufHandler) bool {
|
return func(h *BufHandler) bool {
|
||||||
InfoBar.Prompt("> ", prompt, func(resp string, canceled bool) {
|
InfoBar.Prompt("> ", prompt, nil, func(resp string, canceled bool) {
|
||||||
if !canceled {
|
if !canceled {
|
||||||
HandleCommand(resp)
|
HandleCommand(resp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
|
|
||||||
type EditPane struct {
|
type EditPane struct {
|
||||||
display.Window
|
display.Window
|
||||||
Handler
|
*BufHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
type InfoPane struct {
|
type InfoPane struct {
|
||||||
display.Window
|
display.Window
|
||||||
Handler
|
*InfoHandler
|
||||||
*info.InfoBuf
|
*info.InfoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ func NewBufEditPane(x, y, width, height int, b *buffer.Buffer) *EditPane {
|
|||||||
// TODO: can probably replace editpane with bufhandler entirely
|
// TODO: can probably replace editpane with bufhandler entirely
|
||||||
w := display.NewBufWindow(x, y, width, height, b)
|
w := display.NewBufWindow(x, y, width, height, b)
|
||||||
e.Window = w
|
e.Window = w
|
||||||
e.Handler = NewBufHandler(b, w)
|
e.BufHandler = NewBufHandler(b, w)
|
||||||
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ func NewInfoBar() *InfoPane {
|
|||||||
ib := info.NewBuffer()
|
ib := info.NewBuffer()
|
||||||
w := display.NewInfoWindow(ib)
|
w := display.NewInfoWindow(ib)
|
||||||
e.Window = w
|
e.Window = w
|
||||||
e.Handler = NewBufHandler(ib.Buffer, w)
|
e.InfoHandler = NewInfoHandler(ib, w)
|
||||||
e.InfoBuf = ib
|
e.InfoBuf = ib
|
||||||
|
|
||||||
return e
|
return e
|
||||||
|
|||||||
158
cmd/micro/action/infohandler.go
Normal file
158
cmd/micro/action/infohandler.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package action
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zyedidia/micro/cmd/micro/display"
|
||||||
|
"github.com/zyedidia/micro/cmd/micro/info"
|
||||||
|
"github.com/zyedidia/tcell"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InfoKeyAction func(*InfoHandler)
|
||||||
|
|
||||||
|
type InfoHandler struct {
|
||||||
|
*BufHandler
|
||||||
|
*info.InfoBuf
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInfoHandler(ib *info.InfoBuf, w display.Window) *InfoHandler {
|
||||||
|
ih := new(InfoHandler)
|
||||||
|
ih.InfoBuf = ib
|
||||||
|
ih.BufHandler = NewBufHandler(ib.Buffer, w)
|
||||||
|
|
||||||
|
return ih
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *InfoHandler) HandleEvent(event tcell.Event) {
|
||||||
|
switch e := event.(type) {
|
||||||
|
case *tcell.EventKey:
|
||||||
|
ke := KeyEvent{
|
||||||
|
code: e.Key(),
|
||||||
|
mod: e.Modifiers(),
|
||||||
|
r: e.Rune(),
|
||||||
|
}
|
||||||
|
|
||||||
|
done := h.DoKeyEvent(ke)
|
||||||
|
if !done && e.Key() == tcell.KeyRune {
|
||||||
|
h.DoRuneInsert(e.Rune())
|
||||||
|
}
|
||||||
|
case *tcell.EventMouse:
|
||||||
|
h.BufHandler.HandleEvent(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *InfoHandler) DoKeyEvent(e KeyEvent) bool {
|
||||||
|
done := false
|
||||||
|
if action, ok := BufKeyBindings[e]; ok {
|
||||||
|
estr := BufKeyStrings[e]
|
||||||
|
for _, s := range InfoNones {
|
||||||
|
if s == estr {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for s, a := range InfoOverrides {
|
||||||
|
if s == estr {
|
||||||
|
done = true
|
||||||
|
a(h)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !done {
|
||||||
|
done = action(h.BufHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if done && h.EventCallback != nil {
|
||||||
|
h.EventCallback(strings.TrimSpace(string(h.LineBytes(0))))
|
||||||
|
}
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *InfoHandler) DoRuneInsert(r rune) {
|
||||||
|
h.BufHandler.DoRuneInsert(r)
|
||||||
|
if h.EventCallback != nil {
|
||||||
|
h.EventCallback(strings.TrimSpace(string(h.LineBytes(0))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoNones is a list of actions that should have no effect when executed
|
||||||
|
// by an infohandler
|
||||||
|
var InfoNones = []string{
|
||||||
|
"Save",
|
||||||
|
"SaveAll",
|
||||||
|
"SaveAs",
|
||||||
|
"Find",
|
||||||
|
"FindNext",
|
||||||
|
"FindPrevious",
|
||||||
|
"Center",
|
||||||
|
"DuplicateLine",
|
||||||
|
"MoveLinesUp",
|
||||||
|
"MoveLinesDown",
|
||||||
|
"OpenFile",
|
||||||
|
"Start",
|
||||||
|
"End",
|
||||||
|
"PageUp",
|
||||||
|
"PageDown",
|
||||||
|
"SelectPageUp",
|
||||||
|
"SelectPageDown",
|
||||||
|
"HalfPageUp",
|
||||||
|
"HalfPageDown",
|
||||||
|
"ToggleHelp",
|
||||||
|
"ToggleKeyMenu",
|
||||||
|
"ToggleRuler",
|
||||||
|
"JumpLine",
|
||||||
|
"ClearStatus",
|
||||||
|
"ShellMode",
|
||||||
|
"CommandMode",
|
||||||
|
"AddTab",
|
||||||
|
"PreviousTab",
|
||||||
|
"NextTab",
|
||||||
|
"NextSplit",
|
||||||
|
"PreviousSplit",
|
||||||
|
"Unsplit",
|
||||||
|
"VSplit",
|
||||||
|
"HSplit",
|
||||||
|
"ToggleMacro",
|
||||||
|
"PlayMacro",
|
||||||
|
"Suspend",
|
||||||
|
"ScrollUp",
|
||||||
|
"ScrollDown",
|
||||||
|
"SpawnMultiCursor",
|
||||||
|
"SpawnMultiCursorSelect",
|
||||||
|
"RemoveMultiCursor",
|
||||||
|
"RemoveAllMultiCursors",
|
||||||
|
"SkipMultiCursor",
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoOverrides is the list of actions which have been overriden
|
||||||
|
// by the infohandler
|
||||||
|
var InfoOverrides = map[string]InfoKeyAction{
|
||||||
|
"CursorUp": (*InfoHandler).CursorUp,
|
||||||
|
"CursorDown": (*InfoHandler).CursorDown,
|
||||||
|
"InsertNewline": (*InfoHandler).InsertNewline,
|
||||||
|
"InsertTab": (*InfoHandler).InsertTab,
|
||||||
|
"Escape": (*InfoHandler).Escape,
|
||||||
|
"Quit": (*InfoHandler).Quit,
|
||||||
|
"QuitAll": (*InfoHandler).QuitAll,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *InfoHandler) CursorUp() {
|
||||||
|
// TODO: history
|
||||||
|
}
|
||||||
|
func (h *InfoHandler) CursorDown() {
|
||||||
|
// TODO: history
|
||||||
|
}
|
||||||
|
func (h *InfoHandler) InsertTab() {
|
||||||
|
// TODO: autocomplete
|
||||||
|
}
|
||||||
|
func (h *InfoHandler) InsertNewline() {
|
||||||
|
h.DonePrompt(false)
|
||||||
|
}
|
||||||
|
func (h *InfoHandler) Quit() {
|
||||||
|
h.DonePrompt(true)
|
||||||
|
}
|
||||||
|
func (h *InfoHandler) QuitAll() {
|
||||||
|
h.DonePrompt(true)
|
||||||
|
}
|
||||||
|
func (h *InfoHandler) Escape() {
|
||||||
|
h.DonePrompt(true)
|
||||||
|
}
|
||||||
93
cmd/micro/buffer/search.go
Normal file
93
cmd/micro/buffer/search.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package buffer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/zyedidia/micro/cmd/micro/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *Buffer) findDown(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
|
||||||
|
start.Y = util.Clamp(start.Y, 0, b.LinesNum()-1)
|
||||||
|
|
||||||
|
for i := start.Y; i <= end.Y; i++ {
|
||||||
|
l := b.LineBytes(i)
|
||||||
|
charpos := 0
|
||||||
|
|
||||||
|
if i == start.Y {
|
||||||
|
nchars := utf8.RuneCount(l)
|
||||||
|
start.X = util.Clamp(start.X, 0, nchars-1)
|
||||||
|
l = util.SliceEnd(l, start.X)
|
||||||
|
charpos = start.X
|
||||||
|
}
|
||||||
|
|
||||||
|
match := r.FindIndex(l)
|
||||||
|
|
||||||
|
if match != nil {
|
||||||
|
start := Loc{charpos + util.RunePos(l, match[0]), i}
|
||||||
|
end := Loc{charpos + util.RunePos(l, match[1]), i}
|
||||||
|
return [2]Loc{start, end}, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [2]Loc{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) findUp(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
|
||||||
|
start.Y = util.Clamp(start.Y, 0, b.LinesNum()-1)
|
||||||
|
|
||||||
|
for i := start.Y; i >= end.Y; i-- {
|
||||||
|
l := b.LineBytes(i)
|
||||||
|
|
||||||
|
if i == start.Y {
|
||||||
|
nchars := utf8.RuneCount(l)
|
||||||
|
start.X = util.Clamp(start.X, 0, nchars-1)
|
||||||
|
l = util.SliceStart(l, start.X)
|
||||||
|
}
|
||||||
|
|
||||||
|
match := r.FindIndex(l)
|
||||||
|
|
||||||
|
if match != nil {
|
||||||
|
start := Loc{util.RunePos(l, match[0]), i}
|
||||||
|
end := Loc{util.RunePos(l, match[1]), i}
|
||||||
|
return [2]Loc{start, end}, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [2]Loc{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindNext finds the next occurrence of a given string in the buffer
|
||||||
|
// It returns the start and end location of the match (if found) and
|
||||||
|
// a boolean indicating if it was found
|
||||||
|
// May also return an error if the search regex is invalid
|
||||||
|
func (b *Buffer) FindNext(s string, from Loc, down bool) ([2]Loc, bool, error) {
|
||||||
|
if s == "" {
|
||||||
|
return [2]Loc{}, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var r *regexp.Regexp
|
||||||
|
var err error
|
||||||
|
if b.Settings["ignorecase"].(bool) {
|
||||||
|
r, err = regexp.Compile("(?i)" + s)
|
||||||
|
} else {
|
||||||
|
r, err = regexp.Compile(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return [2]Loc{}, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
var l [2]Loc
|
||||||
|
if down {
|
||||||
|
l, found = b.findDown(r, from, b.End())
|
||||||
|
if !found {
|
||||||
|
l, found = b.findDown(r, b.Start(), from)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l, found = b.findUp(r, from, b.Start())
|
||||||
|
if !found {
|
||||||
|
l, found = b.findUp(r, b.End(), from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l, found, nil
|
||||||
|
}
|
||||||
@@ -317,7 +317,7 @@ func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc, r rune) (tcell.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *BufWindow) showCursor(x, y int, main bool) {
|
func (w *BufWindow) showCursor(x, y int, main bool) {
|
||||||
if !main {
|
if main {
|
||||||
screen.Screen.ShowCursor(x, y)
|
screen.Screen.ShowCursor(x, y)
|
||||||
} else {
|
} else {
|
||||||
r, _, _, _ := screen.Screen.GetContent(x, y)
|
r, _, _, _ := screen.Screen.GetContent(x, y)
|
||||||
@@ -421,8 +421,8 @@ func (w *BufWindow) displayBuffer() {
|
|||||||
|
|
||||||
if showcursor {
|
if showcursor {
|
||||||
for _, c := range cursors {
|
for _, c := range cursors {
|
||||||
if c.X == bloc.X && c.Y == bloc.Y {
|
if c.X == bloc.X && c.Y == bloc.Y && !c.HasSelection() {
|
||||||
w.showCursor(w.X+vloc.X, w.Y+vloc.Y, true)
|
w.showCursor(w.X+vloc.X, w.Y+vloc.Y, c.Num == 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -495,8 +495,8 @@ func (w *BufWindow) displayBuffer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cursors {
|
for _, c := range cursors {
|
||||||
if c.X == bloc.X && c.Y == bloc.Y {
|
if c.X == bloc.X && c.Y == bloc.Y && !c.HasSelection() {
|
||||||
w.showCursor(w.X+vloc.X, w.Y+vloc.Y, true)
|
w.showCursor(w.X+vloc.X, w.Y+vloc.Y, c.Num == 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,10 @@ type InfoBuf struct {
|
|||||||
GutterMessage bool
|
GutterMessage bool
|
||||||
|
|
||||||
PromptCallback func(resp string, canceled bool)
|
PromptCallback func(resp string, canceled bool)
|
||||||
|
EventCallback func(resp string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBuffer returns a new infobuffer
|
||||||
func NewBuffer() *InfoBuf {
|
func NewBuffer() *InfoBuf {
|
||||||
ib := new(InfoBuf)
|
ib := new(InfoBuf)
|
||||||
ib.History = make(map[string][]string)
|
ib.History = make(map[string][]string)
|
||||||
@@ -62,7 +64,11 @@ func (i *InfoBuf) Error(msg ...interface{}) {
|
|||||||
// TODO: add to log?
|
// TODO: add to log?
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *InfoBuf) Prompt(prompt string, msg string, callback func(string, bool)) {
|
// Prompt starts a prompt for the user, it takes a prompt, a possibly partially filled in msg
|
||||||
|
// and callbacks executed when the user executes an event and when the user finishes the prompt
|
||||||
|
// The eventcb passes the current user response as the argument and donecb passes the user's message
|
||||||
|
// and a boolean indicating if the prompt was canceled
|
||||||
|
func (i *InfoBuf) Prompt(prompt string, msg string, eventcb func(string), donecb func(string, bool)) {
|
||||||
// If we get another prompt mid-prompt we cancel the one getting overwritten
|
// If we get another prompt mid-prompt we cancel the one getting overwritten
|
||||||
if i.HasPrompt {
|
if i.HasPrompt {
|
||||||
i.DonePrompt(true)
|
i.DonePrompt(true)
|
||||||
@@ -71,21 +77,27 @@ func (i *InfoBuf) Prompt(prompt string, msg string, callback func(string, bool))
|
|||||||
i.Msg = prompt
|
i.Msg = prompt
|
||||||
i.HasPrompt = true
|
i.HasPrompt = true
|
||||||
i.HasMessage, i.HasError = false, false
|
i.HasMessage, i.HasError = false, false
|
||||||
i.PromptCallback = callback
|
i.PromptCallback = donecb
|
||||||
|
i.EventCallback = eventcb
|
||||||
i.Buffer.Insert(i.Buffer.Start(), msg)
|
i.Buffer.Insert(i.Buffer.Start(), msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DonePrompt finishes the current prompt and indicates whether or not it was canceled
|
||||||
func (i *InfoBuf) DonePrompt(canceled bool) {
|
func (i *InfoBuf) DonePrompt(canceled bool) {
|
||||||
i.HasPrompt = false
|
i.HasPrompt = false
|
||||||
if canceled {
|
if i.PromptCallback != nil {
|
||||||
i.PromptCallback("", true)
|
if canceled {
|
||||||
} else {
|
i.PromptCallback("", true)
|
||||||
i.PromptCallback(strings.TrimSpace(string(i.LineBytes(0))), false)
|
} else {
|
||||||
|
i.PromptCallback(strings.TrimSpace(string(i.LineBytes(0))), false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
i.PromptCallback = nil
|
||||||
|
i.EventCallback = nil
|
||||||
i.Replace(i.Start(), i.End(), "")
|
i.Replace(i.Start(), i.End(), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset resets the messenger's cursor, message and response
|
// Reset resets the infobuffer's msg and info
|
||||||
func (i *InfoBuf) Reset() {
|
func (i *InfoBuf) Reset() {
|
||||||
i.Msg = ""
|
i.Msg = ""
|
||||||
i.HasPrompt, i.HasMessage, i.HasError = false, false, false
|
i.HasPrompt, i.HasMessage, i.HasError = false, false, false
|
||||||
|
|||||||
@@ -188,6 +188,12 @@ func IsStrWhitespace(str string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunePos returns the rune index of a given byte index
|
||||||
|
// Make sure the byte index is not between code points
|
||||||
|
func RunePos(b []byte, i int) int {
|
||||||
|
return utf8.RuneCount(b[:i])
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: consider changing because of snap segfault
|
// TODO: consider changing because of snap segfault
|
||||||
// ReplaceHome takes a path as input and replaces ~ at the start of the path with the user's
|
// ReplaceHome takes a path as input and replaces ~ at the start of the path with the user's
|
||||||
// home directory. Does nothing if the path does not start with '~'.
|
// home directory. Does nothing if the path does not start with '~'.
|
||||||
@@ -302,3 +308,12 @@ func GetCharPosInLine(b []byte, visualPos int, tabsize int) int {
|
|||||||
|
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Clamp(val, min, max int) int {
|
||||||
|
if val < min {
|
||||||
|
val = min
|
||||||
|
} else if val > max {
|
||||||
|
val = max
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user