|
|
@@ -3,13 +3,72 @@ package editor
|
|
|
import (
|
|
|
"moose/internal/layout"
|
|
|
"moose/internal/buffer"
|
|
|
+ "moose/internal/util"
|
|
|
"fmt"
|
|
|
"strings"
|
|
|
+ "strconv"
|
|
|
"github.com/gdamore/tcell/v3/color"
|
|
|
)
|
|
|
|
|
|
func (m *Model) Draw() {
|
|
|
- m.DrawWorkspace(&m.LM.Workspaces[m.LM.ActiveIdx], layout.RectFromScren(m.Screen))
|
|
|
+ screen := layout.RectFromScren(m.Screen)
|
|
|
+ main := layout.Rect{
|
|
|
+ X: screen.X,
|
|
|
+ Y: screen.Y,
|
|
|
+ Width: screen.Width,
|
|
|
+ Height: screen.Height - 2,
|
|
|
+ }
|
|
|
+ palette := layout.Rect{
|
|
|
+ X: screen.X,
|
|
|
+ Y: main.Height,
|
|
|
+ Width: screen.Width,
|
|
|
+ Height: 2,
|
|
|
+ }
|
|
|
+
|
|
|
+ m.DrawWorkspace(&m.LM.Workspaces[m.LM.ActiveIdx], main)
|
|
|
+ m.DrawPalette(palette)
|
|
|
+}
|
|
|
+
|
|
|
+func (m *Model) DrawPalette(rect layout.Rect) {
|
|
|
+ for col := 0; col < rect.Width; col++ {
|
|
|
+ m.Screen.SetContent(rect.X + col, rect.Y, ' ', nil, m.Style.Background(color.Black))
|
|
|
+ }
|
|
|
+
|
|
|
+ modeStr := m.Mode.String()
|
|
|
+ if m.Mode == ModePalette {
|
|
|
+ if strings.HasPrefix(m.BM.PaletteBuffer.String(), "/") {
|
|
|
+ modeStr += " (command)"
|
|
|
+ } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "?") {
|
|
|
+ modeStr += " (find)"
|
|
|
+ } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "=") {
|
|
|
+ modeStr += " (replace)"
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ m.Screen.PutStrStyled(rect.Width - (len(modeStr) + 1), rect.Y, strings.ToUpper(modeStr), m.Style.Background(color.Black))
|
|
|
+
|
|
|
+ logStr := m.DebugLog + ", " + strings.Join(m.AM.CM.Recorded, "+") + ", " + strconv.FormatBool(m.AM.CM.Recording)
|
|
|
+ m.Screen.PutStrStyled(1, rect.Y, logStr, m.Style.Background(color.Black))
|
|
|
+
|
|
|
+ if m.Mode == ModePalette {
|
|
|
+ m.Screen.PutStrStyled(0, rect.Y + 1, m.BM.PaletteBuffer.String(), m.Style)
|
|
|
+
|
|
|
+ for _, cur := range m.BM.PaletteBuffer.CM.Cursors {
|
|
|
+ line, col := buffer.LineCol(&m.BM.PaletteBuffer, cur.Offset)
|
|
|
+ if line + rect.Y != rect.Y { continue }
|
|
|
+ if col + 1 > rect.Width { continue }
|
|
|
+
|
|
|
+ ch := buffer.RuneAt(&m.BM.PaletteBuffer, cur.Offset)
|
|
|
+
|
|
|
+ m.Screen.SetContent(col, rect.Y + 1, ch, nil, m.Style.Reverse(true))
|
|
|
+ }
|
|
|
+ } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.info:") {
|
|
|
+ m.Screen.PutStrStyled(0, rect.Y + 1, string([]rune(m.BM.PaletteBuffer.String())[11:]), m.Style.Foreground(color.LightGray))
|
|
|
+ } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.warn:") {
|
|
|
+ m.Screen.PutStrStyled(0, rect.Y + 1, string([]rune(m.BM.PaletteBuffer.String())[11:]), m.Style.Foreground(color.Orange))
|
|
|
+ } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.error:") {
|
|
|
+ m.Screen.PutStrStyled(0, rect.Y + 1, string([]rune(m.BM.PaletteBuffer.String())[12:]), m.Style.Foreground(color.Red))
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
func (m *Model) DrawWorkspace(w *layout.Workspace, rect layout.Rect) {
|
|
|
@@ -17,23 +76,61 @@ func (m *Model) DrawWorkspace(w *layout.Workspace, rect layout.Rect) {
|
|
|
}
|
|
|
|
|
|
func (m *Model) DrawContainer(c *layout.Container, rect layout.Rect) {
|
|
|
- length := len(c.Children)
|
|
|
+ length := util.NonNilLen(c.Children[:])
|
|
|
+ rect = layout.Rect{
|
|
|
+ X: rect.X,
|
|
|
+ Y: rect.Y + 1,
|
|
|
+ Width: rect.Width,
|
|
|
+ Height: rect.Height,
|
|
|
+ }
|
|
|
rect = layout.RectDivide(rect, c.Split, length)
|
|
|
|
|
|
for i, child := range c.Children {
|
|
|
+ main := layout.RectDisplace(rect, c.Split, i)
|
|
|
switch child.(type) {
|
|
|
case layout.ContainerBuffers: {
|
|
|
cb := child.(layout.ContainerBuffers)
|
|
|
- m.DrawContainerBuffers(&cb, layout.RectDisplace(rect, c.Split, i))
|
|
|
+ tabs := layout.Rect{
|
|
|
+ X: main.X,
|
|
|
+ Y: main.Y - 1,
|
|
|
+ Width: main.Width,
|
|
|
+ Height: 1,
|
|
|
+ }
|
|
|
+
|
|
|
+ m.DrawContainerTabs(&cb, tabs)
|
|
|
+ m.DrawContainerBuffers(&cb, main)
|
|
|
}
|
|
|
case layout.Container: {
|
|
|
c := child.(layout.Container)
|
|
|
- m.DrawContainer(&c, layout.RectDisplace(rect, c.Split, i))
|
|
|
+ m.DrawContainer(&c, main)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+func (m *Model) DrawContainerTabs(c *layout.ContainerBuffers, rect layout.Rect) {
|
|
|
+ tabs := []string{}
|
|
|
+ for _, bufIdx := range c.Buffers {
|
|
|
+ buf := m.BM.Buffers[bufIdx]
|
|
|
+ if buf.Path == "" {
|
|
|
+ tabs = append(tabs, "Buffer " + strconv.Itoa(bufIdx))
|
|
|
+ } else {
|
|
|
+ tabs = append(tabs, buf.Path)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ length := 0
|
|
|
+ for _, tab := range tabs {
|
|
|
+ if length + len(tab) + 2 > rect.Width {
|
|
|
+ m.Screen.PutStrStyled(rect.X + length, rect.Y, ">", m.Style.Foreground(color.White))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ m.Screen.PutStrStyled(rect.X + length, rect.Y, tab, m.Style.Background(color.Black).Foreground(color.White))
|
|
|
+ length += len(tab) + 1
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func (m *Model) DrawContainerBuffers(c *layout.ContainerBuffers, rect layout.Rect) {
|
|
|
m.DrawBuffer(&m.BM.Buffers[c.Buffers[c.ActiveIdx]], c.Buffers[c.ActiveIdx] == m.BM.CurrentIdx, rect)
|
|
|
}
|
|
|
@@ -81,4 +178,39 @@ func (m *Model) DrawBuffer(buf *buffer.Buffer, isActive bool, rect layout.Rect)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
+}
|
|
|
+
|
|
|
+const tabWidth = 4
|
|
|
+
|
|
|
+func expandTabs(s string) string {
|
|
|
+ var b strings.Builder
|
|
|
+ col := 0
|
|
|
+ for _, r := range s {
|
|
|
+ if r == '\t' {
|
|
|
+ spaces := tabWidth - (col % tabWidth)
|
|
|
+ b.WriteString(strings.Repeat(" ", spaces))
|
|
|
+ col += spaces
|
|
|
+ } else {
|
|
|
+ b.WriteRune(r)
|
|
|
+ col++
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return b.String()
|
|
|
+}
|
|
|
+
|
|
|
+func visualCol(lineText string, runeCol int) int {
|
|
|
+ col := 0
|
|
|
+ n := 0
|
|
|
+ for _, r := range lineText {
|
|
|
+ if n >= runeCol {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if r == '\t' {
|
|
|
+ col += tabWidth - (col % tabWidth)
|
|
|
+ } else {
|
|
|
+ col++
|
|
|
+ }
|
|
|
+ n++
|
|
|
+ }
|
|
|
+ return col
|
|
|
+}
|