render.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. package editor
  2. import (
  3. "strings"
  4. "fmt"
  5. "moose/internal/buffer"
  6. "github.com/gdamore/tcell/v3/color"
  7. )
  8. const tabWidth = 4
  9. func expandTabs(s string) string {
  10. var b strings.Builder
  11. col := 0
  12. for _, r := range s {
  13. if r == '\t' {
  14. spaces := tabWidth - (col % tabWidth)
  15. b.WriteString(strings.Repeat(" ", spaces))
  16. col += spaces
  17. } else {
  18. b.WriteRune(r)
  19. col++
  20. }
  21. }
  22. return b.String()
  23. }
  24. func visualCol(lineText string, runeCol int) int {
  25. col := 0
  26. n := 0
  27. for _, r := range lineText {
  28. if n >= runeCol {
  29. break
  30. }
  31. if r == '\t' {
  32. col += tabWidth - (col % tabWidth)
  33. } else {
  34. col++
  35. }
  36. n++
  37. }
  38. return col
  39. }
  40. func (m Model) Render() {
  41. sWidth, sHeight := m.Screen.Size()
  42. maxHeight := sHeight - 2 // TODO: saturating sub?
  43. bLen := len(m.BM.Buffers)
  44. bufWidth := sWidth / bLen
  45. for i := range m.BM.Buffers {
  46. buf := &m.BM.Buffers[i]
  47. primary := buf.CM.Cursors[buf.CM.PrimaryIdx]
  48. curLine, _ := buffer.LineCol(buf, primary.Offset)
  49. buf.ScrollToShow(curLine, maxHeight)
  50. startOffset := buffer.OffsetForLine(buf, buf.TopLine)
  51. endOffset := buf.Rope.Len()
  52. if endLine := buf.TopLine + maxHeight; endLine < buffer.LineCount(buf) {
  53. endOffset = buffer.OffsetForLine(buf, endLine)
  54. }
  55. visible := string(buf.Rope.Slice(startOffset, endOffset))
  56. table := strings.SplitAfter(visible, "\n")
  57. for j, line := range table {
  58. if j + 1 > maxHeight { break }
  59. nums := fmt.Sprintf("%4d ", j + buf.TopLine)
  60. r := []rune(nums + expandTabs(line))
  61. if len(r) + 1 > bufWidth {
  62. r = r[:bufWidth]
  63. }
  64. m.Screen.PutStrStyled(bufWidth*i, j, string(r), m.Style)
  65. }
  66. if i == m.BM.CurrentIdx {
  67. for _, cur := range buf.CM.Cursors {
  68. line, col := buffer.LineCol(buf, cur.Offset)
  69. screenLine := line - buf.TopLine
  70. if screenLine < 0 || screenLine + 1 > maxHeight { continue }
  71. visCol := visualCol(buffer.LineText(buf, line), col)
  72. if visCol + 1 > bufWidth { continue }
  73. ch := buffer.RuneAt(buf, cur.Offset)
  74. if m.Mode == ModeWrite {
  75. m.Screen.SetContent(bufWidth * i + visCol + 5, screenLine, ch, nil, m.Style.Reverse(true))
  76. } else {
  77. m.Screen.SetContent(bufWidth * i + visCol + 5, screenLine, ch, nil, m.Style.Reverse(true).Foreground(color.Gray))
  78. }
  79. }
  80. }
  81. }
  82. for col := 0; col < sWidth; col++ {
  83. m.Screen.SetContent(col, maxHeight, ' ', nil, m.Style.Background(color.Black))
  84. }
  85. modeStr := m.Mode.String()
  86. if m.Mode == ModePalette {
  87. if strings.HasPrefix(m.BM.PaletteBuffer.String(), "/") {
  88. modeStr += " (command)"
  89. } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "?") {
  90. modeStr += " (find)"
  91. } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "=") {
  92. modeStr += " (replace)"
  93. }
  94. }
  95. m.Screen.PutStrStyled(sWidth - (len(modeStr) + 1), maxHeight, strings.ToUpper(modeStr), m.Style.Background(color.Black))
  96. if m.Mode == ModePalette {
  97. m.Screen.PutStrStyled(0, maxHeight + 1, m.BM.PaletteBuffer.String(), m.Style)
  98. for _, cur := range m.BM.PaletteBuffer.CM.Cursors {
  99. line, col := buffer.LineCol(&m.BM.PaletteBuffer, cur.Offset)
  100. if line + maxHeight != maxHeight { continue }
  101. if col + 1 > sWidth { continue }
  102. ch := buffer.RuneAt(&m.BM.PaletteBuffer, cur.Offset)
  103. m.Screen.SetContent(col, maxHeight + 1, ch, nil, m.Style.Reverse(true))
  104. }
  105. } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.info:") {
  106. m.Screen.PutStrStyled(0, maxHeight + 1, string([]rune(m.BM.PaletteBuffer.String())[11:]), m.Style.Foreground(color.LightGray))
  107. } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.warn:") {
  108. m.Screen.PutStrStyled(0, maxHeight + 1, string([]rune(m.BM.PaletteBuffer.String())[11:]), m.Style.Foreground(color.Orange))
  109. } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.error:") {
  110. m.Screen.PutStrStyled(0, maxHeight + 1, string([]rune(m.BM.PaletteBuffer.String())[12:]), m.Style.Foreground(color.Red))
  111. }
  112. }