1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-13 22:12:50 +02:00

Desktop: Resolves #2614: Unindent empty list markup on Enter (#2772)

This commit is contained in:
Shotaro Yamada
2020-06-05 02:36:10 +09:00
committed by GitHub
parent 48098b5c06
commit 949c92f6d6
2 changed files with 148 additions and 25 deletions

View File

@@ -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

View File

@@ -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]);
}