fix: cleaned up and fixed the settings that needed updating

This commit is contained in:
Hayden Hargreaves 2026-03-05 14:34:21 -07:00
parent dc9a814508
commit 098641f5c0
10 changed files with 188 additions and 90 deletions

View File

@ -16,6 +16,7 @@ func main() {
win := core.NewWindowBuilder().
WithBuffer(&buf).
WithOptions(core.NewDefaultWinOptions()).
Build()
model := editor.NewModelBuilder().

View File

@ -241,7 +241,7 @@ func (a InsertTab) Execute(m Model) tea.Cmd {
x, y := win.Cursor.Col, win.Cursor.Line
l := buf.Lines[y]
tabs := strings.Repeat(" ", m.Settings().TabSize)
tabs := strings.Repeat(" ", m.Settings().TabStop)
if x < len(l) {
buf.SetLine(y, l[:x]+tabs+l[x:])
} else {

View File

@ -45,8 +45,8 @@ type Model interface {
Mode() core.Mode
SetMode(mode core.Mode)
Settings() core.Settings
SetSettings(s core.Settings)
Settings() core.EditorSettings
SetSettings(s core.EditorSettings)
// ==================================================
// Registers
@ -55,7 +55,6 @@ type Model interface {
GetRegister(name rune) (core.Register, bool)
SetRegister(name rune, t core.RegisterType, cnt []string) error
UpdateDefaultRegister(t core.RegisterType, cnt []string)
}
// Action is the base interface - anything executable

View File

@ -110,7 +110,15 @@ type Setting struct {
Name string
ShortForm string
Type SettingType
Get func(s core.Settings) any
Get func(s core.EditorSettings) any
Set func(m action.Model, val any)
}
type WindowSetting struct {
Name string
ShortForm string
Type SettingType
Get func(m action.Model) any
Set func(m action.Model, val any)
}
@ -123,50 +131,69 @@ const (
StringSetting
)
// settingsMap defines all available settings
// settingsMap defines all available editor settings
var settingsMap = []Setting{
{
Name: "tabstop",
ShortForm: "ts",
Type: IntSetting,
Get: func(s core.EditorSettings) any { return s.TabStop },
Set: func(m action.Model, val any) {
s := m.Settings()
s.TabStop = val.(int)
m.SetSettings(s)
},
},
}
// windowSettingsMap defines all available window settings
var windowSettingsMap = []WindowSetting{
{
Name: "number",
ShortForm: "nu",
Type: BoolSetting,
Get: func(s core.Settings) any { return s.Number },
Get: func(m action.Model) any { return m.ActiveWindow().Options.Number },
Set: func(m action.Model, val any) {
s := m.Settings()
s.Number = val.(bool)
m.SetSettings(s)
w := m.ActiveWindow()
o := w.Options
o.Number = val.(bool)
w.SetOptions(o)
},
},
{
Name: "relativenumber",
ShortForm: "rnu",
Type: BoolSetting,
Get: func(s core.Settings) any { return s.RelativeNumber },
Get: func(m action.Model) any { return m.ActiveWindow().Options.RelativeNumber },
Set: func(m action.Model, val any) {
s := m.Settings()
s.RelativeNumber = val.(bool)
m.SetSettings(s)
},
},
{
Name: "tabstop",
ShortForm: "ts",
Type: IntSetting,
Get: func(s core.Settings) any { return s.TabSize },
Set: func(m action.Model, val any) {
s := m.Settings()
s.TabSize = val.(int)
m.SetSettings(s)
w := m.ActiveWindow()
o := w.Options
o.RelativeNumber = val.(bool)
w.SetOptions(o)
},
},
{
Name: "scrolloff",
ShortForm: "so",
Type: IntSetting,
Get: func(s core.Settings) any { return s.ScrollOff },
Get: func(m action.Model) any { return m.ActiveWindow().Options.ScrollOff },
Set: func(m action.Model, val any) {
s := m.Settings()
s.ScrollOff = val.(int)
m.SetSettings(s)
w := m.ActiveWindow()
o := w.Options
o.ScrollOff = val.(int)
w.SetOptions(o)
},
},
{
Name: "guttersize",
ShortForm: "gu",
Type: IntSetting,
Get: func(m action.Model) any { return m.ActiveWindow().Options.GutterSize },
Set: func(m action.Model, val any) {
w := m.ActiveWindow()
o := w.Options
o.GutterSize = val.(int)
w.SetOptions(o)
},
},
}
@ -186,14 +213,27 @@ func lookupSetting(name string) *Setting {
return nil
}
// lookupWindowSetting: Finds a window setting by name, short form, or prefix.
func lookupWindowSetting(name string) *WindowSetting {
for i := range windowSettingsMap {
s := &windowSettingsMap[i]
if name == s.Name || name == s.ShortForm {
return s
}
// Prefix matching
if len(name) >= len(s.ShortForm) && strings.HasPrefix(s.Name, name) {
return s
}
}
return nil
}
// parseSetOption: Parses and applies a single :set option.
func parseSetOption(m action.Model, opt string) error {
// Handle toggle: option!
if name, ok := strings.CutSuffix(opt, "!"); ok {
setting := lookupSetting(name)
if setting == nil {
return nil // Unknown setting
}
if setting != nil {
if setting.Type == BoolSetting {
// Toggle the boolean
currentVal := setting.Get(m.Settings()).(bool)
@ -202,26 +242,47 @@ func parseSetOption(m action.Model, opt string) error {
return nil
}
windowSetting := lookupWindowSetting(name)
if windowSetting != nil {
if windowSetting.Type == BoolSetting {
// Toggle the boolean
currentVal := windowSetting.Get(m).(bool)
windowSetting.Set(m, !currentVal)
}
return nil
}
return nil
}
// Handle disable: nooption
if name, ok := strings.CutPrefix(opt, "no"); ok {
setting := lookupSetting(name)
if setting == nil {
return nil
}
if setting != nil {
if setting.Type == BoolSetting {
setting.Set(m, false)
}
return nil
}
windowSetting := lookupWindowSetting(name)
if windowSetting != nil {
if windowSetting.Type == BoolSetting {
windowSetting.Set(m, false)
}
return nil
}
return nil
}
// Handle assignment: option=value
if strings.Contains(opt, "=") {
parts := strings.SplitN(opt, "=", 2)
name, value := parts[0], parts[1]
setting := lookupSetting(name)
if setting == nil {
return nil
}
if setting != nil {
switch setting.Type {
case IntSetting:
intVal, err := strconv.Atoi(value)
@ -239,13 +300,43 @@ func parseSetOption(m action.Model, opt string) error {
return nil
}
// Handle enable: option (boolean only)
setting := lookupSetting(opt)
if setting == nil {
return nil
windowSetting := lookupWindowSetting(name)
if windowSetting != nil {
switch windowSetting.Type {
case IntSetting:
intVal, err := strconv.Atoi(value)
if err != nil {
return err
}
if setting.Type == BoolSetting {
setting.Set(m, true)
windowSetting.Set(m, intVal)
case StringSetting:
windowSetting.Set(m, value)
case BoolSetting:
// Handle :set option=true / :set option=false
boolVal := value == "true" || value == "1" || value == "yes"
windowSetting.Set(m, boolVal)
}
return nil
}
return nil
}
// Handle enable: option (boolean only)
setting := lookupSetting(opt)
if setting != nil {
if setting.Type == BoolSetting {
setting.Set(m, true)
}
return nil
}
windowSetting := lookupWindowSetting(opt)
if windowSetting != nil {
if windowSetting.Type == BoolSetting {
windowSetting.Set(m, true)
}
return nil
}
return nil

View File

@ -1,23 +1,15 @@
package core
// Settings: Configuration options for editor display and behavior.
type Settings struct {
Number bool
RelativeNumber bool
GutterSize int
TabSize int
ScrollOff int
// EditorSettings: Configuration options for editor display and behavior.
type EditorSettings struct {
TabStop int
// TODO: Colors
}
// NewDefaultSettings: Creates a Settings struct with sensible defaults for
// line numbers, gutter width, tab size, and scroll offset.
func NewDefaultSettings() Settings {
return Settings{
Number: true,
RelativeNumber: true,
GutterSize: 5,
TabSize: 2,
ScrollOff: 8,
func NewDefaultSettings() EditorSettings {
return EditorSettings{
TabStop: 2,
}
}

View File

@ -2,12 +2,22 @@ package core
// TODO: No more global settings, window-wide settings
type WinOptions struct {
// Number bool
Number bool
RelativeNumber bool
GutterSize int
// Wrap bool
// Relnumber bool
ScrollOff int
}
func NewDefaultWinOptions() WinOptions {
return WinOptions{
Number: true,
RelativeNumber: true,
GutterSize: 5,
ScrollOff: 8,
}
}
type Window struct {
Id int
Number int // Ignored for now, will be used when splits come into play
@ -181,3 +191,8 @@ func (w *Window) SetDimensions(width, height int) {
w.Width = width
w.Height = height
}
// Window.SetOptions: Sets the options of this window.
func (w *Window) SetOptions(opts WinOptions) {
w.Options = opts
}

View File

@ -102,8 +102,8 @@ func TestCommandSetInteger(t *testing.T) {
sendKeys(tm, ":", "s", "e", "t", " ", "t", "a", "b", "s", "t", "o", "p", "=", "4", "enter")
m := getFinalModel(t, tm)
if m.Settings().TabSize != 4 {
t.Errorf("TabSize = %d, want 4", m.Settings().TabSize)
if m.Settings().TabStop != 4 {
t.Errorf("TabSize = %d, want 4", m.Settings().TabStop)
}
})
@ -114,8 +114,8 @@ func TestCommandSetInteger(t *testing.T) {
sendKeys(tm, ":", "s", "e", "t", " ", "t", "s", "=", "8", "enter")
m := getFinalModel(t, tm)
if m.Settings().TabSize != 8 {
t.Errorf("TabSize = %d, want 8", m.Settings().TabSize)
if m.Settings().TabStop != 8 {
t.Errorf("TabSize = %d, want 8", m.Settings().TabStop)
}
})

View File

@ -42,7 +42,7 @@ type Model struct {
commandOutput string
// Global settings (TODO: This needs to be refactored)
settings core.Settings
settings core.EditorSettings
// Registers
registers map[rune]core.Register // name -> register
@ -185,7 +185,7 @@ func (m *Model) processInsertKey(key string) {
}
case "tab":
tabs := strings.Repeat(" ", m.Settings().TabSize)
tabs := strings.Repeat(" ", m.Settings().TabStop)
if col < len(l) {
buf.SetLine(line, l[:col]+tabs+l[col:])
} else {
@ -282,11 +282,11 @@ func (m *Model) SetMode(mode core.Mode) {
m.mode = mode
}
func (m *Model) Settings() core.Settings {
func (m *Model) Settings() core.EditorSettings {
return m.settings
}
func (m *Model) SetSettings(s core.Settings) {
func (m *Model) SetSettings(s core.EditorSettings) {
m.settings = s
}

View File

@ -93,7 +93,7 @@ func (mb *ModelBuilder) WithTermHeight(height int) *ModelBuilder {
}
// ModelBuilder.WithSettings: Set the editor settings (tabstop, scrolloff, etc).
func (mb *ModelBuilder) WithSettings(settings core.Settings) *ModelBuilder {
func (mb *ModelBuilder) WithSettings(settings core.EditorSettings) *ModelBuilder {
mb.model.settings = settings
return mb
}

View File

@ -19,10 +19,10 @@ func (m Model) View() string {
// Each window has its own status bar and mode
styles := m.Styles()
settings := m.Settings()
options := win.Options
// Draw window
view := viewWindow(win, styles, settings, m.Mode())
view := viewWindow(win, styles, options, m.Mode())
// Command bar is seperate
cmdBar := drawCommandBar(m)
@ -31,7 +31,7 @@ func (m Model) View() string {
// viewWindow: Renders a single window's content including line numbers and buffer text.
// Each window has its own line numbers, gutter, and viewport dimensions.
func viewWindow(w *core.Window, styles style.Styles, settings core.Settings, mode core.Mode) string {
func viewWindow(w *core.Window, styles style.Styles, options core.WinOptions, mode core.Mode) string {
buf := w.Buffer
var view strings.Builder
@ -42,7 +42,7 @@ func viewWindow(w *core.Window, styles style.Styles, settings core.Settings, mod
// Draw buffer lines
for lineNum := start; lineNum < end; lineNum++ {
if lineNum < buf.LineCount() {
line := drawLine(w, styles, settings, mode, buf.Line(lineNum), lineNum)
line := drawLine(w, styles, options, mode, buf.Line(lineNum), lineNum)
view.WriteString(line)
}
view.WriteRune('\n')
@ -57,7 +57,7 @@ func viewWindow(w *core.Window, styles style.Styles, settings core.Settings, mod
// drawLine: Renders a single line with syntax highlighting, cursor, and visual selection.
// Handles gutter, cursor rendering, and visual mode highlighting.
func drawLine(w *core.Window, styles style.Styles, settings core.Settings, mode core.Mode, line string, lineNumber int) string {
func drawLine(w *core.Window, styles style.Styles, options core.WinOptions, mode core.Mode, line string, lineNumber int) string {
runes := []rune(line)
curStyle := styles.CursorStyle(mode)
@ -66,7 +66,7 @@ func drawLine(w *core.Window, styles style.Styles, settings core.Settings, mode
var view strings.Builder
// Draw gutter first
gutter := drawGutter(w, styles, settings, lineNumber)
gutter := drawGutter(w, styles, options, lineNumber)
view.WriteString(gutter)
// Now draw the line content
@ -97,15 +97,15 @@ func drawLine(w *core.Window, styles style.Styles, settings core.Settings, mode
// drawGutter: Renders the line number gutter with support for both absolute and
// relative line numbers, highlighting the current line differently.
func drawGutter(w *core.Window, styles style.Styles, settings core.Settings, curLine int) string {
if !(settings.Number || settings.RelativeNumber) {
func drawGutter(w *core.Window, styles style.Styles, options core.WinOptions, curLine int) string {
if !(options.Number || options.RelativeNumber) {
return ""
}
// Required vars
var (
view strings.Builder
gutSize int = settings.GutterSize - 1 // -1 is for padding
gutSize int = options.GutterSize - 1 // -1 is for padding
currentLine bool = curLine == w.Cursor.Line
lineNumber int
@ -115,7 +115,7 @@ func drawGutter(w *core.Window, styles style.Styles, settings core.Settings, cur
)
// If we have relative setting, set the numbers relatively
if settings.RelativeNumber {
if options.RelativeNumber {
if curLine > w.Cursor.Line {
lineNumber = curLine - w.Cursor.Line
}
@ -126,7 +126,7 @@ func drawGutter(w *core.Window, styles style.Styles, settings core.Settings, cur
}
// If we have number setting AND not relative setting OR we are on current line, use current line number
if (settings.Number && !settings.RelativeNumber) || (settings.Number && currentLine) {
if (options.Number && !options.RelativeNumber) || (options.Number && currentLine) {
lineNumber = curLine + 1
}