move code from parts to contrib (#8319)

This commit is contained in:
Anthony Dresser
2019-11-14 12:23:11 -08:00
committed by GitHub
parent 6438967202
commit 7a2c30e159
619 changed files with 848 additions and 848 deletions

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#d02e00;}</style></defs><title>globalerror_red</title><path class="cls-1" d="M8,0a7.92,7.92,0,0,1,4,1.09A8.15,8.15,0,0,1,14.91,4a8,8,0,0,1,.81,1.91,8,8,0,0,1-.81,6.16A8.15,8.15,0,0,1,12,14.92a8,8,0,0,1-8.07,0,8.15,8.15,0,0,1-2.87-2.87A8,8,0,0,1,1.09,4,8.15,8.15,0,0,1,4,1.11,7.92,7.92,0,0,1,8,0ZM8,15a6.88,6.88,0,0,0,1.86-.25,7,7,0,0,0,4.89-4.89,7.07,7.07,0,0,0,0-3.73A7,7,0,0,0,9.86,1.27a7.07,7.07,0,0,0-3.73,0A7,7,0,0,0,1.25,6.15a7.07,7.07,0,0,0,0,3.73,7,7,0,0,0,4.89,4.89A6.88,6.88,0,0,0,8,15Zm3.46-9.76L8.71,8l2.75,2.76-.7.7L8,8.73,5.24,11.48l-.7-.7L7.29,8,4.54,5.26l.7-.7L8,7.31l2.76-2.75Z"/></svg>

After

Width:  |  Height:  |  Size: 721 B

View File

@@ -0,0 +1,31 @@
<?xml version='1.0' standalone='no' ?>
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'>
<style>
circle {
animation: ball 0.6s linear infinite;
}
circle:nth-child(2) { animation-delay: 0.075s; }
circle:nth-child(3) { animation-delay: 0.15s; }
circle:nth-child(4) { animation-delay: 0.225s; }
circle:nth-child(5) { animation-delay: 0.3s; }
circle:nth-child(6) { animation-delay: 0.375s; }
circle:nth-child(7) { animation-delay: 0.45s; }
circle:nth-child(8) { animation-delay: 0.525s; }
@keyframes ball {
from { opacity: 1; }
to { opacity: 0.3; }
}
</style>
<g>
<circle cx='5' cy='1' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' />
<circle cx='9' cy='5' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='5' cy='9' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='1' cy='5' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,31 @@
<?xml version='1.0' standalone='no' ?>
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'>
<style>
circle {
animation: ball 0.6s linear infinite;
}
circle:nth-child(2) { animation-delay: 0.075s; }
circle:nth-child(3) { animation-delay: 0.15s; }
circle:nth-child(4) { animation-delay: 0.225s; }
circle:nth-child(5) { animation-delay: 0.3s; }
circle:nth-child(6) { animation-delay: 0.375s; }
circle:nth-child(7) { animation-delay: 0.45s; }
circle:nth-child(8) { animation-delay: 0.525s; }
@keyframes ball {
from { opacity: 1; }
to { opacity: 0.3; }
}
</style>
<g style="fill:white;">
<circle cx='5' cy='1' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' />
<circle cx='9' cy='5' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='5' cy='9' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='1' cy='5' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#3062d6;}.cls-2{fill:#fff;}</style></defs><title>cancelledstate_16x16</title><circle class="cls-1" cx="8.07" cy="8.07" r="7.93"/><polygon class="cls-2" points="8.82 8.07 11.53 10.78 10.83 11.48 8.12 8.78 5.41 11.48 4.7 10.78 7.42 8.07 4.65 5.31 5.36 4.61 8.12 7.37 10.83 4.67 11.53 5.36 8.82 8.07"/></svg>

After

Width:  |  Height:  |  Size: 423 B

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#212121;}</style></defs><title>queuedtask_16x16</title><path class="cls-1" d="M4,7a3.45,3.45,0,0,1-.93-.13,3.56,3.56,0,0,1-.84-.35A3.5,3.5,0,0,1,1,5.28,3.54,3.54,0,0,1,.6,4.44a3.54,3.54,0,0,1,0-1.87A3.54,3.54,0,0,1,1,1.74,3.5,3.5,0,0,1,2.21.48,3.55,3.55,0,0,1,3.05.13a3.54,3.54,0,0,1,1.87,0,3.55,3.55,0,0,1,.84.35A3.5,3.5,0,0,1,7,1.74a3.55,3.55,0,0,1,.35.84,3.54,3.54,0,0,1,0,1.87A3.56,3.56,0,0,1,7,5.28,3.5,3.5,0,0,1,5.76,6.54a3.55,3.55,0,0,1-.84.35A3.45,3.45,0,0,1,4,7ZM4,.44a3,3,0,0,0-.81.11A3.08,3.08,0,0,0,1,2.7,3.08,3.08,0,0,0,1,4.33,3.08,3.08,0,0,0,3.17,6.47a3.08,3.08,0,0,0,1.63,0A3.08,3.08,0,0,0,6.95,4.33a3.08,3.08,0,0,0,0-1.63A3.08,3.08,0,0,0,4.8.55,3,3,0,0,0,4,.44ZM4,3.51V1.32H3.55V3.95H5.3V3.51Z"/><rect class="cls-1" x="1" y="14.01" width="14" height="1"/><rect class="cls-1" x="1" y="11.03" width="14" height="1"/><rect class="cls-1" x="1" y="7.96" width="14" height="1"/><rect class="cls-1" x="9" y="4.88" width="5.97" height="1"/><rect class="cls-1" x="9" y="1.88" width="5.97" height="1"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>queuedtask_inverse_16x16</title><path class="cls-1" d="M4.08,7.37a3.45,3.45,0,0,1-.93-.13,3.56,3.56,0,0,1-.84-.35A3.5,3.5,0,0,1,1,5.63,3.54,3.54,0,0,1,.7,4.79a3.54,3.54,0,0,1,0-1.87A3.54,3.54,0,0,1,1,2.09,3.5,3.5,0,0,1,2.31.83,3.55,3.55,0,0,1,3.15.47,3.54,3.54,0,0,1,5,.47a3.55,3.55,0,0,1,.84.35A3.5,3.5,0,0,1,7.11,2.09a3.55,3.55,0,0,1,.35.84,3.54,3.54,0,0,1,0,1.87,3.56,3.56,0,0,1-.35.84A3.5,3.5,0,0,1,5.85,6.89,3.55,3.55,0,0,1,5,7.25,3.45,3.45,0,0,1,4.08,7.37Zm0-6.58A3,3,0,0,0,3.27.9,3.08,3.08,0,0,0,1.12,3a3.08,3.08,0,0,0,0,1.63A3.08,3.08,0,0,0,3.27,6.82a3.08,3.08,0,0,0,1.63,0A3.08,3.08,0,0,0,7,4.67,3.08,3.08,0,0,0,7,3,3.08,3.08,0,0,0,4.9.9,3,3,0,0,0,4.08.79Zm0,3.07V1.67H3.64V4.3H5.4V3.86Z"/><rect class="cls-1" x="1.09" y="14.36" width="14" height="1"/><rect class="cls-1" x="1.09" y="11.38" width="14" height="1"/><rect class="cls-1" x="1.09" y="8.31" width="14" height="1"/><rect class="cls-1" x="9.09" y="5.22" width="5.97" height="1"/><rect class="cls-1" x="9.09" y="2.22" width="5.97" height="1"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#3bb44a;}</style></defs><title>success_16x16</title><path class="cls-1" d="M16,3.16,5.48,13.69,0,8.2l.89-.89,4.6,4.59,9.63-9.62Z"/></svg>

After

Width:  |  Height:  |  Size: 255 B

View File

@@ -0,0 +1,76 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.empty-task-message {
padding: 10px 22px 0 22px;
}
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group {
line-height: 22px;
display: flex;
}
/* task label and description */
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .label {
text-overflow: ellipsis;
overflow: hidden;
font-weight: bold;
}
/* style for server name | database name */
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .description {
text-overflow: ellipsis;
overflow: hidden;
padding-left: 12px
}
/* style for timing */
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .time {
padding-left: 12px;
opacity: .6;
text-overflow: ellipsis;
font-style: italic
}
/* task icon status */
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon {
background-size: 16px;
background-position: left center;
background-repeat: no-repeat;
padding-right: 6px;
min-width: 22px;
height: 22px;
display: inline-block;
}
.vs .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.not-started {
background-image: url('status_queuedtask.svg');
}
.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.not-started,
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.not-started {
background-image: url('status_queuedtask_inverse.svg');
}
.vs .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.in-progress {
background-image: url("loading.svg");
}
.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.in-progress,
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.in-progress {
background-image: url("loading_inverse.svg");
}
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.success {
background-image: url("status_success.svg");
}
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.canceled {
background-image: url("status_cancelled.svg");
}
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.error {
background-image: url("error.svg");
}

View File

@@ -0,0 +1,93 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { localize } from 'vs/nls';
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
import { TasksPanel } from 'sql/workbench/contrib/tasks/browser/tasksPanel';
import * as lifecycle from 'vs/base/common/lifecycle';
import * as ext from 'vs/workbench/common/contributions';
import { ITaskService } from 'sql/platform/tasks/common/tasksService';
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
import { TASKS_PANEL_ID } from 'sql/workbench/contrib/tasks/common/tasks';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { ToggleTasksAction } from 'sql/workbench/contrib/tasks/browser/tasksActions';
export class StatusUpdater extends lifecycle.Disposable implements ext.IWorkbenchContribution {
static ID = 'data.taskhistory.statusUpdater';
private badgeHandle: lifecycle.IDisposable;
constructor(
@IActivityService private readonly activityBarService: IActivityService,
@ITaskService private readonly taskService: ITaskService,
@IPanelService private readonly panelService: IPanelService
) {
super();
this._register(this.taskService.onAddNewTask(args => {
this.panelService.openPanel(TASKS_PANEL_ID, true);
this.onServiceChange();
}));
this._register(this.taskService.onTaskComplete(task => {
this.onServiceChange();
}));
}
private onServiceChange(): void {
lifecycle.dispose(this.badgeHandle);
let numOfInProgressTask: number = this.taskService.getNumberOfInProgressTasks();
let badge: NumberBadge = new NumberBadge(numOfInProgressTask, n => localize('inProgressTasksChangesBadge', "{0} in progress tasks", n));
this.badgeHandle = this.activityBarService.showActivity(TASKS_PANEL_ID, badge, 'taskhistory-viewlet-label');
}
public getId(): string {
return StatusUpdater.ID;
}
public dispose(): void {
lifecycle.dispose(this.badgeHandle);
super.dispose();
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(
new SyncActionDescriptor(
ToggleTasksAction,
ToggleTasksAction.ID,
ToggleTasksAction.LABEL,
{ primary: KeyMod.CtrlCmd | KeyCode.KEY_T }),
'View: Toggle Tasks',
localize('viewCategory', "View")
);
// Register Output Panel
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescriptor(
TasksPanel,
TASKS_PANEL_ID,
localize('tasks', "Tasks"),
'output',
20,
ToggleTasksAction.ID
));
// Register StatusUpdater
(<ext.IWorkbenchContributionsRegistry>Registry.as(ext.Extensions.Workbench)).registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Restored);
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '4_panels',
command: {
id: ToggleTasksAction.ID,
title: localize({ key: 'miViewTasks', comment: ['&& denotes a mnemonic'] }, "&&Tasks")
},
order: 2
});

View File

@@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TaskNode, TaskStatus, TaskExecutionMode } from 'sql/platform/tasks/common/tasksNode';
import { CancelAction, ScriptAction } from 'sql/workbench/contrib/tasks/common/tasksAction';
/**
* Provides actions for the history tasks
*/
export class TaskHistoryActionProvider extends ContributableActionProvider {
constructor(
@IInstantiationService private _instantiationService: IInstantiationService
) {
super();
}
public hasActions(tree: ITree, element: any): boolean {
return element instanceof TaskNode;
}
/**
* Return actions given an element in the tree
*/
public getActions(tree: ITree, element: any): IAction[] {
if (element instanceof TaskNode) {
return this.getTaskHistoryActions(tree, element);
}
return [];
}
public hasSecondaryActions(tree: ITree, element: any): boolean {
return false;
}
/**
* Return actions for history task
*/
public getTaskHistoryActions(tree: ITree, element: TaskNode): IAction[] {
let actions = [];
// get actions for tasks in progress
if (element.status === TaskStatus.InProgress && element.isCancelable) {
actions.push(this._instantiationService.createInstance(CancelAction, CancelAction.ID, CancelAction.LABEL));
}
// get actions for tasks succeeded
if (element.status === TaskStatus.Succeeded || element.status === TaskStatus.SucceededWithWarning) {
if (element.taskExecutionMode === TaskExecutionMode.executeAndScript) {
actions.push(this._instantiationService.createInstance(ScriptAction, ScriptAction.ID, ScriptAction.LABEL));
}
}
return actions;
}
}

View File

@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* 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 { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { TogglePanelAction } from 'vs/workbench/browser/panel';
import { TASKS_PANEL_ID } from 'sql/workbench/contrib/tasks/common/tasks';
export class ToggleTasksAction extends TogglePanelAction {
public static readonly ID = 'workbench.action.tasks.toggleTasks';
public static readonly LABEL = localize('toggleTasks', "Toggle Tasks");
constructor(
id: string, label: string,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IPanelService panelService: IPanelService,
) {
super(id, label, TASKS_PANEL_ID, panelService, layoutService);
}
}

View File

@@ -0,0 +1,80 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITree, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree';
import * as treedefaults from 'vs/base/parts/tree/browser/treeDefaults';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { TaskHistoryActionProvider } from 'sql/workbench/contrib/tasks/browser/tasksActionProvider';
/**
* Extends the tree controller to handle clicks on the tree elements
*/
export class TaskHistoryController extends treedefaults.DefaultController {
constructor(
private actionProvider: TaskHistoryActionProvider,
@IContextMenuService private contextMenuService: IContextMenuService,
@IKeybindingService private keybindingService: IKeybindingService
) {
super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN });
}
public onClick(tree: ITree, element: any, event: IMouseEvent): boolean {
return super.onClick(tree, element, event);
}
protected onLeftClick(tree: ITree, element: any, event: IMouseEvent, origin: string = 'mouse'): boolean {
return super.onLeftClick(tree, element, event, origin);
}
// Do not allow left / right to expand and collapse groups #7848
protected onLeft(tree: ITree, event: IKeyboardEvent): boolean {
return true;
}
protected onRight(tree: ITree, event: IKeyboardEvent): boolean {
return true;
}
protected onEnter(tree: ITree, event: IKeyboardEvent): boolean {
return super.onEnter(tree, event);
}
/**
* Return actions in the context menu
*/
public onContextMenu(tree: ITree, element: any, event: ContextMenuEvent): boolean {
if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') {
return false;
}
// Check if clicked on some element
if (element === tree.getInput()) {
return false;
}
event.preventDefault();
event.stopPropagation();
tree.setFocus(element);
let anchor = { x: event.posx + 1, y: event.posy };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => this.actionProvider.getActions(tree, element),
getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id),
onHide: (wasCancelled?: boolean) => {
if (wasCancelled) {
tree.domFocus();
}
},
getActionsContext: () => (element)
});
return true;
}
}

View File

@@ -0,0 +1,52 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree';
import { TaskNode } from 'sql/platform/tasks/common/tasksNode';
/**
* Implements the DataSource(that returns a parent/children of an element) for the task history
*/
export class TaskHistoryDataSource implements IDataSource {
/**
* Returns the unique identifier of the given element.
* No more than one element may use a given identifier.
*/
public getId(tree: ITree, element: any): string {
if (element instanceof TaskNode) {
return (<TaskNode>element).id;
} else {
return undefined;
}
}
/**
* Returns a boolean value indicating whether the element has children.
*/
public hasChildren(tree: ITree, element: any): boolean {
if (element instanceof TaskNode) {
return (<TaskNode>element).hasChildren;
}
return false;
}
/**
* Returns the element's children as an array in a promise.
*/
public getChildren(tree: ITree, element: any): Promise<any> {
if (element instanceof TaskNode) {
return Promise.resolve((<TaskNode>element).children);
}
return Promise.resolve(null);
}
/**
* Returns the element's parent in a promise.
*/
public getParent(tree: ITree, element: any): Promise<any> {
return Promise.resolve(null);
}
}

View File

@@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* 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/tasksPanel';
import { Dimension } from 'vs/base/browser/dom';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TaskHistoryView } from 'sql/workbench/contrib/tasks/browser/tasksView';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Panel } from 'vs/workbench/browser/panel';
import { TASKS_PANEL_ID } from 'sql/workbench/contrib/tasks/common/tasks';
export class TasksPanel extends Panel {
private _taskHistoryView: TaskHistoryView;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super(TASKS_PANEL_ID, telemetryService, themeService, storageService);
}
public create(parent: HTMLElement): void {
super.create(parent);
this._taskHistoryView = this.instantiationService.createInstance(TaskHistoryView);
this._taskHistoryView.renderBody(parent);
}
public setVisible(visible: boolean): void {
super.setVisible(visible);
this._taskHistoryView.setVisible(visible);
}
public layout({ height }: Dimension): void {
this._taskHistoryView.layout(height);
}
}

View File

@@ -0,0 +1,147 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITree, IRenderer } from 'vs/base/parts/tree/browser/tree';
import { TaskNode, TaskStatus } from 'sql/platform/tasks/common/tasksNode';
import * as dom from 'vs/base/browser/dom';
import { localize } from 'vs/nls';
import * as Utils from 'sql/platform/connection/common/utils';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IntervalTimer } from 'vs/base/common/async';
const $ = dom.$;
export interface ITaskHistoryTemplateData {
root: HTMLElement;
icon: HTMLElement;
label: HTMLSpanElement;
description: HTMLSpanElement;
time: HTMLSpanElement;
disposables: Array<IDisposable>;
}
/**
* Renders the tree items.
* Uses the dom template to render task history.
*/
export class TaskHistoryRenderer implements IRenderer {
public static readonly TASKOBJECT_HEIGHT = 22;
private static readonly FAIL_CLASS = 'error';
private static readonly SUCCESS_CLASS = 'success';
private static readonly INPROGRESS_CLASS = 'in-progress';
private static readonly NOTSTARTED_CLASS = 'not-started';
private static readonly CANCELED_CLASS = 'canceled';
/**
* Returns the element's height in the tree, in pixels.
*/
public getHeight(tree: ITree, element: TaskNode): number {
return TaskHistoryRenderer.TASKOBJECT_HEIGHT;
}
/**
* Returns a template ID for a given element.
*/
public getTemplateId(tree: ITree, element: TaskNode): string {
return element.id;
}
/**
* Render template in a dom element based on template id
*/
public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
const taskTemplate: ITaskHistoryTemplateData = Object.create(null);
taskTemplate.root = dom.append(container, $('.task-group'));
taskTemplate.icon = dom.append(taskTemplate.root, $('.codicon.task-icon'));
taskTemplate.label = dom.append(taskTemplate.root, $('.label'));
taskTemplate.description = dom.append(taskTemplate.root, $('.description'));
taskTemplate.time = dom.append(taskTemplate.root, $('.time'));
taskTemplate.disposables = [];
return taskTemplate;
}
/**
* Render a element, given an object bag returned by the template
*/
public renderElement(tree: ITree, element: TaskNode, templateId: string, templateData: ITaskHistoryTemplateData): void {
let taskStatus;
if (element) {
templateData.icon.className = 'task-icon';
switch (element.status) {
case TaskStatus.Succeeded:
dom.addClass(templateData.icon, TaskHistoryRenderer.SUCCESS_CLASS);
taskStatus = localize('succeeded', "succeeded");
break;
case TaskStatus.Failed:
dom.addClass(templateData.icon, TaskHistoryRenderer.FAIL_CLASS);
taskStatus = localize('failed', "failed");
break;
case TaskStatus.InProgress:
dom.addClass(templateData.icon, TaskHistoryRenderer.INPROGRESS_CLASS);
taskStatus = localize('inProgress', "in progress");
break;
case TaskStatus.NotStarted:
dom.addClass(templateData.icon, TaskHistoryRenderer.NOTSTARTED_CLASS);
taskStatus = localize('notStarted', "not started");
break;
case TaskStatus.Canceled:
dom.addClass(templateData.icon, TaskHistoryRenderer.CANCELED_CLASS);
taskStatus = localize('canceled', "canceled");
break;
case TaskStatus.Canceling:
dom.addClass(templateData.icon, TaskHistoryRenderer.INPROGRESS_CLASS);
taskStatus = localize('canceling', "canceling");
break;
}
// Set hover text for icon to same as task status
templateData.icon.title = taskStatus;
// Determine the task title and set hover text equal to that
templateData.label.textContent = element.taskName + ' ' + taskStatus;
templateData.label.title = templateData.label.textContent;
// Determine the target name and set hover text equal to that
let description = element.serverName;
if (element.databaseName) {
description += ' | ' + element.databaseName;
}
templateData.description.textContent = description;
templateData.description.title = templateData.description.textContent;
this.timer(element, templateData.time);
const timer = new IntervalTimer();
timer.cancelAndSet(() => this.timer(element, templateData.time), 1000);
templateData.disposables.push(timer);
}
}
private timer(taskNode: TaskNode, element: HTMLElement): void {
let timeLabel = '';
if (taskNode.status === TaskStatus.Failed) {
timeLabel += taskNode.startTime + ' Error: ' + taskNode.message;
} else {
if (taskNode.startTime) {
timeLabel = taskNode.startTime;
}
if (taskNode.endTime) {
timeLabel += ' - ' + taskNode.endTime;
}
if (taskNode.timer) {
// Round task duration to seconds and then convert back to milliseconds
let duration = Math.floor(taskNode.timer.elapsed() / 1000) * 1000;
timeLabel += ' (' + Utils.parseNumAsTimeString(duration) + ')';
}
}
element.textContent = timeLabel;
element.title = timeLabel;
}
public disposeTemplate(tree: ITree, templateId: string, templateData: ITaskHistoryTemplateData): void {
dispose(templateData.disposables);
}
}

View File

@@ -0,0 +1,169 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as errors from 'vs/base/common/errors';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import Severity from 'vs/base/common/severity';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { Disposable } from 'vs/base/common/lifecycle';
import { DefaultFilter, DefaultDragAndDrop, DefaultAccessibilityProvider } from 'vs/base/parts/tree/browser/treeDefaults';
import { localize } from 'vs/nls';
import { hide, $, append } from 'vs/base/browser/dom';
import { TaskHistoryRenderer } from 'sql/workbench/contrib/tasks/browser/tasksRenderer';
import { TaskHistoryDataSource } from 'sql/workbench/contrib/tasks/browser/tasksDataSource';
import { TaskHistoryController } from 'sql/workbench/contrib/tasks/browser/tasksController';
import { TaskHistoryActionProvider } from 'sql/workbench/contrib/tasks/browser/tasksActionProvider';
import { ITaskService } from 'sql/platform/tasks/common/tasksService';
import { TaskNode, TaskStatus } from 'sql/platform/tasks/common/tasksNode';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
import { IExpandableTree } from 'sql/workbench/contrib/objectExplorer/browser/treeUpdateUtils';
/**
* TaskHistoryView implements the dynamic tree view.
*/
export class TaskHistoryView extends Disposable {
private _messages: HTMLElement;
private _tree: ITree;
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@ITaskService private _taskService: ITaskService,
@IErrorMessageService private _errorMessageService: IErrorMessageService,
@IThemeService private _themeService: IThemeService
) {
super();
}
/**
* Render the view body
*/
public renderBody(container: HTMLElement): void {
let taskNode = this._taskService.getAllTasks();
// Add div to display no task executed message
this._messages = append(container, $('div.empty-task-message'));
if (taskNode && taskNode.hasChildren) {
hide(this._messages);
}
let noTaskMessage = localize('noTaskMessage', "No task history to display.");
append(this._messages, $('span')).innerText = noTaskMessage;
this._tree = this._register(this.createTaskHistoryTree(container, this._instantiationService));
this._register(this._tree.onDidChangeSelection((event) => this.onSelected(event)));
// Theme styler
this._register(attachListStyler(this._tree, this._themeService));
this._register(this._taskService.onAddNewTask(args => {
hide(this._messages);
this.refreshTree();
}));
this._register(this._taskService.onTaskComplete(task => {
this.updateTask(task);
}));
// Refresh Tree when these events are emitted
this.refreshTree();
}
/**
* Create a task history tree
*/
public createTaskHistoryTree(treeContainer: HTMLElement, instantiationService: IInstantiationService): Tree {
const dataSource = instantiationService.createInstance(TaskHistoryDataSource);
const actionProvider = instantiationService.createInstance(TaskHistoryActionProvider);
const renderer = instantiationService.createInstance(TaskHistoryRenderer);
const controller = instantiationService.createInstance(TaskHistoryController, actionProvider);
const dnd = new DefaultDragAndDrop();
const filter = new DefaultFilter();
const sorter = null;
const accessibilityProvider = new DefaultAccessibilityProvider();
return new Tree(treeContainer, {
dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider
}, {
indentPixels: 10,
twistiePixels: 20,
ariaLabel: localize({ key: 'taskHistory.regTreeAriaLabel', comment: ['TaskHistory'] }, "Task history")
});
}
private updateTask(task: TaskNode): void {
this._tree.refresh(task);
}
public refreshTree(): void {
let selectedElement: any;
let targetsToExpand: any[];
// Focus
this._tree.domFocus();
if (this._tree) {
let selection = this._tree.getSelection();
if (selection && selection.length === 1) {
selectedElement = <any>selection[0];
}
// convert to old VS Code tree interface with expandable methods
let expandableTree: IExpandableTree = <IExpandableTree>this._tree;
targetsToExpand = expandableTree.getExpandedElements();
}
//Get the tree Input
let treeInput = this._taskService.getAllTasks();
if (treeInput) {
this._tree.setInput(treeInput).then(async () => {
// Make sure to expand all folders that where expanded in the previous session
if (targetsToExpand) {
await this._tree.expandAll(targetsToExpand);
}
if (selectedElement) {
this._tree.select(selectedElement);
}
this._tree.getFocus();
}, errors.onUnexpectedError);
}
}
private onSelected(event: any) {
let selection = this._tree.getSelection();
if (selection && selection.length > 0 && (selection[0] instanceof TaskNode)) {
let task = <TaskNode>selection[0];
let isMouseOrigin = event.payload && (event.payload.origin === 'mouse');
let isDoubleClick = isMouseOrigin && event.payload.originalEvent && event.payload.originalEvent.detail === 2;
if (isDoubleClick) {
if (task.status === TaskStatus.Failed) {
let err = task.taskName + ': ' + task.message;
this._errorMessageService.showDialog(Severity.Error, localize('taskError', "Task error"), err);
}
}
}
}
/**
* set the layout of the view
*/
public layout(height: number): void {
this._tree.layout(height);
}
/**
* set the visibility of the view
*/
public setVisible(visible: boolean): void {
if (visible) {
this._tree.onVisible();
} else {
this._tree.onHidden();
}
}
}