pretty.go 15 KB

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