Skip to content

Commit

Permalink
Implement diagnostic quota logic
Browse files Browse the repository at this point in the history
  • Loading branch information
daniilsapa committed Jan 2, 2025
1 parent d2e4c50 commit e55b9fa
Show file tree
Hide file tree
Showing 4 changed files with 298 additions and 1 deletion.
6 changes: 5 additions & 1 deletion packages/steiger/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import { $enabledRules, getEnabledRules, getGlobalIgnores, getPluginByRuleName }
import { runRule } from './features/run-rule'
import { removeGlobalIgnoreFromVfs } from './features/remove-global-ignores-from-vfs'
import { calculateFinalSeverities } from './features/calculate-diagnostic-severities'
import { collapseDiagnostics } from './features/collapse-diagnostics'

async function runRules({ vfs, rules }: { vfs: Folder; rules: Array<Rule> }) {
const vfsWithoutGlobalIgnores = removeGlobalIgnoreFromVfs(vfs, getGlobalIgnores())

const ruleResults = await Promise.all(rules.map((rule) => runRule(vfsWithoutGlobalIgnores, rule)))
return ruleResults.flatMap((r, ruleResultsIndex) => {
const diagnosticsPerRule = ruleResults.map((r, ruleResultsIndex) => {
const { diagnostics } = r
if (diagnostics.length === 0) {
return []
Expand Down Expand Up @@ -44,6 +45,9 @@ async function runRules({ vfs, rules }: { vfs: Folder; rules: Array<Rule> }) {
return finalDiagnostic
})
})
const collapsedDiagnostics = collapseDiagnostics(diagnosticsPerRule)

return collapsedDiagnostics.flat()
}

export const linter = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import { describe, expect, it } from 'vitest'
import { trimDiagnosticsToMeetQuota } from './diagnostic-quota'

const defaultLocation = {
path: '/users/user/file',
}

const defaultSeverity = 'error' as 'warn' | 'error'

describe('trimDiagnosticsToMeetQuota', () => {
it('should return the same diagnostics if they are below the quota', () => {
const diagnosticPerRule = [
[
{
message: 'First rule, first message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'First rule, second message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'First rule, third message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
],
[
{
message: 'Second rule, first message',
ruleName: 'rule-2',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'Second rule, second message',
ruleName: 'rule-2',
location: defaultLocation,
severity: defaultSeverity,
},
],
]

const result = trimDiagnosticsToMeetQuota(diagnosticPerRule, 10)

expect(result).toEqual(diagnosticPerRule)
})

it('should return no diagnostics if the quota is 0', () => {
const diagnosticPerRule = [
[
{
message: 'First rule, first message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'First rule, second message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'First rule, third message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
],
[
{
message: 'Second rule, first message',
ruleName: 'rule-2',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'Second rule, second message',
ruleName: 'rule-2',
location: defaultLocation,
severity: defaultSeverity,
},
],
]

const result = trimDiagnosticsToMeetQuota(diagnosticPerRule, 0)

expect(result).toEqual([[], []])
})

it('should spread the quota evenly between rules', () => {
const diagnosticPerRule = [
[
{
message: 'First rule, first message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'First rule, second message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'First rule, third message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
],
[
{
message: 'Second rule, first message',
ruleName: 'rule-2',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'Second rule, second message',
ruleName: 'rule-2',
location: defaultLocation,
severity: defaultSeverity,
},
],
]

const result = trimDiagnosticsToMeetQuota(diagnosticPerRule, 2)

expect(result).toEqual([
[
{
message: 'First rule, first message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
],
[
{
message: 'Second rule, first message',
ruleName: 'rule-2',
location: defaultLocation,
severity: defaultSeverity,
},
],
])
})

it('should take first x rule diagnostics if the quota is less than the number of rules', () => {
const diagnosticPerRule = [
[
{
message: 'First rule, first message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'First rule, second message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'First rule, third message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
],
[
{
message: 'Second rule, first message',
ruleName: 'rule-2',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'Second rule, second message',
ruleName: 'rule-2',
location: defaultLocation,
severity: defaultSeverity,
},
],
[
{
message: 'Third rule, first message',
ruleName: 'rule-3',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'Third rule, second message',
ruleName: 'rule-3',
location: defaultLocation,
severity: defaultSeverity,
},
],
[
{
message: 'Forth rule, first message',
ruleName: 'rule-4',
location: defaultLocation,
severity: defaultSeverity,
},
{
message: 'Forth rule, second message',
ruleName: 'rule-4',
location: defaultLocation,
severity: defaultSeverity,
},
],
]

const result = trimDiagnosticsToMeetQuota(diagnosticPerRule, 3)

expect(result).toEqual([
[
{
message: 'First rule, first message',
ruleName: 'rule-1',
location: defaultLocation,
severity: defaultSeverity,
},
],
[
{
message: 'Second rule, first message',
ruleName: 'rule-2',
location: defaultLocation,
severity: defaultSeverity,
},
],
[
{
message: 'Third rule, first message',
ruleName: 'rule-3',
location: defaultLocation,
severity: defaultSeverity,
},
],
[],
])
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Diagnostic } from '@steiger/types'

function distributeQuota(buckets: Array<number>, quota: number) {
// const allItems = buckets.reduce((acc, bucket) => acc + bucket, 0)
const quotaPerBucket = buckets.slice(0).fill(0)
let remainingQuota = quota

// Temporary algorithm to distribute the quota
for (let i = 0; remainingQuota > 0; i++) {
const bucket = buckets[i]

// If it went beyond the last bucket, start from the first one
if (bucket === undefined) {
i = -1
continue
}

// If the bucket does not contain any items, skip it
if (bucket !== 0) {
quotaPerBucket[i]++
remainingQuota--
}
}

return quotaPerBucket
}

export function trimDiagnosticsToMeetQuota(diagnosticsPerRule: Array<Array<Diagnostic>>, quota: number) {
const diagnosticCountPerRule = diagnosticsPerRule.map((diagnostics) => diagnostics.length)
const quotaPerRule = distributeQuota(diagnosticCountPerRule, quota)

return diagnosticsPerRule.map((diagnostics, i) => diagnostics.slice(0, quotaPerRule[i]))
}
7 changes: 7 additions & 0 deletions packages/steiger/src/features/collapse-diagnostics/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { trimDiagnosticsToMeetQuota } from './diagnostic-quota'
import { Diagnostic } from '@steiger/types'

const DEFAULT_QUOTA = 20

export const collapseDiagnostics = (diagnosticPerRule: Array<Array<Diagnostic>>) =>
trimDiagnosticsToMeetQuota(diagnosticPerRule, DEFAULT_QUOTA)

0 comments on commit e55b9fa

Please sign in to comment.