一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
77 行
2.5 KiB
TypeScript
77 行
2.5 KiB
TypeScript
import { MarkType } from 'prosemirror-model';
|
|
import { EditorState } from 'prosemirror-state';
|
|
import SelectionFormatting, { MutableSelectionFormatting, defaultSelectionFormatting } from '../../SelectionFormatting';
|
|
import schema from '../schema';
|
|
import { EditorSettings } from '../../types';
|
|
|
|
const computeSelectionFormatting = (state: EditorState, settings: EditorSettings): SelectionFormatting => {
|
|
const doc = state.doc;
|
|
const selection = state.selection;
|
|
const formatting: MutableSelectionFormatting = {
|
|
...defaultSelectionFormatting,
|
|
selectedText: doc.textBetween(selection.from, selection.to),
|
|
spellChecking: settings.spellcheckEnabled,
|
|
};
|
|
|
|
doc.nodesBetween(selection.from, selection.to, (node) => {
|
|
if (node.type === schema.nodes.heading) {
|
|
formatting.headerLevel = node.attrs.level;
|
|
}
|
|
if (node.type === schema.nodes.ordered_list) {
|
|
formatting.inOrderedList = true;
|
|
formatting.listLevel ++;
|
|
}
|
|
if (node.type === schema.nodes.bullet_list) {
|
|
formatting.inUnorderedList = true;
|
|
formatting.listLevel ++;
|
|
}
|
|
if (node.type === schema.nodes.task_list) {
|
|
formatting.inChecklist = true;
|
|
formatting.listLevel ++;
|
|
}
|
|
if (node.type === schema.nodes.pre_block) {
|
|
formatting.inCode = true;
|
|
}
|
|
const linkMark = node.marks.find(mark => mark.type === schema.marks.link);
|
|
if (linkMark) {
|
|
formatting.linkData = {
|
|
linkText: node.textContent,
|
|
linkURL: linkMark.attrs.href,
|
|
};
|
|
formatting.inLink = true;
|
|
}
|
|
});
|
|
|
|
const hasMark = (type: MarkType) => {
|
|
if (!type) {
|
|
throw new Error(`Type not found in schema: ${type}`);
|
|
}
|
|
|
|
// See the corresponding logic in prosemirror-example-setup:
|
|
// https://github.com/ProseMirror/prosemirror-example-setup/blob/8c11be6850604081dceda8f36e08d2426875e19a/src/menu.ts#L58
|
|
//
|
|
// Using state.storedMarks with an empty selection is important to accurately reflect changes made
|
|
// **by the user** to the current marks associated with the cursor.
|
|
if (selection.empty) {
|
|
return !!type.isInSet(state.storedMarks ?? selection.$head.marks());
|
|
} else {
|
|
return doc.rangeHasMark(selection.from, selection.to, type);
|
|
}
|
|
};
|
|
|
|
formatting.inCode ||= hasMark(schema.marks.code);
|
|
if (formatting.inCode) {
|
|
formatting.unspellCheckableRegion = true;
|
|
}
|
|
formatting.italicized = hasMark(schema.marks.emphasis);
|
|
formatting.bolded = hasMark(schema.marks.strong);
|
|
|
|
if (formatting.unspellCheckableRegion) {
|
|
formatting.spellChecking = false;
|
|
}
|
|
|
|
return formatting;
|
|
};
|
|
export default computeSelectionFormatting;
|
|
|