cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.74.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/diags/problems.go

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