tablealigner.go (4032B)
1 package main 2 3 import ( 4 "fmt" 5 "unicode/utf8" 6 "os" 7 "strings" 8 "regexp" 9 "os/exec" 10 ) 11 12 func Print(str string) { 13 cmd := exec.Command("notify-send", str) 14 cmd.Run() 15 } 16 17 // This is definitely not a good solution to wide characters 18 var EMOJIS []rune = []rune{'❌', '✅', '❔', '❓', '📅', '🚗', '🌙', '⏳'} 19 20 func isEmoji(r rune) bool { 21 for _, emoji := range EMOJIS { 22 if r == emoji { 23 return true 24 } 25 } 26 return false 27 } 28 29 func properLen(input string) int { 30 var noEndSpaces string = strings.TrimRight(input, " ") 31 var length int = utf8.RuneCountInString(noEndSpaces) + 1 32 33 for _, r := range noEndSpaces { 34 if isEmoji(r) { 35 length++ 36 } 37 } 38 return length 39 } 40 41 func readFile(filename string) []string { 42 dat, err := os.ReadFile(filename) 43 if err != nil { 44 panic(err) 45 } 46 47 return strings.Split(string(dat), "\n") 48 } 49 50 func testForAlignRow(line string) bool { 51 if len(line) < 1 { 52 return false 53 } 54 55 if line[0] != '|' || line[len(line)-1] != '|' { 56 return false 57 } 58 split := strings.Split(line, "|") 59 var isCorrect = regexp.MustCompile("^(-*:* *)*$").MatchString 60 for _, x := range split { 61 if !isCorrect(x) { 62 return false 63 } 64 } 65 66 return true 67 } 68 69 func testForEnd(line string) bool { 70 if len(line) == 0 || line[0] != '|' { 71 return true 72 } 73 return false 74 } 75 76 func findTables(input []string) [][]int { 77 var tables [][]int 78 79 var inTable bool = false 80 81 for i, line := range input { 82 if !inTable && testForAlignRow(line) { 83 tables = append(tables, []int{i-1}) 84 inTable = true 85 } else if inTable && testForEnd(line) { 86 tables[len(tables)-1] = append(tables[len(tables)-1], i-1) 87 inTable = false 88 } 89 } 90 91 return tables 92 } 93 94 func calcLengths(thisTable []string) []int { 95 numCells := strings.Count(thisTable[0], "|") - 1 96 97 if numCells < 1 { 98 os.Exit(1) 99 } 100 101 var columnLengths []int = make([]int, numCells) 102 103 for i, line := range thisTable { 104 if i == 1 { 105 continue 106 } 107 cells := strings.Split(line, "|") 108 cells = cells[1:len(cells)-1] 109 110 for j, cell := range cells { 111 var cellLen int = properLen(cell) 112 if cellLen > columnLengths[j] { 113 columnLengths[j] = cellLen 114 } 115 } 116 } 117 return columnLengths 118 } 119 120 func alignTable(table []int, data[]string) []string { 121 thisTable := data[table[0]:table[1]+1] 122 columnLengths := calcLengths(thisTable) 123 124 for i:=0; i<len(thisTable); i++ { 125 cells := strings.Split(thisTable[i], "|") 126 cells = cells[1:len(cells)-1] 127 128 newCells := []string{} 129 for j, cell := range cells { 130 cell = strings.TrimRight(cell, " ") + " " 131 var cellLen int = properLen(cell) 132 if cellLen != columnLengths[j] { 133 var paddedCell string = padCell(cell, cellLen, i, j, columnLengths) 134 newCells = append(newCells, paddedCell) 135 136 } else { 137 newCells = append(newCells, cell) 138 } 139 } 140 // Build new row string 141 var newCellString string = "|" 142 for _, cell := range newCells { 143 newCellString = newCellString + cell + "|" 144 } 145 thisTable[i] = newCellString 146 } 147 return data 148 } 149 150 func padCell(cell string, cellLen int, i int, j int, columnLengths []int) string { 151 var paddedCell string 152 if i == 1 { 153 paddedCell = " :" + strings.Repeat("-", columnLengths[j]-4) + ": " 154 } else { 155 paddedCell = cell + strings.Repeat(" ", columnLengths[j]-cellLen) 156 } 157 return paddedCell 158 } 159 160 func alignTables(tables [][]int, data []string) []string { 161 for _, table := range tables { 162 data = alignTable(table, data) 163 } 164 return data 165 } 166 167 func saveToFile(data []string, filename string) { 168 if len(data[len(data)-1]) == 0 { 169 data = data[:len(data)-1] 170 } 171 var toSave string 172 for _, line := range data { 173 toSave = toSave + line + "\n" 174 } 175 err := os.WriteFile(filename, []byte(toSave), 0666) 176 if err != nil { 177 panic(err) 178 } 179 } 180 181 func main() { 182 if len(os.Args) != 3 { 183 fmt.Println("Usage: \n tablealigner input.md output.md") 184 panic("wrong args") 185 } 186 187 var inputFile string = os.Args[1] 188 var outputFile string = os.Args[2] 189 if os.Args[1] == "-s" { 190 inputFile = os.Args[2] 191 outputFile = os.Args[2] 192 } 193 194 data := readFile(inputFile) 195 196 tables := findTables(data) 197 data = alignTables(tables, data) 198 199 saveToFile(data, outputFile) 200 }