1
0

draw.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package editor
  2. import (
  3. "moose/internal/layout"
  4. "moose/internal/buffer"
  5. "moose/internal/util"
  6. "fmt"
  7. "strings"
  8. "strconv"
  9. "github.com/gdamore/tcell/v3/color"
  10. )
  11. func (m *Model) Draw() {
  12. screen := layout.RectFromScren(m.Screen)
  13. main := layout.Rect{
  14. X: screen.X,
  15. Y: screen.Y,
  16. Width: screen.Width,
  17. Height: screen.Height - 2,
  18. }
  19. palette := layout.Rect{
  20. X: screen.X,
  21. Y: main.Height,
  22. Width: screen.Width,
  23. Height: 2,
  24. }
  25. copy := m.LM.Workspaces[m.LM.ActiveIdx]
  26. m.DrawWorkspace(&copy, main)
  27. m.DrawPalette(palette)
  28. }
  29. func (m *Model) DrawPalette(rect layout.Rect) {
  30. for col := 0; col < rect.Width; col++ {
  31. m.Screen.SetContent(rect.X + col, rect.Y, ' ', nil, m.Style.Background(color.Black))
  32. }
  33. modeStr := m.Mode.String()
  34. if m.Mode == ModePalette {
  35. if strings.HasPrefix(m.BM.PaletteBuffer.String(), "/") {
  36. modeStr += " (command)"
  37. } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "?") {
  38. modeStr += " (find)"
  39. } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "=") {
  40. modeStr += " (replace)"
  41. }
  42. }
  43. m.Screen.PutStrStyled(rect.Width - (len(modeStr) + 1), rect.Y, strings.ToUpper(modeStr), m.Style.Background(color.Black))
  44. logStr := m.DebugLog + ", " + strings.Join(m.AM.CM.Recorded, "+") + ", " + strconv.FormatBool(m.AM.CM.Recording)
  45. m.Screen.PutStrStyled(1, rect.Y, logStr, m.Style.Background(color.Black))
  46. if m.Mode == ModePalette {
  47. m.Screen.PutStrStyled(0, rect.Y + 1, m.BM.PaletteBuffer.String(), m.Style)
  48. for _, cur := range m.BM.PaletteBuffer.CM.Cursors {
  49. line, col := buffer.LineCol(&m.BM.PaletteBuffer, cur.Offset)
  50. if line + rect.Y != rect.Y { continue }
  51. if col + 1 > rect.Width { continue }
  52. ch := buffer.RuneAt(&m.BM.PaletteBuffer, cur.Offset)
  53. m.Screen.SetContent(col, rect.Y + 1, ch, nil, m.Style.Reverse(true))
  54. }
  55. } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.info:") {
  56. m.Screen.PutStrStyled(0, rect.Y + 1, string([]rune(m.BM.PaletteBuffer.String())[11:]), m.Style.Foreground(color.LightGray))
  57. } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.warn:") {
  58. m.Screen.PutStrStyled(0, rect.Y + 1, string([]rune(m.BM.PaletteBuffer.String())[11:]), m.Style.Foreground(color.Orange))
  59. } else if strings.HasPrefix(m.BM.PaletteBuffer.String(), "moose.error:") {
  60. m.Screen.PutStrStyled(0, rect.Y + 1, string([]rune(m.BM.PaletteBuffer.String())[12:]), m.Style.Foreground(color.Red))
  61. }
  62. }
  63. func (m *Model) DrawWorkspace(w *layout.Workspace, rect layout.Rect) {
  64. m.DrawContainer(&w.RootContainer, rect)
  65. }
  66. func (m *Model) DrawContainer(c *layout.Container, rect layout.Rect) {
  67. length := util.NonNilLen(c.Children[:])
  68. if length == 0 {
  69. return
  70. }
  71. rect = layout.Rect{
  72. X: rect.X,
  73. Y: rect.Y,
  74. Width: rect.Width,
  75. Height: rect.Height,
  76. }
  77. rect = layout.RectDivide(rect, c.Split, length)
  78. for i, child := range c.Children {
  79. main := layout.RectDisplace(rect, c.Split, i)
  80. switch child.(type) {
  81. case layout.ContainerBuffers: {
  82. cb := child.(layout.ContainerBuffers)
  83. tabs := layout.Rect{
  84. X: main.X,
  85. Y: main.Y,
  86. Width: main.Width,
  87. Height: 1,
  88. }
  89. main.Y += 1
  90. main.Height -= 1
  91. m.DrawContainerTabs(&cb, tabs)
  92. m.DrawContainerBuffers(&cb, main)
  93. }
  94. case layout.Container: {
  95. c := child.(layout.Container)
  96. m.DrawContainer(&c, main)
  97. }
  98. }
  99. }
  100. }
  101. func (m *Model) DrawContainerTabs(c *layout.ContainerBuffers, rect layout.Rect) {
  102. tabs := []string{}
  103. for _, bufIdx := range c.Buffers {
  104. buf := m.BM.Buffers[bufIdx]
  105. if buf.Path == "" {
  106. tabs = append(tabs, "Buffer " + strconv.Itoa(bufIdx))
  107. } else {
  108. tabs = append(tabs, buf.Path)
  109. }
  110. }
  111. length := 0
  112. for _, tab := range tabs {
  113. if length + len(tab) + 2 > rect.Width {
  114. m.Screen.PutStrStyled(rect.X + length, rect.Y, ">", m.Style.Foreground(color.White))
  115. return
  116. }
  117. m.Screen.PutStrStyled(rect.X + length, rect.Y, tab, m.Style.Background(color.Black).Foreground(color.White))
  118. length += len(tab) + 1
  119. }
  120. }
  121. func (m *Model) DrawContainerBuffers(c *layout.ContainerBuffers, rect layout.Rect) {
  122. m.DrawBuffer(&m.BM.Buffers[c.Buffers[c.ActiveIdx]], c.Buffers[c.ActiveIdx] == m.BM.CurrentIdx, rect)
  123. }
  124. func (m *Model) DrawBuffer(buf *buffer.Buffer, isActive bool, rect layout.Rect) {
  125. primary := buf.CM.Cursors[buf.CM.PrimaryIdx]
  126. curLine, _ := buffer.LineCol(buf, primary.Offset)
  127. buf.ScrollToShow(curLine, rect.Height)
  128. startOffset := buffer.OffsetForLine(buf, buf.TopLine)
  129. endOffset := buf.Rope.Len()
  130. if endLine := buf.TopLine + rect.Height; endLine < buffer.LineCount(buf) {
  131. endOffset = buffer.OffsetForLine(buf, endLine)
  132. }
  133. visible := string(buf.Rope.Slice(startOffset, endOffset))
  134. table := strings.SplitAfter(visible, "\n")
  135. for j, line := range table {
  136. if j + 1 > rect.Height { break }
  137. nums := fmt.Sprintf("%4d ", j + buf.TopLine)
  138. r := []rune(nums + expandTabs(line))
  139. if len(r) + 1 > rect.Width {
  140. r = r[:rect.Width]
  141. }
  142. m.Screen.PutStrStyled(rect.X, rect.Y + j, string(r), m.Style)
  143. }
  144. if isActive {
  145. for _, cur := range buf.CM.Cursors {
  146. line, col := buffer.LineCol(buf, cur.Offset)
  147. screenLine := line - buf.TopLine
  148. if screenLine < 0 || screenLine + 1 > rect.Height { continue }
  149. visCol := visualCol(buffer.LineText(buf, line), col)
  150. if visCol + 1 > rect.Width { continue }
  151. ch := buffer.RuneAt(buf, cur.Offset)
  152. if m.Mode == ModeWrite {
  153. m.Screen.SetContent(rect.X + visCol + 5, rect.Y + screenLine, ch, nil, m.Style.Reverse(true))
  154. } else {
  155. m.Screen.SetContent(rect.X + visCol + 5, rect.Y + screenLine, ch, nil, m.Style.Reverse(true).Foreground(color.Gray))
  156. }
  157. }
  158. }
  159. }
  160. const tabWidth = 4
  161. func expandTabs(s string) string {
  162. var b strings.Builder
  163. col := 0
  164. for _, r := range s {
  165. if r == '\t' {
  166. spaces := tabWidth - (col % tabWidth)
  167. b.WriteString(strings.Repeat(" ", spaces))
  168. col += spaces
  169. } else {
  170. b.WriteRune(r)
  171. col++
  172. }
  173. }
  174. return b.String()
  175. }
  176. func visualCol(lineText string, runeCol int) int {
  177. col := 0
  178. n := 0
  179. for _, r := range lineText {
  180. if n >= runeCol {
  181. break
  182. }
  183. if r == '\t' {
  184. col += tabWidth - (col % tabWidth)
  185. } else {
  186. col++
  187. }
  188. n++
  189. }
  190. return col
  191. }