feat(obsidian): add button to disable rules from within popup

This commit is contained in:
Elijah Potter
2025-12-05 10:49:34 -07:00
parent d0d0ac72df
commit 963040cd43
2 changed files with 125 additions and 81 deletions

View File

@@ -131,77 +131,88 @@ export default class State {
const text = view.state.doc.sliceString(-1);
const chars = Array.from(text);
const lints = await this.harper.lint(text);
const lints = await this.harper.organizedLints(text);
return lints.map((lint) => {
const span = lint.span();
return Object.entries(lints).flatMap(([linterName, lints]) =>
lints.map((lint) => {
const span = lint.span();
const actions = lint.suggestions().map((sug) => {
return {
name:
sug.kind() == SuggestionKind.Replace
? sug.get_replacement_text()
: suggestionToLabel(sug),
title: suggestionToLabel(sug),
apply: (view) => {
if (sug.kind() === SuggestionKind.Remove) {
view.dispatch({
changes: {
from: span.start,
to: span.end,
insert: '',
},
});
} else if (sug.kind() === SuggestionKind.Replace) {
view.dispatch({
changes: {
from: span.start,
to: span.end,
insert: sug.get_replacement_text(),
},
});
} else if (sug.kind() === SuggestionKind.InsertAfter) {
view.dispatch({
changes: {
from: span.end,
to: span.end,
insert: sug.get_replacement_text(),
},
});
}
},
};
});
if (lint.lint_kind() === 'Spelling') {
const word = lint.get_problem_text();
actions.push({
name: '📖',
title: `Add “${word}” to your dictionary`,
apply: (_view) => {
this.harper.importWords([word]);
this.reinitialize();
},
const actions = lint.suggestions().map((sug) => {
return {
name:
sug.kind() == SuggestionKind.Replace
? sug.get_replacement_text()
: suggestionToLabel(sug),
title: suggestionToLabel(sug),
apply: (view) => {
if (sug.kind() === SuggestionKind.Remove) {
view.dispatch({
changes: {
from: span.start,
to: span.end,
insert: '',
},
});
} else if (sug.kind() === SuggestionKind.Replace) {
view.dispatch({
changes: {
from: span.start,
to: span.end,
insert: sug.get_replacement_text(),
},
});
} else if (sug.kind() === SuggestionKind.InsertAfter) {
view.dispatch({
changes: {
from: span.end,
to: span.end,
insert: sug.get_replacement_text(),
},
});
}
},
};
});
}
return {
from: span.start,
to: span.end,
severity: 'error',
title: lint.lint_kind_pretty(),
renderMessage: (_view) => {
const node = document.createElement('template');
node.innerHTML = lint.message_html();
return node.content;
},
ignore: async () => {
await this.ignoreLints(text, [lint]);
},
actions,
};
});
if (lint.lint_kind() === 'Spelling') {
const word = lint.get_problem_text();
actions.push({
name: '📖',
title: `Add “${word}” to your dictionary`,
apply: (_view) => {
this.harper.importWords([word]);
this.reinitialize();
},
});
}
return {
from: span.start,
to: span.end,
source: linterName,
severity: 'error',
title: lint.lint_kind_pretty(),
renderMessage: (_view) => {
const node = document.createElement('template');
node.innerHTML = lint.message_html();
return node.content;
},
ignore: async () => {
await this.ignoreLints(text, [lint]);
},
disable: async () => {
const lintConfig = await this.harper.getLintConfig();
lintConfig[linterName] = false;
await this.harper.setLintConfig(lintConfig);
await this.reinitialize();
},
actions,
};
}),
);
},
{
delay: this.delay,

View File

@@ -52,6 +52,8 @@ export interface Diagnostic {
actions?: readonly Action[];
/// A callback for when the user selects to "ignore" the diagnostic.
ignore?: () => void;
/// A callback for when the user selects to "disable" the source of the diagnostic.
disable?: () => void;
}
/// An action associated with a diagnostic.
@@ -428,21 +430,37 @@ function renderDiagnostic(view: EditorView, diagnostic: Diagnostic, inPanel: boo
);
}),
),
diagnostic.ignore &&
elt(
'div',
{
class: 'cm-diagnosticIgnore',
onclick: (e) => {
e.preventDefault();
if (diagnostic.ignore) {
diagnostic.ignore();
}
elt('div', { class: 'cm-diagnosticRow' }, [
diagnostic.ignore &&
elt(
'div',
{
class: 'cm-diagnosticIgnore',
onclick: (e) => {
e.preventDefault();
if (diagnostic.ignore) {
diagnostic.ignore();
}
},
},
},
'Ignore Diagnostic',
),
diagnostic.source && elt('div', { class: 'cm-diagnosticSource' }, diagnostic.source),
'Ignore Diagnostic',
),
diagnostic.disable &&
elt(
'div',
{
class: 'cm-diagnosticDisable',
onclick: (e) => {
e.preventDefault();
if (diagnostic.disable) {
diagnostic.disable();
}
},
title: `Disable ${diagnostic.source}`,
},
'Disable Rule',
),
]),
);
}
@@ -514,6 +532,12 @@ const baseTheme = EditorView.baseTheme({
gap: 'var(--size-4-2)',
},
'.cm-diagnosticRow': {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
},
'.cm-diagnosticAction': {
font: 'inherit',
border: 'none',
@@ -553,6 +577,15 @@ const baseTheme = EditorView.baseTheme({
textDecoration: 'underline',
},
'.cm-diagnosticDisable': {
padding: 'var(--size-4-1) 0px',
fontSize: 'var(--font-ui-small)',
},
'.cm-diagnosticDisable:hover': {
textDecoration: 'underline',
},
'.cm-lintRange': {
backgroundPosition: 'left bottom',
backgroundRepeat: 'repeat-x',