pretty.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. package yu_pretty
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. yu_fast "gogs.qqck.cn/s/tools/fast"
  6. "sort"
  7. "strconv"
  8. )
  9. // Options is Pretty options
  10. type Options struct {
  11. // Width is an max column width for single line arrays
  12. // Default is 80
  13. Width int
  14. // Prefix is a prefix for all lines
  15. // Default is an empty string
  16. Prefix string
  17. // Indent is the nested indentation
  18. // Default is two spaces
  19. Indent string
  20. // SortKeys will sort the keys alphabetically
  21. // Default is false
  22. SortKeys bool
  23. }
  24. // DefaultOptions is the default options for pretty formats.
  25. var DefaultOptions = &Options{Width: 80, Prefix: "", Indent: " ", SortKeys: false}
  26. // Pretty converts the input json into a more human readable format where each
  27. // element is on it's own line with clear indentation.
  28. func Pretty(json []byte) []byte { return PrettyOptions(json, nil) }
  29. // PrettyOptions is like Pretty but with customized options.
  30. func PrettyOptions(json []byte, opts *Options) []byte {
  31. if opts == nil {
  32. opts = DefaultOptions
  33. }
  34. buf := make([]byte, 0, len(json))
  35. if len(opts.Prefix) != 0 {
  36. buf = append(buf, opts.Prefix...)
  37. }
  38. buf, _, _, _ = appendPrettyAny(buf, json, 0, true,
  39. opts.Width, opts.Prefix, opts.Indent, opts.SortKeys,
  40. 0, 0, -1)
  41. if len(buf) > 0 {
  42. buf = append(buf, '\n')
  43. }
  44. return buf
  45. }
  46. // Ugly removes insignificant space characters from the input json byte slice
  47. // and returns the compacted result.
  48. func Ugly(json []byte) []byte {
  49. buf := make([]byte, 0, len(json))
  50. return ugly(buf, json)
  51. }
  52. // UglyInPlace removes insignificant space characters from the input json
  53. // byte slice and returns the compacted result. This method reuses the
  54. // input json buffer to avoid allocations. Do not use the original bytes
  55. // slice upon return.
  56. func UglyInPlace(json []byte) []byte { return ugly(json, json) }
  57. func ugly(dst, src []byte) []byte {
  58. dst = dst[:0]
  59. for i := 0; i < len(src); i++ {
  60. if src[i] > ' ' {
  61. dst = append(dst, src[i])
  62. if src[i] == '"' {
  63. for i = i + 1; i < len(src); i++ {
  64. dst = append(dst, src[i])
  65. if src[i] == '"' {
  66. j := i - 1
  67. for ; ; j-- {
  68. if src[j] != '\\' {
  69. break
  70. }
  71. }
  72. if (j-i)%2 != 0 {
  73. break
  74. }
  75. }
  76. }
  77. }
  78. }
  79. }
  80. return dst
  81. }
  82. func isNaNOrInf(src []byte) bool {
  83. return src[0] == 'i' || // Inf
  84. src[0] == 'I' || // inf
  85. src[0] == '+' || // +Inf
  86. src[0] == 'N' || // Nan
  87. (src[0] == 'n' && len(src) > 1 && src[1] != 'u') // nan
  88. }
  89. func appendPrettyAny(buf, json []byte, i int, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) {
  90. for ; i < len(json); i++ {
  91. if json[i] <= ' ' {
  92. continue
  93. }
  94. if json[i] == '"' {
  95. return appendPrettyString(buf, json, i, nl)
  96. }
  97. if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' || isNaNOrInf(json[i:]) {
  98. return appendPrettyNumber(buf, json, i, nl)
  99. }
  100. if json[i] == '{' {
  101. return appendPrettyObject(buf, json, i, '{', '}', pretty, width, prefix, indent, sortkeys, tabs, nl, max)
  102. }
  103. if json[i] == '[' {
  104. return appendPrettyObject(buf, json, i, '[', ']', pretty, width, prefix, indent, sortkeys, tabs, nl, max)
  105. }
  106. switch json[i] {
  107. case 't':
  108. return append(buf, 't', 'r', 'u', 'e'), i + 4, nl, true
  109. case 'f':
  110. return append(buf, 'f', 'a', 'l', 's', 'e'), i + 5, nl, true
  111. case 'n':
  112. return append(buf, 'n', 'u', 'l', 'l'), i + 4, nl, true
  113. }
  114. }
  115. return buf, i, nl, true
  116. }
  117. type pair struct {
  118. kstart, kend int
  119. vstart, vend int
  120. }
  121. type byKeyVal struct {
  122. sorted bool
  123. json []byte
  124. buf []byte
  125. pairs []pair
  126. }
  127. func (arr *byKeyVal) Len() int {
  128. return len(arr.pairs)
  129. }
  130. func (arr *byKeyVal) Less(i, j int) bool {
  131. if arr.isLess(i, j, byKey) {
  132. return true
  133. }
  134. if arr.isLess(j, i, byKey) {
  135. return false
  136. }
  137. return arr.isLess(i, j, byVal)
  138. }
  139. func (arr *byKeyVal) Swap(i, j int) {
  140. arr.pairs[i], arr.pairs[j] = arr.pairs[j], arr.pairs[i]
  141. arr.sorted = true
  142. }
  143. type byKind int
  144. const (
  145. byKey byKind = 0
  146. byVal byKind = 1
  147. )
  148. type jtype int
  149. const (
  150. jnull jtype = iota
  151. jfalse
  152. jnumber
  153. jstring
  154. jtrue
  155. jjson
  156. )
  157. func getjtype(v []byte) jtype {
  158. if len(v) == 0 {
  159. return jnull
  160. }
  161. switch v[0] {
  162. case '"':
  163. return jstring
  164. case 'f':
  165. return jfalse
  166. case 't':
  167. return jtrue
  168. case 'n':
  169. return jnull
  170. case '[', '{':
  171. return jjson
  172. default:
  173. return jnumber
  174. }
  175. }
  176. func (arr *byKeyVal) isLess(i, j int, kind byKind) bool {
  177. k1 := arr.json[arr.pairs[i].kstart:arr.pairs[i].kend]
  178. k2 := arr.json[arr.pairs[j].kstart:arr.pairs[j].kend]
  179. var v1, v2 []byte
  180. if kind == byKey {
  181. v1 = k1
  182. v2 = k2
  183. } else {
  184. v1 = bytes.TrimSpace(arr.buf[arr.pairs[i].vstart:arr.pairs[i].vend])
  185. v2 = bytes.TrimSpace(arr.buf[arr.pairs[j].vstart:arr.pairs[j].vend])
  186. if len(v1) >= len(k1)+1 {
  187. v1 = bytes.TrimSpace(v1[len(k1)+1:])
  188. }
  189. if len(v2) >= len(k2)+1 {
  190. v2 = bytes.TrimSpace(v2[len(k2)+1:])
  191. }
  192. }
  193. t1 := getjtype(v1)
  194. t2 := getjtype(v2)
  195. if t1 < t2 {
  196. return true
  197. }
  198. if t1 > t2 {
  199. return false
  200. }
  201. if t1 == jstring {
  202. s1 := parsestr(v1)
  203. s2 := parsestr(v2)
  204. return yu_fast.B2S(s1) < yu_fast.B2S(s2)
  205. }
  206. if t1 == jnumber {
  207. n1, _ := strconv.ParseFloat(yu_fast.B2S(v1), 64)
  208. n2, _ := strconv.ParseFloat(yu_fast.B2S(v2), 64)
  209. return n1 < n2
  210. }
  211. return yu_fast.B2S(v1) < yu_fast.B2S(v2)
  212. }
  213. func parsestr(s []byte) []byte {
  214. for i := 1; i < len(s); i++ {
  215. if s[i] == '\\' {
  216. var str string
  217. json.Unmarshal(s, &str)
  218. return []byte(str)
  219. }
  220. if s[i] == '"' {
  221. return s[1:i]
  222. }
  223. }
  224. return nil
  225. }
  226. func appendPrettyObject(buf, json []byte, i int, open, close byte, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) {
  227. var ok bool
  228. if width > 0 {
  229. if pretty && open == '[' && max == -1 {
  230. // here we try to create a single line array
  231. max := width - (len(buf) - nl)
  232. if max > 3 {
  233. s1, s2 := len(buf), i
  234. buf, i, _, ok = appendPrettyObject(buf, json, i, '[', ']', false, width, prefix, "", sortkeys, 0, 0, max)
  235. if ok && len(buf)-s1 <= max {
  236. return buf, i, nl, true
  237. }
  238. buf = buf[:s1]
  239. i = s2
  240. }
  241. } else if max != -1 && open == '{' {
  242. return buf, i, nl, false
  243. }
  244. }
  245. buf = append(buf, open)
  246. i++
  247. var pairs []pair
  248. if open == '{' && sortkeys {
  249. pairs = make([]pair, 0, 8)
  250. }
  251. var n int
  252. for ; i < len(json); i++ {
  253. if json[i] <= ' ' {
  254. continue
  255. }
  256. if json[i] == close {
  257. if pretty {
  258. if open == '{' && sortkeys {
  259. buf = sortPairs(json, buf, pairs)
  260. }
  261. if n > 0 {
  262. nl = len(buf)
  263. if buf[nl-1] == ' ' {
  264. buf[nl-1] = '\n'
  265. } else {
  266. buf = append(buf, '\n')
  267. }
  268. }
  269. if buf[len(buf)-1] != open {
  270. buf = appendTabs(buf, prefix, indent, tabs)
  271. }
  272. }
  273. buf = append(buf, close)
  274. return buf, i + 1, nl, open != '{'
  275. }
  276. if open == '[' || json[i] == '"' {
  277. if n > 0 {
  278. buf = append(buf, ',')
  279. if width != -1 && open == '[' {
  280. buf = append(buf, ' ')
  281. }
  282. }
  283. var p pair
  284. if pretty {
  285. nl = len(buf)
  286. if buf[nl-1] == ' ' {
  287. buf[nl-1] = '\n'
  288. } else {
  289. buf = append(buf, '\n')
  290. }
  291. if open == '{' && sortkeys {
  292. p.kstart = i
  293. p.vstart = len(buf)
  294. }
  295. buf = appendTabs(buf, prefix, indent, tabs+1)
  296. }
  297. if open == '{' {
  298. buf, i, nl, _ = appendPrettyString(buf, json, i, nl)
  299. if sortkeys {
  300. p.kend = i
  301. }
  302. buf = append(buf, ':')
  303. if pretty {
  304. buf = append(buf, ' ')
  305. }
  306. }
  307. buf, i, nl, ok = appendPrettyAny(buf, json, i, pretty, width, prefix, indent, sortkeys, tabs+1, nl, max)
  308. if max != -1 && !ok {
  309. return buf, i, nl, false
  310. }
  311. if pretty && open == '{' && sortkeys {
  312. p.vend = len(buf)
  313. if p.kstart > p.kend || p.vstart > p.vend {
  314. // bad data. disable sorting
  315. sortkeys = false
  316. } else {
  317. pairs = append(pairs, p)
  318. }
  319. }
  320. i--
  321. n++
  322. }
  323. }
  324. return buf, i, nl, open != '{'
  325. }
  326. func sortPairs(json, buf []byte, pairs []pair) []byte {
  327. if len(pairs) == 0 {
  328. return buf
  329. }
  330. vstart := pairs[0].vstart
  331. vend := pairs[len(pairs)-1].vend
  332. arr := byKeyVal{false, json, buf, pairs}
  333. sort.Stable(&arr)
  334. if !arr.sorted {
  335. return buf
  336. }
  337. nbuf := make([]byte, 0, vend-vstart)
  338. for i, p := range pairs {
  339. nbuf = append(nbuf, buf[p.vstart:p.vend]...)
  340. if i < len(pairs)-1 {
  341. nbuf = append(nbuf, ',')
  342. nbuf = append(nbuf, '\n')
  343. }
  344. }
  345. return append(buf[:vstart], nbuf...)
  346. }
  347. func appendPrettyString(buf, json []byte, i, nl int) ([]byte, int, int, bool) {
  348. s := i
  349. i++
  350. for ; i < len(json); i++ {
  351. if json[i] == '"' {
  352. var sc int
  353. for j := i - 1; j > s; j-- {
  354. if json[j] == '\\' {
  355. sc++
  356. } else {
  357. break
  358. }
  359. }
  360. if sc%2 == 1 {
  361. continue
  362. }
  363. i++
  364. break
  365. }
  366. }
  367. return append(buf, json[s:i]...), i, nl, true
  368. }
  369. func appendPrettyNumber(buf, json []byte, i, nl int) ([]byte, int, int, bool) {
  370. s := i
  371. i++
  372. for ; i < len(json); i++ {
  373. if json[i] <= ' ' || json[i] == ',' || json[i] == ':' || json[i] == ']' || json[i] == '}' {
  374. break
  375. }
  376. }
  377. return append(buf, json[s:i]...), i, nl, true
  378. }
  379. func appendTabs(buf []byte, prefix, indent string, tabs int) []byte {
  380. if len(prefix) != 0 {
  381. buf = append(buf, prefix...)
  382. }
  383. if len(indent) == 2 && indent[0] == ' ' && indent[1] == ' ' {
  384. for i := 0; i < tabs; i++ {
  385. buf = append(buf, ' ', ' ')
  386. }
  387. } else {
  388. for i := 0; i < tabs; i++ {
  389. buf = append(buf, indent...)
  390. }
  391. }
  392. return buf
  393. }
  394. // Style is the color style
  395. type Style struct {
  396. Key, String, Number [2]string
  397. True, False, Null [2]string
  398. Escape [2]string
  399. Brackets [2]string
  400. Append func(dst []byte, c byte) []byte
  401. }
  402. func hexp(p byte) byte {
  403. switch {
  404. case p < 10:
  405. return p + '0'
  406. default:
  407. return (p - 10) + 'a'
  408. }
  409. }
  410. // TerminalStyle is for terminals
  411. var TerminalStyle *Style
  412. func init() {
  413. TerminalStyle = &Style{
  414. Key: [2]string{"\x1B[1m\x1B[94m", "\x1B[0m"},
  415. String: [2]string{"\x1B[32m", "\x1B[0m"},
  416. Number: [2]string{"\x1B[33m", "\x1B[0m"},
  417. True: [2]string{"\x1B[36m", "\x1B[0m"},
  418. False: [2]string{"\x1B[36m", "\x1B[0m"},
  419. Null: [2]string{"\x1B[2m", "\x1B[0m"},
  420. Escape: [2]string{"\x1B[35m", "\x1B[0m"},
  421. Brackets: [2]string{"\x1B[1m", "\x1B[0m"},
  422. Append: func(dst []byte, c byte) []byte {
  423. if c < ' ' && (c != '\r' && c != '\n' && c != '\t' && c != '\v') {
  424. dst = append(dst, "\\u00"...)
  425. dst = append(dst, hexp((c>>4)&0xF))
  426. return append(dst, hexp((c)&0xF))
  427. }
  428. return append(dst, c)
  429. },
  430. }
  431. }
  432. // Color will colorize the json. The style parma is used for customizing
  433. // the colors. Passing nil to the style param will use the default
  434. // TerminalStyle.
  435. func Color(src []byte, style *Style) []byte {
  436. if style == nil {
  437. style = TerminalStyle
  438. }
  439. apnd := style.Append
  440. if apnd == nil {
  441. apnd = func(dst []byte, c byte) []byte {
  442. return append(dst, c)
  443. }
  444. }
  445. type stackt struct {
  446. kind byte
  447. key bool
  448. }
  449. var dst []byte
  450. var stack []stackt
  451. for i := 0; i < len(src); i++ {
  452. if src[i] == '"' {
  453. key := len(stack) > 0 && stack[len(stack)-1].key
  454. if key {
  455. dst = append(dst, style.Key[0]...)
  456. } else {
  457. dst = append(dst, style.String[0]...)
  458. }
  459. dst = apnd(dst, '"')
  460. esc := false
  461. uesc := 0
  462. for i = i + 1; i < len(src); i++ {
  463. if src[i] == '\\' {
  464. if key {
  465. dst = append(dst, style.Key[1]...)
  466. } else {
  467. dst = append(dst, style.String[1]...)
  468. }
  469. dst = append(dst, style.Escape[0]...)
  470. dst = apnd(dst, src[i])
  471. esc = true
  472. if i+1 < len(src) && src[i+1] == 'u' {
  473. uesc = 5
  474. } else {
  475. uesc = 1
  476. }
  477. } else if esc {
  478. dst = apnd(dst, src[i])
  479. if uesc == 1 {
  480. esc = false
  481. dst = append(dst, style.Escape[1]...)
  482. if key {
  483. dst = append(dst, style.Key[0]...)
  484. } else {
  485. dst = append(dst, style.String[0]...)
  486. }
  487. } else {
  488. uesc--
  489. }
  490. } else {
  491. dst = apnd(dst, src[i])
  492. }
  493. if src[i] == '"' {
  494. j := i - 1
  495. for ; ; j-- {
  496. if src[j] != '\\' {
  497. break
  498. }
  499. }
  500. if (j-i)%2 != 0 {
  501. break
  502. }
  503. }
  504. }
  505. if esc {
  506. dst = append(dst, style.Escape[1]...)
  507. } else if key {
  508. dst = append(dst, style.Key[1]...)
  509. } else {
  510. dst = append(dst, style.String[1]...)
  511. }
  512. } else if src[i] == '{' || src[i] == '[' {
  513. stack = append(stack, stackt{src[i], src[i] == '{'})
  514. dst = append(dst, style.Brackets[0]...)
  515. dst = apnd(dst, src[i])
  516. dst = append(dst, style.Brackets[1]...)
  517. } else if (src[i] == '}' || src[i] == ']') && len(stack) > 0 {
  518. stack = stack[:len(stack)-1]
  519. dst = append(dst, style.Brackets[0]...)
  520. dst = apnd(dst, src[i])
  521. dst = append(dst, style.Brackets[1]...)
  522. } else if (src[i] == ':' || src[i] == ',') && len(stack) > 0 && stack[len(stack)-1].kind == '{' {
  523. stack[len(stack)-1].key = !stack[len(stack)-1].key
  524. dst = append(dst, style.Brackets[0]...)
  525. dst = apnd(dst, src[i])
  526. dst = append(dst, style.Brackets[1]...)
  527. } else {
  528. var kind byte
  529. if (src[i] >= '0' && src[i] <= '9') || src[i] == '-' || isNaNOrInf(src[i:]) {
  530. kind = '0'
  531. dst = append(dst, style.Number[0]...)
  532. } else if src[i] == 't' {
  533. kind = 't'
  534. dst = append(dst, style.True[0]...)
  535. } else if src[i] == 'f' {
  536. kind = 'f'
  537. dst = append(dst, style.False[0]...)
  538. } else if src[i] == 'n' {
  539. kind = 'n'
  540. dst = append(dst, style.Null[0]...)
  541. } else {
  542. dst = apnd(dst, src[i])
  543. }
  544. if kind != 0 {
  545. for ; i < len(src); i++ {
  546. if src[i] <= ' ' || src[i] == ',' || src[i] == ':' || src[i] == ']' || src[i] == '}' {
  547. i--
  548. break
  549. }
  550. dst = apnd(dst, src[i])
  551. }
  552. if kind == '0' {
  553. dst = append(dst, style.Number[1]...)
  554. } else if kind == 't' {
  555. dst = append(dst, style.True[1]...)
  556. } else if kind == 'f' {
  557. dst = append(dst, style.False[1]...)
  558. } else if kind == 'n' {
  559. dst = append(dst, style.Null[1]...)
  560. }
  561. }
  562. }
  563. }
  564. return dst
  565. }
  566. // Spec strips out comments and trailing commas and convert the input to a
  567. // valid JSON per the official spec: https://tools.ietf.org/html/rfc8259
  568. //
  569. // The resulting JSON will always be the same length as the input and it will
  570. // include all of the same line breaks at matching offsets. This is to ensure
  571. // the result can be later processed by a external parser and that that
  572. // parser will report messages or errors with the correct offsets.
  573. func Spec(src []byte) []byte {
  574. return spec(src, nil)
  575. }
  576. // SpecInPlace is the same as Spec, but this method reuses the input json
  577. // buffer to avoid allocations. Do not use the original bytes slice upon return.
  578. func SpecInPlace(src []byte) []byte {
  579. return spec(src, src)
  580. }
  581. func spec(src, dst []byte) []byte {
  582. dst = dst[:0]
  583. for i := 0; i < len(src); i++ {
  584. if src[i] == '/' {
  585. if i < len(src)-1 {
  586. if src[i+1] == '/' {
  587. dst = append(dst, ' ', ' ')
  588. i += 2
  589. for ; i < len(src); i++ {
  590. if src[i] == '\n' {
  591. dst = append(dst, '\n')
  592. break
  593. } else if src[i] == '\t' || src[i] == '\r' {
  594. dst = append(dst, src[i])
  595. } else {
  596. dst = append(dst, ' ')
  597. }
  598. }
  599. continue
  600. }
  601. if src[i+1] == '*' {
  602. dst = append(dst, ' ', ' ')
  603. i += 2
  604. for ; i < len(src)-1; i++ {
  605. if src[i] == '*' && src[i+1] == '/' {
  606. dst = append(dst, ' ', ' ')
  607. i++
  608. break
  609. } else if src[i] == '\n' || src[i] == '\t' ||
  610. src[i] == '\r' {
  611. dst = append(dst, src[i])
  612. } else {
  613. dst = append(dst, ' ')
  614. }
  615. }
  616. continue
  617. }
  618. }
  619. }
  620. dst = append(dst, src[i])
  621. if src[i] == '"' {
  622. for i = i + 1; i < len(src); i++ {
  623. dst = append(dst, src[i])
  624. if src[i] == '"' {
  625. j := i - 1
  626. for ; ; j-- {
  627. if src[j] != '\\' {
  628. break
  629. }
  630. }
  631. if (j-i)%2 != 0 {
  632. break
  633. }
  634. }
  635. }
  636. } else if src[i] == '}' || src[i] == ']' {
  637. for j := len(dst) - 2; j >= 0; j-- {
  638. if dst[j] <= ' ' {
  639. continue
  640. }
  641. if dst[j] == ',' {
  642. dst[j] = ' '
  643. }
  644. break
  645. }
  646. }
  647. }
  648. return dst
  649. }