1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Merge remote-tracking branch 'upstream/release-2.13' into pr/merge-release-2-13

This commit is contained in:
Henry Heino 2023-12-19 10:23:05 -08:00
commit 6e9ff89dda
7 changed files with 69 additions and 20 deletions

View File

@ -556,6 +556,7 @@ packages/editor/CodeMirror/editorCommands/swapLine.js
packages/editor/CodeMirror/getScrollFraction.js packages/editor/CodeMirror/getScrollFraction.js
packages/editor/CodeMirror/markdown/computeSelectionFormatting.test.js packages/editor/CodeMirror/markdown/computeSelectionFormatting.test.js
packages/editor/CodeMirror/markdown/computeSelectionFormatting.js packages/editor/CodeMirror/markdown/computeSelectionFormatting.js
packages/editor/CodeMirror/markdown/decoratorExtension.test.js
packages/editor/CodeMirror/markdown/decoratorExtension.js packages/editor/CodeMirror/markdown/decoratorExtension.js
packages/editor/CodeMirror/markdown/markdownCommands.bulletedVsChecklist.test.js packages/editor/CodeMirror/markdown/markdownCommands.bulletedVsChecklist.test.js
packages/editor/CodeMirror/markdown/markdownCommands.test.js packages/editor/CodeMirror/markdown/markdownCommands.test.js

1
.gitignore vendored
View File

@ -536,6 +536,7 @@ packages/editor/CodeMirror/editorCommands/swapLine.js
packages/editor/CodeMirror/getScrollFraction.js packages/editor/CodeMirror/getScrollFraction.js
packages/editor/CodeMirror/markdown/computeSelectionFormatting.test.js packages/editor/CodeMirror/markdown/computeSelectionFormatting.test.js
packages/editor/CodeMirror/markdown/computeSelectionFormatting.js packages/editor/CodeMirror/markdown/computeSelectionFormatting.js
packages/editor/CodeMirror/markdown/decoratorExtension.test.js
packages/editor/CodeMirror/markdown/decoratorExtension.js packages/editor/CodeMirror/markdown/decoratorExtension.js
packages/editor/CodeMirror/markdown/markdownCommands.bulletedVsChecklist.test.js packages/editor/CodeMirror/markdown/markdownCommands.bulletedVsChecklist.test.js
packages/editor/CodeMirror/markdown/markdownCommands.test.js packages/editor/CodeMirror/markdown/markdownCommands.test.js

View File

@ -276,6 +276,7 @@ function NoteEditor(props: Props, ref: any) {
const setInitialSelectionJS = props.initialSelection ? ` const setInitialSelectionJS = props.initialSelection ? `
cm.select(${props.initialSelection.start}, ${props.initialSelection.end}); cm.select(${props.initialSelection.start}, ${props.initialSelection.end});
cm.execCommand('scrollSelectionIntoView');
` : ''; ` : '';
const editorSettings: EditorSettings = { const editorSettings: EditorSettings = {
@ -337,6 +338,7 @@ function NoteEditor(props: Props, ref: any) {
const settings = ${JSON.stringify(editorSettings)}; const settings = ${JSON.stringify(editorSettings)};
cm = codeMirrorBundle.initCodeMirror(parentElement, initialText, settings); cm = codeMirrorBundle.initCodeMirror(parentElement, initialText, settings);
${setInitialSelectionJS} ${setInitialSelectionJS}
window.onresize = () => { window.onresize = () => {

View File

@ -9,7 +9,7 @@ import NoteEditor from '../NoteEditor/NoteEditor';
import { Size } from '@joplin/utils/types'; import { Size } from '@joplin/utils/types';
const FileViewer = require('react-native-file-viewer').default; const FileViewer = require('react-native-file-viewer').default;
const React = require('react'); const React = require('react');
import { Keyboard, View, TextInput, StyleSheet, Linking, Image, Share } from 'react-native'; import { Keyboard, View, TextInput, StyleSheet, Linking, Image, Share, NativeSyntheticEvent } from 'react-native';
import { Platform, PermissionsAndroid } from 'react-native'; import { Platform, PermissionsAndroid } from 'react-native';
const { connect } = require('react-redux'); const { connect } = require('react-redux');
// const { MarkdownEditor } = require('@joplin/lib/../MarkdownEditor/index.js'); // const { MarkdownEditor } = require('@joplin/lib/../MarkdownEditor/index.js');
@ -52,10 +52,11 @@ import isEditableResource from '../NoteEditor/ImageEditor/isEditableResource';
import VoiceTypingDialog from '../voiceTyping/VoiceTypingDialog'; import VoiceTypingDialog from '../voiceTyping/VoiceTypingDialog';
import { voskEnabled } from '../../services/voiceTyping/vosk'; import { voskEnabled } from '../../services/voiceTyping/vosk';
import { isSupportedLanguage } from '../../services/voiceTyping/vosk.android'; import { isSupportedLanguage } from '../../services/voiceTyping/vosk.android';
import { ChangeEvent as EditorChangeEvent, UndoRedoDepthChangeEvent } from '@joplin/editor/events'; import { ChangeEvent as EditorChangeEvent, SelectionRangeChangeEvent, UndoRedoDepthChangeEvent } from '@joplin/editor/events';
import { join } from 'path'; import { join } from 'path';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { RefObject } from 'react'; import { RefObject } from 'react';
import { SelectionRange } from '../NoteEditor/types';
const urlUtils = require('@joplin/lib/urlUtils'); const urlUtils = require('@joplin/lib/urlUtils');
const emptyArray: any[] = []; const emptyArray: any[] = [];
@ -177,7 +178,7 @@ class NoteScreenComponent extends BaseScreenComponent<Props, State> implements B
private noteTagDialog_closeRequested: any; private noteTagDialog_closeRequested: any;
private onJoplinLinkClick_: any; private onJoplinLinkClick_: any;
private refreshResource: (resource: any, noteBody?: string)=> Promise<void>; private refreshResource: (resource: any, noteBody?: string)=> Promise<void>;
private selection: any; private selection: SelectionRange;
private menuOptionsCache_: Record<string, any>; private menuOptionsCache_: Record<string, any>;
private focusUpdateIID_: any; private focusUpdateIID_: any;
private folderPickerOptions_: any; private folderPickerOptions_: any;
@ -371,7 +372,6 @@ class NoteScreenComponent extends BaseScreenComponent<Props, State> implements B
this.undoRedoService_stackChange = this.undoRedoService_stackChange.bind(this); this.undoRedoService_stackChange = this.undoRedoService_stackChange.bind(this);
this.screenHeader_undoButtonPress = this.screenHeader_undoButtonPress.bind(this); this.screenHeader_undoButtonPress = this.screenHeader_undoButtonPress.bind(this);
this.screenHeader_redoButtonPress = this.screenHeader_redoButtonPress.bind(this); this.screenHeader_redoButtonPress = this.screenHeader_redoButtonPress.bind(this);
this.body_selectionChange = this.body_selectionChange.bind(this);
this.onBodyViewerLoadEnd = this.onBodyViewerLoadEnd.bind(this); this.onBodyViewerLoadEnd = this.onBodyViewerLoadEnd.bind(this);
this.onBodyViewerCheckboxChange = this.onBodyViewerCheckboxChange.bind(this); this.onBodyViewerCheckboxChange = this.onBodyViewerCheckboxChange.bind(this);
this.onBodyChange = this.onBodyChange.bind(this); this.onBodyChange = this.onBodyChange.bind(this);
@ -640,13 +640,13 @@ class NoteScreenComponent extends BaseScreenComponent<Props, State> implements B
this.scheduleSave(); this.scheduleSave();
} }
private body_selectionChange(event: any) { private onPlainEdtiorSelectionChange = (event: NativeSyntheticEvent<any>) => {
if (this.useEditorBeta()) {
this.selection = event.selection;
} else {
this.selection = event.nativeEvent.selection; this.selection = event.nativeEvent.selection;
} };
}
private onMarkdownEditorSelectionChange = (event: SelectionRangeChangeEvent) => {
this.selection = { start: event.from, end: event.to };
};
public makeSaveAction() { public makeSaveAction() {
return async () => { return async () => {
@ -1508,7 +1508,7 @@ class NoteScreenComponent extends BaseScreenComponent<Props, State> implements B
multiline={true} multiline={true}
value={note.body} value={note.body}
onChangeText={(text: string) => this.body_changeText(text)} onChangeText={(text: string) => this.body_changeText(text)}
onSelectionChange={this.body_selectionChange} onSelectionChange={this.onPlainEdtiorSelectionChange}
blurOnSubmit={false} blurOnSubmit={false}
selectionColor={theme.textSelectionColor} selectionColor={theme.textSelectionColor}
keyboardAppearance={theme.keyboardAppearance} keyboardAppearance={theme.keyboardAppearance}
@ -1530,7 +1530,7 @@ class NoteScreenComponent extends BaseScreenComponent<Props, State> implements B
initialText={note.body} initialText={note.body}
initialSelection={this.selection} initialSelection={this.selection}
onChange={this.onBodyChange} onChange={this.onBodyChange}
onSelectionChange={this.body_selectionChange} onSelectionChange={this.onMarkdownEditorSelectionChange}
onUndoRedoDepthChange={this.onUndoRedoDepthChange} onUndoRedoDepthChange={this.onUndoRedoDepthChange}
onAttach={() => this.showAttachMenu()} onAttach={() => this.showAttachMenu()}
readOnly={this.state.readOnly} readOnly={this.state.readOnly}

View File

@ -0,0 +1,30 @@
import { EditorSelection } from '@codemirror/state';
import createTestEditor from '../testUtil/createTestEditor';
import decoratorExtension from './decoratorExtension';
jest.retryTimes(2);
describe('decoratorExtension', () => {
it('should highlight code blocks within tables', async () => {
// Regression test for https://github.com/laurent22/joplin/issues/9477
const editorText = `
left | right
--------|-------
\`foo\` | bar
`;
const editor = await createTestEditor(
editorText,
// Put the initial cursor at the start of "foo"
EditorSelection.cursor(editorText.indexOf('foo')),
['TableRow', 'InlineCode'],
[decoratorExtension],
);
const codeBlock = editor.contentDOM.querySelector('.cm-inlineCode');
expect(codeBlock.textContent).toBe('`foo`');
expect(codeBlock.parentElement.classList.contains('.cm-tableRow'));
});
});

View File

@ -123,7 +123,7 @@ const nodeNameToMarkDecoration: Record<string, Decoration> = {
}; };
type DecorationDescription = { pos: number; length?: number; decoration: Decoration }; type DecorationDescription = { pos: number; length: number; decoration: Decoration };
// Returns a set of [Decoration]s, associated with block syntax groups that require // Returns a set of [Decoration]s, associated with block syntax groups that require
// full-line styling. // full-line styling.
@ -138,6 +138,7 @@ const computeDecorations = (view: EditorView) => {
const line = view.state.doc.lineAt(pos); const line = view.state.doc.lineAt(pos);
decorations.push({ decorations.push({
pos: line.from, pos: line.from,
length: 0,
decoration, decoration,
}); });
@ -193,13 +194,23 @@ const computeDecorations = (view: EditorView) => {
}); });
} }
decorations.sort((a, b) => a.pos - b.pos); // Decorations need to be sorted in ascending order first by start position,
// then by length. Adding items to the RangeSetBuilder in an incorrect order
// causes an exception to be thrown.
decorations.sort((a, b) => {
const posComparison = a.pos - b.pos;
if (posComparison !== 0) {
return posComparison;
}
const lengthComparison = a.length - b.length;
return lengthComparison;
});
// Items need to be added to a RangeSetBuilder in ascending order
const decorationBuilder = new RangeSetBuilder<Decoration>(); const decorationBuilder = new RangeSetBuilder<Decoration>();
for (const { pos, length, decoration } of decorations) { for (const { pos, length, decoration } of decorations) {
// Null length => entire line // Zero length => entire line
decorationBuilder.add(pos, pos + (length ?? 0), decoration); decorationBuilder.add(pos, pos + length, decoration);
} }
return decorationBuilder.finish(); return decorationBuilder.finish();
}; };

View File

@ -1,7 +1,7 @@
import { markdown } from '@codemirror/lang-markdown'; import { markdown } from '@codemirror/lang-markdown';
import { GFM as GithubFlavoredMarkdownExt } from '@lezer/markdown'; import { GFM as GithubFlavoredMarkdownExt } from '@lezer/markdown';
import { indentUnit, syntaxTree } from '@codemirror/language'; import { indentUnit, syntaxTree } from '@codemirror/language';
import { SelectionRange, EditorSelection, EditorState } from '@codemirror/state'; import { SelectionRange, EditorSelection, EditorState, Extension } from '@codemirror/state';
import { EditorView } from '@codemirror/view'; import { EditorView } from '@codemirror/view';
import { MarkdownMathExtension } from '../markdown/markdownMathParser'; import { MarkdownMathExtension } from '../markdown/markdownMathParser';
import forceFullParse from './forceFullParse'; import forceFullParse from './forceFullParse';
@ -10,7 +10,10 @@ import loadLangauges from './loadLanguages';
// Creates and returns a minimal editor with markdown extensions. Waits to return the editor // Creates and returns a minimal editor with markdown extensions. Waits to return the editor
// until all syntax tree tags in `expectedSyntaxTreeTags` exist. // until all syntax tree tags in `expectedSyntaxTreeTags` exist.
const createTestEditor = async ( const createTestEditor = async (
initialText: string, initialSelection: SelectionRange, expectedSyntaxTreeTags: string[], initialText: string,
initialSelection: SelectionRange,
expectedSyntaxTreeTags: string[],
extraExtensions: Extension[] = [],
): Promise<EditorView> => { ): Promise<EditorView> => {
await loadLangauges(); await loadLangauges();
@ -23,6 +26,7 @@ const createTestEditor = async (
}), }),
indentUnit.of('\t'), indentUnit.of('\t'),
EditorState.tabSize.of(4), EditorState.tabSize.of(4),
extraExtensions,
], ],
}); });