edit: Manage insert and command bindings in their structs.

This commit is contained in:
Qi Xiao 2018-02-06 23:47:55 -08:00
parent cbf6e7c67d
commit d30826d02d
11 changed files with 88 additions and 60 deletions

View File

@ -105,7 +105,7 @@ func complAccept(ed *Editor) {
if 0 <= c.selected && c.selected < len(c.filtered) {
ed.buffer, ed.dot = c.apply(ed.buffer, ed.dot)
}
ed.mode = &ed.insert
ed.SetModeInsert()
}
func complDefault(ed *Editor) {

View File

@ -40,8 +40,6 @@ type Editor struct {
active bool
activeMutex sync.Mutex
insertBinding BindingMap
commandBinding BindingMap
completionBinding BindingMap
navigationBinding BindingMap
@ -68,7 +66,9 @@ type Editor struct {
maxHeight float64
// Modes.
hist *hist
insert *insert
command *command
hist *hist
editorState
}
@ -94,8 +94,6 @@ type editorState struct {
mode Mode
insert insert
command command
completion completion
navigation navigation
@ -196,12 +194,18 @@ func (ed *Editor) SetBuffer(buffer string, dot int) {
// SetMode sets the current mode of the Editor.
func (ed *Editor) SetMode(m Mode) {
if deiniter, ok := ed.mode.(deiniter); ok {
deiniter.Deinit()
}
ed.mode = m
if initer, ok := m.(initer); ok {
initer.Init()
}
}
// SetModeInsert sets the current mode of the Editor to insert mode.
func (ed *Editor) SetModeInsert() {
ed.mode = &ed.insert
ed.SetMode(ed.insert)
}
func (ed *Editor) flash() {
@ -317,13 +321,16 @@ func (ed *Editor) finishReadLine() error {
ed.active = false
// Refresh the terminal for the last time in a clean-ish state.
ed.mode = &ed.insert
ed.SetModeInsert()
ed.tips = nil
ed.dot = len(ed.buffer)
if !ed.RpromptPersistent {
ed.rpromptContent = nil
}
errRefresh := ed.refresh(false, false)
if deiniter, ok := ed.mode.(deiniter); ok {
deiniter.Deinit()
}
ed.out.WriteString("\n")
ed.writer.ResetCurrentBuffer()
@ -356,7 +363,7 @@ func (ed *Editor) ReadLine() (string, error) {
}
}()
ed.mode = &ed.insert
ed.SetModeInsert()
// Find external commands asynchronously, so that slow I/O won't block the
// editor.
@ -425,7 +432,7 @@ MainLoop:
restoreTerminal: ed.restoreTerminal,
isExternal: ed.isExternal,
}
ed.mode = &ed.insert
ed.SetModeInsert()
continue MainLoop
case sys.SIGWINCH:
fullRefresh = true

View File

@ -26,14 +26,11 @@ type hist struct {
}
func init() {
atEditorInit(func(ed *Editor, ns eval.Ns) {
ed.hist = initHist(ed, ns)
})
atEditorInit(initHist)
}
func initHist(ed *Editor, ns eval.Ns) *hist {
func initHist(ed *Editor, ns eval.Ns) {
hist := &hist{ed: ed, binding: EmptyBindingMap}
if ed.Daemon() != nil {
fuser, err := history.NewFuser(ed.Daemon())
if err != nil {
@ -43,6 +40,7 @@ func initHist(ed *Editor, ns eval.Ns) *hist {
ed.AddAfterReadline(hist.appendHistory)
}
}
ed.hist = hist
subns := eval.Ns{
"binding": eval.NewVariableFromPtr(&hist.binding),
@ -58,8 +56,6 @@ func initHist(ed *Editor, ns eval.Ns) *hist {
})
ns.AddNs("history", subns)
return hist
}
func (h *hist) Binding(ed *Editor, k ui.Key) eval.Callable {

View File

@ -14,12 +14,12 @@ import (
// Builtins related to insert and command mode.
func init() {
atEditorInit(func(ed *Editor, ns eval.Ns) {
initCoreModes(ed, ns)
})
atEditorInit(initCoreFns)
atEditorInit(initInsert)
atEditorInit(initCommand)
}
func initCoreModes(ed *Editor, ns eval.Ns) {
func initCoreFns(ed *Editor, ns eval.Ns) {
ns.AddBuiltinFns("edit:", map[string]interface{}{
"kill-line-left": ed.killLineLeft,
"kill-line-right": ed.killLineRight,
@ -50,27 +50,14 @@ func initCoreModes(ed *Editor, ns eval.Ns) {
"end-of-history": ed.endOfHistory,
"redraw": ed.redraw,
})
insertNs := eval.Ns{
"binding": eval.NewVariableFromPtr(&ed.insertBinding),
}
insertNs.AddBuiltinFns("edit:insert:", map[string]interface{}{
"start": ed.insertStart,
"default": ed.insertDefault,
})
ns.AddNs("insert", insertNs)
commandNs := eval.Ns{
"binding": eval.NewVariableFromPtr(&ed.commandBinding),
}
commandNs.AddBuiltinFns("edit:command:", map[string]interface{}{
"start": ed.commandStart,
"default": ed.commandDefault,
})
ns.AddNs("command", commandNs)
}
type insert struct {
binding BindingMap
insertState
}
type insertState struct {
quotePaste bool
// The number of consecutive key inserts. Used for abbreviation expansion.
literalInserts int
@ -79,6 +66,24 @@ type insert struct {
insertedLiteral bool
}
func initInsert(ed *Editor, ns eval.Ns) {
insert := &insert{binding: EmptyBindingMap}
ed.insert = insert
insertNs := eval.Ns{
"binding": eval.NewVariableFromPtr(&insert.binding),
}
insertNs.AddBuiltinFns("edit:insert:", map[string]interface{}{
"start": ed.SetModeInsert,
"default": ed.insertDefault,
})
ns.AddNs("insert", insertNs)
}
func (ins *insert) Deinit() {
ins.insertState = insertState{}
}
// ui.Insert mode is the default mode and has an empty mode.
func (ins *insert) ModeLine() ui.Renderer {
if ins.quotePaste {
@ -87,26 +92,38 @@ func (ins *insert) ModeLine() ui.Renderer {
return nil
}
func (*insert) Binding(ed *Editor, k ui.Key) eval.Callable {
return ed.insertBinding.GetOrDefault(k)
func (ins *insert) Binding(ed *Editor, k ui.Key) eval.Callable {
return ins.binding.GetOrDefault(k)
}
type command struct{}
type command struct {
binding BindingMap
}
func initCommand(ed *Editor, ns eval.Ns) {
command := &command{binding: EmptyBindingMap}
ed.command = command
commandNs := eval.Ns{
"binding": eval.NewVariableFromPtr(&command.binding),
}
commandNs.AddBuiltinFns("edit:command:", map[string]interface{}{
"start": ed.commandStart,
"default": ed.commandDefault,
})
ns.AddNs("command", commandNs)
}
func (*command) ModeLine() ui.Renderer {
return modeLineRenderer{" COMMAND ", ""}
}
func (*command) Binding(ed *Editor, k ui.Key) eval.Callable {
return ed.commandBinding.GetOrDefault(k)
}
func (ed *Editor) insertStart() {
ed.mode = &ed.insert
func (cmd *command) Binding(ed *Editor, k ui.Key) eval.Callable {
return cmd.binding.GetOrDefault(k)
}
func (ed *Editor) commandStart() {
ed.mode = &ed.command
ed.SetMode(ed.command)
}
func (ed *Editor) killLineLeft() {

View File

@ -82,7 +82,7 @@ func (b *lastcmd) Filter(filter string) int {
func (b *lastcmd) Accept(i int, ed *Editor) {
ed.insertAtDot(b.filtered[i].s)
ed.insertStart()
ed.SetModeInsert()
}
func lastcmdStart(ed *Editor) {
@ -112,7 +112,7 @@ func lastcmdAltDefault(ed *Editor) {
logger.Println("accepting")
}
} else {
ed.insertStart()
ed.SetModeInsert()
ed.SetAction(ReprocessKey)
}
}

View File

@ -24,7 +24,7 @@ var listingFns = map[string]func(*Editor){
"accept": func(ed *Editor) { getListing(ed).accept(ed) },
"accept-close": func(ed *Editor) {
getListing(ed).accept(ed)
ed.insertStart()
ed.SetModeInsert()
},
"default": func(ed *Editor) { getListing(ed).defaultBinding(ed) },
}
@ -319,7 +319,7 @@ func (l *listing) handleFilterKey(k ui.Key) bool {
func (l *listing) defaultBinding(ed *Editor) {
if !l.handleFilterKey(ed.lastKey) {
ed.insertStart()
ed.SetModeInsert()
ed.SetAction(ReprocessKey)
}
}

View File

@ -114,7 +114,7 @@ func (loc *location) Accept(i int, ed *Editor) {
if err != nil {
ed.Notify("%v", err)
}
ed.mode = &ed.insert
ed.SetModeInsert()
}
func locStart(ed *Editor) {

View File

@ -11,6 +11,14 @@ type Mode interface {
Binding(*Editor, ui.Key) eval.Callable
}
type initer interface {
Init()
}
type deiniter interface {
Deinit()
}
// CursorOnModeLiner is an optional interface that modes can implement. If a
// mode does and the method returns true, the cursor is placed on the modeline
// when that mode is active.

View File

@ -25,7 +25,7 @@ var narrowFns = map[string]func(*Editor){
"accept": func(ed *Editor) { getNarrow(ed).accept(ed) },
"accept-close": func(ed *Editor) {
getNarrow(ed).accept(ed)
ed.insertStart()
ed.SetModeInsert()
},
"toggle-ignore-duplication": func(ed *Editor) {
l := getNarrow(ed)
@ -291,7 +291,7 @@ func (l *narrow) handleFilterKey(ed *Editor) bool {
l.changeFilter(l.filter + string(k.Rune))
if len(l.filtered) == 1 && l.opts.AutoCommit {
l.accept(ed)
ed.insertStart()
ed.SetModeInsert()
}
return true
}
@ -300,7 +300,7 @@ func (l *narrow) handleFilterKey(ed *Editor) bool {
func (l *narrow) defaultBinding(ed *Editor) {
if !l.handleFilterKey(ed) {
ed.insertStart()
ed.SetModeInsert()
ed.SetAction(ReprocessKey)
}
}

View File

@ -131,7 +131,7 @@ func navInsertSelected(ed *Editor) {
func navInsertSelectedAndQuit(ed *Editor) {
ed.insertAtDot(parse.Quote(ed.navigation.current.selectedName()) + " ")
ed.mode = &ed.insert
ed.SetModeInsert()
}
func navDefault(ed *Editor) {
@ -150,7 +150,7 @@ func navDefault(ed *Editor) {
n.refreshDirPreview()
}
} else {
fn := ed.insertBinding.GetOrDefault(k)
fn := ed.insert.binding.GetOrDefault(k)
if fn == nil {
ed.Notify("key %s unbound and no default binding", k)
} else {

View File

@ -20,7 +20,7 @@ func (ed *Editor) startInsertRaw() {
func insertRaw(ed *Editor, r rune) {
ed.insertAtDot(string(r))
ed.reader.SetRaw(false)
ed.mode = &ed.insert
ed.SetModeInsert()
}
func (rawInsert) Binding(*Editor, ui.Key) eval.Callable {