You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-13 22:12:50 +02:00
@@ -78,7 +78,6 @@ function AceEditor(props: NoteBodyEditorProps, ref: any) {
|
||||
|
||||
const [renderedBody, setRenderedBody] = useState<RenderedBody>(defaultRenderedBody()); // Viewer content
|
||||
const [editor, setEditor] = useState(null);
|
||||
const [lastKeys, setLastKeys] = useState([]);
|
||||
const [webviewReady, setWebviewReady] = useState(false);
|
||||
|
||||
const previousRenderedBody = usePrevious(renderedBody);
|
||||
@@ -335,15 +334,6 @@ function AceEditor(props: NoteBodyEditorProps, ref: any) {
|
||||
wrapSelectionWithStrings('', '', resourceMds.join('\n'));
|
||||
}, [wrapSelectionWithStrings]);
|
||||
|
||||
const onEditorKeyDown = useCallback((event: any) => {
|
||||
setLastKeys(prevLastKeys => {
|
||||
const keys = prevLastKeys.slice();
|
||||
keys.push(event.key);
|
||||
while (keys.length > 2) keys.splice(0, 1);
|
||||
return keys;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const editorCutText = useCallback(() => {
|
||||
const text = selectedText(selectionRange(editor), props.content);
|
||||
if (!text) return;
|
||||
@@ -450,16 +440,12 @@ function AceEditor(props: NoteBodyEditorProps, ref: any) {
|
||||
}
|
||||
|
||||
document.querySelector('#note-editor').addEventListener('paste', onEditorPaste, true);
|
||||
document.querySelector('#note-editor').addEventListener('keydown', onEditorKeyDown);
|
||||
document.querySelector('#note-editor').addEventListener('contextmenu', onEditorContextMenu);
|
||||
|
||||
// Disable Markdown auto-completion (eg. auto-adding a dash after a line with a dash.
|
||||
// https://github.com/ajaxorg/ace/issues/2754
|
||||
// @ts-ignore: Keep the function signature as-is despite unusued arguments
|
||||
editor.getSession().getMode().getNextLineIndent = function(state: any, line: string) {
|
||||
const ls = lastKeys;
|
||||
if (ls.length >= 2 && ls[ls.length - 1] === 'Enter' && ls[ls.length - 2] === 'Enter') return this.$getIndent(line);
|
||||
|
||||
const leftSpaces = lineLeftSpaces(line);
|
||||
const lineNoLeftSpaces = line.trimLeft();
|
||||
|
||||
@@ -475,10 +461,9 @@ function AceEditor(props: NoteBodyEditorProps, ref: any) {
|
||||
|
||||
return () => {
|
||||
document.querySelector('#note-editor').removeEventListener('paste', onEditorPaste, true);
|
||||
document.querySelector('#note-editor').removeEventListener('keydown', onEditorKeyDown);
|
||||
document.querySelector('#note-editor').removeEventListener('contextmenu', onEditorContextMenu);
|
||||
};
|
||||
}, [editor, onEditorPaste, onEditorContextMenu, lastKeys]);
|
||||
}, [editor, onEditorPaste, onEditorContextMenu]);
|
||||
|
||||
useEffect(() => {
|
||||
// We disable dragging ot text because it's not really supported, and
|
||||
|
@@ -1,15 +1,60 @@
|
||||
import { useEffect } from 'react';
|
||||
import { selectionRange } from './index';
|
||||
const markdownUtils = require('lib/markdownUtils');
|
||||
|
||||
interface HookDependencies {
|
||||
editor: any,
|
||||
// The line that contains only `- ` is
|
||||
// recognized as a heading in Ace.
|
||||
function hyphenEmptyListItem(tokens: any[]) {
|
||||
return (
|
||||
tokens.length === 2 &&
|
||||
tokens[0].type === 'markup.heading.2' &&
|
||||
tokens[0].value === '-' &&
|
||||
tokens[1].type === 'text.xml' &&
|
||||
tokens[1].value === ' '
|
||||
);
|
||||
}
|
||||
|
||||
export default function useListIdent(dependencies:HookDependencies) {
|
||||
// Returns tokens of the line if it starts with a 'markup.list' token.
|
||||
function listTokens(editor: any, row: number) {
|
||||
const tokens = editor.session.getTokens(row);
|
||||
if (
|
||||
!(tokens.length > 0 && tokens[0].type === 'markup.list') &&
|
||||
!hyphenEmptyListItem(tokens)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
function countIndent(line: string): number {
|
||||
return line.match(/\t| {4}/g)?.length || 0;
|
||||
}
|
||||
|
||||
// Finds the list item with indent level `prevIndent`.
|
||||
function findPrevListNum(editor: any, row: number, indent: number) {
|
||||
while (row > 0) {
|
||||
row--;
|
||||
const line = editor.session.getLine(row);
|
||||
|
||||
if (countIndent(line) === indent) {
|
||||
const num = markdownUtils.olLineNumber(line.trimLeft());
|
||||
if (num) {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
interface HookDependencies {
|
||||
editor: any;
|
||||
}
|
||||
|
||||
export default function useListIdent(dependencies: HookDependencies) {
|
||||
const { editor } = dependencies;
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor) return;
|
||||
if (!editor) return () => {};
|
||||
|
||||
// Markdown list indentation. (https://github.com/laurent22/joplin/pull/2713)
|
||||
// If the current line starts with `markup.list` token,
|
||||
@@ -20,13 +65,19 @@ export default function useListIdent(dependencies:HookDependencies) {
|
||||
const range = selectionRange(editor);
|
||||
if (range.isEmpty()) {
|
||||
const row = range.start.row;
|
||||
const tokens = this.session.getTokens(row);
|
||||
const tokens = listTokens(this, row);
|
||||
|
||||
if (tokens.length > 0 && tokens[0].type == 'markup.list') {
|
||||
if (tokens.length > 0) {
|
||||
if (tokens[0].value.search(/\d+\./) != -1) {
|
||||
// Resets numbered list to 1.
|
||||
this.session.replace({ start: { row, column: 0 }, end: { row, column: tokens[0].value.length } },
|
||||
tokens[0].value.replace(/\d+\./, '1.'));
|
||||
const line = this.session.getLine(row);
|
||||
const n = findPrevListNum(this, row, countIndent(line) + 1) + 1;
|
||||
this.session.replace(
|
||||
{
|
||||
start: { row, column: 0 },
|
||||
end: { row, column: tokens[0].value.length },
|
||||
},
|
||||
tokens[0].value.replace(/\d+\./, `${n}.`)
|
||||
);
|
||||
}
|
||||
|
||||
this.session.indentRows(row, row, '\t');
|
||||
@@ -36,5 +87,92 @@ export default function useListIdent(dependencies:HookDependencies) {
|
||||
|
||||
if (originalEditorIndent) originalEditorIndent.call(this);
|
||||
};
|
||||
|
||||
// Correct the number of numbered list item when outdenting.
|
||||
editor.commands.addCommand({
|
||||
name: 'markdownOutdent',
|
||||
bindKey: { win: 'Shift+Tab', mac: 'Shift+Tab' },
|
||||
multiSelectAction: 'forEachLine',
|
||||
exec: function(editor: any) {
|
||||
const range = selectionRange(editor);
|
||||
|
||||
if (range.isEmpty()) {
|
||||
const row = range.start.row;
|
||||
|
||||
const tokens = editor.session.getTokens(row);
|
||||
if (tokens.length && tokens[0].type === 'markup.list') {
|
||||
const matches = tokens[0].value.match(/^(\t+)\d+\./);
|
||||
if (matches && matches.length) {
|
||||
const indent = countIndent(matches[1]);
|
||||
const n = findPrevListNum(editor, row, indent - 1) + 1;
|
||||
console.log(n);
|
||||
editor.session.replace(
|
||||
{
|
||||
start: { row, column: 0 },
|
||||
end: { row, column: tokens[0].value.length },
|
||||
},
|
||||
tokens[0].value.replace(/\d+\./, `${n}.`)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editor.blockOutdent();
|
||||
},
|
||||
readonly: false,
|
||||
});
|
||||
|
||||
// Delete a list markup (e.g. `- `) from an empty list item on hitting Enter.
|
||||
// (https://github.com/laurent22/joplin/pull/2772)
|
||||
editor.commands.addCommand({
|
||||
name: 'markdownEnter',
|
||||
bindKey: 'Enter',
|
||||
multiSelectAction: 'forEach',
|
||||
exec: function(editor: any) {
|
||||
const range = editor.getSelectionRange();
|
||||
const tokens = listTokens(editor, range.start.row);
|
||||
|
||||
const emptyListItem =
|
||||
tokens.length === 1 || hyphenEmptyListItem(tokens);
|
||||
const emptyCheckboxItem =
|
||||
tokens.length === 3 &&
|
||||
['[ ]', '[x]'].includes(tokens[1].value) &&
|
||||
tokens[2].value === ' ';
|
||||
|
||||
if (!range.isEmpty() || !(emptyListItem || emptyCheckboxItem)) {
|
||||
editor.insert('\n');
|
||||
// Cursor can go out of the view after inserting '\n'.
|
||||
editor.renderer.scrollCursorIntoView();
|
||||
return;
|
||||
}
|
||||
|
||||
const row = range.start.row;
|
||||
const line = editor.session.getLine(row);
|
||||
let indent = editor
|
||||
.getSession()
|
||||
.getMode()
|
||||
.getNextLineIndent(null, line);
|
||||
if (indent.startsWith('\t')) {
|
||||
indent = indent.slice(1);
|
||||
} else {
|
||||
indent = '';
|
||||
}
|
||||
|
||||
editor.session.replace(
|
||||
{
|
||||
start: { row, column: 0 },
|
||||
end: { row, column: line.length },
|
||||
},
|
||||
indent
|
||||
);
|
||||
},
|
||||
readOnly: false,
|
||||
});
|
||||
|
||||
return () => {
|
||||
editor.indent = originalEditorIndent;
|
||||
editor.commands.removeCommand('markdownOutdent');
|
||||
editor.commands.removeCommand('markdownEnter');
|
||||
};
|
||||
}, [editor]);
|
||||
}
|
||||
|
Reference in New Issue
Block a user