Better table implementation (#11781)
* wip * wip * weird splitview scrolling stuff * working table * remove spliceable table * handling resizing columns * get perf table integrated into grid * make more improvments to table view * testing * wip * wip * fix async data window; add more optimization to scrolling * work on scrolling * fix column resizing * start working on table widget * inital work to get table widget working with styles and mouse controls * fix unrendering selection; fix sizes of cells * support high perf table option; remove unused files; add cell borders to high perf * add accessibility tags * handle borders and row count * more styling changfes * fix strict null checks * adding inital keyboard navigation * center row count; add padding left to rows * inital drag selection * remove drag implementation; it can be done better utilizing the global mouse monitor object * range logic * create custom grid range * work with new range * remove unused code * fix how plus range works * add drag selection; change focus to set selection; fix problem with creating a range with inverse start and end * code cleanup * fix strict-null-checks * fix up perf table * fix layering * inital table service * finish table service * fix some compile errors * fix compile * fix compile * fix up for use * fix layering * remove console use * fix strict nulls
@@ -21,11 +21,13 @@ export interface IScrollableViewOptions {
|
||||
smoothScrolling?: boolean;
|
||||
verticalScrollMode?: ScrollbarVisibility;
|
||||
additionalScrollHeight?: number;
|
||||
scrollDebouce?: number;
|
||||
}
|
||||
|
||||
const DefaultOptions: IScrollableViewOptions = {
|
||||
useShadows: true,
|
||||
verticalScrollMode: ScrollbarVisibility.Auto
|
||||
verticalScrollMode: ScrollbarVisibility.Auto,
|
||||
scrollDebouce: 25
|
||||
};
|
||||
|
||||
export interface IView {
|
||||
@@ -83,7 +85,7 @@ export class ScrollableView extends Disposable {
|
||||
this.domNode.appendChild(this.scrollableElement.getDomNode());
|
||||
container.appendChild(this.domNode);
|
||||
|
||||
this._register(Event.debounce(this.scrollableElement.onScroll, (l, e) => e, 25)(this.onScroll, this));
|
||||
this._register(Event.debounce(this.scrollableElement.onScroll, (l, e) => e, getOrDefault(options, o => o.scrollDebouce, DefaultOptions.scrollDebouce))(this.onScroll, this));
|
||||
|
||||
// Prevent the monaco-scrollable-element from scrolling
|
||||
// https://github.com/Microsoft/vscode/issues/44181
|
||||
|
||||
97
src/sql/base/browser/ui/table/highPerf/cellCache.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ITableRenderer } from 'sql/base/browser/ui/table/highPerf/table';
|
||||
|
||||
import { $, removeClass } from 'vs/base/browser/dom';
|
||||
export interface ICell {
|
||||
domNode: HTMLElement | null;
|
||||
templateData: any;
|
||||
templateId: string;
|
||||
}
|
||||
|
||||
function removeFromParent(element: HTMLElement): void {
|
||||
try {
|
||||
if (element.parentElement) {
|
||||
element.parentElement.removeChild(element);
|
||||
}
|
||||
} catch (e) {
|
||||
// this will throw if this happens due to a blur event, nasty business
|
||||
}
|
||||
}
|
||||
|
||||
export class CellCache<T> implements IDisposable {
|
||||
|
||||
private cache = new Map<string, ICell[]>();
|
||||
|
||||
constructor(private renderers: Map<string, ITableRenderer<T, any>>) { }
|
||||
|
||||
alloc(templateId: string): ICell {
|
||||
let result = this.getTemplateCache(templateId).pop();
|
||||
|
||||
if (!result) {
|
||||
const domNode = $('.monaco-perftable-cell');
|
||||
const renderer = this.getRenderer(templateId);
|
||||
const templateData = renderer.renderTemplate(domNode);
|
||||
result = { domNode, templateId, templateData };
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private getTemplateCache(templateId: string): ICell[] {
|
||||
let result = this.cache.get(templateId);
|
||||
|
||||
if (!result) {
|
||||
result = [];
|
||||
this.cache.set(templateId, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private getRenderer(templateId: string): ITableRenderer<T, any> {
|
||||
const renderer = this.renderers.get(templateId);
|
||||
if (!renderer) {
|
||||
throw new Error(`No renderer found for ${templateId}`);
|
||||
}
|
||||
return renderer;
|
||||
}
|
||||
|
||||
release(cell: ICell) {
|
||||
const { domNode, templateId } = cell;
|
||||
if (domNode) {
|
||||
removeClass(domNode, 'scrolling');
|
||||
removeFromParent(domNode);
|
||||
}
|
||||
|
||||
const cache = this.getTemplateCache(templateId);
|
||||
cache.push(cell);
|
||||
}
|
||||
|
||||
private garbageCollect(): void {
|
||||
if (!this.renderers) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cache.forEach((cachedRows, templateId) => {
|
||||
for (const cachedRow of cachedRows) {
|
||||
const renderer = this.getRenderer(templateId);
|
||||
renderer.disposeTemplate(cachedRow.templateData);
|
||||
cachedRow.domNode = null;
|
||||
cachedRow.templateData = null;
|
||||
}
|
||||
});
|
||||
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.garbageCollect();
|
||||
this.cache.clear();
|
||||
this.renderers = null!; // StrictNullOverride: nulling out ok in dispose
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,15 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.vs-dark .side-by-side-editor > .master-editor-container {
|
||||
box-shadow: -6px 0 5px -5px black;
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
|
||||
export class RowCache implements IDisposable {
|
||||
dispose(): void {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.side-by-side-editor > .master-editor-container {
|
||||
box-shadow: -6px 0 5px -5px #DDD;
|
||||
}
|
||||
export interface IRow {
|
||||
domNode: HTMLElement | null;
|
||||
}
|
||||
106
src/sql/base/browser/ui/table/highPerf/table.css
Normal file
@@ -0,0 +1,106 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-perftable {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-perftable.mouse-support {
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: -moz-none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.monaco-perftable > .monaco-scrollable-element {
|
||||
height: calc(100% - 22px);
|
||||
}
|
||||
|
||||
.monaco-perftable .monaco-perftable-cell {
|
||||
display: inline-block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-perftable-rows {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-perftable.horizontal-scrolling .monaco-perftable-rows {
|
||||
width: auto;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.monaco-perftable-row {
|
||||
position: absolute;
|
||||
-moz-box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.monaco-perftable.mouse-support .monaco-perftable-row {
|
||||
cursor: pointer;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
/* for OS X balperftableic scrolling */
|
||||
.monaco-perftable-row.scrolling {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Focus */
|
||||
.monaco-perftable.element-focused, .monaco-perftable.selection-single, .monaco-perftable.selection-multiple {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
.monaco-perftable-header {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-perftable-header-cell {
|
||||
display: inline-block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
padding-left: 3px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-perftable .sash-container {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.monaco-perftable .sash-container > .monaco-sash {
|
||||
pointer-events: initial;
|
||||
}
|
||||
|
||||
.monaco-perftable .monaco-perftable-cell * {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
.monaco-perftable .monaco-perftable-cell .row-count {
|
||||
text-align: center;
|
||||
padding-left: 0;
|
||||
}
|
||||
96
src/sql/base/browser/ui/table/highPerf/table.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IGridRange } from 'sql/base/common/gridRange';
|
||||
import { IGridPosition } from 'sql/base/common/gridPosition';
|
||||
|
||||
export interface ITableRenderer<T, TTemplateData> {
|
||||
renderTemplate(container: HTMLElement): TTemplateData;
|
||||
renderCell(element: T, index: number, cell: number, columnId: string, templateData: TTemplateData, width: number | undefined): void;
|
||||
disposeCell?(element: T, index: number, cell: number, olumnId: string, templateData: TTemplateData, width: number | undefined): void;
|
||||
disposeTemplate(templateData: TTemplateData): void;
|
||||
}
|
||||
|
||||
export interface IStaticTableRenderer<T, TTemplateData> extends ITableRenderer<T, TTemplateData> {
|
||||
renderCell(element: T | undefined, index: number, cell: number, columnId: string, templateData: TTemplateData, width: number | undefined): void;
|
||||
disposeCell?(element: T | undefined, index: number, cell: number, columnId: string, templateData: TTemplateData, width: number | undefined): void;
|
||||
}
|
||||
|
||||
export class TableError extends Error {
|
||||
|
||||
constructor(user: string, message: string) {
|
||||
super(`TableError [${user}] ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITableDataSource<T> {
|
||||
getRow(index: number): Promise<T>;
|
||||
}
|
||||
|
||||
export interface ITableEvent<T> {
|
||||
elements: T[];
|
||||
indexes: IGridRange[];
|
||||
browserEvent?: UIEvent;
|
||||
}
|
||||
|
||||
export interface ITableMouseEvent<T> {
|
||||
browserEvent: MouseEvent;
|
||||
buttons: number;
|
||||
element: T | undefined;
|
||||
index: IGridPosition | undefined;
|
||||
}
|
||||
|
||||
export interface ITableContextMenuEvent<T> {
|
||||
browserEvent: UIEvent;
|
||||
element: T | undefined;
|
||||
index: IGridPosition | undefined;
|
||||
anchor: HTMLElement | { x: number; y: number; };
|
||||
}
|
||||
|
||||
export interface ITableDragEvent {
|
||||
start: IGridPosition;
|
||||
current: IGridPosition;
|
||||
}
|
||||
|
||||
export interface ITableColumn<T, TTemplateData> {
|
||||
/**
|
||||
* Renderer associated with this column
|
||||
*/
|
||||
renderer: ITableRenderer<T, TTemplateData> | IStaticTableRenderer<T, TTemplateData>;
|
||||
/**
|
||||
* Initial width of this column
|
||||
*/
|
||||
width?: number;
|
||||
/**
|
||||
* Minimum allowed width of this column
|
||||
*/
|
||||
minWidth?: number;
|
||||
/**
|
||||
* Is this column resizable?
|
||||
*/
|
||||
resizeable?: boolean;
|
||||
/**
|
||||
* This string will be added to the cell as a class
|
||||
* Useful for styling specific columns
|
||||
*/
|
||||
cellClass?: string;
|
||||
/**
|
||||
* Specifies this column doesn't need data to render
|
||||
* Useful when you don't need to wait for data you render a column
|
||||
*/
|
||||
static?: boolean;
|
||||
id: string;
|
||||
/**
|
||||
* Name to display in the column header
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface IStaticColumn<T, TTemplateData> extends ITableColumn<T, TTemplateData> {
|
||||
/**
|
||||
* Renderer associated with this column
|
||||
*/
|
||||
renderer: IStaticTableRenderer<T, TTemplateData>;
|
||||
}
|
||||
727
src/sql/base/browser/ui/table/highPerf/tableView.ts
Normal file
@@ -0,0 +1,727 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./table';
|
||||
|
||||
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { Range, IRange } from 'vs/base/common/range';
|
||||
import { getOrDefault } from 'vs/base/common/objects';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { Sash, Orientation, ISashEvent as IBaseSashEvent } from 'vs/base/browser/ui/sash/sash';
|
||||
import { firstIndex } from 'vs/base/common/arrays';
|
||||
|
||||
import { CellCache, ICell } from 'sql/base/browser/ui/table/highPerf/cellCache';
|
||||
import { ITableRenderer, ITableDataSource, ITableMouseEvent, IStaticTableRenderer, ITableColumn } from 'sql/base/browser/ui/table/highPerf/table';
|
||||
import { GridPosition } from 'sql/base/common/gridPosition';
|
||||
|
||||
export interface IAriaSetProvider<T> {
|
||||
getSetSize(element: T, index: number, listLength: number): number;
|
||||
getPosInSet(element: T, index: number): number;
|
||||
}
|
||||
|
||||
export interface ITableViewOptions<T> {
|
||||
rowHeight?: number;
|
||||
mouseSupport?: boolean;
|
||||
initialLength?: number;
|
||||
rowCountColumn?: boolean;
|
||||
headerHeight?: number;
|
||||
}
|
||||
|
||||
const DefaultOptions = {
|
||||
rowHeight: 22,
|
||||
columnWidth: 120,
|
||||
minWidth: 20,
|
||||
resizeable: true,
|
||||
headerHeight: 22
|
||||
};
|
||||
|
||||
interface IInternalColumn<T, TTemplateData> extends ITableColumn<T, TTemplateData> {
|
||||
domNode?: HTMLElement;
|
||||
left?: number;
|
||||
}
|
||||
|
||||
interface IInternalStaticColumn<T, TTemplateData> extends IInternalColumn<T, TTemplateData> {
|
||||
renderer: IStaticTableRenderer<T, TTemplateData>;
|
||||
}
|
||||
|
||||
interface ISashItem {
|
||||
sash: Sash;
|
||||
disposable: IDisposable;
|
||||
}
|
||||
|
||||
interface ISashDragState {
|
||||
current: number;
|
||||
index: number;
|
||||
start: number;
|
||||
sizes: Array<number>;
|
||||
lefts: Array<number>;
|
||||
}
|
||||
|
||||
interface ISashEvent<T> {
|
||||
sash: Sash;
|
||||
column: IInternalColumn<T, any>;
|
||||
start: number;
|
||||
current: number;
|
||||
}
|
||||
|
||||
function removeFromParent(element: HTMLElement): void {
|
||||
try {
|
||||
if (element.parentElement) {
|
||||
element.parentElement.removeChild(element);
|
||||
}
|
||||
} catch (e) {
|
||||
// this will throw if this happens due to a blur event, nasty business
|
||||
}
|
||||
}
|
||||
|
||||
interface IAsyncRowItem<T> {
|
||||
readonly id: string;
|
||||
element: T | undefined;
|
||||
row: HTMLElement | null;
|
||||
cells: ICell[] | null;
|
||||
size: number;
|
||||
datapromise: CancelablePromise<void> | null;
|
||||
}
|
||||
|
||||
export class TableView<T> implements IDisposable {
|
||||
private static InstanceCount = 0;
|
||||
readonly domId = `table_id_${++TableView.InstanceCount}`;
|
||||
|
||||
readonly domNode = DOM.$('.monaco-perftable');
|
||||
|
||||
private visibleRows: IAsyncRowItem<T>[] = [];
|
||||
private cache: CellCache<T>;
|
||||
private renderers = new Map<string, ITableRenderer<T /* TODO@joao */, any>>();
|
||||
private lastRenderTop = 0;
|
||||
private lastRenderHeight = 0;
|
||||
private readonly rowsContainer = DOM.$('.monaco-perftable-rows');
|
||||
private scrollableElement: ScrollableElement;
|
||||
private _scrollHeight: number = 0;
|
||||
private _scrollWidth: number = 0;
|
||||
private scrollableElementUpdateDisposable: IDisposable | null = null;
|
||||
// private ariaSetProvider: IAriaSetProvider<T>;
|
||||
private canUseTranslate3d: boolean | undefined = undefined;
|
||||
public readonly rowHeight: number;
|
||||
private _length: number = 0;
|
||||
|
||||
private columns: IInternalColumn<T, any>[];
|
||||
private staticColumns: IInternalStaticColumn<T, any>[];
|
||||
private columnSashs: ISashItem[] = [];
|
||||
private sashDragState?: ISashDragState;
|
||||
private headerContainer!: HTMLElement;
|
||||
|
||||
private scheduledRender?: IDisposable;
|
||||
private bigNumberDelta = 0;
|
||||
|
||||
private headerHeight: number;
|
||||
|
||||
private disposables: IDisposable[];
|
||||
|
||||
get contentHeight(): number { return this.length * this.rowHeight; }
|
||||
|
||||
get onDidScroll(): Event<ScrollEvent> { return this.scrollableElement.onScroll; }
|
||||
|
||||
private _orthogonalStartSash: Sash | undefined;
|
||||
get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; }
|
||||
set orthogonalStartSash(sash: Sash | undefined) {
|
||||
for (const sashItem of this.columnSashs) {
|
||||
sashItem.sash.orthogonalStartSash = sash;
|
||||
}
|
||||
|
||||
this._orthogonalStartSash = sash;
|
||||
}
|
||||
|
||||
private _orthogonalEndSash: Sash | undefined;
|
||||
get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; }
|
||||
set orthogonalEndSash(sash: Sash | undefined) {
|
||||
for (const sashItem of this.columnSashs) {
|
||||
sashItem.sash.orthogonalEndSash = sash;
|
||||
}
|
||||
|
||||
this._orthogonalEndSash = sash;
|
||||
}
|
||||
|
||||
get sashes(): Sash[] {
|
||||
return this.columnSashs.map(s => s.sash);
|
||||
}
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
columns: ITableColumn<T, any>[],
|
||||
private readonly dataSource: ITableDataSource<T>,
|
||||
options: ITableViewOptions<T> = DefaultOptions as ITableViewOptions<T>,
|
||||
) {
|
||||
for (const column of columns) {
|
||||
this.renderers.set(column.id, column.renderer);
|
||||
}
|
||||
|
||||
this.columns = columns.slice();
|
||||
this.staticColumns = this.columns.filter(c => c.static);
|
||||
|
||||
this.cache = new CellCache(this.renderers);
|
||||
|
||||
this.domNode.setAttribute('role', 'grid');
|
||||
this.domNode.setAttribute('aria-rowcount', '0');
|
||||
this.domNode.setAttribute('aria-readonly', 'true');
|
||||
|
||||
DOM.addClass(this.domNode, this.domId);
|
||||
this.domNode.tabIndex = 0;
|
||||
|
||||
DOM.toggleClass(this.domNode, 'mouse-support', typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true);
|
||||
|
||||
// this.ariaSetProvider = { getSetSize: (e, i, length) => length, getPosInSet: (_, index) => index + 1 };
|
||||
|
||||
this.rowHeight = getOrDefault(options, o => o.rowHeight, DefaultOptions.rowHeight);
|
||||
this.headerHeight = getOrDefault(options, o => o.headerHeight, DefaultOptions.headerHeight);
|
||||
|
||||
let left = 0;
|
||||
this.columns = this.columns.map(c => {
|
||||
c.width = getOrDefault(c, o => o.width, DefaultOptions.columnWidth);
|
||||
c.minWidth = getOrDefault(c, o => o.minWidth, DefaultOptions.minWidth);
|
||||
c.resizeable = getOrDefault(c, c => c.resizeable, DefaultOptions.resizeable);
|
||||
c.left = left;
|
||||
left += c.width;
|
||||
return c;
|
||||
});
|
||||
|
||||
this.rowsContainer.setAttribute('role', 'rowgroup');
|
||||
|
||||
this.scrollableElement = new ScrollableElement(this.rowsContainer, {
|
||||
horizontal: ScrollbarVisibility.Auto,
|
||||
vertical: ScrollbarVisibility.Auto,
|
||||
useShadows: true
|
||||
});
|
||||
|
||||
this.renderHeader(this.domNode);
|
||||
|
||||
this.domNode.appendChild(this.scrollableElement.getDomNode());
|
||||
container.appendChild(this.domNode);
|
||||
|
||||
this.disposables = [/*this.gesture,*/ this.scrollableElement, this.cache];
|
||||
|
||||
this.scrollableElement.onScroll(this.onScroll, this, this.disposables);
|
||||
|
||||
// Prevent the monaco-scrollable-element from scrolling
|
||||
// https://github.com/Microsoft/vscode/issues/44181
|
||||
domEvent(this.scrollableElement.getDomNode(), 'scroll')
|
||||
(e => (e.target as HTMLElement).scrollTop = 0, null, this.disposables);
|
||||
|
||||
this.updateScrollWidth();
|
||||
this.layout();
|
||||
}
|
||||
|
||||
private renderHeader(container: HTMLElement): void {
|
||||
this.headerContainer = DOM.append(container, DOM.$('.monaco-perftable-header'));
|
||||
const sashContainer = DOM.append(this.headerContainer, DOM.$('.sash-container'));
|
||||
this.headerContainer.style.height = this.headerHeight + 'px';
|
||||
this.headerContainer.style.lineHeight = this.headerHeight + 'px';
|
||||
this.headerContainer.setAttribute('role', 'rowgroup');
|
||||
const headerCellContainer = DOM.append(this.headerContainer, DOM.$('.monaco-perftable-header-cell-container'));
|
||||
headerCellContainer.setAttribute('role', 'row');
|
||||
|
||||
for (const column of this.columns) {
|
||||
this.createHeaderSash(sashContainer, column);
|
||||
const domNode = DOM.append(headerCellContainer, DOM.$('.monaco-perftable-header-cell'));
|
||||
domNode.setAttribute('role', 'columnheader');
|
||||
domNode.style.width = column.width + 'px';
|
||||
domNode.style.left = column.left + 'px';
|
||||
domNode.innerText = column.name;
|
||||
column.domNode = domNode;
|
||||
}
|
||||
}
|
||||
|
||||
private createHeaderSash(sashContainer: HTMLElement, column: ITableColumn<T, any>): void {
|
||||
const layoutProvider = {
|
||||
getVerticalSashLeft: (sash: Sash) => {
|
||||
let left = 0;
|
||||
for (const c of this.columns) {
|
||||
left += c.width!;
|
||||
if (column === c) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
};
|
||||
const sash = new Sash(sashContainer, layoutProvider, {
|
||||
orientation: Orientation.VERTICAL,
|
||||
orthogonalStartSash: this.orthogonalStartSash,
|
||||
orthogonalEndSash: this.orthogonalEndSash
|
||||
});
|
||||
|
||||
const sashEventMapper = (e: IBaseSashEvent) => ({ sash, column, start: e.startX, current: e.currentX });
|
||||
|
||||
const onStart = Event.map(sash.onDidStart, sashEventMapper);
|
||||
const onStartDisposable = onStart(this.onSashStart, this);
|
||||
const onChange = Event.map(sash.onDidChange, sashEventMapper);
|
||||
const onChangeDisposable = onChange(this.onSashChange, this);
|
||||
// const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.columnSashs, item => item.sash === sash));
|
||||
// const onEndDisposable = onEnd(this.onSashEnd, this);
|
||||
// const onDidReset = Event.map(sash.onDidEnd, () => firstIndex(this.columnSashs, item => item.sash === sash));
|
||||
// const onDidResetDisposable = onDidReset(this.onDidSashReset, this);
|
||||
|
||||
const disposable = combinedDisposable(onStartDisposable, onChangeDisposable, /*onEndDisposable, onDidResetDisposable, */sash);
|
||||
const sashItem: ISashItem = { sash, disposable };
|
||||
this.columnSashs.push(sashItem);
|
||||
if (!column.resizeable) {
|
||||
sash.hide();
|
||||
}
|
||||
}
|
||||
|
||||
private onSashStart({ sash, start }: ISashEvent<T>): void {
|
||||
const index = firstIndex(this.columnSashs, item => item.sash === sash);
|
||||
const sizes = this.columns.map(i => i.width!);
|
||||
const lefts = this.columns.map(i => i.left!);
|
||||
this.sashDragState = { start, current: start, index, sizes, lefts };
|
||||
}
|
||||
|
||||
private onSashChange({ column, current }: ISashEvent<T>): void {
|
||||
const { index, start, sizes, lefts } = this.sashDragState!;
|
||||
this.sashDragState!.current = current;
|
||||
|
||||
const delta = current - start;
|
||||
const adjustedDelta = sizes[index] + delta < column.minWidth! ? column.minWidth! - sizes[index] : delta;
|
||||
|
||||
column.width = sizes[index] + adjustedDelta;
|
||||
column.domNode!.style.width = column.width + 'px';
|
||||
for (let i = index + 1; i < this.columns.length; i++) {
|
||||
const resizeColumn = this.columns[i];
|
||||
resizeColumn.left = lefts[i] + adjustedDelta;
|
||||
resizeColumn.domNode!.style.left = resizeColumn.left + 'px';
|
||||
}
|
||||
for (const [index, row] of this.visibleRows.entries()) {
|
||||
if (row) {
|
||||
this.updateRowInDOM(row, index);
|
||||
}
|
||||
}
|
||||
this.updateScrollWidth();
|
||||
this.layout();
|
||||
}
|
||||
|
||||
private eventuallyUpdateScrollDimensions(): void {
|
||||
this._scrollHeight = this.contentHeight;
|
||||
this.rowsContainer.style.height = `${this._scrollHeight}px`;
|
||||
|
||||
if (!this.scrollableElementUpdateDisposable) {
|
||||
this.scrollableElementUpdateDisposable = DOM.scheduleAtNextAnimationFrame(() => {
|
||||
this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight });
|
||||
this.updateScrollWidth();
|
||||
this.scrollableElementUpdateDisposable = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private updateScrollWidth(): void {
|
||||
this._scrollWidth = this.columns.reduce((p, c) => p += c.width!, 0);
|
||||
this.rowsContainer.style.width = `${this.scrollWidth}px`;
|
||||
this.headerContainer.style.width = `${this.scrollWidth}px`;
|
||||
this.scrollableElement.setScrollDimensions({ scrollWidth: this.scrollWidth });
|
||||
}
|
||||
|
||||
private onScroll(e: ScrollEvent): void {
|
||||
if (this.scheduledRender) {
|
||||
this.scheduledRender.dispose();
|
||||
}
|
||||
this.scheduledRender = DOM.runAtThisOrScheduleAtNextAnimationFrame(() => {
|
||||
this.render(e.scrollTop, e.height, e.scrollLeft, e.scrollWidth);
|
||||
});
|
||||
}
|
||||
|
||||
private getRenderRange(renderTop: number, renderHeight: number): IRange {
|
||||
const start = Math.floor(renderTop / this.rowHeight);
|
||||
const end = Math.ceil((renderTop + renderHeight) / this.rowHeight);
|
||||
return {
|
||||
start,
|
||||
end
|
||||
};
|
||||
}
|
||||
|
||||
private getNextToLastElement(ranges: IRange[]): HTMLElement | null {
|
||||
const lastRange = ranges[ranges.length - 1];
|
||||
|
||||
if (!lastRange) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const nextToLastItem = this.visibleRows[lastRange.end];
|
||||
|
||||
if (!nextToLastItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!nextToLastItem.row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return nextToLastItem.row;
|
||||
}
|
||||
|
||||
private render(renderTop: number, renderHeight: number, renderLeft: number, scrollWidth: number): void {
|
||||
const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight);
|
||||
const renderRange = this.getRenderRange(renderTop, renderHeight);
|
||||
renderRange.end = renderRange.end > this.length ? this.length : renderRange.end;
|
||||
|
||||
// IE (all versions) cannot handle units above about 1,533,908 px, so every 500k pixels bring numbers down
|
||||
const STEP_SIZE = 500000;
|
||||
this.bigNumberDelta = 0;
|
||||
if (renderTop >= STEP_SIZE) {
|
||||
// Compute a delta that guarantees that lines are positioned at `lineHeight` increments
|
||||
this.bigNumberDelta = Math.floor(renderTop / STEP_SIZE) * STEP_SIZE;
|
||||
this.bigNumberDelta = Math.floor(this.bigNumberDelta / this.rowHeight) * this.rowHeight;
|
||||
const rangesToUpdate = Range.intersect(previousRenderRange, renderRange);
|
||||
for (let i = rangesToUpdate.start; i < rangesToUpdate.end; i++) {
|
||||
this.updateRowInDOM(this.visibleRows[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
const rangesToInsert = Range.relativeComplement(renderRange, previousRenderRange);
|
||||
const rangesToRemove = Range.relativeComplement(previousRenderRange, renderRange);
|
||||
const beforeElement = this.getNextToLastElement(rangesToInsert);
|
||||
|
||||
for (const range of rangesToInsert) {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
this.insertRowInDOM(i, beforeElement);
|
||||
}
|
||||
}
|
||||
|
||||
for (const range of rangesToRemove) {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
this.removeRowFromDOM(i);
|
||||
}
|
||||
}
|
||||
|
||||
const canUseTranslate3d = !isWindows && !browser.isFirefox && browser.getZoomLevel() === 0;
|
||||
|
||||
if (canUseTranslate3d) {
|
||||
const transform = `translate3d(-${renderLeft}px, -${renderTop - this.bigNumberDelta}px, 0px)`;
|
||||
this.rowsContainer.style.transform = transform;
|
||||
this.rowsContainer.style.webkitTransform = transform;
|
||||
this.headerContainer.style.transform = `translate3d(-${renderLeft}px, 0px, 0px)`;
|
||||
this.headerContainer.style.webkitTransform = `translate3d(-${renderLeft}px, 0px, 0px)`;
|
||||
|
||||
if (canUseTranslate3d !== this.canUseTranslate3d) {
|
||||
this.rowsContainer.style.left = '0';
|
||||
this.headerContainer.style.left = '0';
|
||||
this.rowsContainer.style.top = '0';
|
||||
}
|
||||
} else {
|
||||
this.rowsContainer.style.left = `-${renderLeft}px`;
|
||||
this.headerContainer.style.left = `-${renderLeft}px`;
|
||||
this.rowsContainer.style.top = `-${renderTop - this.bigNumberDelta}px`;
|
||||
|
||||
if (canUseTranslate3d !== this.canUseTranslate3d) {
|
||||
this.rowsContainer.style.transform = '';
|
||||
this.rowsContainer.style.webkitTransform = '';
|
||||
}
|
||||
}
|
||||
|
||||
this.canUseTranslate3d = canUseTranslate3d;
|
||||
|
||||
this.lastRenderTop = renderTop;
|
||||
this.lastRenderHeight = renderHeight;
|
||||
}
|
||||
|
||||
public layout(height?: number, width?: number): void {
|
||||
const scrollDimensions: INewScrollDimensions = {
|
||||
height: typeof height === 'number' ? height : DOM.getContentHeight(this.domNode)
|
||||
};
|
||||
scrollDimensions.height = scrollDimensions.height! - this.headerHeight;
|
||||
if (this.scrollableElementUpdateDisposable) {
|
||||
this.scrollableElementUpdateDisposable.dispose();
|
||||
this.scrollableElementUpdateDisposable = null;
|
||||
scrollDimensions.scrollHeight = this.scrollHeight;
|
||||
}
|
||||
|
||||
this.scrollableElement.setScrollDimensions(scrollDimensions);
|
||||
|
||||
this.scrollableElement.setScrollDimensions({
|
||||
width: typeof width === 'number' ? width : DOM.getContentWidth(this.domNode)
|
||||
});
|
||||
|
||||
this.columnSashs.forEach(s => s.sash.layout());
|
||||
}
|
||||
|
||||
getScrollTop(): number {
|
||||
const scrollPosition = this.scrollableElement.getScrollPosition();
|
||||
return scrollPosition.scrollTop;
|
||||
}
|
||||
|
||||
getScrollLeft(): number {
|
||||
const scrollPosition = this.scrollableElement.getScrollPosition();
|
||||
return scrollPosition.scrollLeft;
|
||||
}
|
||||
|
||||
setScrollTop(scrollTop: number): void {
|
||||
if (this.scrollableElementUpdateDisposable) {
|
||||
this.scrollableElementUpdateDisposable.dispose();
|
||||
this.scrollableElementUpdateDisposable = null;
|
||||
this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight });
|
||||
}
|
||||
|
||||
this.scrollableElement.setScrollPosition({ scrollTop });
|
||||
}
|
||||
|
||||
get scrollTop(): number {
|
||||
return this.getScrollTop();
|
||||
}
|
||||
|
||||
set scrollTop(scrollTop: number) {
|
||||
this.setScrollTop(scrollTop);
|
||||
}
|
||||
|
||||
get scrollHeight(): number {
|
||||
return this._scrollHeight + 10;
|
||||
}
|
||||
|
||||
get scrollWidth(): number {
|
||||
return this._scrollWidth + 10;
|
||||
}
|
||||
|
||||
get scrollLeft(): number {
|
||||
return this.getScrollLeft();
|
||||
}
|
||||
|
||||
domElement(index: number, column: number): HTMLElement | null {
|
||||
const row = this.visibleRows[index];
|
||||
const cell = row && row.cells && row.cells[column];
|
||||
return cell && cell.domNode;
|
||||
}
|
||||
|
||||
element(index: number): T | undefined {
|
||||
return this.visibleRows[index].element;
|
||||
}
|
||||
|
||||
column(index: number): ITableColumn<T, any> | undefined {
|
||||
return this.columns[index];
|
||||
}
|
||||
|
||||
indexOfColumn(columnId: string): number | undefined {
|
||||
return firstIndex(this.columns, v => v.id === columnId);
|
||||
}
|
||||
|
||||
get renderHeight(): number {
|
||||
const scrollDimensions = this.scrollableElement.getScrollDimensions();
|
||||
return scrollDimensions.height;
|
||||
}
|
||||
|
||||
private insertRowInDOM(index: number, beforeElement: HTMLElement | null): void {
|
||||
let row = this.visibleRows[index];
|
||||
|
||||
if (!row) {
|
||||
row = {
|
||||
id: String(index),
|
||||
element: undefined,
|
||||
row: null,
|
||||
size: this.rowHeight,
|
||||
cells: null,
|
||||
datapromise: null
|
||||
};
|
||||
row.datapromise = createCancelablePromise(token => {
|
||||
return this.dataSource.getRow(index).then(d => {
|
||||
row.element = d;
|
||||
});
|
||||
});
|
||||
row.datapromise.finally(() => row.datapromise = null);
|
||||
this.visibleRows[index] = row;
|
||||
}
|
||||
|
||||
if (!row.row) {
|
||||
this.allocRow(row, index);
|
||||
row.row!.setAttribute('role', 'treeitem');
|
||||
}
|
||||
|
||||
if (!row.row!.parentElement) {
|
||||
if (beforeElement) {
|
||||
this.rowsContainer.insertBefore(row.row!, beforeElement);
|
||||
} else {
|
||||
this.rowsContainer.appendChild(row.row!);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateRowInDOM(row, index);
|
||||
|
||||
if (row.datapromise) {
|
||||
row.datapromise.then(() => this.renderRow(row, index));
|
||||
// in this case we can special case the row count column
|
||||
for (const [i, column] of this.staticColumns.entries()) {
|
||||
const cell = row.cells![i];
|
||||
column.renderer.renderCell(undefined, index, i, column.id, cell.templateData, column.width);
|
||||
}
|
||||
} else {
|
||||
this.renderRow(row, index);
|
||||
}
|
||||
}
|
||||
|
||||
private allocRow(row: IAsyncRowItem<T>, index: number): void {
|
||||
row.cells = new Array<ICell>();
|
||||
row.row = DOM.$('.monaco-perftable-row');
|
||||
for (const [index, column] of this.columns.entries()) {
|
||||
const cell = this.cache.alloc(column.id);
|
||||
row.cells[index] = cell;
|
||||
if (column.cellClass) {
|
||||
DOM.addClass(cell.domNode!, column.cellClass);
|
||||
}
|
||||
row.row.appendChild(cell.domNode!);
|
||||
}
|
||||
}
|
||||
|
||||
private renderRow(row: IAsyncRowItem<T>, index: number): void {
|
||||
for (const [i, column] of this.columns.entries()) {
|
||||
const cell = row.cells![i];
|
||||
column.renderer.renderCell(row.element!, index, i, column.id, cell.templateData, column.width);
|
||||
}
|
||||
}
|
||||
|
||||
private updateRowInDOM(row: IAsyncRowItem<T>, index: number): void {
|
||||
row.row!.style.top = `${this.elementTop(index)}px`;
|
||||
row.row!.style.height = `${row.size}px`;
|
||||
|
||||
for (const [columnIndex, column] of this.columns.entries()) {
|
||||
const cell = row.cells![columnIndex].domNode;
|
||||
cell!.style.width = `${column.width}px`;
|
||||
cell!.style.left = `${column.left}px`;
|
||||
cell!.style.height = `${row.size}px`;
|
||||
cell!.style.lineHeight = `${row.size}px`;
|
||||
cell!.setAttribute('data-column-id', `${columnIndex}`);
|
||||
cell!.setAttribute('role', 'gridcell');
|
||||
row.row!.setAttribute('id', this.getElementDomId(index, columnIndex));
|
||||
}
|
||||
|
||||
row.row!.setAttribute('data-index', `${index}`);
|
||||
row.row!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false');
|
||||
row.row!.setAttribute('role', 'row');
|
||||
// row.row!.setAttribute('aria-setsize', String(this.ariaSetProvider.getSetSize(row.element, index, this.length)));
|
||||
// row.row!.setAttribute('aria-posinset', String(this.ariaSetProvider.getPosInSet(row.element, index)));
|
||||
row.row!.setAttribute('id', this.getElementDomId(index));
|
||||
}
|
||||
|
||||
private removeRowFromDOM(index: number): void {
|
||||
const item = this.visibleRows[index];
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
let canceled = false;
|
||||
if (item.datapromise) {
|
||||
item.datapromise.cancel();
|
||||
canceled = true;
|
||||
}
|
||||
|
||||
for (const [i, column] of this.columns.entries()) {
|
||||
const cell = item.cells![i];
|
||||
|
||||
if (!canceled) {
|
||||
const renderer = column.renderer;
|
||||
if (renderer && renderer.disposeCell) {
|
||||
renderer.disposeCell(item.element!, index, i, column.id, cell.templateData, column.width);
|
||||
}
|
||||
}
|
||||
|
||||
this.cache.release(cell!);
|
||||
}
|
||||
|
||||
removeFromParent(item.row!);
|
||||
|
||||
delete this.visibleRows[index];
|
||||
}
|
||||
|
||||
@memoize get onMouseClick(): Event<ITableMouseEvent<T>> { return Event.map(domEvent(this.domNode, 'click'), e => this.toMouseEvent(e)); }
|
||||
@memoize get onMouseDblClick(): Event<ITableMouseEvent<T>> { return Event.map(domEvent(this.domNode, 'dblclick'), e => this.toMouseEvent(e)); }
|
||||
@memoize get onMouseMiddleClick(): Event<ITableMouseEvent<T>> { return Event.filter(Event.map(domEvent(this.domNode, 'auxclick'), e => this.toMouseEvent(e as MouseEvent)), e => e.browserEvent.button === 1); }
|
||||
@memoize get onMouseUp(): Event<ITableMouseEvent<T>> { return Event.map(domEvent(this.domNode, 'mouseup'), e => this.toMouseEvent(e)); }
|
||||
@memoize get onMouseDown(): Event<ITableMouseEvent<T>> { return Event.map(domEvent(this.domNode, 'mousedown'), e => this.toMouseEvent(e)); }
|
||||
@memoize get onMouseOver(): Event<ITableMouseEvent<T>> { return Event.map(domEvent(this.domNode, 'mouseover'), e => this.toMouseEvent(e)); }
|
||||
@memoize get onMouseMove(): Event<ITableMouseEvent<T>> { return Event.map(domEvent(this.domNode, 'mousemove'), e => this.toMouseEvent(e)); }
|
||||
@memoize get onMouseOut(): Event<ITableMouseEvent<T>> { return Event.map(domEvent(this.domNode, 'mouseout'), e => this.toMouseEvent(e)); }
|
||||
@memoize get onContextMenu(): Event<ITableMouseEvent<T>> { return Event.map(domEvent(this.domNode, 'contextmenu'), e => this.toMouseEvent(e)); }
|
||||
|
||||
public toMouseEvent(browserEvent: MouseEvent): ITableMouseEvent<T> {
|
||||
const index = this.getItemIndexFromEventTarget(browserEvent.target || null);
|
||||
const item = typeof index === 'undefined' ? undefined : this.visibleRows[index.row];
|
||||
const element = item && item.element;
|
||||
const buttons = browserEvent.buttons;
|
||||
return { browserEvent, buttons, index, element };
|
||||
}
|
||||
|
||||
public getItemIndexFromEventTarget(target: EventTarget | null): GridPosition | undefined {
|
||||
let element: HTMLElement | null = target as (HTMLElement | null);
|
||||
|
||||
while (element instanceof HTMLElement && element !== this.rowsContainer) {
|
||||
const rawColumn = element.getAttribute('data-column-id');
|
||||
|
||||
if (rawColumn) {
|
||||
const column = Number(rawColumn);
|
||||
|
||||
if (!isNaN(column)) {
|
||||
while (element instanceof HTMLElement && element !== this.rowsContainer) {
|
||||
const rawIndex = element.getAttribute('data-index');
|
||||
|
||||
if (rawIndex) {
|
||||
const row = Number(rawIndex);
|
||||
|
||||
if (!isNaN(row)) {
|
||||
return new GridPosition(row, column);
|
||||
}
|
||||
}
|
||||
|
||||
element = element.parentElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
element = element!.parentElement;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
elementTop(index: number): number {
|
||||
return Math.floor(index * this.rowHeight) - this.bigNumberDelta;
|
||||
}
|
||||
|
||||
getElementDomId(index: number, column?: number): string {
|
||||
if (column) {
|
||||
return `${this.domId}_${index}_${column}`;
|
||||
} else {
|
||||
return `${this.domId}_${index}`;
|
||||
}
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
set length(length: number) {
|
||||
this.domNode.setAttribute('aria-rowcount', `${length}`);
|
||||
const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight);
|
||||
const potentialRerenderRange = { start: this.length, end: length };
|
||||
const rerenderRange = Range.intersect(potentialRerenderRange, previousRenderRange);
|
||||
this._length = length;
|
||||
for (let i = rerenderRange.start; i < rerenderRange.end; i++) {
|
||||
if (this.visibleRows[i]) {
|
||||
this.removeRowFromDOM(i);
|
||||
}
|
||||
this.insertRowInDOM(i, null);
|
||||
}
|
||||
this.eventuallyUpdateScrollDimensions();
|
||||
}
|
||||
|
||||
get columnLength(): number {
|
||||
return this.columns.length;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
1060
src/sql/base/browser/ui/table/highPerf/tableWidget.ts
Normal file
155
src/sql/base/browser/ui/table/highPerf/virtualizedWindow.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
|
||||
class DataWindow<T> {
|
||||
private _data: T[] | undefined;
|
||||
private _length: number = 0;
|
||||
private _offsetFromDataSource: number = -1;
|
||||
|
||||
private dataReady?: CancelablePromise<void>;
|
||||
|
||||
constructor(
|
||||
private loadFunction: (offset: number, count: number) => Promise<T[]>
|
||||
) { }
|
||||
|
||||
dispose() {
|
||||
this._data = undefined;
|
||||
if (this.dataReady) {
|
||||
this.dataReady.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
get start(): number {
|
||||
return this._offsetFromDataSource;
|
||||
}
|
||||
|
||||
get end(): number {
|
||||
return this._offsetFromDataSource + this._length;
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
public contains(dataSourceIndex: number): boolean {
|
||||
return dataSourceIndex >= this.start && dataSourceIndex < this.end;
|
||||
}
|
||||
|
||||
public getItem(index: number): Promise<T> {
|
||||
return this.dataReady!.then(() => this._data![index - this._offsetFromDataSource]);
|
||||
}
|
||||
|
||||
public positionWindow(offset: number, length: number): void {
|
||||
this._offsetFromDataSource = offset;
|
||||
this._length = length;
|
||||
this._data = undefined;
|
||||
|
||||
if (this.dataReady) {
|
||||
this.dataReady.cancel();
|
||||
}
|
||||
|
||||
if (length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dataReady = createCancelablePromise(token => {
|
||||
return this.loadFunction(offset, length).then(data => {
|
||||
if (!token.isCancellationRequested) {
|
||||
this._data = data;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class VirtualizedWindow<T> {
|
||||
private _bufferWindowBefore: DataWindow<T>;
|
||||
private _window: DataWindow<T>;
|
||||
private _bufferWindowAfter: DataWindow<T>;
|
||||
|
||||
constructor(
|
||||
private readonly windowSize: number,
|
||||
private _length: number,
|
||||
loadFn: (offset: number, count: number) => Promise<T[]>
|
||||
) {
|
||||
|
||||
this._bufferWindowBefore = new DataWindow(loadFn);
|
||||
this._window = new DataWindow(loadFn);
|
||||
this._bufferWindowAfter = new DataWindow(loadFn);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._bufferWindowAfter.dispose();
|
||||
this._bufferWindowBefore.dispose();
|
||||
this._window.dispose();
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
set length(length: number) {
|
||||
if (this.length !== length) {
|
||||
const oldLength = this.length;
|
||||
this._length = length;
|
||||
if (this._window.length !== this.windowSize) {
|
||||
this.resetWindowsAroundIndex(oldLength);
|
||||
} else if (this._bufferWindowAfter.length !== this.windowSize) {
|
||||
this._bufferWindowAfter.positionWindow(this._bufferWindowAfter.start, Math.min(this._bufferWindowAfter.start + this.windowSize, this.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getIndex(index: number): Promise<T> {
|
||||
|
||||
if (index < this._bufferWindowBefore.start || index >= this._bufferWindowAfter.end) {
|
||||
this.resetWindowsAroundIndex(index);
|
||||
}
|
||||
// scrolling up
|
||||
else if (this._bufferWindowBefore.contains(index)) {
|
||||
const beforeWindow = this._bufferWindowAfter;
|
||||
this._bufferWindowAfter = this._window;
|
||||
this._window = this._bufferWindowBefore;
|
||||
this._bufferWindowBefore = beforeWindow;
|
||||
// ensure we aren't buffer invalid data
|
||||
const beforeStart = Math.max(0, this._window.start - this.windowSize);
|
||||
// ensure if we got hinder in our start index that we update out length to not overlap
|
||||
const beforeLength = this._window.start - beforeStart;
|
||||
this._bufferWindowBefore.positionWindow(beforeStart, beforeLength);
|
||||
}
|
||||
// scroll down
|
||||
else if (this._bufferWindowAfter.contains(index)) {
|
||||
const afterWindow = this._bufferWindowBefore;
|
||||
this._bufferWindowBefore = this._window;
|
||||
this._window = this._bufferWindowAfter;
|
||||
this._bufferWindowAfter = afterWindow;
|
||||
// ensure we aren't buffer invalid data
|
||||
const afterStart = this._window.end;
|
||||
// ensure if we got hinder in our start index that we update out length to not overlap
|
||||
const afterLength = afterStart + this.windowSize > this.length ? this.length - afterStart : this.windowSize;
|
||||
this._bufferWindowAfter.positionWindow(afterStart, afterLength);
|
||||
}
|
||||
|
||||
// at this point we know the current window will have the index
|
||||
return this._window.getItem(index);
|
||||
}
|
||||
|
||||
private resetWindowsAroundIndex(index: number): void {
|
||||
|
||||
let bufferWindowBeforeStart = Math.max(0, index - this.windowSize * 1.5);
|
||||
let bufferWindowBeforeEnd = Math.max(0, index - this.windowSize / 2);
|
||||
this._bufferWindowBefore.positionWindow(bufferWindowBeforeStart, bufferWindowBeforeEnd - bufferWindowBeforeStart);
|
||||
|
||||
let mainWindowStart = bufferWindowBeforeEnd;
|
||||
let mainWindowEnd = Math.min(mainWindowStart + this.windowSize, this.length);
|
||||
this._window.positionWindow(mainWindowStart, mainWindowEnd - mainWindowStart);
|
||||
|
||||
let bufferWindowAfterStart = mainWindowEnd;
|
||||
let bufferWindowAfterEnd = Math.min(bufferWindowAfterStart + this.windowSize, this.length);
|
||||
this._bufferWindowAfter.positionWindow(bufferWindowAfterStart, bufferWindowAfterEnd - bufferWindowAfterStart);
|
||||
}
|
||||
}
|
||||
177
src/sql/base/common/gridPosition.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* A position in a grid. This interface is suitable for serialization.
|
||||
*/
|
||||
export interface IGridPosition {
|
||||
/**
|
||||
* line number (starts at 1)
|
||||
*/
|
||||
readonly row: number;
|
||||
/**
|
||||
* column
|
||||
*/
|
||||
readonly column: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A position in a grid.
|
||||
*/
|
||||
export class GridPosition {
|
||||
/**
|
||||
* row (starts at 1)
|
||||
*/
|
||||
public readonly row: number;
|
||||
/**
|
||||
* column
|
||||
*/
|
||||
public readonly column: number;
|
||||
|
||||
constructor(row: number, column: number) {
|
||||
this.row = row;
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new postion from this position.
|
||||
*
|
||||
* @param newRow new row
|
||||
* @param newColumn new column
|
||||
*/
|
||||
with(newRow: number = this.row, newColumn: number = this.column): GridPosition {
|
||||
if (newRow === this.row && newColumn === this.column) {
|
||||
return this;
|
||||
} else {
|
||||
return new GridPosition(newRow, newColumn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a new grid position from this grid position.
|
||||
*
|
||||
* @param deltaRow row delta
|
||||
* @param deltaColumn column delta
|
||||
*/
|
||||
delta(deltaRow: number = 0, deltaColumn: number = 0): GridPosition {
|
||||
return this.with(this.row + deltaRow, this.column + deltaColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if this grid position equals other grid position
|
||||
*/
|
||||
public equals(other: IGridPosition): boolean {
|
||||
return GridPosition.equals(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if grid position `a` equals grid position `b`
|
||||
*/
|
||||
public static equals(a: IGridPosition | null, b: IGridPosition | null): boolean {
|
||||
if (!a && !b) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
!!a &&
|
||||
!!b &&
|
||||
a.row === b.row &&
|
||||
a.column === b.column
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if this grid position is before other grid position.
|
||||
* If the two grid positions are equal, the result will be false.
|
||||
*/
|
||||
public isBefore(other: IGridPosition): boolean {
|
||||
return GridPosition.isBefore(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if grid position `a` is before grid position `b`.
|
||||
* If the two grid positions are equal, the result will be false.
|
||||
*/
|
||||
public static isBefore(a: IGridPosition, b: IGridPosition): boolean {
|
||||
if (a.row < b.row) {
|
||||
return true;
|
||||
}
|
||||
if (b.row < a.row) {
|
||||
return false;
|
||||
}
|
||||
return a.column < b.column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if this grid position is before other grid position.
|
||||
* If the two grid positions are equal, the result will be true.
|
||||
*/
|
||||
public isBeforeOrEqual(other: IGridPosition): boolean {
|
||||
return GridPosition.isBeforeOrEqual(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if grid position `a` is before grid position `b`.
|
||||
* If the two grid positions are equal, the result will be true.
|
||||
*/
|
||||
public static isBeforeOrEqual(a: IGridPosition, b: IGridPosition): boolean {
|
||||
if (a.row < b.row) {
|
||||
return true;
|
||||
}
|
||||
if (b.row < a.row) {
|
||||
return false;
|
||||
}
|
||||
return a.column <= b.column;
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that compares grid positions, useful for sorting
|
||||
*/
|
||||
public static compare(a: IGridPosition, b: IGridPosition): number {
|
||||
let aRow = a.row | 0;
|
||||
let bRow = b.row | 0;
|
||||
|
||||
if (aRow === bRow) {
|
||||
let aColumn = a.column | 0;
|
||||
let bColumn = b.column | 0;
|
||||
return aColumn - bColumn;
|
||||
}
|
||||
|
||||
return aRow - bRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone this grid position.
|
||||
*/
|
||||
public clone(): GridPosition {
|
||||
return new GridPosition(this.row, this.column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to a human-readable representation.
|
||||
*/
|
||||
public toString(): string {
|
||||
return '(' + this.row + ',' + this.column + ')';
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
/**
|
||||
* Create a `GridPosition` from an `IGridPosition`.
|
||||
*/
|
||||
public static lift(pos: IGridPosition): GridPosition {
|
||||
return new GridPosition(pos.row, pos.column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if `obj` is an `IGridPosition`.
|
||||
*/
|
||||
public static isIGridPosition(obj: any): obj is IGridPosition {
|
||||
return (
|
||||
obj
|
||||
&& (typeof obj.row === 'number')
|
||||
&& (typeof obj.column === 'number')
|
||||
);
|
||||
}
|
||||
}
|
||||
361
src/sql/base/common/gridRange.ts
Normal file
@@ -0,0 +1,361 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IGridPosition, GridPosition } from 'sql/base/common/gridPosition';
|
||||
import { isNumber } from 'vs/base/common/types';
|
||||
|
||||
/**
|
||||
* A range in a grid. This interface is suitable for serialization.
|
||||
*/
|
||||
export interface IGridRange {
|
||||
/**
|
||||
* Row on which the range starts (starts at 1).
|
||||
*/
|
||||
readonly startRow: number;
|
||||
/**
|
||||
* Column on which the range starts in line `startRow` (starts at 1).
|
||||
*/
|
||||
readonly startColumn: number;
|
||||
/**
|
||||
* Row on which the range ends.
|
||||
*/
|
||||
readonly endRow: number;
|
||||
/**
|
||||
* Column on which the range ends in line `endRow`.
|
||||
*/
|
||||
readonly endColumn: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A range in a grid. (startRow,startColumn) is <= (endRow,endColumn)
|
||||
*/
|
||||
export class GridRange {
|
||||
|
||||
/**
|
||||
* Row on which the range starts (starts at 1).
|
||||
*/
|
||||
public readonly startRow: number;
|
||||
/**
|
||||
* Column on which the range starts in line `startRow` (starts at 1).
|
||||
*/
|
||||
public readonly startColumn: number;
|
||||
/**
|
||||
* Row on which the range ends.
|
||||
*/
|
||||
public readonly endRow: number;
|
||||
/**
|
||||
* Column on which the range ends in line `endRow`.
|
||||
*/
|
||||
public readonly endColumn: number;
|
||||
|
||||
constructor(startRow: number, startColumn: number, endRow?: number, endColumn?: number) {
|
||||
this.startRow = isNumber(endRow) ? Math.min(startRow, endRow) : startRow;
|
||||
this.startColumn = isNumber(endColumn) ? Math.min(startColumn, endColumn) : startColumn;
|
||||
this.endRow = isNumber(endRow) ? Math.max(endRow, startRow) : startRow;
|
||||
this.endColumn = isNumber(endColumn) ? Math.max(endColumn, startColumn) : startColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if position is in this range. If the position is at the edges, will return true.
|
||||
*/
|
||||
public containsPosition(position: IGridPosition): boolean {
|
||||
return GridRange.containsPosition(this, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if `position` is in `range`. If the position is at the edges, will return true.
|
||||
*/
|
||||
public static containsPosition(range: IGridRange, position: IGridPosition): boolean {
|
||||
return position.row >= range.startRow
|
||||
&& position.row <= range.endRow
|
||||
&& position.column >= range.startColumn
|
||||
&& position.column <= range.endColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if range is in this range. If the range is equal to this range, will return true.
|
||||
*/
|
||||
public containsRange(range: IGridRange): boolean {
|
||||
return GridRange.containsRange(this, range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if `otherRange` is in `range`. If the ranges are equal, will return true.
|
||||
*/
|
||||
public static containsRange(range: IGridRange, otherRange: IGridRange): boolean {
|
||||
if (otherRange.startRow < range.startRow || otherRange.endRow < range.startRow) {
|
||||
return false;
|
||||
}
|
||||
if (otherRange.startRow > range.endRow || otherRange.endRow > range.endRow) {
|
||||
return false;
|
||||
}
|
||||
if (otherRange.startRow === range.startRow && otherRange.startColumn < range.startColumn) {
|
||||
return false;
|
||||
}
|
||||
if (otherRange.endRow === range.endRow && otherRange.endColumn > range.endColumn) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A reunion of the two ranges.
|
||||
* The smallest position will be used as the start point, and the largest one as the end point.
|
||||
*/
|
||||
public plusRange(range: IGridRange): GridRange {
|
||||
return GridRange.plusRange(this, range);
|
||||
}
|
||||
|
||||
/**
|
||||
* A reunion of the two ranges.
|
||||
* The smallest position will be used as the start point, and the largest one as the end point.
|
||||
*/
|
||||
public static plusRange(a: IGridRange, b: IGridRange): GridRange {
|
||||
let startRow = Math.min(a.startRow, b.startRow);
|
||||
let startColumn = Math.min(a.startColumn, b.startColumn);
|
||||
let endRow = Math.max(a.endRow, b.endRow);
|
||||
let endColumn = Math.max(a.endColumn, b.endColumn);
|
||||
|
||||
return new GridRange(startRow, startColumn, endRow, endColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* A intersection of the two ranges.
|
||||
*/
|
||||
public intersectRanges(range: IGridRange): GridRange | null {
|
||||
return GridRange.intersectRanges(this, range);
|
||||
}
|
||||
|
||||
/**
|
||||
* A intersection of the two ranges.
|
||||
*/
|
||||
public static intersectRanges(a: IGridRange, b: IGridRange): GridRange | null {
|
||||
let resultStartRow = a.startRow;
|
||||
let resultStartColumn = a.startColumn;
|
||||
let resultEndRow = a.endRow;
|
||||
let resultEndColumn = a.endColumn;
|
||||
let otherStartRow = b.startRow;
|
||||
let otherStartColumn = b.startColumn;
|
||||
let otherEndRow = b.endRow;
|
||||
let otherEndColumn = b.endColumn;
|
||||
|
||||
if (resultStartRow < otherStartRow) {
|
||||
resultStartRow = otherStartRow;
|
||||
resultStartColumn = otherStartColumn;
|
||||
} else if (resultStartRow === otherStartRow) {
|
||||
resultStartColumn = Math.max(resultStartColumn, otherStartColumn);
|
||||
}
|
||||
|
||||
if (resultEndRow > otherEndRow) {
|
||||
resultEndRow = otherEndRow;
|
||||
resultEndColumn = otherEndColumn;
|
||||
} else if (resultEndRow === otherEndRow) {
|
||||
resultEndColumn = Math.min(resultEndColumn, otherEndColumn);
|
||||
}
|
||||
|
||||
// Check if selection is now empty
|
||||
if (resultStartRow > resultEndRow) {
|
||||
return null;
|
||||
}
|
||||
if (resultStartRow === resultEndRow && resultStartColumn > resultEndColumn) {
|
||||
return null;
|
||||
}
|
||||
return new GridRange(resultStartRow, resultStartColumn, resultEndRow, resultEndColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if this range equals other.
|
||||
*/
|
||||
public equalsRange(other: IGridRange | null): boolean {
|
||||
return GridRange.equalsRange(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if range `a` equals `b`.
|
||||
*/
|
||||
public static equalsRange(a: IGridRange | null, b: IGridRange | null): boolean {
|
||||
return (
|
||||
!!a &&
|
||||
!!b &&
|
||||
a.startRow === b.startRow &&
|
||||
a.startColumn === b.startColumn &&
|
||||
a.endRow === b.endRow &&
|
||||
a.endColumn === b.endColumn
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the end position (which will be after or equal to the start position)
|
||||
*/
|
||||
public getEndPosition(): GridPosition {
|
||||
return new GridPosition(this.endRow, this.endColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the start position (which will be before or equal to the end position)
|
||||
*/
|
||||
public getStartPosition(): GridPosition {
|
||||
return new GridPosition(this.startRow, this.startColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform to a user presentable string representation.
|
||||
*/
|
||||
public toString(): string {
|
||||
return '[' + this.startRow + ',' + this.startColumn + ' -> ' + this.endRow + ',' + this.endColumn + ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new range using this range's start position, and using endRow and endColumn as the end position.
|
||||
*/
|
||||
public setEndPosition(endRow: number, endColumn: number): GridRange {
|
||||
return new GridRange(this.startRow, this.startColumn, endRow, endColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new range using this range's end position, and using startRow and startColumn as the start position.
|
||||
*/
|
||||
public setStartPosition(startRow: number, startColumn: number): GridRange {
|
||||
return new GridRange(startRow, startColumn, this.endRow, this.endColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new empty range using this range's start position.
|
||||
*/
|
||||
public collapseToStart(): GridRange {
|
||||
return GridRange.collapseToStart(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new empty range using this range's start position.
|
||||
*/
|
||||
public static collapseToStart(range: IGridRange): GridRange {
|
||||
return new GridRange(range.startRow, range.startColumn, range.startRow, range.startColumn);
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
public static fromPositions(start: IGridPosition, end: IGridPosition = start): GridRange {
|
||||
return new GridRange(start.row, start.column, end.row, end.column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `GridRange` from an `IGridRange`.
|
||||
*/
|
||||
public static lift(range: undefined | null): null;
|
||||
public static lift(range: IGridRange): GridRange;
|
||||
public static lift(range: IGridRange | undefined | null): GridRange | null {
|
||||
if (!range) {
|
||||
return null;
|
||||
}
|
||||
return new GridRange(range.startRow, range.startColumn, range.endRow, range.endColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if `obj` is an `IGridRange`.
|
||||
*/
|
||||
public static isIRange(obj: any): obj is IGridRange {
|
||||
return (
|
||||
obj
|
||||
&& (typeof obj.startRow === 'number')
|
||||
&& (typeof obj.startColumn === 'number')
|
||||
&& (typeof obj.endRow === 'number')
|
||||
&& (typeof obj.endColumn === 'number')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the two ranges are touching in any way.
|
||||
*/
|
||||
public static areIntersectingOrTouching(a: IGridRange, b: IGridRange): boolean {
|
||||
// Check if `a` is before `b`
|
||||
if (a.endRow < b.startRow || (a.endRow === b.startRow && a.endColumn < b.startColumn)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if `b` is before `a`
|
||||
if (b.endRow < a.startRow || (b.endRow === a.startRow && b.endColumn < a.startColumn)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// These ranges must intersect
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the two ranges are intersecting. If the ranges are touching it returns true.
|
||||
*/
|
||||
public static areIntersecting(a: IGridRange, b: IGridRange): boolean {
|
||||
// Check if `a` is before `b`
|
||||
if (a.endRow < b.startRow || (a.endRow === b.startRow && a.endColumn <= b.startColumn)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if `b` is before `a`
|
||||
if (b.endRow < a.startRow || (b.endRow === a.startRow && b.endColumn <= a.startColumn)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// These ranges must intersect
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that compares ranges, useful for sorting ranges
|
||||
* It will first compare ranges on the startPosition and then on the endPosition
|
||||
*/
|
||||
public static compareRangesUsingStarts(a: IGridRange | null | undefined, b: IGridRange | null | undefined): number {
|
||||
if (a && b) {
|
||||
const aStartRow = a.startRow | 0;
|
||||
const bStartRow = b.startRow | 0;
|
||||
|
||||
if (aStartRow === bStartRow) {
|
||||
const aStartColumn = a.startColumn | 0;
|
||||
const bStartColumn = b.startColumn | 0;
|
||||
|
||||
if (aStartColumn === bStartColumn) {
|
||||
const aEndRow = a.endRow | 0;
|
||||
const bEndRow = b.endRow | 0;
|
||||
|
||||
if (aEndRow === bEndRow) {
|
||||
const aEndColumn = a.endColumn | 0;
|
||||
const bEndColumn = b.endColumn | 0;
|
||||
return aEndColumn - bEndColumn;
|
||||
}
|
||||
return aEndRow - bEndRow;
|
||||
}
|
||||
return aStartColumn - bStartColumn;
|
||||
}
|
||||
return aStartRow - bStartRow;
|
||||
}
|
||||
const aExists = (a ? 1 : 0);
|
||||
const bExists = (b ? 1 : 0);
|
||||
return aExists - bExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that compares ranges, useful for sorting ranges
|
||||
* It will first compare ranges on the endPosition and then on the startPosition
|
||||
*/
|
||||
public static compareRangesUsingEnds(a: IGridRange, b: IGridRange): number {
|
||||
if (a.endRow === b.endRow) {
|
||||
if (a.endColumn === b.endColumn) {
|
||||
if (a.startRow === b.startRow) {
|
||||
return a.startColumn - b.startColumn;
|
||||
}
|
||||
return a.startRow - b.startRow;
|
||||
}
|
||||
return a.endColumn - b.endColumn;
|
||||
}
|
||||
return a.endRow - b.endRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the range spans multiple lines.
|
||||
*/
|
||||
public static spansMultipleLines(range: IGridRange): boolean {
|
||||
return range.endRow > range.startRow;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,8 @@ export interface IQueryEditorConfiguration {
|
||||
},
|
||||
readonly streaming: boolean,
|
||||
readonly copyIncludeHeaders: boolean,
|
||||
readonly copyRemoveNewLine: boolean
|
||||
readonly copyRemoveNewLine: boolean,
|
||||
readonly optimizedTable: boolean
|
||||
},
|
||||
readonly messages: {
|
||||
readonly showBatchTime: boolean,
|
||||
|
||||
205
src/sql/platform/table/browser/tableService.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 { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Table, DefaultStyleController, ITableOptions } from 'sql/base/browser/ui/table/highPerf/tableWidget';
|
||||
import { RawContextKey, IContextKey, ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { DisposableStore, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { createStyleSheet } from 'vs/base/browser/dom';
|
||||
import { attachHighPerfTableStyler as attachTableStyler, defaultHighPerfTableStyles } from 'sql/platform/theme/common/styler';
|
||||
import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ITableDataSource, ITableColumn } from 'sql/base/browser/ui/table/highPerf/table';
|
||||
import { IColorMapping, computeStyles } from 'vs/platform/theme/common/styler';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
export const ITableService = createDecorator<ITableService>('tableService');
|
||||
|
||||
export type TableWidget = Table<any>;
|
||||
|
||||
export interface ITableService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
/**
|
||||
* Returns the currently focused table widget if any.
|
||||
*/
|
||||
readonly lastFocusedTable: TableWidget | undefined;
|
||||
}
|
||||
|
||||
interface IRegisteredTable {
|
||||
widget: TableWidget;
|
||||
extraContextKeys?: (IContextKey<boolean>)[];
|
||||
}
|
||||
|
||||
export class TableService implements ITableService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private disposables = new DisposableStore();
|
||||
private tables: IRegisteredTable[] = [];
|
||||
private _lastFocusedWidget: TableWidget | undefined = undefined;
|
||||
|
||||
get lastFocusedTable(): TableWidget | undefined {
|
||||
return this._lastFocusedWidget;
|
||||
}
|
||||
|
||||
constructor(@IThemeService themeService: IThemeService) {
|
||||
// create a shared default tree style sheet for performance reasons
|
||||
const styleController = new DefaultStyleController(createStyleSheet(), '');
|
||||
this.disposables.add(attachTableStyler(styleController, themeService));
|
||||
}
|
||||
|
||||
register(widget: TableWidget, extraContextKeys?: (IContextKey<boolean>)[]): IDisposable {
|
||||
if (this.tables.some(l => l.widget === widget)) {
|
||||
throw new Error('Cannot register the same widget multiple times');
|
||||
}
|
||||
|
||||
// Keep in our tables table
|
||||
const registeredTable: IRegisteredTable = { widget, extraContextKeys };
|
||||
this.tables.push(registeredTable);
|
||||
|
||||
// Check for currently being focused
|
||||
if (widget.getHTMLElement() === document.activeElement) {
|
||||
this._lastFocusedWidget = widget;
|
||||
}
|
||||
|
||||
return combinedDisposable(
|
||||
widget.onDidFocus(() => this._lastFocusedWidget = widget),
|
||||
toDisposable(() => this.tables.splice(this.tables.indexOf(registeredTable), 1)),
|
||||
widget.onDidDispose(() => {
|
||||
this.tables = this.tables.filter(l => l !== registeredTable);
|
||||
if (this._lastFocusedWidget === widget) {
|
||||
this._lastFocusedWidget = undefined;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
const RawWorkbenchTableFocusContextKey = new RawContextKey<boolean>('tableFocus', true);
|
||||
export const WorkbenchTableFocusContextKey = ContextKeyExpr.and(RawWorkbenchTableFocusContextKey, ContextKeyExpr.not(InputFocusedContextKey));
|
||||
export const WorkbenchTableHasSelectionOrFocus = new RawContextKey<boolean>('tableHasSelectionOrFocus', false);
|
||||
export const WorkbenchTableDoubleSelection = new RawContextKey<boolean>('tableDoubleSelection', false);
|
||||
export const WorkbenchTableMultiSelection = new RawContextKey<boolean>('tableMultiSelection', false);
|
||||
export const WorkbenchTableSupportsKeyboardNavigation = new RawContextKey<boolean>('tableSupportsKeyboardNavigation', true);
|
||||
export const WorkbenchTableAutomaticKeyboardNavigationKey = 'tableAutomaticKeyboardNavigation';
|
||||
export const WorkbenchTableAutomaticKeyboardNavigation = new RawContextKey<boolean>(WorkbenchTableAutomaticKeyboardNavigationKey, true);
|
||||
export let didBindWorkbenchTableAutomaticKeyboardNavigation = false;
|
||||
|
||||
function createScopedContextKeyService(contextKeyService: IContextKeyService, widget: TableWidget): IContextKeyService {
|
||||
const result = contextKeyService.createScoped(widget.getHTMLElement());
|
||||
RawWorkbenchTableFocusContextKey.bindTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
export const multiSelectModifierSettingKey = 'workbench.table.multiSelectModifier';
|
||||
export const openModeSettingKey = 'workbench.table.openMode';
|
||||
export const horizontalScrollingKey = 'workbench.table.horizontalScrolling';
|
||||
export const keyboardNavigationSettingKey = 'workbench.table.keyboardNavigation';
|
||||
export const automaticKeyboardNavigationSettingKey = 'workbench.table.automaticKeyboardNavigation';
|
||||
|
||||
function useAltAsMultipleSelectionModifier(configurationService: IConfigurationService): boolean {
|
||||
return configurationService.getValue(multiSelectModifierSettingKey) === 'alt';
|
||||
}
|
||||
|
||||
function toWorkbenchTableOptions<T>(options: ITableOptions<T>): [ITableOptions<T>, IDisposable] {
|
||||
const disposables = new DisposableStore();
|
||||
const result = { ...options };
|
||||
|
||||
return [result, disposables];
|
||||
}
|
||||
|
||||
export interface IWorkbenchTableOptions<T> extends ITableOptions<T> {
|
||||
readonly overrideStyles?: IColorMapping;
|
||||
}
|
||||
|
||||
export class WorkbenchTable<T> extends Table<T> {
|
||||
|
||||
readonly contextKeyService: IContextKeyService;
|
||||
private readonly configurationService: IConfigurationService;
|
||||
|
||||
private tableHasSelectionOrFocus: IContextKey<boolean>;
|
||||
private tableDoubleSelection: IContextKey<boolean>;
|
||||
private tableMultiSelection: IContextKey<boolean>;
|
||||
|
||||
private _useAltAsMultipleSelectionModifier: boolean;
|
||||
|
||||
constructor(
|
||||
user: string,
|
||||
container: HTMLElement,
|
||||
columns: ITableColumn<T, any>[],
|
||||
dataSource: ITableDataSource<T>,
|
||||
options: IWorkbenchTableOptions<T>,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@ITableService tableService: ITableService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
const [workbenchTableOptions, workbenchTableOptionsDisposable] = toWorkbenchTableOptions(options);
|
||||
|
||||
super(user, container, columns, dataSource,
|
||||
{
|
||||
keyboardSupport: false,
|
||||
...computeStyles(themeService.getColorTheme(), defaultHighPerfTableStyles),
|
||||
...workbenchTableOptions
|
||||
}
|
||||
);
|
||||
|
||||
this.disposables.add(workbenchTableOptionsDisposable);
|
||||
|
||||
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
|
||||
this.configurationService = configurationService;
|
||||
|
||||
this.tableHasSelectionOrFocus = WorkbenchTableHasSelectionOrFocus.bindTo(this.contextKeyService);
|
||||
this.tableDoubleSelection = WorkbenchTableDoubleSelection.bindTo(this.contextKeyService);
|
||||
this.tableMultiSelection = WorkbenchTableMultiSelection.bindTo(this.contextKeyService);
|
||||
|
||||
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
|
||||
|
||||
this.disposables.add(this.contextKeyService);
|
||||
this.disposables.add((tableService as TableService).register(this));
|
||||
|
||||
if (options.overrideStyles) {
|
||||
this.disposables.add(attachTableStyler(this, themeService, options.overrideStyles));
|
||||
}
|
||||
|
||||
this.disposables.add(this.onSelectionChange(() => {
|
||||
const selection = this.getSelection();
|
||||
const focus = this.getFocus();
|
||||
|
||||
this.tableHasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
|
||||
this.tableMultiSelection.set(selection.length > 1);
|
||||
this.tableDoubleSelection.set(selection.length === 2);
|
||||
}));
|
||||
this.disposables.add(this.onFocusChange(() => {
|
||||
const selection = this.getSelection();
|
||||
const focus = this.getFocus();
|
||||
|
||||
this.tableHasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
|
||||
}));
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.disposables.add(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
|
||||
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(this.configurationService);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
get useAltAsMultipleSelectionModifier(): boolean {
|
||||
return this._useAltAsMultipleSelectionModifier;
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(ITableService, TableService, true);
|
||||
@@ -9,12 +9,14 @@ import * as nls from 'vs/nls';
|
||||
|
||||
export const tableHeaderBackground = registerColor('table.headerBackground', { dark: new Color(new RGBA(51, 51, 52)), light: new Color(new RGBA(245, 245, 245)), hc: '#333334' }, nls.localize('tableHeaderBackground', "Table header background color"));
|
||||
export const tableHeaderForeground = registerColor('table.headerForeground', { dark: new Color(new RGBA(229, 229, 229)), light: new Color(new RGBA(16, 16, 16)), hc: '#e5e5e5' }, nls.localize('tableHeaderForeground', "Table header foreground color"));
|
||||
export const listFocusAndSelectionBackground = registerColor('list.focusAndSelectionBackground', { dark: '#2c3295', light: '#2c3295', hc: null }, nls.localize('listFocusAndSelectionBackground', "List/Table background color for the selected and focus item when the list/table is active"));
|
||||
export const tableCellOutline = registerColor('table.cell.outline', { dark: '#e3e4e229', light: '#33333333', hc: '#e3e4e229' }, nls.localize('tableCellOutline', 'Color of the outline of a cell.'));
|
||||
|
||||
export const disabledInputBackground = registerColor('input.disabled.background', { dark: '#444444', light: '#dcdcdc', hc: Color.black }, nls.localize('disabledInputBoxBackground', "Disabled Input box background."));
|
||||
export const disabledInputForeground = registerColor('input.disabled.foreground', { dark: '#888888', light: '#888888', hc: foreground }, nls.localize('disabledInputBoxForeground', "Disabled Input box foreground."));
|
||||
export const buttonFocusOutline = registerColor('button.focusOutline', { dark: '#eaeaea', light: '#666666', hc: null }, nls.localize('buttonFocusOutline', "Button outline color when focused."));
|
||||
export const disabledCheckboxForeground = registerColor('checkbox.disabled.foreground', { dark: '#888888', light: '#888888', hc: Color.black }, nls.localize('disabledCheckboxforeground', "Disabled checkbox foreground."));
|
||||
|
||||
export const listFocusAndSelectionBackground = registerColor('list.focusAndSelectionBackground', { dark: '#2c3295', light: '#2c3295', hc: null }, nls.localize('listFocusAndSelectionBackground', "List/Table background color for the selected and focus item when the list/table is active"));
|
||||
|
||||
// SQL Agent Colors
|
||||
export const tableBackground = registerColor('agent.tableBackground', { light: '#fffffe', dark: '#333333', hc: Color.black }, nls.localize('agentTableBackground', "SQL Agent Table background color."));
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as sqlcolors from './colors';
|
||||
import * as colors from './colors';
|
||||
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import * as cr from 'vs/platform/theme/common/colorRegistry';
|
||||
import { attachStyler } from 'vs/platform/theme/common/styler';
|
||||
import { attachStyler, IColorMapping, IStyleOverrides } from 'vs/platform/theme/common/styler';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IThemable } from 'vs/base/common/styler';
|
||||
|
||||
@@ -29,7 +29,7 @@ export function attachDropdownStyler(widget: IThemable, themeService: IThemeServ
|
||||
buttonBackground: (style && style.buttonBackground) || cr.buttonBackground,
|
||||
buttonHoverBackground: (style && style.buttonHoverBackground) || cr.buttonHoverBackground,
|
||||
buttonBorder: cr.contrastBorder,
|
||||
buttonFocusOutline: (style && style.buttonFocusOutline) || sqlcolors.buttonFocusOutline
|
||||
buttonFocusOutline: (style && style.buttonFocusOutline) || colors.buttonFocusOutline
|
||||
}, widget);
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ export function attachInputBoxStyler(widget: IThemable, themeService: IThemeServ
|
||||
return attachStyler(themeService, {
|
||||
inputBackground: (style && style.inputBackground) || cr.inputBackground,
|
||||
inputForeground: (style && style.inputForeground) || cr.inputForeground,
|
||||
disabledInputBackground: (style && style.disabledInputBackground) || sqlcolors.disabledInputBackground,
|
||||
disabledInputForeground: (style && style.disabledInputForeground) || sqlcolors.disabledInputForeground,
|
||||
disabledInputBackground: (style && style.disabledInputBackground) || colors.disabledInputBackground,
|
||||
disabledInputForeground: (style && style.disabledInputForeground) || colors.disabledInputForeground,
|
||||
inputBorder: (style && style.inputBorder) || cr.inputBorder,
|
||||
inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder,
|
||||
inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground,
|
||||
@@ -88,8 +88,8 @@ export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeSer
|
||||
selectListBackground: (style && style.selectListBackground) || cr.selectListBackground,
|
||||
selectForeground: (style && style.selectForeground) || cr.selectForeground,
|
||||
selectBorder: (style && style.selectBorder) || cr.selectBorder,
|
||||
disabledSelectBackground: (style && style.disabledSelectBackground) || sqlcolors.disabledInputBackground,
|
||||
disabledSelectForeground: (style && style.disabledSelectForeground) || sqlcolors.disabledInputForeground,
|
||||
disabledSelectBackground: (style && style.disabledSelectBackground) || colors.disabledInputBackground,
|
||||
disabledSelectForeground: (style && style.disabledSelectForeground) || colors.disabledInputForeground,
|
||||
inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder,
|
||||
inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground,
|
||||
inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder,
|
||||
@@ -156,7 +156,7 @@ export function attachTableStyler(widget: IThemable, themeService: IThemeService
|
||||
listFocusForeground: (style && style.listFocusForeground) || cr.listFocusForeground,
|
||||
listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || cr.listActiveSelectionBackground,
|
||||
listActiveSelectionForeground: (style && style.listActiveSelectionForeground) || cr.listActiveSelectionForeground,
|
||||
listFocusAndSelectionBackground: style && style.listFocusAndSelectionBackground || sqlcolors.listFocusAndSelectionBackground,
|
||||
listFocusAndSelectionBackground: style && style.listFocusAndSelectionBackground || colors.listFocusAndSelectionBackground,
|
||||
listFocusAndSelectionForeground: (style && style.listFocusAndSelectionForeground) || cr.listActiveSelectionForeground,
|
||||
listInactiveFocusBackground: (style && style.listInactiveFocusBackground),
|
||||
listInactiveSelectionBackground: (style && style.listInactiveSelectionBackground) || cr.listInactiveSelectionBackground,
|
||||
@@ -168,11 +168,60 @@ export function attachTableStyler(widget: IThemable, themeService: IThemeService
|
||||
listSelectionOutline: (style && style.listSelectionOutline) || cr.activeContrastBorder,
|
||||
listHoverOutline: (style && style.listHoverOutline) || cr.activeContrastBorder,
|
||||
listInactiveFocusOutline: style && style.listInactiveFocusOutline,
|
||||
tableHeaderBackground: (style && style.tableHeaderBackground) || sqlcolors.tableHeaderBackground,
|
||||
tableHeaderForeground: (style && style.tableHeaderForeground) || sqlcolors.tableHeaderForeground
|
||||
tableHeaderBackground: (style && style.tableHeaderBackground) || colors.tableHeaderBackground,
|
||||
tableHeaderForeground: (style && style.tableHeaderForeground) || colors.tableHeaderForeground
|
||||
}, widget);
|
||||
}
|
||||
|
||||
export interface ITableStyleOverrides extends IStyleOverrides {
|
||||
listFocusBackground?: cr.ColorIdentifier,
|
||||
listFocusForeground?: cr.ColorIdentifier,
|
||||
listActiveSelectionBackground?: cr.ColorIdentifier,
|
||||
listActiveSelectionForeground?: cr.ColorIdentifier,
|
||||
listFocusAndSelectionBackground?: cr.ColorIdentifier,
|
||||
listFocusAndSelectionForeground?: cr.ColorIdentifier,
|
||||
listInactiveFocusBackground?: cr.ColorIdentifier,
|
||||
listInactiveSelectionBackground?: cr.ColorIdentifier,
|
||||
listInactiveSelectionForeground?: cr.ColorIdentifier,
|
||||
listHoverBackground?: cr.ColorIdentifier,
|
||||
listHoverForeground?: cr.ColorIdentifier,
|
||||
listDropBackground?: cr.ColorIdentifier,
|
||||
listFocusOutline?: cr.ColorIdentifier,
|
||||
listInactiveFocusOutline?: cr.ColorIdentifier,
|
||||
listSelectionOutline?: cr.ColorIdentifier,
|
||||
listHoverOutline?: cr.ColorIdentifier,
|
||||
tableHeaderBackground?: cr.ColorIdentifier,
|
||||
tableHeaderForeground?: cr.ColorIdentifier,
|
||||
cellOutlineColor?: cr.ColorIdentifier,
|
||||
tableHeaderAndRowCountColor?: cr.ColorIdentifier
|
||||
}
|
||||
|
||||
export function attachHighPerfTableStyler(widget: IThemable, themeService: IThemeService, overrides?: IColorMapping): IDisposable {
|
||||
return attachStyler(themeService, { ...defaultHighPerfTableStyles, ...(overrides || {}) }, widget);
|
||||
}
|
||||
|
||||
export const defaultHighPerfTableStyles: IColorMapping = {
|
||||
listFocusBackground: cr.listFocusBackground,
|
||||
listFocusForeground: cr.listFocusForeground,
|
||||
listActiveSelectionBackground: cr.listActiveSelectionBackground,
|
||||
listActiveSelectionForeground: cr.listActiveSelectionForeground,
|
||||
listFocusAndSelectionBackground: colors.listFocusAndSelectionBackground,
|
||||
listFocusAndSelectionForeground: cr.listActiveSelectionForeground,
|
||||
listInactiveFocusBackground: cr.listInactiveFocusBackground,
|
||||
listInactiveSelectionBackground: cr.listInactiveSelectionBackground,
|
||||
listInactiveSelectionForeground: cr.listInactiveSelectionForeground,
|
||||
listHoverBackground: cr.listHoverBackground,
|
||||
listHoverForeground: cr.listHoverForeground,
|
||||
listDropBackground: cr.listDropBackground,
|
||||
listFocusOutline: cr.activeContrastBorder,
|
||||
listSelectionOutline: cr.activeContrastBorder,
|
||||
listHoverOutline: cr.activeContrastBorder,
|
||||
tableHeaderBackground: colors.tableHeaderBackground,
|
||||
tableHeaderForeground: colors.tableHeaderForeground,
|
||||
cellOutlineColor: colors.tableCellOutline,
|
||||
tableHeaderAndRowCountColor: colors.tableCellOutline
|
||||
};
|
||||
|
||||
export function attachEditableDropdownStyler(widget: IThemable, themeService: IThemeService, style?: {
|
||||
listFocusBackground?: cr.ColorIdentifier,
|
||||
listFocusForeground?: cr.ColorIdentifier,
|
||||
@@ -245,13 +294,13 @@ export function attachButtonStyler(widget: IThemable, themeService: IThemeServic
|
||||
buttonBackground: (style && style.buttonBackground) || cr.buttonBackground,
|
||||
buttonHoverBackground: (style && style.buttonHoverBackground) || cr.buttonHoverBackground,
|
||||
buttonBorder: cr.contrastBorder,
|
||||
buttonFocusOutline: (style && style.buttonFocusOutline) || sqlcolors.buttonFocusOutline
|
||||
buttonFocusOutline: (style && style.buttonFocusOutline) || colors.buttonFocusOutline
|
||||
}, widget);
|
||||
}
|
||||
|
||||
export function attachCheckboxStyler(widget: IThemable, themeService: IThemeService, style?: { disabledCheckboxForeground?: cr.ColorIdentifier })
|
||||
: IDisposable {
|
||||
return attachStyler(themeService, {
|
||||
disabledCheckboxForeground: (style && style.disabledCheckboxForeground) || sqlcolors.disabledCheckboxForeground
|
||||
disabledCheckboxForeground: (style && style.disabledCheckboxForeground) || colors.disabledCheckboxForeground
|
||||
}, widget);
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ class DataResourceTable extends GridTableBase<any> {
|
||||
@IUntitledTextEditorService untitledEditorService: IUntitledTextEditorService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super(state, createResultSet(source), contextMenuService, instantiationService, editorService, untitledEditorService, configurationService);
|
||||
super(state, createResultSet(source), undefined, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService);
|
||||
this._gridDataProvider = this.instantiationService.createInstance(DataResourceDataProvider, source, this.resultSet, this.cellModel.notebookModel.notebookUri.toString());
|
||||
this._chart = this.instantiationService.createInstance(ChartView, false);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import 'vs/css!./media/gridPanel';
|
||||
|
||||
import { ITableStyles, ITableMouseEvent } from 'sql/base/browser/ui/table/interfaces';
|
||||
import { attachTableStyler } from 'sql/platform/theme/common/styler';
|
||||
import QueryRunner, { QueryGridDataProvider } from 'sql/workbench/services/query/common/queryRunner';
|
||||
import { ResultSetSummary, IColumn } from 'sql/workbench/services/query/common/query';
|
||||
@@ -17,9 +18,9 @@ import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelect
|
||||
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
|
||||
import { escape } from 'sql/base/common/strings';
|
||||
import { hyperLinkFormatter, textFormatter } from 'sql/base/browser/ui/table/formatters';
|
||||
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
|
||||
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
|
||||
import { ITableStyles, ITableMouseEvent } from 'sql/base/browser/ui/table/interfaces';
|
||||
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
|
||||
import { GridTable as HighPerfGridTable } from 'sql/workbench/contrib/query/browser/highPerfGridPanel';
|
||||
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -27,9 +28,8 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { range, find } from 'vs/base/common/arrays';
|
||||
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { Disposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { range, find } from 'vs/base/common/arrays';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { isInDOM, Dimension } from 'vs/base/browser/dom';
|
||||
@@ -47,6 +47,7 @@ import { SaveFormat } from 'sql/workbench/services/query/common/resultSerializer
|
||||
import { Progress } from 'vs/platform/progress/common/progress';
|
||||
import { ScrollableView, IView } from 'sql/base/browser/ui/scrollableView/scrollableView';
|
||||
import { IQueryEditorConfiguration } from 'sql/platform/query/common/query';
|
||||
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
|
||||
|
||||
const ROW_HEIGHT = 29;
|
||||
const HEADER_HEIGHT = 26;
|
||||
@@ -64,24 +65,25 @@ const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ES
|
||||
export class GridPanel extends Disposable {
|
||||
private container = document.createElement('div');
|
||||
private scrollableView: ScrollableView;
|
||||
private tables: GridTable<any>[] = [];
|
||||
private tables: Array<GridTable<any> | HighPerfGridTable<any>> = [];
|
||||
private tableDisposable = this._register(new DisposableStore());
|
||||
private queryRunnerDisposables = this._register(new DisposableStore());
|
||||
private currentHeight: number;
|
||||
|
||||
private runner: QueryRunner;
|
||||
|
||||
private maximizedGrid: GridTable<any>;
|
||||
private maximizedGrid: GridTable<any> | HighPerfGridTable<any>;
|
||||
private _state: GridPanelState | undefined;
|
||||
|
||||
private readonly optimized = this.configurationService.getValue<IQueryEditorConfiguration>('queryEditor').results.optimizedTable;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ILogService private readonly logService: ILogService
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
) {
|
||||
super();
|
||||
this.scrollableView = new ScrollableView(this.container);
|
||||
this.scrollableView = new ScrollableView(this.container, { scrollDebouce: this.optimized ? 0 : undefined });
|
||||
this.scrollableView.onDidScroll(e => {
|
||||
if (this.state && this.scrollableView.length !== 0) {
|
||||
this.state.scrollPosition = e.scrollTop;
|
||||
@@ -97,12 +99,7 @@ export class GridPanel extends Disposable {
|
||||
}
|
||||
|
||||
public layout(size: Dimension): void {
|
||||
this.scrollableView.layout(size.height);
|
||||
// if the size hasn't change it won't layout our table so we have to do it manually
|
||||
if (size.height === this.currentHeight) {
|
||||
this.tables.map(e => e.layout());
|
||||
}
|
||||
this.currentHeight = size.height;
|
||||
this.scrollableView.layout(size.height, size.width);
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
@@ -203,9 +200,9 @@ export class GridPanel extends Disposable {
|
||||
}
|
||||
|
||||
private addResultSet(resultSet: ResultSetSummary[]) {
|
||||
let tables: GridTable<any>[] = [];
|
||||
const tables: Array<GridTable<any> | HighPerfGridTable<any>> = [];
|
||||
|
||||
for (let set of resultSet) {
|
||||
for (const set of resultSet) {
|
||||
// ensure we aren't adding a resultSet that is already visible
|
||||
if (find(this.tables, t => t.resultSet.batchId === set.batchId && t.resultSet.id === set.id)) {
|
||||
continue;
|
||||
@@ -220,7 +217,12 @@ export class GridPanel extends Disposable {
|
||||
this.state.tableStates.push(tableState);
|
||||
}
|
||||
}
|
||||
let table = this.instantiationService.createInstance(GridTable, this.runner, set, tableState);
|
||||
let table: GridTable<any> | HighPerfGridTable<any>;
|
||||
if (this.optimized) {
|
||||
table = this.instantiationService.createInstance(HighPerfGridTable, this.runner, set, tableState);
|
||||
} else {
|
||||
table = this.instantiationService.createInstance(GridTable, this.runner, set, tableState);
|
||||
}
|
||||
this.tableDisposable.add(tableState.onMaximizedChange(e => {
|
||||
if (e) {
|
||||
this.maximizeTable(table.id);
|
||||
@@ -365,12 +367,12 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
|
||||
constructor(
|
||||
state: GridTableState,
|
||||
protected _resultSet: ResultSetSummary,
|
||||
protected contextMenuService: IContextMenuService,
|
||||
protected instantiationService: IInstantiationService,
|
||||
protected editorService: IEditorService,
|
||||
protected untitledEditorService: IUntitledTextEditorService,
|
||||
protected configurationService: IConfigurationService,
|
||||
private readonly options: IGridTableOptions = { actionOrientation: ActionsOrientation.VERTICAL }
|
||||
private readonly options: IGridTableOptions = { actionOrientation: ActionsOrientation.VERTICAL },
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IInstantiationService protected readonly instantiationService: IInstantiationService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IUntitledTextEditorService private readonly untitledEditorService: IUntitledTextEditorService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
) {
|
||||
super();
|
||||
let config = this.configurationService.getValue<{ rowHeight: number }>('resultsGrid');
|
||||
@@ -768,7 +770,7 @@ class GridTable<T> extends GridTableBase<T> {
|
||||
@IUntitledTextEditorService untitledEditorService: IUntitledTextEditorService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super(state, resultSet, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService);
|
||||
super(state, resultSet, undefined, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService);
|
||||
this._gridDataProvider = this.instantiationService.createInstance(QueryGridDataProvider, this._runner, resultSet.batchId, resultSet.id);
|
||||
}
|
||||
|
||||
|
||||
165
src/sql/workbench/contrib/query/browser/highPerfGridPanel.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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/optimizedGridPanel';
|
||||
|
||||
import { ITableRenderer, ITableColumn } from 'sql/base/browser/ui/table/highPerf/table';
|
||||
import { VirtualizedWindow } from 'sql/base/browser/ui/table/highPerf/virtualizedWindow';
|
||||
import { attachHighPerfTableStyler } from 'sql/platform/theme/common/styler';
|
||||
import QueryRunner from 'sql/workbench/services/query/common/queryRunner';
|
||||
import { GridTableState } from 'sql/workbench/common/editor/query/gridTableState';
|
||||
import { ResultSetSummary } from 'sql/workbench/services/query/common/query';
|
||||
|
||||
import { append, $ } from 'vs/base/browser/dom';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { WorkbenchTable } from 'sql/platform/table/browser/tableService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IView } from 'sql/base/browser/ui/scrollableView/scrollableView';
|
||||
|
||||
type ICellTemplate = HTMLElement;
|
||||
|
||||
class TableFormatter<T> implements ITableRenderer<T, ICellTemplate> {
|
||||
renderTemplate(container: HTMLElement): ICellTemplate {
|
||||
return append(container, $('.cell'));
|
||||
}
|
||||
|
||||
renderCell(element: T, index: number, cellIndex: number, columnId: string, templateData: ICellTemplate, width: number): void {
|
||||
templateData.innerText = element[columnId];
|
||||
}
|
||||
|
||||
disposeCell?(element: T, index: number, cellIndex: number, columnId: string, templateData: ICellTemplate, width: number): void {
|
||||
templateData.innerText = '';
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: ICellTemplate): void {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const ROW_HEIGHT = 22;
|
||||
const HEADER_HEIGHT = 22;
|
||||
const MIN_GRID_HEIGHT_ROWS = 8;
|
||||
const ESTIMATED_SCROLL_BAR_SIZE = 10;
|
||||
const BOTTOM_PADDING = 15;
|
||||
|
||||
// this handles min size if rows is greater than the min grid visible rows
|
||||
const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_SIZE;
|
||||
|
||||
export class GridTable<T> extends Disposable implements IView {
|
||||
|
||||
private _onDidChange = new Emitter<number>();
|
||||
public readonly onDidChange: Event<number> = this._onDidChange.event;
|
||||
|
||||
private virtWindow: VirtualizedWindow<T>;
|
||||
private table: WorkbenchTable<T>;
|
||||
private tableContainer: HTMLElement;
|
||||
private columns: ITableColumn<T, ICellTemplate>[];
|
||||
|
||||
public id = generateUuid();
|
||||
readonly element = $('.grid-panel.optimized');
|
||||
|
||||
private _state: GridTableState;
|
||||
|
||||
private rowHeight: number;
|
||||
|
||||
public get resultSet(): ResultSetSummary {
|
||||
return this._resultSet;
|
||||
}
|
||||
|
||||
// this handles if the row count is small, like 4-5 rows
|
||||
private get maxSize(): number {
|
||||
return ((this.resultSet.rowCount) * this.rowHeight) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_SIZE;
|
||||
}
|
||||
|
||||
// worthless for this table
|
||||
public isOnlyTable: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly runner: QueryRunner,
|
||||
private _resultSet: ResultSetSummary,
|
||||
state: GridTableState,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
this.tableContainer = append(this.element, $('.table-panel'));
|
||||
let config = this.configurationService.getValue<{ rowHeight: number }>('resultsGrid');
|
||||
this.rowHeight = config && config.rowHeight ? config.rowHeight : ROW_HEIGHT;
|
||||
this.state = state;
|
||||
|
||||
this.columns = this.resultSet.columnInfo.map<ITableColumn<T, any>>((c, i) => ({
|
||||
id: i.toString(),
|
||||
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
|
||||
? 'XML Showplan'
|
||||
: escape(c.columnName),
|
||||
renderer: new TableFormatter(),
|
||||
width: this.state.columnSizes && this.state.columnSizes[i] ? this.state.columnSizes[i] : undefined
|
||||
}));
|
||||
|
||||
this.virtWindow = new VirtualizedWindow<T>(50, this.resultSet.rowCount, (offset, count) => {
|
||||
return Promise.resolve(this.runner.getQueryRows(offset, count, this._resultSet.batchId, this._resultSet.id).then(r => {
|
||||
return r.rows.map(c => c.reduce((p, c, i) => {
|
||||
p[this.columns[i].id] = c.displayValue;
|
||||
return p;
|
||||
}, Object.create(null)));
|
||||
}));
|
||||
});
|
||||
|
||||
this.table = this._register(this.instantiationService.createInstance(WorkbenchTable, 'gridPanel', this.tableContainer, this.columns, {
|
||||
getRow: index => this.virtWindow.getIndex(index)
|
||||
}, { rowHeight: this.rowHeight, headerHeight: HEADER_HEIGHT, rowCountColumn: false }) as WorkbenchTable<T>);
|
||||
|
||||
this.table.length = this.resultSet.rowCount;
|
||||
|
||||
this._register(attachHighPerfTableStyler(this.table, this.themeService));
|
||||
}
|
||||
|
||||
public get state(): GridTableState {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public set state(val: GridTableState) {
|
||||
this._state = val;
|
||||
}
|
||||
|
||||
public updateResult(resultSet: ResultSetSummary) {
|
||||
this._resultSet = resultSet;
|
||||
if (this.table) {
|
||||
this.virtWindow.length = resultSet.rowCount;
|
||||
this.table.length = resultSet.rowCount;
|
||||
}
|
||||
this._onDidChange.fire(undefined);
|
||||
}
|
||||
|
||||
public layout(height: number, width: number): void {
|
||||
this.tableContainer.style.width = `${width - ESTIMATED_SCROLL_BAR_SIZE}px`;
|
||||
this.table.layout(height, width - ESTIMATED_SCROLL_BAR_SIZE);
|
||||
}
|
||||
|
||||
public get minimumSize(): number {
|
||||
// clamp between ensuring we can show the actionbar, while also making sure we don't take too much space
|
||||
// if there is only one table then allow a minimum size of ROW_HEIGHT
|
||||
return Math.max(Math.min(this.maxSize, MIN_GRID_HEIGHT), BOTTOM_PADDING);
|
||||
}
|
||||
|
||||
public get maximumSize(): number {
|
||||
return Math.max(this.maxSize, BOTTOM_PADDING);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this.element.remove();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public style(): void {
|
||||
}
|
||||
|
||||
public focus() { }
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .binarydiff-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.monaco-workbench .binarydiff-right {
|
||||
border-left: 3px solid #DDD;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .binarydiff-right {
|
||||
border-left: 3px solid rgb(20, 20, 20);
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black .binarydiff-right {
|
||||
border-left: 3px solid #6FC3DF;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle fill="#C5C5C5" cx="8" cy="8" r="4"/></svg>
|
||||
|
Before Width: | Height: | Size: 167 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle fill="#424242" cx="8" cy="8" r="4"/></svg>
|
||||
|
Before Width: | Height: | Size: 167 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#e8e8e8" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
|
||||
|
Before Width: | Height: | Size: 307 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#424242" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
|
||||
|
Before Width: | Height: | Size: 307 B |
@@ -1,152 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench.vs > .editor > .content.drag {
|
||||
background-color: #ECECEC;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .editor > .content.drag {
|
||||
background-color: #2D2D2D;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.dragging > .monaco-sash {
|
||||
display: none; /* hide sashes while dragging editors around */
|
||||
}
|
||||
|
||||
#monaco-workbench-editor-move-overlay,
|
||||
#monaco-workbench-editor-drop-overlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
#monaco-workbench-editor-drop-overlay {
|
||||
opacity: 0; /* initially not visible until moving around */
|
||||
}
|
||||
|
||||
.vs #monaco-workbench-editor-drop-overlay,
|
||||
.monaco-workbench.vs > .editor.empty > .content.dropfeedback {
|
||||
background-color: rgba(51,153,255, 0.18);
|
||||
}
|
||||
|
||||
.vs-dark #monaco-workbench-editor-drop-overlay,
|
||||
.monaco-workbench.vs-dark > .editor.empty > .content.dropfeedback {
|
||||
background-color: rgba(83, 89, 93, 0.5);
|
||||
}
|
||||
|
||||
.hc-black #monaco-workbench-editor-drop-overlay,
|
||||
.monaco-workbench.hc-black > .editor.empty > .content.dropfeedback {
|
||||
background: none !important;
|
||||
outline: 2px dashed #f38518;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content > .one-editor-silo {
|
||||
position: absolute;
|
||||
box-sizing: border-box; /* use border box to be able to draw a border as separator between editors */
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content > .one-editor-silo.editor-one {
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content > .one-editor-silo.dragging {
|
||||
z-index: 70;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .editor > .content.vertical-layout > .one-editor-silo.dragging {
|
||||
border-left: 1px solid #E7E7E7;
|
||||
border-right: 1px solid #E7E7E7;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .editor > .content.horizontal-layout > .one-editor-silo.dragging {
|
||||
border-top: 1px solid #E7E7E7;
|
||||
border-bottom: 1px solid #E7E7E7;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .editor > .content.vertical-layout > .one-editor-silo.editor-two,
|
||||
.monaco-workbench.vs > .editor > .content.vertical-layout > .one-editor-silo.editor-three {
|
||||
border-left: 1px solid #E7E7E7;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .editor > .content.horizontal-layout > .one-editor-silo.editor-two,
|
||||
.monaco-workbench.vs > .editor > .content.horizontal-layout > .one-editor-silo.editor-three {
|
||||
border-top: 1px solid #E7E7E7;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .editor > .content.vertical-layout > .one-editor-silo.dragging {
|
||||
border-left: 1px solid #444;
|
||||
border-right: 1px solid #444;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .editor > .content.horizontal-layout > .one-editor-silo.dragging {
|
||||
border-top: 1px solid #444;
|
||||
border-bottom: 1px solid #444;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .editor > .content.vertical-layout > .one-editor-silo.editor-two,
|
||||
.monaco-workbench.vs-dark > .editor > .content.vertical-layout > .one-editor-silo.editor-three {
|
||||
border-left: 1px solid #444;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .editor > .content.horizontal-layout > .one-editor-silo.editor-two,
|
||||
.monaco-workbench.vs-dark > .editor > .content.horizontal-layout > .one-editor-silo.editor-three {
|
||||
border-top: 1px solid #444;
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black > .editor > .content.vertical-layout > .one-editor-silo.dragging {
|
||||
border-left: 1px solid #6FC3DF;
|
||||
border-right: 1px solid #6FC3DF;
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black > .editor > .content.horizontal-layout > .one-editor-silo.dragging {
|
||||
border-top: 1px solid #6FC3DF;
|
||||
border-bottom: 1px solid #6FC3DF;
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black > .editor > .content.vertical-layout > .one-editor-silo.editor-two,
|
||||
.monaco-workbench.hc-black > .editor > .content.vertical-layout > .one-editor-silo.editor-three {
|
||||
border-left: 1px solid #6FC3DF;
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black > .editor > .content.horizontal-layout > .one-editor-silo.editor-two,
|
||||
.monaco-workbench.hc-black > .editor > .content.horizontal-layout > .one-editor-silo.editor-three {
|
||||
border-top: 1px solid #6FC3DF;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.draggedunder {
|
||||
transition: left 200ms ease-out;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.vertical-layout > .editor-three.draggedunder {
|
||||
transition-property: right;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.draggedunder {
|
||||
transition: top 200ms ease-out;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.horizontal-layout > .editor-three.draggedunder {
|
||||
transition-property: bottom;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content > .one-editor-silo > .container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content > .one-editor-silo > .container > .editor-container {
|
||||
height: calc(100% - 35px); /* Editor is below editor title */
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench.vs .monaco-editor-background {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .monaco-editor-background {
|
||||
background-color: #1E1E1E;
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black .monaco-editor-background {
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor {
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor.empty {
|
||||
background-image: url('letterpress.svg');
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .part.editor.empty {
|
||||
background-image: url('letterpress-dark.svg');
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black .part.editor.empty {
|
||||
background-image: url('letterpress-hc.svg');
|
||||
}
|
||||
|
||||
@media
|
||||
(-webkit-min-device-pixel-ratio: 2),
|
||||
(min-resolution: 192dppx) {
|
||||
.monaco-workbench .part.editor {
|
||||
background-size: 260px 260px;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .editor-statusbar-item > a:not(:first-child) {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-statusbar-item > .editor-status-mode,
|
||||
.monaco-workbench .editor-statusbar-item > .editor-status-encoding,
|
||||
.monaco-workbench .editor-statusbar-item > .editor-status-eol,
|
||||
.monaco-workbench .editor-statusbar-item > .editor-status-selection,
|
||||
.monaco-workbench .editor-statusbar-item > .editor-status-indentation,
|
||||
.monaco-workbench .editor-statusbar-item > .editor-status-metadata {
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-statusbar-item > .editor-status-metadata {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-statusbar-item > .editor-status-tabfocusmode {
|
||||
padding: 0 5px 0 5px;
|
||||
background-color: brown !important;
|
||||
}
|
||||
@@ -13,3 +13,8 @@
|
||||
width : 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.grid-panel .cell {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" width="260" height="260" viewBox="0 0 260 260"><style>.st0{opacity:.25}.st1{opacity:3e-2}.st2{fill:#fff}</style><path d="M194 2L92.4 103.6 27.6 53.2 2 66v128l25.6 12.8 64.8-50.4L194 258l64-25.6V27.6L194 2zM27 169V91l39 39-39 39zm99.4-39L194 77v106l-67.6-53z" class="st0"/><path class="st1 st2" d="M194 2l64 25.6v204.8L194 258 92.4 156.4l-64.8 50.4L2 194V66l25.6-12.8 64.8 50.4L194 2m0 181V77l-67.6 53 67.6 53M27 169l39-39-39-39v78M193.8.8l-.5.5-101 101-64.1-49.9-.5-.4-.6.3L1.6 65.1l-.6.3v129.2l.6.3 25.6 12.8.6.3.5-.4 64.1-49.9 101 101 .5.5.6-.2 64-25.6.6-.3V26.9l-.6-.3-64-25.6-.7-.2zM128 130l65-50.9V181l-65-51zM28 166.6V93.4L64.6 130 28 166.6z"/></svg>
|
||||
|
Before Width: | Height: | Size: 709 B |
@@ -1 +0,0 @@
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" width="260" height="260" viewBox="0 0 260 260"><style>.st0{fill:#fff;fill-opacity:.13;enable-background:new}</style><path class="st0" d="M194 2L92.4 103.6 27.6 53.2 2 66v128l25.6 12.8 64.8-50.4L194 258l64-25.6V27.6L194 2zM27 169V91l39 39-39 39zm99.4-39L194 77v106l-67.6-53z"/></svg>
|
||||
|
Before Width: | Height: | Size: 335 B |
@@ -1 +0,0 @@
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" width="260" height="260" viewBox="0 0 260 260"><style>.st0{opacity:.1}.st1{opacity:5e-2}.st2{fill:#231f20}</style><path d="M194 2L92.4 103.6 27.6 53.2 2 66v128l25.6 12.8 64.8-50.4L194 258l64-25.6V27.6L194 2zM27 169V91l39 39-39 39zm99.4-39L194 77v106l-67.6-53z" class="st0"/><path class="st1 st2" d="M194 2l64 25.6v204.8L194 258 92.4 156.4l-64.8 50.4L2 194V66l25.6-12.8 64.8 50.4L194 2m0 181V77l-67.6 53 67.6 53M27 169l39-39-39-39v78M193.8.8l-.5.5-101 101-64.1-49.9-.5-.4-.6.3L1.6 65.1l-.6.3v129.2l.6.3 25.6 12.8.6.3.5-.4 64.1-49.9 101 101 .5.5.6-.2 64-25.6.6-.3V26.9l-.6-.3-64-25.6-.7-.2zM128 130l65-50.9V181l-65-51zM28 166.6V93.4L64.6 130 28 166.6z"/></svg>
|
||||
|
Before Width: | Height: | Size: 711 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-1 -3 16 16" enable-background="new -1 -3 16 16"><path fill="#C5C5C5" d="M1 4h7l-3-3h3l4 4-4 4h-3l3-3h-7v-2z"/></svg>
|
||||
|
Before Width: | Height: | Size: 189 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-1 -3 16 16" enable-background="new -1 -3 16 16"><path fill="#656565" d="M1 4h7l-3-3h3l4 4-4 4h-3l3-3h-7v-2z"/></svg>
|
||||
|
Before Width: | Height: | Size: 189 B |
@@ -1,38 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Title Label */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label {
|
||||
line-height: 35px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
position: relative;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .monaco-icon-label::before {
|
||||
height: 35px; /* tweak the icon size of the editor labels when icons are enabled */
|
||||
}
|
||||
|
||||
/* Title Actions */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions {
|
||||
display: flex;
|
||||
flex: initial;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .title-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action {
|
||||
background: url('close-dirty.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action,
|
||||
.monaco-workbench.hc-black > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action {
|
||||
background: url('close-dirty-inverse.svg') center center no-repeat;
|
||||
}
|
||||
@@ -3,6 +3,12 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry.editor-preview {
|
||||
font-style: italic;
|
||||
.grid-panel.optimized {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.grid-panel.optimized > .table-panel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>ParseQuery_16x</title><rect width="16" height="16" fill="#f6f6f6" opacity="0"/><polygon points="4.382 15 0.382 7 5.618 7 6.5 8.764 10.382 1 15.618 1 8.618 15 4.382 15" fill="#f6f6f6"/><polygon points="11 2 6.5 11 5 8 2 8 5 14 8 14 14 2 11 2" fill="#424242"/></svg>
|
||||
|
Before Width: | Height: | Size: 331 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-1 -3 16 16" enable-background="new -1 -3 16 16"><polygon fill="#C5C5C5" points="13,4 6,4 9,1 6,1 2,5 6,9 9,9 6,6 13,6"/></svg>
|
||||
|
Before Width: | Height: | Size: 199 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-1 -3 16 16" enable-background="new -1 -3 16 16"><polygon fill="#656565" points="13,4 6,4 9,1 6,1 2,5 6,9 9,9 6,6 13,6"/></svg>
|
||||
|
Before Width: | Height: | Size: 199 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1,.cls-2{fill:#f6f6f6;}.cls-1{opacity:0;}.cls-3{fill:#388a34;}.cls-4{fill:#f0eff1;}</style></defs><title>StartWithoutDebug@2x</title><g id="Layer_2" data-name="Layer 2"><g id="outline"><rect class="cls-1" width="16" height="16"/><path class="cls-2" d="M3,0,13.67,8,3,16Z"/></g><g id="color_action"><path class="cls-3" d="M6,6,8.67,8,6,10V6M4,2V14l8-6L4,2Z"/></g><g id="icon_fg"><path class="cls-4" d="M8.67,8,6,10V6Z"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 511 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2b282e}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 15H0V1h16v14z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M1 2v12h14V2H1zm13 11H2v-3h12v3zm0-5H2V5h12v3z" id="iconBg"/><g id="iconFg" style="display: none;"><path class="icon-vs-fg" d="M14 8H2V5h12v3zm0 2H2v3h12v-3z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 578 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 15H0V1h16v14z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M1 2v12h14V2H1zm13 11H2v-3h12v3zm0-5H2V5h12v3z" id="iconBg"/><g id="iconFg" style="display: none;"><path class="icon-vs-fg" d="M14 8H2V5h12v3zm0 2H2v3h12v-3z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 578 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 -1 16 16" enable-background="new 0 -1 16 16"><path fill="#C5C5C5" d="M1 1v12h14v-12h-14zm1 3h4.999v8h-4.999v-8zm12 8h-5.001v-8h5.001v8z"/></svg>
|
||||
|
Before Width: | Height: | Size: 218 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 -1 16 16" enable-background="new 0 -1 16 16"><path fill="#656565" d="M1 1v12h14v-12h-14zm1 3h4.999v8h-4.999v-8zm12 8h-5.001v-8h5.001v8z"/></svg>
|
||||
|
Before Width: | Height: | Size: 218 B |
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#C5C5C5;}
|
||||
</style>
|
||||
<g id="outline">
|
||||
</g>
|
||||
<g id="icon_x5F_bg">
|
||||
<path class="st0" d="M7.6,5H3c0,0-1,0-1,1c0,0.8,0,5.4,0,8c0,1,1,1,1,1s1.5,0,3,0s3,0,3,0s1,0,1-1c0-2.6,0-6.9,0-6.9L7.6,5z M9,14
|
||||
H3V6h4v2h2V14z"/>
|
||||
<path class="st0" d="M9.6,3H5c0,0-1,0-1,1h5v0.9L10.2,6H11v7c1,0,1-1,1-1V5.1L9.6,3z"/>
|
||||
<path class="st0" d="M11.6,1H7c0,0-1,0-1,1h5v0.9L12.2,4H13v7c1,0,1-1,1-1V3.1L11.6,1z"/>
|
||||
</g>
|
||||
<g id="color_x5F_action">
|
||||
</g>
|
||||
<g id="icon_x5F_fg">
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 822 B |
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#656565;}
|
||||
</style>
|
||||
<g id="outline">
|
||||
</g>
|
||||
<g id="icon_x5F_bg">
|
||||
<path class="st0" d="M7.6,5H3c0,0-1,0-1,1c0,0.8,0,5.4,0,8c0,1,1,1,1,1s1.5,0,3,0s3,0,3,0s1,0,1-1c0-2.6,0-6.9,0-6.9L7.6,5z M9,14
|
||||
H3V6h4v2h2V14z"/>
|
||||
<path class="st0" d="M9.6,3H5c0,0-1,0-1,1h5v0.9L10.2,6H11v7c1,0,1-1,1-1V5.1L9.6,3z"/>
|
||||
<path class="st0" d="M11.6,1H7c0,0-1,0-1,1h5v0.9L12.2,4H13v7c1,0,1-1,1-1V3.1L11.6,1z"/>
|
||||
</g>
|
||||
<g id="color_x5F_action">
|
||||
</g>
|
||||
<g id="icon_x5F_fg">
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 822 B |
@@ -1,220 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Title Container */
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title.tabs {
|
||||
background: #F3F3F3;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title.tabs {
|
||||
background: #252526;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.tabs > .monaco-scrollable-element {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.tabs > .monaco-scrollable-element .scrollbar {
|
||||
z-index: 3; /* on top of tabs */
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Tabs Container */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container {
|
||||
display: flex;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container.scroll {
|
||||
overflow: scroll !important;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Tab */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab {
|
||||
display: flex;
|
||||
width: 120px;
|
||||
min-width: fit-content;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
height: 35px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:not(.active) {
|
||||
background-color: #ECECEC;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:not(.active) {
|
||||
background-color: #2D2D2D;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab {
|
||||
border-left-color: #F3F3F3;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active:last-child {
|
||||
border-right-color: #F3F3F3;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab {
|
||||
border-left-color: #252526;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active:last-child {
|
||||
border-right-color: #252526;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:first-child,
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:first-child {
|
||||
border-left-color: transparent;
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab {
|
||||
border-left-color: #6FC3DF;
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active {
|
||||
outline: 2px solid #f38518;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container.dropfeedback,
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dropfeedback {
|
||||
background-color: #DDECFF;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container.dropfeedback,
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dropfeedback {
|
||||
background-color: #383B3D;
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container.dropfeedback,
|
||||
.monaco-workbench.hc-black > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dropfeedback {
|
||||
background: none !important;
|
||||
outline: 2px dashed #f38518;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* Tab Label */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .monaco-icon-label::before {
|
||||
height: 16px; /* tweak the icon size of the editor labels when icons are enabled */
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label {
|
||||
opacity: 0.7 !important;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label {
|
||||
opacity: 0.5 !important;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active .tab-label,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dropfeedback .tab-label {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
/* Tab Close */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab > .tab-close {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
width: 28px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button > .tab-close {
|
||||
display: none; /* hide the close action bar when we are configured to hide it */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.active > .tab-close .action-label, /* always show it for active tab */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab > .tab-close .action-label:focus, /* always show it on focus */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab:hover > .tab-close .action-label, /* always show it on hover */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.active:hover > .tab-close .action-label, /* always show it on hover */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.dirty > .tab-close .action-label { /* always show it for dirty tabs */
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active > .tab-close .action-label, /* show dimmed for inactive group */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* show dimmed for inactive group */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty > .tab-close .action-label, /* show dimmed for inactive group */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:hover > .tab-close .action-label { /* show dimmed for inactive group */
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab > .tab-close .action-label {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background-size: 16px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action {
|
||||
background: url('close-dirty.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action,
|
||||
.monaco-workbench.hc-black > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action {
|
||||
background: url('close-dirty-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover {
|
||||
background: url('close.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover,
|
||||
.monaco-workbench.hc-black > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover {
|
||||
background: url('close-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
/* No Tab Close Button */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button {
|
||||
padding-right: 28px; /* make room for dirty indication when we are running without close button */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button.dirty {
|
||||
background-repeat: no-repeat;
|
||||
background-position-y: center;
|
||||
background-position-x: calc(100% - 6px); /* to the right of the tab label */
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button.dirty {
|
||||
background-image: url('close-dirty.svg');
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button.dirty,
|
||||
.monaco-workbench.hc-black > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button.dirty {
|
||||
background-image: url('close-dirty-inverse.svg');
|
||||
}
|
||||
|
||||
/* Editor Actions */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions {
|
||||
cursor: default;
|
||||
flex: initial;
|
||||
padding-left: 4px;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench.vs .textdiff-editor-action.next {
|
||||
background: url('next-diff.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs .textdiff-editor-action.previous {
|
||||
background: url('previous-diff.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .textdiff-editor-action.next,
|
||||
.monaco-workbench.hc-black .textdiff-editor-action.next {
|
||||
background: url('next-diff-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .textdiff-editor-action.previous,
|
||||
.monaco-workbench.hc-black .textdiff-editor-action.previous {
|
||||
background: url('previous-diff-inverse.svg') center center no-repeat;
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Editor Label */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label {
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a {
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .monaco-icon-label::before,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .monaco-icon-label::before,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label span,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label span {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .title-label a,
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a {
|
||||
color: rgba(51, 51, 51, 0.5);
|
||||
}
|
||||
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title.active .title-label a,
|
||||
.monaco-workbench.vs > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab .tab-label a {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .title-label a,
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title.active .title-label a,
|
||||
.monaco-workbench.vs-dark > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab .tab-label a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Title Actions */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label {
|
||||
display: block;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
min-width: 28px;
|
||||
background-size: 16px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.hc-black > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label,
|
||||
.monaco-workbench.hc-black > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label {
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label .label,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label .label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Drag Cursor */
|
||||
.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo > .container > .title,
|
||||
.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo > .container > .title.tabs .scrollbar .slider,
|
||||
.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo > .container > .title .monaco-icon-label::before,
|
||||
.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo > .container > .title .title-label a,
|
||||
.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo > .container > .title .title-label span {
|
||||
cursor: -webkit-grab;
|
||||
}
|
||||
|
||||
#monaco-workbench-editor-move-overlay,
|
||||
.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag,
|
||||
.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag > .container > .title,
|
||||
.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag > .container > .title.tabs .scrollbar .slider,
|
||||
.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag > .container > .title .monaco-icon-label::before,
|
||||
.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag > .container > .title .title-label a,
|
||||
.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag > .container > .title .title-label span {
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
|
||||
.monaco-workbench .close-editor-action {
|
||||
background: url('close.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .close-editor-action,
|
||||
.monaco-workbench.hc-black .close-editor-action {
|
||||
background: url('close-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action {
|
||||
background: url('split-editor-vertical.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action,
|
||||
.monaco-workbench.hc-black > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action {
|
||||
background: url('split-editor-vertical-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action {
|
||||
background: url('split-editor-horizontal.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action,
|
||||
.monaco-workbench.hc-black > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action {
|
||||
background: url('split-editor-horizontal-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench .show-group-editors-action {
|
||||
background: url('stackview.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .show-group-editors-action,
|
||||
.monaco-workbench.hc-black .show-group-editors-action {
|
||||
background: url('stackview-inverse.svg') center center no-repeat;
|
||||
}
|
||||
@@ -365,6 +365,11 @@ const queryEditorConfiguration: IConfigurationNode = {
|
||||
'description': localize('queryEditor.results.copyRemoveNewLine', "Configuration options for copying multi-line results from the Results View"),
|
||||
'default': true
|
||||
},
|
||||
'queryEditor.results.optimizedTable': {
|
||||
'type': 'boolean',
|
||||
'description': localize('queryEditor.results.optimizedTable', "(Experimental) Use a optimized table in the results out. Some functionality might be missing and in the works."),
|
||||
'default': false
|
||||
},
|
||||
'queryEditor.messages.showBatchTime': {
|
||||
'type': 'boolean',
|
||||
'description': localize('queryEditor.messages.showBatchTime', "Should execution time be shown for individual batches"),
|
||||
|
||||