insert-formatting.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. // License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
  2. package sgr
  3. import (
  4. "fmt"
  5. "slices"
  6. "strconv"
  7. "strings"
  8. "unicode/utf8"
  9. "kitty/tools/utils"
  10. "kitty/tools/utils/style"
  11. "kitty/tools/wcswidth"
  12. )
  13. var _ = fmt.Print
  14. type UnderlineStyle uint8
  15. const (
  16. No_underline UnderlineStyle = iota
  17. Straight_underline
  18. Double_underline
  19. Curly_underline
  20. Dotted_underline
  21. Dashed_underline
  22. )
  23. type Color struct {
  24. Red, Green, Blue uint8
  25. Is_numbered bool
  26. }
  27. func (self *Color) Set(val any) (err error) {
  28. switch v := val.(type) {
  29. case int:
  30. self.Is_numbered = true
  31. self.Red = uint8(v)
  32. case style.RGBA:
  33. self.Is_numbered = false
  34. self.Red, self.Green, self.Blue = v.Red, v.Green, v.Blue
  35. case string:
  36. rgba, err := style.ParseColor(v)
  37. if err != nil {
  38. return err
  39. }
  40. self.Is_numbered = false
  41. self.Red, self.Green, self.Blue = rgba.Red, rgba.Green, rgba.Blue
  42. default:
  43. return fmt.Errorf("Unknown type to set color from: %T", v)
  44. }
  45. return nil
  46. }
  47. func (self Color) AsCSI(base int) string {
  48. if self.Is_numbered && base < 50 {
  49. if self.Red < 8 {
  50. return strconv.Itoa(base + int(self.Red))
  51. }
  52. if self.Red < 16 {
  53. return strconv.Itoa(base + 52 + int(self.Red))
  54. }
  55. return fmt.Sprintf("%d:5:%d", base+8, self.Red)
  56. }
  57. return fmt.Sprintf("%d:2:%d:%d:%d", base+8, self.Red, self.Green, self.Blue)
  58. }
  59. func (self *Color) FromNumber(n uint8) {
  60. self.Is_numbered, self.Red = true, n
  61. }
  62. func (self *Color) FromExtended(nums ...int) bool {
  63. switch nums[0] {
  64. case 5:
  65. if len(nums) > 1 {
  66. self.Red = uint8(nums[1])
  67. self.Is_numbered = true
  68. return true
  69. }
  70. case 2:
  71. if len(nums) > 3 {
  72. self.Is_numbered = false
  73. self.Red, self.Green, self.Blue = uint8(nums[1]), uint8(nums[2]), uint8(nums[3])
  74. return true
  75. }
  76. }
  77. return false
  78. }
  79. type BoolVal struct{ Is_set, Val bool }
  80. type UnderlineStyleVal struct {
  81. Is_set bool
  82. Val UnderlineStyle
  83. }
  84. type ColorVal struct {
  85. Is_set, Is_default bool
  86. Val Color
  87. }
  88. type SGR struct {
  89. Italic, Reverse, Bold, Dim, Strikethrough BoolVal
  90. Underline_style UnderlineStyleVal
  91. Foreground, Background, Underline_color ColorVal
  92. }
  93. func (self *BoolVal) AsCSI(set, reset string) string {
  94. if !self.Is_set {
  95. return ""
  96. }
  97. if self.Val {
  98. return set
  99. }
  100. return reset
  101. }
  102. func (self *UnderlineStyleVal) AsCSI() string {
  103. if !self.Is_set {
  104. return ""
  105. }
  106. return fmt.Sprintf("4:%d;", self.Val)
  107. }
  108. func (self *ColorVal) AsCSI(base int) string {
  109. if !self.Is_set {
  110. return ""
  111. }
  112. if self.Is_default {
  113. return strconv.Itoa(base + 9)
  114. }
  115. return self.Val.AsCSI(base)
  116. }
  117. func (self *SGR) AsCSI() string {
  118. ans := make([]byte, 0, 16)
  119. w := func(x string) {
  120. if x != "" {
  121. ans = append(ans, x...)
  122. ans = append(ans, ';')
  123. }
  124. }
  125. w(self.Bold.AsCSI("1", "221"))
  126. w(self.Dim.AsCSI("2", "222"))
  127. w(self.Italic.AsCSI("3", "23"))
  128. w(self.Reverse.AsCSI("7", "27"))
  129. w(self.Strikethrough.AsCSI("9", "29"))
  130. w(self.Underline_style.AsCSI())
  131. w(self.Foreground.AsCSI(30))
  132. w(self.Background.AsCSI(40))
  133. w(self.Underline_color.AsCSI(50))
  134. if len(ans) > 0 {
  135. ans = ans[:len(ans)-1]
  136. ans = append(ans, 'm')
  137. }
  138. return utils.UnsafeBytesToString(ans)
  139. }
  140. func (self *SGR) IsEmpty() bool {
  141. return !(self.Foreground.Is_set || self.Background.Is_set || self.Underline_color.Is_set || self.Underline_style.Is_set || self.Italic.Is_set || self.Bold.Is_set || self.Reverse.Is_set || self.Dim.Is_set || self.Strikethrough.Is_set)
  142. }
  143. func (self *SGR) ApplyMask(other SGR) {
  144. if other.Italic.Is_set {
  145. self.Italic.Is_set = false
  146. }
  147. if other.Reverse.Is_set {
  148. self.Reverse.Is_set = false
  149. }
  150. if other.Bold.Is_set {
  151. self.Bold.Is_set = false
  152. }
  153. if other.Dim.Is_set {
  154. self.Dim.Is_set = false
  155. }
  156. if other.Strikethrough.Is_set {
  157. self.Strikethrough.Is_set = false
  158. }
  159. if other.Underline_style.Is_set {
  160. self.Underline_style.Is_set = false
  161. }
  162. if other.Foreground.Is_set {
  163. self.Foreground.Is_set = false
  164. }
  165. if other.Background.Is_set {
  166. self.Background.Is_set = false
  167. }
  168. if other.Underline_color.Is_set {
  169. self.Underline_color.Is_set = false
  170. }
  171. }
  172. func (self *SGR) ApplySGR(other SGR) {
  173. if other.Italic.Is_set {
  174. self.Italic = other.Italic
  175. }
  176. if other.Reverse.Is_set {
  177. self.Reverse = other.Reverse
  178. }
  179. if other.Bold.Is_set {
  180. self.Bold = other.Bold
  181. }
  182. if other.Dim.Is_set {
  183. self.Dim = other.Dim
  184. }
  185. if other.Strikethrough.Is_set {
  186. self.Strikethrough = other.Strikethrough
  187. }
  188. if other.Underline_style.Is_set {
  189. self.Underline_style = other.Underline_style
  190. }
  191. if other.Foreground.Is_set {
  192. self.Foreground = other.Foreground
  193. }
  194. if other.Background.Is_set {
  195. self.Background = other.Background
  196. }
  197. if other.Underline_color.Is_set {
  198. self.Underline_color = other.Underline_color
  199. }
  200. }
  201. func SGRFromCSI(csi string) (ans SGR) {
  202. if !strings.HasSuffix(csi, "m") {
  203. return
  204. }
  205. csi = csi[:len(csi)-1]
  206. if csi == "" {
  207. csi = "0"
  208. }
  209. parts := strings.Split(csi, ";")
  210. nums := make([]int, 0, 8)
  211. for _, part := range parts {
  212. subparts := strings.Split(part, ":")
  213. nums = nums[:0]
  214. for _, b := range subparts {
  215. q, err := strconv.Atoi(b)
  216. if err == nil {
  217. nums = append(nums, q)
  218. }
  219. }
  220. if len(nums) == 0 {
  221. continue
  222. }
  223. switch nums[0] {
  224. case 0:
  225. ans = SGR{}
  226. case 1:
  227. ans.Bold.Val, ans.Bold.Is_set = true, true
  228. case 221:
  229. ans.Bold.Val, ans.Bold.Is_set = false, true
  230. case 2:
  231. ans.Dim.Val, ans.Dim.Is_set = true, true
  232. case 222:
  233. ans.Dim.Val, ans.Dim.Is_set = false, true
  234. case 22:
  235. ans.Dim.Val, ans.Bold.Val = false, false
  236. ans.Dim.Is_set, ans.Bold.Is_set = true, true
  237. case 3:
  238. ans.Italic.Is_set, ans.Italic.Val = true, true
  239. case 23:
  240. ans.Italic.Is_set, ans.Italic.Val = true, false
  241. case 7:
  242. ans.Reverse.Is_set, ans.Reverse.Val = true, true
  243. case 27:
  244. ans.Reverse.Is_set, ans.Reverse.Val = true, false
  245. case 9:
  246. ans.Strikethrough.Is_set, ans.Strikethrough.Val = true, true
  247. case 29:
  248. ans.Strikethrough.Is_set, ans.Strikethrough.Val = true, false
  249. case 24:
  250. ans.Underline_style.Is_set, ans.Underline_style.Val = true, No_underline
  251. case 4:
  252. us := 1
  253. if len(nums) > 1 {
  254. us = nums[1]
  255. }
  256. switch us {
  257. case 0:
  258. ans.Underline_style.Is_set, ans.Underline_style.Val = true, No_underline
  259. case 1:
  260. ans.Underline_style.Is_set, ans.Underline_style.Val = true, Straight_underline
  261. case 2:
  262. ans.Underline_style.Is_set, ans.Underline_style.Val = true, Double_underline
  263. case 3:
  264. ans.Underline_style.Is_set, ans.Underline_style.Val = true, Curly_underline
  265. case 4:
  266. ans.Underline_style.Is_set, ans.Underline_style.Val = true, Dotted_underline
  267. case 5:
  268. ans.Underline_style.Is_set, ans.Underline_style.Val = true, Dashed_underline
  269. }
  270. case 30, 31, 32, 33, 34, 35, 36, 37:
  271. ans.Foreground.Is_set, ans.Foreground.Is_default = true, false
  272. ans.Foreground.Val.FromNumber(uint8(nums[0] - 30))
  273. case 90, 91, 92, 93, 94, 95, 96, 97:
  274. ans.Foreground.Is_set, ans.Foreground.Is_default = true, false
  275. ans.Foreground.Val.FromNumber(uint8(nums[0] - 82))
  276. case 38:
  277. if ans.Foreground.Val.FromExtended(nums[1:]...) {
  278. ans.Foreground.Is_set, ans.Foreground.Is_default = true, false
  279. }
  280. case 39:
  281. ans.Foreground.Is_set, ans.Foreground.Is_default = true, true
  282. case 40, 41, 42, 43, 44, 45, 46, 47:
  283. ans.Background.Is_set, ans.Background.Is_default = true, false
  284. ans.Background.Val.FromNumber(uint8(nums[0] - 40))
  285. case 100, 101, 102, 103, 104, 105, 106, 107:
  286. ans.Background.Is_set, ans.Background.Is_default = true, false
  287. ans.Background.Val.FromNumber(uint8(nums[0] - 92))
  288. case 48:
  289. if ans.Background.Val.FromExtended(nums[1:]...) {
  290. ans.Background.Is_set, ans.Background.Is_default = true, false
  291. }
  292. case 49:
  293. ans.Background.Is_set, ans.Background.Is_default = true, true
  294. case 58:
  295. if ans.Underline_color.Val.FromExtended(nums[1:]...) {
  296. ans.Underline_color.Is_set, ans.Underline_color.Is_default = true, false
  297. }
  298. case 59:
  299. ans.Underline_color.Is_set, ans.Underline_color.Is_default = true, true
  300. }
  301. }
  302. return
  303. }
  304. type Span struct {
  305. Offset, Size int // in bytes
  306. opening_sgr, closing_sgr SGR
  307. }
  308. func NewSpan(offset, size int) *Span {
  309. return &Span{Offset: offset, Size: size}
  310. }
  311. func (self *BoolVal) Set(val bool) {
  312. self.Is_set = true
  313. self.Val = val
  314. }
  315. func (self *ColorVal) Set(val any) {
  316. self.Is_set = true
  317. if val == nil {
  318. self.Is_default = true
  319. } else {
  320. self.Is_default = false
  321. if err := self.Val.Set(val); err != nil {
  322. panic(err)
  323. }
  324. }
  325. }
  326. func (self *Span) SetForeground(val any) *Span {
  327. self.opening_sgr.Foreground.Set(val)
  328. return self
  329. }
  330. func (self *Span) SetBackground(val any) *Span {
  331. self.opening_sgr.Background.Set(val)
  332. return self
  333. }
  334. func (self *Span) SetUnderlineColor(val any) *Span {
  335. self.opening_sgr.Underline_color.Set(val)
  336. return self
  337. }
  338. func (self *Span) SetItalic(val bool) *Span {
  339. self.opening_sgr.Italic.Set(val)
  340. return self
  341. }
  342. func (self *Span) SetBold(val bool) *Span {
  343. self.opening_sgr.Bold.Set(val)
  344. return self
  345. }
  346. func (self *Span) SetReverse(val bool) *Span {
  347. self.opening_sgr.Reverse.Set(val)
  348. return self
  349. }
  350. func (self *Span) SetDim(val bool) *Span {
  351. self.opening_sgr.Dim.Set(val)
  352. return self
  353. }
  354. func (self *Span) SetStrikethrough(val bool) *Span {
  355. self.opening_sgr.Strikethrough.Set(val)
  356. return self
  357. }
  358. func (self *Span) SetUnderlineStyle(val UnderlineStyle) *Span {
  359. self.opening_sgr.Underline_style.Is_set = true
  360. self.opening_sgr.Underline_style.Val = val
  361. return self
  362. }
  363. func (self *Span) SetClosingForeground(val any) *Span {
  364. self.closing_sgr.Foreground.Set(val)
  365. return self
  366. }
  367. func (self *Span) SetClosingBackground(val any) *Span {
  368. self.closing_sgr.Background.Set(val)
  369. return self
  370. }
  371. func (self *Span) SetClosingUnderlineColor(val any) *Span {
  372. self.closing_sgr.Underline_color.Set(val)
  373. return self
  374. }
  375. func (self *Span) SetClosingItalic(val bool) *Span {
  376. self.closing_sgr.Italic.Set(val)
  377. return self
  378. }
  379. func (self *Span) SetClosingBold(val bool) *Span {
  380. self.closing_sgr.Bold.Set(val)
  381. return self
  382. }
  383. func (self *Span) SetClosingReverse(val bool) *Span {
  384. self.closing_sgr.Reverse.Set(val)
  385. return self
  386. }
  387. func (self *Span) SetClosingDim(val bool) *Span {
  388. self.closing_sgr.Dim.Set(val)
  389. return self
  390. }
  391. func (self *Span) SetClosingStrikethrough(val bool) *Span {
  392. self.closing_sgr.Strikethrough.Set(val)
  393. return self
  394. }
  395. func (self *Span) SetClosingUnderlineStyle(val UnderlineStyle) *Span {
  396. self.closing_sgr.Underline_style.Is_set = true
  397. self.opening_sgr.Underline_style.Val = val
  398. return self
  399. }
  400. // Insert formatting into text at the specified offsets, overriding any existing formatting, and restoring
  401. // existing formatting after the replaced sections.
  402. func InsertFormatting(text string, spans ...*Span) string {
  403. spans = utils.Filter(spans, func(s *Span) bool { return !s.opening_sgr.IsEmpty() })
  404. if len(spans) == 0 {
  405. return text
  406. }
  407. var in_span *Span
  408. ans := make([]byte, 0, 2*len(text))
  409. var overall_sgr_state SGR
  410. slices.SortFunc(spans, func(a, b *Span) int { return a.Offset - b.Offset })
  411. text_len := 0
  412. var ep *wcswidth.EscapeCodeParser
  413. write_csi := func(csi string) {
  414. if csi != "" {
  415. ans = append(ans, 0x1b, '[')
  416. ans = append(ans, csi...)
  417. }
  418. }
  419. open_span := func() {
  420. in_span = spans[0]
  421. spans = spans[1:]
  422. if in_span.Size > 0 {
  423. write_csi(in_span.opening_sgr.AsCSI())
  424. } else {
  425. in_span = nil
  426. }
  427. }
  428. close_span := func() {
  429. write_csi(in_span.closing_sgr.AsCSI())
  430. write_csi(overall_sgr_state.AsCSI())
  431. in_span = nil
  432. }
  433. ep = &wcswidth.EscapeCodeParser{
  434. HandleRune: func(ch rune) error {
  435. var rlen int
  436. if in_span == nil {
  437. if len(spans) > 0 && text_len >= spans[0].Offset {
  438. open_span()
  439. return ep.HandleRune(ch)
  440. }
  441. before := len(ans)
  442. ans = utf8.AppendRune(ans, ch)
  443. rlen = len(ans) - before
  444. } else {
  445. rlen = utf8.RuneLen(ch)
  446. if text_len+rlen > in_span.Offset+in_span.Size {
  447. close_span()
  448. }
  449. ans = utf8.AppendRune(ans, ch)
  450. }
  451. text_len += rlen
  452. return nil
  453. },
  454. HandleCSI: func(csib []byte) error {
  455. csi := utils.UnsafeBytesToString(csib)
  456. if len(csi) == 0 || csi[len(csi)-1] != 'm' {
  457. write_csi(csi)
  458. return nil
  459. }
  460. sgr := SGRFromCSI(csi)
  461. overall_sgr_state.ApplySGR(sgr)
  462. if in_span == nil {
  463. write_csi(csi)
  464. } else {
  465. sgr.ApplyMask(in_span.opening_sgr)
  466. csi := sgr.AsCSI()
  467. write_csi(csi)
  468. }
  469. return nil
  470. },
  471. HandleOSC: func(osc []byte) error {
  472. ans = append(ans, 0x1b, ']')
  473. ans = append(ans, osc...)
  474. ans = append(ans, 0x1b, '\\')
  475. return nil
  476. },
  477. }
  478. ep.ParseString(text)
  479. if in_span != nil {
  480. close_span()
  481. }
  482. return utils.UnsafeBytesToString(ans)
  483. }