Improve table editing
This commit is contained in:
@@ -23,9 +23,30 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enter/Tab cell navigation must yield to an open autocomplete popup so those
|
||||||
|
// keys still accept a suggestion (e.g. a [[wiki-link]] typed inside a cell).
|
||||||
|
// The table keymap sits at Prec.highest, so without this guard it would
|
||||||
|
// swallow the key before the completion keymap ever sees it. Detect the popup
|
||||||
|
// via its tooltip element rather than exporting completionStatus from the
|
||||||
|
// vendored bundle (which would force a bundle rebuild).
|
||||||
|
function completionOpen() {
|
||||||
|
return !!document.querySelector('.cm-tooltip-autocomplete');
|
||||||
|
}
|
||||||
|
|
||||||
|
function tableNavKey(fn) {
|
||||||
|
var handler = tableKey(fn);
|
||||||
|
return function (view) {
|
||||||
|
if (completionOpen()) return false;
|
||||||
|
return handler(view);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
var tableKeymap = [
|
var tableKeymap = [
|
||||||
{ key: 'Shift-Enter', run: tableKey(T.insertRowBelow) },
|
{ key: 'Shift-Enter', run: tableKey(T.insertRowBelow) },
|
||||||
{ key: 'Shift-Delete', run: tableKey(T.deleteRow) },
|
{ key: 'Shift-Delete', run: tableKey(T.deleteRow) },
|
||||||
|
{ key: 'Enter', run: tableNavKey(T.nextRowSameColumn) },
|
||||||
|
{ key: 'Tab', run: tableNavKey(T.nextCell) },
|
||||||
|
{ key: 'Shift-Tab', run: tableNavKey(T.prevCell) },
|
||||||
];
|
];
|
||||||
|
|
||||||
var state = CM.EditorState.create({
|
var state = CM.EditorState.create({
|
||||||
|
|||||||
@@ -262,8 +262,94 @@ window.EditorTables = (function () {
|
|||||||
return formatTableText(newLines.join('\n'), Math.min(newCursor, newLines.join('\n').length));
|
return formatTableText(newLines.join('\n'), Math.min(newCursor, newLines.join('\n').length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Absolute cursor offset at the start of the `col`-th cell's content on
|
||||||
|
// `lineIdx` of a *formatted* table (i.e. just past the "| " / " | " that
|
||||||
|
// opens the cell). Clamps to the last cell if col overflows the row.
|
||||||
|
function cellCursor(text, lineIdx, col) {
|
||||||
|
var lines = text.split('\n');
|
||||||
|
var offset = 0;
|
||||||
|
for (var i = 0; i < lineIdx; i++) offset += lines[i].length + 1;
|
||||||
|
var line = lines[lineIdx] || '';
|
||||||
|
var pipes = [];
|
||||||
|
for (var c = 0; c < line.length; c++) if (line.charAt(c) === '|') pipes.push(c);
|
||||||
|
if (pipes.length === 0) return Math.min(offset, text.length);
|
||||||
|
var p = pipes[Math.min(col, pipes.length - 1)];
|
||||||
|
return Math.min(offset + p + 2, text.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared engine for the Enter / Tab / Shift-Tab cell navigation. Reformats
|
||||||
|
// the table and drops the cursor into the target cell, appending an empty
|
||||||
|
// row when navigation runs past the last row. Returns null when the cursor
|
||||||
|
// is not in a table (so the caller can fall back to the editor default).
|
||||||
|
function moveInTable(text, cursorPos, mode) {
|
||||||
|
var range = findTableRange(text, cursorPos);
|
||||||
|
if (!range) return null;
|
||||||
|
var colIdx = getCursorColumn(text, cursorPos);
|
||||||
|
if (colIdx === null) return null;
|
||||||
|
|
||||||
|
var sepRel = -1, colCount = 0;
|
||||||
|
for (var i = range.start; i <= range.end; i++) {
|
||||||
|
var cells = parseTableRow(range.lines[i]);
|
||||||
|
if (cells.length > colCount) colCount = cells.length;
|
||||||
|
if (sepRel === -1 && isSeparatorRow(cells)) sepRel = i - range.start;
|
||||||
|
}
|
||||||
|
if (sepRel === -1) return null;
|
||||||
|
|
||||||
|
var lastRel = range.end - range.start;
|
||||||
|
var rowRel = range.cursorLine - range.start;
|
||||||
|
var targetRow = rowRel, targetCol = colIdx;
|
||||||
|
|
||||||
|
if (mode === 'enter') {
|
||||||
|
targetRow = rowRel + 1;
|
||||||
|
if (targetRow === sepRel) targetRow++;
|
||||||
|
} else if (mode === 'tab') {
|
||||||
|
targetCol = colIdx + 1;
|
||||||
|
if (targetCol >= colCount) {
|
||||||
|
targetCol = 0;
|
||||||
|
targetRow = rowRel + 1;
|
||||||
|
if (targetRow === sepRel) targetRow++;
|
||||||
|
}
|
||||||
|
} else if (mode === 'shifttab') {
|
||||||
|
targetCol = colIdx - 1;
|
||||||
|
if (targetCol < 0) {
|
||||||
|
targetCol = colCount - 1;
|
||||||
|
targetRow = rowRel - 1;
|
||||||
|
if (targetRow === sepRel) targetRow--;
|
||||||
|
}
|
||||||
|
if (targetRow < 0) return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = range.lines.slice();
|
||||||
|
if (targetRow > lastRel) {
|
||||||
|
var emptyCells = [];
|
||||||
|
for (var c = 0; c < colCount; c++) emptyCells.push('');
|
||||||
|
var emptyLine = '| ' + emptyCells.join(' | ') + ' |';
|
||||||
|
while (lastRel < targetRow) {
|
||||||
|
lines.splice(range.start + lastRel + 1, 0, emptyLine);
|
||||||
|
lastRel++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tableStartOffset = 0;
|
||||||
|
for (var i = 0; i < range.start; i++) tableStartOffset += lines[i].length + 1;
|
||||||
|
var formatted = formatTableText(lines.join('\n'), tableStartOffset);
|
||||||
|
if (!formatted) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: formatted.text,
|
||||||
|
cursor: cellCursor(formatted.text, range.start + targetRow, targetCol),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextRowSameColumn(text, cursorPos) { return moveInTable(text, cursorPos, 'enter'); }
|
||||||
|
function nextCell(text, cursorPos) { return moveInTable(text, cursorPos, 'tab'); }
|
||||||
|
function prevCell(text, cursorPos) { return moveInTable(text, cursorPos, 'shifttab'); }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
formatTableText: formatTableText,
|
formatTableText: formatTableText,
|
||||||
|
nextRowSameColumn: nextRowSameColumn,
|
||||||
|
nextCell: nextCell,
|
||||||
|
prevCell: prevCell,
|
||||||
setColumnAlignment: setColumnAlignment,
|
setColumnAlignment: setColumnAlignment,
|
||||||
insertColumn: insertColumn,
|
insertColumn: insertColumn,
|
||||||
deleteColumn: deleteColumn,
|
deleteColumn: deleteColumn,
|
||||||
|
|||||||
Reference in New Issue
Block a user