parent
e80d909439
commit
afa911d942
8 changed files with 92 additions and 28 deletions
|
@ -691,7 +691,9 @@ export class Repository implements Disposable {
|
|||
this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, onDotGitFileChange, outputChannel));
|
||||
|
||||
const root = Uri.file(repository.root);
|
||||
this._sourceControl = scm.createSourceControl('git', 'Git', root);
|
||||
this._sourceControl = scm.createSourceControl('git', 'Git', root, {
|
||||
treeRendering: true
|
||||
});
|
||||
|
||||
this._sourceControl.acceptInputCommand = { command: 'git.commit', title: localize('commit', "Commit"), arguments: [this._sourceControl] };
|
||||
this._sourceControl.quickDiffProvider = this;
|
||||
|
|
29
src/vs/vscode.proposed.d.ts
vendored
29
src/vs/vscode.proposed.d.ts
vendored
|
@ -764,6 +764,35 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Joao: SCM tree rendering
|
||||
|
||||
/**
|
||||
* Options for creating a [SourceControl](#SourceControl) instance.
|
||||
*/
|
||||
export interface SourceControlOptions {
|
||||
|
||||
/**
|
||||
* Whether tree rendering is supported by the [SourceControl](#SourceControl) instance.
|
||||
*/
|
||||
readonly treeRendering?: boolean;
|
||||
}
|
||||
|
||||
export namespace scm {
|
||||
|
||||
/**
|
||||
* Creates a new [source control](#SourceControl) instance.
|
||||
*
|
||||
* @param id An `id` for the source control. Something short, e.g.: `git`.
|
||||
* @param label A human-readable string for the source control. E.g.: `Git`.
|
||||
* @param rootUri An optional Uri of the root of the source control. E.g.: `Uri.parse(workspaceRoot)`.
|
||||
* @param options Additional options for creating the source control.
|
||||
* @return An instance of [source control](#SourceControl).
|
||||
*/
|
||||
export function createSourceControl(id: string, label: string, rootUri?: Uri, options?: SourceControlOptions): SourceControl;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Joao: SCM Input Box
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event';
|
|||
import { assign } from 'vs/base/common/objects';
|
||||
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../common/extHost.protocol';
|
||||
import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMProviderProps, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../common/extHost.protocol';
|
||||
import { Command } from 'vs/editor/common/modes';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ISplice, Sequence } from 'vs/base/common/sequence';
|
||||
|
@ -128,12 +128,15 @@ class MainThreadSCMProvider implements ISCMProvider {
|
|||
private readonly _onDidChange = new Emitter<void>();
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
get treeRendering(): boolean { return this._props.treeRendering; }
|
||||
|
||||
constructor(
|
||||
private readonly proxy: ExtHostSCMShape,
|
||||
private readonly _handle: number,
|
||||
private readonly _contextValue: string,
|
||||
private readonly _label: string,
|
||||
private readonly _rootUri: URI | undefined,
|
||||
private readonly _props: SCMProviderProps,
|
||||
@ISCMService scmService: ISCMService
|
||||
) { }
|
||||
|
||||
|
@ -287,8 +290,8 @@ export class MainThreadSCM implements MainThreadSCMShape {
|
|||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
$registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined): void {
|
||||
const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri && URI.revive(rootUri), this.scmService);
|
||||
$registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined, props: SCMProviderProps): void {
|
||||
const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri && URI.revive(rootUri), props, this.scmService);
|
||||
const repository = this.scmService.registerSCMProvider(provider);
|
||||
this._repositories.set(handle, repository);
|
||||
|
||||
|
|
|
@ -709,8 +709,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
get inputBox() {
|
||||
return extHostSCM.getLastInputBox(extension)!; // Strict null override - Deprecated api
|
||||
},
|
||||
createSourceControl(id: string, label: string, rootUri?: vscode.Uri) {
|
||||
return extHostSCM.createSourceControl(extension, id, label, rootUri);
|
||||
createSourceControl(id: string, label: string, rootUri?: vscode.Uri, opts?: vscode.SourceControlOptions) {
|
||||
return extHostSCM.createSourceControl(extension, id, label, rootUri, opts);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -656,6 +656,10 @@ export interface MainThreadExtensionServiceShape extends IDisposable {
|
|||
$onExtensionHostExit(code: number): void;
|
||||
}
|
||||
|
||||
export interface SCMProviderProps {
|
||||
readonly treeRendering: boolean;
|
||||
}
|
||||
|
||||
export interface SCMProviderFeatures {
|
||||
hasQuickDiffProvider?: boolean;
|
||||
count?: number;
|
||||
|
@ -689,7 +693,7 @@ export type SCMRawResourceSplices = [
|
|||
];
|
||||
|
||||
export interface MainThreadSCMShape extends IDisposable {
|
||||
$registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined): void;
|
||||
$registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined, props: SCMProviderProps): void;
|
||||
$updateSourceControl(handle: number, features: SCMProviderFeatures): void;
|
||||
$unregisterSourceControl(handle: number): void;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { debounce } from 'vs/base/common/decorators';
|
|||
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto } from './extHost.protocol';
|
||||
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto, SCMProviderProps } from './extHost.protocol';
|
||||
import { sortedDiff, equals } from 'vs/base/common/arrays';
|
||||
import { comparePaths } from 'vs/base/common/comparers';
|
||||
import * as vscode from 'vscode';
|
||||
|
@ -452,10 +452,15 @@ class ExtHostSourceControl implements vscode.SourceControl {
|
|||
private _commands: ExtHostCommands,
|
||||
private _id: string,
|
||||
private _label: string,
|
||||
private _rootUri?: vscode.Uri
|
||||
private _rootUri: vscode.Uri | undefined,
|
||||
_props: SCMProviderProps
|
||||
) {
|
||||
if (!_extension.enableProposedApi && _props.treeRendering) {
|
||||
throw new Error(`[${_extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${_extension.identifier.value}`);
|
||||
}
|
||||
|
||||
this._inputBox = new ExtHostSCMInputBox(_extension, this._proxy, this.handle);
|
||||
this._proxy.$registerSourceControl(this.handle, _id, _label, _rootUri);
|
||||
this._proxy.$registerSourceControl(this.handle, _id, _label, _rootUri, _props);
|
||||
}
|
||||
|
||||
private updatedResourceGroups = new Set<ExtHostSourceControlResourceGroup>();
|
||||
|
@ -517,6 +522,12 @@ class ExtHostSourceControl implements vscode.SourceControl {
|
|||
}
|
||||
}
|
||||
|
||||
function asProps(options: vscode.SourceControlOptions | undefined): SCMProviderProps {
|
||||
return {
|
||||
treeRendering: options && !!options.treeRendering || false
|
||||
};
|
||||
}
|
||||
|
||||
export class ExtHostSCM implements ExtHostSCMShape {
|
||||
|
||||
private static _handlePool: number = 0;
|
||||
|
@ -576,11 +587,11 @@ export class ExtHostSCM implements ExtHostSCMShape {
|
|||
});
|
||||
}
|
||||
|
||||
createSourceControl(extension: IExtensionDescription, id: string, label: string, rootUri: vscode.Uri | undefined): vscode.SourceControl {
|
||||
createSourceControl(extension: IExtensionDescription, id: string, label: string, rootUri: vscode.Uri | undefined, options?: vscode.SourceControlOptions): vscode.SourceControl {
|
||||
this.logService.trace('ExtHostSCM#createSourceControl', extension.identifier.value, id, label, rootUri);
|
||||
|
||||
const handle = ExtHostSCM._handlePool++;
|
||||
const sourceControl = new ExtHostSourceControl(extension, this._proxy, this._commands, id, label, rootUri);
|
||||
const sourceControl = new ExtHostSourceControl(extension, this._proxy, this._commands, id, label, rootUri, asProps(options));
|
||||
this._sourceControls.set(handle, sourceControl);
|
||||
|
||||
const sourceControls = this._sourceControlsByExtension.get(ExtensionIdentifier.toKey(extension.identifier)) || [];
|
||||
|
|
|
@ -11,7 +11,7 @@ import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs
|
|||
import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet';
|
||||
import { append, $, addClass, toggleClass, trackFocus, removeClass } from 'vs/base/browser/dom';
|
||||
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
|
||||
import { ISCMRepository, ISCMResourceGroup, ISCMResource, InputValidationType } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { ISCMRepository, ISCMResourceGroup, ISCMResource, InputValidationType, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels';
|
||||
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
@ -36,7 +36,7 @@ import { ThrottledDelayer, disposableTimeout } from 'vs/base/common/async';
|
|||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { ITreeNode, ITreeFilter, ITreeSorter, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
|
||||
import { ISequence, ISplice } from 'vs/base/common/sequence';
|
||||
import { ISplice } from 'vs/base/common/sequence';
|
||||
import { ResourceTree, IBranchNode, INode } from 'vs/base/common/resourceTree';
|
||||
import { ObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/tree/objectTree';
|
||||
import { Iterator } from 'vs/base/common/iterator';
|
||||
|
@ -402,6 +402,12 @@ class ViewModel {
|
|||
|
||||
get mode(): ViewModelMode { return this._mode; }
|
||||
set mode(mode: ViewModelMode) {
|
||||
mode = this.provider.treeRendering ? mode : ViewModelMode.List;
|
||||
|
||||
if (mode === this._mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._mode = mode;
|
||||
|
||||
for (const item of this.items) {
|
||||
|
@ -425,7 +431,7 @@ class ViewModel {
|
|||
private disposables = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
private groups: ISequence<ISCMResourceGroup>,
|
||||
private provider: ISCMProvider,
|
||||
private tree: ObjectTree<TreeElement, FuzzyScore>,
|
||||
private _mode: ViewModelMode,
|
||||
@IEditorService protected editorService: IEditorService,
|
||||
|
@ -484,8 +490,8 @@ class ViewModel {
|
|||
setVisible(visible: boolean): void {
|
||||
if (visible) {
|
||||
this.visibilityDisposables = new DisposableStore();
|
||||
this.groups.onDidSplice(this.onDidSpliceGroups, this, this.visibilityDisposables);
|
||||
this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: this.groups.elements });
|
||||
this.provider.groups.onDidSplice(this.onDidSpliceGroups, this, this.visibilityDisposables);
|
||||
this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: this.provider.groups.elements });
|
||||
|
||||
if (typeof this.scrollTop === 'number') {
|
||||
this.tree.scrollTop = this.scrollTop;
|
||||
|
@ -533,8 +539,8 @@ class ViewModel {
|
|||
}
|
||||
|
||||
// go backwards from last group
|
||||
for (let i = this.groups.elements.length - 1; i >= 0; i--) {
|
||||
const group = this.groups.elements[i];
|
||||
for (let i = this.provider.groups.elements.length - 1; i >= 0; i--) {
|
||||
const group = this.provider.groups.elements[i];
|
||||
|
||||
for (const resource of group.elements) {
|
||||
if (isEqual(uri, resource.sourceUri)) {
|
||||
|
@ -759,19 +765,25 @@ export class RepositoryPanel extends ViewletPanel {
|
|||
this._register(this.tree.onContextMenu(this.onListContextMenu, this));
|
||||
this._register(this.tree);
|
||||
|
||||
let mode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewModelMode.List : ViewModelMode.Tree;
|
||||
let mode: ViewModelMode;
|
||||
|
||||
const rootUri = this.repository.provider.rootUri;
|
||||
if (!this.repository.provider.treeRendering) {
|
||||
mode = ViewModelMode.List;
|
||||
} else {
|
||||
mode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewModelMode.List : ViewModelMode.Tree;
|
||||
|
||||
if (typeof rootUri !== 'undefined') {
|
||||
const storageMode = this.storageService.get(`scm.repository.viewMode:${rootUri.toString()}`, StorageScope.WORKSPACE) as ViewModelMode;
|
||||
const rootUri = this.repository.provider.rootUri;
|
||||
|
||||
if (typeof storageMode === 'string') {
|
||||
mode = storageMode;
|
||||
if (typeof rootUri !== 'undefined') {
|
||||
const storageMode = this.storageService.get(`scm.repository.viewMode:${rootUri.toString()}`, StorageScope.WORKSPACE) as ViewModelMode;
|
||||
|
||||
if (typeof storageMode === 'string') {
|
||||
mode = storageMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.viewModel = this.instantiationService.createInstance(ViewModel, this.repository.provider.groups, this.tree, mode);
|
||||
this.viewModel = this.instantiationService.createInstance(ViewModel, this.repository.provider, this.tree, mode);
|
||||
this._register(this.viewModel);
|
||||
|
||||
addClass(this.listContainer, 'file-icon-themable-tree');
|
||||
|
@ -781,8 +793,10 @@ export class RepositoryPanel extends ViewletPanel {
|
|||
this._register(this.themeService.onDidFileIconThemeChange(this.updateIndentStyles, this));
|
||||
this._register(this.viewModel.onDidChangeMode(this.onDidChangeMode, this));
|
||||
|
||||
this.toggleViewModelModeAction = new ToggleViewModeAction(this.viewModel);
|
||||
this._register(this.toggleViewModelModeAction);
|
||||
if (this.repository.provider.treeRendering) {
|
||||
this.toggleViewModelModeAction = new ToggleViewModeAction(this.viewModel);
|
||||
this._register(this.toggleViewModelModeAction);
|
||||
}
|
||||
|
||||
this._register(this.onDidChangeBodyVisibility(this._onDidChangeVisibility, this));
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ export interface ISCMProvider extends IDisposable {
|
|||
readonly acceptInputCommand?: Command;
|
||||
readonly statusBarCommands?: Command[];
|
||||
readonly onDidChange: Event<void>;
|
||||
readonly treeRendering: boolean;
|
||||
|
||||
getOriginalResource(uri: URI): Promise<URI | null>;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue