Skip to content

Commit

Permalink
Separate quick diff API from SCM
Browse files Browse the repository at this point in the history
Part 1: split up the API.
upcoming in part 2: In editor UX for multiple quick diffs.
Fixes #169012
  • Loading branch information
alexr00 committed Jan 4, 2023
1 parent 2377c7a commit 8dd8242
Show file tree
Hide file tree
Showing 13 changed files with 255 additions and 42 deletions.
1 change: 1 addition & 0 deletions src/vs/workbench/api/browser/extensionHost.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import './mainThreadLogService';
import './mainThreadMessageService';
import './mainThreadOutputService';
import './mainThreadProgress';
import './mainThreadQuickDiff';
import './mainThreadQuickOpen';
import './mainThreadRemoteConnectionData';
import './mainThreadSaveParticipant';
Expand Down
54 changes: 54 additions & 0 deletions src/vs/workbench/api/browser/mainThreadQuickDiff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ExtHostContext, ExtHostQuickDiffShape, MainContext, MainThreadQuickDiffShape } from 'vs/workbench/api/common/extHost.protocol';
import { IQuickDiffService, QuickDiffProvider } from 'vs/workbench/contrib/scm/common/quickDiff';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';

@extHostNamedCustomer(MainContext.MainThreadQuickDiff)
export class MainThreadQuickDiff implements MainThreadQuickDiffShape {

private readonly proxy: ExtHostQuickDiffShape;
private providers = new Map<number, QuickDiffProvider>();
private providerDisposables = new Map<number, IDisposable>();

constructor(
extHostContext: IExtHostContext,
@IQuickDiffService private readonly quickDiffService: IQuickDiffService
) {
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostQuickDiff);
}

async $registerQuickDiffProvider(handle: number, label: string, rootUri: UriComponents | undefined): Promise<void> {
const provider: QuickDiffProvider = {
label,
rootUri: URI.revive(rootUri),
getOriginalResource: async (uri: URI) => {
return URI.revive(await this.proxy.$provideOriginalResource(handle, uri, new CancellationTokenSource().token));
}
};
this.providers.set(handle, provider);
const disposable = this.quickDiffService.addQuickDiffProvider(provider);
this.providerDisposables.set(handle, disposable);
}

async $unregisterQuickDiffProvider(handle: number): Promise<void> {
if (this.providers.has(handle)) {
this.providers.delete(handle);
}
if (this.providerDisposables.has(handle)) {
this.providerDisposables.delete(handle);
}
}

dispose(): void {
this.providers.clear();
dispose(this.providerDisposables.values());
this.providerDisposables.clear();
}
}
20 changes: 16 additions & 4 deletions src/vs/workbench/api/browser/mainThreadSCM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { MarshalledId } from 'vs/base/common/marshallingIds';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IQuickDiffService, QuickDiffProvider } from 'vs/workbench/contrib/scm/common/quickDiff';

class MainThreadSCMResourceGroup implements ISCMResourceGroup {

Expand Down Expand Up @@ -89,7 +90,7 @@ class MainThreadSCMResource implements ISCMResource {
}
}

class MainThreadSCMProvider implements ISCMProvider {
class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider {

private static ID_HANDLE = 0;
private _id = `scm${MainThreadSCMProvider.ID_HANDLE++}`;
Expand Down Expand Up @@ -133,12 +134,15 @@ class MainThreadSCMProvider implements ISCMProvider {
private readonly _onDidChange = new Emitter<void>();
readonly onDidChange: Event<void> = this._onDidChange.event;

private _quickDiff: IDisposable | undefined;

constructor(
private readonly proxy: ExtHostSCMShape,
private readonly _handle: number,
private readonly _contextValue: string,
private readonly _label: string,
private readonly _rootUri: URI | undefined
private readonly _rootUri: URI | undefined,
private readonly _quickDiffService: IQuickDiffService
) { }

$updateSourceControl(features: SCMProviderFeatures): void {
Expand All @@ -152,6 +156,13 @@ class MainThreadSCMProvider implements ISCMProvider {
if (typeof features.statusBarCommands !== 'undefined') {
this._onDidChangeStatusBarCommands.fire(this.statusBarCommands!);
}

if (features.hasQuickDiffProvider && !this._quickDiff) {
this._quickDiff = this._quickDiffService.addQuickDiffProvider(this);
} else if (features.hasQuickDiffProvider === false && this._quickDiff) {
this._quickDiff.dispose();
this._quickDiff = undefined;
}
}

$registerGroups(_groups: [number /*handle*/, string /*id*/, string /*label*/, SCMGroupFeatures][]): void {
Expand Down Expand Up @@ -284,7 +295,8 @@ export class MainThreadSCM implements MainThreadSCMShape {
constructor(
extHostContext: IExtHostContext,
@ISCMService private readonly scmService: ISCMService,
@ISCMViewService private readonly scmViewService: ISCMViewService
@ISCMViewService private readonly scmViewService: ISCMViewService,
@IQuickDiffService private readonly quickDiffService: IQuickDiffService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostSCM);
}
Expand All @@ -300,7 +312,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
}

$registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined): void {
const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri ? URI.revive(rootUri) : undefined);
const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri ? URI.revive(rootUri) : undefined, this.quickDiffService);
const repository = this.scmService.registerSCMProvider(provider);
this._repositories.set(handle, repository);

Expand Down
6 changes: 6 additions & 0 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debu
import { IExtHostLocalizationService } from 'vs/workbench/api/common/extHostLocalizationService';
import { EditSessionIdentityMatch } from 'vs/platform/workspace/common/editSessions';
import { ExtHostProfileContentHandlers } from 'vs/workbench/api/common/extHostProfileContentHandler';
import { ExtHostQuickDiff } from 'vs/workbench/api/common/extHostQuickDiff';

export interface IExtensionRegistries {
mine: ExtensionDescriptionRegistry;
Expand Down Expand Up @@ -175,6 +176,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostLogService, extHostDocumentsAndEditors));
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, createExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostQuickDiff = rpcProtocol.set(ExtHostContext.ExtHostQuickDiff, new ExtHostQuickDiff(rpcProtocol));
const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, createExtHostComments(rpcProtocol, extHostCommands, extHostDocuments));
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol));
Expand Down Expand Up @@ -802,6 +804,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension, 'profileContentHandlers');
return extHostProfileContentHandlers.registrProfileContentHandler(extension, id, handler);
},
async registerQuickDiffProvider(quickDiffProvider: vscode.QuickDiffProvider, label: string, rootUri?: vscode.Uri): Promise<vscode.Disposable> {
checkProposedApiEnabled(extension, 'quickDiffProvider');
return extHostQuickDiff.registerQuickDiffProvider(quickDiffProvider, label, rootUri);
},
get tabGroups(): vscode.TabGroups {
return extHostEditorTabs.tabGroups;
}
Expand Down
11 changes: 11 additions & 0 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,11 @@ export interface MainThreadSCMShape extends IDisposable {
$setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void;
}

export interface MainThreadQuickDiffShape extends IDisposable {
$registerQuickDiffProvider(handle: number, label: string, rootUri: UriComponents | undefined): Promise<void>;
$unregisterQuickDiffProvider(handle: number): Promise<void>;
}

export type DebugSessionUUID = string;

export interface IDebugConfiguration {
Expand Down Expand Up @@ -1869,6 +1874,10 @@ export interface ExtHostSCMShape {
$setSelectedSourceControl(selectedSourceControlHandle: number | undefined): Promise<void>;
}

export interface ExtHostQuickDiffShape {
$provideOriginalResource(sourceControlHandle: number, uri: UriComponents, token: CancellationToken): Promise<UriComponents | null>;
}

export interface ExtHostTaskShape {
$provideTasks(handle: number, validTypes: { [key: string]: boolean }): Promise<tasks.ITaskSetDTO>;
$resolveTask(handle: number, taskDTO: tasks.ITaskDTO): Promise<tasks.ITaskDTO | undefined>;
Expand Down Expand Up @@ -2332,6 +2341,7 @@ export const MainContext = {
MainThreadMessageService: createProxyIdentifier<MainThreadMessageServiceShape>('MainThreadMessageService'),
MainThreadOutputService: createProxyIdentifier<MainThreadOutputServiceShape>('MainThreadOutputService'),
MainThreadProgress: createProxyIdentifier<MainThreadProgressShape>('MainThreadProgress'),
MainThreadQuickDiff: createProxyIdentifier<MainThreadQuickDiffShape>('MainThreadQuickDiff'),
MainThreadQuickOpen: createProxyIdentifier<MainThreadQuickOpenShape>('MainThreadQuickOpen'),
MainThreadStatusBar: createProxyIdentifier<MainThreadStatusBarShape>('MainThreadStatusBar'),
MainThreadSecretState: createProxyIdentifier<MainThreadSecretStateShape>('MainThreadSecretState'),
Expand Down Expand Up @@ -2384,6 +2394,7 @@ export const ExtHostContext = {
ExtHostLanguages: createProxyIdentifier<ExtHostLanguagesShape>('ExtHostLanguages'),
ExtHostLanguageFeatures: createProxyIdentifier<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures'),
ExtHostQuickOpen: createProxyIdentifier<ExtHostQuickOpenShape>('ExtHostQuickOpen'),
ExtHostQuickDiff: createProxyIdentifier<ExtHostQuickDiffShape>('ExtHostQuickDiff'),
ExtHostExtensionService: createProxyIdentifier<ExtHostExtensionServiceShape>('ExtHostExtensionService'),
ExtHostLogLevelServiceShape: createProxyIdentifier<ExtHostLogLevelServiceShape>('ExtHostLogLevelServiceShape'),
ExtHostTerminalService: createProxyIdentifier<ExtHostTerminalServiceShape>('ExtHostTerminalService'),
Expand Down
44 changes: 44 additions & 0 deletions src/vs/workbench/api/common/extHostQuickDiff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import type * as vscode from 'vscode';
import { CancellationToken } from 'vs/base/common/cancellation';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ExtHostQuickDiffShape, IMainContext, MainContext, MainThreadQuickDiffShape } from 'vs/workbench/api/common/extHost.protocol';
import { asPromise } from 'vs/base/common/async';

export class ExtHostQuickDiff implements ExtHostQuickDiffShape {
private static handlePool: number = 0;

private proxy: MainThreadQuickDiffShape;
private providers: Map<number, vscode.QuickDiffProvider> = new Map();

constructor(
mainContext: IMainContext
) {
this.proxy = mainContext.getProxy(MainContext.MainThreadQuickDiff);
}

$provideOriginalResource(handle: number, uriComponents: UriComponents, token: CancellationToken): Promise<UriComponents | null> {
const uri = URI.revive(uriComponents);
const provider = this.providers.get(handle);

if (!provider) {
return Promise.resolve(null);
}

return asPromise(() => provider.provideOriginalResource!(uri, token))
.then<UriComponents | null>(r => r || null);
}

async registerQuickDiffProvider(quickDiffProvider: vscode.QuickDiffProvider, label: string, rootUri?: vscode.Uri): Promise<vscode.Disposable> {
const handle = ExtHostQuickDiff.handlePool++;
this.providers.set(handle, quickDiffProvider);
await this.proxy.$registerQuickDiffProvider(handle, label, rootUri);
return {
dispose: () => this.providers.delete(handle)
};
}
}
6 changes: 3 additions & 3 deletions src/vs/workbench/contrib/format/browser/formatModified.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Progress } from 'vs/platform/progress/common/progress';
import { getOriginalResource } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator';
import { ISCMService } from 'vs/workbench/contrib/scm/common/scm';
import { IQuickDiffService } from 'vs/workbench/contrib/scm/common/quickDiff';

registerEditorAction(class FormatModifiedAction extends EditorAction {

Expand Down Expand Up @@ -50,11 +50,11 @@ registerEditorAction(class FormatModifiedAction extends EditorAction {


export async function getModifiedRanges(accessor: ServicesAccessor, modified: ITextModel): Promise<Range[] | undefined | null> {
const scmService = accessor.get(ISCMService);
const quickDiffService = accessor.get(IQuickDiffService);
const workerService = accessor.get(IEditorWorkerService);
const modelService = accessor.get(ITextModelService);

const original = await getOriginalResource(scmService, modified.uri);
const original = await getOriginalResource(quickDiffService, modified.uri);
if (!original) {
return null; // let undefined signify no changes, null represents no source control (there's probably a better way, but I can't think of one rn)
}
Expand Down
44 changes: 9 additions & 35 deletions src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import * as nls from 'vs/nls';

import 'vs/css!./media/dirtydiffDecorator';
import { ThrottledDelayer, first } from 'vs/base/common/async';
import { ThrottledDelayer } from 'vs/base/common/async';
import { IDisposable, dispose, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import * as ext from 'vs/workbench/common/contributions';
Expand All @@ -15,7 +15,7 @@ import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/se
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { URI } from 'vs/base/common/uri';
import { ISCMService, ISCMRepository, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm';
import { ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { IColorTheme, themeColorFromId, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { editorErrorForeground, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry';
Expand All @@ -34,7 +34,7 @@ import { IDiffEditorOptions, EditorOption } from 'vs/editor/common/config/editor
import { Action, IAction, ActionRunner } from 'vs/base/common/actions';
import { IActionBarOptions } from 'vs/base/browser/ui/actionbar/actionbar';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { basename, isEqualOrParent } from 'vs/base/common/resources';
import { basename } from 'vs/base/common/resources';
import { MenuId, IMenuService, IMenu, MenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions';
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IEditorModel, ScrollType, IEditorContribution, IDiffEditorModel } from 'vs/editor/common/editorCommon';
Expand All @@ -51,11 +51,11 @@ import { TextCompareEditorActiveContext } from 'vs/workbench/common/contextkeys'
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IChange } from 'vs/editor/common/diff/smartLinesDiffComputer';
import { Color } from 'vs/base/common/color';
import { Iterable } from 'vs/base/common/iterator';
import { ResourceMap } from 'vs/base/common/map';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor';
import { FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files';
import { IQuickDiffService } from 'vs/workbench/contrib/scm/common/quickDiff';

class DiffActionRunner extends ActionRunner {

Expand Down Expand Up @@ -1067,37 +1067,10 @@ function compareChanges(a: IChange, b: IChange): number {
return a.originalEndLineNumber - b.originalEndLineNumber;
}

export function createProviderComparer(uri: URI): (a: ISCMProvider, b: ISCMProvider) => number {
return (a, b) => {
const aIsParent = isEqualOrParent(uri, a.rootUri!);
const bIsParent = isEqualOrParent(uri, b.rootUri!);

if (aIsParent && bIsParent) {
return a.rootUri!.fsPath.length - b.rootUri!.fsPath.length;
} else if (aIsParent) {
return -1;
} else if (bIsParent) {
return 1;
} else {
return 0;
}
};
}

export async function getOriginalResource(scmService: ISCMService, uri: URI): Promise<URI | null> {
const providers = Iterable.map(scmService.repositories, r => r.provider);
const rootedProviders = Array.from(Iterable.filter(providers, p => !!p.rootUri));

rootedProviders.sort(createProviderComparer(uri));

const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri)));

if (result) {
return result;
}

const nonRootedProviders = Iterable.filter(providers, p => !p.rootUri);
return first(Array.from(nonRootedProviders, p => () => p.getOriginalResource(uri)));
export async function getOriginalResource(quickDiffService: IQuickDiffService, uri: URI): Promise<URI | null> {
const quickDiffs = await quickDiffService.getQuickDiffs(uri);
return quickDiffs ? quickDiffs[0].originalResource : null;
}

export class DirtyDiffModel extends Disposable {
Expand All @@ -1123,6 +1096,7 @@ export class DirtyDiffModel extends Disposable {
constructor(
textFileModel: IResolvedTextFileEditorModel,
@ISCMService private readonly scmService: ISCMService,
@IQuickDiffService private readonly quickDiffService: IQuickDiffService,
@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@ITextModelService private readonly textModelResolverService: ITextModelService,
Expand Down Expand Up @@ -1278,7 +1252,7 @@ export class DirtyDiffModel extends Disposable {
}

const uri = this._model.resource;
return getOriginalResource(this.scmService, uri);
return getOriginalResource(this.quickDiffService, uri);
}

findNextClosestChange(lineNumber: number, inclusive = true): number {
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/contrib/scm/browser/scm.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { SCMRepositoriesViewPane } from 'vs/workbench/contrib/scm/browser/scmRep
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { Context as SuggestContext } from 'vs/editor/contrib/suggest/browser/suggest';
import { MANAGE_TRUST_COMMAND_ID, WorkspaceTrustContext } from 'vs/workbench/contrib/workspace/common/workspace';
import { IQuickDiffService } from 'vs/workbench/contrib/scm/common/quickDiff';
import { QuickDiffService } from 'vs/workbench/contrib/scm/common/quickDiffService';

ModesRegistry.registerLanguage({
id: 'scminput',
Expand Down Expand Up @@ -385,3 +387,4 @@ MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, {

registerSingleton(ISCMService, SCMService, InstantiationType.Delayed);
registerSingleton(ISCMViewService, SCMViewService, InstantiationType.Delayed);
registerSingleton(IQuickDiffService, QuickDiffService, InstantiationType.Delayed);
Loading

0 comments on commit 8dd8242

Please sign in to comment.