diff --git a/examples/browser/package.json b/examples/browser/package.json index 2c7fa34c1502d..0a156334ad030 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -24,7 +24,6 @@ "@theia/filesystem": "^0.3.19", "@theia/getting-started": "^0.3.19", "@theia/git": "^0.3.19", - "@theia/scm": "^0.3.19", "@theia/java": "^0.3.19", "@theia/java-debug": "^0.3.19", "@theia/json": "^0.3.19", @@ -45,6 +44,7 @@ "@theia/preview": "^0.3.19", "@theia/process": "^0.3.19", "@theia/python": "^0.3.19", + "@theia/scm": "^0.3.19", "@theia/search-in-workspace": "^0.3.19", "@theia/task": "^0.3.19", "@theia/terminal": "^0.3.19", diff --git a/packages/git/package.json b/packages/git/package.json index 974ec3b6822a6..f6f942b315b53 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -8,8 +8,8 @@ "@theia/filesystem": "^0.3.19", "@theia/languages": "^0.3.19", "@theia/navigator": "^0.3.19", - "@theia/workspace": "^0.3.19", "@theia/scm": "^0.3.19", + "@theia/workspace": "^0.3.19", "@types/diff": "^3.2.2", "@types/fs-extra": "^4.0.2", "@types/p-queue": "^2.3.1", diff --git a/packages/git/src/browser/git-view-contribution.ts b/packages/git/src/browser/git-view-contribution.ts index 7b9ded994dbb6..17f86b26c9ee9 100644 --- a/packages/git/src/browser/git-view-contribution.ts +++ b/packages/git/src/browser/git-view-contribution.ts @@ -37,7 +37,7 @@ import { GitQuickOpenService, GitAction } from './git-quick-open-service'; import { GitSyncService } from './git-sync-service'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { GitPrompt } from '../common/git-prompt'; -import { ScmRepository, ScmService, ScmCommand } from '@theia/scm/lib/common'; +import { ScmRepository, ScmService, ScmCommand } from '@theia/scm/lib/browser'; import { GitRepositoryProvider } from './git-repository-provider'; export const GIT_WIDGET_FACTORY_ID = 'git'; @@ -124,7 +124,7 @@ export class GitViewContribution extends AbstractViewContribution static GIT_REPOSITORY_STATUS = 'git-repository-status'; static GIT_SYNC_STATUS = 'git-sync-status'; - static ID_HANDLE = 0; + private static ID_HANDLE = 0; protected toDispose = new DisposableCollection(); @@ -167,7 +167,7 @@ export class GitViewContribution extends AbstractViewContribution if (this.hasMultipleRepositories()) { const path = new URI(repository.localUri).path; this.scmService.selectedRepositories.forEach(scmRepo => scmRepo.setSelected(false)); - const scmRepository = this.scmService.repositories.find(scmRepo => scmRepo.provider.rootUri === repository.localUri); + const scmRepository = this.scmService.repositories.find(scmRepo => scmRepo.provider.rootUri === repository.localUri); if (scmRepository) { scmRepository.setSelected(true); } @@ -228,6 +228,7 @@ export class GitViewContribution extends AbstractViewContribution ); } + /** Detect and handle added or removed repositories. */ checkNewOrRemovedRepositories() { const added = this.repositoryProvider @@ -248,7 +249,7 @@ export class GitViewContribution extends AbstractViewContribution this.dirtyRepositories = this.repositoryProvider.allRepositories; } - registerScmProvider(repository: Repository): ScmRepository { + private registerScmProvider(repository: Repository): ScmRepository { const uri = repository.localUri; const disposableCollection = new DisposableCollection(); const onDidChangeStatusBarCommandsEmitter = new Emitter(); diff --git a/packages/plugin-ext/src/api/plugin-api.ts b/packages/plugin-ext/src/api/plugin-api.ts index 16a05ad6a4014..81baa43244bb1 100644 --- a/packages/plugin-ext/src/api/plugin-api.ts +++ b/packages/plugin-ext/src/api/plugin-api.ts @@ -62,7 +62,7 @@ import { IJSONSchema, IJSONSchemaSnippet } from '@theia/core/lib/common/json-sch import { DebuggerDescription } from '@theia/debug/lib/common/debug-service'; import { DebugProtocol } from 'vscode-debugprotocol'; import { SymbolInformation } from 'vscode-languageserver-types'; -import { ScmCommand } from '@theia/scm/lib/common'; +import { ScmCommand } from '@theia/scm/lib/browser'; export interface PluginInitData { plugins: PluginMetadata[]; diff --git a/packages/plugin-ext/src/main/browser/scm-main.ts b/packages/plugin-ext/src/main/browser/scm-main.ts index 11b8fd336900f..071d0205eaee9 100644 --- a/packages/plugin-ext/src/main/browser/scm-main.ts +++ b/packages/plugin-ext/src/main/browser/scm-main.ts @@ -30,7 +30,7 @@ import { ScmResourceGroup, ScmService, ScmCommand -} from '@theia/scm/lib/common'; +} from '@theia/scm/lib/browser'; import { RPCProtocol } from '../../api/rpc-protocol'; import { interfaces } from 'inversify'; import { CancellationToken, DisposableCollection, Emitter, Event } from '@theia/core'; diff --git a/packages/plugin-ext/src/plugin/scm.ts b/packages/plugin-ext/src/plugin/scm.ts index a154c2860a3e1..80a94e2c43e9b 100644 --- a/packages/plugin-ext/src/plugin/scm.ts +++ b/packages/plugin-ext/src/plugin/scm.ts @@ -17,7 +17,6 @@ import * as theia from '@theia/plugin'; import { CommandRegistryExt, Plugin as InternalPlugin, PLUGIN_RPC_CONTEXT, ScmExt, ScmMain } from '../api/plugin-api'; import { RPCProtocol } from '../api/rpc-protocol'; -import { ScmCommand } from '@theia/scm/lib/common'; import { CancellationToken } from '@theia/core'; import { UriComponents } from '../common/uri-components'; import URI from '@theia/core/lib/common/uri'; @@ -183,7 +182,7 @@ class SourceControlImpl implements theia.SourceControl { this._acceptInputCommand = acceptInputCommand; if (acceptInputCommand) { - const command: ScmCommand = { + const command = { id: acceptInputCommand.id, text: acceptInputCommand.label ? acceptInputCommand.label : '' }; diff --git a/packages/scm/src/common/index.ts b/packages/scm/src/browser/index.ts similarity index 96% rename from packages/scm/src/common/index.ts rename to packages/scm/src/browser/index.ts index e4c4d22ed2181..68ff8632ca7c1 100644 --- a/packages/scm/src/common/index.ts +++ b/packages/scm/src/browser/index.ts @@ -14,4 +14,4 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -export * from './scm'; +export * from './scm-service'; diff --git a/packages/scm/src/browser/scm-contribution.ts b/packages/scm/src/browser/scm-contribution.ts index ca69823eeb2de..b9ffe51f32983 100644 --- a/packages/scm/src/browser/scm-contribution.ts +++ b/packages/scm/src/browser/scm-contribution.ts @@ -14,8 +14,14 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ import { inject, injectable } from 'inversify'; -import { FrontendApplicationContribution, StatusBar, StatusBarAlignment, StatusBarEntry } from '@theia/core/lib/browser'; -import { ScmCommand, ScmService } from '../common/scm'; +import { + FrontendApplication, + FrontendApplicationContribution, + StatusBar, + StatusBarAlignment, + StatusBarEntry +} from '@theia/core/lib/browser'; +import { ScmCommand, ScmService } from './scm-service'; @injectable() export class ScmContribution implements FrontendApplicationContribution { @@ -41,4 +47,8 @@ export class ScmContribution implements FrontendApplicationContribution { } }); } + + onStop(app: FrontendApplication): void { + this.scmService.dispose(); + } } diff --git a/packages/scm/src/browser/scm-frontend-module.ts b/packages/scm/src/browser/scm-frontend-module.ts index 23b6e489806f1..7eae491e85243 100644 --- a/packages/scm/src/browser/scm-frontend-module.ts +++ b/packages/scm/src/browser/scm-frontend-module.ts @@ -16,9 +16,8 @@ import { ContainerModule } from 'inversify'; import { ScmContribution } from './scm-contribution'; -import { ScmServiceImpl } from './scm-service-impl'; import { FrontendApplicationContribution } from '@theia/core/lib/browser'; -import { ScmService } from '../common/scm'; +import { ScmService, ScmServiceImpl } from './scm-service'; export default new ContainerModule(bind => { bind(ScmContribution).toSelf().inSingletonScope(); diff --git a/packages/scm/src/browser/scm-service-impl.ts b/packages/scm/src/browser/scm-service.ts similarity index 59% rename from packages/scm/src/browser/scm-service-impl.ts rename to packages/scm/src/browser/scm-service.ts index a62312c4c3fb5..f997c7158839c 100644 --- a/packages/scm/src/browser/scm-service-impl.ts +++ b/packages/scm/src/browser/scm-service.ts @@ -13,15 +13,118 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { - InputValidator, - ScmInput, - ScmProvider, - ScmRepository, - ScmService -} from '../common/scm'; -import { Disposable, Emitter, Event } from '@theia/core/lib/common'; +import { Disposable, DisposableCollection, Emitter, Event } from '@theia/core/lib/common'; import { injectable } from 'inversify'; +import URI from '@theia/core/lib/common/uri'; + +export const ScmService = Symbol('ScmService'); +export interface ScmService extends Disposable { + + readonly onDidAddRepository: Event; + readonly onDidRemoveRepository: Event; + + readonly repositories: ScmRepository[]; + readonly selectedRepositories: ScmRepository[]; + readonly onDidChangeSelectedRepositories: Event; + + registerScmProvider(provider: ScmProvider): ScmRepository; +} + +export interface ScmProvider extends Disposable { + readonly label: string; + readonly id: string; + readonly contextValue: string; + + readonly groups: ScmResourceGroup[]; + + readonly onDidChangeResources: Event; + + readonly rootUri?: string; + readonly count?: number; + readonly commitTemplate?: string; + readonly onDidChangeCommitTemplate?: Event; + readonly onDidChangeStatusBarCommands?: Event; + readonly acceptInputCommand?: ScmCommand; + readonly statusBarCommands?: ScmCommand[]; + readonly onDidChange: Event; + + getOriginalResource(uri: URI): Promise; +} + +export interface ScmResourceGroup { + readonly resources: ScmResource[]; + readonly provider: ScmProvider; + readonly label: string; + readonly id: string; + readonly hideWhenEmpty: boolean | undefined; + readonly onDidChange: Event; +} + +export interface ScmResource { + readonly resourceGroup: ScmResourceGroup; + readonly sourceUri: URI; + readonly decorations?: ScmResourceDecorations; + + open(): Promise; +} + +export interface ScmResourceDecorations { + icon?: URI; + tooltip?: string; + strikeThrough?: boolean; + faded?: boolean; + + source?: string; + letter?: string; +} + +export interface ScmCommand { + id: string; + text: string; + tooltip?: string; + command?: string; +} + +export const enum InputValidationType { + Error = 0, + Warning = 1, + Information = 2 +} + +export interface InputValidation { + message: string; + type: InputValidationType; +} + +export interface InputValidator { + (value: string, cursorPosition: number): Promise; +} + +export interface ScmInput { + value: string; + readonly onDidChange: Event; + + placeholder: string; + readonly onDidChangePlaceholder: Event; + + validateInput: InputValidator; + readonly onDidChangeValidateInput: Event; + + visible: boolean; + readonly onDidChangeVisibility: Event; +} + +export interface ScmRepository extends Disposable { + readonly onDidFocus: Event; + readonly selected: boolean; + readonly onDidChangeSelection: Event; + readonly provider: ScmProvider; + readonly input: ScmInput; + + focus(): void; + + setSelected(selected: boolean): void; +} @injectable() export class ScmServiceImpl implements ScmService { @@ -29,12 +132,19 @@ export class ScmServiceImpl implements ScmService { private _repositories: ScmRepository[] = []; private _selectedRepositories: ScmRepository[] = []; + private disposableCollection: DisposableCollection = new DisposableCollection(); private onDidChangeSelectedRepositoriesEmitter = new Emitter(); private onDidAddProviderEmitter = new Emitter(); private onDidRemoveProviderEmitter = new Emitter(); readonly onDidChangeSelectedRepositories: Event = this.onDidChangeSelectedRepositoriesEmitter.event; + constructor() { + this.disposableCollection.push(this.onDidChangeSelectedRepositoriesEmitter); + this.disposableCollection.push(this.onDidAddProviderEmitter); + this.disposableCollection.push(this.onDidRemoveProviderEmitter); + } + get repositories(): ScmRepository[] { return [...this._repositories]; } @@ -47,7 +157,9 @@ export class ScmServiceImpl implements ScmService { return this.onDidAddProviderEmitter.event; } - get onDidRemoveRepository(): Event { return this.onDidRemoveProviderEmitter.event; } + get onDidRemoveRepository(): Event { + return this.onDidRemoveProviderEmitter.event; + } registerScmProvider(provider: ScmProvider): ScmRepository { @@ -57,10 +169,7 @@ export class ScmServiceImpl implements ScmService { this.providerIds.add(provider.id); - function toDisposable(fn: () => void): Disposable { - return { dispose() { fn(); } }; - } - const disposable: Disposable = toDisposable(() => { + const disposable: Disposable = Disposable.create(() => { const index = this._repositories.indexOf(repository); if (index < 0) { return; @@ -72,7 +181,7 @@ export class ScmServiceImpl implements ScmService { this.onDidChangeSelection(); }); - const repository = new SCMRepositoryImpl(provider, disposable); + const repository = new ScmRepositoryImpl(provider, disposable); const selectedDisposable = repository.onDidChangeSelection(this.onDidChangeSelection, this); this._repositories.push(repository); @@ -90,9 +199,13 @@ export class ScmServiceImpl implements ScmService { this._selectedRepositories = this._repositories.filter(r => r.selected); this.onDidChangeSelectedRepositoriesEmitter.fire(this.selectedRepositories); } + + dispose(): void { + this.disposableCollection.dispose(); + } } -class SCMRepositoryImpl implements ScmRepository { +class ScmRepositoryImpl implements ScmRepository { private _onDidFocus = new Emitter(); readonly onDidFocus: Event = this._onDidFocus.event; @@ -105,7 +218,7 @@ class SCMRepositoryImpl implements ScmRepository { private _onDidChangeSelection = new Emitter(); readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; - readonly input: ScmInput = new SCMInputImpl(); + readonly input: ScmInput = new ScmInputImpl(); constructor( public readonly provider: ScmProvider, @@ -127,7 +240,7 @@ class SCMRepositoryImpl implements ScmRepository { } } -class SCMInputImpl implements ScmInput { +class ScmInputImpl implements ScmInput { private _value = ''; diff --git a/packages/scm/src/common/scm.ts b/packages/scm/src/common/scm.ts deleted file mode 100644 index a673907e71651..0000000000000 --- a/packages/scm/src/common/scm.ts +++ /dev/null @@ -1,126 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 Red Hat, Inc. and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * This Source Code may also be made available under the following Secondary - * Licenses when the conditions for such availability set forth in the Eclipse - * Public License v. 2.0 are satisfied: GNU General Public License, version 2 - * with the GNU Classpath Exception which is available at - * https://www.gnu.org/software/classpath/license.html. - * - * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 - ********************************************************************************/ -import URI from '@theia/core/lib/common/uri'; -import { Disposable, Event } from '@theia/core/lib/common'; - -export interface ScmResourceDecorations { - icon?: URI; - tooltip?: string; - strikeThrough?: boolean; - faded?: boolean; - - source?: string; - letter?: string; -} - -export interface ScmResource { - readonly resourceGroup: ScmResourceGroup; - readonly sourceUri: URI; - readonly decorations?: ScmResourceDecorations; - - open(): Promise; -} - -export interface ScmResourceGroup { - readonly resources: ScmResource[]; - readonly provider: ScmProvider; - readonly label: string; - readonly id: string; - readonly hideWhenEmpty: boolean | undefined; - readonly onDidChange: Event; -} - -export interface ScmProvider extends Disposable { - readonly label: string; - readonly id: string; - readonly contextValue: string; - - readonly groups: ScmResourceGroup[]; - - readonly onDidChangeResources: Event; - - readonly rootUri?: string; - readonly count?: number; - readonly commitTemplate?: string; - readonly onDidChangeCommitTemplate?: Event; - readonly onDidChangeStatusBarCommands?: Event; - readonly acceptInputCommand?: ScmCommand; - readonly statusBarCommands?: ScmCommand[]; - readonly onDidChange: Event; - - getOriginalResource(uri: URI): Promise; -} - -export interface ScmCommand { - id: string; - text: string; - tooltip?: string; - command?: string; -} - -export const enum InputValidationType { - Error = 0, - Warning = 1, - Information = 2 -} - -export interface InputValidation { - message: string; - type: InputValidationType; -} - -export interface InputValidator { - (value: string, cursorPosition: number): Promise; -} - -export interface ScmInput { - value: string; - readonly onDidChange: Event; - - placeholder: string; - readonly onDidChangePlaceholder: Event; - - validateInput: InputValidator; - readonly onDidChangeValidateInput: Event; - - visible: boolean; - readonly onDidChangeVisibility: Event; -} - -export interface ScmRepository extends Disposable { - readonly onDidFocus: Event; - readonly selected: boolean; - readonly onDidChangeSelection: Event; - readonly provider: ScmProvider; - readonly input: ScmInput; - - focus(): void; - - setSelected(selected: boolean): void; -} - -export const ScmService = Symbol('ScmService'); -export interface ScmService { - - readonly onDidAddRepository: Event; - readonly onDidRemoveRepository: Event; - - readonly repositories: ScmRepository[]; - readonly selectedRepositories: ScmRepository[]; - readonly onDidChangeSelectedRepositories: Event; - - registerScmProvider(provider: ScmProvider): ScmRepository; -}