cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.73.5

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/diags/problems.go

139lines · modecode

1package diags
2
3import (
4 "fmt"
5 "slices"
6 "strings"
7
8 "github.com/cloudflare/pint/internal/output"
9)
10
11func countDigits(n int) (c int) {
12 for n > 0 {
13 n /= 10
14 c++
15 }
16 return c
17}
18
19type Diagnostic struct {
20 Message string
21 Pos PositionRanges
22 FirstColumn int // 1-indexed
23 LastColumn int // 1-indexed
24}
25
26func lineCoverage(diags []Diagnostic) (lines []int) {
27 for _, diag := range diags {
28 for _, pos := range diag.Pos {
29 if !slices.Contains(lines, pos.Line) {
30 lines = append(lines, pos.Line)
31 }
32 }
33 }
34 slices.Sort(lines)
35 return lines
36}
37
38func InjectDiagnostics(content string, diags []Diagnostic, color output.Color) string {
39 lines := lineCoverage(diags)
40 lastLine := slices.Max(lines)
41
42 diagPositions := make([]PositionRanges, len(diags))
43 for i, diag := range diags {
44 dl := diag.Pos.Len()
45 diagPositions[i] = readRange(
46 min(diag.FirstColumn, dl),
47 min(diag.LastColumn, dl),
48 diag.Pos,
49 )
50 }
51
52 var buf strings.Builder
53 nextLine := make([]strings.Builder, len(diags))
54 needsNextLine := make([]bool, len(diags))
55
56 disablePoints := make([]bool, len(diags))
57 for i, a := range diags {
58 for j := range i {
59 b := diags[j]
60 if a.FirstColumn == b.FirstColumn && a.LastColumn == b.LastColumn {
61 disablePoints[i] = true
62 }
63 }
64 }
65
66 digits := countDigits(lastLine)
67 nrFmt := fmt.Sprintf("%%%dd", digits)
68
69 var lastWriteLine int
70 for lineIndex, line := range strings.Split(content, "\n") {
71
72 if lineIndex+1 > lastLine {
73 break
74 }
75 if !slices.Contains(lines, lineIndex+1) {
76 continue
77 }
78
79 for i := range diags {
80 needsNextLine[i] = false
81 if lineIndex+1 == diagPositions[i].Lines().Last {
82 needsNextLine[i] = true
83 }
84 }
85
86 prefix := fmt.Sprintf(nrFmt+" | ", lineIndex+1)
87
88 if lastWriteLine > 0 && lineIndex+1-lastWriteLine > 1 {
89 buf.WriteString(output.MaybeColor(output.White, color == output.None, strings.Repeat(" ", digits)))
90 buf.WriteString(" | [...]\n")
91 }
92 lastWriteLine = lineIndex + 1
93
94 buf.WriteString(output.MaybeColor(output.White, color == output.None, prefix))
95 for i, ok := range needsNextLine {
96 if ok {
97 nextLine[i].WriteString(strings.Repeat(" ", digits+3))
98 }
99 }
100
101 for columnIndex, r := range line {
102 buf.WriteRune(r)
103
104 for i, ok := range needsNextLine {
105 if !ok {
106 continue
107 }
108 for _, pos := range diagPositions[i] {
109 if pos.Line != lineIndex+1 {
110 continue
111 }
112 before := pos.FirstColumn > columnIndex+1
113 inside := pos.FirstColumn <= columnIndex+1 && pos.LastColumn >= columnIndex+1
114 switch {
115 case before:
116 nextLine[i].WriteRune(' ')
117 case inside && disablePoints[i]:
118 nextLine[i].WriteRune(' ')
119 case inside && !disablePoints[i]:
120 nextLine[i].WriteRune('^')
121 }
122 }
123 }
124 }
125 buf.WriteRune('\n')
126
127 for i, ok := range needsNextLine {
128 if ok {
129 buf.WriteString(output.MaybeColor(color, color == output.None, nextLine[i].String()))
130 buf.WriteRune(' ')
131 buf.WriteString(output.MaybeColor(color, color == output.None, diags[i].Message))
132 buf.WriteRune('\n')
133 nextLine[i].Reset()
134 }
135 }
136 }
137
138 return buf.String()
139}
140