| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721 |
- package editor
- import (
- "context"
- "moose/internal/about"
- "moose/internal/buffer"
- "moose/internal/layout"
- "moose/internal/util"
- "os"
- "slices"
- "strconv"
- "strings"
- "github.com/zyedidia/rope"
- "golang.design/x/clipboard"
- )
- type ActionManager = struct {
- Common []Action
- Normal []Action
- Insert []Action
- Palette []Action
- CM ChordManager
- RCM ChordManager
- }
- type Action = struct {
- Bindings []Binding
- Commands []string
- AskArgs bool
- Callback func(*Model, []string)
- }
- type Binding = struct {
- Type BindingType
- Single string
- Chord []string
- }
- type BindingType int
- const (
- BindingSingle BindingType = iota
- BindingChord
- )
- type ChordManager = struct {
- Recording bool
- Recorded []string
- }
- func NewChordManager() ChordManager {
- return ChordManager{
- Recording: false,
- Recorded: []string{},
- }
- }
- func DefaultActionManager() ActionManager {
- am := ActionManager{
- CM: NewChordManager(),
- Common: []Action{
- Action{
- Commands: []string{"bsel"},
- AskArgs: true,
- Callback: func(m *Model, args []string) {
- if len(args) < 1 {
- return
- }
- n, err := strconv.Atoi(args[0])
- if err != nil || n < 1 {
- return
- }
- targetTab := n - 1
- ws, ok := m.LM.Workspaces[m.LM.ActiveIdx]
- if !ok {
- return
- }
- selected := false
- ws.RootContainer.WalkAndMutateActive(func(cb *layout.ContainerBuffers) layout.ContainerNode {
- if targetTab >= 0 && targetTab < len(cb.Buffers) {
- cb.ActiveIdx = targetTab
- m.BM.CurrentIdx = cb.Buffers[targetTab]
- selected = true
- }
- return *cb
- })
- if selected {
- m.LM.Workspaces[m.LM.ActiveIdx] = ws
- m.Mode = ModeNormal
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+tab",
- },
- },
- Callback: func(m *Model, args []string) {
- ws, ok := m.LM.Workspaces[m.LM.ActiveIdx]
- if !ok {
- return
- }
- ws.RootContainer.WalkAndMutateActive(func(cb *layout.ContainerBuffers) layout.ContainerNode {
- if len(cb.Buffers) > 0 {
- cb.ActiveIdx = (cb.ActiveIdx + 1) % len(cb.Buffers)
- m.BM.CurrentIdx = cb.Buffers[cb.ActiveIdx]
- }
- return *cb
- })
- m.LM.Workspaces[m.LM.ActiveIdx] = ws
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+shift+tab",
- },
- },
- Callback: func(m *Model, args []string) {
- ws, ok := m.LM.Workspaces[m.LM.ActiveIdx]
- if !ok {
- return
- }
- ws.RootContainer.WalkAndMutateActive(func(cb *layout.ContainerBuffers) layout.ContainerNode {
- if len(cb.Buffers) > 0 {
- cb.ActiveIdx = (cb.ActiveIdx - 1 + len(cb.Buffers)) % len(cb.Buffers)
- m.BM.CurrentIdx = cb.Buffers[cb.ActiveIdx]
- }
- return *cb
- })
- m.LM.Workspaces[m.LM.ActiveIdx] = ws
- },
- },
- Action{
- Commands: []string{"version"},
- Callback: func(m *Model, args []string) {
- m.BM.PaletteBuffer.Clear()
- m.BM.PaletteBuffer.Insert("moose.info:Moose version '" + about.Version + "'")
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "f12",
- },
- },
- Callback: func(m *Model, args []string) {
- m.Mode = ModeNormal
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+q",
- },
- },
- Commands: []string{"q", "quit"},
- AskArgs: true,
- Callback: func(m *Model, args []string) {
- m.Quit()
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+s",
- },
- },
- Commands: []string{"s", "save"},
- AskArgs: false,
- Callback: func(m *Model, args []string) {
- path := ""
- if len(args) < 1 || args[0] == "" {
- if m.BM.Current().Path == "" {
- m.Mode = ModeNormal
- m.BM.PaletteBuffer.Clear()
- m.BM.PaletteBuffer.Insert("moose.error:Missing filename argument for save")
- return
- }
- }
- if len(args) > 0 && args[0] != "" {
- m.BM.Current().Path = args[0]
- }
- path = m.BM.Current().Path
- data := []byte(m.BM.Current().String())
- err := os.WriteFile(path, data, 0644)
- if err != nil {
- m.Mode = ModeNormal
- m.BM.PaletteBuffer.Clear()
- m.BM.PaletteBuffer.Insert("moose.error:Could not save to file \"" + path + "\": " + err.Error())
- return
- }
- m.BM.PaletteBuffer.Clear()
- m.BM.PaletteBuffer.Insert("moose.info:Wrote " + strconv.Itoa(len(data)) + " bytes to \"" + path + "\"")
- return
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+shift+s",
- },
- },
- Commands: []string{"s", "save"},
- AskArgs: true,
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+e",
- },
- },
- Commands: []string{"e", "edit"},
- AskArgs: true,
- Callback: func(m *Model, args []string) {
- if len(args) < 1 {
- m.Mode = ModeNormal
- m.BM.PaletteBuffer.Clear()
- m.BM.PaletteBuffer.Insert("moose.error:Did not specify path for edit")
- return
- }
- content, err := os.ReadFile(args[0])
- if err != nil {
- m.Mode = ModeNormal
- m.BM.PaletteBuffer.Clear()
- m.BM.PaletteBuffer.Insert("moose.error:Could not edit file \"" + args[0] + "\": " + err.Error())
- return
- }
- m.BM.Current().Clear()
- m.BM.Current().Rope = rope.New(content)
- m.BM.Current().LI.Rebuild(m.BM.Current().Rope)
- m.BM.Current().Path = args[0]
- m.BM.Current().History = buffer.UndoStack{}
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "left",
- },
- },
- Callback: func(m *Model, args []string) {
- if m.Mode == ModePalette {
- m.BM.PaletteBuffer.MoveHoriz(-1)
- } else {
- m.BM.Current().MoveHoriz(-1)
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "right",
- },
- },
- Callback: func(m *Model, args []string) {
- if m.Mode == ModePalette {
- m.BM.PaletteBuffer.MoveHoriz(1)
- } else {
- m.BM.Current().MoveHoriz(1)
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "up",
- },
- },
- Callback: func(m *Model, args []string) {
- if m.Mode != ModePalette {
- m.BM.Current().MoveVert(-1)
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "down",
- },
- },
- Callback: func(m *Model, args []string) {
- if m.Mode != ModePalette {
- m.BM.Current().MoveVert(1)
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "shift+left",
- },
- },
- AskArgs: false,
- Callback: func(m *Model, args []string) {
- if m.Mode == ModePalette {
- m.BM.PaletteBuffer.MoveWordHoriz(-1)
- } else {
- m.BM.Current().MoveWordHoriz(-1)
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "shift+right",
- },
- },
- AskArgs: false,
- Callback: func(m *Model, args []string) {
- if m.Mode == ModePalette {
- m.BM.PaletteBuffer.MoveWordHoriz(1)
- } else {
- m.BM.Current().MoveWordHoriz(1)
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingChord,
- Chord: []string{"ctrl+l", "enter"},
- },
- },
- Commands: []string{"lbuf"},
- AskArgs: false,
- Callback: func(m *Model, args []string) {
- m.AddBuffer(true)
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+left",
- },
- },
- Callback: func(m *Model, args []string) {
- m.CycleActiveBuffer(-1.0, 0.0)
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+v",
- },
- },
- Callback: func(m *Model, args []string) {
- if m.LM.CurrentSplit == layout.SplitHorizontal {
- m.LM.CurrentSplit = layout.SplitVertical
- } else {
- m.LM.CurrentSplit = layout.SplitHorizontal
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+down",
- },
- },
- Callback: func(m *Model, args []string) {
- m.CycleActiveBuffer(0.0, 1.0)
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+up",
- },
- },
- Callback: func(m *Model, args []string) {
- m.CycleActiveBuffer(0.0, -1.0)
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+right",
- },
- },
- Callback: func(m *Model, args []string) {
- m.CycleActiveBuffer(1.0, 0.0)
- },
- },
- },
- Normal: []Action{
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "z",
- },
- },
- Commands: []string{"u", "undo"},
- AskArgs: false,
- Callback: func(m *Model, args []string) {
- if m.Mode == ModePalette {
- m.BM.PaletteBuffer.Undo()
- } else {
- m.BM.Current().Undo()
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "y",
- },
- },
- Commands: []string{"r", "redo"},
- AskArgs: false,
- Callback: func(m *Model, args []string) {
- if m.Mode == ModePalette {
- m.BM.PaletteBuffer.Redo()
- } else {
- m.BM.Current().Redo()
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingChord,
- Chord: []string{"d", "d"},
- },
- },
- Callback: func(m *Model, args []string) {
- m.BM.Current().DeleteLine()
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "w",
- },
- },
- Callback: func(m *Model, args []string) {
- m.Mode = ModeWrite
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "q",
- },
- },
- Callback: func(m *Model, args []string) {
- m.Mode = ModePalette
- m.BM.PaletteBuffer.Clear()
- m.BM.PaletteBuffer.Insert("/")
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "f",
- },
- },
- AskArgs: true,
- Callback: func(m *Model, args []string) {
- m.Mode = ModePalette
- m.BM.PaletteBuffer.Clear()
- m.BM.PaletteBuffer.Insert("?")
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "v",
- },
- },
- Commands: []string{"p", "paste"},
- AskArgs: false,
- Callback: func(m *Model, args []string) {
- text, err := clipboard.Read(context.Background(), clipboard.FmtText)
- if err != nil {
- m.DebugLog = "moose.error:Clipboard read failed: " + err.Error()
- return
- }
- if s := string(text); s != "" {
- if m.Mode == ModePalette {
- m.BM.PaletteBuffer.Insert(s)
- } else {
- m.BM.Current().Insert(s)
- }
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "enter",
- },
- },
- Commands: []string{"buf"},
- AskArgs: false,
- Callback: func(m *Model, args []string) {
- m.AddBuffer(false)
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+x",
- },
- },
- Callback: func(m *Model, args []string) {
- m.RemoveActiveBuffer()
- },
- },
- },
- Insert: []Action{
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "shift+up",
- },
- },
- Commands: []string{"curup"},
- Callback: func(m *Model, args []string) {
- m.BM.Current().AddCursorVert(-1)
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "shift+down",
- },
- },
- Commands: []string{"curdown"},
- Callback: func(m *Model, args []string) {
- m.BM.Current().AddCursorVert(1)
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "esc",
- },
- },
- Commands: []string{"clrc", "clearcursors"},
- Callback: func(m *Model, args []string) {
- m.BM.Current().ClearCursors()
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "tab",
- },
- },
- Callback: func(m *Model, args []string) {
- for _ = range 4 {
- m.BM.Current().Insert(" ")
- }
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "shift+tab",
- },
- },
- Callback: func(m *Model, args []string) {
- // TODO: implement m.BM.Current().remove...
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "backspace",
- },
- },
- Callback: func(m *Model, args []string) {
- m.BM.Current().Delete()
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "enter",
- },
- },
- Callback: func(m *Model, args []string) {
- m.BM.Current().Insert("\n")
- },
- },
- },
- Palette: []Action{
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "backspace",
- },
- },
- Callback: func(m *Model, args []string) {
- m.BM.PaletteBuffer.Delete()
- },
- },
- Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "enter",
- },
- },
- Callback: func(m *Model, _ []string) {
- input := m.BM.PaletteBuffer.String()
- args := strings.Split(input, " ")
- if args[0] == "" {
- m.Mode = ModeNormal
- return
- }
- cmd := string([]rune(args[0])[1:])
- if strings.HasPrefix(args[0], "/") {
- for _, action := range append(m.AM.Common, append(m.AM.Normal, m.AM.Insert...)...) {
- if action.Callback == nil {
- continue
- }
- if len(action.Commands) > 0 && slices.Contains(util.StandardizeBindingsArray(action.Commands), util.StandardizeBinding(cmd)) {
- m.Mode = ModeNormal
- action.Callback(m, args[1:])
- return
- }
- }
- m.Mode = ModeNormal
- m.BM.PaletteBuffer.Clear()
- m.BM.PaletteBuffer.Insert("moose.error:Unknown command \"" + cmd + "\"")
- } else if strings.HasPrefix(args[0], "?") {
- m.Mode = ModeFind
- m.BM.PaletteBuffer.Clear()
- m.BM.PaletteBuffer.Insert("moose.error:Find mode unimplemented \"" + cmd + "\"")
- } else {
- m.Mode = ModeNormal
- m.BM.PaletteBuffer.Clear()
- m.BM.PaletteBuffer.Insert("moose.error:Unknown palette input \"" + args[0] + "\"")
- }
- },
- },
- },
- }
- for i := range 9 {
- am.Common = append(am.Common, Action{
- Bindings: []Binding{
- Binding{
- Type: BindingSingle,
- Single: "ctrl+" + strconv.Itoa(i+1),
- },
- },
- Callback: func(m *Model, args []string) {
- m.Mode = ModeNormal
- m.LM.ActiveIdx = i
- if bufIdx := m.LM.GetActiveBufferIdx(); bufIdx >= 0 && bufIdx < len(m.BM.Buffers) {
- m.BM.CurrentIdx = bufIdx
- } else if len(m.BM.Buffers) > 0 {
- m.BM.CurrentIdx = 0
- }
- },
- })
- }
- return am
- }
|