1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-11-27 08:21:03 +02:00

Desktop: Fixes #10538: Fix wrong text selected when adding a link in the beta editor (#10542)

This commit is contained in:
Henry Heino 2024-06-10 23:41:41 -07:00 committed by GitHub
parent 629e968878
commit b17f28ce94
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 98 additions and 55 deletions

View File

@ -762,8 +762,6 @@ packages/editor/CodeMirror/markdown/markdownCommands.toggleList.test.js
packages/editor/CodeMirror/markdown/markdownCommands.js
packages/editor/CodeMirror/markdown/markdownMathParser.test.js
packages/editor/CodeMirror/markdown/markdownMathParser.js
packages/editor/CodeMirror/markdown/markdownReformatter.test.js
packages/editor/CodeMirror/markdown/markdownReformatter.js
packages/editor/CodeMirror/pluginApi/PluginLoader.js
packages/editor/CodeMirror/pluginApi/codeMirrorRequire.js
packages/editor/CodeMirror/pluginApi/customEditorCompletion.test.js
@ -776,6 +774,8 @@ packages/editor/CodeMirror/testUtil/loadLanguages.js
packages/editor/CodeMirror/testUtil/pressReleaseKey.js
packages/editor/CodeMirror/testUtil/typeText.js
packages/editor/CodeMirror/theme.js
packages/editor/CodeMirror/util/editorStateUtils.test.js
packages/editor/CodeMirror/util/editorStateUtils.js
packages/editor/CodeMirror/util/isInSyntaxNode.js
packages/editor/CodeMirror/util/setupVim.js
packages/editor/SelectionFormatting.js

4
.gitignore vendored
View File

@ -741,8 +741,6 @@ packages/editor/CodeMirror/markdown/markdownCommands.toggleList.test.js
packages/editor/CodeMirror/markdown/markdownCommands.js
packages/editor/CodeMirror/markdown/markdownMathParser.test.js
packages/editor/CodeMirror/markdown/markdownMathParser.js
packages/editor/CodeMirror/markdown/markdownReformatter.test.js
packages/editor/CodeMirror/markdown/markdownReformatter.js
packages/editor/CodeMirror/pluginApi/PluginLoader.js
packages/editor/CodeMirror/pluginApi/codeMirrorRequire.js
packages/editor/CodeMirror/pluginApi/customEditorCompletion.test.js
@ -755,6 +753,8 @@ packages/editor/CodeMirror/testUtil/loadLanguages.js
packages/editor/CodeMirror/testUtil/pressReleaseKey.js
packages/editor/CodeMirror/testUtil/typeText.js
packages/editor/CodeMirror/theme.js
packages/editor/CodeMirror/util/editorStateUtils.test.js
packages/editor/CodeMirror/util/editorStateUtils.js
packages/editor/CodeMirror/util/isInSyntaxNode.js
packages/editor/CodeMirror/util/setupVim.js
packages/editor/SelectionFormatting.js

View File

@ -12,25 +12,6 @@ import { focus } from '@joplin/lib/utils/focusHandler';
const logger = Logger.create('CodeMirror 6 commands');
const wrapSelectionWithStrings = (editor: CodeMirrorControl, string1: string, string2 = '', defaultText = '') => {
if (editor.somethingSelected()) {
editor.wrapSelections(string1, string2);
} else {
editor.wrapSelections(string1 + defaultText, string2);
// Now select the default text so the user can replace it
const selections = editor.listSelections();
const newSelections = [];
for (let i = 0; i < selections.length; i++) {
const s = selections[i];
const anchor = { line: s.anchor.line, ch: s.anchor.ch + string1.length };
const head = { line: s.head.line, ch: s.head.ch - string2.length };
newSelections.push({ anchor: anchor, head: head });
}
editor.setSelections(newSelections);
}
};
interface Props {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
webviewRef: RefObject<any>;
@ -92,7 +73,9 @@ const useEditorCommands = (props: Props) => {
textLink: async () => {
const url = await dialogs.prompt(_('Insert Hyperlink'));
focus('useEditorCommands::textLink', editorRef.current);
if (url) wrapSelectionWithStrings(editorRef.current, '[', `](${url})`);
if (url) {
editorRef.current.wrapSelections('[', `](${url})`);
}
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
insertText: (value: any) => editorRef.current.insertText(value),

View File

@ -426,34 +426,6 @@ export default class CodeMirror5Emulation extends BaseCodeMirror5Emulation {
);
}
// TODO: Currently copied from useCursorUtils.ts.
// TODO: Remove the duplicate code when CodeMirror 5 is eventually removed.
public wrapSelections(string1: string, string2: string) {
const selectedStrings = this.getSelections();
// Batches the insert operations, if this wasn't done the inserts
// could potentially overwrite one another
this.operation(() => {
for (let i = 0; i < selectedStrings.length; i++) {
const selected = selectedStrings[i];
// Remove white space on either side of selection
const start = selected.search(/[^\s]/);
const end = selected.search(/[^\s](?=[\s]*$)/);
const core = selected.substring(start, end - start + 1);
// If selection can be toggled do that
if (core.startsWith(string1) && core.endsWith(string2)) {
const inside = core.substring(string1.length, core.length - string1.length - string2.length);
selectedStrings[i] = selected.substring(0, start) + inside + selected.substring(end + 1);
} else {
selectedStrings[i] = selected.substring(0, start) + string1 + core + string2 + selected.substring(end + 1);
}
}
this.replaceSelections(selectedStrings);
});
}
public static commands = (() => {
const commands: Record<string, CodeMirror5Command> = {
...BaseCodeMirror5Emulation.commands,

View File

@ -2,6 +2,7 @@ import { ViewPlugin } from '@codemirror/view';
import createEditorControl from './testUtil/createEditorControl';
import { EditorCommandType } from '../types';
import pressReleaseKey from './testUtil/pressReleaseKey';
import { EditorSelection } from '@codemirror/state';
describe('CodeMirrorControl', () => {
it('clearHistory should clear the undo/redo history', () => {
@ -113,4 +114,71 @@ describe('CodeMirrorControl', () => {
control.updateBody('Hello\r\nWorld\r\ntest');
control.updateBody('Hello\r\n');
});
it.each([
{
initialText: 'Hello\nWorld',
selection: EditorSelection.cursor(5),
left: '[[',
right: ']].',
typeText: null,
expected: 'Hello[[]].\nWorld',
},
{
initialText: 'Hello\nWorld',
selection: EditorSelection.cursor(0),
left: 'before',
right: 'after.',
typeText: ' cursor ',
expected: 'before cursor after.Hello\nWorld',
},
{
initialText: 'Hello\nWorld',
selection: EditorSelection.range(0, 5),
left: '[',
right: '](test)',
typeText: null,
expected: '[Hello](test)\nWorld',
},
{
initialText: 'Hello\nWorld',
selection: EditorSelection.range(0, 5),
left: '[',
right: '](test)',
typeText: 'replaced',
expected: '[replaced](test)\nWorld',
},
{
initialText: 'Hello\nWorld',
selection: EditorSelection.create([
EditorSelection.cursor(0), EditorSelection.cursor(5),
]),
left: '[',
right: '](test)',
typeText: 'cursor',
expected: '[cursor](test)Hello[cursor](test)\nWorld',
},
{
initialText: 'This is a\ntest',
selection: EditorSelection.create([
EditorSelection.range(5, 9), EditorSelection.cursor(14),
]),
left: '[',
right: ']',
typeText: 'cursor',
expected: 'This [cursor]\ntest[cursor]',
},
])('wrapSelections should surround all selections with the given text (case %#)', ({ initialText, selection, typeText, left, right, expected }) => {
const control = createEditorControl(initialText);
control.editor.dispatch({
selection,
});
control.wrapSelections(left, right);
if (typeText) {
control.insertText(typeText);
}
expect(control.editor.state.doc.toString()).toBe(expected);
});
});

View File

@ -8,6 +8,7 @@ import { SearchQuery, setSearchQuery } from '@codemirror/search';
import PluginLoader from './pluginApi/PluginLoader';
import customEditorCompletion, { editorCompletionSource, enableLanguageDataAutocomplete } from './pluginApi/customEditorCompletion';
import { CompletionSource } from '@codemirror/autocomplete';
import { RegionSpec, toggleInlineSelectionFormat } from './util/editorStateUtils';
interface Callbacks {
onUndoRedo(): void;
@ -93,6 +94,25 @@ export default class CodeMirrorControl extends CodeMirror5Emulation implements E
this.editor.dispatch(this.editor.state.replaceSelection(text), { userEvent });
}
public wrapSelections(start: string, end: string) {
const regionSpec = RegionSpec.of({ template: { start, end } });
this.editor.dispatch(
this.editor.state.changeByRange(range => {
const update = toggleInlineSelectionFormat(this.editor.state, regionSpec, range);
if (!update.range.empty) {
// Deselect the start and end characters (roughly preserve the original
// selection).
update.range = EditorSelection.range(
update.range.from + start.length,
update.range.to - end.length,
);
}
return update;
}),
);
}
public updateBody(newBody: string) {
// TODO: doc.toString() can be slow for large documents.
const currentBody = this.editor.state.doc.toString();

View File

@ -11,7 +11,7 @@ import {
RegionSpec, growSelectionToNode, renumberSelectedLists,
toggleInlineFormatGlobally, toggleRegionFormatGlobally, toggleSelectedLinesStartWith,
isIndentationEquivalent, stripBlockquote, tabsToSpaces,
} from './markdownReformatter';
} from '../util/editorStateUtils';
import intersectsSyntaxNode from '../util/isInSyntaxNode';
const startingSpaceRegex = /^(\s*)/;

View File

@ -1,6 +1,6 @@
import {
findInlineMatch, MatchSide, RegionSpec, renumberSelectedLists, tabsToSpaces, toggleRegionFormatGlobally,
} from './markdownReformatter';
} from './editorStateUtils';
import { Text as DocumentText, EditorSelection, EditorState } from '@codemirror/state';
import { indentUnit } from '@codemirror/language';
import createTestEditor from '../testUtil/createTestEditor';

View File

@ -268,7 +268,7 @@ const toggleInlineRegionSurrounded = (
changes.push({
from: sel.to,
insert: spec.template.start,
insert: spec.template.end,
});
// If not a caret,