Просмотр исходного кода

Add tab movement action, add color configuration

Johan Rong 1 неделя назад
Родитель
Сommit
922a73900a

+ 1 - 5
cmd/moose/main.go

@@ -6,7 +6,6 @@ import (
 	"moose/internal/editor"
 	"moose/internal/extension"
 	"github.com/gdamore/tcell/v3"
-	"github.com/gdamore/tcell/v3/color"
 	"golang.design/x/clipboard"
 	"github.com/davecgh/go-spew/spew"
 )
@@ -28,10 +27,7 @@ func main() {
 		os.Exit(1)
 	}
 
-	style := tcell.StyleDefault.Background(color.Reset).Foreground(color.Reset)
-	s.SetStyle(style)
-
-	m := editor.NewModel(s, style)
+	m := editor.NewModel(s)
 	m.AddBuffer(false)
 
 	init_extensions(&m)

+ 1 - 0
go.mod

@@ -13,6 +13,7 @@ require (
 require (
 	github.com/clipperhouse/displaywidth v0.11.0 // indirect
 	github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
+	github.com/creasty/defaults v1.8.0 // indirect
 	github.com/ebitengine/purego v0.10.1 // indirect
 	github.com/gdamore/encoding v1.0.1 // indirect
 	github.com/lucasb-eyer/go-colorful v1.4.0 // indirect

+ 2 - 0
go.sum

@@ -2,6 +2,8 @@ github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSE
 github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
 github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
 github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
+github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk=
+github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY=

+ 82 - 30
internal/editor/action.go

@@ -1,16 +1,17 @@
 package editor
 
-import(
-	"strings"
-	"strconv"
-	"slices"
+import (
+	"moose/internal/about"
+	"moose/internal/buffer"
+	"moose/internal/layout"
+	"moose/internal/util"
 	"os"
+	"slices"
+	"strconv"
+	"strings"
+
 	"github.com/zyedidia/rope"
 	"golang.design/x/clipboard"
-	"moose/internal/buffer"
-	"moose/internal/util"
-	"moose/internal/about"
-	"moose/internal/layout"
 )
 
 type ActionManager = struct {
@@ -18,15 +19,15 @@ type ActionManager = struct {
 	Normal  []Action
 	Insert  []Action
 	Palette []Action
-	CM		ChordManager
+	CM      ChordManager
 	RCM     ChordManager
 }
 
 type Action = struct {
-	Bindings  []Binding
-	Commands  []string
-	AskArgs   bool
-	Callback  func(*Model, []string)
+	Bindings []Binding
+	Commands []string
+	AskArgs  bool
+	Callback func(*Model, []string)
 }
 
 type Binding = struct {
@@ -36,6 +37,7 @@ type Binding = struct {
 }
 
 type BindingType int
+
 const (
 	BindingSingle BindingType = iota
 	BindingChord
@@ -55,8 +57,8 @@ func NewChordManager() ChordManager {
 
 func DefaultActionManager() ActionManager {
 	am := ActionManager{
-		CM:	     NewChordManager(),
-		Common:  []Action{
+		CM: NewChordManager(),
+		Common: []Action{
 			Action{
 				Commands: []string{"bsel"},
 				AskArgs:  true,
@@ -93,6 +95,54 @@ func DefaultActionManager() ActionManager {
 					}
 				},
 			},
+			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) {
@@ -119,7 +169,7 @@ func DefaultActionManager() ActionManager {
 					},
 				},
 				Commands: []string{"q", "quit"},
-				AskArgs: true,
+				AskArgs:  true,
 				Callback: func(m *Model, args []string) {
 					m.Quit()
 				},
@@ -132,7 +182,7 @@ func DefaultActionManager() ActionManager {
 					},
 				},
 				Commands: []string{"s", "save"},
-				AskArgs: false,
+				AskArgs:  false,
 				Callback: func(m *Model, args []string) {
 					path := ""
 					if len(args) < 1 || args[0] == "" {
@@ -172,7 +222,7 @@ func DefaultActionManager() ActionManager {
 					},
 				},
 				Commands: []string{"s", "save"},
-				AskArgs: true,
+				AskArgs:  true,
 			},
 			Action{
 				Bindings: []Binding{
@@ -182,7 +232,7 @@ func DefaultActionManager() ActionManager {
 					},
 				},
 				Commands: []string{"e", "edit"},
-				AskArgs: true,
+				AskArgs:  true,
 				Callback: func(m *Model, args []string) {
 					if len(args) < 1 {
 						m.Mode = ModeNormal
@@ -230,7 +280,7 @@ func DefaultActionManager() ActionManager {
 				},
 				Callback: func(m *Model, args []string) {
 					if m.Mode == ModePalette {
-						m.BM.PaletteBuffer.MoveHoriz(1)					
+						m.BM.PaletteBuffer.MoveHoriz(1)
 					} else {
 						m.BM.Current().MoveHoriz(1)
 					}
@@ -302,7 +352,7 @@ func DefaultActionManager() ActionManager {
 					},
 				},
 				Commands: []string{"lbuf"},
-				AskArgs: false,
+				AskArgs:  false,
 				Callback: func(m *Model, args []string) {
 					m.AddBuffer(true)
 				},
@@ -367,7 +417,7 @@ func DefaultActionManager() ActionManager {
 				},
 			},
 		},
-		Normal:  []Action{
+		Normal: []Action{
 			Action{
 				Bindings: []Binding{
 					Binding{
@@ -459,7 +509,7 @@ func DefaultActionManager() ActionManager {
 					},
 				},
 				Commands: []string{"p", "paste"},
-				AskArgs: false,
+				AskArgs:  false,
 				Callback: func(m *Model, args []string) {
 					text := clipboard.Read(clipboard.FmtText)
 
@@ -480,7 +530,7 @@ func DefaultActionManager() ActionManager {
 					},
 				},
 				Commands: []string{"buf"},
-				AskArgs: false,
+				AskArgs:  false,
 				Callback: func(m *Model, args []string) {
 					m.AddBuffer(false)
 				},
@@ -497,7 +547,7 @@ func DefaultActionManager() ActionManager {
 				},
 			},
 		},
-		Insert:  []Action{
+		Insert: []Action{
 			Action{
 				Bindings: []Binding{
 					Binding{
@@ -556,7 +606,7 @@ func DefaultActionManager() ActionManager {
 				},
 				Callback: func(m *Model, args []string) {
 					// TODO: implement m.BM.Current().remove...
-					
+
 				},
 			},
 			Action{
@@ -613,9 +663,11 @@ func DefaultActionManager() ActionManager {
 					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 action.Callback == nil {
+								continue
+							}
 
-							if len(action.Commands) > 0 &&  slices.Contains(util.StandardizeBindingsArray(action.Commands), util.StandardizeBinding(cmd)) {
+							if len(action.Commands) > 0 && slices.Contains(util.StandardizeBindingsArray(action.Commands), util.StandardizeBinding(cmd)) {
 								m.Mode = ModeNormal
 								action.Callback(m, args[1:])
 								return
@@ -644,7 +696,7 @@ func DefaultActionManager() ActionManager {
 			Bindings: []Binding{
 				Binding{
 					Type:   BindingSingle,
-					Single: "ctrl+" + strconv.Itoa(i + 1),
+					Single: "ctrl+" + strconv.Itoa(i+1),
 				},
 			},
 			Callback: func(m *Model, args []string) {
@@ -661,4 +713,4 @@ func DefaultActionManager() ActionManager {
 	}
 
 	return am
-}
+}

+ 52 - 0
internal/editor/config.go

@@ -0,0 +1,52 @@
+package editor
+
+import (
+	"github.com/creasty/defaults"
+	"github.com/gdamore/tcell/v3"
+)
+
+type Config struct {
+	StyleDefault tcell.Style
+	Style        Style
+}
+
+type Style struct {
+	MainBackground            string `default:"#090603"`
+	MainForeground            string `default:"#dddddd"`
+	LineNumberBackground      string `default:"#090603"`
+	LineNumberForeground      string `default:"#dddddd"`
+	CursorColor               string `default:"#777777"`
+	CursorColorWrite          string `default:"#dddddd"`
+	PaletteBarBackground      string `default:"#3b3b3b"`
+	PaletteBarForeground      string `default:"#dddddd"`
+	PaletteInputBackground    string `default:"#090603"`
+	PaletteInputForeground    string `default:"#dddddd"`
+	InfoMsgBackground         string `default:"#090603"`
+	InfoMsgForeground         string `default:"#dddddd"`
+	WarnMsgBackground         string `default:"#090603"`
+	WarnMsgForeground         string `default:"#efc541"`
+	ErrorMsgBackground        string `default:"#090603"`
+	ErrorMsgForeground        string `default:"#ff5e56"`
+	WorkspaceBackground       string `default:"#3b3b3b"`
+	WorkspaceForeground       string `default:"#b9b9b9"`
+	WorkspaceBackgroundActive string `default:"#3b3b3b"`
+	WorkspaceForegroundActive string `default:"#dddddd"`
+	TabBackground             string `default:"#3b3b3b"`
+	TabForeground             string `default:"#b9b9b9"`
+	TabBackgroundActive       string `default:"#3b3b3b"`
+	TabForegroundActive       string `default:"#dddddd"`
+}
+
+func DefaultConfig() Config {
+	config := Config{}
+	if err := defaults.Set(&config); err != nil {
+		panic(err)
+	}
+
+	config.StyleDefault = tcell.StyleDefault.Background(tcell.GetColor(config.Style.MainBackground)).Foreground(tcell.GetColor(config.Style.MainForeground))
+
+	return config
+}
+
+func (m *Model) ReloadConfig() {
+}

+ 77 - 64
internal/editor/draw.go

@@ -1,40 +1,41 @@
 package editor
 
 import (
-	"moose/internal/layout"
+	"fmt"
+	"math"
 	"moose/internal/buffer"
+	"moose/internal/layout"
 	"moose/internal/util"
-	"fmt"
-	"strings"
-	"strconv"
-	"github.com/gdamore/tcell/v3/color"
 	"slices"
-	"math"
+	"strconv"
+	"strings"
+
+	"github.com/gdamore/tcell/v3"
 )
 
 func (m *Model) Draw() {
 	screen := layout.RectFromScren(m.Screen)
 	main := layout.Rect{
-		X: screen.X,
-		Y: screen.Y,
-		Width: screen.Width,
+		X:      screen.X,
+		Y:      screen.Y,
+		Width:  screen.Width,
 		Height: screen.Height - 2,
 	}
 	palette := layout.Rect{
-		X: screen.X,
-		Y: main.Height,
-		Width: screen.Width,
+		X:      screen.X,
+		Y:      main.Height,
+		Width:  screen.Width,
 		Height: 2,
 	}
 
 	copy := m.LM.Workspaces[m.LM.ActiveIdx]
 	m.DrawWorkspace(&copy, main)
-	m.DrawPalette(palette)	
+	m.DrawPalette(palette)
 }
 
 func (m *Model) generateChordStr() string {
 	chordStr := ""
-	
+
 	if len(m.AM.RCM.Recorded) > 0 {
 		chordStr += strings.Join(m.AM.RCM.Recorded, "") + " + "
 	}
@@ -48,7 +49,7 @@ func (m *Model) generateChordStr() string {
 
 func (m *Model) DrawPalette(rect layout.Rect) {
 	for col := 0; col < rect.Width; col++ {
-		m.Screen.SetContent(rect.X + col, rect.Y, ' ', nil, m.Style.Background(color.Black))
+		m.Screen.SetContent(rect.X+col, rect.Y, ' ', nil, m.Config.StyleDefault.Background(tcell.GetColor(m.Config.Style.PaletteBarBackground)).Foreground(tcell.GetColor(m.Config.Style.PaletteBarForeground)))
 	}
 
 	modeStr := m.Mode.String()
@@ -61,7 +62,7 @@ func (m *Model) DrawPalette(rect layout.Rect) {
 			modeStr += " (replace)"
 		}
 	}
-	m.Screen.PutStrStyled(rect.Width - (len(modeStr) + 1), rect.Y, strings.ToUpper(modeStr), m.Style.Background(color.Black))
+	m.Screen.PutStrStyled(rect.Width-(len(modeStr)+1), rect.Y, strings.ToUpper(modeStr), m.Config.StyleDefault.Foreground(tcell.GetColor(m.Config.Style.PaletteBarForeground)).Background(tcell.GetColor(m.Config.Style.PaletteBarBackground)))
 
 	splitStr := ""
 	if m.LM.CurrentSplit == layout.SplitHorizontal {
@@ -69,7 +70,7 @@ func (m *Model) DrawPalette(rect layout.Rect) {
 	} else {
 		splitStr = "V"
 	}
-	m.Screen.PutStrStyled(rect.Width - (len(modeStr) + 1) - 5, rect.Y, splitStr, m.Style.Background(color.Black))
+	m.Screen.PutStrStyled(rect.Width-(len(modeStr)+1)-5, rect.Y, splitStr, m.Config.StyleDefault.Foreground(tcell.GetColor(m.Config.Style.PaletteBarForeground)).Background(tcell.GetColor(m.Config.Style.PaletteBarBackground)))
 
 	populatedWorkspaces := []string{strconv.Itoa(m.LM.ActiveIdx + 1)}
 	for workspaceIdx, workspace := range m.LM.Workspaces {
@@ -78,35 +79,39 @@ func (m *Model) DrawPalette(rect layout.Rect) {
 		}
 
 		if !workspace.IsEmpty() {
-			populatedWorkspaces = append(populatedWorkspaces, strconv.Itoa(workspaceIdx + 1))
+			populatedWorkspaces = append(populatedWorkspaces, strconv.Itoa(workspaceIdx+1))
 		}
 	}
 	slices.Sort(populatedWorkspaces)
 	workspacesStr := strings.Join(populatedWorkspaces, " ")
-	m.Screen.PutStrStyled(1, rect.Y, workspacesStr, m.Style.Background(color.Black))
+	m.Screen.PutStrStyled(1, rect.Y, workspacesStr, m.Config.StyleDefault.Foreground(tcell.GetColor(m.Config.Style.WorkspaceForeground)).Background(tcell.GetColor(m.Config.Style.WorkspaceBackground)))
 
 	chordStr := m.generateChordStr()
-	m.Screen.PutStrStyled(len(workspacesStr) + 2, rect.Y, chordStr, m.Style.Background(color.Black))
-	m.Screen.PutStrStyled(len(chordStr) + 1, rect.Y, m.DebugLog, m.Style.Background(color.Black))
+	m.Screen.PutStrStyled(len(workspacesStr)+2, rect.Y, chordStr, m.Config.StyleDefault.Foreground(tcell.GetColor(m.Config.Style.PaletteBarForeground)).Background(tcell.GetColor(m.Config.Style.PaletteBarBackground)))
+	m.Screen.PutStrStyled(len(chordStr)+1, rect.Y, m.DebugLog, m.Config.StyleDefault.Foreground(tcell.GetColor(m.Config.Style.PaletteBarForeground)).Background(tcell.GetColor(m.Config.Style.PaletteInputBackground)))
 
 	if m.Mode == ModePalette {
-		m.Screen.PutStrStyled(0, rect.Y + 1, m.BM.PaletteBuffer.String(), m.Style)
+		m.Screen.PutStrStyled(0, rect.Y+1, m.BM.PaletteBuffer.String(), m.Config.StyleDefault.Foreground(tcell.GetColor(m.Config.Style.PaletteInputForeground)).Background(tcell.GetColor(m.Config.Style.PaletteInputBackground)))
 
 		for _, cur := range m.BM.PaletteBuffer.CM.Cursors {
 			line, col := buffer.LineCol(&m.BM.PaletteBuffer, cur.Offset)
-			if line + rect.Y != rect.Y { continue }
-			if col + 1 > rect.Width { continue }
+			if line+rect.Y != rect.Y {
+				continue
+			}
+			if col+1 > rect.Width {
+				continue
+			}
 
 			ch := buffer.RuneAt(&m.BM.PaletteBuffer, cur.Offset)
 
-			m.Screen.SetContent(col, rect.Y + 1, ch, nil, m.Style.Reverse(true))
+			m.Screen.SetContent(col, rect.Y+1, ch, nil, m.Config.StyleDefault.Background(tcell.GetColor(m.Config.Style.CursorColorWrite)))
 		}
 	} else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.info:") {
-		m.Screen.PutStrStyled(0, rect.Y + 1, string([]rune(m.BM.PaletteBuffer.String())[11:]), m.Style.Foreground(color.LightGray))
+		m.Screen.PutStrStyled(0, rect.Y+1, string([]rune(m.BM.PaletteBuffer.String())[11:]), m.Config.StyleDefault.Foreground(tcell.GetColor(m.Config.Style.InfoMsgForeground)).Background(tcell.GetColor(m.Config.Style.InfoMsgBackground)))
 	} else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.warn:") {
-		m.Screen.PutStrStyled(0, rect.Y + 1, string([]rune(m.BM.PaletteBuffer.String())[11:]), m.Style.Foreground(color.Orange))
+		m.Screen.PutStrStyled(0, rect.Y+1, string([]rune(m.BM.PaletteBuffer.String())[11:]), m.Config.StyleDefault.Foreground(tcell.GetColor(m.Config.Style.WarnMsgForeground)).Background(tcell.GetColor(m.Config.Style.WarnMsgBackground)))
 	} else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.error:") {
-		m.Screen.PutStrStyled(0, rect.Y + 1, string([]rune(m.BM.PaletteBuffer.String())[12:]), m.Style.Foreground(color.Red))
+		m.Screen.PutStrStyled(0, rect.Y+1, string([]rune(m.BM.PaletteBuffer.String())[12:]), m.Config.StyleDefault.Foreground(tcell.GetColor(m.Config.Style.ErrorMsgForeground)).Background(tcell.GetColor(m.Config.Style.ErrorMsgBackground)))
 	}
 }
 
@@ -119,11 +124,11 @@ func (m *Model) DrawContainer(c *layout.Container, rect layout.Rect) {
 	if length == 0 {
 		return
 	}
-	
+
 	rect = layout.Rect{
-		X: rect.X,
-		Y: rect.Y,
-		Width: rect.Width,
+		X:      rect.X,
+		Y:      rect.Y,
+		Width:  rect.Width,
 		Height: rect.Height,
 	}
 	rect = layout.RectDivide(rect, c.Split, length)
@@ -132,25 +137,27 @@ func (m *Model) DrawContainer(c *layout.Container, rect layout.Rect) {
 		main := layout.RectDisplace(rect, c.Split, i)
 
 		switch child.(type) {
-		case layout.ContainerBuffers: {
-			cb := child.(layout.ContainerBuffers)
-			tabs := layout.Rect{
-				X: main.X,
-				Y: main.Y,
-				Width: main.Width,
-				Height: 1,
+		case layout.ContainerBuffers:
+			{
+				cb := child.(layout.ContainerBuffers)
+				tabs := layout.Rect{
+					X:      main.X,
+					Y:      main.Y,
+					Width:  main.Width,
+					Height: 1,
+				}
+
+				main.Y += 1
+				main.Height -= 1
+
+				m.DrawContainerTabs(&cb, tabs)
+				m.DrawContainerBuffers(&cb, main)
+			}
+		case layout.Container:
+			{
+				c := child.(layout.Container)
+				m.DrawContainer(&c, main)
 			}
-
-			main.Y += 1
-			main.Height -= 1
-			
-			m.DrawContainerTabs(&cb, tabs)
-			m.DrawContainerBuffers(&cb, main)
-		}
-		case layout.Container: {
-			c := child.(layout.Container)
-			m.DrawContainer(&c, main)
-		}
 		}
 	}
 }
@@ -169,27 +176,27 @@ func (m *Model) DrawContainerTabs(c *layout.ContainerBuffers, rect layout.Rect)
 
 		buf := m.BM.Buffers[bufIdx]
 		if buf.Path == "" {
-			tabs = append(tabs, "Buffer " + strconv.Itoa(bufIdx))
+			tabs = append(tabs, "Buffer "+strconv.Itoa(bufIdx))
 		} else {
 			tabs = append(tabs, buf.Path)
-		}		
+		}
 	}
 
 	length := 0
 	for i, tab := range tabs {
-		if length + len(tab) + 2 > rect.Width {
-			m.Screen.PutStrStyled(rect.X + length, rect.Y, ">", m.Style.Foreground(color.White))
+		if length+len(tab)+2 > rect.Width {
+			m.Screen.PutStrStyled(rect.X+length, rect.Y, ">", m.Config.StyleDefault.Foreground(tcell.GetColor(m.Config.Style.TabForeground)).Background(tcell.GetColor(m.Config.Style.TabBackground)))
 			return
 		}
 
-		style := m.Style.Background(color.Black).Foreground(color.DarkGray)
+		style := m.Config.StyleDefault.Background(tcell.GetColor(m.Config.Style.TabBackground)).Foreground(tcell.GetColor(m.Config.Style.TabForeground))
 		if i == activeTabIdx {
-			style = style.Foreground(color.White)
+			style = style.Foreground(tcell.GetColor(m.Config.Style.TabForegroundActive)).Background(tcell.GetColor(m.Config.Style.TabBackgroundActive))
 		}
 
 		tabStr := strconv.Itoa(int(math.Abs(float64((activeTabIdx - i))))) + ": " + tab
 
-		m.Screen.PutStrStyled(rect.X + length, rect.Y, tabStr, style)
+		m.Screen.PutStrStyled(rect.X+length, rect.Y, tabStr, style)
 		length += len(tabStr) + 1
 	}
 }
@@ -219,34 +226,40 @@ func (m *Model) DrawBuffer(buf *buffer.Buffer, isActive bool, rect layout.Rect)
 	visible := string(buf.Rope.Slice(startOffset, endOffset))
 	table := strings.SplitAfter(visible, "\n")
 	for j, line := range table {
-		if j + 1 > rect.Height { break }
+		if j+1 > rect.Height {
+			break
+		}
 
 		lineNum := j + buf.TopLine
 		relLine := lineNum - curLine
 		nums := fmt.Sprintf("%4d ", int(math.Abs(float64(relLine))))
 		r := []rune(nums + expandTabs(line))
-		if len(r) + 1 > rect.Width {
+		if len(r)+1 > rect.Width {
 			r = r[:rect.Width]
 		}
 
-		m.Screen.PutStrStyled(rect.X, rect.Y + j, string(r), m.Style)
+		m.Screen.PutStrStyled(rect.X, rect.Y+j, string(r), m.Config.StyleDefault.Foreground(tcell.GetColor(m.Config.Style.MainForeground)).Background(tcell.GetColor(m.Config.Style.MainBackground)))
 	}
 
 	if isActive {
 		for _, cur := range buf.CM.Cursors {
 			line, col := buffer.LineCol(buf, cur.Offset)
 			screenLine := line - buf.TopLine
-			if screenLine < 0 || screenLine + 1 > rect.Height { continue }
+			if screenLine < 0 || screenLine+1 > rect.Height {
+				continue
+			}
 
 			visCol := visualCol(buffer.LineText(buf, line), col)
-			if visCol + 1 > rect.Width { continue }
+			if visCol+1 > rect.Width {
+				continue
+			}
 
 			ch := buffer.RuneAt(buf, cur.Offset)
 
 			if m.Mode == ModeWrite {
-				m.Screen.SetContent(rect.X + visCol + 5, rect.Y + screenLine, ch, nil, m.Style.Reverse(true))
+				m.Screen.SetContent(rect.X+visCol+5, rect.Y+screenLine, ch, nil, m.Config.StyleDefault.Background(tcell.GetColor(m.Config.Style.CursorColorWrite)))
 			} else {
-				m.Screen.SetContent(rect.X + visCol + 5, rect.Y + screenLine, ch, nil, m.Style.Reverse(true).Foreground(color.Gray))	
+				m.Screen.SetContent(rect.X+visCol+5, rect.Y+screenLine, ch, nil, m.Config.StyleDefault.Background(tcell.GetColor(m.Config.Style.CursorColor)))
 			}
 		}
 	}

+ 3 - 3
internal/editor/model.go

@@ -8,7 +8,7 @@ import (
 
 type Model struct {
 	Screen	   tcell.Screen
-	Style	   tcell.Style
+	Config     Config
 	Mode 	   Mode
 	BM		   buffer.BufferManager
 	AM         ActionManager
@@ -17,12 +17,12 @@ type Model struct {
 	DebugLog   string
 }
 
-func NewModel(screen tcell.Screen, style tcell.Style) Model {
+func NewModel(screen tcell.Screen) Model {
     blank := buffer.NewBuffer()
 
     model := Model{
         Screen: screen,
-        Style:  style,
+        Config: DefaultConfig(),
         Mode:   ModeNormal,
         BM: buffer.BufferManager{
             Buffers:       []buffer.Buffer{blank},

+ 17 - 0
internal/extension/config.go

@@ -0,0 +1,17 @@
+package extension
+
+import (
+	lua "github.com/yuin/gopher-lua"
+)
+
+func GetConfigTable(em *ExtensionManager) *lua.LTable {
+	config := em.L.NewTable()
+	em.L.SetFuncs(config, map[string]lua.LGFunction{
+		"set": func(L *lua.LState) int {
+			em.M.DebugLog += "hi from lua"
+			return 1
+		},
+	})
+
+	return config
+}

+ 3 - 2
internal/extension/extension.go

@@ -11,7 +11,7 @@ import (
 
 type ExtensionManager struct {
 	L *lua.LState
-	Model *editor.Model
+	M *editor.Model
 	LoadedFiles []string
 }
 
@@ -21,7 +21,7 @@ var embeddedScripts embed.FS
 func NewExtensionManager(m *editor.Model) *ExtensionManager {
 	em := &ExtensionManager{
 		L: lua.NewState(),
-		Model: m,
+		M: m,
 	}
 
 	em.registerAPI()
@@ -92,6 +92,7 @@ func (em *ExtensionManager) registerAPI() {
 
 	moose := em.L.NewTable()
 	em.L.SetGlobal("ms", moose)
+	moose.RawSetString("config", GetConfigTable(em))
 }
 
 func (em *ExtensionManager) LoadFile(path string) error {

+ 3 - 1
internal/extension/lua/init.lua

@@ -1,2 +1,4 @@
 test = require("test")
-test.run()
+test.run()
+
+-- ms.config.set()

+ 4 - 3
internal/util/util.go

@@ -1,8 +1,8 @@
 package util
 
 import (
-	"strings"
 	"slices"
+	"strings"
 )
 
 func StandardizeBindingsArray(arr []string) []string {
@@ -18,8 +18,9 @@ func StandardizeBinding(binding string) string {
 
 	for i, s := range arr {
 		lower := strings.ToLower(s)
+		lower = strings.ReplaceAll(lower, "backtab", "shift+tab")
 		arr[i] = lower
-		
+
 		if strings.HasPrefix(lower, "rune[") && strings.HasSuffix(lower, "]") {
 			arr[i] = lower[5 : len(s)-1]
 		}
@@ -37,4 +38,4 @@ func NonNilLen[T any](slice []T) int {
 		}
 	}
 	return count
-}
+}