148 lines
3.0 KiB
Go
148 lines
3.0 KiB
Go
package canvas
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"path"
|
||
|
|
|
||
|
|
"github.com/mattn/go-runewidth"
|
||
|
|
|
||
|
|
"code.chimeric.al/dwrz/wen/internal/editor/buffer"
|
||
|
|
"code.chimeric.al/dwrz/wen/internal/editor/prompt"
|
||
|
|
"code.chimeric.al/dwrz/wen/pkg/color"
|
||
|
|
"code.chimeric.al/dwrz/wen/pkg/terminal"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Bars is the number of terminal rows reserved at the bottom of the screen
|
||
|
|
// for the status bar and message bar.
|
||
|
|
const Bars = 2
|
||
|
|
|
||
|
|
func (c *Canvas) Render(b *buffer.Buffer, prompt *prompt.Prompt) error {
|
||
|
|
c.mu.Lock()
|
||
|
|
defer c.mu.Unlock()
|
||
|
|
|
||
|
|
size, err := c.terminal.Size()
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("failed to get terminal size: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
var (
|
||
|
|
cursor = b.Cursor()
|
||
|
|
height = int(size.Rows) - Bars
|
||
|
|
width = int(size.Columns)
|
||
|
|
output = b.Render(height, width)
|
||
|
|
)
|
||
|
|
|
||
|
|
// Move the cursor to the top left.
|
||
|
|
c.buf.WriteString(terminal.CursorHide)
|
||
|
|
c.buf.WriteString(terminal.CursorTopLeft)
|
||
|
|
|
||
|
|
// Print each line.
|
||
|
|
for _, line := range output.Lines {
|
||
|
|
c.buf.WriteString(terminal.EraseLine)
|
||
|
|
c.buf.WriteString(line)
|
||
|
|
c.buf.WriteString("\r\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Draw the status bar.
|
||
|
|
c.statusBar(b, width, cursor.Line(), cursor.Glyph())
|
||
|
|
|
||
|
|
// Draw the message bar.
|
||
|
|
c.messageBar(prompt, width)
|
||
|
|
|
||
|
|
// Set the cursor.
|
||
|
|
if prompt.IsPrompt() {
|
||
|
|
c.buf.WriteString(fmt.Sprintf(
|
||
|
|
terminal.SetCursorFmt, size.Rows,
|
||
|
|
runewidth.StringWidth(prompt.Text())+prompt.Cursor()+2,
|
||
|
|
))
|
||
|
|
} else {
|
||
|
|
c.buf.WriteString(fmt.Sprintf(
|
||
|
|
terminal.SetCursorFmt, output.Line, output.Glyph,
|
||
|
|
))
|
||
|
|
}
|
||
|
|
|
||
|
|
c.buf.WriteString(terminal.CursorShow)
|
||
|
|
|
||
|
|
c.out.Write(c.buf.Bytes())
|
||
|
|
|
||
|
|
c.buf.Reset()
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (c *Canvas) statusBar(b *buffer.Buffer, width, y, x int) {
|
||
|
|
c.buf.WriteString(terminal.EraseLine)
|
||
|
|
c.buf.WriteString(color.Inverse)
|
||
|
|
|
||
|
|
// Icon
|
||
|
|
icon := "文 "
|
||
|
|
c.buf.WriteString(icon)
|
||
|
|
width -= runewidth.StringWidth(icon)
|
||
|
|
|
||
|
|
// Calculate the length of the cursor, so we can determine how much
|
||
|
|
// space we have left for the name of the buffer.
|
||
|
|
cursor := fmt.Sprintf(" %d:%d", y, x)
|
||
|
|
width -= runewidth.StringWidth(cursor)
|
||
|
|
|
||
|
|
// Filename.
|
||
|
|
// TODO: handle long filenames (shorten filepath).
|
||
|
|
name := path.Base(b.Name())
|
||
|
|
nw := runewidth.StringWidth(name)
|
||
|
|
if nw <= width {
|
||
|
|
c.buf.WriteString(name)
|
||
|
|
width -= nw
|
||
|
|
} else {
|
||
|
|
for _, r := range name {
|
||
|
|
rw := runewidth.RuneWidth(r)
|
||
|
|
if width-rw >= 0 {
|
||
|
|
c.buf.WriteRune(r)
|
||
|
|
width -= rw
|
||
|
|
} else {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add empty spaces to the end of the line.
|
||
|
|
for i := width; i > 0; i-- {
|
||
|
|
c.buf.WriteRune(' ')
|
||
|
|
}
|
||
|
|
|
||
|
|
// Cursor
|
||
|
|
c.buf.WriteString(cursor)
|
||
|
|
|
||
|
|
c.buf.WriteString(color.Reset)
|
||
|
|
c.buf.WriteString("\r\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
// TODO: handle messages that are too long for one line.
|
||
|
|
func (c *Canvas) messageBar(p *prompt.Prompt, width int) {
|
||
|
|
c.buf.WriteString(terminal.EraseLine)
|
||
|
|
|
||
|
|
var (
|
||
|
|
text = p.Text()
|
||
|
|
line = p.Line(0, width) // TODO: handle offset.
|
||
|
|
output string
|
||
|
|
)
|
||
|
|
switch {
|
||
|
|
case text == "" && line == "":
|
||
|
|
output = ""
|
||
|
|
case line == "":
|
||
|
|
output = text
|
||
|
|
default:
|
||
|
|
output = fmt.Sprintf("%s %s", text, line)
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, r := range output {
|
||
|
|
rw := runewidth.RuneWidth(r)
|
||
|
|
if width-rw < 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
c.buf.WriteRune(r)
|
||
|
|
width -= rw
|
||
|
|
}
|
||
|
|
for i := width; i > 0; i-- {
|
||
|
|
c.buf.WriteRune(' ')
|
||
|
|
}
|
||
|
|
}
|