Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rework monaco commands to align with VS Code #7539

Merged
merged 3 commits into from
Apr 15, 2020

Conversation

akosyakov
Copy link
Member

@akosyakov akosyakov commented Apr 9, 2020

What it does

  • Reverts 40b1d45 to align with VS Code behaviour as before
  • Refactors monaco-commands to fix Find/Replace/Undo/Redo/Select All, but at the same time without breaking previous behaviour:
    /**
    * Register commands from Monaco to Theia registry.
    *
    * Monaco has different kind of commands which should be handled differently by Theia.
    *
    * ### Editor Actions
    *
    * They should be registered with a label to be visible in the quick command palette.
    *
    * Such actions should be enabled only if the current editor is available and
    * it supports such action in the current context.
    *
    * ### Editor Commands
    *
    * Such actions should be enabled only if the current editor is available.
    *
    * `actions.find` and `editor.action.startFindReplaceAction` are registed as handlers for `find` and `replace`.
    * If handlers are not enabled then the core should prevent the default browser behaviour.
    * Other Theia extensions can register alternative implementations using custom enablement.
    *
    * ### Global Commands
    *
    * These commands are not necessary dependend on the current editor and enabled always.
    * But they depend on services which are global in VS Code, but bound to the editor in Monaco,
    * i.e. `ICodeEditorService` or `IContextKeyService`. We should take care of providing Theia implementations for such services.
    *
    * #### Global Native or Editor Commands
    *
    * Namely: `undo`, `redo` and `editor.action.selectAll`. They depend on `ICodeEditorService`.
    * They will try to delegate to the current editor and if it is not available delegate to the browser.
    * They are registered as handlers for corresponding core commands always.
    * Other Theia extensions can provide alternative implementations by introducing a dependency to `@theia/monaco` extension.
    *
    * #### Global Language Commands
    *
    * Like `_executeCodeActionProvider`, they depend on `ICodeEditorService` and `ITextModelService`.
    *
    * #### Global Context Commands
    *
    * It is `setContext`. It depends on `IContextKeyService`.
    *
    * #### Global Editor Commands
    *
    * Like `openReferenceToSide` and `openReference`, they depend on `IListService`.
    * We treat all commands which don't match any other category of global commands as global editor commands
    * and execute them using the instantiation service of the current editor.
    */
    protected registerMonacoCommands(): void {
    const editorRegistry = monaco.editorExtensions.EditorExtensionsRegistry;
    const editorActions = new Map(editorRegistry.getEditorActions().map(({ id, label }) => [id, label]));
    const { codeEditorService, textModelService, contextKeyService } = this;
    const [, globalInstantiationService] = monaco.services.StaticServices.init({ codeEditorService, textModelService, contextKeyService });
    const monacoCommands = monaco.commands.CommandsRegistry.getCommands();
    for (const id of monacoCommands.keys()) {
    if (MonacoCommands.EXCLUDE_ACTIONS.has(id)) {
    continue;
    }
    const handler: CommandHandler = {
    execute: (...args) => {
    const editor = codeEditorService.getActiveCodeEditor();
    if (editorActions.has(id)) {
    const action = editor && editor.getAction(id);
    if (!action) {
    return;
    }
    return action.run();
    }
    if (editorRegistry.getEditorCommand(id)) {
    if (!editor) {
    return;
    }
    return (editor._commandService as MonacoCommandService).executeMonacoCommand(id, ...args);
    }
    let instantiationService;
    if (id.startsWith('_execute') || id === 'setContext' || MonacoCommands.COMMON_ACTIONS.has(id)) {
    instantiationService = globalInstantiationService;
    } else if (editor) {
    instantiationService = editor._instantiationService;
    }
    if (!instantiationService) {
    return;
    }
    return instantiationService.invokeFunction(
    monacoCommands.get(id)!.handler,
    ...args
    );
    },
    isEnabled: () => {
    const editor = codeEditorService.getActiveCodeEditor();
    if (editorActions.has(id)) {
    const action = editor && editor.getAction(id);
    return !!action && action.isSupported();
    }
    if (editorRegistry.getEditorCommand(id)) {
    return !!editor;
    }
    return true;
    }
    };
    const label = editorActions.get(id);
    this.commandRegistry.registerCommand({ id, label }, handler);
    const coreCommand = MonacoCommands.COMMON_ACTIONS.get(id);
    if (coreCommand) {
    this.commandRegistry.registerHandler(coreCommand, handler);
    }
    }
    }
  • Adds integration tests for Find/Replace/Undo/Redo/Select All commands

How to test

  • Find/Replace -> if the current editor then it should be used, if not then either suppressed or a widget should be used, i.e. the terminal
    • integration tests added to cover common cases
  • Undo/Redo/Select All -> if the current editor then it should be used, otherwise delegate to DOM, rely on Monaco for implementation. Check input areas with and without editors in other views, git, SIW, terminal find, webview (gitlens welcome)
    • integration tests added to cover common cases
  • Editor Actions like manipulation of selections
    • should be available in the command palette when the current editor is available and work properly
    • language features should be disable when they are not provided even for the current editor
    • test inline editors as debug console input and breakpoint expression editor
    • test embedded editors as peek references
  • there should NOT be any exceptions in logs about missing service if the gitlens extension is installed
  • integration tests should pass

Review checklist

Reminder for reviewers

@akosyakov akosyakov added the monaco issues related to monaco label Apr 9, 2020
@akosyakov akosyakov force-pushed the akosyakov/ctrl-f-does-not-trigger-7525 branch 3 times, most recently from 40ef2e1 to 42ea5bd Compare April 9, 2020 14:46
@akosyakov akosyakov added commands issues related to application commands vscode issues related to VSCode compatibility labels Apr 9, 2020
@akosyakov akosyakov marked this pull request as ready for review April 9, 2020 14:58
@kittaakos
Copy link
Contributor

Executing Undo whilst the focus is an input; executes the command in the editor:

screencast 2020-04-14 09-18-06

@kittaakos
Copy link
Contributor

Monaco editor commands incorrectly execute with the outer editor context, instead of the embedded one. Steps to reproduce:

  • Open an editor,
  • Open an embedded editor,
  • Set the focus in the inner editor,
  • Execute Copy Line Up, the command will copy a line based on the selection in the outer editor instead of the inner one.

screencast 2020-04-14 09-25-08

@akosyakov
Copy link
Member Author

Executing Undo whilst the focus is an input; executes the command in the editor:

You did not do any changes in the input, only focused it? I think it is the same in VS Code.

packages/monaco/src/typings/monaco/index.d.ts Show resolved Hide resolved
packages/monaco/src/typings/monaco/index.d.ts Outdated Show resolved Hide resolved
packages/monaco/src/typings/monaco/index.d.ts Outdated Show resolved Hide resolved
packages/monaco/src/typings/monaco/index.d.ts Outdated Show resolved Hide resolved
Comment on lines -196 to -207
commands.registerCommand({ id: 'actions.find' }, {
execute: () => commands.executeCommand('actions.find')
});
commands.registerCommand({ id: 'undo' }, {
execute: () => commands.executeCommand('undo')
});
commands.registerCommand({ id: 'redo' }, {
execute: () => commands.executeCommand('redo')
});
commands.registerCommand({ id: 'editor.action.startFindReplaceAction' }, {
execute: () => commands.executeCommand('editor.action.startFindReplaceAction')
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can I verify this change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to call these commands via CommandService. I think we did it already by using undo/redo/select and find.

The issue with this code that it tries to override commands contributed by the monaco extension. In the good case it does nothing, just a warning in logs that such command already registered (what is happening on master i believe), in the worse it registers before monaco and causes infinity recursion.

CHANGELOG.md Outdated Show resolved Hide resolved
@kittaakos

This comment has been minimized.

@akosyakov
Copy link
Member Author

It works in Code:

@kittaakos in VS Code git input is a Monaco editor, try with the search in workspace input

@kittaakos
Copy link
Contributor

@kittaakos in VS Code git input is a Monaco editor, try with the search in workspace input

You're right. The behavior isn't nice in VS Code editor, but Theia works as VS Code. I had to use the SIW input instead of the SCM textArea monaco-editor.

screencast 2020-04-14 10-01-36

@akosyakov akosyakov force-pushed the akosyakov/ctrl-f-does-not-trigger-7525 branch from 42ea5bd to 021fca5 Compare April 14, 2020 08:49
@akosyakov
Copy link
Member Author

akosyakov commented Apr 14, 2020

Monaco editor commands incorrectly execute with the outer editor context, instead of the embedded one. Steps to reproduce:

You are right about getFocusedCodeEditor. It has to be checked as well before getActiveCodeEditor. Focused code editors include inline and embedded editors, active code editors include only last focused standalone or diff editors.

I've adjuster handlers and added js-docs to exposed getFocusedCodeEditor to make it clear.

@akosyakov akosyakov force-pushed the akosyakov/ctrl-f-does-not-trigger-7525 branch 3 times, most recently from 2a1d9a0 to 49e9334 Compare April 14, 2020 13:28
@akosyakov
Copy link
Member Author

@kittaakos I've answered/addressed all your comments.

Dirty editor tests should be good now too. Code was assuming that we always load editor model to open from the disk or already processed all disk changes. But tests revealed that there are cases when we did not receive all events yet but already trying to open an editor. I've added a guard that if a model looks invalid then sync with the disk before throwing an invalid model exception.

I've also updated How to test section to reflect inline and embedded editors.

@kittaakos
Copy link
Contributor

@akosyakov, can you please rebase from the master HEAD? I want to verify #7523. Currently, it does not work.

@akosyakov akosyakov force-pushed the akosyakov/ctrl-f-does-not-trigger-7525 branch from 49e9334 to 692f9a6 Compare April 14, 2020 14:48
@akosyakov
Copy link
Member Author

@kittaakos done, trying it as well as soon as the local build is through

Copy link
Contributor

@kittaakos kittaakos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have verified it once more with the electron example; it works as expected! Very slick fix 👍

@akosyakov akosyakov merged commit 62d5323 into master Apr 15, 2020
@akosyakov akosyakov deleted the akosyakov/ctrl-f-does-not-trigger-7525 branch April 15, 2020 07:31
@tavoda
Copy link

tavoda commented Apr 15, 2020

git pull (I'm on master)
yarn build
cd examples/browser
yarn start

Still same error. Using theia/plugins/vscodevim.vim-1.13.1.vsix with size 1952450. Do I have to do some 'clean' or using different vscode-vim plugin from other registry?

@tavoda
Copy link

tavoda commented Apr 15, 2020

Found something at startup for CTRL+F:
root WARN A command editor.action.startFindReplaceAction is already registered.
root WARN Collided keybinding is ignored; {"command":"passthrough","keybinding":"ctrl+f","context":"terminalActive"} collided with {"command":"terminal:find","keybinding":
"ctrlcmd+f","context":"terminalActive"}
root WARN Could not register keybinding:
{"command":"passthrough","keybinding":"ctrl+f","context":"terminalActive"}
Error: "ctrl+f" is in collision with something else [scope:0]
root WARN Collided keybinding is ignored; {"command":"passthrough","keybinding":"ctrl+k","context":"terminalActive"} collided with {"command":"terminal:clear","keybinding"
:"ctrlcmd+k","context":"terminalActive"}
root WARN Could not register keybinding:
{"command":"passthrough","keybinding":"ctrl+k","context":"terminalActive"}
Error: "ctrl+k" is in collision with something else [scope:0]

@akosyakov
Copy link
Member Author

A command editor.action.startFindReplaceAction is already registered.

@tavoda sounds like it was old version. This PR removes double registration: #7539 (comment)

@tavoda
Copy link

tavoda commented Apr 15, 2020

Once more:
git pull
yarn run clean
yarn run build
cd examples/browser
yarn start -> ERROR?!?!?
yarn build
yarn start

EVERYTHING OK, CTRL + F is working 😎 thank you
Now I can try to replace VSCODE with THEIA for everyday work.

@tavoda
Copy link

tavoda commented Apr 15, 2020

Hmmm too soon, now basic movements like hjkl is not working, instead of movement it looks like in insert mode and <ESC> doesn't help. However CTRL+F is working 😢

@akosyakov
Copy link
Member Author

@tavoda thanks for checking, please open an issue 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
commands issues related to application commands monaco issues related to monaco vscode issues related to VSCode compatibility
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants