file.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package util
  2. import (
  3. "fmt"
  4. "path/filepath"
  5. "strings"
  6. "unicode"
  7. "github.com/charmbracelet/lipgloss/v2/compat"
  8. "github.com/charmbracelet/x/ansi"
  9. "github.com/sst/opencode/internal/styles"
  10. "github.com/sst/opencode/internal/theme"
  11. )
  12. var RootPath string
  13. var CwdPath string
  14. type fileRenderer struct {
  15. filename string
  16. content string
  17. height int
  18. }
  19. type fileRenderingOption func(*fileRenderer)
  20. func WithTruncate(height int) fileRenderingOption {
  21. return func(c *fileRenderer) {
  22. c.height = height
  23. }
  24. }
  25. func RenderFile(
  26. filename string,
  27. content string,
  28. width int,
  29. options ...fileRenderingOption) string {
  30. t := theme.CurrentTheme()
  31. renderer := &fileRenderer{
  32. filename: filename,
  33. content: content,
  34. }
  35. for _, option := range options {
  36. option(renderer)
  37. }
  38. lines := []string{}
  39. for line := range strings.SplitSeq(content, "\n") {
  40. line = strings.TrimRightFunc(line, unicode.IsSpace)
  41. line = strings.ReplaceAll(line, "\t", " ")
  42. lines = append(lines, line)
  43. }
  44. content = strings.Join(lines, "\n")
  45. if renderer.height > 0 {
  46. content = TruncateHeight(content, renderer.height)
  47. }
  48. content = fmt.Sprintf("```%s\n%s\n```", Extension(renderer.filename), content)
  49. content = ToMarkdown(content, width, t.BackgroundPanel())
  50. return content
  51. }
  52. func TruncateHeight(content string, height int) string {
  53. lines := strings.Split(content, "\n")
  54. if len(lines) > height {
  55. return strings.Join(lines[:height], "\n")
  56. }
  57. return content
  58. }
  59. func Relative(path string) string {
  60. path = strings.TrimPrefix(path, CwdPath+"/")
  61. return strings.TrimPrefix(path, RootPath+"/")
  62. }
  63. func Extension(path string) string {
  64. ext := filepath.Ext(path)
  65. if ext == "" {
  66. ext = ""
  67. } else {
  68. ext = strings.ToLower(ext[1:])
  69. }
  70. return ext
  71. }
  72. func ToMarkdown(content string, width int, backgroundColor compat.AdaptiveColor) string {
  73. r := styles.GetMarkdownRenderer(width-6, backgroundColor)
  74. content = strings.ReplaceAll(content, RootPath+"/", "")
  75. rendered, _ := r.Render(content)
  76. lines := strings.Split(rendered, "\n")
  77. if len(lines) > 0 {
  78. firstLine := lines[0]
  79. cleaned := ansi.Strip(firstLine)
  80. nospace := strings.ReplaceAll(cleaned, " ", "")
  81. if nospace == "" {
  82. lines = lines[1:]
  83. }
  84. if len(lines) > 0 {
  85. lastLine := lines[len(lines)-1]
  86. cleaned = ansi.Strip(lastLine)
  87. nospace = strings.ReplaceAll(cleaned, " ", "")
  88. if nospace == "" {
  89. lines = lines[:len(lines)-1]
  90. }
  91. }
  92. }
  93. content = strings.Join(lines, "\n")
  94. return strings.TrimSuffix(content, "\n")
  95. }