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:
commit
6e9ff89dda
@ -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
1
.gitignore
vendored
@ -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
|
||||||
|
@ -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 = () => {
|
||||||
|
@ -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.nativeEvent.selection;
|
||||||
this.selection = event.selection;
|
};
|
||||||
} else {
|
|
||||||
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}
|
||||||
|
@ -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'));
|
||||||
|
});
|
||||||
|
});
|
@ -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();
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user