git: give up on using the editor

fixes #20983
This commit is contained in:
Joao Moreno 2017-02-21 11:55:21 +01:00
parent 6595091690
commit ca1deee166
13 changed files with 112 additions and 201 deletions

View file

@ -212,14 +212,6 @@
"category": "Git"
}
],
"keybindings": [
{
"command": "git.commitWithInput",
"key": "ctrl+enter",
"mac": "cmd+enter",
"when": "inSCMInput"
}
],
"menus": {
"commandPalette": [
{

View file

@ -49,6 +49,10 @@ export class GitSCMProvider implements SCMProvider {
return this.commandCenter.open(resource);
}
acceptChanges(): ProviderResult<void> {
return this.commandCenter.commitWithInput();
}
drag(resource: Resource, resourceGroup: ResourceGroup): void {
console.log('drag', resource, resourceGroup);
}

View file

@ -14,5 +14,10 @@
"win32ShellNameShort": "C&ode - OSS",
"darwinBundleIdentifier": "com.visualstudio.code.oss",
"reportIssueUrl": "https://github.com/Microsoft/vscode/issues/new",
"urlProtocol": "code-oss"
"urlProtocol": "code-oss",
"extensionsGallery": {
"serviceUrl": "https://marketplace.visualstudio.com/_apis/public/gallery",
"cacheUrl": "https://vscode.blob.core.windows.net/gallery/index",
"itemUrl": "https://marketplace.visualstudio.com/items"
}
}

View file

@ -139,6 +139,7 @@ declare module 'vscode' {
getOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult<Uri>;
open?(resource: SCMResource, token: CancellationToken): ProviderResult<void>;
drag?(resource: SCMResource, resourceGroup: SCMResourceGroup, token: CancellationToken): ProviderResult<void>;
acceptChanges?(token: CancellationToken): ProviderResult<void>;
}
export interface SCMInputBox {

View file

@ -245,6 +245,7 @@ export abstract class MainProcessExtensionServiceShape {
export interface SCMProviderFeatures {
label: string;
supportsOpen: boolean;
supportsAcceptChanges: boolean;
supportsDrag: boolean;
supportsOriginalResource: boolean;
}
@ -394,6 +395,7 @@ export abstract class ExtHostTerminalServiceShape {
export abstract class ExtHostSCMShape {
$open(id: string, resourceGroupId: string, uri: string): TPromise<void> { throw ni(); }
$acceptChanges(id: string): TPromise<void> { throw ni(); }
$drag(id: string, fromResourceGroupId: string, fromUri: string, toResourceGroupId: string): TPromise<void> { throw ni(); }
$getOriginalResource(id: string, uri: URI): TPromise<URI> { throw ni(); }
$onInputBoxValueChange(value: string): TPromise<void> { throw ni(); }

View file

@ -152,6 +152,7 @@ export class ExtHostSCM {
this._proxy.$register(providerId, {
label: provider.label,
supportsOpen: !!provider.open,
supportsAcceptChanges: !!provider.acceptChanges,
supportsDrag: !!provider.drag,
supportsOriginalResource: !!provider.getOriginalResource
});
@ -217,6 +218,16 @@ export class ExtHostSCM {
return asWinJsPromise(token => provider.open(resource, token));
}
$acceptChanges(providerId: string): TPromise<void> {
const provider = this._providers[providerId];
if (!provider) {
return TPromise.as(null);
}
return asWinJsPromise(token => provider.acceptChanges(token));
}
$drag(providerId: string, fromResourceGroupId: string, fromUri: string, toResourceGroupId: string): TPromise<void> {
const provider = this._providers[providerId];

View file

@ -52,6 +52,14 @@ class MainThreadSCMProvider implements ISCMProvider {
return this.proxy.$open(this.id, resource.resourceGroupId, resource.uri.toString());
}
acceptChanges(): TPromise<void> {
if (!this.features.supportsAcceptChanges) {
return TPromise.as(null);
}
return this.proxy.$acceptChanges(this.id);
}
drag(from: ISCMResource, to: ISCMResourceGroup): TPromise<void> {
if (!this.features.supportsDrag) {
return TPromise.as(null);
@ -125,8 +133,8 @@ export class MainThreadSCM extends MainThreadSCMShape {
super();
this.proxy = threadService.get(ExtHostContext.ExtHostSCM);
this.inputBoxListener = this.scmService.inputBoxModel.onDidChangeContent(e => {
this.proxy.$onInputBoxValueChange(this.scmService.inputBoxModel.getValue());
this.inputBoxListener = this.scmService.input.onDidChange(value => {
this.proxy.$onInputBoxValueChange(value);
});
}
@ -156,7 +164,7 @@ export class MainThreadSCM extends MainThreadSCMShape {
}
$setInputBoxValue(value: string): void {
this.scmService.inputBoxModel.setValue(value);
this.scmService.input.value = value;
}
dispose(): void {

View file

@ -48,6 +48,10 @@ export class GitSCMProvider implements IWorkbenchContribution, ISCMProvider, ITe
return TPromise.wrapError<void>('not implemented');
}
acceptChanges(): TPromise<void> {
return TPromise.wrapError<void>('not implemented');
}
drag(from: ISCMResource, to: ISCMResourceGroup): TPromise<void> {
return TPromise.wrapError<void>('not implemented');
}

View file

@ -9,10 +9,6 @@
background-position: center;
}
.scm-viewlet > .scm-editor {
padding: 5px 9px 5px 16px;
}
.scm-viewlet .monaco-list-row {
padding: 0 12px 0 20px;
line-height: 22px;
@ -76,18 +72,27 @@
display: none;
}
.monaco-shell.vs .scm-viewlet > .scm-editor > .monaco-editor.focused {
outline: 1px solid rgba(0, 122, 204, 0.4);
.scm-viewlet > .scm-editor {
padding: 5px 9px 5px 16px;
}
.monaco-shell.vs-dark .scm-viewlet > .scm-editor > .monaco-editor.focused {
outline: 1px solid rgba(14, 99, 156, 0.6);
.scm-viewlet > .scm-editor {
box-sizing: border-box;
padding: 5px 9px 5px 16px;
}
.monaco-shell.hc-black .scm-viewlet > .scm-editor > .monaco-editor {
outline: 1px solid #6fc3df;
.scm-viewlet > .scm-editor > .monaco-inputbox {
width: 100%;
}
.monaco-shell.hc-black .scm-viewlet > .scm-editor > .monaco-editor.focused {
outline: 2px solid #f38518;
.scm-viewlet > .scm-editor > .monaco-inputbox > .wrapper > .mirror {
max-height: 134px;
}
.scm-viewlet > .scm-editor > .monaco-inputbox > .wrapper > textarea.input {
min-height: 26px;
}
.scm-viewlet > .scm-editor.scroll > .monaco-inputbox > .wrapper > textarea.input {
overflow-y: scroll;
}

View file

@ -1,145 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IEditorOptions, IDimension } from 'vs/editor/common/editorCommon';
import { EditorAction, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { IEditorContributionCtor } from 'vs/editor/browser/editorBrowser';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { MenuPreventer } from 'vs/editor/contrib/multicursor/browser/menuPreventer';
import { SelectionClipboard } from 'vs/editor/contrib/selectionClipboard/electron-browser/selectionClipboard';
import { ContextMenuController } from 'vs/editor/contrib/contextmenu/browser/contextmenu';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IThemeService } from 'vs/workbench/services/themes/common/themeService';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IModeService } from 'vs/editor/common/services/modeService';
import { ISCMService } from 'vs/workbench/services/scm/common/scm';
class SCMCodeEditorWidget extends CodeEditorWidget {
constructor(
domElement: HTMLElement,
options: IEditorOptions,
@IInstantiationService instantiationService: IInstantiationService,
@ICodeEditorService codeEditorService: ICodeEditorService,
@ICommandService commandService: ICommandService,
@IContextKeyService contextKeyService: IContextKeyService
) {
super(domElement, options, instantiationService, codeEditorService, commandService, contextKeyService);
}
protected _getContributions(): IEditorContributionCtor[] {
return [
MenuPreventer,
SelectionClipboard,
ContextMenuController
];
}
protected _getActions(): EditorAction[] {
return CommonEditorRegistry.getEditorActions();
}
}
export const InSCMInputContextKey = new RawContextKey<boolean>('inSCMInput', false);
export class SCMEditor {
private static VerticalPadding = 4;
private editor: SCMCodeEditorWidget;
private disposables: IDisposable[] = [];
private get editorOptions(): IEditorOptions {
return {
wrappingColumn: 0,
overviewRulerLanes: 0,
glyphMargin: false,
lineNumbers: 'off',
folding: false,
selectOnLineNumbers: false,
selectionHighlight: false,
scrollbar: {
horizontal: 'hidden'
},
lineDecorationsWidth: 3,
scrollBeyondLastLine: false,
theme: this.themeService.getColorTheme().id,
renderLineHighlight: 'none',
fixedOverflowWidgets: true,
acceptSuggestionOnEnter: false,
wordWrap: true
};
}
constructor(
container: HTMLElement,
@IThemeService private themeService: IThemeService,
@IInstantiationService instantiationService: IInstantiationService,
@IModeService private modeService: IModeService,
@IModelService private modelService: IModelService,
@IContextKeyService private contextKeyService: IContextKeyService,
@ISCMService private scmService: ISCMService
) {
const scopedContextKeyService = this.contextKeyService.createScoped(container);
InSCMInputContextKey.bindTo(scopedContextKeyService).set(true);
this.disposables.push(scopedContextKeyService);
const services = new ServiceCollection();
services.set(IContextKeyService, scopedContextKeyService);
const scopedInstantiationService = instantiationService.createChild(services);
this.editor = scopedInstantiationService.createInstance(SCMCodeEditorWidget, container, this.editorOptions);
this.themeService.onDidColorThemeChange(e => this.editor.updateOptions(this.editorOptions), null, this.disposables);
const model = this.scmService.inputBoxModel;
this.editor.setModel(model);
this.editor.changeViewZones(accessor => {
accessor.addZone({
afterLineNumber: 0,
heightInPx: SCMEditor.VerticalPadding,
domNode: document.createElement('div')
});
});
}
private get lineHeight(): number {
return this.editor.getConfiguration().lineHeight;
}
// TODO@joao TODO@alex isn't there a better way to get the number of lines?
private get lineCount(): number {
const model = this.scmService.inputBoxModel;
const modelLength = model.getValueLength();
const lastPosition = model.getPositionAt(modelLength);
const lastLineTop = this.editor.getTopForPosition(lastPosition.lineNumber, lastPosition.column);
const viewHeight = lastLineTop + this.lineHeight;
return viewHeight / this.lineHeight;
}
get viewHeight(): number {
return Math.min(this.lineCount, 8) * this.lineHeight + (SCMEditor.VerticalPadding);
}
layout(dimension: IDimension): void {
this.editor.layout(dimension);
}
focus(): void {
this.editor.focus();
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}

View file

@ -8,10 +8,13 @@
import 'vs/css!./media/scmViewlet';
import { TPromise } from 'vs/base/common/winjs.base';
import { chain } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IDisposable, dispose, empty as EmptyDisposable } from 'vs/base/common/lifecycle';
import { Builder, Dimension } from 'vs/base/browser/builder';
import { Viewlet } from 'vs/workbench/browser/viewlet';
import { append, $, toggleClass } from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { IDelegate, IRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list';
@ -31,7 +34,7 @@ import { createActionItem } from 'vs/platform/actions/browser/menuItemActionItem
import { SCMMenus } from './scmMenus';
import { ActionBar, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { IThemeService } from 'vs/workbench/services/themes/common/themeService';
import { SCMEditor } from './scmEditor';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { IModelService } from 'vs/editor/common/services/modelService';
function isSCMResource(element: ISCMResourceGroup | ISCMResource): element is ISCMResource {
@ -153,7 +156,8 @@ class Delegate implements IDelegate<ISCMResourceGroup | ISCMResource> {
export class SCMViewlet extends Viewlet {
private cachedDimension: Dimension;
private editor: SCMEditor;
private inputBoxContainer: HTMLElement;
private inputBox: InputBox;
private listContainer: HTMLElement;
private list: List<ISCMResourceGroup | ISCMResource>;
private menus: SCMMenus;
@ -199,12 +203,20 @@ export class SCMViewlet extends Viewlet {
parent.addClass('scm-viewlet');
const root = parent.getHTMLElement();
const editorContainer = append(root, $('.scm-editor'));
this.inputBoxContainer = append(root, $('.scm-editor'));
this.editor = this.instantiationService.createInstance(SCMEditor, editorContainer);
this.disposables.push(this.editor);
this.inputBox = new InputBox(this.inputBoxContainer, this.contextViewService, { flexibleHeight: true });
this.disposables.push(this.inputBox);
this.disposables.push(this.scmService.inputBoxModel.onDidChangeContent(() => this.layout()));
this.inputBox.value = this.scmService.input.value;
this.inputBox.onDidChange(value => this.scmService.input.value = value, null, this.disposables);
this.scmService.input.onDidChange(value => this.inputBox.value = value, null, this.disposables);
this.disposables.push(this.scmService.input.onDidChange(() => this.layout()));
chain(domEvent(this.inputBox.inputElement, 'keydown'))
.map(e => new StandardKeyboardEvent(e))
.filter(e => e.equals(KeyMod.CtrlCmd | KeyCode.Enter) || e.equals(KeyMod.CtrlCmd | KeyCode.KEY_S))
.on(this.acceptChanges, this, this.disposables);
this.listContainer = append(root, $('.scm-status.show-file-icons'));
const delegate = new Delegate();
@ -257,13 +269,14 @@ export class SCMViewlet extends Viewlet {
}
this.cachedDimension = dimension;
this.inputBox.layout();
const editorHeight = this.editor.viewHeight;
this.editor.layout({ width: dimension.width - 25, height: editorHeight });
const editorHeight = this.inputBox.height;
const listHeight = dimension.height - (editorHeight + 12 /* margin */);
this.listContainer.style.height = `${listHeight}px`;
this.list.layout(listHeight);
toggleClass(this.inputBoxContainer, 'scroll', editorHeight >= 134);
}
getOptimalWidth(): number {
@ -272,13 +285,17 @@ export class SCMViewlet extends Viewlet {
focus(): void {
super.focus();
this.editor.focus();
this.inputBox.focus();
}
private open(e: ISCMResource): void {
this.scmService.activeProvider.open(e);
}
private acceptChanges(): void {
this.scmService.activeProvider.acceptChanges();
}
getActions(): IAction[] {
return this.menus.getTitleActions();
}

View file

@ -10,7 +10,6 @@ import URI from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import Event from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IModel } from 'vs/editor/common/editorCommon';
export interface IBaselineResourceProvider {
getBaselineResource(resource: URI): TPromise<URI>;
@ -45,17 +44,23 @@ export interface ISCMProvider extends IDisposable {
readonly state?: string;
open(uri: ISCMResource): TPromise<void>;
acceptChanges(): TPromise<void>;
drag(from: ISCMResource, to: ISCMResourceGroup): TPromise<void>;
getOriginalResource(uri: URI): TPromise<URI>;
}
export interface ISCMInput {
value: string;
readonly onDidChange: Event<string>;
}
export interface ISCMService {
readonly _serviceBrand: any;
readonly onDidChangeProvider: Event<ISCMProvider>;
readonly providers: ISCMProvider[];
readonly input: ISCMInput;
activeProvider: ISCMProvider | undefined;
readonly inputBoxModel: IModel;
registerSCMProvider(provider: ISCMProvider): IDisposable;
}

View file

@ -7,14 +7,26 @@
import { IDisposable, toDisposable, empty as EmptyDisposable } from 'vs/base/common/lifecycle';
import Event, { Emitter } from 'vs/base/common/event';
import { memoize } from 'vs/base/common/decorators';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IModel } from 'vs/editor/common/editorCommon';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { RawText } from 'vs/editor/common/model/textModel';
import { Model } from 'vs/editor/common/model/model';
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
import { ISCMService, ISCMProvider } from './scm';
import { ISCMService, ISCMProvider, ISCMInput } from './scm';
class SCMInput implements ISCMInput {
private _value = '';
get value(): string {
return this._value;
}
set value(value: string) {
this._value = value;
this._onDidChange.fire(value);
}
private _onDidChange = new Emitter<string>();
get onDidChange(): Event<string> { return this._onDidChange.event; }
}
export class SCMService implements ISCMService {
@ -55,24 +67,14 @@ export class SCMService implements ISCMService {
private _onDidChangeProvider = new Emitter<ISCMProvider>();
get onDidChangeProvider(): Event<ISCMProvider> { return this._onDidChangeProvider.event; }
private _inputBoxModel: IModel;
get inputBoxModel(): IModel { return this._inputBoxModel; }
@memoize
get input(): ISCMInput { return new SCMInput(); }
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@IModeService modeService: IModeService,
@IModelService modelService: IModelService
@IContextKeyService contextKeyService: IContextKeyService
) {
this.activeProviderContextKey = contextKeyService.createKey<string | undefined>('scmProvider', void 0);
this.activeProviderStateContextKey = contextKeyService.createKey<string | undefined>('scmProviderState', void 0);
const options = modelService.getCreationOptions('git-commit');
const rawText = RawText.fromString('', options);
this._inputBoxModel = new Model(rawText, PLAINTEXT_LANGUAGE_IDENTIFIER);
modeService.getOrCreateMode('git-commit')
.done(mode => this._inputBoxModel.setMode(mode.getLanguageIdentifier()));
}
registerSCMProvider(provider: ISCMProvider): IDisposable {