Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor error printer
Browse files Browse the repository at this point in the history
notJoon committed Jul 17, 2024
1 parent e32d535 commit 75e8004
Showing 2 changed files with 51 additions and 83 deletions.
128 changes: 48 additions & 80 deletions internal/print.go
Original file line number Diff line number Diff line change
@@ -5,114 +5,82 @@ import (
"strings"
)

// FormatIssuesWithArrows formats a list of issues with the corresponding lines of source code
// and points to the specific location of each issue using arrows. It considers the visual
// column of each issue, adjusting for tabs and line number padding.
//
// Parameters:
// - issues: A slice of Issue structs, each representing a linting issue.
// - sourceCode: A pointer to the SourceCode struct containing the lines of code.
//
// Returns:
// - A formatted string highlighting the issues with arrows pointing to their locations.
const (
greenColor = "\033[32m"
resetColor = "\033[0m"
tabWidth = 8
)

func FormatIssuesWithArrows(issues []Issue, sourceCode *SourceCode) string {
var builder strings.Builder
for _, issue := range issues {
// write error header
builder.WriteString(fmt.Sprintf("error: %s\n", issue.Rule))
builder.WriteString(fmt.Sprintf(" --> %s\n", issue.Filename))

builder.WriteString(formatIssueHeader(issue))
if issue.Rule == "unnecessary-else" {
ifStartLine := issue.Start.Line - 2 // Start from the 'if' line
elseEndLine := issue.End.Line

// calculate max line number for padding
maxLineNumber := elseEndLine
maxLineNumberStr := fmt.Sprintf("%d", maxLineNumber)
lineNumberPadding := strings.Repeat(" ", len(maxLineNumberStr)-1)

builder.WriteString(fmt.Sprintf(" %s|\n", lineNumberPadding))

maxWidth := 0
padding := ""
for i := ifStartLine; i <= elseEndLine; i++ {
line := sourceCode.Lines[i-1]
expandedLine := expandTabs(line)
lineNumberStr := fmt.Sprintf("%d", i)
padding = strings.Repeat(" ", len(maxLineNumberStr)-len(lineNumberStr))
formattedLine := fmt.Sprintf("%s%s | %s\n", padding, lineNumberStr, expandedLine)
builder.WriteString(formattedLine)
if len(expandedLine) > maxWidth {
maxWidth = len(expandedLine)
}
}

builder.WriteString(fmt.Sprintf(" %s| %s\n", padding, strings.Repeat("~", maxWidth-1)))
builder.WriteString(fmt.Sprintf(" %s| %s\n\n", padding, issue.Message))
builder.WriteString(formatUnnecessaryElse(issue, sourceCode))
} else {
lineNumberStr := fmt.Sprintf("%d", issue.Start.Line)
lineNumberPadding := strings.Repeat(" ", len(lineNumberStr)-1)
builder.WriteString(formatGeneralIssue(issue, sourceCode))
}
}
return builder.String()
}

builder.WriteString(fmt.Sprintf(" %s|\n", lineNumberPadding))
func formatIssueHeader(issue Issue) string {
return fmt.Sprintf("error: %s\n --> %s\n", issue.Rule, issue.Filename)
}

// write the problematic line with line number
line := sourceCode.Lines[issue.Start.Line-1]
expandedLine := expandTabs(line)
builder.WriteString(fmt.Sprintf("%d | %s\n", issue.Start.Line, expandedLine))
func formatUnnecessaryElse(issue Issue, sourceCode *SourceCode) string {
var result strings.Builder
ifStartLine, elseEndLine := issue.Start.Line-2, issue.End.Line
maxLineNumberStr := fmt.Sprintf("%d", elseEndLine)
padding := strings.Repeat(" ", len(maxLineNumberStr)-1)

// calculate the visual column, considering expanded tabs
visualColumn := calculateVisualColumn(expandedLine, issue.Start.Column)
result.WriteString(fmt.Sprintf(" %s|\n", padding))

// write the arrow pointing to the issue
builder.WriteString(fmt.Sprintf(" %s| ", lineNumberPadding))
builder.WriteString(strings.Repeat(" ", visualColumn))
builder.WriteString("^ ")
builder.WriteString(issue.Message)
builder.WriteString("\n\n")
}
for i := ifStartLine; i <= elseEndLine; i++ {
line := expandTabs(sourceCode.Lines[i-1])
lineNumberStr := fmt.Sprintf("%d", i)
linePadding := strings.Repeat(" ", len(maxLineNumberStr)-len(lineNumberStr))
result.WriteString(fmt.Sprintf("%s%s | %s\n", linePadding, lineNumberStr, line))
}
return builder.String()

result.WriteString(fmt.Sprintf(" %s| %s\n", padding, strings.Repeat("~", len(sourceCode.Lines[elseEndLine-1])-1)))
result.WriteString(fmt.Sprintf(" %s| %s\n\n", padding, issue.Message))
return result.String()
}

func formatGeneralIssue(issue Issue, sourceCode *SourceCode) string {
var result strings.Builder
lineNumberStr := fmt.Sprintf("%d", issue.Start.Line)
padding := strings.Repeat(" ", len(lineNumberStr)-1)
result.WriteString(fmt.Sprintf(" %s|\n", padding))
line := expandTabs(sourceCode.Lines[issue.Start.Line-1])
result.WriteString(fmt.Sprintf("%d | %s\n", issue.Start.Line, line))
visualColumn := calculateVisualColumn(line, issue.Start.Column)
result.WriteString(fmt.Sprintf(" %s| %s^ %s\n\n", padding, strings.Repeat(" ", visualColumn), issue.Message))
return result.String()
}

// expandTabs replaces tab characters with spaces, considering a tab width of 8.
// It ensures that each tab character is expanded to the appropriate number of spaces
// based on its position in the line.
func expandTabs(line string) string {
var expanded strings.Builder
column := 0
for _, ch := range line {
for i, ch := range line {
if ch == '\t' {
// Add the appropriate number of spaces for a tab character
spaceCount := 8 - (column % 8)
for i := 0; i < spaceCount; i++ {
expanded.WriteByte(' ')
column++
}
spaceCount := tabWidth - (i % tabWidth)
expanded.WriteString(strings.Repeat(" ", spaceCount))
} else {
expanded.WriteRune(ch)
column++
}
}
return expanded.String()
}

// calculateVisualColumn computes the visual column index for a given source code line,
// considering tabs and their expanded width.
//
// Parameters:
// - line: A string representing a single line of source code.
// - column: The 1-based column index of the issue in the source code.
//
// Returns:
// - The 0-based visual column index accounting for tab expansion.
func calculateVisualColumn(line string, column int) int {
visualColumn := 0
for i, ch := range line {
if i+1 >= column {
if i+1 == column {
break
}
if ch == '\t' {
visualColumn += 8 - (visualColumn % 8)
visualColumn += tabWidth - (visualColumn % tabWidth)
} else {
visualColumn++
}
6 changes: 3 additions & 3 deletions internal/print_test.go
Original file line number Diff line number Diff line change
@@ -140,8 +140,8 @@ error: empty-if
error: example
--> test.go
|
10 | println("end")
| ^ example issue
10 | println("end")
| ^ example issue
`

@@ -183,7 +183,7 @@ func TestFormatIssuesWithArrows_UnnecessaryElse(t *testing.T) {
6 | } else {
7 | return false
8 | }
| ~~~~~~~~~~~~~~~~~~~
| ~~~~
| unnecessary else block
`

0 comments on commit 75e8004

Please sign in to comment.