gitea源码

console.go 2.2KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package console
  4. import (
  5. "bytes"
  6. "io"
  7. "unicode/utf8"
  8. "code.gitea.io/gitea/modules/markup"
  9. "code.gitea.io/gitea/modules/setting"
  10. "code.gitea.io/gitea/modules/typesniffer"
  11. "code.gitea.io/gitea/modules/util"
  12. trend "github.com/buildkite/terminal-to-html/v3"
  13. )
  14. func init() {
  15. markup.RegisterRenderer(Renderer{})
  16. }
  17. // Renderer implements markup.Renderer
  18. type Renderer struct{}
  19. var _ markup.RendererContentDetector = (*Renderer)(nil)
  20. // Name implements markup.Renderer
  21. func (Renderer) Name() string {
  22. return "console"
  23. }
  24. // Extensions implements markup.Renderer
  25. func (Renderer) Extensions() []string {
  26. return []string{".sh-session"}
  27. }
  28. // SanitizerRules implements markup.Renderer
  29. func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
  30. return []setting.MarkupSanitizerRule{
  31. {Element: "span", AllowAttr: "class", Regexp: `^term-((fg[ix]?|bg)\d+|container)$`},
  32. }
  33. }
  34. // CanRender implements markup.RendererContentDetector
  35. func (Renderer) CanRender(filename string, sniffedType typesniffer.SniffedType, prefetchBuf []byte) bool {
  36. if !sniffedType.IsTextPlain() {
  37. return false
  38. }
  39. s := util.UnsafeBytesToString(prefetchBuf)
  40. rs := []rune(s)
  41. cnt := 0
  42. firstErrPos := -1
  43. isCtrlSep := func(p int) bool {
  44. return p < len(rs) && (rs[p] == ';' || rs[p] == 'm')
  45. }
  46. for i, c := range rs {
  47. if c == 0 {
  48. return false
  49. }
  50. if c == '\x1b' {
  51. match := i+1 < len(rs) && rs[i+1] == '['
  52. if match && (isCtrlSep(i+2) || isCtrlSep(i+3) || isCtrlSep(i+4) || isCtrlSep(i+5)) {
  53. cnt++
  54. }
  55. }
  56. if c == utf8.RuneError && firstErrPos == -1 {
  57. firstErrPos = i
  58. }
  59. }
  60. if firstErrPos != -1 && firstErrPos != len(rs)-1 {
  61. return false
  62. }
  63. return cnt >= 2 // only render it as console output if there are at least two escape sequences
  64. }
  65. // Render renders terminal colors to HTML with all specific handling stuff.
  66. func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
  67. buf, err := io.ReadAll(input)
  68. if err != nil {
  69. return err
  70. }
  71. buf = []byte(trend.Render(buf))
  72. buf = bytes.ReplaceAll(buf, []byte("\n"), []byte(`<br>`))
  73. _, err = output.Write(buf)
  74. return err
  75. }