/.fzf/src/ansi.go

https://bitbucket.org/wjkoh/dotfiles · Go · 156 lines · 133 code · 18 blank · 5 comment · 45 complexity · 1c85e13480ab2f9bfb1467f27354072c MD5 · raw file

  1. package fzf
  2. import (
  3. "bytes"
  4. "regexp"
  5. "strconv"
  6. "strings"
  7. "unicode/utf8"
  8. )
  9. type ansiOffset struct {
  10. offset [2]int32
  11. color ansiState
  12. }
  13. type ansiState struct {
  14. fg int
  15. bg int
  16. bold bool
  17. }
  18. func (s *ansiState) colored() bool {
  19. return s.fg != -1 || s.bg != -1 || s.bold
  20. }
  21. func (s *ansiState) equals(t *ansiState) bool {
  22. if t == nil {
  23. return !s.colored()
  24. }
  25. return s.fg == t.fg && s.bg == t.bg && s.bold == t.bold
  26. }
  27. var ansiRegex *regexp.Regexp
  28. func init() {
  29. ansiRegex = regexp.MustCompile("\x1b\\[[0-9;]*[mK]")
  30. }
  31. func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, []ansiOffset, *ansiState) {
  32. var offsets []ansiOffset
  33. var output bytes.Buffer
  34. if state != nil {
  35. offsets = append(offsets, ansiOffset{[2]int32{0, 0}, *state})
  36. }
  37. idx := 0
  38. for _, offset := range ansiRegex.FindAllStringIndex(str, -1) {
  39. prev := str[idx:offset[0]]
  40. output.WriteString(prev)
  41. if proc != nil && !proc(prev, state) {
  42. return "", nil, nil
  43. }
  44. newState := interpretCode(str[offset[0]:offset[1]], state)
  45. if !newState.equals(state) {
  46. if state != nil {
  47. // Update last offset
  48. (&offsets[len(offsets)-1]).offset[1] = int32(utf8.RuneCount(output.Bytes()))
  49. }
  50. if newState.colored() {
  51. // Append new offset
  52. state = newState
  53. newLen := int32(utf8.RuneCount(output.Bytes()))
  54. offsets = append(offsets, ansiOffset{[2]int32{newLen, newLen}, *state})
  55. } else {
  56. // Discard state
  57. state = nil
  58. }
  59. }
  60. idx = offset[1]
  61. }
  62. rest := str[idx:]
  63. if len(rest) > 0 {
  64. output.WriteString(rest)
  65. if state != nil {
  66. // Update last offset
  67. (&offsets[len(offsets)-1]).offset[1] = int32(utf8.RuneCount(output.Bytes()))
  68. }
  69. }
  70. if proc != nil {
  71. proc(rest, state)
  72. }
  73. return output.String(), offsets, state
  74. }
  75. func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
  76. // State
  77. var state *ansiState
  78. if prevState == nil {
  79. state = &ansiState{-1, -1, false}
  80. } else {
  81. state = &ansiState{prevState.fg, prevState.bg, prevState.bold}
  82. }
  83. if ansiCode[len(ansiCode)-1] == 'K' {
  84. return state
  85. }
  86. ptr := &state.fg
  87. state256 := 0
  88. init := func() {
  89. state.fg = -1
  90. state.bg = -1
  91. state.bold = false
  92. state256 = 0
  93. }
  94. ansiCode = ansiCode[2 : len(ansiCode)-1]
  95. if len(ansiCode) == 0 {
  96. init()
  97. }
  98. for _, code := range strings.Split(ansiCode, ";") {
  99. if num, err := strconv.Atoi(code); err == nil {
  100. switch state256 {
  101. case 0:
  102. switch num {
  103. case 38:
  104. ptr = &state.fg
  105. state256++
  106. case 48:
  107. ptr = &state.bg
  108. state256++
  109. case 39:
  110. state.fg = -1
  111. case 49:
  112. state.bg = -1
  113. case 1:
  114. state.bold = true
  115. case 0:
  116. init()
  117. default:
  118. if num >= 30 && num <= 37 {
  119. state.fg = num - 30
  120. } else if num >= 40 && num <= 47 {
  121. state.bg = num - 40
  122. }
  123. }
  124. case 1:
  125. switch num {
  126. case 5:
  127. state256++
  128. default:
  129. state256 = 0
  130. }
  131. case 2:
  132. *ptr = num
  133. state256 = 0
  134. }
  135. }
  136. }
  137. return state
  138. }