Skip to content

Commit df4bd40

Browse files
committed
Adopt the UndoRedo service in the editor
1 parent 757006f commit df4bd40

18 files changed

Lines changed: 438 additions & 456 deletions

File tree

src/vs/editor/browser/controller/coreCommands.ts

Lines changed: 142 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands
2525
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
2626
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
2727
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
28+
import { EditorOption } from 'vs/editor/common/config/editorOptions';
2829

2930
const CORE_WEIGHT = KeybindingWeight.EditorCore;
3031

@@ -1529,6 +1530,102 @@ export namespace CoreNavigationCommands {
15291530
});
15301531
}
15311532

1533+
/**
1534+
* A command that will:
1535+
* 1. invoke a command on the focused editor.
1536+
* 2. otherwise, invoke a browser built-in command on the `activeElement`.
1537+
* 3. otherwise, invoke a command on the workbench active editor.
1538+
*/
1539+
abstract class EditorOrNativeTextInputCommand extends Command {
1540+
1541+
public runCommand(accessor: ServicesAccessor, args: any): void {
1542+
1543+
const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
1544+
// Only if editor text focus (i.e. not if editor has widget focus).
1545+
if (focusedEditor && focusedEditor.hasTextFocus()) {
1546+
return this.runEditorCommand(accessor, focusedEditor, args);
1547+
}
1548+
1549+
// Ignore this action when user is focused on an element that allows for entering text
1550+
const activeElement = <HTMLElement>document.activeElement;
1551+
if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
1552+
return this.runDOMCommand();
1553+
}
1554+
1555+
// Redirecting to active editor
1556+
const activeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor();
1557+
if (activeEditor) {
1558+
activeEditor.focus();
1559+
return this.runEditorCommand(accessor, activeEditor, args);
1560+
}
1561+
}
1562+
1563+
public abstract runDOMCommand(): void;
1564+
public abstract runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void;
1565+
}
1566+
1567+
class SelectAllCommand extends EditorOrNativeTextInputCommand {
1568+
constructor() {
1569+
super({
1570+
id: 'editor.action.selectAll',
1571+
precondition: EditorContextKeys.textInputFocus,
1572+
kbOpts: {
1573+
weight: CORE_WEIGHT,
1574+
kbExpr: null,
1575+
primary: KeyMod.CtrlCmd | KeyCode.KEY_A
1576+
},
1577+
menuOpts: [{
1578+
menuId: MenuId.MenubarSelectionMenu,
1579+
group: '1_basic',
1580+
title: nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"),
1581+
order: 1
1582+
}, {
1583+
menuId: MenuId.CommandPalette,
1584+
group: '',
1585+
title: nls.localize('selectAll', "Select All"),
1586+
order: 1
1587+
}]
1588+
});
1589+
}
1590+
public runDOMCommand(): void {
1591+
document.execCommand('selectAll');
1592+
}
1593+
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
1594+
args = args || {};
1595+
args.source = 'keyboard';
1596+
CoreNavigationCommands.SelectAll.runEditorCommand(accessor, editor, args);
1597+
}
1598+
}
1599+
1600+
class UndoCommand extends EditorOrNativeTextInputCommand {
1601+
public runDOMCommand(): void {
1602+
document.execCommand('undo');
1603+
}
1604+
public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void {
1605+
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) {
1606+
return;
1607+
}
1608+
editor.getModel().undo();
1609+
}
1610+
}
1611+
1612+
class RedoCommand extends EditorOrNativeTextInputCommand {
1613+
public runDOMCommand(): void {
1614+
document.execCommand('redo');
1615+
}
1616+
public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void {
1617+
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) {
1618+
return;
1619+
}
1620+
editor.getModel().redo();
1621+
}
1622+
}
1623+
1624+
function registerCommand<T extends Command>(command: T): T {
1625+
command.register();
1626+
return command;
1627+
}
1628+
15321629
export namespace CoreEditingCommands {
15331630

15341631
export abstract class CoreEditingCommand extends EditorCommand {
@@ -1659,62 +1756,53 @@ export namespace CoreEditingCommands {
16591756
}
16601757
});
16611758

1662-
}
1663-
1664-
function registerCommand(command: Command) {
1665-
command.register();
1666-
}
1667-
1668-
/**
1669-
* A command that will:
1670-
* 1. invoke a command on the focused editor.
1671-
* 2. otherwise, invoke a browser built-in command on the `activeElement`.
1672-
* 3. otherwise, invoke a command on the workbench active editor.
1673-
*/
1674-
class EditorOrNativeTextInputCommand extends Command {
1675-
1676-
private readonly _editorHandler: string | EditorCommand;
1677-
private readonly _inputHandler: string;
1678-
1679-
constructor(opts: ICommandOptions & { editorHandler: string | EditorCommand; inputHandler: string; }) {
1680-
super(opts);
1681-
this._editorHandler = opts.editorHandler;
1682-
this._inputHandler = opts.inputHandler;
1683-
}
1684-
1685-
public runCommand(accessor: ServicesAccessor, args: any): void {
1686-
1687-
const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
1688-
// Only if editor text focus (i.e. not if editor has widget focus).
1689-
if (focusedEditor && focusedEditor.hasTextFocus()) {
1690-
return this._runEditorHandler(accessor, focusedEditor, args);
1691-
}
1759+
export const Undo: UndoCommand = registerCommand(new UndoCommand({
1760+
id: 'undo',
1761+
precondition: EditorContextKeys.writable,
1762+
kbOpts: {
1763+
weight: CORE_WEIGHT,
1764+
kbExpr: EditorContextKeys.textInputFocus,
1765+
primary: KeyMod.CtrlCmd | KeyCode.KEY_Z
1766+
},
1767+
menuOpts: [{
1768+
menuId: MenuId.MenubarEditMenu,
1769+
group: '1_do',
1770+
title: nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"),
1771+
order: 1
1772+
}, {
1773+
menuId: MenuId.CommandPalette,
1774+
group: '',
1775+
title: nls.localize('undo', "Undo"),
1776+
order: 1
1777+
}]
1778+
}));
16921779

1693-
// Ignore this action when user is focused on an element that allows for entering text
1694-
const activeElement = <HTMLElement>document.activeElement;
1695-
if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
1696-
document.execCommand(this._inputHandler);
1697-
return;
1698-
}
1780+
export const DefaultUndo: UndoCommand = registerCommand(new UndoCommand({ id: 'default:undo', precondition: EditorContextKeys.writable }));
16991781

1700-
// Redirecting to active editor
1701-
const activeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor();
1702-
if (activeEditor) {
1703-
activeEditor.focus();
1704-
return this._runEditorHandler(accessor, activeEditor, args);
1705-
}
1706-
}
1782+
export const Redo: RedoCommand = registerCommand(new RedoCommand({
1783+
id: 'redo',
1784+
precondition: EditorContextKeys.writable,
1785+
kbOpts: {
1786+
weight: CORE_WEIGHT,
1787+
kbExpr: EditorContextKeys.textInputFocus,
1788+
primary: KeyMod.CtrlCmd | KeyCode.KEY_Y,
1789+
secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z],
1790+
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }
1791+
},
1792+
menuOpts: [{
1793+
menuId: MenuId.MenubarEditMenu,
1794+
group: '1_do',
1795+
title: nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"),
1796+
order: 2
1797+
}, {
1798+
menuId: MenuId.CommandPalette,
1799+
group: '',
1800+
title: nls.localize('redo', "Redo"),
1801+
order: 1
1802+
}]
1803+
}));
17071804

1708-
private _runEditorHandler(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
1709-
const HANDLER = this._editorHandler;
1710-
if (typeof HANDLER === 'string') {
1711-
editor.trigger('keyboard', HANDLER, args);
1712-
} else {
1713-
args = args || {};
1714-
args.source = 'keyboard';
1715-
HANDLER.runEditorCommand(accessor, editor, args);
1716-
}
1717-
}
1805+
export const DefaultRedo: RedoCommand = registerCommand(new RedoCommand({ id: 'default:redo', precondition: EditorContextKeys.writable }));
17181806
}
17191807

17201808
/**
@@ -1743,78 +1831,7 @@ class EditorHandlerCommand extends Command {
17431831
}
17441832
}
17451833

1746-
registerCommand(new EditorOrNativeTextInputCommand({
1747-
editorHandler: CoreNavigationCommands.SelectAll,
1748-
inputHandler: 'selectAll',
1749-
id: 'editor.action.selectAll',
1750-
precondition: EditorContextKeys.textInputFocus,
1751-
kbOpts: {
1752-
weight: CORE_WEIGHT,
1753-
kbExpr: null,
1754-
primary: KeyMod.CtrlCmd | KeyCode.KEY_A
1755-
},
1756-
menuOpts: [{
1757-
menuId: MenuId.MenubarSelectionMenu,
1758-
group: '1_basic',
1759-
title: nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"),
1760-
order: 1
1761-
}, {
1762-
menuId: MenuId.CommandPalette,
1763-
group: '',
1764-
title: nls.localize('selectAll', "Select All"),
1765-
order: 1
1766-
}]
1767-
}));
1768-
1769-
registerCommand(new EditorOrNativeTextInputCommand({
1770-
editorHandler: Handler.Undo,
1771-
inputHandler: 'undo',
1772-
id: Handler.Undo,
1773-
precondition: EditorContextKeys.writable,
1774-
kbOpts: {
1775-
weight: CORE_WEIGHT,
1776-
kbExpr: EditorContextKeys.textInputFocus,
1777-
primary: KeyMod.CtrlCmd | KeyCode.KEY_Z
1778-
},
1779-
menuOpts: [{
1780-
menuId: MenuId.MenubarEditMenu,
1781-
group: '1_do',
1782-
title: nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"),
1783-
order: 1
1784-
}, {
1785-
menuId: MenuId.CommandPalette,
1786-
group: '',
1787-
title: nls.localize('undo', "Undo"),
1788-
order: 1
1789-
}]
1790-
}));
1791-
registerCommand(new EditorHandlerCommand('default:' + Handler.Undo, Handler.Undo));
1792-
1793-
registerCommand(new EditorOrNativeTextInputCommand({
1794-
editorHandler: Handler.Redo,
1795-
inputHandler: 'redo',
1796-
id: Handler.Redo,
1797-
precondition: EditorContextKeys.writable,
1798-
kbOpts: {
1799-
weight: CORE_WEIGHT,
1800-
kbExpr: EditorContextKeys.textInputFocus,
1801-
primary: KeyMod.CtrlCmd | KeyCode.KEY_Y,
1802-
secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z],
1803-
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }
1804-
},
1805-
menuOpts: [{
1806-
menuId: MenuId.MenubarEditMenu,
1807-
group: '1_do',
1808-
title: nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"),
1809-
order: 2
1810-
}, {
1811-
menuId: MenuId.CommandPalette,
1812-
group: '',
1813-
title: nls.localize('redo', "Redo"),
1814-
order: 1
1815-
}]
1816-
}));
1817-
registerCommand(new EditorHandlerCommand('default:' + Handler.Redo, Handler.Redo));
1834+
registerCommand(new SelectAllCommand());
18181835

18191836
function registerOverwritableCommand(handlerId: string, description?: ICommandHandlerDescription): void {
18201837
registerCommand(new EditorHandlerCommand('default:' + handlerId, handlerId));

src/vs/editor/browser/widget/codeEditorWidget.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,11 +1543,18 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
15431543
};
15441544
}
15451545

1546+
const onDidChangeTextFocus = (textFocus: boolean) => {
1547+
if (this._modelData) {
1548+
this._modelData.cursor.setHasFocus(textFocus);
1549+
}
1550+
this._editorTextFocus.setValue(textFocus);
1551+
};
1552+
15461553
const viewOutgoingEvents = new ViewOutgoingEvents(viewModel);
15471554
viewOutgoingEvents.onDidContentSizeChange = (e) => this._onDidContentSizeChange.fire(e);
15481555
viewOutgoingEvents.onDidScroll = (e) => this._onDidScrollChange.fire(e);
1549-
viewOutgoingEvents.onDidGainFocus = () => this._editorTextFocus.setValue(true);
1550-
viewOutgoingEvents.onDidLoseFocus = () => this._editorTextFocus.setValue(false);
1556+
viewOutgoingEvents.onDidGainFocus = () => onDidChangeTextFocus(true);
1557+
viewOutgoingEvents.onDidLoseFocus = () => onDidChangeTextFocus(false);
15511558
viewOutgoingEvents.onContextMenu = (e) => this._onContextMenu.fire(e);
15521559
viewOutgoingEvents.onMouseDown = (e) => this._onMouseDown.fire(e);
15531560
viewOutgoingEvents.onMouseUp = (e) => this._onMouseUp.fire(e);

0 commit comments

Comments
 (0)