Skip to content

Commit

Permalink
perf: Reuse Cached Template (#103)
Browse files Browse the repository at this point in the history
# Description

- Caching the template and reuse them when it needed
- refactor `calculateVisualColumn` function to calculate columns when
tab is exists
  • Loading branch information
notJoon authored Oct 30, 2024
1 parent dba079d commit a559cb9
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 27 deletions.
63 changes: 39 additions & 24 deletions formatter/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"strings"
"sync"
"text/template"
"unicode"

Expand Down Expand Up @@ -39,20 +40,20 @@ type issueFormatter interface {
IssueTemplate() string
}

var formatterCache = map[string]issueFormatter{
CycloComplexity: &CyclomaticComplexityFormatter{},
SliceBound: &SliceBoundsCheckFormatter{},
MissingModPackage: &MissingModPackageFormatter{},
}

// getIssueFormatter is a factory function that returns the appropriate IssueFormatter
// based on the given rule.
// If no specific formatter is found for the given rule, it returns a GeneralIssueFormatter.
func getIssueFormatter(rule string) issueFormatter {
switch rule {
case CycloComplexity:
return &CyclomaticComplexityFormatter{}
case SliceBound:
return &SliceBoundsCheckFormatter{}
case MissingModPackage:
return &MissingModPackageFormatter{}
default:
return &GeneralIssueFormatter{}
if formatter, ok := formatterCache[rule]; ok {
return formatter
}
return &GeneralIssueFormatter{}
}

// GenerateFormattedIssue formats a slice of issues into a human-readable string.
Expand Down Expand Up @@ -87,6 +88,29 @@ type IssueData struct {
CommonIndent string
}

var funcMap = template.FuncMap{
"header": header,
"suggestion": suggestion,
"note": note,
"snippet": codeSnippet,
"underlineAndMessage": underlineAndMessage,
"message": message,
"warning": warning,
"complexityInfo": complexityInfo,
}

var templateCache sync.Map

func getCachedTemplate(tmplStr string) *template.Template {
if t, ok := templateCache.Load(tmplStr); ok {
return t.(*template.Template)
}

newTmpl := template.Must(template.New("issue").Funcs(funcMap).Parse(tmplStr))
templateCache.Store(tmplStr, newTmpl)
return newTmpl
}

func buildIssue(issue tt.Issue, snippet *internal.SourceCode, formatter issueFormatter) string {
startLine := issue.Start.Line
endLine := issue.End.Line
Expand Down Expand Up @@ -118,19 +142,8 @@ func buildIssue(issue tt.Issue, snippet *internal.SourceCode, formatter issueFor
SnippetLines: snippet.Lines,
}

funcMap := template.FuncMap{
"header": header,
"suggestion": suggestion,
"note": note,
"snippet": codeSnippet,
"underlineAndMessage": underlineAndMessage,
"message": message,
"warning": warning,
"complexityInfo": complexityInfo,
}

issueTemplate := formatter.IssueTemplate()
tmpl := template.Must(template.New("issue").Funcs(funcMap).Parse(issueTemplate))
tmpl := getCachedTemplate(issueTemplate)

var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
Expand Down Expand Up @@ -232,7 +245,7 @@ func suggestion(suggestion string, padding string, maxLineNumWidth int, startLin
endString += noStyle.Sprintf("%s\n", line)
}

endString += lineStyle.Sprintf("%s|\n\n", padding)
endString += lineStyle.Sprintf("%s|\n", padding)
return endString
}

Expand Down Expand Up @@ -267,9 +280,11 @@ func calculateMaxLineNumWidth(endLine int) int {
// calculateVisualColumn calculates the visual column position
// in a string. taking into account tab characters.
func calculateVisualColumn(line string, column int) int {
if column < 0 {
return 0
if !strings.ContainsRune(line, '\t') || column <= 1 {
return column - 1 // adjust to 0-based index
}

// calculate visual column only if there is a tab character
visualColumn := 0
for i, ch := range line {
if i+1 == column {
Expand Down
1 change: 0 additions & 1 deletion formatter/formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ suggestion:
5 | Remove the type conversion. Change ` + "`int(myInt)`" + ` to just ` + "`myInt`" + `.
|
`

result := GenerateFormattedIssue([]tt.Issue{issue}, snippet)
Expand Down
5 changes: 3 additions & 2 deletions formatter/slice_bound.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ func (f *SliceBoundsCheckFormatter) IssueTemplate() string {
`
}

// TODO: make this as a note
func warning(category string) string {
var endString string
endString = warningStyle.Sprint("warning: ")
if category == "index-access" {
endString += "Index access without bounds checking can lead to runtime panics.\n"
endString += "Index access without bounds checking can lead to runtime panics.\n\n"
} else if category == "slice-expression" {
endString += "Slice expressions without proper length checks may cause unexpected behavior.\n"
endString += "Slice expressions without proper length checks may cause unexpected behavior.\n\n"
}

return endString
Expand Down

0 comments on commit a559cb9

Please sign in to comment.