Skip to content

Commit

Permalink
Add ranged annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
meyermarcel committed Jun 26, 2024
1 parent e909629 commit a058a27
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 63 deletions.
42 changes: 27 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

# Annot

Annotate a string line with arrows leading to a description.
Annotate a string line leading to a description.

```
The quick brown fox jumps over the lazy dog
↑ ↑ ↑
│ └─ adjective └─ adjective
└─ adjective
The greatest enemy of knowledge is not ignorance, it is the illusion of knowledge.
↑ └──┬───┘ └───┬───┘ ↑
│ └─ adjective │ └─ comma
│ │
└─ article └─ facts, information, and skills acquired
through experience or education;
the theoretical or practical understanding
of a subject.
```

## Installation
Expand All @@ -30,21 +33,30 @@ import (
)

func main() {
fmt.Println("The quick brown fox jumps over the lazy dog")
fmt.Println("The greatest enemy of knowledge is not ignorance, it is the illusion of knowledge.")
fmt.Println(annot.String(
&annot.Annot{Col: 6, Lines: []string{"adjective"}},
&annot.Annot{Col: 12, Lines: []string{"adjective"}},
&annot.Annot{Col: 36, Lines: []string{"adjective"}},
&annot.Annot{Col: 1, Lines: []string{"article"}},
&annot.Annot{Col: 4, ColEnd: 11, Lines: []string{"adjective"}},
&annot.Annot{Col: 22, ColEnd: 30, Lines: []string{
"facts, information, and skills acquired",
"through experience or education;",
"the theoretical or practical understanding",
"of a subject.",
}},
&annot.Annot{Col: 48, Lines: []string{"comma"}},
))
}
```

Output:

```
The quick brown fox jumps over the lazy dog
↑ ↑ ↑
│ └─ adjective └─ adjective
└─ adjective
The greatest enemy of knowledge is not ignorance, it is the illusion of knowledge.
↑ └──┬───┘ └───┬───┘ ↑
│ └─ adjective │ └─ comma
│ │
└─ article └─ facts, information, and skills acquired
through experience or education;
the theoretical or practical understanding
of a subject.
```
68 changes: 55 additions & 13 deletions annot.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@ type Annot struct {
// E.g. 0 draws an arrow to the first character in a line.
Col int

// ColEnd needs to be higher than Col. If ColEnd is set a
// range is annotated.
ColEnd int

// Lines is the text of the annotation represented in one or more lines.
Lines []string

pipeColPos int

row int
lines []*line
pipeLeadingSpaces []int
Expand Down Expand Up @@ -109,7 +115,18 @@ func Write(w io.Writer, annots ...*Annot) error {
return a.Col - b.Col
})

for _, a := range annots {
for aIdx, a := range annots {
if a.ColEnd != 0 {
if a.Col >= a.ColEnd {
return newColExceedsColEndError(aIdx+1, a.Col, a.ColEnd)
}
a.pipeColPos = (a.Col + a.ColEnd) / 2
} else {
a.pipeColPos = a.Col
}
if aIdx > 0 && annots[aIdx-1].ColEnd != 0 && annots[aIdx-1].ColEnd >= a.Col {
return newOverlapError(annots[aIdx-1].ColEnd, aIdx, a.Col)
}
a.createLines()
}

Expand All @@ -127,13 +144,13 @@ func Write(w io.Writer, annots ...*Annot) error {
func (a *Annot) createLines() {
if len(a.Lines) == 0 {
a.lines = make([]*line, 1)
a.lines[0] = &line{}
a.lines[0] = &line{leadingSpaces: a.pipeColPos}
return
}

a.lines = make([]*line, len(a.Lines))
for i := range a.Lines {
leadingSpaces := a.Col
leadingSpaces := a.pipeColPos
if i > 0 {
leadingSpaces += 3
}
Expand Down Expand Up @@ -166,9 +183,9 @@ func setSpace(rowBefore int, a *Annot, rightAnnots []*Annot) {
closestA, s := closestAnnot(rowBefore, rightAnnots, 0)
switch s {
case above:
closestA.pipeLeadingSpaces[rowBefore] = closestA.Col + s.colPosShift() - a.Col - 1
closestA.pipeLeadingSpaces[rowBefore] = closestA.pipeColPos + s.colPosShift() - a.pipeColPos - 1
case lineOne, lineTwo, linesAfterSecond:
closestA.lines[rowBefore-closestA.row].leadingSpaces = closestA.Col + s.colPosShift() - a.Col - 1
closestA.lines[rowBefore-closestA.row].leadingSpaces = closestA.pipeColPos + s.colPosShift() - a.pipeColPos - 1
case noAnnot, trailingSpaceLines:
// Do nothing
}
Expand All @@ -195,11 +212,11 @@ func checkLineAndSetSpace(row, aLineIdx int, a *Annot, rightAnnots []*Annot) boo
// 3 for "└─ " or " " (indentation)
lineLength := 3 + a.lines[aLineIdx].length

remainingSpaces := closestA.Col + s.colPosShift() - a.Col - lineLength
remainingSpaces := closestA.pipeColPos + s.colPosShift() - a.pipeColPos - lineLength

if remainingSpaces-s.space() < 0 {
a.row++
a.pipeLeadingSpaces = append(a.pipeLeadingSpaces, a.Col)
a.pipeLeadingSpaces = append(a.pipeLeadingSpaces, a.pipeColPos)
return false
}

Expand All @@ -213,7 +230,7 @@ func checkLineAndSetSpace(row, aLineIdx int, a *Annot, rightAnnots []*Annot) boo
if s2 == noAnnot {
return true
}
leadingSpaces2 := closestA2.Col + s2.colPosShift() - a.Col - lineLength
leadingSpaces2 := closestA2.pipeColPos + s2.colPosShift() - a.pipeColPos - lineLength
if s2 == above {
closestA2.pipeLeadingSpaces[rowPlusLineIdx] = leadingSpaces2
break
Expand Down Expand Up @@ -248,19 +265,17 @@ func closestAnnot(row int, rightAnnots []*Annot, trailingVerticalSpaceLinesCount
}

func write(writer io.Writer, annots []*Annot) error {
lastColPos := 0
rowCount := 0

b := &strings.Builder{}

for _, a := range annots {
b.WriteString(strings.Repeat(" ", a.Col-lastColPos))
b.WriteString("↑")
lastColPos = a.Col + 1

// Also use this loop to calculate rowCount
rowCount = max(rowCount, a.row+len(a.lines))
}

b.WriteString(writeArrowOrRange(annots))

b.WriteString("\n")
_, err := fmt.Fprint(writer, b.String())
if err != nil {
Expand Down Expand Up @@ -295,3 +310,30 @@ func write(writer io.Writer, annots []*Annot) error {

return nil
}

func writeArrowOrRange(annots []*Annot) string {
widthWritten := 0

b := &strings.Builder{}

for _, a := range annots {
if a.ColEnd == 0 {
b.WriteString(strings.Repeat(" ", a.pipeColPos-widthWritten))
b.WriteString("↑")
widthWritten = a.pipeColPos + 1
} else {
b.WriteString(strings.Repeat(" ", a.Col-widthWritten))
if a.Col == a.pipeColPos {
b.WriteString("├")
} else {
b.WriteString("└")
b.WriteString(strings.Repeat("─", a.pipeColPos-a.Col-1))
b.WriteString("┬")
}
b.WriteString(strings.Repeat("─", a.ColEnd-a.pipeColPos-1))
b.WriteString("┘")
widthWritten = a.ColEnd + 1
}
}
return b.String()
}
Loading

0 comments on commit a058a27

Please sign in to comment.