file.go 2.6 KB

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