All checks were successful
Run Test Suite / test (push) Successful in 15s
Also adjusted some of the IO tests for writing and force writing.
121 lines
3.0 KiB
Go
121 lines
3.0 KiB
Go
package program
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
|
"git.gophernest.net/azpect/TextEditor/internal/editor"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
)
|
|
|
|
type ProgramBuilder struct {
|
|
model *editor.Model
|
|
opts []tea.ProgramOption
|
|
}
|
|
|
|
func NewProgramBuilder() *ProgramBuilder {
|
|
return &ProgramBuilder{
|
|
model: &editor.Model{},
|
|
opts: []tea.ProgramOption{},
|
|
}
|
|
}
|
|
|
|
// ProgramBuilder.FileProgram: Sets the internal state of the builder to the required
|
|
// state to start the program (editor) with a filename. This is what will happen when
|
|
// a user runs 'gim <filename>'.
|
|
func (p *ProgramBuilder) FileProgram(filename string) *ProgramBuilder {
|
|
// Only difference: open the file
|
|
ext := filepath.Ext(filename)
|
|
|
|
file, err := os.Open(filename)
|
|
notFound := errors.Is(err, os.ErrNotExist)
|
|
if err != nil && !notFound {
|
|
// TODO: Handle this
|
|
panic(fmt.Errorf("Failed to find file: %w", err))
|
|
}
|
|
if file != nil {
|
|
defer file.Close()
|
|
}
|
|
|
|
buf := core.NewBufferBuilder().
|
|
WithType(core.FileBuffer).
|
|
WithFilename(filename).
|
|
WithFiletype(ext).
|
|
Build()
|
|
|
|
win := core.NewWindowBuilder().
|
|
WithBuffer(&buf).
|
|
WithOptions(core.NewDefaultWinOptions()).
|
|
Build()
|
|
|
|
p.model = editor.NewModelBuilder().
|
|
AddBuffer(&buf).
|
|
AddWindow(&win).
|
|
WithActiveWindowId(win.Id).
|
|
Build()
|
|
|
|
// If we did not find the file, all we need to do is set the filename and type
|
|
if notFound {
|
|
return p
|
|
}
|
|
|
|
// Otherwise we have to create everything, then read the file (since we need settings)
|
|
// COPIED FROM `internal/command/handlers.go`
|
|
var lines []string
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
line = strings.TrimSuffix(line, "\r")
|
|
|
|
// BUG: This is bad, we don't want to this, but we have to
|
|
cleaned := strings.ReplaceAll(line, "\t", strings.Repeat(" ", p.model.Settings().TabStop))
|
|
lines = append(lines, cleaned)
|
|
}
|
|
|
|
// Only setting lines if we found some
|
|
if len(lines) > 0 {
|
|
p.model.ActiveBuffer().SetLines(lines)
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
// ProgramBuilder.EmptyProgram: Sets the internal state of the builder to the required
|
|
// state to start the program (editor) in the current directory. This is what will
|
|
// happen when a user runs 'gim'.
|
|
func (p *ProgramBuilder) EmptyProgram() *ProgramBuilder {
|
|
buf := core.NewBufferBuilder().
|
|
ReadOnly().
|
|
Build()
|
|
|
|
win := core.NewWindowBuilder().
|
|
WithBuffer(&buf).
|
|
WithOptions(core.NewDefaultWinOptions()).
|
|
Build()
|
|
|
|
p.model = editor.NewModelBuilder().
|
|
AddBuffer(&buf).
|
|
AddWindow(&win).
|
|
WithActiveWindowId(win.Id).
|
|
Build()
|
|
|
|
return p
|
|
}
|
|
|
|
// ProgramBuilder.WithOpt: Add an option to the list of options that will be used when
|
|
// the program is built.
|
|
func (p *ProgramBuilder) WithOpt(opt tea.ProgramOption) *ProgramBuilder {
|
|
p.opts = append(p.opts, opt)
|
|
return p
|
|
}
|
|
|
|
// ProgramBuilder.Build: Build and return the configured tea.Program instance.
|
|
func (p *ProgramBuilder) Build() *tea.Program {
|
|
return tea.NewProgram(p.model, p.opts...)
|
|
}
|