feat: implemented buffer commands! All of them are tested
This commit is contained in:
parent
10e37b82af
commit
d5ad3dcdcd
@ -243,7 +243,7 @@ func cmdEdit(m action.Model, args []string, force bool) tea.Cmd {
|
|||||||
// Register Commands
|
// Register Commands
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
// cmdRegisters: Handles :register command (debug - displays register content).
|
// cmdRegisters: Handles :register command
|
||||||
func cmdRegisters(m action.Model, args []string, force bool) tea.Cmd {
|
func cmdRegisters(m action.Model, args []string, force bool) tea.Cmd {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
regs := m.Registers()
|
regs := m.Registers()
|
||||||
@ -307,6 +307,365 @@ func cmdRegisters(m action.Model, args []string, force bool) tea.Cmd {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
// Buffer Commands
|
||||||
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
// Switching
|
||||||
|
// - :b <n> — switch to buffer number n
|
||||||
|
// - :b <name> — switch by partial filename match
|
||||||
|
// - :bn — next buffer (wraps)
|
||||||
|
// - :bp — previous buffer (wraps, currently panics on wrap-back)
|
||||||
|
// - :bf — first buffer
|
||||||
|
// - :bl — last buffer
|
||||||
|
// Opening / closing
|
||||||
|
// - :e <file> — open file into new buffer
|
||||||
|
// - :bd — delete (unload) current buffer
|
||||||
|
// - :bd <n> — delete buffer n
|
||||||
|
// - :bw — wipe buffer completely
|
||||||
|
|
||||||
|
// cmdListBuffers: Handles :buffers & :ls command. Lists the active buffers.
|
||||||
|
func cmdListBuffers(m action.Model, args []string, force bool) tea.Cmd {
|
||||||
|
_, _ = args, force
|
||||||
|
|
||||||
|
// What we should display
|
||||||
|
// ------------------------------
|
||||||
|
// - % — current buffer
|
||||||
|
// - # — alternate buffer (last active)
|
||||||
|
// - a — active (loaded and visible)
|
||||||
|
// - h — hidden (loaded but not visible)
|
||||||
|
// - + — modified (unsaved changes)
|
||||||
|
// - - — not modifiable
|
||||||
|
|
||||||
|
curBuf := m.ActiveBuffer()
|
||||||
|
bufs := m.Buffers()
|
||||||
|
var lines []string
|
||||||
|
for _, buf := range bufs {
|
||||||
|
// Skip unlisted buffers
|
||||||
|
if !buf.Listed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var flags strings.Builder
|
||||||
|
if buf.Id == curBuf.Id {
|
||||||
|
flags.WriteRune('%')
|
||||||
|
} else {
|
||||||
|
flags.WriteRune(' ')
|
||||||
|
}
|
||||||
|
// TODO: Implement alternate buffer
|
||||||
|
|
||||||
|
// Cannot really display the a and h, since we don't have visible flags yet
|
||||||
|
// For now, we will have a loaded flag, 'l'
|
||||||
|
if buf.Loaded {
|
||||||
|
flags.WriteRune('l')
|
||||||
|
}
|
||||||
|
flags.WriteRune(' ')
|
||||||
|
if buf.Modified {
|
||||||
|
flags.WriteRune('+')
|
||||||
|
}
|
||||||
|
if buf.ReadOnly {
|
||||||
|
flags.WriteRune('-')
|
||||||
|
}
|
||||||
|
|
||||||
|
line := fmt.Sprintf("%3d %s \"%s\"", buf.Id, flags.String(), buf.Filename)
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SetMode(core.CommandOutputMode)
|
||||||
|
m.SetCommandOutput(&core.CommandOutput{
|
||||||
|
Title: ":buffers",
|
||||||
|
Lines: lines,
|
||||||
|
Inline: false,
|
||||||
|
IsError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cmdNextBuffer: Handles :bn command. Moves to the next buffer based on ID.
|
||||||
|
func cmdNextBuffer(m action.Model, args []string, force bool) tea.Cmd {
|
||||||
|
_, _ = args, force
|
||||||
|
|
||||||
|
bufs := m.Buffers()
|
||||||
|
curBuf := m.ActiveBuffer()
|
||||||
|
|
||||||
|
ids := make([]int, len(bufs))
|
||||||
|
var curIndex int
|
||||||
|
for i, buf := range bufs {
|
||||||
|
if buf.Listed {
|
||||||
|
ids[i] = buf.Id
|
||||||
|
if buf.Id == curBuf.Id {
|
||||||
|
curIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextId := (curIndex + 1) % len(ids)
|
||||||
|
|
||||||
|
m.ActiveWindow().SetBuffer(bufs[nextId])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cmdPrevBuffer: Handles :bp command. Moves to the previous buffer based on ID.
|
||||||
|
func cmdPrevBuffer(m action.Model, args []string, force bool) tea.Cmd {
|
||||||
|
_, _ = args, force
|
||||||
|
|
||||||
|
bufs := m.Buffers()
|
||||||
|
curBuf := m.ActiveBuffer()
|
||||||
|
|
||||||
|
ids := make([]int, len(bufs))
|
||||||
|
var curIndex int
|
||||||
|
for i, buf := range bufs {
|
||||||
|
if buf.Listed {
|
||||||
|
ids[i] = buf.Id
|
||||||
|
if buf.Id == curBuf.Id {
|
||||||
|
curIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prevId := ((curIndex - 1) + len(ids)) % len(ids)
|
||||||
|
|
||||||
|
m.ActiveWindow().SetBuffer(bufs[prevId])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cmdFirstBuffer: Handles :bf command. Moves to the first buffer based on ID.
|
||||||
|
func cmdFirstBuffer(m action.Model, args []string, force bool) tea.Cmd {
|
||||||
|
_, _ = args, force
|
||||||
|
|
||||||
|
bufs := m.Buffers()
|
||||||
|
|
||||||
|
ids := make([]int, len(bufs))
|
||||||
|
for i, buf := range bufs {
|
||||||
|
if buf.Listed {
|
||||||
|
ids[i] = buf.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.ActiveWindow().SetBuffer(bufs[0])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cmdLastBuffer: Handles :bf command. Moves to the last buffer based on ID.
|
||||||
|
func cmdLastBuffer(m action.Model, args []string, force bool) tea.Cmd {
|
||||||
|
_, _ = args, force
|
||||||
|
|
||||||
|
bufs := m.Buffers()
|
||||||
|
|
||||||
|
ids := make([]int, len(bufs))
|
||||||
|
for i, buf := range bufs {
|
||||||
|
if buf.Listed {
|
||||||
|
ids[i] = buf.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.ActiveWindow().SetBuffer(bufs[len(bufs)-1])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cmdSelectBuffer: Handles :b command. Moves to the selected buffer based on ID or filename.
|
||||||
|
func cmdSelectBuffer(m action.Model, args []string, force bool) tea.Cmd {
|
||||||
|
_ = force
|
||||||
|
|
||||||
|
// Cannot function without args
|
||||||
|
if len(args) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 1 {
|
||||||
|
m.SetCommandOutput(&core.CommandOutput{
|
||||||
|
Lines: []string{fmt.Sprintf("Trailing characters: %s", strings.Join(args[1:], " "))},
|
||||||
|
Inline: true,
|
||||||
|
IsError: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bufs := m.Buffers()
|
||||||
|
|
||||||
|
// If we can parse the input as number, try an ID
|
||||||
|
tgtId, err := strconv.Atoi(args[0])
|
||||||
|
if err == nil {
|
||||||
|
for i, buf := range bufs {
|
||||||
|
if buf.Id == tgtId && buf.Listed {
|
||||||
|
m.ActiveWindow().SetBuffer(bufs[i])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SetCommandOutput(&core.CommandOutput{
|
||||||
|
Lines: []string{fmt.Sprintf("Buffer id %d does not exist", tgtId)},
|
||||||
|
Inline: true,
|
||||||
|
IsError: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, try to match using filename
|
||||||
|
query := args[0]
|
||||||
|
var matches []int
|
||||||
|
for i, buf := range bufs {
|
||||||
|
if strings.Contains(buf.Filename, query) && buf.Listed {
|
||||||
|
matches = append(matches, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(matches) == 0 {
|
||||||
|
m.SetCommandOutput(&core.CommandOutput{
|
||||||
|
Lines: []string{fmt.Sprintf("No matches for for %s", query)},
|
||||||
|
Inline: true,
|
||||||
|
IsError: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(matches) > 1 {
|
||||||
|
m.SetCommandOutput(&core.CommandOutput{
|
||||||
|
Lines: []string{fmt.Sprintf("More than one match for %s", query)},
|
||||||
|
Inline: true,
|
||||||
|
IsError: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m.ActiveWindow().SetBuffer(bufs[matches[0]])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cmdDeleteBuffer: Handles :bd command. Deletes (unloads) a buffer.
|
||||||
|
func cmdDeleteBuffer(m action.Model, args []string, force bool) tea.Cmd {
|
||||||
|
// This will be as dynamic as possible, just get a list of indexes, then unlist them all
|
||||||
|
var indexes []int
|
||||||
|
bufs := m.Buffers()
|
||||||
|
|
||||||
|
// If the deleted buffer was the active one, Vim switches to the most recent entry in the jump
|
||||||
|
// list that points into a loaded buffer. This is not simply "the previous buffer" — it's
|
||||||
|
// jump-list-based, so it could be any recently visited loaded buffer.
|
||||||
|
// THOUGH: I am not building vim, so it does not have to be the same
|
||||||
|
|
||||||
|
// Need to close any windows associated with the closed buffers. Once many windows are implemented.
|
||||||
|
|
||||||
|
// No args, unlist current buffer
|
||||||
|
if len(args) == 0 {
|
||||||
|
curBuf := m.ActiveBuffer()
|
||||||
|
|
||||||
|
if curBuf.Modified && !force {
|
||||||
|
m.SetCommandOutput(&core.CommandOutput{
|
||||||
|
Lines: []string{fmt.Sprintf("No write since last change to buffer %d (Add ! to continue)", curBuf.Id)},
|
||||||
|
Inline: true,
|
||||||
|
IsError: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, buf := range bufs {
|
||||||
|
if buf.Id == curBuf.Id {
|
||||||
|
indexes = append(indexes, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
|
||||||
|
// Arg can be ID or name
|
||||||
|
ArgumentList:
|
||||||
|
for _, arg := range args {
|
||||||
|
// Try to get ID, if we can, move until we find it
|
||||||
|
id, err := strconv.Atoi(arg)
|
||||||
|
if err == nil {
|
||||||
|
for index, buf := range bufs {
|
||||||
|
if buf.Id == id && buf.Listed {
|
||||||
|
if buf.Modified && !force {
|
||||||
|
m.SetCommandOutput(&core.CommandOutput{
|
||||||
|
Lines: []string{fmt.Sprintf("No write since last change to buffer %d (Add ! to continue)", buf.Id)},
|
||||||
|
Inline: true,
|
||||||
|
IsError: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
indexes = append(indexes, index)
|
||||||
|
continue ArgumentList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue ArgumentList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed to parse, fuzzy match on names
|
||||||
|
var matches []int
|
||||||
|
for index, buf := range bufs {
|
||||||
|
if strings.Contains(buf.Filename, arg) && buf.Listed {
|
||||||
|
matches = append(matches, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(matches) > 1 {
|
||||||
|
m.SetCommandOutput(&core.CommandOutput{
|
||||||
|
Lines: []string{fmt.Sprintf("More than one match for %s", arg)},
|
||||||
|
Inline: true,
|
||||||
|
IsError: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(matches) > 0 {
|
||||||
|
|
||||||
|
if bufs[matches[0]].Modified && !force {
|
||||||
|
m.SetCommandOutput(&core.CommandOutput{
|
||||||
|
Lines: []string{fmt.Sprintf(
|
||||||
|
"No write since last change to buffer %d (Add ! to continue)",
|
||||||
|
bufs[matches[0]].Id),
|
||||||
|
},
|
||||||
|
Inline: true,
|
||||||
|
IsError: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
indexes = append(indexes, matches[0])
|
||||||
|
continue ArgumentList
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SetCommandOutput(&core.CommandOutput{
|
||||||
|
Lines: []string{fmt.Sprintf("No matching buffer for %s", arg)},
|
||||||
|
Inline: true,
|
||||||
|
IsError: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple error output
|
||||||
|
if len(indexes) == 0 {
|
||||||
|
m.SetCommandOutput(&core.CommandOutput{
|
||||||
|
Lines: []string{"No buffers were deleted"},
|
||||||
|
Inline: true,
|
||||||
|
IsError: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we can delete the buffers
|
||||||
|
for _, i := range indexes {
|
||||||
|
bufs[i].SetListed(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to first listed buffer
|
||||||
|
// TODO: Switch to alternate buffer if available, once implemented
|
||||||
|
if !m.ActiveBuffer().Listed {
|
||||||
|
for _, buf := range bufs {
|
||||||
|
if buf.Listed {
|
||||||
|
m.ActiveWindow().SetBuffer(buf)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
// Settings Commands
|
// Settings Commands
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -10,8 +10,8 @@ import (
|
|||||||
|
|
||||||
// Command: Represents a command that can be executed from command mode.
|
// Command: Represents a command that can be executed from command mode.
|
||||||
type Command struct {
|
type Command struct {
|
||||||
Name string // Full name: "quit"
|
Name string // Full name: "quit"
|
||||||
ShortForm string // Minimum abbreviation: "q"
|
ShortForm string // Minimum abbreviation: "q"
|
||||||
Handler func(m action.Model, args []string, force bool) tea.Cmd // Handler function
|
Handler func(m action.Model, args []string, force bool) tea.Cmd // Handler function
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,6 +162,55 @@ func (r *Registry) registerDefaults() {
|
|||||||
Handler: cmdRegisters,
|
Handler: cmdRegisters,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Buffer commands
|
||||||
|
r.Register(Command{
|
||||||
|
Name: "buffers",
|
||||||
|
ShortForm: "buffers",
|
||||||
|
Handler: cmdListBuffers,
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Register(Command{
|
||||||
|
Name: "ls",
|
||||||
|
ShortForm: "ls",
|
||||||
|
Handler: cmdListBuffers,
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Register(Command{
|
||||||
|
Name: "bn",
|
||||||
|
ShortForm: "bn",
|
||||||
|
Handler: cmdNextBuffer,
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Register(Command{
|
||||||
|
Name: "bp",
|
||||||
|
ShortForm: "bp",
|
||||||
|
Handler: cmdPrevBuffer,
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Register(Command{
|
||||||
|
Name: "bf",
|
||||||
|
ShortForm: "bf",
|
||||||
|
Handler: cmdFirstBuffer,
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Register(Command{
|
||||||
|
Name: "bl",
|
||||||
|
ShortForm: "bl",
|
||||||
|
Handler: cmdLastBuffer,
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Register(Command{
|
||||||
|
Name: "b",
|
||||||
|
ShortForm: "b",
|
||||||
|
Handler: cmdSelectBuffer,
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Register(Command{
|
||||||
|
Name: "bdelete",
|
||||||
|
ShortForm: "bd",
|
||||||
|
Handler: cmdDeleteBuffer,
|
||||||
|
})
|
||||||
|
|
||||||
// File commands
|
// File commands
|
||||||
r.Register(Command{
|
r.Register(Command{
|
||||||
Name: "edit",
|
Name: "edit",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user