wrapper.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. // License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
  2. package style
  3. import (
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. "sync"
  8. "kitty/tools/utils/shlex"
  9. )
  10. type escape_code interface {
  11. prefix() string
  12. suffix() string
  13. is_empty() bool
  14. }
  15. // bool values {{{
  16. type bool_value struct {
  17. is_set, val bool
  18. }
  19. func (self bool_value) as_sgr(start, end string, prefix, suffix []string) ([]string, []string) {
  20. if self.is_set {
  21. if !self.val {
  22. start, end = end, start
  23. }
  24. prefix = append(prefix, start)
  25. suffix = append(suffix, end)
  26. }
  27. return prefix, suffix
  28. }
  29. func (self *bool_value) set_val(val bool) {
  30. self.is_set = true
  31. self.val = val
  32. }
  33. func (self *bool_value) from_string(raw string) bool {
  34. switch strings.ToLower(raw) {
  35. case "y", "yes", "true", "1":
  36. self.set_val(true)
  37. return true
  38. case "n", "no", "false", "0":
  39. self.set_val(false)
  40. return true
  41. default:
  42. return false
  43. }
  44. }
  45. // }}}
  46. // color values {{{
  47. type RGBA struct {
  48. Red, Green, Blue, Inverse_alpha uint8
  49. }
  50. func (self RGBA) AsRGBSharp() string {
  51. return fmt.Sprintf("#%02x%02x%02x", self.Red, self.Green, self.Blue)
  52. }
  53. func (self *RGBA) parse_rgb_strings(r string, g string, b string) bool {
  54. var rv, gv, bv uint64
  55. var err error
  56. if rv, err = strconv.ParseUint(r, 16, 8); err != nil {
  57. return false
  58. }
  59. if gv, err = strconv.ParseUint(g, 16, 8); err != nil {
  60. return false
  61. }
  62. if bv, err = strconv.ParseUint(b, 16, 8); err != nil {
  63. return false
  64. }
  65. self.Red, self.Green, self.Blue = uint8(rv), uint8(gv), uint8(bv)
  66. return true
  67. }
  68. func (self *RGBA) AsRGB() uint32 {
  69. return uint32(self.Blue) | (uint32(self.Green) << 8) | (uint32(self.Red) << 16)
  70. }
  71. func (self *RGBA) IsDark() bool {
  72. return self.Red < 155 && self.Green < 155 && self.Blue < 155
  73. }
  74. func (self *RGBA) FromRGB(col uint32) {
  75. self.Red = uint8((col >> 16) & 0xff)
  76. self.Green = uint8((col >> 8) & 0xff)
  77. self.Blue = uint8((col) & 0xff)
  78. }
  79. type color_type struct {
  80. is_numbered bool
  81. val RGBA
  82. }
  83. func (self color_type) as_sgr(number_base int, prefix, suffix []string) ([]string, []string) {
  84. suffix = append(suffix, strconv.Itoa(number_base+9))
  85. if self.is_numbered {
  86. num := int(self.val.Red)
  87. if num < 16 && number_base < 50 {
  88. if num > 7 {
  89. number_base += 60
  90. num -= 8
  91. }
  92. prefix = append(prefix, strconv.Itoa(number_base+num))
  93. } else {
  94. prefix = append(prefix, fmt.Sprintf("%d:5:%d", number_base+8, num))
  95. }
  96. } else {
  97. prefix = append(prefix, fmt.Sprintf("%d:2:%d:%d:%d", number_base+8, self.val.Red, self.val.Green, self.val.Blue))
  98. }
  99. return prefix, suffix
  100. }
  101. type color_value struct {
  102. is_set bool
  103. val color_type
  104. }
  105. func parse_sharp(color string) (ans RGBA, err error) {
  106. if len(color)%3 != 0 {
  107. return RGBA{}, fmt.Errorf("Not a valid color: #%s", color)
  108. }
  109. part_size := len(color) / 3
  110. r, g, b := color[:part_size], color[part_size:2*part_size], color[part_size*2:]
  111. if part_size == 1 {
  112. r += r
  113. g += g
  114. b += b
  115. }
  116. if !ans.parse_rgb_strings(r, g, b) {
  117. err = fmt.Errorf("Not a valid color: #%s", color)
  118. }
  119. return
  120. }
  121. func parse_rgb(color string) (ans RGBA, err error) {
  122. colors := strings.Split(color, "/")
  123. if len(colors) == 3 && ans.parse_rgb_strings(colors[0], colors[1], colors[2]) {
  124. return
  125. }
  126. err = fmt.Errorf("Not a valid RGB color: %#v", color)
  127. return
  128. }
  129. func ParseColor(color string) (RGBA, error) {
  130. raw := strings.TrimSpace(strings.ToLower(color))
  131. if val, ok := ColorNames[raw]; ok {
  132. return val, nil
  133. }
  134. if strings.HasPrefix(raw, "#") {
  135. return parse_sharp(raw[1:])
  136. }
  137. if strings.HasPrefix(raw, "rgb:") {
  138. return parse_rgb(raw[4:])
  139. }
  140. return RGBA{}, fmt.Errorf("Not a valid color name: %#v", color)
  141. }
  142. type NullableColor struct {
  143. Color RGBA
  144. IsSet bool
  145. }
  146. func ParseColorOrNone(color string) (NullableColor, error) {
  147. raw := strings.TrimSpace(strings.ToLower(color))
  148. if raw == "none" {
  149. return NullableColor{}, nil
  150. }
  151. c, err := ParseColor(raw)
  152. return NullableColor{Color: c, IsSet: err == nil}, err
  153. }
  154. var named_colors = map[string]uint8{
  155. "black": 0, "red": 1, "green": 2, "yellow": 3, "blue": 4, "magenta": 5, "cyan": 6, "gray": 7, "white": 7,
  156. "hi-black": 8, "hi-red": 9, "hi-green": 10, "hi-yellow": 11, "hi-blue": 12, "hi-magenta": 13, "hi-cyan": 14, "hi-gray": 15, "hi-white": 15,
  157. "bright-black": 8, "bright-red": 9, "bright-green": 10, "bright-yellow": 11, "bright-blue": 12, "bright-magenta": 13, "bright-cyan": 14, "bright-gray": 15, "bright-white": 15,
  158. "intense-black": 8, "intense-red": 9, "intense-green": 10, "intense-yellow": 11, "intense-blue": 12, "intense-magenta": 13, "intense-cyan": 14, "intense-gray": 15, "intense-white": 15,
  159. }
  160. func (self *color_value) from_string(raw string) bool {
  161. if n, ok := named_colors[raw]; ok {
  162. self.is_set = true
  163. self.val = color_type{val: RGBA{Red: n}, is_numbered: true}
  164. return true
  165. }
  166. a, err := strconv.Atoi(raw)
  167. if err == nil && 0 <= a && a <= 255 {
  168. self.is_set = true
  169. self.val = color_type{val: RGBA{Red: uint8(a)}, is_numbered: true}
  170. return true
  171. }
  172. c, err := ParseColor(raw)
  173. if err != nil {
  174. return false
  175. }
  176. self.is_set = true
  177. self.val = color_type{val: c}
  178. return true
  179. }
  180. func (self color_value) as_sgr(number_base int, prefix, suffix []string) ([]string, []string) {
  181. if self.is_set {
  182. prefix, suffix = self.val.as_sgr(number_base, prefix, suffix)
  183. }
  184. return prefix, suffix
  185. }
  186. // }}}
  187. // underline values {{{
  188. type underline_style uint8
  189. const (
  190. no_underline underline_style = 0
  191. straight_underline underline_style = 1
  192. double_underline underline_style = 2
  193. curly_underline underline_style = 3
  194. dotted_underline underline_style = 4
  195. dashed_underline underline_style = 5
  196. nil_underline underline_style = 255
  197. )
  198. type underline_value struct {
  199. is_set bool
  200. style underline_style
  201. }
  202. func (self *underline_value) from_string(val string) bool {
  203. ans := nil_underline
  204. switch val {
  205. case "true", "yes", "y", "straight", "single":
  206. ans = straight_underline
  207. case "false", "no", "n", "none":
  208. ans = no_underline
  209. case "double":
  210. ans = double_underline
  211. case "curly":
  212. ans = curly_underline
  213. case "dotted":
  214. ans = dotted_underline
  215. case "dashed":
  216. ans = dashed_underline
  217. }
  218. if ans == nil_underline {
  219. return false
  220. }
  221. self.is_set = true
  222. self.style = ans
  223. return true
  224. }
  225. func (self underline_value) as_sgr(prefix, suffix []string) ([]string, []string) {
  226. if self.is_set {
  227. s, e := "4:0", "4:0"
  228. if self.style != no_underline {
  229. s = "4:" + strconv.Itoa(int(self.style))
  230. }
  231. prefix = append(prefix, s)
  232. suffix = append(suffix, e)
  233. }
  234. return prefix, suffix
  235. }
  236. // }}}
  237. type sgr_code struct {
  238. bold, italic, reverse, dim, strikethrough bool_value
  239. fg, bg, uc color_value
  240. underline underline_value
  241. _prefix, _suffix string
  242. }
  243. func (self sgr_code) prefix() string {
  244. return self._prefix
  245. }
  246. func (self sgr_code) suffix() string {
  247. return self._suffix
  248. }
  249. func (self sgr_code) is_empty() bool {
  250. return self._prefix == ""
  251. }
  252. type url_code struct {
  253. url string
  254. }
  255. func (self url_code) prefix() string {
  256. return fmt.Sprintf("\x1b]8;;%s\x1b\\", self.url)
  257. }
  258. func (self url_code) suffix() string {
  259. return "\x1b]8;;\x1b\\"
  260. }
  261. func (self url_code) is_empty() bool {
  262. return self.url == ""
  263. }
  264. func (self *sgr_code) update() {
  265. p := make([]string, 0, 1)
  266. s := make([]string, 0, 1)
  267. p, s = self.bold.as_sgr("1", "221", p, s)
  268. p, s = self.dim.as_sgr("2", "222", p, s)
  269. p, s = self.italic.as_sgr("3", "23", p, s)
  270. p, s = self.reverse.as_sgr("7", "27", p, s)
  271. p, s = self.strikethrough.as_sgr("9", "29", p, s)
  272. p, s = self.underline.as_sgr(p, s)
  273. p, s = self.fg.as_sgr(30, p, s)
  274. p, s = self.bg.as_sgr(40, p, s)
  275. p, s = self.uc.as_sgr(50, p, s)
  276. if len(p) > 0 {
  277. self._prefix = "\x1b[" + strings.Join(p, ";") + "m"
  278. } else {
  279. self._prefix = ""
  280. }
  281. if len(s) > 0 {
  282. self._suffix = "\x1b[" + strings.Join(s, ";") + "m"
  283. } else {
  284. self._suffix = ""
  285. }
  286. }
  287. func parse_spec(spec string) []escape_code {
  288. ans := make([]escape_code, 0, 1)
  289. sgr := sgr_code{}
  290. sparts, _ := shlex.Split(spec)
  291. for _, p := range sparts {
  292. key, val, found := strings.Cut(p, "=")
  293. if !found {
  294. val = "true"
  295. }
  296. switch key {
  297. case "fg":
  298. sgr.fg.from_string(val)
  299. case "bg":
  300. sgr.bg.from_string(val)
  301. case "bold", "b":
  302. sgr.bold.from_string(val)
  303. case "italic", "i":
  304. sgr.italic.from_string(val)
  305. case "reverse":
  306. sgr.reverse.from_string(val)
  307. case "dim", "faint":
  308. sgr.dim.from_string(val)
  309. case "underline", "u":
  310. sgr.underline.from_string(val)
  311. case "strikethrough", "s":
  312. sgr.strikethrough.from_string(val)
  313. case "ucol", "underline_color", "uc":
  314. sgr.uc.from_string(val)
  315. }
  316. }
  317. sgr.update()
  318. if !sgr.is_empty() {
  319. ans = append(ans, &sgr)
  320. }
  321. return ans
  322. }
  323. var parsed_spec_cache = make(map[string][]escape_code)
  324. var parsed_spec_cache_mutex = sync.Mutex{}
  325. func cached_parse_spec(spec string) []escape_code {
  326. parsed_spec_cache_mutex.Lock()
  327. defer parsed_spec_cache_mutex.Unlock()
  328. if val, ok := parsed_spec_cache[spec]; ok {
  329. return val
  330. }
  331. ans := parse_spec(spec)
  332. parsed_spec_cache[spec] = ans
  333. return ans
  334. }
  335. func prefix_for_spec(spec string) string {
  336. sb := strings.Builder{}
  337. for _, ec := range cached_parse_spec(spec) {
  338. sb.WriteString(ec.prefix())
  339. }
  340. return sb.String()
  341. }
  342. func suffix_for_spec(spec string) string {
  343. sb := strings.Builder{}
  344. for _, ec := range cached_parse_spec(spec) {
  345. sb.WriteString(ec.suffix())
  346. }
  347. return sb.String()
  348. }