-
Notifications
You must be signed in to change notification settings - Fork 1
/
justify.go
161 lines (134 loc) · 3.91 KB
/
justify.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package buffer
import (
"fmt"
"regexp"
"strings"
"sync"
)
func (buffer *Buffer) Justify(startRow, endRow, lineLen int, comStrs []string) error {
// Get indents and remainders.
subBuffer := buffer.InclSlice(startRow, endRow)
indents, remainders, err := subBuffer.getIndentsRemainders(comStrs)
if err != nil {
return err
}
// Find paragraphs.
paragraphs, err := findParagraphs(indents, remainders)
if err != nil {
return fmt.Errorf("error dividing text into paragraphs: %s", err)
}
// Justify each paragraph and append to a buffer.
fBuffer := MakeBuffer([]string{})
for i, p := range paragraphs {
start := p
end := subBuffer.Length() - 1
if i+1 < len(paragraphs) {
end = paragraphs[i+1] - 1
}
bigStr := strings.Join(remainders[start:end+1], " ")
splitBuf := MakeSplitBuffer(bigStr, lineLen-len(indents[p]))
// Put indentation back
splitBuf.IndentByStr(indents[p], 0, -1)
fBuffer.Append(splitBuf.Lines()...)
}
// Replace the original buffer lines with the justified ones.
buffer.ReplaceLines(fBuffer.Lines(), startRow, endRow)
return nil
}
func (buffer *Buffer) justify(lineLen int) *Buffer {
// Make one long string out of the remainder of the lines.
bigStr := buffer.ToString(" ")
// Justify.
splitBuf := MakeSplitBuffer(bigStr, lineLen)
return &splitBuf
}
func (buffer *Buffer) getIndentsRemainders(comStrs []string) (indents, remainders []string, err error) {
indents = make([]string, buffer.Length())
remainders = make([]string, buffer.Length())
// Construct the regexp to find indentation (including comments strings).
pattern := "^[ \t]*(//|#)*[ \t]*"
re, err := regexp.Compile(pattern)
if err != nil {
return indents, remainders, err
}
// Separete lines into indent + remainder.
for i, line := range buffer.Lines() {
lineStr := line.ToString()
indent := re.FindString(lineStr)
indents[i] = indent
remainders[i] = lineStr[len(indent):]
}
return indents, remainders, nil
}
// Separate lines of text into paragraphs based on indentation.
// Return a list of paragraph starts.
func findParagraphs(indents []string, remainders []string) ([]int, error) {
paragraphs := []int{0}
// Sanity check
if len(indents) != len(remainders) {
return paragraphs, fmt.Errorf("indents and remainders are different lengths")
}
// Trivial case
if len(indents) == 0 {
return paragraphs, nil
}
lastIndent := indents[0]
lastRemainder := remainders[0]
for i, indent := range indents {
remainder := remainders[i]
if indent != lastIndent {
// If indentation changes, then start a new paragraph.
paragraphs = append(paragraphs, i)
} else if remainder != "" && lastRemainder == "" {
paragraphs = append(paragraphs, i)
} else if remainder == "" && lastRemainder != "" {
paragraphs = append(paragraphs, i)
}
lastRemainder = remainder
lastIndent = indent
}
return paragraphs, nil
}
func (buffer *Buffer) getParagraphs() [][]int {
offsets := [][]int{}
start := 0
prevIsBlank := true
for i, line := range buffer.Lines() {
// Blank line after non blanks.
if line.Length() == 0 && !prevIsBlank && i > 0 {
offsets = append(offsets, []int{start, i - 1})
}
// Non blank line after a blank.
if line.Length() > 0 && prevIsBlank {
start = i
}
prevIsBlank = line.Length() == 0
}
if !prevIsBlank {
offsets = append(offsets, []int{start, buffer.Length() - 1})
}
return offsets
}
// MakeSplitBuffer creates a buffer from a long string by splitting
// the string at a certain length.
func MakeSplitBuffer(bigString string, lineLen int) Buffer {
words := strings.Fields(bigString)
if len(words) == 0 {
return MakeBuffer([]string{""})
}
lines := []Line{}
lineStr := words[0]
for _, word := range words[1:] {
if lineLen > 0 && len(lineStr)+len(word) >= lineLen {
lines = append(lines, MakeLine(lineStr))
lineStr = word
} else {
lineStr += " " + word
}
}
lines = append(lines, MakeLine(lineStr))
return Buffer{
lines: lines,
mutex: &sync.Mutex{},
}
}