From 3eddf716787f46bc43e7a1509f92b3c5fe8dc7ce Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 3 Feb 2017 17:20:26 +0100 Subject: [PATCH] scm: hide input box model from extension host --- extensions/git/src/commands.ts | 10 ++-- extensions/git/src/main.ts | 2 +- src/vs/vscode.proposed.d.ts | 7 +++ src/vs/workbench/api/node/extHost.api.impl.ts | 5 ++ src/vs/workbench/api/node/extHost.protocol.ts | 2 + src/vs/workbench/api/node/extHostSCM.ts | 42 ++++++++++++++++ src/vs/workbench/api/node/mainThreadSCM.ts | 13 ++++- .../parts/scm/electron-browser/scmEditor.ts | 50 ++++--------------- .../parts/scm/electron-browser/scmViewlet.ts | 3 +- src/vs/workbench/services/scm/common/scm.ts | 2 + .../services/scm/common/scmService.ts | 21 +++++++- 11 files changed, 107 insertions(+), 50 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 64038a3e0f5..b070818b735 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -8,7 +8,6 @@ import { Uri, commands, scm, Disposable, SCMResourceGroup, SCMResource, window, workspace, QuickPickItem, OutputChannel } from 'vscode'; import { IRef, RefType } from './git'; import { Model, Resource, Status } from './model'; -import { CommitController } from './commit'; import * as path from 'path'; import * as nls from 'vscode-nls'; @@ -128,7 +127,6 @@ export class CommandCenter { constructor( private model: Model, - private commitController: CommitController, private outputChannel: OutputChannel ) { this.disposables = CommandCenter.Commands @@ -331,7 +329,7 @@ export class CommandCenter { @CommandCenter.Command('git.commit') @CommandCenter.CatchErrors async commit(): Promise { - const message = this.commitController.message; + const message = scm.inputBox.value; const didCommit = await this._commit(async () => { if (message) { @@ -345,17 +343,17 @@ export class CommandCenter { }); if (message && didCommit) { - this.commitController.message = ''; + scm.inputBox.value = ''; } } @CommandCenter.Command('git.commitWithInput') @CommandCenter.CatchErrors async commitWithInput(): Promise { - const didCommit = await this._commit(async () => this.commitController.message); + const didCommit = await this._commit(async () => scm.inputBox.value); if (didCommit) { - this.commitController.message = ''; + scm.inputBox.value = ''; } } diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 61b145296be..bff3fc3cdca 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -43,7 +43,7 @@ async function init(disposables: Disposable[]): Promise { git.onOutput(str => outputChannel.append(str), null, disposables); const commitHandler = new CommitController(model); - const commandCenter = new CommandCenter(model, commitHandler, outputChannel); + const commandCenter = new CommandCenter(model, outputChannel); const provider = new GitSCMProvider(model, commandCenter); const contentProvider = new GitContentProvider(git, rootPath, onGitChange); const checkoutStatusBar = new CheckoutStatusBar(model); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 8f6deb35339..dc4f1b3f2a6 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -140,9 +140,16 @@ declare module 'vscode' { drag?(resource: SCMResource, resourceGroup: SCMResourceGroup, token: CancellationToken): ProviderResult; } + export interface SCMInputBox { + value: string; + readonly onDidChange: Event; + } + export namespace scm { export const onDidChangeActiveProvider: Event; export let activeProvider: SCMProvider | undefined; + export const inputBox: SCMInputBox; + export function getResourceFromURI(uri: Uri): SCMResource | SCMResourceGroup | undefined; export function registerSCMProvider(id: string, provider: SCMProvider): Disposable; } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 267ed86f9cb..4ba4cd1e85d 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -424,6 +424,11 @@ export function createApiFactory(initData: IInitData, threadService: IThreadServ return extHostSCM.onDidChangeActiveProvider; } + @proposed(extension) + get inputBox() { + return extHostSCM.inputBox; + } + @proposed(extension) getResourceFromURI(uri) { return extHostSCM.getResourceFromURI(uri); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index f5d14ea492c..3e227a1150a 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -259,6 +259,7 @@ export abstract class MainThreadSCMShape { $register(id: string, features: SCMProviderFeatures): void { throw ni(); } $unregister(id: string): void { throw ni(); } $onChange(id: string, resources: SCMRawResourceGroup[], count: number | undefined): void { throw ni(); } + $setInputBoxValue(value: string): void { throw ni(); } } // -- extension host @@ -394,6 +395,7 @@ export abstract class ExtHostSCMShape { $open(id: string, resourceGroupId: string, uri: string): TPromise { throw ni(); } $drag(id: string, fromResourceGroupId: string, fromUri: string, toResourceGroupId: string): TPromise { throw ni(); } $getOriginalResource(id: string, uri: URI): TPromise { throw ni(); } + $onInputBoxValueChange(value: string): TPromise { throw ni(); } } // --- proxy identifiers diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index 9010ae0ab43..c874314608e 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -33,6 +33,39 @@ export interface Cache { }; } +class ExtHostSCMInputBox { + + private _value: string = ''; + + get value(): string { + return this._value; + } + + set value(value: string) { + this._proxy.$setInputBoxValue(value); + this.updateValue(value); + } + + private _onDidChange = new Emitter(); + + get onDidChange(): Event { + return this._onDidChange.event; + } + + constructor(private _proxy: MainThreadSCMShape) { + // noop + } + + $onInputBoxValueChange(value: string): void { + this.updateValue(value); + } + + private updateValue(value: string): void { + this._value = value; + this._onDidChange.fire(value); + } +} + export class ExtHostSCM { private _proxy: MainThreadSCMShape; @@ -44,10 +77,14 @@ export class ExtHostSCM { private _activeProvider: vscode.SCMProvider; get activeProvider(): vscode.SCMProvider | undefined { return this._activeProvider; } + private _inputBox: ExtHostSCMInputBox; + get inputBox(): vscode.SCMInputBox { return this._inputBox; } + private cache: Cache = Object.create(null); constructor(threadService: IThreadService) { this._proxy = threadService.get(MainContext.MainThreadSCM); + this._inputBox = new ExtHostSCMInputBox(this._proxy); } getResourceFromURI(uri: vscode.Uri): vscode.SCMResource | vscode.SCMResourceGroup | undefined { @@ -209,4 +246,9 @@ export class ExtHostSCM { return asWinJsPromise(token => provider.getOriginalResource(uri, token)); } + + $onInputBoxValueChange(value: string): TPromise { + this._inputBox.$onInputBoxValueChange(value); + return TPromise.as(null); + } } diff --git a/src/vs/workbench/api/node/mainThreadSCM.ts b/src/vs/workbench/api/node/mainThreadSCM.ts index 097981ac59a..0962c9453c7 100644 --- a/src/vs/workbench/api/node/mainThreadSCM.ts +++ b/src/vs/workbench/api/node/mainThreadSCM.ts @@ -110,13 +110,19 @@ export class MainThreadSCM extends MainThreadSCMShape { private proxy: ExtHostSCMShape; private providers: { [id: string]: MainThreadSCMProvider; } = Object.create(null); + private inputBoxListener: IDisposable; constructor( @IThreadService threadService: IThreadService, - @IInstantiationService private instantiationService: IInstantiationService + @IInstantiationService private instantiationService: IInstantiationService, + @ISCMService private scmService: ISCMService ) { super(); this.proxy = threadService.get(ExtHostContext.ExtHostSCM); + + this.inputBoxListener = this.scmService.inputBoxModel.onDidChangeContent(e => { + this.proxy.$onInputBoxValueChange(this.scmService.inputBoxModel.getValue()); + }); } $register(id: string, features: SCMProviderFeatures): void { @@ -144,10 +150,15 @@ export class MainThreadSCM extends MainThreadSCMShape { provider.$onChange(rawResourceGroups, count); } + $setInputBoxValue(value: string): void { + this.scmService.inputBoxModel.setValue(value); + } + dispose(): void { Object.keys(this.providers) .forEach(id => this.providers[id].dispose()); this.providers = Object.create(null); + this.inputBoxListener = dispose(this.inputBoxListener); } } diff --git a/src/vs/workbench/parts/scm/electron-browser/scmEditor.ts b/src/vs/workbench/parts/scm/electron-browser/scmEditor.ts index a2fb66de18f..9f24b14f6c6 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmEditor.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmEditor.ts @@ -5,9 +5,7 @@ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IModel, IEditorOptions, IDimension } from 'vs/editor/common/editorCommon'; -import { memoize } from 'vs/base/common/decorators'; +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'; @@ -24,11 +22,10 @@ import { TabCompletionController } from 'vs/editor/contrib/suggest/browser/tabCo import { ModesHoverController } from 'vs/editor/contrib/hover/browser/hover'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; -import URI from 'vs/base/common/uri'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import Event, { Emitter } from 'vs/base/common/event'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ITextModelResolverService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ISCMService } from 'vs/workbench/services/scm/common/scm'; class SCMCodeEditorWidget extends CodeEditorWidget { @@ -62,23 +59,11 @@ class SCMCodeEditorWidget extends CodeEditorWidget { export const InSCMInputContextKey = new RawContextKey('inSCMInput', false); -export class SCMEditor implements ITextModelContentProvider { +export class SCMEditor { private editor: SCMCodeEditorWidget; - private model: IModel; private disposables: IDisposable[] = []; - @memoize - get onDidChangeContent(): Event { - let listener: IDisposable; - const emitter = new Emitter({ - onFirstListenerAdd: () => listener = this.model.onDidChangeContent(() => emitter.fire()), - onLastListenerRemove: () => dispose(listener) - }); - - return emitter.event; - } - private get editorOptions(): IEditorOptions { return { wrappingColumn: 0, @@ -105,12 +90,11 @@ export class SCMEditor implements ITextModelContentProvider { container: HTMLElement, @IThemeService private themeService: IThemeService, @IInstantiationService instantiationService: IInstantiationService, + @IModeService private modeService: IModeService, @IModelService private modelService: IModelService, @IContextKeyService private contextKeyService: IContextKeyService, - @ITextModelResolverService private textModelResolverService: ITextModelResolverService + @ISCMService private scmService: ISCMService ) { - textModelResolverService.registerTextModelContentProvider('scm', this); - const scopedContextKeyService = this.contextKeyService.createScoped(container); InSCMInputContextKey.bindTo(scopedContextKeyService).set(true); this.disposables.push(scopedContextKeyService); @@ -122,10 +106,7 @@ export class SCMEditor implements ITextModelContentProvider { this.editor = scopedInstantiationService.createInstance(SCMCodeEditorWidget, container, this.editorOptions); this.themeService.onDidColorThemeChange(e => this.editor.updateOptions(this.editorOptions), null, this.disposables); - textModelResolverService.createModelReference(URI.parse('scm:input')).done(ref => { - this.model = ref.object.textEditorModel; - this.editor.setModel(this.model); - }); + this.editor.setModel(this.scmService.inputBoxModel); } get lineHeight(): number { @@ -134,12 +115,9 @@ export class SCMEditor implements ITextModelContentProvider { // TODO@joao TODO@alex isn't there a better way to get the number of lines? get lineCount(): number { - if (!this.model) { - return 0; - } - - const modelLength = this.model.getValueLength(); - const lastPosition = this.model.getPositionAt(modelLength); + 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; @@ -154,14 +132,6 @@ export class SCMEditor implements ITextModelContentProvider { this.editor.focus(); } - provideTextContent(resource: URI): TPromise { - if (resource.toString() !== 'scm:input') { - return TPromise.as(null); - } - - return TPromise.as(this.modelService.createModel('', null, resource)); - } - dispose(): void { this.disposables = dispose(this.disposables); } diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index af019e77619..f302f3a7380 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -190,9 +190,10 @@ export class SCMViewlet extends Viewlet { const editorContainer = append(root, $('.scm-editor')); this.editor = this.instantiationService.createInstance(SCMEditor, editorContainer); - this.editor.onDidChangeContent(() => this.layout(), null, this.disposables); this.disposables.push(this.editor); + this.disposables.push(this.scmService.inputBoxModel.onDidChangeContent(() => this.layout())); + // this.inputBox = new InputBox(this.inputBoxContainer, this.contextViewService, { // placeholder: localize('accept', "Message (press {0} to submit)", SCMViewlet.ACCEPT_KEYBINDING), // ariaLabel: localize('acceptAria', "Changes: Type message and press {0} to accept the changes", SCMViewlet.ACCEPT_KEYBINDING), diff --git a/src/vs/workbench/services/scm/common/scm.ts b/src/vs/workbench/services/scm/common/scm.ts index b22c3b79319..12dc2b0e22c 100644 --- a/src/vs/workbench/services/scm/common/scm.ts +++ b/src/vs/workbench/services/scm/common/scm.ts @@ -10,6 +10,7 @@ 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; @@ -53,6 +54,7 @@ export interface ISCMService { readonly onDidChangeProvider: Event; readonly providers: ISCMProvider[]; activeProvider: ISCMProvider | undefined; + readonly inputBoxModel: IModel; registerSCMProvider(provider: ISCMProvider): IDisposable; } \ No newline at end of file diff --git a/src/vs/workbench/services/scm/common/scmService.ts b/src/vs/workbench/services/scm/common/scmService.ts index d4aeb497ec4..957003bb734 100644 --- a/src/vs/workbench/services/scm/common/scmService.ts +++ b/src/vs/workbench/services/scm/common/scmService.ts @@ -8,6 +8,12 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import Event, { Emitter } from 'vs/base/common/event'; 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'; export class SCMService implements ISCMService { @@ -42,10 +48,23 @@ export class SCMService implements ISCMService { private _onDidChangeProvider = new Emitter(); get onDidChangeProvider(): Event { return this._onDidChangeProvider.event; } + private _inputBoxModel: IModel; + get inputBoxModel(): IModel { return this._inputBoxModel; } + constructor( - @IContextKeyService private contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @IModeService modeService: IModeService, + @IModelService modelService: IModelService ) { this.activeProviderContextKey = contextKeyService.createKey('scm.provider', 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 { -- GitLab