You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

table.go 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. package org
  2. import (
  3. "regexp"
  4. "strconv"
  5. "strings"
  6. "unicode/utf8"
  7. )
  8. type Table struct {
  9. Rows []Row
  10. ColumnInfos []ColumnInfo
  11. }
  12. type Row struct {
  13. Columns []Column
  14. IsSpecial bool
  15. }
  16. type Column struct {
  17. Children []Node
  18. *ColumnInfo
  19. }
  20. type ColumnInfo struct {
  21. Align string
  22. Len int
  23. }
  24. var tableSeparatorRegexp = regexp.MustCompile(`^(\s*)(\|[+-|]*)\s*$`)
  25. var tableRowRegexp = regexp.MustCompile(`^(\s*)(\|.*)`)
  26. var columnAlignRegexp = regexp.MustCompile(`^<(l|c|r)>$`)
  27. func lexTable(line string) (token, bool) {
  28. if m := tableSeparatorRegexp.FindStringSubmatch(line); m != nil {
  29. return token{"tableSeparator", len(m[1]), m[2], m}, true
  30. } else if m := tableRowRegexp.FindStringSubmatch(line); m != nil {
  31. return token{"tableRow", len(m[1]), m[2], m}, true
  32. }
  33. return nilToken, false
  34. }
  35. func (d *Document) parseTable(i int, parentStop stopFn) (int, Node) {
  36. rawRows, start := [][]string{}, i
  37. for ; !parentStop(d, i); i++ {
  38. if t := d.tokens[i]; t.kind == "tableRow" {
  39. rawRow := strings.FieldsFunc(d.tokens[i].content, func(r rune) bool { return r == '|' })
  40. for i := range rawRow {
  41. rawRow[i] = strings.TrimSpace(rawRow[i])
  42. }
  43. rawRows = append(rawRows, rawRow)
  44. } else if t.kind == "tableSeparator" {
  45. rawRows = append(rawRows, nil)
  46. } else {
  47. break
  48. }
  49. }
  50. table := Table{nil, getColumnInfos(rawRows)}
  51. for _, rawColumns := range rawRows {
  52. row := Row{nil, isSpecialRow(rawColumns)}
  53. if len(rawColumns) != 0 {
  54. for i := range table.ColumnInfos {
  55. column := Column{nil, &table.ColumnInfos[i]}
  56. if i < len(rawColumns) {
  57. column.Children = d.parseInline(rawColumns[i])
  58. }
  59. row.Columns = append(row.Columns, column)
  60. }
  61. }
  62. table.Rows = append(table.Rows, row)
  63. }
  64. return i - start, table
  65. }
  66. func getColumnInfos(rows [][]string) []ColumnInfo {
  67. columnCount := 0
  68. for _, columns := range rows {
  69. if n := len(columns); n > columnCount {
  70. columnCount = n
  71. }
  72. }
  73. columnInfos := make([]ColumnInfo, columnCount)
  74. for i := 0; i < columnCount; i++ {
  75. countNumeric, countNonNumeric := 0, 0
  76. for _, columns := range rows {
  77. if i >= len(columns) {
  78. continue
  79. }
  80. if n := utf8.RuneCountInString(columns[i]); n > columnInfos[i].Len {
  81. columnInfos[i].Len = n
  82. }
  83. if m := columnAlignRegexp.FindStringSubmatch(columns[i]); m != nil && isSpecialRow(columns) {
  84. switch m[1] {
  85. case "l":
  86. columnInfos[i].Align = "left"
  87. case "c":
  88. columnInfos[i].Align = "center"
  89. case "r":
  90. columnInfos[i].Align = "right"
  91. }
  92. } else if _, err := strconv.ParseFloat(columns[i], 32); err == nil {
  93. countNumeric++
  94. } else if strings.TrimSpace(columns[i]) != "" {
  95. countNonNumeric++
  96. }
  97. }
  98. if columnInfos[i].Align == "" && countNumeric >= countNonNumeric {
  99. columnInfos[i].Align = "right"
  100. }
  101. }
  102. return columnInfos
  103. }
  104. func isSpecialRow(rawColumns []string) bool {
  105. isAlignRow := true
  106. for _, rawColumn := range rawColumns {
  107. if !columnAlignRegexp.MatchString(rawColumn) && rawColumn != "" {
  108. isAlignRow = false
  109. }
  110. }
  111. return isAlignRow
  112. }
  113. func (n Table) String() string { return orgWriter.WriteNodesAsString(n) }