Compare commits
4 Commits
ccb061989a
...
93968e7333
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93968e7333 | ||
|
|
098641f5c0 | ||
|
|
dc9a814508 | ||
|
|
03c3a41162 |
@ -16,6 +16,7 @@ func main() {
|
||||
|
||||
win := core.NewWindowBuilder().
|
||||
WithBuffer(&buf).
|
||||
WithOptions(core.NewDefaultWinOptions()).
|
||||
Build()
|
||||
|
||||
model := editor.NewModelBuilder().
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,14 +300,44 @@ func parseSetOption(m action.Model, opt string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle enable: option (boolean only)
|
||||
setting := lookupSetting(opt)
|
||||
if setting == nil {
|
||||
windowSetting := lookupWindowSetting(name)
|
||||
if windowSetting != nil {
|
||||
switch windowSetting.Type {
|
||||
case IntSetting:
|
||||
intVal, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -62,8 +72,10 @@ func (w *Window) AdjustScroll() {
|
||||
return
|
||||
}
|
||||
|
||||
viewPort := w.ViewportHeight()
|
||||
|
||||
// Effective scrollOff (can't be more than half the viewport)
|
||||
off := min(w.Options.ScrollOff, w.Height/2)
|
||||
off := min(w.Options.ScrollOff, viewPort/2)
|
||||
|
||||
// Cursor too close to top — scroll up
|
||||
if w.Cursor.Line < w.ScrollY+off {
|
||||
@ -71,15 +83,27 @@ func (w *Window) AdjustScroll() {
|
||||
}
|
||||
|
||||
// Cursor too close to bottom — scroll down
|
||||
if w.Cursor.Line > w.ScrollY+w.Height-1-off {
|
||||
w.ScrollY = w.Cursor.Line - w.Height + 1 + off
|
||||
if w.Cursor.Line > w.ScrollY+viewPort-1-off {
|
||||
w.ScrollY = w.Cursor.Line - viewPort + 1 + off
|
||||
}
|
||||
|
||||
// Clamp scrollY to valid range
|
||||
maxScroll := max(0, w.Buffer.LineCount()-w.Height)
|
||||
maxScroll := max(0, w.Buffer.LineCount()-viewPort)
|
||||
w.ScrollY = max(0, min(w.ScrollY, maxScroll))
|
||||
}
|
||||
|
||||
// ==================================================
|
||||
// Getters (for computed values)
|
||||
// ==================================================
|
||||
|
||||
func (w *Window) ViewportHeight() int {
|
||||
// TODO: This will need more magic when splits come into play
|
||||
|
||||
// -1 for command bar
|
||||
// -1 for status line
|
||||
return w.Height - 2
|
||||
}
|
||||
|
||||
// ==================================================
|
||||
// Setters
|
||||
// ==================================================
|
||||
@ -167,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
|
||||
}
|
||||
|
||||
@ -20,9 +20,7 @@ func NewWindowBuilder() *WindowBuilder {
|
||||
ScrollY: 0,
|
||||
Height: 0,
|
||||
Width: 0,
|
||||
Options: WinOptions{
|
||||
ScrollOff: 8, // 8 is default
|
||||
},
|
||||
Options: NewDefaultWinOptions(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ func TestCommandSetBoolean(t *testing.T) {
|
||||
sendKeys(tm, ":", "s", "e", "t", " ", "n", "o", "n", "u", "m", "b", "e", "r", "enter")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Settings().Number {
|
||||
if m.ActiveWindow().Options.Number {
|
||||
t.Error("expected Number to be false after :set nonumber")
|
||||
}
|
||||
})
|
||||
@ -33,7 +33,7 @@ func TestCommandSetBoolean(t *testing.T) {
|
||||
sendKeys(tm, ":", "s", "e", "t", " ", "n", "u", "enter")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if !m.Settings().Number {
|
||||
if !m.ActiveWindow().Options.Number {
|
||||
t.Error("expected Number to be true after :set nu")
|
||||
}
|
||||
})
|
||||
@ -46,7 +46,7 @@ func TestCommandSetBoolean(t *testing.T) {
|
||||
sendKeys(tm, ":", "s", "e", "t", " ", "n", "u", "m", "b", "e", "r", "!", "enter")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Settings().Number {
|
||||
if m.ActiveWindow().Options.Number {
|
||||
t.Error("expected Number to be false after :set number!")
|
||||
}
|
||||
})
|
||||
@ -60,7 +60,7 @@ func TestCommandSetBoolean(t *testing.T) {
|
||||
sendKeys(tm, ":", "s", "e", "t", " ", "n", "u", "!", "enter")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if !m.Settings().Number {
|
||||
if !m.ActiveWindow().Options.Number {
|
||||
t.Error("expected Number to be true after double toggle")
|
||||
}
|
||||
})
|
||||
@ -73,7 +73,7 @@ func TestCommandSetBoolean(t *testing.T) {
|
||||
sendKeys(tm, ":", "s", "e", "t", " ", "n", "o", "r", "n", "u", "enter")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Settings().RelativeNumber {
|
||||
if m.ActiveWindow().Options.RelativeNumber {
|
||||
t.Error("expected RelativeNumber to be false after :set nornu")
|
||||
}
|
||||
})
|
||||
@ -87,7 +87,7 @@ func TestCommandSetBoolean(t *testing.T) {
|
||||
sendKeys(tm, ":", "s", "e", "t", " ", "r", "n", "u", "enter")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if !m.Settings().RelativeNumber {
|
||||
if !m.ActiveWindow().Options.RelativeNumber {
|
||||
t.Error("expected RelativeNumber to be true after :set rnu")
|
||||
}
|
||||
})
|
||||
@ -95,15 +95,15 @@ func TestCommandSetBoolean(t *testing.T) {
|
||||
|
||||
func TestCommandSetInteger(t *testing.T) {
|
||||
t.Run("':set tabstop=4' sets tab size", func(t *testing.T) {
|
||||
// Default TabSize=2
|
||||
// Default TabStop=2
|
||||
lines := []string{"hello"}
|
||||
tm := newTestModelWithLines(t, lines)
|
||||
|
||||
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("TabStop = %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("TabStop = %d, want 8", m.Settings().TabStop)
|
||||
}
|
||||
})
|
||||
|
||||
@ -127,8 +127,8 @@ func TestCommandSetInteger(t *testing.T) {
|
||||
sendKeys(tm, ":", "s", "e", "t", " ", "s", "c", "r", "o", "l", "l", "o", "f", "f", "=", "5", "enter")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Settings().ScrollOff != 5 {
|
||||
t.Errorf("ScrollOff = %d, want 5", m.Settings().ScrollOff)
|
||||
if m.ActiveWindow().Options.ScrollOff != 5 {
|
||||
t.Errorf("ScrollOff = %d, want 5", m.ActiveWindow().Options.ScrollOff)
|
||||
}
|
||||
})
|
||||
|
||||
@ -139,8 +139,8 @@ func TestCommandSetInteger(t *testing.T) {
|
||||
sendKeys(tm, ":", "s", "e", "t", " ", "s", "o", "=", "1", "0", "enter")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Settings().ScrollOff != 10 {
|
||||
t.Errorf("ScrollOff = %d, want 10", m.Settings().ScrollOff)
|
||||
if m.ActiveWindow().Options.ScrollOff != 10 {
|
||||
t.Errorf("ScrollOff = %d, want 10", m.ActiveWindow().Options.ScrollOff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -334,146 +334,3 @@ func (m *Model) UpdateDefaultRegister(t core.RegisterType, cnt []string) {
|
||||
m.SetRegister('0', t, cnt)
|
||||
m.SetRegister('"', t, cnt)
|
||||
}
|
||||
|
||||
// ==================================================
|
||||
// Depreciated
|
||||
// ==================================================
|
||||
// func (m *Model) Lines() []string {
|
||||
// win := m.ActiveWindow()
|
||||
// return win.Buffer.Lines
|
||||
// }
|
||||
//
|
||||
// func (m *Model) Line(idx int) string {
|
||||
// win := m.ActiveWindow()
|
||||
// return win.Buffer.Line(idx)
|
||||
// }
|
||||
//
|
||||
// func (m *Model) SetLine(idx int, content string) {
|
||||
// win := m.ActiveWindow()
|
||||
// win.Buffer.SetLine(idx, content)
|
||||
// }
|
||||
//
|
||||
// func (m *Model) InsertLine(idx int, content string) {
|
||||
// win := m.ActiveWindow()
|
||||
// win.Buffer.InsertLine(idx, content)
|
||||
// }
|
||||
//
|
||||
// func (m *Model) DeleteLine(idx int) {
|
||||
// win := m.ActiveWindow()
|
||||
// win.Buffer.DeleteLine(idx)
|
||||
// }
|
||||
//
|
||||
// func (m *Model) LineCount() int {
|
||||
// win := m.ActiveWindow()
|
||||
// return win.Buffer.LineCount()
|
||||
// }
|
||||
//
|
||||
// func (m *Model) CursorX() int {
|
||||
// win := m.ActiveWindow()
|
||||
// return win.Cursor.Col
|
||||
// }
|
||||
//
|
||||
// func (m *Model) CursorY() int {
|
||||
// win := m.ActiveWindow()
|
||||
// return win.Cursor.Line
|
||||
// }
|
||||
//
|
||||
// func (m *Model) SetCursorX(x int) {
|
||||
// win := m.ActiveWindow()
|
||||
// win.Cursor.Col = x
|
||||
// }
|
||||
//
|
||||
// func (m *Model) SetCursorY(y int) {
|
||||
// win := m.ActiveWindow()
|
||||
// win.Cursor.Line = y
|
||||
// }
|
||||
//
|
||||
// // Anchor methods
|
||||
// func (m *Model) AnchorX() int {
|
||||
// win := m.ActiveWindow()
|
||||
// return win.Anchor.Col
|
||||
// }
|
||||
//
|
||||
// func (m *Model) AnchorY() int {
|
||||
// win := m.ActiveWindow()
|
||||
// return win.Anchor.Line
|
||||
// }
|
||||
//
|
||||
// func (m *Model) SetAnchorX(x int) {
|
||||
// win := m.ActiveWindow()
|
||||
// win.Anchor.Col = x
|
||||
// }
|
||||
//
|
||||
// func (m *Model) SetAnchorY(y int) {
|
||||
// win := m.ActiveWindow()
|
||||
// win.Anchor.Line = y
|
||||
// }
|
||||
//
|
||||
// func (m *Model) GetCursorPosition() *action.Position {
|
||||
// // Return a copy of the position
|
||||
// win := m.ActiveWindow()
|
||||
// pos := win.Cursor
|
||||
// return &pos
|
||||
// }
|
||||
//
|
||||
// // Window
|
||||
// func (m *Model) ScrollY() int {
|
||||
// win := m.ActiveWindow()
|
||||
// return win.ScrollY
|
||||
// }
|
||||
//
|
||||
// func (m *Model) SetScrollY(y int) {
|
||||
// win := m.ActiveWindow()
|
||||
// win.ScrollY = y
|
||||
// }
|
||||
//
|
||||
// func (m *Model) WinH() int {
|
||||
// win := m.ActiveWindow()
|
||||
// return win.Height
|
||||
// }
|
||||
//
|
||||
// func (m *Model) WinW() int {
|
||||
// win := m.ActiveWindow()
|
||||
// return win.Width
|
||||
// }
|
||||
//
|
||||
// func (m *Model) ViewPortH() int {
|
||||
// win := m.ActiveWindow()
|
||||
// return win.Height - 2
|
||||
// }
|
||||
//
|
||||
// func (m *Model) ClampCursorX() {
|
||||
// win := m.ActiveWindow()
|
||||
// win.ClampCursorX()
|
||||
// }
|
||||
//
|
||||
// func (m *Model) ActiveWindowId() int {
|
||||
// return m.activeWindowId
|
||||
// }
|
||||
//
|
||||
// // TODO: MOVE THIS
|
||||
// // AdjustScroll ensures the cursor stays within the viewport with scrollOff margins.
|
||||
// // Call this after any cursor movement.
|
||||
// func (m *Model) AdjustScroll() {
|
||||
// viewportHeight := m.ViewPortH()
|
||||
// if viewportHeight <= 0 {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // Effective scrollOff (can't be more than half the viewport)
|
||||
// off := min(m.Settings().ScrollOff, viewportHeight/2)
|
||||
//
|
||||
// // Cursor too close to top — scroll up
|
||||
// if m.CursorY() < m.ScrollY()+off {
|
||||
// m.SetScrollY(m.CursorY() - off)
|
||||
// }
|
||||
//
|
||||
// // Cursor too close to bottom — scroll down
|
||||
// if m.CursorY() > m.ScrollY()+viewportHeight-1-off {
|
||||
// m.SetScrollY(m.CursorY() - viewportHeight + 1 + off)
|
||||
// }
|
||||
//
|
||||
// // Clamp scrollY to valid range
|
||||
// maxScroll := max(0, m.LineCount()-viewportHeight)
|
||||
// m.SetScrollY(max(0, min(m.ScrollY(), maxScroll)))
|
||||
// }
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -5,25 +5,275 @@ import (
|
||||
"strings"
|
||||
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
||||
)
|
||||
|
||||
// Model.View: Renders the complete editor view including buffer content, line
|
||||
// numbers, status bar, and command line.
|
||||
func (m Model) View() string {
|
||||
win := m.ActiveWindow()
|
||||
|
||||
// NOTES:
|
||||
// One single command line across entire viewport
|
||||
// Each window has its own line numbers and gutter
|
||||
// Each window has its own status bar and mode
|
||||
|
||||
styles := m.Styles()
|
||||
options := win.Options
|
||||
|
||||
// Draw window
|
||||
view := viewWindow(win, styles, options, m.Mode())
|
||||
|
||||
// Command bar is seperate
|
||||
cmdBar := drawCommandBar(m)
|
||||
return view + cmdBar
|
||||
}
|
||||
|
||||
// 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, options core.WinOptions, mode core.Mode) string {
|
||||
buf := w.Buffer
|
||||
var view strings.Builder
|
||||
|
||||
// Compute window size (y)
|
||||
start := w.ScrollY
|
||||
end := w.ScrollY + w.ViewportHeight()
|
||||
|
||||
// Draw buffer lines
|
||||
for lineNum := start; lineNum < end; lineNum++ {
|
||||
if lineNum < buf.LineCount() {
|
||||
line := drawLine(w, styles, options, mode, buf.Line(lineNum), lineNum)
|
||||
view.WriteString(line)
|
||||
}
|
||||
view.WriteRune('\n')
|
||||
}
|
||||
|
||||
// Draw status line
|
||||
statusBar := drawStatusBar(w, mode)
|
||||
view.WriteString(statusBar + "\n")
|
||||
|
||||
return view.String()
|
||||
}
|
||||
|
||||
// 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, options core.WinOptions, mode core.Mode, line string, lineNumber int) string {
|
||||
runes := []rune(line)
|
||||
|
||||
curStyle := styles.CursorStyle(mode)
|
||||
visStyle := styles.VisualHighlight
|
||||
|
||||
var view strings.Builder
|
||||
|
||||
// Draw gutter first
|
||||
gutter := drawGutter(w, styles, options, lineNumber)
|
||||
view.WriteString(gutter)
|
||||
|
||||
// Now draw the line content
|
||||
for col := 0; col <= len(runes); col++ {
|
||||
// Current char is cursor
|
||||
if col == w.Cursor.Col && lineNumber == w.Cursor.Line {
|
||||
if col < len(runes) {
|
||||
view.WriteString(curStyle.Render(string(runes[col])))
|
||||
} else {
|
||||
view.WriteString(curStyle.Render(" "))
|
||||
}
|
||||
|
||||
// Not cursor, but not end
|
||||
} else if col < len(runes) {
|
||||
if mode.IsVisualMode() && posInsideSelection(w, mode, col, lineNumber) {
|
||||
view.WriteString(visStyle.Render(string(runes[col])))
|
||||
} else {
|
||||
view.WriteRune(runes[col])
|
||||
}
|
||||
// Allow highlight on blank lines or chars
|
||||
} else if mode.IsVisualMode() && posInsideSelection(w, mode, col, lineNumber) {
|
||||
view.WriteString(visStyle.Render(" "))
|
||||
}
|
||||
}
|
||||
|
||||
return view.String()
|
||||
}
|
||||
|
||||
// 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, options core.WinOptions, curLine int) string {
|
||||
if !(options.Number || options.RelativeNumber) {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Required vars
|
||||
var (
|
||||
view strings.Builder
|
||||
gutSize int = options.GutterSize - 1 // -1 is for padding
|
||||
currentLine bool = curLine == w.Cursor.Line
|
||||
lineNumber int
|
||||
|
||||
gutter string
|
||||
gutterStyle = styles.Gutter
|
||||
gutterStyleCur = styles.GutterCurrentLine
|
||||
)
|
||||
|
||||
// If we have relative setting, set the numbers relatively
|
||||
if options.RelativeNumber {
|
||||
if curLine > w.Cursor.Line {
|
||||
lineNumber = curLine - w.Cursor.Line
|
||||
}
|
||||
|
||||
if curLine < w.Cursor.Line {
|
||||
lineNumber = w.Cursor.Line - curLine
|
||||
}
|
||||
}
|
||||
|
||||
// If we have number setting AND not relative setting OR we are on current line, use current line number
|
||||
if (options.Number && !options.RelativeNumber) || (options.Number && currentLine) {
|
||||
lineNumber = curLine + 1
|
||||
}
|
||||
|
||||
// Draw the gutter
|
||||
gutter = fmt.Sprintf("%*d ", gutSize, lineNumber)
|
||||
if currentLine {
|
||||
view.WriteString(gutterStyleCur.Render(gutter))
|
||||
} else {
|
||||
view.WriteString(gutterStyle.Render(gutter))
|
||||
}
|
||||
|
||||
return view.String()
|
||||
|
||||
// if m.Settings().Number || m.Settings().RelativeNumber {
|
||||
// var (
|
||||
// gutter string
|
||||
// currentLine bool = false
|
||||
// lineNumber int
|
||||
// )
|
||||
//
|
||||
// if m.Settings().RelativeNumber {
|
||||
// // Relative line numbers: show distance from cursor, current line shows absolute
|
||||
// if i > win.Cursor.Line {
|
||||
// lineNumber = i - win.Cursor.Line
|
||||
// gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber)
|
||||
// } else if i < win.Cursor.Line {
|
||||
// lineNumber = win.Cursor.Line - i
|
||||
// gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber)
|
||||
// } else {
|
||||
// // Current line: show absolute number if Number is also set, otherwise show 0
|
||||
// currentLine = true
|
||||
// if m.Settings().Number {
|
||||
// lineNumber = i + 1
|
||||
// gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber)
|
||||
// } else {
|
||||
// gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, 0)
|
||||
// }
|
||||
// }
|
||||
// } else if m.Settings().Number {
|
||||
// // Absolute line numbers only
|
||||
// lineNumber = i + 1
|
||||
// currentLine = (i == win.Cursor.Line)
|
||||
// gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber)
|
||||
// }
|
||||
// if currentLine {
|
||||
// view.WriteString(m.Styles().GutterCurrentLine.Render(gutter))
|
||||
// } else {
|
||||
// view.WriteString(m.Styles().Gutter.Render(gutter))
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// drawStatusBar: Renders the status bar with mode and cursor position,
|
||||
// padding the middle with spaces to fill the terminal width.
|
||||
func drawStatusBar(w *core.Window, mode core.Mode) string {
|
||||
left := leftBar(w, mode)
|
||||
right := rightBar(w, mode)
|
||||
|
||||
diff := w.Width - (len(left) + len(right))
|
||||
|
||||
// This happens when the terminal spawns
|
||||
if diff <= 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
middle := strings.Repeat(" ", diff)
|
||||
return left + middle + right
|
||||
}
|
||||
|
||||
// leftBar: Returns the left side of the status bar showing the current mode.
|
||||
func leftBar(w *core.Window, mode core.Mode) string {
|
||||
buf := w.Buffer
|
||||
return fmt.Sprintf(" %s %s", mode.ToString(), buf.Filename)
|
||||
}
|
||||
|
||||
// rightBar: Returns the right side of the status bar showing cursor position
|
||||
// and selection count in visual mode.
|
||||
func rightBar(w *core.Window, mode core.Mode) (bar string) {
|
||||
if mode.IsVisualMode() {
|
||||
lineCount := max(w.Anchor.Line, w.Cursor.Line) - min(w.Anchor.Line, w.Cursor.Line) + 1
|
||||
bar = fmt.Sprintf("%d:%d <%d>", w.Cursor.Line+1, w.Cursor.Col+1, lineCount)
|
||||
} else {
|
||||
bar = fmt.Sprintf("%d:%d ", w.Cursor.Line+1, w.Cursor.Col+1)
|
||||
}
|
||||
buf := w.Buffer
|
||||
bar = fmt.Sprintf("%s %s", buf.Filetype, bar)
|
||||
return
|
||||
}
|
||||
|
||||
// drawCommandBar: Renders the command line showing command input, errors, or
|
||||
// output depending on the current mode and state.
|
||||
func drawCommandBar(m Model) string {
|
||||
// Compute left bar (command side)
|
||||
var leftBar string
|
||||
if m.Mode() == core.CommandMode {
|
||||
leftBar = ":"
|
||||
cmd := m.Command()
|
||||
cur := m.CommandCursor()
|
||||
for i := 0; i < len(cmd); i++ {
|
||||
if i == cur {
|
||||
leftBar += m.Styles().CursorStyle(m.Mode()).Render(string(cmd[i]))
|
||||
} else {
|
||||
leftBar += string(cmd[i])
|
||||
}
|
||||
}
|
||||
// Cursor at end of command
|
||||
if cur >= len(cmd) {
|
||||
leftBar += m.Styles().CursorStyle(m.Mode()).Render(" ")
|
||||
}
|
||||
// bar = fmt.Sprintf("%s %d", bar, cur)
|
||||
} else if m.CommandError() != nil {
|
||||
leftBar = m.Styles().CommandError.Render(m.CommandError().Error())
|
||||
} else if strings.TrimSpace(m.CommandOutput()) != "" {
|
||||
leftBar = m.CommandOutput()
|
||||
} else if strings.TrimSpace(m.Command()) != "" {
|
||||
leftBar = fmt.Sprintf(":%s", m.Command())
|
||||
}
|
||||
|
||||
// Compute right bar
|
||||
|
||||
var rightBar string
|
||||
if len(m.input.Pending()) > 0 {
|
||||
width := 10 // Size of the block to display
|
||||
rightBar = fmt.Sprintf("%-*s", width, m.input.Pending())
|
||||
}
|
||||
|
||||
dif := m.termWidth - (len(leftBar) + len(rightBar))
|
||||
|
||||
bar := leftBar + strings.Repeat(" ", dif) + rightBar
|
||||
return bar
|
||||
}
|
||||
|
||||
// posInsideSelection: Returns true if the given position is inside the current
|
||||
// visual selection, handling all three visual modes differently.
|
||||
func posInsideSelection(m Model, col, line int) bool {
|
||||
win := m.ActiveWindow()
|
||||
|
||||
switch m.Mode() {
|
||||
func posInsideSelection(w *core.Window, mode core.Mode, col, line int) bool {
|
||||
switch mode {
|
||||
case core.VisualLineMode:
|
||||
startY := min(win.Anchor.Line, win.Cursor.Line)
|
||||
endY := max(win.Anchor.Line, win.Cursor.Line)
|
||||
startY := min(w.Anchor.Line, w.Cursor.Line)
|
||||
endY := max(w.Anchor.Line, w.Cursor.Line)
|
||||
return line >= startY && line <= endY
|
||||
|
||||
case core.VisualMode:
|
||||
ax := win.Anchor.Col
|
||||
ay := win.Anchor.Line
|
||||
ax := w.Anchor.Col
|
||||
ay := w.Anchor.Line
|
||||
|
||||
cx := win.Cursor.Col
|
||||
cy := win.Cursor.Line
|
||||
cx := w.Cursor.Col
|
||||
cy := w.Cursor.Line
|
||||
|
||||
// Normalize so start is always before end in document order
|
||||
var startX, startY, endX, endY int
|
||||
@ -41,10 +291,10 @@ func posInsideSelection(m Model, col, line int) bool {
|
||||
return afterStart && beforeEnd
|
||||
|
||||
case core.VisualBlockMode:
|
||||
startX := min(win.Anchor.Col, win.Cursor.Col)
|
||||
startY := min(win.Anchor.Line, win.Cursor.Line)
|
||||
endX := max(win.Anchor.Col, win.Cursor.Col)
|
||||
endY := max(win.Anchor.Line, win.Cursor.Line)
|
||||
startX := min(w.Anchor.Col, w.Cursor.Col)
|
||||
startY := min(w.Anchor.Line, w.Cursor.Line)
|
||||
endX := max(w.Anchor.Col, w.Cursor.Col)
|
||||
endY := max(w.Anchor.Line, w.Cursor.Line)
|
||||
|
||||
return col >= startX && col <= endX &&
|
||||
line >= startY && line <= endY
|
||||
@ -53,183 +303,3 @@ func posInsideSelection(m Model, col, line int) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// posIsAnchor: Returns true if the given position matches the anchor position
|
||||
// used for visual mode debugging/rendering.
|
||||
func posIsAnchor(m Model, col, line int) bool {
|
||||
win := m.ActiveWindow()
|
||||
ax := win.Anchor.Col
|
||||
ay := win.Anchor.Line
|
||||
return col == ax && line == ay
|
||||
}
|
||||
|
||||
// Model.View: Renders the complete editor view including buffer content, line
|
||||
// numbers, status bar, and command line.
|
||||
func (m Model) View() string {
|
||||
win := m.ActiveWindow()
|
||||
buf := m.ActiveBuffer()
|
||||
|
||||
var view strings.Builder
|
||||
|
||||
viewportHeight := win.Height - 2
|
||||
start := win.ScrollY
|
||||
end := win.ScrollY + viewportHeight
|
||||
|
||||
for i := start; i < end; i++ {
|
||||
|
||||
if i < buf.LineCount() {
|
||||
|
||||
if m.Settings().Number || m.Settings().RelativeNumber {
|
||||
var (
|
||||
gutter string
|
||||
currentLine bool = false
|
||||
lineNumber int
|
||||
)
|
||||
|
||||
if m.Settings().RelativeNumber {
|
||||
// Relative line numbers: show distance from cursor, current line shows absolute
|
||||
if i > win.Cursor.Line {
|
||||
lineNumber = i - win.Cursor.Line
|
||||
gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber)
|
||||
} else if i < win.Cursor.Line {
|
||||
lineNumber = win.Cursor.Line - i
|
||||
gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber)
|
||||
} else {
|
||||
// Current line: show absolute number if Number is also set, otherwise show 0
|
||||
currentLine = true
|
||||
if m.Settings().Number {
|
||||
lineNumber = i + 1
|
||||
gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber)
|
||||
} else {
|
||||
gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, 0)
|
||||
}
|
||||
}
|
||||
} else if m.Settings().Number {
|
||||
// Absolute line numbers only
|
||||
lineNumber = i + 1
|
||||
currentLine = (i == win.Cursor.Line)
|
||||
gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber)
|
||||
}
|
||||
if currentLine {
|
||||
view.WriteString(m.Styles().GutterCurrentLine.Render(gutter))
|
||||
} else {
|
||||
view.WriteString(m.Styles().Gutter.Render(gutter))
|
||||
}
|
||||
}
|
||||
|
||||
runes := []rune(buf.Lines[i])
|
||||
for x := 0; x <= len(runes); x++ {
|
||||
if win.Cursor.Line == i && win.Cursor.Col == x {
|
||||
if x < len(runes) {
|
||||
view.WriteString(m.Styles().CursorStyle(m.Mode()).Render(string(runes[x])))
|
||||
} else {
|
||||
view.WriteString(m.Styles().CursorStyle(m.Mode()).Render(" "))
|
||||
}
|
||||
} else if x < len(runes) {
|
||||
if m.Mode().IsVisualMode() && posIsAnchor(m, x, i) {
|
||||
view.WriteString(m.Styles().VisualAnchor.Render(string(runes[x])))
|
||||
} else if m.Mode().IsVisualMode() && posInsideSelection(m, x, i) {
|
||||
view.WriteString(m.Styles().VisualHighlight.Render(string(runes[x])))
|
||||
} else {
|
||||
view.WriteRune(runes[x])
|
||||
}
|
||||
// To highlight blank lines when in visual mode
|
||||
} else if m.Mode().IsVisualMode() && posInsideSelection(m, x, i) {
|
||||
view.WriteString(m.Styles().VisualHighlight.Render(" "))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Empty lines beyond file content
|
||||
if m.Settings().Number || m.Settings().RelativeNumber {
|
||||
format := fmt.Sprintf("%%-%ds ", m.Settings().GutterSize-1)
|
||||
fmt.Fprintf(&view, format, "~")
|
||||
} else {
|
||||
view.WriteString("~")
|
||||
}
|
||||
}
|
||||
|
||||
view.WriteString("\n")
|
||||
}
|
||||
|
||||
view.WriteString(drawStatusBar(m))
|
||||
view.WriteString("\n")
|
||||
view.WriteString(drawCommandBar(m))
|
||||
|
||||
return view.String()
|
||||
}
|
||||
|
||||
func viewWindow(w core.Window) string {
|
||||
var view string
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
// drawStatusBar: Renders the status bar with mode and cursor position,
|
||||
// padding the middle with spaces to fill the terminal width.
|
||||
func drawStatusBar(m Model) string {
|
||||
left := leftBar(m)
|
||||
right := rightBar(m)
|
||||
|
||||
diff := m.termWidth - (len(left) + len(right))
|
||||
|
||||
// This happens when the terminal spawns
|
||||
if diff <= 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
middle := strings.Repeat(" ", diff)
|
||||
return left + middle + right
|
||||
}
|
||||
|
||||
// leftBar: Returns the left side of the status bar showing the current mode.
|
||||
func leftBar(m Model) string {
|
||||
return fmt.Sprintf(" %s", m.Mode().ToString())
|
||||
}
|
||||
|
||||
// rightBar: Returns the right side of the status bar showing cursor position
|
||||
// and selection count in visual mode.
|
||||
func rightBar(m Model) (bar string) {
|
||||
win := m.ActiveWindow()
|
||||
|
||||
if m.Mode().IsVisualMode() {
|
||||
lineCount := max(win.Anchor.Line, win.Cursor.Line) - min(win.Anchor.Line, win.Cursor.Line) + 1
|
||||
bar = fmt.Sprintf("%d:%d <%d>", win.Cursor.Line, win.Cursor.Col, lineCount)
|
||||
} else {
|
||||
bar = fmt.Sprintf("%d:%d ", win.Cursor.Line, win.Cursor.Col)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// drawCommandBar: Renders the command line showing command input, errors, or
|
||||
// output depending on the current mode and state.
|
||||
func drawCommandBar(m Model) (bar string) {
|
||||
if m.Mode() == core.CommandMode {
|
||||
bar = ":"
|
||||
cmd := m.Command()
|
||||
cur := m.CommandCursor()
|
||||
for i := 0; i < len(cmd); i++ {
|
||||
if i == cur {
|
||||
bar += m.Styles().CursorStyle(m.Mode()).Render(string(cmd[i]))
|
||||
} else {
|
||||
bar += string(cmd[i])
|
||||
}
|
||||
}
|
||||
// Cursor at end of command
|
||||
if cur >= len(cmd) {
|
||||
bar += m.Styles().CursorStyle(m.Mode()).Render(" ")
|
||||
}
|
||||
// bar = fmt.Sprintf("%s %d", bar, cur)
|
||||
} else if m.CommandError() != nil {
|
||||
bar = m.Styles().CommandError.Render(m.CommandError().Error())
|
||||
} else if strings.TrimSpace(m.CommandOutput()) != "" {
|
||||
bar = m.CommandOutput()
|
||||
} else if strings.TrimSpace(m.Command()) != "" {
|
||||
bar = fmt.Sprintf(":%s", m.Command())
|
||||
} else if len(m.input.Pending()) > 0 {
|
||||
// Get width of window and padding
|
||||
rep := m.ActiveWindow().Width - 10 // 10 is padding
|
||||
bar = fmt.Sprintf("%s%s", strings.Repeat(" ", rep), m.input.Pending())
|
||||
}
|
||||
|
||||
return bar
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user