printer

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 12, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrZeroLengthDuplicate = errors.New("zero length duplicate found")

ErrZeroLengthDuplicate indicates a duplicate group with no nodes was encountered.

Functions

func BuildCloneGroups

func BuildCloneGroups(duplChan <-chan syntax.Match) map[string][][]*syntax.Node

BuildCloneGroups builds a map of hash to clone groups from matches.

func ComputeUniqueCounts

func ComputeUniqueCounts(groups map[string][][]*syntax.Node) map[string]int

ComputeUniqueCounts calculates unique file counts for each clone group.

func EvaluateActionability added in v0.2.0

func EvaluateActionability(nodeSeqs [][]*syntax.Node) domain.CloneActionability

EvaluateActionability analyzes a clone group and determines whether it represents actionable duplication or idiomatic boilerplate noise.

A group is actionable when it contains real logic that can be extracted, composed, or otherwise refactored. Non-actionable patterns are standard Go idioms that cannot be eliminated without breaking semantics.

To be non-actionable, EVERY clone in the group must match the same boilerplate pattern. If any clone differs, the group is actionable.

func ExtractSortCriteria

func ExtractSortCriteria(sortBy ...config.SortCriteria) config.SortCriteria

ExtractSortCriteria extracts the sort criteria from variadic sortBy parameter. Returns SortBySize as default if no criteria is provided.

func GetCloneSize

func GetCloneSize(group [][]*syntax.Node) int

GetCloneSize returns the size (token count) of the first clone in a group. Returns 0 if the group is empty or has no fragments.

func NodesToGroup added in v0.2.0

func NodesToGroup(
	fread ReadFile,
	hash string,
	dups [][]*syntax.Node,
) (domain.ProcessedCloneGroup, error)

NodesToGroup converts raw syntax.Node groups into a ProcessedCloneGroup.

func ProcessClones added in v0.2.0

func ProcessClones(fread ReadFile, dups [][]*syntax.Node) ([]domain.ProcessedClone, error)

ProcessClones converts raw syntax.Node groups into ProcessedClone slices. This is the single point where [][]*syntax.Node is decoded into domain types, eliminating the need for each printer to understand AST internals.

func SortCloneGroupKeys

func SortCloneGroupKeys(
	keys []string,
	sortBy config.SortCriteria,
	groups map[string][][]*syntax.Node,
	uniqueCounts map[string]int,
)

SortCloneGroupKeys sorts clone group hashes based on specified criteria.

func SortCloneGroups

func SortCloneGroups(groups []CloneGroup, sortBy config.SortCriteria)

SortCloneGroups sorts CloneGroup arrays by specified criteria.

func SortClonesByHash

func SortClonesByHash(sorted [][]*syntax.Node) [][]*syntax.Node

SortClonesByHash sorts clone groups by hash (alphabetical, ascending order).

func SortClonesByOccurrence

func SortClonesByOccurrence(groups [][]*syntax.Node) [][]*syntax.Node

SortClonesByOccurrence sorts clone groups by number of files (most files first, descending order).

func SortClonesBySize

func SortClonesBySize(cloneGroups [][]*syntax.Node) [][]*syntax.Node

SortClonesBySize sorts clone groups by token count (largest first, descending order).

func SortClonesByTotalTokens

func SortClonesByTotalTokens(nodeGroups [][]*syntax.Node) [][]*syntax.Node

SortClonesByTotalTokens sorts clone groups by total token count across all files (largest first).

func SortNodesByCriteria

func SortNodesByCriteria(dups [][]*syntax.Node, sortBy config.SortCriteria) [][]*syntax.Node

SortNodesByCriteria applies sorting criteria to node arrays using a unified switch.

func SortProcessedClonesByCriteria added in v0.2.0

func SortProcessedClonesByCriteria(clones []domain.ProcessedClone, sortBy config.SortCriteria)

func TestCloneSortingWithData

func TestCloneSortingWithData(
	t *testing.T,
	sortFunc func([][]*syntax.Node) [][]*syntax.Node,
	sortName string,
	clones [][]*syntax.Node,
)

TestCloneSortingWithData is a helper function for testing clone sorting algorithms that takes pre-created clones.

func WordDiff

func WordDiff(base, compared string) string

WordDiff performs a word-level diff between two strings using go-diff. Returns HTML-formatted string with highlighted word changes.

Types

type CloneCategory

type CloneCategory = domain.CloneCategory

type CloneClassification

type CloneClassification = domain.CloneClassification

type CloneDiff

type CloneDiff struct {
	CloneWithContent

	Diff DiffResult
}

CloneDiff represents a clone with its diff against the base.

type CloneGroup

type CloneGroup struct {
	Hash  string      `json:"hash"`
	Size  int         `json:"size"`
	Files []JSONClone `json:"files"`
}

type CloneGroupDiff

type CloneGroupDiff struct {
	Base       *CloneWithContent
	Others     []CloneDiff
	HasAnyDiff bool
	// Aggregate statistics across all diffs in this group
	TotalAdded    int
	TotalRemoved  int
	TotalModified int
}

CloneGroupDiff computes diffs between all clones in a group. Returns the base clone (first) and diffs for all other clones.

func ComputeCloneGroupDiff

func ComputeCloneGroupDiff(clones []domain.ProcessedClone) CloneGroupDiff

ComputeCloneGroupDiff computes diffs for a group of clones.

type CloneGroupViewData added in v0.3.0

type CloneGroupViewData struct {
	GroupNum    int
	Category    CloneCategory
	Priority    ClonePriority
	HasTest     bool
	Occurrences int
	TotalTokens int
	BadgesHTML  string
	Suggestion  string
	Clones      []CloneOccurrenceData
}

type CloneOccurrenceData added in v0.3.0

type CloneOccurrenceData struct {
	VSCodeLink templ.SafeURL
	Filename   string
	LineStart  int
	Fragment   string
}

type ClonePriority

type ClonePriority = domain.ClonePriority

type CloneWithContent

type CloneWithContent struct {
	CloneWithContentMixin

	Content []byte
}

CloneWithContent represents a clone with its file content.

type CloneWithContentMixin

type CloneWithContentMixin struct {
	Filename  string
	LineStart int
	LineEnd   int
}

CloneWithContentMixin provides common location fields for clone structures.

type DiffAggregateStats added in v0.3.0

type DiffAggregateStats struct {
	TotalAdded    int
	TotalRemoved  int
	TotalModified int
	HasStats      bool
}

type DiffLine

type DiffLine struct {
	Content    string
	Type       DiffLineType
	LineNumber int // Line number in the original file
}

DiffLine represents a single line in a diff view.

type DiffLineType

type DiffLineType int

DiffLineType indicates the type of diff line.

const (
	DiffLineEqual DiffLineType = iota
	DiffLineAdded
	DiffLineRemoved
	DiffLineModified
)

type DiffResult

type DiffResult struct {
	Base     []DiffLine
	Compared []DiffLine
	HasDiff  bool
}

DiffResult represents the diff between two code fragments.

func LineDiff

func LineDiff(base, compared []byte) DiffResult

LineDiff performs a line-by-line diff between two code fragments. It uses a simple but effective algorithm optimized for code comparison: 1. Split into lines 2. Find matching lines 3. Mark additions/removals/modifications

Size efficiency: O(n) memory, minimal allocations.

type DiffViewData added in v0.3.0

type DiffViewData struct {
	GroupNum       int
	Base           *CloneWithContent
	Others         []CloneDiff
	AggregateStats DiffAggregateStats
}

type FileInfo

type FileInfo struct {
	CloneWithContentMixin

	Content []byte
	Node    *syntax.Node
}

FileInfo represents processed file information.

func ProcessFileContent

func ProcessFileContent(fread ReadFile, node *syntax.Node) (*FileInfo, error)

ProcessFileContent unified file processing for all printers.

func ProcessNodeRange

func ProcessNodeRange(fread ReadFile, startNode, endNode *syntax.Node) (*FileInfo, error)

ProcessNodeRange processes a range of nodes (start to end).

type FileStatMixin

type FileStatMixin struct {
	// contains filtered or unexported fields
}

FileStatMixin provides common fields for file statistics.

type HashSetter

type HashSetter interface {
	SetHash(hash string)
}

type Issue

type Issue struct {
	From, To domain.ProcessedClone
}

type Issuer

type Issuer struct {
	ReadFile
}

func NewIssuer

func NewIssuer(fread ReadFile) *Issuer

func (*Issuer) MakeIssues

func (p *Issuer) MakeIssues(dups [][]*syntax.Node) ([]Issue, error)

type JSONClone

type JSONClone struct {
	Filename      string `json:"filename"`
	LineStart     int    `json:"line_start"`
	LineEnd       int    `json:"line_end"`
	Fragment      string `json:"fragment"`
	Category      string `json:"category,omitempty"`
	Priority      string `json:"priority,omitempty"`
	Actionability string `json:"actionability,omitempty"`
}

type JSONOutput

type JSONOutput struct {
	Version         string       `json:"version"`
	Timestamp       time.Time    `json:"timestamp"`
	Threshold       int          `json:"threshold"`
	FilesAnalyzed   int          `json:"files_analyzed"`
	DetectionMethod string       `json:"detection_method,omitempty"`
	CloneGroups     []CloneGroup `json:"clone_groups"`
	Summary         Summary      `json:"summary"`
}

type JSONPrinter

type JSONPrinter struct {
	ReadFile
	// contains filtered or unexported fields
}

func (*JSONPrinter) OutputJSON

func (p *JSONPrinter) OutputJSON(
	threshold int,
	sortBy config.SortCriteria,
	detectionMethod string,
) error

func (*JSONPrinter) OutputSimpleJSON

func (p *JSONPrinter) OutputSimpleJSON() error

func (*JSONPrinter) PrintClones

func (p *JSONPrinter) PrintClones(
	group domain.ProcessedCloneGroup,
	sortBy ...config.SortCriteria,
) error

func (*JSONPrinter) PrintFooter

func (*JSONPrinter) PrintFooter() error

func (*JSONPrinter) PrintHeader

func (p *JSONPrinter) PrintHeader() error

func (*JSONPrinter) SetFilesCount

func (p *JSONPrinter) SetFilesCount(count int)

func (*JSONPrinter) SetHash

func (p *JSONPrinter) SetHash(hash string)

type LineRangeMixin

type LineRangeMixin struct {
	StartLine int `json:"startLine"`
	EndLine   int `json:"endLine,omitempty"`
}

type PatternLabel added in v0.3.0

type PatternLabel string

PatternLabel identifies which non-actionable pattern was detected. Empty string means actionable.

const (
	PatternNone             PatternLabel = ""
	PatternTestData         PatternLabel = "testdata-pair"
	PatternTableDrivenTest  PatternLabel = "table-driven-test"
	PatternTestScaffolding  PatternLabel = "test-scaffolding"
	PatternDataDominated    PatternLabel = "data-dominated"
	PatternSignatureOnly    PatternLabel = "signature-only"
	PatternRAIIDefer        PatternLabel = "raii-defer"
	PatternErrorPropagation PatternLabel = "error-propagation"
	PatternInterfaceImpl    PatternLabel = "interface-implementation"
)

func EvaluateActionabilityWithLabel added in v0.3.0

func EvaluateActionabilityWithLabel(nodeSeqs [][]*syntax.Node) (PatternLabel, domain.CloneActionability)

EvaluateActionabilityWithLabel returns both the actionability and the pattern label that caused it. This allows downstream code to adjust category/suggestion based on which specific pattern was detected.

type Printer

type Printer interface {
	PrintHeader() error
	PrintClones(group domain.ProcessedCloneGroup, sortBy ...config.SortCriteria) error
	PrintFooter() error
}

func NewHTML

func NewHTML(w io.Writer, fread ReadFile, threshold ...int) Printer

NewHTML creates a new HTML printer. Supports optional threshold parameter (default: 15).

func NewHTMLWithOptions

func NewHTMLWithOptions(
	w io.Writer,
	fread ReadFile,
	diffMode config.DiffMode,
	metadata ReportMetadata,
	threshold ...int,
) Printer

NewHTMLWithOptions creates a new HTML printer with full options. diffMode enables visual diff highlighting between duplicate occurrences. metadata contains CLI settings to display in the report.

func NewJSON

func NewJSON(w io.Writer, fread ReadFile) Printer

func NewPlumbing

func NewPlumbing(w io.Writer, fread ReadFile) Printer

func NewSARIF

func NewSARIF(w io.Writer, fread ReadFile, threshold int) Printer

NewSARIF creates a new SARIF format printer with default settings.

func NewSARIFWithConfig added in v0.2.0

func NewSARIFWithConfig(w io.Writer, fread ReadFile, cfg SARIFConfig) Printer

NewSARIFWithConfig creates a new SARIF format printer with explicit config.

func NewStats

func NewStats(writer io.Writer, fileReader ReadFile, minTokens int) Printer

NewStats creates a new stats printer.

func NewText

func NewText(w io.Writer, fread ReadFile) Printer

type ReadFile

type ReadFile func(filename string) ([]byte, error)

type ReportMetadata

type ReportMetadata struct {
	Semantic         bool
	DetectionMethods []string
	SortBy           string
	IncludeSQLC      bool
	IncludeTempl     bool
}

ReportMetadata contains CLI settings used for the report.

type RichTextSetter added in v0.2.0

type RichTextSetter interface {
	SetRichText(enabled bool)
}

RichTextSetter enables enhanced text output with classification badges.

type SARIFArtifactLocation

type SARIFArtifactLocation struct {
	URI string `json:"uri"`
}

SARIFArtifactLocation represents the artifact (file) location.

type SARIFConfig added in v0.2.0

type SARIFConfig struct {
	Threshold int
	Version   string
}

SARIFConfig contains configuration for SARIF output.

type SARIFConfiguration

type SARIFConfiguration struct {
	Level string `json:"level"`
}

SARIFConfiguration represents rule configuration.

type SARIFDriver

type SARIFDriver struct {
	Name           string      `json:"name"`
	Version        string      `json:"version"`
	InformationURI string      `json:"informationUri"`
	Rules          []SARIFRule `json:"rules"`
}

SARIFDriver represents the tool driver information.

type SARIFFingerprints

type SARIFFingerprints struct {
	// ContentFingerprint is a hash that uniquely identifies the duplicate content.
	ContentFingerprint string `json:"contentFingerprint,omitempty"`
	// PartialFingerprint provides approximate matching for related content.
	PartialFingerprint string `json:"partialFingerprint,omitempty"`
}

SARIFFingerprints represents fingerprints for deduplication. SARIF spec: fingerprints are short strings that can be used to relate related results.

type SARIFInvocation

type SARIFInvocation struct {
	ExecutionSuccessful bool   `json:"executionSuccessful"`
	StartTimeUTC        string `json:"startTimeUtc,omitempty"`
	EndTimeUTC          string `json:"endTimeUtc,omitempty"`
}

SARIFInvocation represents an invocation of the tool.

type SARIFLocation

type SARIFLocation struct {
	PhysicalLocation SARIFPhysicalLocation `json:"physicalLocation"`
}

SARIFLocation represents a location in the code.

type SARIFMessage

type SARIFMessage struct {
	Text string `json:"text"`
}

SARIFMessage represents a message in a result.

type SARIFOutput

type SARIFOutput struct {
	Schema  string     `json:"$schema"`
	Version string     `json:"version"`
	Runs    []SARIFRun `json:"runs"`
}

SARIFOutput represents the SARIF (Static Analysis Results Interchange Format) output. This format is used by security tools like GitHub Advanced Security, CodeQL, etc. Spec: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html

type SARIFPhysicalLocation

type SARIFPhysicalLocation struct {
	ArtifactLocation SARIFArtifactLocation `json:"artifactLocation"`
	Region           SARIFRegion           `json:"region"`
}

SARIFPhysicalLocation represents the physical file location.

type SARIFRegion

type SARIFRegion struct {
	LineRangeMixin
}

SARIFRegion represents a region within a file.

type SARIFResult

type SARIFResult struct {
	RuleID       string            `json:"ruleId"`
	Level        string            `json:"level"`
	Message      SARIFMessage      `json:"message"`
	Locations    []SARIFLocation   `json:"locations"`
	Fingerprints SARIFFingerprints `json:"fingerprints"`
}

SARIFResult represents a single result (finding).

type SARIFRule

type SARIFRule struct {
	ID                   string             `json:"id"`
	Name                 string             `json:"name"`
	ShortDescription     SARIFTextContent   `json:"shortDescription"`
	FullDescription      SARIFTextContent   `json:"fullDescription"`
	DefaultConfiguration SARIFConfiguration `json:"defaultConfiguration"`
	HelpURI              string             `json:"helpUri,omitempty"`
}

SARIFRule represents a rule/check that was violated.

type SARIFRun

type SARIFRun struct {
	Tool        SARIFTool         `json:"tool"`
	Results     []SARIFResult     `json:"results"`
	Invocations []SARIFInvocation `json:"invocations,omitempty"`
}

SARIFRun represents a single analysis run.

type SARIFTextContent

type SARIFTextContent struct {
	Text string `json:"text"`
}

SARIFTextContent represents text content in SARIF.

type SARIFTool

type SARIFTool struct {
	Driver SARIFDriver `json:"driver"`
}

SARIFTool represents the tool that performed the analysis.

type SimpleCloneGroup

type SimpleCloneGroup struct {
	Hash      string            `json:"hash"`
	Score     int               `json:"score"`
	Instances []SimpleJSONClone `json:"instances"`
}

type SimpleJSONClone

type SimpleJSONClone struct {
	LineRangeMixin

	Filename   string `json:"filename"`
	TokenCount int    `json:"token_count"`
}

type SimpleJSONOutput

type SimpleJSONOutput []SimpleCloneGroup

type StatsConfig

type StatsConfig struct {
	Format              config.OutputFormat
	FilesCount          int
	DetectionMethods    string
	SemanticDetection   bool
	Timestamp           string
	AnalysisDuration    time.Duration
	TotalEstimatedLines int
	FilesFiltered       int
	FilterBreakdown     map[string]int
}

StatsConfig holds configuration for statistics output. Used by StatsPrinter.ApplyStatsConfig to set all stats metadata in one call.

type StatsData

type StatsData struct {
	// Count metrics
	TotalFilesScanned int `json:"total_files_scanned"`
	TotalCloneGroups  int `json:"total_clone_groups"`
	TotalClones       int `json:"total_clones"`

	// Size metrics
	TotalDuplicateLines int `json:"total_duplicate_lines"`
	TotalTokens         int `json:"total_tokens"`
	TotalEstimatedLines int `json:"total_estimated_lines"` // Estimated total lines for duplication percentage
	AverageCloneSize    int `json:"average_clone_size"`

	// Complexity and impact metrics
	ComplexityScore  float64 `json:"complexity_score"`
	ImpactScore      int     `json:"impact_score"`
	DuplicationRatio float64 `json:"duplication_ratio"` // Percentage of duplicated code

	// Quality metrics
	HealthScore domain.HealthScore `json:"health_score"` // A-F grade based on metrics

	// Time metrics
	AnalysisDuration string `json:"analysis_duration"` // Time taken for analysis
	Timestamp        string `json:"timestamp"`         // ISO 8601 timestamp

	// Aggregation metrics
	FileDuplication   map[string]int `json:"file_duplication"`             // filename -> duplicate line count
	SizeDistribution  map[string]int `json:"size_distribution"`            // size range -> count (lines)
	TokenDistribution map[string]int `json:"token_distribution"`           // token range -> count
	SeverityBreakdown map[string]int `json:"severity_breakdown"`           // severity -> count (small/medium/large/huge)
	CategoryBreakdown map[string]int `json:"category_breakdown,omitempty"` // category -> count (function/test/struct/etc)
	PriorityBreakdown map[string]int `json:"priority_breakdown,omitempty"` // priority -> count (critical/high/medium/low)

	// Actionability metrics
	ActionableGroups    int `json:"actionable_groups,omitempty"`     // Groups that can be refactored
	NonActionableGroups int `json:"non_actionable_groups,omitempty"` // Groups that are idiomatic boilerplate

	// Test vs production separation
	TestCloneGroups       int `json:"test_clone_groups,omitempty"`       // Groups found in test files
	ProductionCloneGroups int `json:"production_clone_groups,omitempty"` // Groups found in production files

	// Top impactful clones to fix
	TopClones []TopCloneGroup `json:"top_clones,omitempty"` // Highest priority actionable clones

	// Filter metrics (NEW)
	FilesFiltered   int            `json:"files_filtered,omitempty"`   // Total files filtered out
	FilterBreakdown map[string]int `json:"filter_breakdown,omitempty"` // Reason -> count (e.g., "templ" -> 12)

	// Detection mode
	DetectionMode     string `json:"detection_mode,omitempty"`             // "semantic" or "structural"
	DetectionModeDesc string `json:"detection_mode_description,omitempty"` // Human-readable description

	// Metadata
	DetectionMethods  string `json:"detection_methods"`  // Comma-separated detection methods used
	SemanticDetection bool   `json:"semantic_detection"` // Whether semantic-aware detection was enabled
}

StatsData holds all aggregated statistics about code duplication analysis.

Fields: - Count metrics: TotalFilesScanned, TotalCloneGroups, TotalClones - Size metrics: TotalTokens, TotalDuplicateLines, AverageCloneSize - Complexity metrics: ComplexityScore, ImpactScore - Quality metrics: DuplicationRatio, HealthScore - Time metrics: AnalysisDuration, Timestamp - Aggregation metrics: FileDuplication, SizeDistribution - Filter metrics: FilesFiltered, FilterBreakdown (NEW) - Metadata: DetectionMethods

JSON Marshaling: - All fields are JSON tagged for easy marshaling - Use printer.JSONPrinter for formatted JSON output.

type StatsPrinter

type StatsPrinter interface {
	Printer
	ApplyStatsConfig(config StatsConfig)
	SetFilterStats(filesFiltered int, breakdown map[string]int)
	GetStatsData() *StatsData
}

StatsPrinter extends Printer interface with stats configuration.

type StyleMixin

type StyleMixin struct {
	// contains filtered or unexported fields
}

StyleMixin holds common lipgloss style fields.

type Summary

type Summary struct {
	TotalCloneGroups int     `json:"total_clone_groups"`
	TotalClones      int     `json:"total_clones"`
	ComplexityScore  float64 `json:"complexity_score"`
	ImpactScore      int     `json:"impact_score,omitempty"`
}

type SummaryViewData added in v0.3.0

type SummaryViewData struct {
	TotalClones    int
	TotalTokens    int
	ProdCount      int
	TestCount      int
	CategoryCounts map[CloneCategory]int
	PriorityCounts map[ClonePriority]int
	HasData        bool
}

type TextPrinter

type TextPrinter struct {
	ReadFile
	// contains filtered or unexported fields
}

func (*TextPrinter) OutputText

func (p *TextPrinter) OutputText(threshold int, sortBy config.SortCriteria) error

func (*TextPrinter) PrintClones

func (p *TextPrinter) PrintClones(
	group domain.ProcessedCloneGroup,
	sortBy ...config.SortCriteria,
) error

func (*TextPrinter) PrintFooter

func (p *TextPrinter) PrintFooter() error

func (*TextPrinter) PrintHeader

func (p *TextPrinter) PrintHeader() error

func (*TextPrinter) SetFileDuplicate

func (p *TextPrinter) SetFileDuplicate(isDupe bool)

func (*TextPrinter) SetHash

func (p *TextPrinter) SetHash(hash string)

func (*TextPrinter) SetRichText added in v0.2.0

func (p *TextPrinter) SetRichText(enabled bool)

type TopCloneGroup added in v0.2.0

type TopCloneGroup struct {
	Priority       domain.ClonePriority `json:"priority"`
	Category       domain.CloneCategory `json:"category"`
	Lines          int                  `json:"lines"`
	Files          int                  `json:"files"`
	Suggestion     string               `json:"suggestion"`
	FirstFile      string               `json:"firstFile"`
	FirstLineStart int                  `json:"firstLineStart"`
}

TopCloneGroup represents a high-impact clone group for the "top clones to fix" preview.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL