120 lines
2.3 KiB
Go
120 lines
2.3 KiB
Go
package editor
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"log/slog"
|
||
|
|
"runtime/debug"
|
||
|
|
|
||
|
|
"code.chimeric.al/dwrz/wen/internal/editor/event"
|
||
|
|
)
|
||
|
|
|
||
|
|
func (e *Editor) Run(ctx context.Context, files []string) error {
|
||
|
|
ctx, cancel := context.WithCancel(ctx)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
if err := e.terminal.SetRaw(); err != nil {
|
||
|
|
return fmt.Errorf("failed to set raw mode: %w", err)
|
||
|
|
}
|
||
|
|
e.canvas.Reset()
|
||
|
|
|
||
|
|
// Log build info.
|
||
|
|
var commit, time string
|
||
|
|
bi, ok := debug.ReadBuildInfo()
|
||
|
|
if ok {
|
||
|
|
for _, s := range bi.Settings {
|
||
|
|
switch s.Key {
|
||
|
|
case "vcs.revision":
|
||
|
|
commit = s.Value
|
||
|
|
case "vcs.time":
|
||
|
|
time = s.Value
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
e.log.Debug(
|
||
|
|
"文 version",
|
||
|
|
slog.String("commit", commit),
|
||
|
|
slog.String("time", time),
|
||
|
|
)
|
||
|
|
|
||
|
|
// Reset the terminal before exiting.
|
||
|
|
defer func() {
|
||
|
|
e.canvas.Reset()
|
||
|
|
|
||
|
|
if err := e.terminal.Reset(); err != nil {
|
||
|
|
e.log.Error(
|
||
|
|
"failed to reset terminal attributes",
|
||
|
|
slog.Any("error", err),
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
// Start reading user input.
|
||
|
|
// The reader may be blocked in a read when the editor ends.
|
||
|
|
// The reader stops when the process stops.
|
||
|
|
go func() {
|
||
|
|
if err := e.reader.Run(ctx); err != nil && !errors.Is(err, context.Canceled) {
|
||
|
|
e.events <- event.NewError(err)
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
// Open the files and render the initial state.
|
||
|
|
e.load(files)
|
||
|
|
if err := e.canvas.Render(e.active, e.prompt); err != nil {
|
||
|
|
return fmt.Errorf("failed to render: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Main loop.
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case <-ctx.Done():
|
||
|
|
e.log.DebugContext(ctx, "context done: stopping")
|
||
|
|
return nil
|
||
|
|
|
||
|
|
case ev := <-e.events:
|
||
|
|
switch t := ev.(type) {
|
||
|
|
case *event.Error:
|
||
|
|
return t
|
||
|
|
|
||
|
|
case *event.Message:
|
||
|
|
e.prompt.SetText(t.Text())
|
||
|
|
|
||
|
|
if err := e.canvas.Render(
|
||
|
|
e.active, e.prompt,
|
||
|
|
); err != nil {
|
||
|
|
return fmt.Errorf(
|
||
|
|
"failed to render: %w", err,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
case *event.Quit:
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
case event := <-e.input:
|
||
|
|
if e.prompt.IsPrompt() {
|
||
|
|
if err := e.promptInput(event); err != nil {
|
||
|
|
return fmt.Errorf(
|
||
|
|
"failed to process input: %w",
|
||
|
|
err,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if err := e.bufferInput(event); err != nil {
|
||
|
|
return fmt.Errorf(
|
||
|
|
"failed to process input: %w",
|
||
|
|
err,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := e.canvas.Render(
|
||
|
|
e.active, e.prompt,
|
||
|
|
); err != nil {
|
||
|
|
return fmt.Errorf("failed to render: %w", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|