Added temporary command x-replace

This runs a search and replace over the entire model.  This is just
temporary at the moment in order to get something working.

Also added hitting backspace on an empty entrybox to cancel it.
This commit is contained in:
Leon Mika 2020-09-29 23:08:57 +00:00
parent 04aeff6b52
commit 6e6e586f1d
5 changed files with 71 additions and 20 deletions

View file

@ -5,6 +5,8 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"github.com/lmika/shellwords"
"github.com/lmika/ted/ui" "github.com/lmika/ted/ui"
) )
@ -60,20 +62,19 @@ func (cm *CommandMapping) KeyMapping(key rune) *Command {
// Evaluate a command // Evaluate a command
func (cm *CommandMapping) Eval(ctx *CommandContext, expr string) error { func (cm *CommandMapping) Eval(ctx *CommandContext, expr string) error {
// TODO: Use propper expression language here // TODO: Use propper expression language here
cmd := cm.Commands[expr] toks := shellwords.Split(expr)
if len(toks) == 0 {
return nil
}
cmd := cm.Commands[toks[0]]
if cmd != nil { if cmd != nil {
return cmd.Do(ctx) return cmd.Do(ctx.WithArgs(toks[1:]))
} }
return fmt.Errorf("no such command: %v", expr) return fmt.Errorf("no such command: %v", expr)
} }
//func (cm *CommandMapping) DoEval(ctx *CommandContext, expr string) {
// if err := cm.Eval(ctx, expr); err != nil {
// ctx.ShowError(err)
// }
//}
// Registers the standard view navigation commands. These commands require the frame // Registers the standard view navigation commands. These commands require the frame
func (cm *CommandMapping) RegisterViewCommands() { func (cm *CommandMapping) RegisterViewCommands() {
cm.Define("move-down", "Moves the cursor down one row", "", gridNavOperation(func(grid *ui.Grid) { grid.MoveBy(0, 1) })) cm.Define("move-down", "Moves the cursor down one row", "", gridNavOperation(func(grid *ui.Grid) { grid.MoveBy(0, 1) }))
@ -153,6 +154,34 @@ func (cm *CommandMapping) RegisterViewCommands() {
} }
} }
}) })
cm.Define("x-replace", "Performs a search and replace", "", func(ctx *CommandContext) error {
if len(ctx.Args()) != 2 {
return errors.New("Usage: x-replace MATCH REPLACEMENT")
}
match := ctx.Args()[0]
repl := ctx.Args()[1]
re, err := regexp.Compile(match)
if err != nil {
return fmt.Errorf("invalid regexp: %v", err)
}
matchCount := 0
height, width := ctx.ModelVC().Model().Dimensions()
for r := 0; r < height; r++ {
for c := 0; c < width; c++ {
cell := ctx.ModelVC().Model().CellValue(r, c)
if re.FindStringIndex(cell) != nil {
ctx.ModelVC().SetCellValue(r, c, re.ReplaceAllString(cell, repl))
matchCount++
}
}
}
ctx.Frame().ShowMessage(fmt.Sprintf("Replaced %d matches", matchCount))
return nil
})
cm.Define("open-right", "Inserts a column to the right of the curser", "", func(ctx *CommandContext) error { cm.Define("open-right", "Inserts a column to the right of the curser", "", func(ctx *CommandContext) error {
grid := ctx.Frame().Grid() grid := ctx.Frame().Grid()

1
go.mod
View file

@ -3,6 +3,7 @@ module github.com/lmika/ted
go 1.15 go 1.15
require ( require (
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe
github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1
) )

2
go.sum
View file

@ -1,3 +1,5 @@
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe h1:1UXS/6OFkbi6JrihPykmYO1VtsABB02QQ+YmYYzTY18=
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe/go.mod h1:qpdOkLougV5Yry4Px9f1w1pNMavcr6Z67VW5Ro+vW5I=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 h1:lh3PyZvY+B9nFliSGTn5uFuqQQJGuNrD0MLCokv09ag= github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 h1:lh3PyZvY+B9nFliSGTn5uFuqQQJGuNrD0MLCokv09ag=

View file

@ -1,8 +1,9 @@
package main package main
import ( import (
"github.com/lmika/ted/ui"
"regexp" "regexp"
"github.com/lmika/ted/ui"
) )
// The session is responsible for managing the UI and the model and handling // The session is responsible for managing the UI and the model and handling
@ -61,7 +62,7 @@ func (session *Session) KeyPressed(key rune, mod int) {
cmd := session.Commands.KeyMapping(key) cmd := session.Commands.KeyMapping(key)
if cmd != nil { if cmd != nil {
err := cmd.Do(&CommandContext{session}) err := cmd.Do(&CommandContext{session, nil})
if err != nil { if err != nil {
session.Frame.ShowMessage(err.Error()) session.Frame.ShowMessage(err.Error())
} }
@ -71,6 +72,18 @@ func (session *Session) KeyPressed(key rune, mod int) {
// The command context used by the session // The command context used by the session
type CommandContext struct { type CommandContext struct {
session *Session session *Session
args []string
}
func (scc *CommandContext) WithArgs(args []string) *CommandContext {
return &CommandContext{
session: scc.session,
args: args,
}
}
func (scc *CommandContext) Args() []string {
return scc.args
} }
func (scc *CommandContext) ModelVC() *ModelViewCtrl { func (scc *CommandContext) ModelVC() *ModelViewCtrl {

View file

@ -124,6 +124,8 @@ func (te *TextEntry) KeyPressed(key rune, mod int) {
if mod&ModKeyAlt != 0 { if mod&ModKeyAlt != 0 {
te.backspaceWhile(unicode.IsSpace) te.backspaceWhile(unicode.IsSpace)
te.backspaceWhile(func(r rune) bool { return !unicode.IsSpace(r) }) te.backspaceWhile(func(r rune) bool { return !unicode.IsSpace(r) })
} else if te.cursorOffset == 0 {
te.cancelAndExit()
} else { } else {
te.backspace() te.backspace()
} }
@ -136,14 +138,18 @@ func (te *TextEntry) KeyPressed(key rune, mod int) {
te.OnEntry(te.value) te.OnEntry(te.value)
} }
} else if key == KeyCtrlC { } else if key == KeyCtrlC {
if te.OnCancel != nil { te.cancelAndExit()
te.OnCancel()
}
} }
//panic(fmt.Sprintf("Entered key: '%x', mod: '%x'", key, mod)) //panic(fmt.Sprintf("Entered key: '%x', mod: '%x'", key, mod))
} }
func (te *TextEntry) cancelAndExit() {
if te.OnCancel != nil {
te.OnCancel()
}
}
// Backspace // Backspace
func (te *TextEntry) backspace() { func (te *TextEntry) backspace() {
te.removeCharAtPos(te.cursorOffset - 1) te.removeCharAtPos(te.cursorOffset - 1)