From f761926314723e7c2560143e00be97ada496e330 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 05:43:34 +0000 Subject: [PATCH 1/3] Initial plan From b769372edab076e1927879bdbc2ebcd14ddb956e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 05:45:53 +0000 Subject: [PATCH 2/3] fix: ignore all same-word diagnostics when clicking Ignore Co-authored-by: cyanzhong <6745066+cyanzhong@users.noreply.github.com> --- src/card.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/card.ts b/src/card.ts index bd43f6d..84ec595 100644 --- a/src/card.ts +++ b/src/card.ts @@ -35,7 +35,7 @@ export function ignoreDiagnostic(view: EditorView, diag: Diagnostic, extraEffect } const { diagnostics } = view.state.field(diagnosticsField); const filtered = diagnostics.filter(d => - !(d.from === diag.from && d.to === diag.to && d.lintKind === diag.lintKind), + !(d.problemText === diag.problemText && d.lintKind === diag.lintKind), ); view.dispatch({ effects: [setDiagnosticsEffect.of(filtered), ...extraEffects] }); } From 7bf15f5e7977eac5a483cfd1342dfa36657f4732 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 12:32:27 +0000 Subject: [PATCH 3/3] remove lintKind check, handle empty problemText fallback, add ignoreDiagnostic tests Co-authored-by: cyanzhong <6745066+cyanzhong@users.noreply.github.com> --- src/card.ts | 4 ++- tests/card.test.ts | 88 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/src/card.ts b/src/card.ts index 84ec595..b69d546 100644 --- a/src/card.ts +++ b/src/card.ts @@ -35,7 +35,9 @@ export function ignoreDiagnostic(view: EditorView, diag: Diagnostic, extraEffect } const { diagnostics } = view.state.field(diagnosticsField); const filtered = diagnostics.filter(d => - !(d.problemText === diag.problemText && d.lintKind === diag.lintKind), + diag.problemText + ? d.problemText !== diag.problemText + : !(d.from === diag.from && d.to === diag.to && d.lintKind === diag.lintKind), ); view.dispatch({ effects: [setDiagnosticsEffect.of(filtered), ...extraEffects] }); } diff --git a/tests/card.test.ts b/tests/card.test.ts index 9f92e69..e9d4f19 100644 --- a/tests/card.test.ts +++ b/tests/card.test.ts @@ -1,5 +1,89 @@ -import { describe, expect, it } from 'vitest'; -import { cardContentCSS } from '../src/card'; +import { describe, expect, it, vi } from 'vitest'; +import { EditorState } from '@codemirror/state'; +import type { EditorView } from '@codemirror/view'; +import { diagnosticsField, setDiagnosticsEffect } from '../src/decoration'; +import type { Diagnostic } from '../src/decoration'; + +vi.mock('../src/lint', () => ({ + shouldAddToDict: false, + addToDictionary: vi.fn(), +})); + +// Import after mock so the module picks up the mocked lint +const { cardContentCSS, ignoreDiagnostic } = await import('../src/card'); + +function makeDiag(overrides: Partial & { from: number; to: number }): Diagnostic { + return { + lintKind: 'Spelling', + title: 'Spelling', + messageHtml: '

Unknown word

', + problemText: 'MarkEdit', + actions: [], + ...overrides, + }; +} + +function createMockView(diagnostics: Diagnostic[]) { + const state = EditorState.create({ extensions: [diagnosticsField] }) + .update({ effects: setDiagnosticsEffect.of(diagnostics) }).state; + + let dispatched: Diagnostic[] | undefined; + const view = { + state, + dispatch(tr: { effects: unknown[] }) { + for (const e of tr.effects) { + if ((e as { is: (t: unknown) => boolean }).is(setDiagnosticsEffect)) { + dispatched = (e as { value: Diagnostic[] }).value; + } + } + }, + } as unknown as EditorView; + + return { view, getDispatched: () => dispatched }; +} + +describe('ignoreDiagnostic', () => { + it('removes all diagnostics with the same problemText', () => { + const d1 = makeDiag({ from: 0, to: 8 }); + const d2 = makeDiag({ from: 20, to: 28 }); + const d3 = makeDiag({ from: 40, to: 48 }); + const { view, getDispatched } = createMockView([d1, d2, d3]); + + ignoreDiagnostic(view, d1); + expect(getDispatched()).toHaveLength(0); + }); + + it('removes same problemText across different lintKinds', () => { + const d1 = makeDiag({ from: 0, to: 8, lintKind: 'Spelling' }); + const d2 = makeDiag({ from: 20, to: 28, lintKind: 'Style' }); + const { view, getDispatched } = createMockView([d1, d2]); + + ignoreDiagnostic(view, d1); + expect(getDispatched()).toHaveLength(0); + }); + + it('keeps diagnostics with different problemText', () => { + const d1 = makeDiag({ from: 0, to: 8, problemText: 'MarkEdit' }); + const d2 = makeDiag({ from: 20, to: 25, problemText: 'other' }); + const { view, getDispatched } = createMockView([d1, d2]); + + ignoreDiagnostic(view, d1); + const remaining = getDispatched()!; + expect(remaining).toHaveLength(1); + expect(remaining[0].problemText).toBe('other'); + }); + + it('falls back to positional match when problemText is empty', () => { + const d1 = makeDiag({ from: 0, to: 5, problemText: '', lintKind: 'Grammar' }); + const d2 = makeDiag({ from: 10, to: 15, problemText: '', lintKind: 'Grammar' }); + const { view, getDispatched } = createMockView([d1, d2]); + + ignoreDiagnostic(view, d1); + const remaining = getDispatched()!; + expect(remaining).toHaveLength(1); + expect(remaining[0].from).toBe(10); + }); +}); describe('cardContentCSS', () => { it('includes message, button, ignore, and actions styles', () => {