/* * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors * * SPDX-License-Identifier: EPL-2.0 */ import { setDiagnosticsEffect } from '@codemirror/lint'; import { StateField, RangeSet, type Extension, type EditorState, } from '@codemirror/state'; import DiagnosticValue, { type Severity } from './DiagnosticValue'; type SeverityCounts = Partial>; interface ExposedDiagnostics { readonly diagnostics: RangeSet; readonly severityCounts: SeverityCounts; } function countSeverities( diagnostics: RangeSet, ): SeverityCounts { const severityCounts: SeverityCounts = {}; const iter = diagnostics.iter(); while (iter.value !== null) { const { value: { severity }, } = iter; severityCounts[severity] = (severityCounts[severity] ?? 0) + 1; iter.next(); } return severityCounts; } const exposedDiagnosticsState = StateField.define({ create() { return { diagnostics: RangeSet.of([]), severityCounts: {}, }; }, update({ diagnostics: diagnosticsSet, severityCounts }, transaction) { let newDiagnosticsSet = diagnosticsSet; if (transaction.docChanged) { newDiagnosticsSet = newDiagnosticsSet.map(transaction.changes); } transaction.effects.forEach((effect) => { if (effect.is(setDiagnosticsEffect)) { const diagnostics = effect.value.map(({ severity, from, to }) => DiagnosticValue.VALUES[severity].range(from, to), ); diagnostics.sort(({ from: a }, { from: b }) => a - b); newDiagnosticsSet = RangeSet.of(diagnostics); } }); return { diagnostics: newDiagnosticsSet, severityCounts: // Only recompute if the diagnostics were changed. diagnosticsSet === newDiagnosticsSet ? severityCounts : countSeverities(newDiagnosticsSet), }; }, }); const exposeDiagnostics: Extension = [exposedDiagnosticsState]; export default exposeDiagnostics; export function getDiagnostics(state: EditorState): RangeSet { return state.field(exposedDiagnosticsState).diagnostics; } export function countDiagnostics( state: EditorState, severity: Severity, ): number { return state.field(exposedDiagnosticsState).severityCounts[severity] ?? 0; }