mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-31 09:10:30 -04:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
1344
src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts
Normal file
1344
src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-2 -2 16 16" enable-background="new -2 -2 16 16"><polygon fill="#C5C5C5" points="9,0 4.5,9 3,6 0,6 3,12 6,12 12,0"/></svg>
|
||||
|
After Width: | Height: | Size: 194 B |
1
src/vs/workbench/contrib/scm/browser/media/check.svg
Normal file
1
src/vs/workbench/contrib/scm/browser/media/check.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon points="5.382,13 2.382,7 6.618,7 7,7.764 9.382,3 13.618,3 8.618,13" fill="#F6F6F6"/><path d="M12 4l-4 8h-2l-2-4h2l1 2 3-6h2z" fill="#424242"/></svg>
|
||||
|
After Width: | Height: | Size: 220 B |
@@ -0,0 +1,59 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-editor .dirty-diff-glyph {
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.monaco-editor .dirty-diff-deleted:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
box-sizing: border-box;
|
||||
width: 4px;
|
||||
height: 0;
|
||||
z-index: 9;
|
||||
border-top: 4px solid transparent;
|
||||
border-bottom: 4px solid transparent;
|
||||
transition: border-top-width 80ms linear, border-bottom-width 80ms linear, bottom 80ms linear;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.monaco-editor .dirty-diff-glyph:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
height: 100%;
|
||||
width: 0;
|
||||
left: -2px;
|
||||
transition: width 80ms linear, left 80ms linear;
|
||||
}
|
||||
|
||||
.monaco-editor .dirty-diff-deleted:before {
|
||||
margin-left: 3px;
|
||||
height: 0;
|
||||
bottom: 0;
|
||||
transition: height 80ms linear;
|
||||
}
|
||||
|
||||
.monaco-editor .margin-view-overlays > div:hover > .dirty-diff-glyph:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
height: 100%;
|
||||
width: 9px;
|
||||
left: -6px;
|
||||
}
|
||||
|
||||
.monaco-editor .margin-view-overlays > div:hover > .dirty-diff-deleted:after {
|
||||
bottom: 0;
|
||||
border-top-width: 0;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
/* Hide glyph decorations when inside the inline diff editor */
|
||||
.monaco-editor.modified-in-monaco-diff-editor .margin-view-overlays > div > .dirty-diff-glyph {
|
||||
display: none;
|
||||
}
|
||||
1
src/vs/workbench/contrib/scm/browser/media/icon-dark.svg
Normal file
1
src/vs/workbench/contrib/scm/browser/media/icon-dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" height="28" viewBox="0 0 28 28" width="28" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m20 0c-2.22 0-4 1.78-4 4 0 1.46.82 2.76 2 3.44v2.56l-4 4-4-4v-2.56c1.18-.68 2-1.96 2-3.44 0-2.22-1.78-4-4-4s-4 1.78-4 4c0 1.46.82 2.76 2 3.44v3.56l6 6v3.56c-1.18.68-2 1.96-2 3.44 0 2.22 1.78 4 4 4s4-1.78 4-4c0-1.46-.82-2.76-2-3.44v-3.56l6-6v-3.56c1.18-.68 2-1.96 2-3.44 0-2.22-1.78-4-4-4zm-12 6.4c-1.32 0-2.4-1.1-2.4-2.4s1.1-2.4 2.4-2.4 2.4 1.1 2.4 2.4-1.1 2.4-2.4 2.4zm6 20c-1.32 0-2.4-1.1-2.4-2.4s1.1-2.4 2.4-2.4 2.4 1.1 2.4 2.4-1.1 2.4-2.4 2.4zm6-20c-1.32 0-2.4-1.1-2.4-2.4s1.1-2.4 2.4-2.4 2.4 1.1 2.4 2.4-1.1 2.4-2.4 2.4z" fill="#fff" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 690 B |
12
src/vs/workbench/contrib/scm/browser/media/icon-light.svg
Normal file
12
src/vs/workbench/contrib/scm/browser/media/icon-light.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="10px" height="16px" viewBox="0 0 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>repo-forked</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Octicons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="repo-forked" fill="#424242">
|
||||
<path d="M8,1 C6.89,1 6,1.89 6,3 C6,3.73 6.41,4.38 7,4.72 L7,6 L5,8 L3,6 L3,4.72 C3.59,4.38 4,3.74 4,3 C4,1.89 3.11,1 2,1 C0.89,1 0,1.89 0,3 C0,3.73 0.41,4.38 1,4.72 L1,6.5 L4,9.5 L4,11.28 C3.41,11.62 3,12.26 3,13 C3,14.11 3.89,15 5,15 C6.11,15 7,14.11 7,13 C7,12.27 6.59,11.62 6,11.28 L6,9.5 L9,6.5 L9,4.72 C9.59,4.38 10,3.74 10,3 C10,1.89 9.11,1 8,1 L8,1 Z M2,4.2 C1.34,4.2 0.8,3.65 0.8,3 C0.8,2.35 1.35,1.8 2,1.8 C2.65,1.8 3.2,2.35 3.2,3 C3.2,3.65 2.65,4.2 2,4.2 L2,4.2 Z M5,14.2 C4.34,14.2 3.8,13.65 3.8,13 C3.8,12.35 4.35,11.8 5,11.8 C5.65,11.8 6.2,12.35 6.2,13 C6.2,13.65 5.65,14.2 5,14.2 L5,14.2 Z M8,4.2 C7.34,4.2 6.8,3.65 6.8,3 C6.8,2.35 7.35,1.8 8,1.8 C8.65,1.8 9.2,2.35 9.2,3 C9.2,3.65 8.65,4.2 8,4.2 L8,4.2 Z" id="Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
164
src/vs/workbench/contrib/scm/browser/media/scmViewlet.css
Normal file
164
src/vs/workbench/contrib/scm/browser/media/scmViewlet.css
Normal file
@@ -0,0 +1,164 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .activitybar > .content .monaco-action-bar .action-label.scm {
|
||||
-webkit-mask: url('icon-dark.svg') no-repeat 50% 50%;
|
||||
}
|
||||
|
||||
.monaco-workbench .viewlet.scm-viewlet .collapsible.header .actions {
|
||||
width: initial;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.scm-viewlet .empty-message {
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
padding: 10px 22px 0 22px;
|
||||
}
|
||||
|
||||
.scm-viewlet:not(.empty) .empty-message,
|
||||
.scm-viewlet.empty .monaco-panel-view {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scm-viewlet .scm-status {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .scm-provider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-item {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.scm-viewlet .scm-provider > .count {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
.scm-viewlet .scm-provider > .count.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scm-viewlet .scm-provider > .type,
|
||||
.scm-viewlet .scm-provider > .name > .type {
|
||||
opacity: 0.7;
|
||||
margin-left: 0.5em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row {
|
||||
padding: 0 12px 0 20px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .resource-group {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .resource-group > .name {
|
||||
flex: 1;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .resource {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .resource.faded {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .resource > .name {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .resource > .name.strike-through > .monaco-icon-label > .monaco-icon-label-description-container > .label-name {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .resource > .name > .monaco-icon-label::after {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .resource > .decoration-icon {
|
||||
width: 16px;
|
||||
height: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list .monaco-list-row > .resource > .name > .monaco-icon-label > .actions {
|
||||
flex-grow: 100;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list .monaco-list-row > .resource-group > .actions,
|
||||
.scm-viewlet .monaco-list .monaco-list-row > .resource > .name > .monaco-icon-label > .actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list .monaco-list-row:hover > .resource-group > .actions,
|
||||
.scm-viewlet .monaco-list .monaco-list-row:hover > .resource > .name > .monaco-icon-label > .actions,
|
||||
.scm-viewlet .monaco-list .monaco-list-row.selected > .resource-group > .actions,
|
||||
.scm-viewlet .monaco-list .monaco-list-row.focused > .resource-group > .actions,
|
||||
.scm-viewlet .monaco-list .monaco-list-row.selected > .resource > .name > .monaco-icon-label > .actions,
|
||||
.scm-viewlet .monaco-list .monaco-list-row.focused > .resource > .name > .monaco-icon-label > .actions,
|
||||
.scm-viewlet .monaco-list:not(.selection-multiple) .monaco-list-row > .resource:hover > .actions {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.scm-viewlet .scm-status.show-actions > .monaco-list .monaco-list-row > .resource-group > .actions,
|
||||
.scm-viewlet .scm-status.show-actions > .monaco-list .monaco-list-row > .resource > .name > .monaco-icon-label > .actions {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row > .resource > .name > .monaco-icon-label > .actions .action-label,
|
||||
.scm-viewlet .monaco-list-row > .resource-group > .actions .action-label {
|
||||
width: 16px;
|
||||
height: 100%;
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.scm-viewlet .scm-editor {
|
||||
box-sizing: border-box;
|
||||
padding: 5px 9px 5px 16px;
|
||||
}
|
||||
|
||||
.scm-viewlet .scm-editor.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scm-viewlet .scm-editor > .monaco-inputbox {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
139
src/vs/workbench/contrib/scm/browser/scm.contribution.ts
Normal file
139
src/vs/workbench/contrib/scm/browser/scm.contribution.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { DirtyDiffWorkbenchController } from './dirtydiffDecorator';
|
||||
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet';
|
||||
import { VIEWLET_ID, ISCMRepository, ISCMService } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { StatusUpdater, StatusBarController } from './scmActivity';
|
||||
import { SCMViewlet } from 'vs/workbench/contrib/scm/browser/scmViewlet';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ContextKeyDefinedExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { SCMService } from 'vs/workbench/contrib/scm/common/scmService';
|
||||
|
||||
class OpenSCMViewletAction extends ShowViewletAction {
|
||||
|
||||
static readonly ID = VIEWLET_ID;
|
||||
static LABEL = localize('toggleGitViewlet', "Show Git");
|
||||
|
||||
constructor(id: string, label: string, @IViewletService viewletService: IViewletService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService) {
|
||||
super(id, label, VIEWLET_ID, viewletService, editorGroupService, layoutService);
|
||||
}
|
||||
}
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
|
||||
.registerWorkbenchContribution(DirtyDiffWorkbenchController, LifecyclePhase.Restored);
|
||||
|
||||
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor(
|
||||
SCMViewlet,
|
||||
VIEWLET_ID,
|
||||
localize('source control', "Source Control"),
|
||||
'scm',
|
||||
// {{SQL CARBON EDIT}}
|
||||
12
|
||||
));
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
|
||||
.registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Restored);
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
|
||||
.registerWorkbenchContribution(StatusBarController, LifecyclePhase.Restored);
|
||||
|
||||
// Register Action to Open Viewlet
|
||||
Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions).registerWorkbenchAction(
|
||||
new SyncActionDescriptor(OpenSCMViewletAction, VIEWLET_ID, localize('toggleSCMViewlet', "Show SCM"), {
|
||||
primary: 0,
|
||||
win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G },
|
||||
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G },
|
||||
mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_G }
|
||||
}),
|
||||
'View: Show SCM',
|
||||
localize('view', "View")
|
||||
);
|
||||
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
|
||||
id: 'scm',
|
||||
order: 5,
|
||||
title: localize('scmConfigurationTitle', "SCM"),
|
||||
type: 'object',
|
||||
properties: {
|
||||
'scm.alwaysShowProviders': {
|
||||
type: 'boolean',
|
||||
description: localize('alwaysShowProviders', "Controls whether to always show the Source Control Provider section."),
|
||||
default: false
|
||||
},
|
||||
'scm.providers.visible': {
|
||||
type: 'number',
|
||||
description: localize('providersVisible', "Controls how many providers are visible in the Source Control Provider section. Set to `0` to be able to manually resize the view."),
|
||||
default: 10
|
||||
},
|
||||
'scm.diffDecorations': {
|
||||
type: 'string',
|
||||
enum: ['all', 'gutter', 'overview', 'none'],
|
||||
default: 'all',
|
||||
description: localize('diffDecorations', "Controls diff decorations in the editor.")
|
||||
},
|
||||
'scm.diffDecorationsGutterWidth': {
|
||||
type: 'number',
|
||||
enum: [1, 2, 3, 4, 5],
|
||||
default: 3,
|
||||
description: localize('diffGutterWidth', "Controls the width(px) of diff decorations in gutter (added & modified).")
|
||||
},
|
||||
'scm.alwaysShowActions': {
|
||||
type: 'boolean',
|
||||
description: localize('alwaysShowActions', "Controls whether inline actions are always visible in the Source Control view."),
|
||||
default: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// View menu
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
|
||||
group: '3_views',
|
||||
command: {
|
||||
id: VIEWLET_ID,
|
||||
title: localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "S&&CM")
|
||||
},
|
||||
// {{SQL CARBON EDIT}} - Change the order
|
||||
order: 5
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'scm.acceptInput',
|
||||
description: { description: localize('scm accept', "SCM: Accept Input"), args: [] },
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: new ContextKeyDefinedExpr('scmRepository'),
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Enter,
|
||||
handler: accessor => {
|
||||
const contextKeyService = accessor.get(IContextKeyService);
|
||||
const context = contextKeyService.getContext(document.activeElement);
|
||||
const repository = context.getValue<ISCMRepository>('scmRepository');
|
||||
|
||||
if (!repository || !repository.provider.acceptInputCommand) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
const id = repository.provider.acceptInputCommand.id;
|
||||
const args = repository.provider.acceptInputCommand.arguments;
|
||||
|
||||
const commandService = accessor.get(ICommandService);
|
||||
return commandService.executeCommand(id, ...(args || []));
|
||||
}
|
||||
});
|
||||
|
||||
registerSingleton(ISCMService, SCMService);
|
||||
205
src/vs/workbench/contrib/scm/browser/scmActivity.ts
Normal file
205
src/vs/workbench/contrib/scm/browser/scmActivity.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { basename } from 'vs/base/common/resources';
|
||||
import { IDisposable, dispose, Disposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { VIEWLET_ID, ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { commonPrefixLength } from 'vs/base/common/strings';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class StatusUpdater implements IWorkbenchContribution {
|
||||
|
||||
private badgeDisposable: IDisposable = Disposable.None;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@ISCMService private readonly scmService: ISCMService,
|
||||
@IActivityService private readonly activityService: IActivityService,
|
||||
@ILogService private readonly logService: ILogService
|
||||
) {
|
||||
for (const repository of this.scmService.repositories) {
|
||||
this.onDidAddRepository(repository);
|
||||
}
|
||||
|
||||
this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables);
|
||||
this.render();
|
||||
}
|
||||
|
||||
private onDidAddRepository(repository: ISCMRepository): void {
|
||||
const provider = repository.provider;
|
||||
const onDidChange = Event.any(provider.onDidChange, provider.onDidChangeResources);
|
||||
const changeDisposable = onDidChange(() => this.render());
|
||||
|
||||
const onDidRemove = Event.filter(this.scmService.onDidRemoveRepository, e => e === repository);
|
||||
const removeDisposable = onDidRemove(() => {
|
||||
disposable.dispose();
|
||||
this.disposables = this.disposables.filter(d => d !== removeDisposable);
|
||||
this.render();
|
||||
});
|
||||
|
||||
const disposable = combinedDisposable([changeDisposable, removeDisposable]);
|
||||
this.disposables.push(disposable);
|
||||
}
|
||||
|
||||
private render(): void {
|
||||
this.badgeDisposable.dispose();
|
||||
|
||||
const count = this.scmService.repositories.reduce((r, repository) => {
|
||||
if (typeof repository.provider.count === 'number') {
|
||||
return r + repository.provider.count;
|
||||
} else {
|
||||
return r + repository.provider.groups.elements.reduce<number>((r, g) => r + g.elements.length, 0);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
// TODO@joao: remove
|
||||
this.logService.trace('SCM#StatusUpdater.render', count);
|
||||
|
||||
if (count > 0) {
|
||||
const badge = new NumberBadge(count, num => localize('scmPendingChangesBadge', '{0} pending changes', num));
|
||||
this.badgeDisposable = this.activityService.showActivity(VIEWLET_ID, badge, 'scm-viewlet-label');
|
||||
} else {
|
||||
this.badgeDisposable = Disposable.None;
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.badgeDisposable.dispose();
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
|
||||
export class StatusBarController implements IWorkbenchContribution {
|
||||
|
||||
private statusBarDisposable: IDisposable = Disposable.None;
|
||||
private focusDisposable: IDisposable = Disposable.None;
|
||||
private focusedRepository: ISCMRepository | undefined = undefined;
|
||||
private focusedProviderContextKey: IContextKey<string | undefined>;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@ISCMService private readonly scmService: ISCMService,
|
||||
@IStatusbarService private readonly statusbarService: IStatusbarService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IEditorService private readonly editorService: IEditorService
|
||||
) {
|
||||
this.focusedProviderContextKey = contextKeyService.createKey<string | undefined>('scmProvider', undefined);
|
||||
this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables);
|
||||
|
||||
for (const repository of this.scmService.repositories) {
|
||||
this.onDidAddRepository(repository);
|
||||
}
|
||||
|
||||
editorService.onDidActiveEditorChange(this.onDidActiveEditorChange, this, this.disposables);
|
||||
}
|
||||
|
||||
private onDidActiveEditorChange(): void {
|
||||
if (!this.editorService.activeEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resource = this.editorService.activeEditor.getResource();
|
||||
|
||||
if (!resource || resource.scheme !== 'file') {
|
||||
return;
|
||||
}
|
||||
|
||||
let bestRepository: ISCMRepository | null = null;
|
||||
let bestMatchLength = Number.NEGATIVE_INFINITY;
|
||||
|
||||
for (const repository of this.scmService.repositories) {
|
||||
const root = repository.provider.rootUri;
|
||||
|
||||
if (!root) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const rootFSPath = root.fsPath;
|
||||
const prefixLength = commonPrefixLength(rootFSPath, resource.fsPath);
|
||||
|
||||
if (prefixLength === rootFSPath.length && prefixLength > bestMatchLength) {
|
||||
bestRepository = repository;
|
||||
bestMatchLength = prefixLength;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestRepository) {
|
||||
this.onDidFocusRepository(bestRepository);
|
||||
}
|
||||
}
|
||||
|
||||
private onDidAddRepository(repository: ISCMRepository): void {
|
||||
const changeDisposable = repository.onDidFocus(() => this.onDidFocusRepository(repository));
|
||||
const onDidRemove = Event.filter(this.scmService.onDidRemoveRepository, e => e === repository);
|
||||
const removeDisposable = onDidRemove(() => {
|
||||
disposable.dispose();
|
||||
this.disposables = this.disposables.filter(d => d !== removeDisposable);
|
||||
|
||||
if (this.scmService.repositories.length === 0) {
|
||||
this.onDidFocusRepository(undefined);
|
||||
} else if (this.focusedRepository === repository) {
|
||||
this.scmService.repositories[0].focus();
|
||||
}
|
||||
});
|
||||
|
||||
const disposable = combinedDisposable([changeDisposable, removeDisposable]);
|
||||
this.disposables.push(disposable);
|
||||
|
||||
if (!this.focusedRepository) {
|
||||
this.onDidFocusRepository(repository);
|
||||
}
|
||||
}
|
||||
|
||||
private onDidFocusRepository(repository: ISCMRepository | undefined): void {
|
||||
if (this.focusedRepository === repository) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.focusedRepository = repository;
|
||||
this.focusedProviderContextKey.set(repository && repository.provider.id);
|
||||
this.focusDisposable.dispose();
|
||||
|
||||
if (repository && repository.provider.onDidChangeStatusBarCommands) {
|
||||
this.focusDisposable = repository.provider.onDidChangeStatusBarCommands(() => this.render(repository));
|
||||
}
|
||||
|
||||
this.render(repository);
|
||||
}
|
||||
|
||||
private render(repository: ISCMRepository | undefined): void {
|
||||
this.statusBarDisposable.dispose();
|
||||
|
||||
if (!repository) {
|
||||
return;
|
||||
}
|
||||
|
||||
const commands = repository.provider.statusBarCommands || [];
|
||||
const label = repository.provider.rootUri
|
||||
? `${basename(repository.provider.rootUri)} (${repository.provider.label})`
|
||||
: repository.provider.label;
|
||||
|
||||
const disposables = commands.map(c => this.statusbarService.addEntry({
|
||||
text: c.title,
|
||||
tooltip: `${label} - ${c.tooltip}`,
|
||||
command: c.id,
|
||||
arguments: c.arguments
|
||||
}, MainThreadStatusBarAlignment.LEFT, 10000));
|
||||
|
||||
this.statusBarDisposable = combinedDisposable(disposables);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.focusDisposable.dispose();
|
||||
this.statusBarDisposable.dispose();
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
173
src/vs/workbench/contrib/scm/browser/scmMenus.ts
Normal file
173
src/vs/workbench/contrib/scm/browser/scmMenus.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/scmViewlet';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { fillInContextMenuActions, fillInActionBarActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { ISCMProvider, ISCMResource, ISCMResourceGroup } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { isSCMResource } from './scmUtil';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { ISplice } from 'vs/base/common/sequence';
|
||||
|
||||
function actionEquals(a: IAction, b: IAction): boolean {
|
||||
return a.id === b.id;
|
||||
}
|
||||
|
||||
interface ISCMResourceGroupMenuEntry extends IDisposable {
|
||||
readonly group: ISCMResourceGroup;
|
||||
}
|
||||
|
||||
interface ISCMMenus {
|
||||
readonly resourceGroupMenu: IMenu;
|
||||
readonly resourceMenu: IMenu;
|
||||
}
|
||||
|
||||
export function getSCMResourceContextKey(resource: ISCMResourceGroup | ISCMResource): string {
|
||||
return isSCMResource(resource) ? resource.resourceGroup.id : resource.id;
|
||||
}
|
||||
|
||||
export class SCMMenus implements IDisposable {
|
||||
|
||||
private contextKeyService: IContextKeyService;
|
||||
private titleMenu: IMenu;
|
||||
private titleActions: IAction[] = [];
|
||||
private titleSecondaryActions: IAction[] = [];
|
||||
|
||||
private readonly _onDidChangeTitle = new Emitter<void>();
|
||||
readonly onDidChangeTitle: Event<void> = this._onDidChangeTitle.event;
|
||||
|
||||
private readonly resourceGroupMenuEntries: ISCMResourceGroupMenuEntry[] = [];
|
||||
private readonly resourceGroupMenus = new Map<ISCMResourceGroup, ISCMMenus>();
|
||||
|
||||
private readonly disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
provider: ISCMProvider | undefined,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IMenuService private readonly menuService: IMenuService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService
|
||||
) {
|
||||
this.contextKeyService = contextKeyService.createScoped();
|
||||
const scmProviderKey = this.contextKeyService.createKey<string | undefined>('scmProvider', undefined);
|
||||
|
||||
if (provider) {
|
||||
scmProviderKey.set(provider.contextValue);
|
||||
this.onDidSpliceGroups({ start: 0, deleteCount: 0, toInsert: provider.groups.elements });
|
||||
provider.groups.onDidSplice(this.onDidSpliceGroups, this, this.disposables);
|
||||
} else {
|
||||
scmProviderKey.set('');
|
||||
}
|
||||
|
||||
this.titleMenu = this.menuService.createMenu(MenuId.SCMTitle, this.contextKeyService);
|
||||
this.disposables.push(this.titleMenu);
|
||||
|
||||
this.titleMenu.onDidChange(this.updateTitleActions, this, this.disposables);
|
||||
this.updateTitleActions();
|
||||
}
|
||||
|
||||
private updateTitleActions(): void {
|
||||
const primary: IAction[] = [];
|
||||
const secondary: IAction[] = [];
|
||||
|
||||
fillInActionBarActions(this.titleMenu, { shouldForwardArgs: true }, { primary, secondary });
|
||||
|
||||
if (equals(primary, this.titleActions, actionEquals) && equals(secondary, this.titleSecondaryActions, actionEquals)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.titleActions = primary;
|
||||
this.titleSecondaryActions = secondary;
|
||||
|
||||
this._onDidChangeTitle.fire();
|
||||
}
|
||||
|
||||
getTitleActions(): IAction[] {
|
||||
return this.titleActions;
|
||||
}
|
||||
|
||||
getTitleSecondaryActions(): IAction[] {
|
||||
return this.titleSecondaryActions;
|
||||
}
|
||||
|
||||
getResourceGroupContextActions(group: ISCMResourceGroup): IAction[] {
|
||||
return this.getActions(MenuId.SCMResourceGroupContext, group).secondary;
|
||||
}
|
||||
|
||||
getResourceContextActions(resource: ISCMResource): IAction[] {
|
||||
return this.getActions(MenuId.SCMResourceContext, resource).secondary;
|
||||
}
|
||||
|
||||
private getActions(menuId: MenuId, resource: ISCMResourceGroup | ISCMResource): { primary: IAction[]; secondary: IAction[]; } {
|
||||
const contextKeyService = this.contextKeyService.createScoped();
|
||||
contextKeyService.createKey('scmResourceGroup', getSCMResourceContextKey(resource));
|
||||
|
||||
const menu = this.menuService.createMenu(menuId, contextKeyService);
|
||||
const primary: IAction[] = [];
|
||||
const secondary: IAction[] = [];
|
||||
const result = { primary, secondary };
|
||||
fillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g));
|
||||
|
||||
menu.dispose();
|
||||
contextKeyService.dispose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getResourceGroupMenu(group: ISCMResourceGroup): IMenu {
|
||||
if (!this.resourceGroupMenus.has(group)) {
|
||||
throw new Error('SCM Resource Group menu not found');
|
||||
}
|
||||
|
||||
return this.resourceGroupMenus.get(group)!.resourceGroupMenu;
|
||||
}
|
||||
|
||||
getResourceMenu(group: ISCMResourceGroup): IMenu {
|
||||
if (!this.resourceGroupMenus.has(group)) {
|
||||
throw new Error('SCM Resource Group menu not found');
|
||||
}
|
||||
|
||||
return this.resourceGroupMenus.get(group)!.resourceMenu;
|
||||
}
|
||||
|
||||
private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice<ISCMResourceGroup>): void {
|
||||
const menuEntriesToInsert = toInsert.map<ISCMResourceGroupMenuEntry>(group => {
|
||||
const contextKeyService = this.contextKeyService.createScoped();
|
||||
contextKeyService.createKey('scmProvider', group.provider.contextValue);
|
||||
contextKeyService.createKey('scmResourceGroup', getSCMResourceContextKey(group));
|
||||
|
||||
const resourceGroupMenu = this.menuService.createMenu(MenuId.SCMResourceGroupContext, contextKeyService);
|
||||
const resourceMenu = this.menuService.createMenu(MenuId.SCMResourceContext, contextKeyService);
|
||||
|
||||
this.resourceGroupMenus.set(group, { resourceGroupMenu, resourceMenu });
|
||||
|
||||
return {
|
||||
group,
|
||||
dispose() {
|
||||
contextKeyService.dispose();
|
||||
resourceGroupMenu.dispose();
|
||||
resourceMenu.dispose();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const deleted = this.resourceGroupMenuEntries.splice(start, deleteCount, ...menuEntriesToInsert);
|
||||
|
||||
for (const entry of deleted) {
|
||||
this.resourceGroupMenus.delete(entry.group);
|
||||
entry.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this.disposables);
|
||||
dispose(this.resourceGroupMenuEntries);
|
||||
this.resourceGroupMenus.clear();
|
||||
}
|
||||
}
|
||||
10
src/vs/workbench/contrib/scm/browser/scmUtil.ts
Normal file
10
src/vs/workbench/contrib/scm/browser/scmUtil.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ISCMResourceGroup, ISCMResource } from 'vs/workbench/contrib/scm/common/scm';
|
||||
|
||||
export function isSCMResource(element: ISCMResourceGroup | ISCMResource): element is ISCMResource {
|
||||
return !!(element as ISCMResource).sourceUri;
|
||||
}
|
||||
1253
src/vs/workbench/contrib/scm/browser/scmViewlet.ts
Normal file
1253
src/vs/workbench/contrib/scm/browser/scmViewlet.ts
Normal file
File diff suppressed because it is too large
Load Diff
124
src/vs/workbench/contrib/scm/common/scm.ts
Normal file
124
src/vs/workbench/contrib/scm/common/scm.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views';
|
||||
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 { Command } from 'vs/editor/common/modes';
|
||||
import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ISequence } from 'vs/base/common/sequence';
|
||||
|
||||
export const VIEWLET_ID = 'workbench.view.scm';
|
||||
export const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID);
|
||||
|
||||
export interface IBaselineResourceProvider {
|
||||
getBaselineResource(resource: URI): Promise<URI>;
|
||||
}
|
||||
|
||||
export const ISCMService = createDecorator<ISCMService>('scm');
|
||||
|
||||
export interface ISCMResourceDecorations {
|
||||
icon?: URI;
|
||||
iconDark?: URI;
|
||||
tooltip?: string;
|
||||
strikeThrough?: boolean;
|
||||
faded?: boolean;
|
||||
|
||||
source?: string;
|
||||
letter?: string;
|
||||
color?: ColorIdentifier;
|
||||
}
|
||||
|
||||
export interface ISCMResource {
|
||||
readonly resourceGroup: ISCMResourceGroup;
|
||||
readonly sourceUri: URI;
|
||||
readonly decorations: ISCMResourceDecorations;
|
||||
open(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ISCMResourceGroup extends ISequence<ISCMResource> {
|
||||
readonly provider: ISCMProvider;
|
||||
readonly label: string;
|
||||
readonly id: string;
|
||||
readonly hideWhenEmpty: boolean;
|
||||
readonly onDidChange: Event<void>;
|
||||
}
|
||||
|
||||
export interface ISCMProvider extends IDisposable {
|
||||
readonly label: string;
|
||||
readonly id: string;
|
||||
readonly contextValue: string;
|
||||
|
||||
readonly groups: ISequence<ISCMResourceGroup>;
|
||||
|
||||
// TODO@Joao: remove
|
||||
readonly onDidChangeResources: Event<void>;
|
||||
|
||||
readonly rootUri?: URI;
|
||||
readonly count?: number;
|
||||
readonly commitTemplate?: string;
|
||||
readonly onDidChangeCommitTemplate?: Event<string>;
|
||||
readonly onDidChangeStatusBarCommands?: Event<Command[]>;
|
||||
readonly acceptInputCommand?: Command;
|
||||
readonly statusBarCommands?: Command[];
|
||||
readonly onDidChange: Event<void>;
|
||||
|
||||
getOriginalResource(uri: URI): Promise<URI | null>;
|
||||
}
|
||||
|
||||
export const enum InputValidationType {
|
||||
Error = 0,
|
||||
Warning = 1,
|
||||
Information = 2
|
||||
}
|
||||
|
||||
export interface IInputValidation {
|
||||
message: string;
|
||||
type: InputValidationType;
|
||||
}
|
||||
|
||||
export interface IInputValidator {
|
||||
(value: string, cursorPosition: number): Promise<IInputValidation | undefined>;
|
||||
}
|
||||
|
||||
export interface ISCMInput {
|
||||
value: string;
|
||||
readonly onDidChange: Event<string>;
|
||||
|
||||
placeholder: string;
|
||||
readonly onDidChangePlaceholder: Event<string>;
|
||||
|
||||
validateInput: IInputValidator;
|
||||
readonly onDidChangeValidateInput: Event<void>;
|
||||
|
||||
visible: boolean;
|
||||
readonly onDidChangeVisibility: Event<boolean>;
|
||||
}
|
||||
|
||||
export interface ISCMRepository extends IDisposable {
|
||||
readonly onDidFocus: Event<void>;
|
||||
readonly selected: boolean;
|
||||
readonly onDidChangeSelection: Event<boolean>;
|
||||
readonly provider: ISCMProvider;
|
||||
readonly input: ISCMInput;
|
||||
focus(): void;
|
||||
setSelected(selected: boolean): void;
|
||||
}
|
||||
|
||||
export interface ISCMService {
|
||||
|
||||
readonly _serviceBrand: any;
|
||||
readonly onDidAddRepository: Event<ISCMRepository>;
|
||||
readonly onDidRemoveRepository: Event<ISCMRepository>;
|
||||
|
||||
readonly repositories: ISCMRepository[];
|
||||
readonly selectedRepositories: ISCMRepository[];
|
||||
readonly onDidChangeSelectedRepositories: Event<ISCMRepository[]>;
|
||||
|
||||
registerSCMProvider(provider: ISCMProvider): ISCMRepository;
|
||||
}
|
||||
175
src/vs/workbench/contrib/scm/common/scmService.ts
Normal file
175
src/vs/workbench/contrib/scm/common/scmService.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ISCMService, ISCMProvider, ISCMInput, ISCMRepository, IInputValidator } from './scm';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
|
||||
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; }
|
||||
|
||||
private _placeholder = '';
|
||||
|
||||
get placeholder(): string {
|
||||
return this._placeholder;
|
||||
}
|
||||
|
||||
set placeholder(placeholder: string) {
|
||||
this._placeholder = placeholder;
|
||||
this._onDidChangePlaceholder.fire(placeholder);
|
||||
}
|
||||
|
||||
private _onDidChangePlaceholder = new Emitter<string>();
|
||||
get onDidChangePlaceholder(): Event<string> { return this._onDidChangePlaceholder.event; }
|
||||
|
||||
private _visible = true;
|
||||
|
||||
get visible(): boolean {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
set visible(visible: boolean) {
|
||||
this._visible = visible;
|
||||
this._onDidChangeVisibility.fire(visible);
|
||||
}
|
||||
|
||||
private _onDidChangeVisibility = new Emitter<boolean>();
|
||||
get onDidChangeVisibility(): Event<boolean> { return this._onDidChangeVisibility.event; }
|
||||
|
||||
private _validateInput: IInputValidator = () => Promise.resolve(undefined);
|
||||
|
||||
get validateInput(): IInputValidator {
|
||||
return this._validateInput;
|
||||
}
|
||||
|
||||
set validateInput(validateInput: IInputValidator) {
|
||||
this._validateInput = validateInput;
|
||||
this._onDidChangeValidateInput.fire();
|
||||
}
|
||||
|
||||
private _onDidChangeValidateInput = new Emitter<void>();
|
||||
get onDidChangeValidateInput(): Event<void> { return this._onDidChangeValidateInput.event; }
|
||||
}
|
||||
|
||||
class SCMRepository implements ISCMRepository {
|
||||
|
||||
private _onDidFocus = new Emitter<void>();
|
||||
readonly onDidFocus: Event<void> = this._onDidFocus.event;
|
||||
|
||||
private _selected = false;
|
||||
get selected(): boolean {
|
||||
return this._selected;
|
||||
}
|
||||
|
||||
private _onDidChangeSelection = new Emitter<boolean>();
|
||||
readonly onDidChangeSelection: Event<boolean> = this._onDidChangeSelection.event;
|
||||
|
||||
readonly input: ISCMInput = new SCMInput();
|
||||
|
||||
constructor(
|
||||
public readonly provider: ISCMProvider,
|
||||
private disposable: IDisposable
|
||||
) { }
|
||||
|
||||
focus(): void {
|
||||
this._onDidFocus.fire();
|
||||
}
|
||||
|
||||
setSelected(selected: boolean): void {
|
||||
this._selected = selected;
|
||||
this._onDidChangeSelection.fire(selected);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposable.dispose();
|
||||
this.provider.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class SCMService implements ISCMService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private _providerIds = new Set<string>();
|
||||
private _repositories: ISCMRepository[] = [];
|
||||
get repositories(): ISCMRepository[] { return [...this._repositories]; }
|
||||
|
||||
private _selectedRepositories: ISCMRepository[] = [];
|
||||
get selectedRepositories(): ISCMRepository[] { return [...this._selectedRepositories]; }
|
||||
|
||||
private _onDidChangeSelectedRepositories = new Emitter<ISCMRepository[]>();
|
||||
readonly onDidChangeSelectedRepositories: Event<ISCMRepository[]> = this._onDidChangeSelectedRepositories.event;
|
||||
|
||||
private _onDidAddProvider = new Emitter<ISCMRepository>();
|
||||
get onDidAddRepository(): Event<ISCMRepository> { return this._onDidAddProvider.event; }
|
||||
|
||||
private _onDidRemoveProvider = new Emitter<ISCMRepository>();
|
||||
get onDidRemoveRepository(): Event<ISCMRepository> { return this._onDidRemoveProvider.event; }
|
||||
|
||||
constructor(@ILogService private readonly logService: ILogService) { }
|
||||
|
||||
registerSCMProvider(provider: ISCMProvider): ISCMRepository {
|
||||
this.logService.trace('SCMService#registerSCMProvider');
|
||||
|
||||
if (this._providerIds.has(provider.id)) {
|
||||
throw new Error(`SCM Provider ${provider.id} already exists.`);
|
||||
}
|
||||
|
||||
this._providerIds.add(provider.id);
|
||||
|
||||
const disposable = toDisposable(() => {
|
||||
const index = this._repositories.indexOf(repository);
|
||||
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
selectedDisposable.dispose();
|
||||
this._providerIds.delete(provider.id);
|
||||
this._repositories.splice(index, 1);
|
||||
this._onDidRemoveProvider.fire(repository);
|
||||
this.onDidChangeSelection();
|
||||
});
|
||||
|
||||
const repository = new SCMRepository(provider, disposable);
|
||||
const selectedDisposable = repository.onDidChangeSelection(this.onDidChangeSelection, this);
|
||||
|
||||
this._repositories.push(repository);
|
||||
this._onDidAddProvider.fire(repository);
|
||||
|
||||
// automatically select the first repository
|
||||
if (this._repositories.length === 1) {
|
||||
repository.setSelected(true);
|
||||
}
|
||||
|
||||
return repository;
|
||||
}
|
||||
|
||||
private onDidChangeSelection(): void {
|
||||
const selectedRepositories = this._repositories.filter(r => r.selected);
|
||||
|
||||
if (equals(this._selectedRepositories, selectedRepositories)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._selectedRepositories = this._repositories.filter(r => r.selected);
|
||||
this._onDidChangeSelectedRepositories.fire(this.selectedRepositories);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user