src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

render.go (2989B)


      1 package canvas
      2 
      3 import (
      4 	"fmt"
      5 	"path"
      6 
      7 	"github.com/mattn/go-runewidth"
      8 
      9 	"code.dwrz.net/src/pkg/color"
     10 	"code.dwrz.net/src/pkg/editor/buffer"
     11 	"code.dwrz.net/src/pkg/editor/prompt"
     12 	"code.dwrz.net/src/pkg/terminal"
     13 )
     14 
     15 func (c *Canvas) Render(b *buffer.Buffer, prompt *prompt.Prompt) error {
     16 	c.mu.Lock()
     17 	defer c.mu.Unlock()
     18 
     19 	size, err := c.terminal.Size()
     20 	if err != nil {
     21 		return fmt.Errorf("failed to get terminal size: %w", err)
     22 	}
     23 
     24 	var (
     25 		cursor = b.Cursor()
     26 		bars   = 2
     27 		height = int(size.Rows) - bars
     28 		width  = int(size.Columns)
     29 		output = b.Render(height, width)
     30 	)
     31 
     32 	// Move the cursor to the top left.
     33 	c.buf.WriteString(terminal.CursorHide)
     34 	c.buf.WriteString(terminal.CursorTopLeft)
     35 
     36 	// Print each line.
     37 	for _, line := range output.Lines {
     38 		c.buf.WriteString(terminal.EraseLine)
     39 		c.buf.WriteString(line)
     40 		c.buf.WriteString("\r\n")
     41 	}
     42 
     43 	// Draw the status bar.
     44 	c.statusBar(b, width, cursor.Line(), cursor.Glyph())
     45 
     46 	// Draw the message bar.
     47 	c.messageBar(prompt, width)
     48 
     49 	// Set the cursor.
     50 	if prompt.IsPrompt() {
     51 		// FIX: +1's.
     52 		c.buf.WriteString(fmt.Sprintf(
     53 			terminal.SetCursorFmt, size.Rows+1,
     54 			runewidth.StringWidth(prompt.Text())+prompt.Cursor()+2,
     55 		))
     56 	} else {
     57 		c.buf.WriteString(fmt.Sprintf(
     58 			terminal.SetCursorFmt, output.Line, output.Glyph,
     59 		))
     60 	}
     61 
     62 	c.buf.WriteString(terminal.CursorShow)
     63 
     64 	c.out.Write(c.buf.Bytes())
     65 
     66 	c.buf.Reset()
     67 
     68 	return nil
     69 }
     70 
     71 func (c *Canvas) statusBar(b *buffer.Buffer, width, y, x int) {
     72 	c.buf.WriteString(terminal.EraseLine)
     73 	c.buf.WriteString(color.Inverse)
     74 
     75 	// Icon
     76 	icon := "文 "
     77 	c.buf.WriteString(icon)
     78 	width -= runewidth.StringWidth(icon)
     79 
     80 	// Calculate the length of the cursor, so we can determine how much
     81 	// space we have left for the name of the buffer.
     82 	cursor := fmt.Sprintf(" %d:%d", y, x)
     83 	width -= runewidth.StringWidth(cursor)
     84 
     85 	// Filename.
     86 	// TODO: handle long filenames (shorten filepath).
     87 	name := path.Base(b.Name())
     88 	nw := runewidth.StringWidth(name)
     89 	if nw <= width {
     90 		c.buf.WriteString(name)
     91 		width -= nw
     92 	} else {
     93 		for _, r := range name {
     94 			rw := runewidth.RuneWidth(r)
     95 			if width-rw >= 0 {
     96 				c.buf.WriteRune(r)
     97 				width -= rw
     98 			} else {
     99 				break
    100 			}
    101 		}
    102 	}
    103 
    104 	// Add empty spaces to the end of the line.
    105 	for i := width; i >= 0; i-- {
    106 		c.buf.WriteRune(' ')
    107 	}
    108 
    109 	// Cursor
    110 	c.buf.WriteString(cursor)
    111 
    112 	c.buf.WriteString(color.Reset)
    113 	c.buf.WriteString("\r\n")
    114 }
    115 
    116 // TODO: handle messages that are too long for one line.
    117 func (c *Canvas) messageBar(p *prompt.Prompt, width int) {
    118 	c.buf.WriteString(terminal.EraseLine)
    119 
    120 	var (
    121 		text = p.Text()
    122 		line = p.Line(0, width) // TODO: handle offset.
    123 	)
    124 	switch {
    125 	case text == "" && line == "":
    126 		c.buf.WriteString(fmt.Sprintf("%*s", width, " "))
    127 
    128 	// TODO: handle text + line; offset.
    129 	case len(text) > width:
    130 		c.buf.WriteString(fmt.Sprintf("%*s", width, text))
    131 
    132 	default:
    133 		output := fmt.Sprintf("%s %s", text, line)
    134 
    135 		c.buf.WriteString(fmt.Sprintf(
    136 			"%s %*s",
    137 			output, width-len(output)-1, " ", // FIX -1
    138 		))
    139 	}
    140 }