Compare commits

...

29 Commits

Author SHA1 Message Date
Matt Irvine
d2b6f6844d Add connection API method to get URI (#2021) 2018-07-30 10:52:24 -07:00
Matt Irvine
e9ef95ef1f Save query result selection/scroll when switching tabs (#2052) 2018-07-27 14:01:34 -07:00
Aditya Bist
10eeb5374f enabled button to import queries from sql files (#2042) 2018-07-27 13:44:20 -07:00
Karl Burtram
332951bc8e Pick-up newer version proxy node module (#2074) 2018-07-27 15:20:25 -04:00
Amir Ali Omidi
4daf3280ff Bring in all the extensibility updates we added during the hackathon (#2056)
Extensibility updates
2018-07-26 16:33:32 -07:00
Aditya Bist
87bb2c74d9 added .sql to associated files to sql ops (#2060) 2018-07-26 16:33:08 -07:00
Abbie Petchtes
ba011853a0 Add text editor component for model view (#2058)
* add text editor component for model view

* add comments
2018-07-26 15:52:33 -07:00
Aditya Bist
461a158ac3 bumped version for insider build (#2053) 2018-07-26 13:41:21 -07:00
Aditya Bist
85f59f1103 removed the export from defaultSort function (#2048) 2018-07-26 11:10:14 -07:00
Aditya Bist
335b9f445f Feature: Parse Query Syntax (#1997)
* feature/parseSyntax

* added error when no connection

* removed elapsed time logic

* code review comments

* changed error message to match SSMS
2018-07-25 16:11:58 -07:00
ranasaria
7cda45c904 fix for issue #1604 (#2029)
* Adding min height to .connection-dialog .tabbedPanel so that connection dialog add a vertical scroll bar appropriately when the ops studio window is vertically resized.
2018-07-25 12:31:17 -07:00
Anthony Dresser
4159fdc1a3 add off by one handler for selection (#2009) 2018-07-25 12:30:38 -07:00
Matt Irvine
190da30979 Update some Agent license headers (#2008) 2018-07-24 18:14:57 -07:00
Aditya Bist
ed9c74b900 localized the unlocalized strings (#2018) 2018-07-24 16:49:06 -07:00
Leila Lali
6783766c33 declarative table layout and option (#2007)
* added scroll bar to table content
2018-07-24 15:04:21 -07:00
Karl Burtram
b27018b379 Update SQL Tools to 1.5.0-alpha.14 2018-07-24 12:22:21 -07:00
Karl Burtram
021d07e04a Add VS Code version to product metadata (#1998) 2018-07-24 13:54:29 -04:00
Ian Y. Choi
bf0baec392 Fixes a typo: Mimunum -> Minimum (#1994)
(trivial)
2018-07-24 12:35:46 -04:00
Karl Burtram
923cbac400 Add // {{SQL CARBON EDIT}} for previous commit 2018-07-23 19:09:56 -07:00
Karl Burtram
ab938f2536 Remove @ from word separators (#1990) 2018-07-23 21:10:01 -04:00
Amir Ali Omidi
a55b1804e9 Tackles issue #1723 (#1988)
* Tackles issue #1723
2018-07-23 16:55:53 -07:00
Matt Irvine
4bfa6b3a5d Save editor cursor/scroll position when switching sql files (#1978)
* Save editor layouts when switching sql files

* Move import

* Restore the view state after laying out the editor
2018-07-20 10:48:12 -04:00
Anthony Dresser
d20f24be18 Bump slickgrid to fix html content issue (#1980)
* add ' to escape strings for html

* update slickgrid to fix injection
2018-07-20 10:47:16 -04:00
Matt Irvine
8a17bae7a6 Loading spinner while validating next/done (#1975) 2018-07-19 17:31:30 -07:00
Anthony Dresser
d14c73fad5 vbump service-downloader (#1965) 2018-07-19 14:06:41 -07:00
Anthony Dresser
ce878e1def Rework slickgrid keyboard navigation (#1930)
* rewrite keybind nav to handle ctrl + home and end

* testing different options

* working on removed slickgrid changes we don't need

* formatting

* handle click handler to rowNumber

* fixing various bugs

* formatting

* readd click column to select

* add shift key to column select

* added logic for additional keybindings on grid

* add down and up arrow into keyboard navigation

* update styling and update slickgrid

* formatting

* update angular-slickgrid version

* remove index.js changes
2018-07-19 14:05:54 -07:00
Karl Burtram
a64a0d1db6 Bump SQL Ops to 0.32.1 for August iteration 2018-07-19 11:43:32 -07:00
Anthony Dresser
feab43f16d add ' to escape strings for html (#1974) 2018-07-19 14:42:29 -04:00
Karl Burtram
0d60fe775f Update Readme and Changelog (#1968) 2018-07-19 01:21:06 -04:00
76 changed files with 1427 additions and 565 deletions

View File

@@ -1,5 +1,26 @@
# Change Log
## Version 0.31.4
* Release date: July 19, 2018
* Release status: Public Preview
## What's new in this version
* SQL Server Agent for SQL Operations Studio extension improvements
* Added view of Alerts, Operators, and Proxies and icons on left pane
* Added dialogs for New Job, New Job Step, New Alert, and New Operator
* Added Delete Job, Delete Alert, and Delete Operator (right-click)
* Added Previous Runs visualization
* Added Filters for each column name
* SQL Server Profiler for SQL Operations Studio extension improvements
* Added Hotkeys to quickly launch and start/stop Profiler
* Added 5 Default Templates to view Extended Events
* Added Server/Database connection name
* Added support for Azure SQL Database instances
* Added suggestion to exit Profiler when tab is closed when Profiler is still running
* Release of Combine Scripts Extension
* Wizard and Dialog Extensibility
* Fix GitHub Issues
## Version 0.30.6
* Release date: June 20, 2018
* Release status: Public Preview

View File

@@ -8,12 +8,12 @@ SQL Operations Studio is a data management tool that enables you to work with SQ
Platform | Link
-- | --
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=875602
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=875603
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=875604
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=875605
Linux RPM | https://go.microsoft.com/fwlink/?linkid=875606
Linux DEB | https://go.microsoft.com/fwlink/?linkid=875607
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=2005949
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=2005950
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=2005959
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=2005960
Linux RPM | https://go.microsoft.com/fwlink/?linkid=2006083
Linux DEB | https://go.microsoft.com/fwlink/?linkid=2006084
Go to our [download page](https://aka.ms/sqlopsstudio) for more specific instructions.
@@ -85,7 +85,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Chinese (Traditional): Bruce Chen, Chiayi Yen, Kevin Yang, Winnie Lin, 保哥 Will, 謝政廷
* Korean: Do-Kyun Kim, Evelyn Kim, Helen Jung, Hong Jmee, jeongwoo choi, Jun Hyoung Lee, Jungsun Kim정선, Justin Yoo, Kavrith mucha, Kiwoong Youm, MinGyu Ju, MVP_JUNO BEA, Sejun Kim, SOONMAN KWON, sung man ko, Yeongrak Choi, younggun kim, Youngjae Kim, 소영 이
* Russian: Andrey Veselov, Anton Fontanov, Anton Savin, Elena Ostrovskaia, Igor Babichev, Maxim Zelensky, Rodion Fedechkin, Tasha T, Vladimir Zyryanov
* Portuguese Brazil: Daniel de Sousa, Diogo Duarte, Douglas Correa, Douglas Eccker, José Emanuel Mendes, Marcelo Fernandes, Marcondes Alexandre, Roberto Fonseca, Rodrigo Crespi
* Portuguese Brazil: Daniel de Sousa, Diogo Duarte, Douglas Correa, Douglas Eccker, José Emanuel Mendes, Marcelo Fernandes, Marcondes Alexandre, Roberto Fonseca, Rodrigo Crespi
And of course we'd like to thank the authors of all upstream dependencies. Please see a full list in the [ThirdPartyNotices.txt](https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/ThirdPartyNotices.txt)

View File

@@ -61,6 +61,7 @@ Type: filesandordirs; Name: "{app}\_"
[Tasks]
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
Name: "associatewithfiles"; Description: "{cm:AssociateWithFiles,{#NameShort}}"; GroupDescription: "{cm:Other}"; Flags: unchecked
Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}"
Name: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{cm:Other}"; Check: WizardSilent
@@ -82,6 +83,13 @@ Root: HKCR; Subkey: "{#RegValueName}SourceFile\DefaultIcon"; ValueType: string;
Root: HKCR; Subkey: "{#RegValueName}SourceFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin"; Tasks: addtopath; Check: NeedsAddPath(ExpandConstant('{app}\bin'))
Root: HKCU; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.sql"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SQL}"; Flags: uninsdeletekey; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\code_file.ico"; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles
[Code]
// Don't allow installing conflicting architectures
function InitializeSetup(): Boolean;

View File

@@ -143,13 +143,16 @@ export class JobStepDialog {
width: '80px',
isFile: true
}).component();
this.openButton.enabled = false;
this.parseButton = view.modelBuilder.button()
.withProperties({
label: this.ParseCommandText,
width: '80px',
isFile: false
}).component();
this.openButton.onDidClick(e => {
let queryContent = e;
this.commandTextBox.value = queryContent;
});
this.parseButton.onDidClick(e => {
if (this.commandTextBox.value) {
queryProvider.parseSyntax(this.ownerUri, this.commandTextBox.value).then(result => {

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

View File

@@ -660,7 +660,7 @@
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.0",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
"vscode-extension-telemetry": "^0.0.15"
},
"devDependencies": {

View File

@@ -1,6 +1,6 @@
{
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "1.5.0-alpha.12",
"version": "1.5.0-alpha.14",
"downloadFileNames": {
"Windows_86": "win-x86-netcoreapp2.1.zip",
"Windows_64": "win-x64-netcoreapp2.1.zip",

View File

@@ -296,9 +296,9 @@ semver@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
"service-downloader@github:anthonydresser/service-downloader#0.1.2":
version "0.1.2"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/2aa9b336b6442e17e24693ddc907030575539798"
"service-downloader@github:anthonydresser/service-downloader#0.1.4":
version "0.1.4"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/3c0abdf8603aca85d2eacfac3c547173e41bf0c7"
dependencies:
decompress "^4.2.0"
eventemitter2 "^5.0.1"

View File

@@ -12,8 +12,9 @@
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
{ "open": "\"", "close": "\"", "notIn": ["string"] },
{ "open": "N'", "close": "'", "notIn": ["string", "comment"] },
{ "open": "'", "close": "'", "notIn": ["string", "comment"] }
],
"surroundingPairs": [
["{", "}"],

View File

@@ -1,6 +1,6 @@
{
"name": "sqlops",
"version": "0.31.4",
"version": "0.32.2",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": {
"name": "Microsoft Corporation"
@@ -34,7 +34,7 @@
"@angular/router": "~4.1.3",
"@angular/upgrade": "~4.1.3",
"angular2-grid": "2.0.6",
"angular2-slickgrid": "git://github.com/Microsoft/angular2-slickgrid.git#1.3.11",
"angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.3.12",
"applicationinsights": "0.18.0",
"chart.js": "^2.6.0",
"fast-plist": "0.1.2",
@@ -43,8 +43,8 @@
"getmac": "1.0.7",
"graceful-fs": "4.1.11",
"html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.4",
"http-proxy-agent": "0.2.7",
"https-proxy-agent": "0.3.6",
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.1",
"iconv-lite": "0.4.19",
"jquery": "3.1.0",
"jschardet": "1.6.0",
@@ -60,7 +60,7 @@
"reflect-metadata": "^0.1.8",
"rxjs": "5.4.0",
"semver": "4.3.6",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.23",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.25",
"spdlog": "0.6.0",
"sudo-prompt": "^8.0.0",
"svg.js": "^2.2.5",

View File

@@ -30,6 +30,7 @@
"gettingStartedUrl": "https://go.microsoft.com/fwlink/?linkid=862039",
"releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=875578",
"documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277",
"vscodeVersion": "1.23.1",
"commit": "9ca6200018fc206d67a47229f991901a8a453781",
"date": "2017-12-15T12:00:00.000Z",
"recommendedExtensions": [
@@ -42,4 +43,4 @@
"extensionsGallery": {
"serviceUrl": "https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery.json"
}
}
}

View File

@@ -301,6 +301,7 @@ export default class MainController implements vscode.Disposable {
page1.registerContent(async (view) => {
await this.getTabContent(view, customButton1, customButton2, 800);
});
/*
wizard.registerOperation({
displayName: 'test task',
description: 'task description',
@@ -311,7 +312,7 @@ export default class MainController implements vscode.Disposable {
setTimeout(() => {
op.updateStatus(sqlops.TaskStatus.Succeeded);
}, 5000);
});
});*/
wizard.pages = [page1, page2];
wizard.open();
}
@@ -354,15 +355,32 @@ export default class MainController implements vscode.Disposable {
webview2.message = count;
});
let flexModel = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'column',
alignItems: 'flex-start',
height: 500
}).withItems([
webview1, webview2
], { flex: '1 1 50%' })
let editor1 = view.modelBuilder.editor()
.withProperties({
content: 'select * from sys.tables'
})
.component();
let editor2 = view.modelBuilder.editor()
.withProperties({
content: 'print("Hello World !")',
languageMode: 'python'
})
.component();
let flexModel = view.modelBuilder.flexContainer().component();
flexModel.addItem(editor1, { flex: '1' });
flexModel.addItem(editor2, { flex: '1' });
flexModel.setLayout({
flexFlow: 'column',
alignItems: 'stretch',
height: '100%'
});
view.onClosed((params) => {
vscode.window.showInformationMessage('editor1: language: ' + editor1.languageMode + ' Content1: ' + editor1.content);
vscode.window.showInformationMessage('editor2: language: ' + editor2.languageMode + ' Content2: ' + editor2.content);
});
await view.initializeModel(flexModel);
});
editor.openEditor();

View File

@@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import {
Component, Inject, forwardRef, ElementRef, OnInit, Input,
Output, OnChanges, SimpleChanges, EventEmitter
} from '@angular/core';
import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/dropdown';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@Component({
selector: 'editable-select-box',
template: ''
})
export class EditableDropDown extends AngularDisposable implements OnInit, OnChanges {
private _selectbox: Dropdown;
@Input() options: string[];
@Input() selectedOption: string;
@Input() onlyEmitOnChange = false;
@Output() onDidSelect = new EventEmitter<string>();
private _previousVal: string;
constructor(
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(IThemeService) private themeService: IThemeService,
@Inject(IContextViewService) private contextViewService: IContextViewService
) {
super();
}
ngOnInit(): void {
let dropdownOptions: IDropdownOptions = {
values: [],
strictSelection: false,
placeholder: '',
maxHeight: 125,
ariaLabel: '',
actionLabel: ''
};
this._selectbox = new Dropdown(this._el.nativeElement, this.contextViewService, this.themeService, dropdownOptions);
this._selectbox.values = this.options;
this._selectbox.value = this.selectedOption;
this._selectbox.onValueChange(e => {
if (this.onlyEmitOnChange) {
if (this._previousVal !== e) {
this.onDidSelect.emit(e);
this._previousVal = e;
}
} else {
this.onDidSelect.emit(e);
}
});
this._register(attachEditableDropdownStyler(this._selectbox, this.themeService));
}
ngOnChanges(changes: SimpleChanges): void {
}
public get value(): string {
return this._selectbox.value;
}
}

View File

@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
/**
* Implements the various additional navigation keybindings we want out of slickgrid
*/
export class AdditionalKeyBindings<T> implements Slick.Plugin<T> {
private grid: Slick.Grid<T>;
private handler = new Slick.EventHandler();
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.handler.subscribe(this.grid.onKeyDown, (e, args) => this.handleKeyDown(e, args));
}
public destroy() {
this.handler.unsubscribeAll();
}
private handleKeyDown(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs<T>): void {
let event = new StandardKeyboardEvent(e);
let handled = true;
if (event.equals(KeyCode.RightArrow | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(args.row, this.grid.getColumns().length - 1);
} else if (event.equals(KeyCode.LeftArrow | KeyMod.CtrlCmd)) {
// account for row column
if (this.grid.canCellBeActive(args.row, 0)) {
this.grid.setActiveCell(args.row, 0);
} else {
this.grid.setActiveCell(args.row, 1);
}
} else if (event.equals(KeyCode.UpArrow | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(0, args.cell);
} else if (event.equals(KeyCode.DownArrow | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(this.grid.getDataLength() - 1, args.cell);
} else if (event.equals(KeyCode.Home | KeyMod.CtrlCmd)) {
// account for row column
if (this.grid.canCellBeActive(0, 0)) {
this.grid.setActiveCell(0, 0);
} else {
this.grid.setActiveCell(0, 1);
}
} else if (event.equals(KeyCode.End | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(this.grid.getDataLength() - 1, this.grid.getColumns().length - 1);
} else {
handled = false;
}
if (handled) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
}
}

View File

@@ -16,7 +16,7 @@ export class AutoColumnSize<T> implements Slick.Plugin<T> {
private _context: CanvasRenderingContext2D;
private _options: IAutoColumnSizeOptions;
constructor(options: IAutoColumnSizeOptions) {
constructor(options: IAutoColumnSizeOptions = defaultOptions) {
this._options = mixin(options, defaultOptions, false);
}

View File

@@ -0,0 +1,192 @@
// Drag select selection model gist taken from https://gist.github.com/skoon/5312536
// heavily modified
import { mixin } from 'vs/base/common/objects';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { IThemeService } from 'vs/platform/theme/common/themeService';
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangeselector');
export interface ICellRangeSelector<T> extends Slick.Plugin<T> {
onCellRangeSelected: Slick.Event<{ range: Slick.Range }>;
onBeforeCellRangeSelected: Slick.Event<Slick.Cell>;
}
export interface ICellSelectionModelOptions {
cellRangeSelector?: any;
selectActiveCell?: boolean;
}
const defaults: ICellSelectionModelOptions = {
selectActiveCell: true
};
export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slick.Range>> {
private grid: Slick.Grid<T>;
private selector: ICellRangeSelector<T>;
private ranges: Array<Slick.Range> = [];
public onSelectedRangesChanged = new Slick.Event<Array<Slick.Range>>();
constructor(private options: ICellSelectionModelOptions = defaults) {
this.options = mixin(this.options, defaults, false);
if (this.options.cellRangeSelector) {
this.selector = this.options.cellRangeSelector;
} else {
// this is added by the noderequires above
this.selector = new (<any>Slick).CellRangeSelector({ selectionCss: { 'border': '2px dashed grey' } });
}
}
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.grid.onActiveCellChanged.subscribe((e, args) => this.handleActiveCellChange(e, args));
this.grid.onKeyDown.subscribe(e => this.handleKeyDown(e));
this.grid.onHeaderClick.subscribe((e: MouseEvent, args) => this.handleHeaderClick(e, args));
this.grid.registerPlugin(this.selector);
this.selector.onCellRangeSelected.subscribe((e, args) => this.handleCellRangeSelected(e, args));
this.selector.onBeforeCellRangeSelected.subscribe((e, args) => this.handleBeforeCellRangeSelected(e, args));
}
public destroy() {
this.grid.onActiveCellChanged.unsubscribe((e, args) => this.handleActiveCellChange(e, args));
this.grid.onKeyDown.unsubscribe(e => this.handleKeyDown(e));
this.selector.onCellRangeSelected.unsubscribe((e, args) => this.handleCellRangeSelected(e, args));
this.selector.onBeforeCellRangeSelected.unsubscribe((e, args) => this.handleBeforeCellRangeSelected(e, args));
this.grid.unregisterPlugin(this.selector);
}
private removeInvalidRanges(ranges: Array<Slick.Range>): Array<Slick.Range> {
let result: Array<Slick.Range> = [];
for (let i = 0; i < ranges.length; i++) {
let r = ranges[i];
if (this.grid.canCellBeSelected(r.fromRow, r.fromCell) && this.grid.canCellBeSelected(r.toRow, r.toCell)) {
result.push(r);
} else if (this.grid.canCellBeSelected(r.fromRow, r.fromCell + 1) && this.grid.canCellBeSelected(r.toRow, r.toCell)) {
// account for number row
result.push(new Slick.Range(r.fromRow, r.fromCell + 1, r.toRow, r.toCell));
}
}
return result;
}
public setSelectedRanges(ranges: Array<Slick.Range>): void {
// simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged
if ((!this.ranges || this.ranges.length === 0) && (!ranges || ranges.length === 0)) {
return;
}
this.ranges = this.removeInvalidRanges(ranges);
this.onSelectedRangesChanged.notify(this.ranges);
}
public getSelectedRanges() {
return this.ranges;
}
private handleBeforeCellRangeSelected(e, args: Slick.Cell) {
if (this.grid.getEditorLock().isActive()) {
e.stopPropagation();
return false;
}
return true;
}
private handleCellRangeSelected(e, args: { range: Slick.Range }) {
this.grid.setActiveCell(args.range.fromRow, args.range.fromCell, false, false, true);
this.setSelectedRanges([args.range]);
}
private handleActiveCellChange(e, args) {
if (this.options.selectActiveCell && !isUndefinedOrNull(args.row) && !isUndefinedOrNull(args.cell)) {
this.setSelectedRanges([new Slick.Range(args.row, args.cell)]);
} else if (!this.options.selectActiveCell) {
// clear the previous selection once the cell changes
this.setSelectedRanges([]);
}
}
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) {
if (!isUndefinedOrNull(args.column)) {
let columnIndex = this.grid.getColumnIndex(args.column.id);
if (this.grid.canCellBeSelected(0, columnIndex)) {
let ranges: Array<Slick.Range>;
if (e.shiftKey) {
ranges = this.getSelectedRanges();
ranges.push(new Slick.Range(0, columnIndex, this.grid.getDataLength() - 1, columnIndex));
} else {
ranges = [new Slick.Range(0, columnIndex, this.grid.getDataLength() - 1, columnIndex)];
}
this.grid.setActiveCell(0, columnIndex);
this.setSelectedRanges(ranges);
}
}
}
private handleKeyDown(e) {
/***
* Кey codes
* 37 left
* 38 up
* 39 right
* 40 down
*/
let ranges, last;
let active = this.grid.getActiveCell();
let metaKey = e.ctrlKey || e.metaKey;
if (active && e.shiftKey && !metaKey && !e.altKey &&
(e.which === 37 || e.which === 39 || e.which === 38 || e.which === 40)) {
ranges = this.getSelectedRanges();
if (!ranges.length) {
ranges.push(new Slick.Range(active.row, active.cell));
}
// keyboard can work with last range only
last = ranges.pop();
// can't handle selection out of active cell
if (!last.contains(active.row, active.cell)) {
last = new Slick.Range(active.row, active.cell);
}
let dRow = last.toRow - last.fromRow,
dCell = last.toCell - last.fromCell,
// walking direction
dirRow = active.row === last.fromRow ? 1 : -1,
dirCell = active.cell === last.fromCell ? 1 : -1;
if (e.which === 37) {
dCell -= dirCell;
} else if (e.which === 39) {
dCell += dirCell;
} else if (e.which === 38) {
dRow -= dirRow;
} else if (e.which === 40) {
dRow += dirRow;
}
// define new selection range
let new_last = new Slick.Range(active.row, active.cell, active.row + dirRow * dRow, active.cell + dirCell * dCell);
if (this.removeInvalidRanges([new_last]).length) {
ranges.push(new_last);
let viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow;
let viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell;
this.grid.scrollRowIntoView(viewRow, false);
this.grid.scrollCellIntoView(viewRow, viewCell, false);
} else {
ranges.push(last);
}
this.setSelectedRanges(ranges);
e.preventDefault();
e.stopPropagation();
}
}
}

View File

@@ -1,364 +0,0 @@
// Drag select selection model gist taken from https://gist.github.com/skoon/5312536
// heavily modified
import { clone } from 'sql/base/common/objects';
export class DragCellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slick.Range>> {
private readonly keyColResizeIncr = 5;
private _grid: Slick.Grid<T>;
private _ranges: Array<Slick.Range> = [];
private _dragging = false;
private _handler = new Slick.EventHandler();
public onSelectedRangesChanged = new Slick.Event<Slick.Range[]>();
public init(grid: Slick.Grid<T>): void {
this._grid = grid;
this._handler.subscribe(this._grid.onActiveCellChanged, (e: Event, data: Slick.OnActiveCellChangedEventArgs<T>) => this.handleActiveCellChange(e, data));
this._handler.subscribe(this._grid.onKeyDown, (e: JQueryInputEventObject) => this.handleKeyDown(e));
this._handler.subscribe(this._grid.onClick, (e: MouseEvent) => this.handleClick(e));
this._handler.subscribe(this._grid.onDrag, (e: MouseEvent) => this.handleDrag(e));
this._handler.subscribe(this._grid.onDragInit, (e: MouseEvent) => this.handleDragInit(e));
this._handler.subscribe(this._grid.onDragStart, (e: MouseEvent) => this.handleDragStart(e));
this._handler.subscribe(this._grid.onDragEnd, (e: MouseEvent) => this.handleDragEnd(e));
this._handler.subscribe(this._grid.onHeaderClick, (e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) => this.handleHeaderClick(e, args));
}
public destroy(): void {
this._handler.unsubscribeAll();
}
private rangesToRows(ranges: Array<Slick.Range>): Array<number> {
let rows = [];
for (let i = 0; i < ranges.length; i++) {
for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
rows.push(j);
}
}
return rows;
}
private rowsToRanges(rows: Array<number>): Array<Slick.Range> {
let ranges = [];
let lastCell = this._grid.getColumns().length - 1;
for (let i = 0; i < rows.length; i++) {
ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell));
}
return ranges;
}
public getSelectedRows(): Array<number> {
return this.rangesToRows(this._ranges);
}
public setSelectedRows(rows: Array<number>) {
this.setSelectedRanges(this.rowsToRanges(rows));
}
public setSelectedRanges(ranges: Array<Slick.Range>) {
this._ranges = ranges;
this.onSelectedRangesChanged.notify(this._ranges);
}
public getSelectedRanges(): Array<Slick.Range> {
return this._ranges;
}
private handleActiveCellChange(e: Event, data: Slick.OnActiveCellChangedEventArgs<T>) { }
private isNavigationKey(e: BaseJQueryEventObject) {
// Nave keys (home, end, arrows) are all in sequential order so use a
switch (e.which) {
case $.ui.keyCode.HOME:
case $.ui.keyCode.END:
case $.ui.keyCode.LEFT:
case $.ui.keyCode.UP:
case $.ui.keyCode.RIGHT:
case $.ui.keyCode.DOWN:
return true;
default:
return false;
}
}
private navigateLeft(e: JQueryInputEventObject, activeCell: Slick.Cell) {
if (activeCell.cell > 1) {
let isHome = e.which === $.ui.keyCode.HOME;
let newActiveCellColumn = isHome ? 1 : activeCell.cell - 1;
// Unsure why but for range, must record 1 index less than expected
let newRangeColumn = newActiveCellColumn - 1;
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the rightmost edge of the range and we navigate left,
// we want to deselect the rightmost cell
if (last.fromCell <= newRangeColumn) { last.toCell -= 1; }
let fromRow = Math.min(activeCell.row, last.fromRow);
let fromCell = Math.min(newRangeColumn, last.fromCell);
let toRow = Math.max(activeCell.row, last.toRow);
let toCell = Math.max(newRangeColumn, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row, newRangeColumn, activeCell.row, newRangeColumn)];
}
this._grid.setActiveCell(activeCell.row, newActiveCellColumn);
this.setSelectedRanges(this._ranges);
}
}
private navigateRight(e: JQueryInputEventObject, activeCell: Slick.Cell) {
let columnLength = this._grid.getColumns().length;
if (activeCell.cell < columnLength) {
let isEnd = e.which === $.ui.keyCode.END;
let newActiveCellColumn = isEnd ? columnLength : activeCell.cell + 1;
// Unsure why but for range, must record 1 index less than expected
let newRangeColumn = newActiveCellColumn - 1;
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the leftmost edge of the range and we navigate right,
// we want to deselect the leftmost cell
if (newRangeColumn <= last.toCell) { last.fromCell += 1; }
let fromRow = Math.min(activeCell.row, last.fromRow);
let fromCell = Math.min(newRangeColumn, last.fromCell);
let toRow = Math.max(activeCell.row, last.toRow);
let toCell = Math.max(newRangeColumn, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row, newRangeColumn, activeCell.row, newRangeColumn)];
}
this._grid.setActiveCell(activeCell.row, newActiveCellColumn);
this.setSelectedRanges(this._ranges);
}
}
private handleKeyDown(e: JQueryInputEventObject) {
let activeCell = this._grid.getActiveCell();
if (activeCell) {
// navigation keys
if (this.isNavigationKey(e)) {
e.stopImmediatePropagation();
if (e.ctrlKey || e.metaKey) {
let event = new CustomEvent('gridnav', {
detail: {
which: e.which,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
shiftKey: e.shiftKey,
altKey: e.altKey
}
});
window.dispatchEvent(event);
return;
}
// end key
if (e.which === $.ui.keyCode.END) {
this.navigateRight(e, activeCell);
}
// home key
if (e.which === $.ui.keyCode.HOME) {
this.navigateLeft(e, activeCell);
}
// left arrow
if (e.which === $.ui.keyCode.LEFT) {
// column resize
if ((e.ctrlKey || e.metaKey) && e.shiftKey) {
let allColumns = clone(this._grid.getColumns());
allColumns[activeCell.cell - 1].width = allColumns[activeCell.cell - 1].width - this.keyColResizeIncr;
this._grid.setColumnWidths(allColumns);
} else {
this.navigateLeft(e, activeCell);
}
// up arrow
} else if (e.which === $.ui.keyCode.UP && activeCell.row > 0) {
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the bottommost edge of the range and we navigate up,
// we want to deselect the bottommost row
let newRangeRow = activeCell.row - 1;
if (last.fromRow <= newRangeRow) { last.toRow -= 1; }
let fromRow = Math.min(activeCell.row - 1, last.fromRow);
let fromCell = Math.min(activeCell.cell - 1, last.fromCell);
let toRow = Math.max(newRangeRow, last.toRow);
let toCell = Math.max(activeCell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row - 1, activeCell.cell - 1, activeCell.row - 1, activeCell.cell - 1)];
}
this._grid.setActiveCell(activeCell.row - 1, activeCell.cell);
this.setSelectedRanges(this._ranges);
// right arrow
} else if (e.which === $.ui.keyCode.RIGHT) {
// column resize
if ((e.ctrlKey || e.metaKey) && e.shiftKey) {
let allColumns = clone(this._grid.getColumns());
allColumns[activeCell.cell - 1].width = allColumns[activeCell.cell - 1].width + this.keyColResizeIncr;
this._grid.setColumnWidths(allColumns);
} else {
this.navigateRight(e, activeCell);
}
// down arrow
} else if (e.which === $.ui.keyCode.DOWN && activeCell.row < this._grid.getDataLength() - 1) {
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the topmost edge of the range and we navigate down,
// we want to deselect the topmost row
let newRangeRow = activeCell.row + 1;
if (newRangeRow <= last.toRow) { last.fromRow += 1; }
let fromRow = Math.min(activeCell.row + 1, last.fromRow);
let fromCell = Math.min(activeCell.cell - 1, last.fromCell);
let toRow = Math.max(activeCell.row + 1, last.toRow);
let toCell = Math.max(activeCell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row + 1, activeCell.cell - 1, activeCell.row + 1, activeCell.cell - 1)];
}
this._grid.setActiveCell(activeCell.row + 1, activeCell.cell);
this.setSelectedRanges(this._ranges);
}
}
}
}
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) {
let columnIndex = this._grid.getColumnIndex(args.column.id);
if (e.ctrlKey || e.metaKey) {
this._ranges.push(new Slick.Range(0, columnIndex, this._grid.getDataLength() - 1, columnIndex));
this._grid.setActiveCell(0, columnIndex + 1);
} else if (e.shiftKey && this._ranges.length) {
let last = this._ranges.pop().fromCell;
let from = Math.min(columnIndex, last);
let to = Math.max(columnIndex, last);
this._ranges = [];
for (let i = from; i <= to; i++) {
if (i !== last) {
this._ranges.push(new Slick.Range(0, i, this._grid.getDataLength() - 1, i));
}
}
this._ranges.push(new Slick.Range(0, last, this._grid.getDataLength() - 1, last));
} else {
this._ranges = [new Slick.Range(0, columnIndex, this._grid.getDataLength() - 1, columnIndex)];
this._grid.resetActiveCell();
}
this.setSelectedRanges(this._ranges);
e.stopImmediatePropagation();
return true;
}
private handleClick(e: MouseEvent) {
let cell = this._grid.getCellFromEvent(e);
if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {
return false;
}
if (!e.ctrlKey && !e.shiftKey && !e.metaKey) {
if (cell.cell !== 0) {
this._ranges = [new Slick.Range(cell.row, cell.cell - 1, cell.row, cell.cell - 1)];
this.setSelectedRanges(this._ranges);
this._grid.setActiveCell(cell.row, cell.cell);
return true;
} else {
this._ranges = [new Slick.Range(cell.row, 0, cell.row, this._grid.getColumns().length - 1)];
this.setSelectedRanges(this._ranges);
this._grid.setActiveCell(cell.row, 1);
return true;
}
}
else if (this._grid.getOptions().multiSelect) {
if (e.ctrlKey || e.metaKey) {
if (cell.cell === 0) {
this._ranges.push(new Slick.Range(cell.row, 0, cell.row, this._grid.getColumns().length - 1));
this._grid.setActiveCell(cell.row, 1);
} else {
this._ranges.push(new Slick.Range(cell.row, cell.cell - 1, cell.row, cell.cell - 1));
this._grid.setActiveCell(cell.row, cell.cell);
}
} else if (this._ranges.length && e.shiftKey) {
let last = this._ranges.pop();
if (cell.cell === 0) {
let fromRow = Math.min(cell.row, last.fromRow);
let toRow = Math.max(cell.row, last.fromRow);
this._ranges = [new Slick.Range(fromRow, 0, toRow, this._grid.getColumns().length - 1)];
} else {
let fromRow = Math.min(cell.row, last.fromRow);
let fromCell = Math.min(cell.cell - 1, last.fromCell);
let toRow = Math.max(cell.row, last.toRow);
let toCell = Math.max(cell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
}
}
}
this.setSelectedRanges(this._ranges);
return true;
}
private handleDragInit(e: MouseEvent) {
e.stopImmediatePropagation();
}
private handleDragStart(e: MouseEvent) {
let cell = this._grid.getCellFromEvent(e);
e.stopImmediatePropagation();
this._dragging = true;
if (e.ctrlKey || e.metaKey) {
this._ranges.push(new Slick.Range(cell.row, cell.cell));
this._grid.setActiveCell(cell.row, cell.cell);
} else if (this._ranges.length && e.shiftKey) {
let last = this._ranges.pop();
let fromRow = Math.min(cell.row, last.fromRow);
let fromCell = Math.min(cell.cell - 1, last.fromCell);
let toRow = Math.max(cell.row, last.toRow);
let toCell = Math.max(cell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(cell.row, cell.cell)];
this._grid.setActiveCell(cell.row, cell.cell);
}
this.setSelectedRanges(this._ranges);
}
private handleDrag(e: MouseEvent) {
if (this._dragging) {
let cell = this._grid.getCellFromEvent(e);
let activeCell = this._grid.getActiveCell();
if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {
return false;
}
this._ranges.pop();
if (activeCell.cell === 0) {
let lastCell = this._grid.getColumns().length - 1;
let firstRow = Math.min(cell.row, activeCell.row);
let lastRow = Math.max(cell.row, activeCell.row);
this._ranges.push(new Slick.Range(firstRow, 0, lastRow, lastCell));
} else {
let firstRow = Math.min(cell.row, activeCell.row);
let lastRow = Math.max(cell.row, activeCell.row);
let firstColumn = Math.min(cell.cell - 1, activeCell.cell - 1);
let lastColumn = Math.max(cell.cell - 1, activeCell.cell - 1);
this._ranges.push(new Slick.Range(firstRow, firstColumn, lastRow, lastColumn));
}
this.setSelectedRanges(this._ranges);
return true;
}
return false;
}
private handleDragEnd(e: MouseEvent) {
this._dragging = false;
}
}

View File

@@ -0,0 +1,70 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { range } from 'vs/base/common/arrays';
export interface IRowNumberColumnOptions {
numberOfRows: number;
cssClass?: string;
}
const sizePerDigit = 15;
export class RowNumberColumn<T> implements Slick.Plugin<T> {
private handler = new Slick.EventHandler();
private grid: Slick.Grid<T>;
constructor(private options: IRowNumberColumnOptions) {
}
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.handler
.subscribe(this.grid.onClick, (e, args) => this.handleClick(e, args))
.subscribe(this.grid.onHeaderClick, (e, args) => this.handleHeaderClick(e, args));
}
public destroy() {
this.handler.unsubscribeAll();
}
private handleClick(e: MouseEvent, args: Slick.OnClickEventArgs<T>): void {
if (this.grid.getColumns()[args.cell].id === 'rowNumber') {
this.grid.setActiveCell(args.row, 1);
this.grid.setSelectedRows([args.row]);
}
}
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>): void {
if (args.column.id === 'rowNumber') {
this.grid.setActiveCell(0, 1);
this.grid.setSelectedRows(range(this.grid.getDataLength()));
}
}
public getColumnDefinition(): Slick.Column<T> {
return {
id: 'rowNumber',
name: '',
field: 'rowNumber',
width: this.options.numberOfRows.toString().length * sizePerDigit,
resizable: false,
cssClass: this.options.cssClass,
focusable: false,
selectable: false,
formatter: (r, c, v, cd, dc) => this.formatter(r, c, v, cd, dc)
};
}
private formatter(row, cell, value, columnDef: Slick.Column<T>, dataContext): string {
if (dataContext) {
return `<span>${row}</span>`;
}
return null;
}
}

View File

@@ -14,7 +14,7 @@ export interface IFindPosition {
row: number;
}
export function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T> {
function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T> {
let field = args.sortCol.field;
let sign = args.sortAsc ? 1 : -1;
return data.sort((a, b) => (a[field] === b[field] ? 0 : (a[field] > b[field] ? 1 : -1)) * sign);

View File

@@ -15,6 +15,7 @@ export function escape(html: string): string {
case '>': return '&gt;';
case '&': return '&amp;';
case '"': return '&quot;';
case '\'': return '&#39';
default: return match;
}
});

View File

@@ -152,10 +152,12 @@ export interface IConnectionManagementService {
getAdvancedProperties(): sqlops.ConnectionOption[];
getConnectionId(connectionProfile: IConnectionProfile): string;
getConnectionUri(connectionProfile: IConnectionProfile): string;
getFormattedUri(uri: string, connectionProfile: IConnectionProfile): string;
getConnectionUriFromId(connectionId: string): string;
isConnected(fileUri: string): boolean;
/**

View File

@@ -648,6 +648,15 @@ export class ConnectionManagementService extends Disposable implements IConnecti
return this._connectionStatusManager.getActiveConnectionProfiles();
}
public getConnectionUriFromId(connectionId: string): string {
let connection = this.getActiveConnections().find(connection => connection.id === connectionId);
if (connection) {
return this.getConnectionUri(connection);
} else {
return undefined;
}
}
public saveProfileGroup(profile: IConnectionProfileGroup): Promise<string> {
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.AddServerGroup);
return new Promise<string>((resolve, reject) => {
@@ -704,7 +713,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
return false;
}
public getConnectionId(connectionProfile: IConnectionProfile): string {
public getConnectionUri(connectionProfile: IConnectionProfile): string {
return this._connectionStatusManager.getOriginalOwnerUri(Utils.generateUri(connectionProfile));
}
@@ -716,7 +725,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
*/
public getFormattedUri(uri: string, connectionProfile: IConnectionProfile): string {
if (this._connectionStatusManager.isDefaultTypeUri(uri)) {
return this.getConnectionId(connectionProfile);
return this.getConnectionUri(connectionProfile);
} else {
return uri;
}

View File

@@ -63,7 +63,7 @@ export class ConnectionController implements IConnectionComponentController {
tempProfile.password = password;
tempProfile.groupFullName = '';
tempProfile.saveProfile = false;
let uri = this._connectionManagementService.getConnectionId(tempProfile);
let uri = this._connectionManagementService.getConnectionUri(tempProfile);
return new Promise<string[]>((resolve, reject) => {
if (this._databaseCache.has(uri)) {
let cachedDatabases: string[] = this._databaseCache.get(uri);

View File

@@ -22,6 +22,7 @@
border-top-color: transparent;
height: calc(100% - 350px);
display: block;
min-height: 120px;
}
.connection-recent, .connection-saved {

View File

@@ -57,13 +57,14 @@ import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsV
import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editabledropdown.component';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, AlertsViewComponent, ProxiesViewComponent, OperatorsViewComponent,
DashboardModelViewContainer, ModelComponentWrapper, Checkbox, SelectBox, InputBox,];
DashboardModelViewContainer, ModelComponentWrapper, Checkbox, EditableDropDown, SelectBox, InputBox,];
/* Panel */
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';

View File

@@ -16,7 +16,7 @@ const properties: IJSONSchema = {
properties: {
yAxisMin: {
type: 'number',
description: nls.localize('yAxisMin', "Minumum value of the y axis")
description: nls.localize('yAxisMin', "Minimum value of the y axis")
},
yAxisMax: {
type: 'number',
@@ -28,7 +28,7 @@ const properties: IJSONSchema = {
},
xAxisMin: {
type: 'number',
description: nls.localize('xAxisMin', "Minumum value of the x axis")
description: nls.localize('xAxisMin', "Minimum value of the x axis")
},
xAxisMax: {
type: 'number',

View File

@@ -11,8 +11,8 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work
import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { Table } from 'sql/base/browser/ui/table/table';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { DragCellSelectionModel } from 'sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin';
import { attachTableStyler} from 'sql/common/theme/styler';
import { attachTableStyler } from 'sql/common/theme/styler';
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
@Component({
template: ''
@@ -63,7 +63,7 @@ export default class TableInsight extends Disposable implements IInsightsView, O
private createTable() {
if (!this.table) {
this.table = new Table(this._elementRef.nativeElement, this.dataView, this.columns, { showRowNumber: true });
this.table.setSelectionModel(new DragCellSelectionModel());
this.table.setSelectionModel(new CellSelectionModel());
this._register(attachTableStyler(this.table, this.themeService));
}
}

View File

@@ -146,7 +146,7 @@ export class BackupUiService implements IBackupUiService {
let backupOptions = this.getOptions(this._currentProvider);
return new TPromise<void>(() => {
let uri = this._connectionManagementService.getConnectionId(connection)
let uri = this._connectionManagementService.getConnectionUri(connection)
+ ProviderConnectionInfo.idSeparator
+ ConnectionUtils.ConnectionUriBackupIdAttributeName
+ ProviderConnectionInfo.nameValueSeparator

View File

@@ -293,7 +293,7 @@ export class RestoreDialogController implements IRestoreDialogController {
return new TPromise<void>((resolve, reject) => {
let result: void;
this._ownerUri = this._connectionService.getConnectionId(connection)
this._ownerUri = this._connectionService.getConnectionUri(connection)
+ ProviderConnectionInfo.idSeparator
+ Utils.ConnectionUriRestoreIdAttributeName
+ ProviderConnectionInfo.nameValueSeparator

View File

@@ -4,7 +4,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IColumnDefinition, IObservableCollection, IGridDataRow } from 'angular2-slickgrid';
import { ISlickColumn, IObservableCollection, IGridDataRow } from 'angular2-slickgrid';
export interface ISlickRange {
fromCell: number;
@@ -42,7 +42,7 @@ export interface IGridIcon {
export interface IGridDataSet {
dataRows: IObservableCollection<IGridDataRow>;
columnDefinitions: IColumnDefinition[];
columnDefinitions: ISlickColumn<any>[];
resized: any; // EventEmitter<any>;
totalRows: number;
batchId: number;

View File

@@ -17,14 +17,13 @@
showDataTypeIcon="false"
showHeader="true"
[resized]="dataSet.resized"
[plugins]="slickgridPlugins"
[plugins]="plugins[i]"
(activeCellChanged)="onActiveCellChanged($event)"
(cellEditBegin)="onCellEditBegin($event)"
(cellEditExit)="onCellEditEnd($event)"
(rowEditBegin)="onRowEditBegin($event)"
(rowEditExit)="onRowEditEnd($event)"
(contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
[isColumnEditable]="onIsColumnEditable"
[isCellEditValid]="onIsCellEditValid"
[overrideCellFn]="overrideCellFn"
enableEditing="true"

View File

@@ -24,6 +24,9 @@ import { error } from 'sql/base/common/log';
import { clone, mixin } from 'sql/base/common/objects';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import { escape } from 'sql/base/common/strings';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -36,7 +39,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export const EDITDATA_SELECTOR: string = 'editdata-component';
@Component({
@@ -66,6 +68,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
private newRowVisible: boolean;
private removingNewRow: boolean;
private rowIdMappings: { [gridRowId: number]: number } = {};
protected plugins = new Array<Array<Slick.Plugin<any>>>();
// Edit Data functions
public onActiveCellChanged: (event: { row: number, column: number }) => void;
@@ -167,17 +170,6 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.onRowEditEnd = (event: { row: number }): void => { };
this.onIsColumnEditable = (column: number): boolean => {
let result = false;
// Check that our variables exist
if (column !== undefined && !!this.dataSet && !!this.dataSet.columnDefinitions[column]) {
result = this.dataSet.columnDefinitions[column].isEditable;
}
// If no column definition exists then the row is not editable
return result;
};
this.overrideCellFn = (rowNumber, columnId, value?, data?): string => {
let returnVal = '';
if (Services.DBCellValue.isDBCellValue(value)) {
@@ -197,9 +189,9 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
self.idMapping[rowIndex] = row.id;
rowIndex++;
return {
values: row.cells.map(c => {
values: [{}].concat(row.cells.map(c => {
return mixin({ ariaLabel: escape(c.displayValue) }, c);
}), row: row.id
})), row: row.id
};
});
@@ -351,6 +343,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
let maxHeight = this.getMaxHeight(resultSet.rowCount);
let minHeight = this.getMinHeight(resultSet.rowCount);
let rowNumberColumn = new RowNumberColumn({ numberOfRows: resultSet.rowCount });
// Store the result set from the event
let dataSet: IGridDataSet = {
resized: undefined,
@@ -365,7 +359,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.loadDataFunction,
index => { return { values: [] }; }
),
columnDefinitions: resultSet.columnInfo.map((c, i) => {
columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson;
let linkType = c.isXml ? 'xml' : 'json';
@@ -374,13 +368,14 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan'
: escape(c.columnName),
type: self.stringToFieldType('string'),
field: i.toString(),
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined,
isEditable: c.isUpdatable
};
})
}))
};
self.plugins.push([rowNumberColumn, new AutoColumnSize(), new AdditionalKeyBindings()]);
self.dataSet = dataSet;
// Create a dataSet to render without rows to reduce DOM size

View File

@@ -26,6 +26,7 @@ import * as GridContentEvents from 'sql/parts/grid/common/gridContentEvents';
import { ResultsVisibleContext, ResultsGridFocussedContext, ResultsMessagesFocussedContext, QueryEditorVisibleContext } from 'sql/parts/query/common/queryContext';
import { error } from 'sql/base/common/log';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
import { IAction } from 'vs/base/common/actions';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
@@ -33,8 +34,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { DragCellSelectionModel } from 'sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@@ -42,14 +41,8 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export abstract class GridParentComponent {
// CONSTANTS
// tslint:disable:no-unused-variable
protected get selectionModel(): DragCellSelectionModel<any> {
return new DragCellSelectionModel<any>();
}
protected get slickgridPlugins(): Array<any> {
return [
new AutoColumnSize<any>({})
];
}
protected get selectionModel() { return new CellSelectionModel(); }
protected _rowHeight = 29;
protected _defaultNumShowingRows = 8;
protected Constants = Constants;
@@ -94,7 +87,6 @@ export abstract class GridParentComponent {
public onRowEditBegin: (event: { row: number }) => void;
public onRowEditEnd: (event: { row: number }) => void;
public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean;
public onIsColumnEditable: (column: number) => boolean;
public overrideCellFn: (rowNumber, columnId, value?, data?) => string;
public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>;
@@ -244,21 +236,25 @@ export abstract class GridParentComponent {
this.messagesFocussedContextKey.set(false);
}
protected getSelection(index?: number): ISlickRange[] {
let selection = this.slickgrids.toArray()[index || this.activeGrid].getSelectedRanges();
selection = selection.map(c => { return <ISlickRange>{ fromCell: c.fromCell - 1, toCell: c.toCell - 1, toRow: c.toRow, fromRow: c.fromRow }; });
return selection;
}
private copySelection(): void {
let messageText = this.getMessageText();
if (messageText.length > 0) {
this.clipboardService.writeText(messageText);
} else {
let activeGrid = this.activeGrid;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges();
this.dataService.copyResults(selection, this.renderedDataSets[activeGrid].batchId, this.renderedDataSets[activeGrid].resultId);
this.dataService.copyResults(this.getSelection(activeGrid), this.renderedDataSets[activeGrid].batchId, this.renderedDataSets[activeGrid].resultId);
}
}
private copyWithHeaders(): void {
let activeGrid = this.activeGrid;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges();
this.dataService.copyResults(selection, this.renderedDataSets[activeGrid].batchId,
this.dataService.copyResults(this.getSelection(activeGrid), this.renderedDataSets[activeGrid].batchId,
this.renderedDataSets[activeGrid].resultId, true);
}
@@ -372,8 +368,7 @@ export abstract class GridParentComponent {
let activeGrid = this.activeGrid;
let batchId = this.renderedDataSets[activeGrid].batchId;
let resultId = this.renderedDataSets[activeGrid].resultId;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges();
this.dataService.sendSaveRequest({ batchIndex: batchId, resultSetNumber: resultId, format: format, selection: selection });
this.dataService.sendSaveRequest({ batchIndex: batchId, resultSetNumber: resultId, format: format, selection: this.getSelection(activeGrid) });
}
protected _keybindingFor(action: IAction): ResolvedKeybinding {
@@ -385,7 +380,7 @@ export abstract class GridParentComponent {
let slick: any = this.slickgrids.toArray()[index];
let grid = slick._grid;
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
let selection = this.getSelection(index);
if (selection && selection.length === 0) {
let cell = (grid as Slick.Grid<any>).getCellFromEvent(event);
@@ -423,7 +418,9 @@ export abstract class GridParentComponent {
let self = this;
return (gridIndex: number) => {
self.activeGrid = gridIndex;
self.slickgrids.toArray()[this.activeGrid].selection = true;
let grid = self.slickgrids.toArray()[self.activeGrid];
grid.setActive();
grid.selection = true;
};
}
@@ -433,22 +430,6 @@ export abstract class GridParentComponent {
}
}
/**
* Used to convert the string to a enum compatible with SlickGrid
*/
protected stringToFieldType(input: string): FieldType {
let fieldtype: FieldType;
switch (input) {
case 'string':
fieldtype = FieldType.String;
break;
default:
fieldtype = FieldType.String;
break;
}
return fieldtype;
}
/**
* Makes a resultset take up the full result height if this is not already true
* Otherwise rerenders the result sets from default

View File

@@ -10,9 +10,9 @@
<span> {{LocalizedConstants.resultPaneLabel}} </span>
<span class="queryResultsShortCut"> {{resultShortcut}} </span>
</div>
<div id="results" *ngIf="renderedDataSets.length > 0" class="results vertBox scrollable"
(onScroll)="onScroll($event)" [scrollEnabled]="scrollEnabled" [class.hidden]="!resultActive"
(focusin)="onGridFocus()" (focusout)="onGridFocusout()">
<div #resultsScrollBox id="results" *ngIf="renderedDataSets.length > 0" class="results vertBox scrollable"
(onScroll)="onScroll($event)" [scrollEnabled]="scrollEnabled" [class.hidden]="!resultActive"
(focusin)="onGridFocus()" (focusout)="onGridFocusout()">
<div class="boxRow content horzBox slickgrid" *ngFor="let dataSet of renderedDataSets; let i = index"
[style.max-height]="dataSet.maxHeight" [style.min-height]="dataSet.minHeight">
<slick-grid #slickgrid id="slickgrid_{{i}}" [columnDefinitions]="dataSet.columnDefinitions"
@@ -20,12 +20,11 @@
[dataRows]="dataSet.dataRows"
(contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
enableAsyncPostRender="true"
showDataTypeIcon="false"
showHeader="true"
[resized]="dataSet.resized"
(mousedown)="navigateToGrid(i)"
[selectionModel]="selectionModel"
[plugins]="slickgridPlugins"
[plugins]="plugins[i]"
class="boxCol content vertBox slickgrid"
[rowHeight]="rowHeight">
</slick-grid>

View File

@@ -15,7 +15,7 @@ import {
ElementRef, QueryList, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject,
ViewChildren, forwardRef, EventEmitter, Input, ViewChild
} from '@angular/core';
import { IGridDataRow, SlickGrid, VirtualizedCollection } from 'angular2-slickgrid';
import { IGridDataRow, SlickGrid, VirtualizedCollection, ISlickRange } from 'angular2-slickgrid';
import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants';
import * as Services from 'sql/parts/grid/services/sharedServices';
@@ -28,6 +28,9 @@ import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { clone, mixin } from 'sql/base/common/objects';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { escape } from 'sql/base/common/strings';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import { format } from 'vs/base/common/strings';
import * as DOM from 'vs/base/browser/dom';
@@ -61,7 +64,9 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
// create a function alias to use inside query.component
// tslint:disable-next-line:no-unused-variable
private stringsFormat: any = format;
protected stringsFormat: any = format;
protected plugins = new Array<Array<Slick.Plugin<any>>>();
// tslint:disable-next-line:no-unused-variable
private dataIcons: IGridIcon[] = [
@@ -86,7 +91,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
icon: () => { return 'saveCsv'; },
hoverText: () => { return LocalizedConstants.saveCSVLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
let selection = this.getSelection(index);
if (selection.length <= 1) {
this.handleContextClick({ type: 'savecsv', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
@@ -99,7 +104,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
icon: () => { return 'saveJson'; },
hoverText: () => { return LocalizedConstants.saveJSONLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
let selection = this.getSelection(index);
if (selection.length <= 1) {
this.handleContextClick({ type: 'savejson', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
@@ -112,7 +117,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
icon: () => { return 'saveExcel'; },
hoverText: () => { return LocalizedConstants.saveExcelLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
let selection = this.getSelection(index);
if (selection.length <= 1) {
this.handleContextClick({ type: 'saveexcel', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
@@ -156,6 +161,13 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
public showChartRequested: EventEmitter<IGridDataSet> = new EventEmitter<IGridDataSet>();
public goToNextQueryOutputTabRequested: EventEmitter<void> = new EventEmitter<void>();
private savedViewState: {
gridSelections: ISlickRange[][];
resultsScroll: number;
messagePaneScroll: number;
slickGridScrolls: { vertical: number; horizontal: number }[];
};
@Input() public queryParameters: IQueryComponentParams;
@ViewChildren('slickgrid') slickgrids: QueryList<SlickGrid>;
@@ -163,6 +175,8 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
@ViewChild('resultsPane', { read: ElementRef }) private _resultsPane: ElementRef;
@ViewChild('queryLink', { read: ElementRef }) private _queryLinkElement: ElementRef;
@ViewChild('messagesContainer', { read: ElementRef }) private _messagesContainer: ElementRef;
@ViewChild('resultsScrollBox', { read: ElementRef }) private _resultsScrollBox: ElementRef;
@ViewChildren('slickgrid', { read: ElementRef }) private _slickgridElements: QueryList<ElementRef>;
constructor(
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef,
@@ -220,6 +234,10 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
}
self._cd.detectChanges();
});
this.queryParameters.onSaveViewState(() => this.saveViewState());
this.queryParameters.onRestoreViewState(() => this.restoreViewState());
this.dataService.onAngularLoaded();
}
@@ -302,9 +320,9 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
for (let row = 0; row < rows.rows.length; row++) {
// Push row values onto end of gridData for slickgrid
gridData.push({
values: rows.rows[row].map(c => {
values: [{}].concat(rows.rows[row].map(c => {
return mixin({ ariaLabel: escape(c.displayValue) }, c);
})
}))
});
}
@@ -331,6 +349,8 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
minHeight = minHeightNumber.toString() + 'px';
}
let rowNumberColumn = new RowNumberColumn({ numberOfRows: resultSet.rowCount });
// Store the result set from the event
let dataSet: IGridDataSet = {
resized: undefined,
@@ -345,7 +365,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
loadDataFunction,
index => { return { values: [] }; }
),
columnDefinitions: resultSet.columnInfo.map((c, i) => {
columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson;
let linkType = c.isXml ? 'xml' : 'json';
@@ -354,12 +374,13 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan'
: escape(c.columnName),
type: self.stringToFieldType('string'),
field: i.toString(),
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined
};
})
}))
};
self.plugins.push([rowNumberColumn, new AutoColumnSize(), new AdditionalKeyBindings()]);
self.dataSets.push(dataSet);
// check if the resultset is for a query plan
@@ -643,6 +664,43 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
}
}
private saveViewState(): void {
let gridSelections = this.slickgrids.map(grid => grid.getSelectedRanges());
let resultsScrollElement = (this._resultsScrollBox.nativeElement as HTMLElement);
let resultsScroll = resultsScrollElement.scrollTop;
let messagePaneScroll = (this._messagesContainer.nativeElement as HTMLElement).scrollTop;
let slickGridScrolls = this._slickgridElements.map(element => {
// Get the slick grid's viewport element and save its scroll position
let scrollElement = (element.nativeElement as HTMLElement).children[0].children[3];
return {
vertical: scrollElement.scrollTop,
horizontal: scrollElement.scrollLeft
};
});
this.savedViewState = {
gridSelections,
messagePaneScroll,
resultsScroll,
slickGridScrolls
};
}
private restoreViewState(): void {
if (this.savedViewState) {
this.slickgrids.forEach((grid, index) => grid.selection = this.savedViewState.gridSelections[index]);
(this._resultsScrollBox.nativeElement as HTMLElement).scrollTop = this.savedViewState.resultsScroll;
(this._messagesContainer.nativeElement as HTMLElement).scrollTop = this.savedViewState.messagePaneScroll;
this._slickgridElements.forEach((element, index) => {
let scrollElement = (element.nativeElement as HTMLElement).children[0].children[3];
let savedScroll = this.savedViewState.slickGridScrolls[index];
scrollElement.scrollTop = savedScroll.vertical;
scrollElement.scrollLeft = savedScroll.horizontal;
});
this.savedViewState = undefined;
}
}
layout() {
this.resizeGrids();
}

View File

@@ -27,7 +27,7 @@ import { Color } from 'vs/base/common/color';
<div>
<label for={{this.label}}>
<div #input style="width: 100%">
<input *ngIf="this.isFile === true" id={{this.label}} type="file" style="display: none">
<input #fileInput *ngIf="this.isFile === true" id={{this.label}} type="file" accept=".sql" style="display: none">
</div>
</label>
</div>
@@ -39,17 +39,16 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
private _button: Button;
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
@ViewChild('fileInput', { read: ElementRef }) private _fileInputContainer: ElementRef;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
) {
super(changeRef);
}
ngOnInit(): void {
this.baseInit();
}
ngAfterViewInit(): void {
@@ -61,10 +60,27 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND, buttonForeground: SIDE_BAR_TITLE_FOREGROUND
}));
this._register(this._button.onDidClick(e => {
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidClick,
args: e
});
if (this._fileInputContainer) {
const self = this;
this._fileInputContainer.nativeElement.onchange = () => {
let file = self._fileInputContainer.nativeElement.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let text = (<FileReader>e.target).result;
self.fileContent = text;
self._onEventEmitter.fire({
eventType: ComponentEventType.onDidClick,
args: self.fileContent
});
};
reader.readAsText(file);
};
} else {
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidClick,
args: e
});
}
}));
}
}
@@ -133,6 +149,18 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
this.setPropertyFromUI<sqlops.ButtonProperties, boolean>(this.setFileProperties, newValue);
}
private get fileContent(): string {
return this.getPropertyOrDefault<sqlops.ButtonProperties, string>((props) => props.fileContent, '');
}
private set fileContent(newValue: string) {
this.setPropertyFromUI<sqlops.ButtonProperties, string>(this.setFileContentProperties, newValue);
}
private setFileContentProperties(properties: sqlops.ButtonProperties, fileContent: string) : void {
properties.fileContent = fileContent;
}
private setValueProperties(properties: sqlops.ButtonProperties, label: string): void {
properties.label = label;
}

View File

@@ -20,6 +20,7 @@ import TableComponent from './table.component';
import TextComponent from './text.component';
import LoadingComponent from './loadingComponent.component';
import FileBrowserTreeComponent from './fileBrowserTree.component';
import EditorComponent from './editor.component';
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
@@ -74,3 +75,6 @@ registerComponentType(LOADING_COMPONENT, ModelComponentTypes.LoadingComponent, L
export const FILEBROWSERTREE_COMPONENT = 'filebrowsertree-component';
registerComponentType(FILEBROWSERTREE_COMPONENT, ModelComponentTypes.FileBrowserTree, FileBrowserTreeComponent);
export const EDITOR_COMPONENT = 'editor-component';
registerComponentType(EDITOR_COMPONENT, ModelComponentTypes.Editor, EditorComponent);

View File

@@ -17,6 +17,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
import { Event, Emitter } from 'vs/base/common/event';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editabledropdown.component';
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
import * as nls from 'vs/nls';
@@ -24,29 +25,38 @@ import * as nls from 'vs/nls';
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
boolean = 'boolean',
editableCategory = 'editableCategory'
}
@Component({
selector: 'modelview-declarativeTable',
template: `
<table role=grid aria-labelledby="ID_REF" #container *ngIf="columns" class="declarative-table">
<thead>
<tr style="display:block;">
<ng-container *ngFor="let column of columns;let h = index">
<th class="declarative-table-header" tabindex="-1" role="button" aria-sort="none">{{column.displayName}}</th>
<td class="declarative-table-header" tabindex="-1" [style.width]="getColumnWidth(h)" role="button" aria-sort="none">{{column.displayName}}</td>
</ng-container>
</tr>
</thead>
<ng-container *ngIf="data">
<tbody style="display: block; width:100%; overflow-y: scroll" [style.height]="getHeight()">
<ng-container *ngFor="let row of data;let r = index">
<tr class="declarative-table-row">
<ng-container *ngFor="let cellData of row;let c = index">
<td class="declarative-table-cell" tabindex="-1" role="button" [style.width]="getColumnWidth(c)">
<checkbox *ngIf="isCheckBox(c)" label="" (onChange)="onCheckBoxChanged($event,r,c)" [enabled]="isControlEnabled(c)" [checked]="isChecked(r,c)"></checkbox>
<select-box *ngIf="isSelectBox(c)" [options]="GetOptions(c)" (onDidSelect)="onSelectBoxChanged($event,r,c)" [selectedOption]="GetSelectedOptionDisplayName(r,c)"></select-box>
<editable-select-box *ngIf="isEditableSelectBox(c)" [options]="GetOptions(c)" (onDidSelect)="onSelectBoxChanged($event,r,c)" [selectedOption]="GetSelectedOptionDisplayName(r,c)"></editable-select-box>
<input-box *ngIf="isInputBox(c)" [value]="cellData" (onDidChange)="onInputBoxChanged($event,r,c)"></input-box>
<ng-container *ngIf="isLabel(c)" >{{cellData}}</ng-container>
</td>
</ng-container>
</tr>
</ng-container>
</tbody>
</ng-container>
</table>
`
@@ -109,10 +119,20 @@ export default class DeclarativeTableComponent extends ComponentBase implements
this.onCellDataChanged(e, row, cell);
}
private onSelectBoxChanged(e: ISelectData, row: number, cell: number): void {
private onSelectBoxChanged(e: ISelectData | string, row: number, cell: number): void {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
if (column.categoryValues) {
this.onCellDataChanged(column.categoryValues[e.index].name, row, cell);
if (typeof e === 'string') {
let category = column.categoryValues.find(c => c.displayName === e);
if (category) {
this.onCellDataChanged(category.name, row, cell);
} else {
this.onCellDataChanged(e, row, cell);
}
} else {
this.onCellDataChanged(column.categoryValues[e.index].name, row, cell);
}
}
}
@@ -135,6 +155,11 @@ export default class DeclarativeTableComponent extends ComponentBase implements
return column.valueType === DeclarativeDataType.category;
}
private isEditableSelectBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.editableCategory;
}
private isInputBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.string && !column.isReadOnly;
@@ -142,7 +167,7 @@ export default class DeclarativeTableComponent extends ComponentBase implements
private getColumnWidth(cell: number): string {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return this.convertSize(column.width);
return this.convertSize(column.width, '30px');
}
private GetOptions(cell: number): string[] {
@@ -155,7 +180,13 @@ export default class DeclarativeTableComponent extends ComponentBase implements
let cellData = this.data[row][cell];
if (cellData && column.categoryValues) {
let category = column.categoryValues.find(v => v.name === cellData);
return category.displayName;
if (category) {
return category.displayName;
} else if (this.isEditableSelectBox(cell)) {
return cellData;
} else {
return undefined;
}
} else {
return '';
}

View File

@@ -2,7 +2,6 @@
.declarative-table {
padding: 5px 30px 0px 30px;
box-sizing: border-box;
width:100%;
border-collapse: collapse;
}
@@ -13,6 +12,7 @@
padding: 5px;
border: 1px solid gray;
background-color: #F5F5F5;
vertical-align: top;
}
.vs-dark .declarative-table-header {

View File

@@ -0,0 +1,135 @@
/*---------------------------------------------------------------------------------------------
* 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!./editor';
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList
} from '@angular/core';
import * as sqlops from 'sqlops';
import * as DOM from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITextModel } from 'vs/editor/common/model';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import URI from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
import { QueryTextEditor } from 'sql/parts/modelComponents/queryTextEditor';
@Component({
template: '',
selector: 'modelview-editor-component'
})
export default class EditorComponent extends ComponentBase implements IComponent, OnDestroy {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
private _editor: QueryTextEditor;
private _editorInput: UntitledEditorInput;
private _editorModel: ITextModel;
private _renderedContent: string;
private _langaugeMode: string;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(IInstantiationService) private _instantiationService: IInstantiationService,
@Inject(IModelService) private _modelService: IModelService,
@Inject(IModeService) private _modeService: IModeService
) {
super(changeRef);
}
ngOnInit(): void {
this.baseInit();
this._createEditor();
this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, e => {
this.layout();
}));
}
private _createEditor(): void {
this._editor = this._instantiationService.createInstance(QueryTextEditor);
this._editor.create(this._el.nativeElement);
this._editor.setVisible(true);
this._editorInput = this._instantiationService.createInstance(UntitledEditorInput, URI.from({ scheme: Schemas.untitled, path: `${this.descriptor.type}-${this.descriptor.id}` }), false, 'sql', '', '');
this._editor.setInput(this._editorInput, undefined);
this._editorInput.resolve().then(model => this._editorModel = model.textEditorModel);
this._register(this._editor);
this._register(this._editorInput);
this._register(this._editorModel.onDidChangeContent(e => {
this.content = this._editorModel.getValue();
}));
}
ngOnDestroy(): void {
this.baseDestroy();
}
/// IComponent implementation
public layout(): void {
let width: number = this.convertSizeToNumber(this.width);
let height: number = this.convertSizeToNumber(this.height);
this._editor.layout(new DOM.Dimension(
width && width > 0 ? width : DOM.getContentWidth(this._el.nativeElement),
height && height > 0 ? height : DOM.getContentHeight(this._el.nativeElement)));
}
/// Editor Functions
private updateModel() {
if (this._editorModel) {
this._renderedContent = this.content;
this._modelService.updateModel(this._editorModel, this._renderedContent);
}
}
private updateLanguageMode() {
if (this._editorModel && this._editor) {
this._langaugeMode = this.languageMode;
this._modeService.getOrCreateMode(this._langaugeMode).then((modeValue) => {
this._modelService.setMode(this._editorModel, modeValue);
});
}
}
/// IComponent implementation
public setLayout(layout: any): void {
// TODO allow configuring the look and feel
this.layout();
}
public setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties);
if (this.content !== this._renderedContent) {
this.updateModel();
}
if (this.languageMode !== this._langaugeMode) {
this.updateLanguageMode();
}
}
// CSS-bound properties
public get content(): string {
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.content, undefined);
}
public set content(newValue: string) {
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, content) => { properties.content = content; }, newValue);
}
public get languageMode(): string {
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.languageMode, undefined);
}
public set languageMode(newValue: string) {
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, languageMode) => { properties.languageMode = languageMode; }, newValue);
}
}

View File

@@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
modelview-editor-component {
height: 100%;
width : 100%;
display: block;
}

View File

@@ -0,0 +1,113 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import * as nls from 'vs/nls';
import * as DOM from 'vs/base/browser/dom';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { TPromise } from 'vs/base/common/winjs.base';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { EditorOptions } from 'vs/workbench/common/editor';
import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { IEditorContributionCtor } from 'vs/editor/browser/editorExtensions';
import { FoldingController } from 'vs/editor/contrib/folding/folding';
class QueryCodeEditor extends CodeEditor {
protected _getContributions(): IEditorContributionCtor[] {
let contributions = super._getContributions();
let skipContributions = [FoldingController.prototype];
contributions = contributions.filter(c => skipContributions.indexOf(c.prototype) === -1);
return contributions;
}
}
/**
* Extension of TextResourceEditor that is always readonly rather than only with non UntitledInputs
*/
export class QueryTextEditor extends BaseTextEditor {
public static ID = 'modelview.editors.textEditor';
private _dimension: DOM.Dimension;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IModeService modeService: IModeService,
@ITextFileService textFileService: ITextFileService,
@IEditorGroupService editorGroupService: IEditorGroupService
) {
super(QueryTextEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
}
public createEditorControl(parent: HTMLElement, configuration: IEditorOptions): editorCommon.IEditor {
return this.instantiationService.createInstance(QueryCodeEditor, parent, configuration);
}
protected getConfigurationOverrides(): IEditorOptions {
const options = super.getConfigurationOverrides();
if (this.input) {
options.inDiffEditor = true;
options.scrollBeyondLastLine = false;
options.folding = false;
options.renderWhitespace = 'none';
options.wordWrap = 'on';
options.renderIndentGuides = false;
options.rulers = [];
options.glyphMargin = true;
options.minimap = {
enabled: false
};
}
return options;
}
setInput(input: UntitledEditorInput, options: EditorOptions): TPromise<void> {
return super.setInput(input, options)
.then(() => this.input.resolve()
.then(editorModel => editorModel.load())
.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
}
protected getAriaLabel(): string {
return nls.localize('queryTextEditorAriaLabel', 'modelview code editor for view model.');
}
public layout(dimension?: DOM.Dimension){
if (dimension) {
this._dimension = dimension;
}
this.getControl().layout(dimension);
}
public setWidth(width: number) {
if (this._dimension) {
this._dimension.width = width;
this.layout();
}
}
public setHeight(height: number) {
if (this._dimension) {
this._dimension.height = height;
this.layout();
}
}
}

View File

@@ -22,7 +22,7 @@ import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectio
@Component({
selector: 'modelview-table',
template: `
<div #table style="width: 100%;height:100%"></div>
<div #table style="width: 100%;height:100%" [style.font-size]="fontSize"></div>
`
})
export default class TableComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@@ -98,7 +98,7 @@ export default class TableComponent extends ComponentBase implements IComponent,
syncColumnCellResize: true,
enableColumnReorder: false,
enableCellNavigation: true,
forceFitColumns: true,
forceFitColumns: true
};
this._table = new Table<Slick.SlickData>(this._inputContainer.nativeElement, this._tableData, this._tableColumns, options);
@@ -178,6 +178,10 @@ export default class TableComponent extends ComponentBase implements IComponent,
return this.getPropertyOrDefault<sqlops.TableComponentProperties, string[]>((props) => props.columns, []);
}
public get fontSize(): number | string {
return this.getPropertyOrDefault<sqlops.TableComponentProperties, number | string>((props) => props.fontSize, '');
}
public set columns(newValue: string[]) {
this.setPropertyFromUI<sqlops.TableComponentProperties, string[]>((props, value) => props.columns = value, newValue);
}

View File

@@ -185,7 +185,7 @@ export class OEScriptSelectAction extends ScriptSelectAction {
}
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode);
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile);
var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
var metadata = this._objectExplorerTreeNode.metadata;
@@ -255,7 +255,7 @@ export class OEScriptCreateAction extends ScriptCreateAction {
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile);
var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => {
@@ -291,7 +291,7 @@ export class OEScriptExecuteAction extends ScriptExecuteAction {
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile);
var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => {
@@ -327,7 +327,7 @@ export class OEScriptAlterAction extends ScriptAlterAction {
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile);
var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => {
@@ -363,7 +363,7 @@ export class OEScriptDeleteAction extends ScriptDeleteAction {
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile);
var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => {

View File

@@ -10,7 +10,7 @@ import * as DOM from 'vs/base/browser/dom';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorControl, Position, IEditor } from 'vs/platform/editor/common/editor';
import { IEditorControl, Position, IEditor, IEditorInput } from 'vs/platform/editor/common/editor';
import { VerticalFlexibleSash, HorizontalFlexibleSash, IFlexibleSash } from 'sql/parts/query/views/flexibleSash';
import { Orientation } from 'vs/base/browser/ui/sash/sash';
@@ -31,6 +31,8 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer
import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IRange } from 'vs/editor/common/core/range';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { Emitter } from 'vs/base/common/event';
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
import { QueryInput } from 'sql/parts/query/common/queryInput';
@@ -40,7 +42,7 @@ import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
import {
RunQueryAction, CancelQueryAction, ListDatabasesAction, ListDatabasesActionItem,
ConnectDatabaseAction, ToggleConnectDatabaseAction, EstimatedQueryPlanAction,
ActualQueryPlanAction
ActualQueryPlanAction, ParseSyntaxAction
} from 'sql/parts/query/execution/queryActions';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService';
@@ -86,6 +88,10 @@ export class QueryEditor extends BaseEditor {
private _listDatabasesAction: ListDatabasesAction;
private _estimatedQueryPlanAction: EstimatedQueryPlanAction;
private _actualQueryPlanAction: ActualQueryPlanAction;
private _parseSyntaxAction: ParseSyntaxAction;
private _savedViewStates = new Map<IEditorInput, IEditorViewState>();
private _resultViewStateChangeEmitters = new Map<QueryResultsInput, { onSaveViewState: Emitter<void>; onRestoreViewState: Emitter<void> }>();
constructor(
@ITelemetryService _telemetryService: ITelemetryService,
@@ -365,6 +371,22 @@ export class QueryEditor extends BaseEditor {
return true;
}
public getAllText(): string {
if (this._sqlEditor && this._sqlEditor.getControl()) {
let control = this._sqlEditor.getControl();
let codeEditor: CodeEditor = <CodeEditor>control;
if (codeEditor) {
let value = codeEditor.getValue();
if (value !== undefined && value.length > 0) {
return value;
} else {
return '';
}
}
}
return undefined;
}
public getSelectionText(): string {
if (this._sqlEditor && this._sqlEditor.getControl()) {
let control = this._sqlEditor.getControl();
@@ -434,6 +456,7 @@ export class QueryEditor extends BaseEditor {
this._listDatabasesAction = this._instantiationService.createInstance(ListDatabasesAction, this);
this._estimatedQueryPlanAction = this._instantiationService.createInstance(EstimatedQueryPlanAction, this);
this._actualQueryPlanAction = this._instantiationService.createInstance(ActualQueryPlanAction, this);
this._parseSyntaxAction = this._instantiationService.createInstance(ParseSyntaxAction, this);
// Create HTML Elements for the taskbar
let separator = Taskbar.createTaskbarSeparator();
@@ -448,6 +471,7 @@ export class QueryEditor extends BaseEditor {
{ action: this._listDatabasesAction },
{ element: separator },
{ action: this._estimatedQueryPlanAction },
{ action: this._parseSyntaxAction }
];
this._taskbar.setContent(content);
}
@@ -481,10 +505,17 @@ export class QueryEditor extends BaseEditor {
private _updateInput(oldInput: QueryInput, newInput: QueryInput, options?: EditorOptions): TPromise<void> {
if (this._sqlEditor) {
let sqlEditorViewState = this._sqlEditor.getControl().saveViewState();
this._savedViewStates.set(this._sqlEditor.input, sqlEditorViewState);
this._sqlEditor.clearInput();
}
if (oldInput) {
let resultViewStateChangeEmitters = this._resultViewStateChangeEmitters.get(oldInput.results);
if (resultViewStateChangeEmitters) {
resultViewStateChangeEmitters.onSaveViewState.fire();
}
this._disposeEditors();
}
@@ -557,7 +588,15 @@ export class QueryEditor extends BaseEditor {
// Run all three steps synchronously
return createEditors()
.then(onEditorsCreated)
.then(doLayout);
.then(doLayout)
.then(() => {
if (this._resultViewStateChangeEmitters.has(newInput.results)) {
this._resultViewStateChangeEmitters.get(newInput.results).onRestoreViewState.fire();
}
if (this._savedViewStates.has(newInput.sql)) {
this._sqlEditor.getControl().restoreViewState(this._savedViewStates.get(newInput.sql));
}
});
}
/**
@@ -588,6 +627,14 @@ export class QueryEditor extends BaseEditor {
*/
private _onResultsEditorCreated(resultsEditor: QueryResultsEditor, resultsInput: QueryResultsInput, options: EditorOptions): TPromise<void> {
this._resultsEditor = resultsEditor;
if (!this._resultViewStateChangeEmitters.has(resultsInput)) {
this._resultViewStateChangeEmitters.set(resultsInput, {
onRestoreViewState: new Emitter<void>(),
onSaveViewState: new Emitter<void>()
});
}
let emitters = this._resultViewStateChangeEmitters.get(resultsInput);
this._resultsEditor.setViewStateChangeEvents(emitters.onRestoreViewState.event, emitters.onSaveViewState.event);
return this._resultsEditor.setInput(resultsInput, options);
}

View File

@@ -26,6 +26,7 @@ import { IQueryComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { QueryOutputModule } from 'sql/parts/query/views/queryOutput.module';
import { QUERY_OUTPUT_SELECTOR } from 'sql/parts/query/views/queryOutput.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
export const RESULTS_GRID_DEFAULTS = {
cellPadding: [6, 10, 5],
@@ -95,6 +96,8 @@ export class QueryResultsEditor extends BaseEditor {
public static AngularSelectorString: string = 'slickgrid-container.slickgridContainer';
protected _rawOptions: BareResultsGridInfo;
protected _input: QueryResultsInput;
private _restoreViewStateEvent: Event<void>;
private _saveViewStateEvent: Event<void>;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -149,6 +152,11 @@ export class QueryResultsEditor extends BaseEditor {
return TPromise.wrap<void>(null);
}
public setViewStateChangeEvents(onRestoreViewStateEvent: Event<void>, onSaveViewStateEvent: Event<void>) {
this._restoreViewStateEvent = onRestoreViewStateEvent;
this._saveViewStateEvent = onSaveViewStateEvent;
}
/**
* Load the angular components and record for this input that we have done so
*/
@@ -169,7 +177,11 @@ export class QueryResultsEditor extends BaseEditor {
// Note: pass in input so on disposal this is cleaned up.
// Otherwise many components will be left around and be subscribed
// to events from the backing data service
let params: IQueryComponentParams = { dataService: dataService };
let params: IQueryComponentParams = {
dataService: dataService,
onSaveViewState: this._saveViewStateEvent,
onRestoreViewState: this._restoreViewStateEvent
};
bootstrapAngular(this._instantiationService,
QueryOutputModule,
this.getContainer(),

View File

@@ -25,6 +25,7 @@ import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
/**
* Action class that query-based Actions will extend. This base class automatically handles activating and
@@ -417,6 +418,55 @@ export class ListDatabasesAction extends QueryTaskbarAction {
}
}
/**
* Action class that parses the query string in the current SQL text document.
*/
export class ParseSyntaxAction extends QueryTaskbarAction {
public static EnabledClass = '';
public static ID = 'parseQueryAction';
constructor(
editor: QueryEditor,
@IConnectionManagementService connectionManagementService: IConnectionManagementService,
@IQueryManagementService private _queryManagementService: IQueryManagementService,
@INotificationService private _notificationService: INotificationService,
) {
super(connectionManagementService, editor, ParseSyntaxAction.ID, ParseSyntaxAction.EnabledClass);
this.enabled = true;
this.label = nls.localize('parseSyntaxLabel', 'Parse Query');
}
public run(): TPromise<void> {
if (!this.editor.isSelectionEmpty()) {
if (this.isConnected(this.editor)) {
let text = this.editor.getSelectionText();
if (text === '') {
text = this.editor.getAllText();
}
this._queryManagementService.parseSyntax(this.editor.connectedUri, text).then(result => {
if (result && result.parseable) {
this._notificationService.notify({
severity: Severity.Info,
message: nls.localize('queryActions.parseSyntaxSuccess', 'Commands completed successfully')
});
} else if (result && result.errors.length > 0) {
let errorMessage = nls.localize('queryActions.parseSyntaxFailure', 'Command failed: ');
this._notificationService.error(`${errorMessage}${result.errors[0]}`);
}
});
} else {
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('queryActions.notConnected', 'Please connect to a server')
});
}
}
return TPromise.as(null);
}
}
/*
* Action item that handles the dropdown (combobox) that lists the available databases.
* Based off StartDebugActionItem.

View File

@@ -17,7 +17,6 @@ import { IQueryComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { QueryComponent } from 'sql/parts/grid/views/query/query.component';
import { QueryPlanComponent } from 'sql/parts/queryPlan/queryPlan.component';
import { TopOperationsComponent } from 'sql/parts/queryPlan/topOperations.component';
import { ChartViewerComponent } from 'sql/parts/grid/views/query/chartViewer.component';
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import { PanelComponent, IPanelOptions } from 'sql/base/browser/ui/panel/panel.component';

View File

@@ -22,6 +22,7 @@ import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/boot
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editabledropdown.component';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -36,6 +37,7 @@ export const DialogModule = (params, selector: string, instantiationService: IIn
declarations: [
Checkbox,
SelectBox,
EditableDropDown,
InputBox,
DialogContainer,
WizardNavigation,

View File

@@ -125,11 +125,19 @@ export class DialogModal extends Modal {
public async done(): Promise<void> {
if (this._doneButton.enabled) {
let buttonSpinnerHandler = setTimeout(() => {
this._doneButton.enabled = false;
this._doneButton.element.innerHTML = '&nbsp';
this._doneButton.element.classList.add('validating');
}, 100);
if (await this._dialog.validateClose()) {
this._onDone.fire();
this.dispose();
this.hide();
}
clearTimeout(buttonSpinnerHandler);
this._doneButton.element.classList.remove('validating');
this.updateButtonElement(this._doneButton, this._dialog.okButton, true);
}
}

View File

@@ -31,6 +31,21 @@
margin: 0;
}
.footer-button .validating {
background-size: 15px;
background-repeat: no-repeat;
background-position: center;
}
.vs .footer-button .validating {
background-image: url("loading.svg");
}
.vs-dark .footer-button .validating,
.hc-black .footer-button .validating {
background-image: url("loading_inverse.svg");
}
.dialogModal-wizardHeader {
padding: 10px 30px;
}

View File

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

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

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

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -120,6 +120,7 @@ export class WizardModal extends Modal {
buttonElement.label = dialogButton.label;
buttonElement.enabled = requirePageValid ? dialogButton.enabled && this._wizard.pages[this._wizard.currentPage].valid : dialogButton.enabled;
dialogButton.hidden ? buttonElement.element.parentElement.classList.add('dialogModal-hidden') : buttonElement.element.parentElement.classList.remove('dialogModal-hidden');
this.setButtonsForPage(this._wizard.currentPage);
}
protected renderBody(container: HTMLElement): void {
@@ -167,7 +168,7 @@ export class WizardModal extends Modal {
this.done(validate);
return;
}
if (validate && !await this._wizard.validateNavigation(index)) {
if (validate && !await this.validateNavigation(index)) {
return;
}
this._dialogPanes.forEach((dialogPane, page) => {
@@ -185,20 +186,24 @@ export class WizardModal extends Modal {
}
private setButtonsForPage(index: number) {
if (this._wizard.pages[index - 1]) {
this._previousButton.element.parentElement.classList.remove('dialogModal-hidden');
this._previousButton.enabled = this._wizard.pages[index - 1].enabled;
} else {
this._previousButton.element.parentElement.classList.add('dialogModal-hidden');
if (this._previousButton) {
if (this._wizard.pages[index - 1]) {
this._previousButton.element.parentElement.classList.remove('dialogModal-hidden');
this._previousButton.enabled = this._wizard.pages[index - 1].enabled;
} else {
this._previousButton.element.parentElement.classList.add('dialogModal-hidden');
}
}
if (this._wizard.pages[index + 1]) {
this._nextButton.element.parentElement.classList.remove('dialogModal-hidden');
this._nextButton.enabled = this._wizard.pages[index + 1].enabled;
this._doneButton.element.parentElement.classList.add('dialogModal-hidden');
} else {
this._nextButton.element.parentElement.classList.add('dialogModal-hidden');
this._doneButton.element.parentElement.classList.remove('dialogModal-hidden');
if (this._nextButton && this._doneButton) {
if (this._wizard.pages[index + 1]) {
this._nextButton.element.parentElement.classList.remove('dialogModal-hidden');
this._nextButton.enabled = this._wizard.pages[index + 1].enabled;
this._doneButton.element.parentElement.classList.add('dialogModal-hidden');
} else {
this._nextButton.element.parentElement.classList.add('dialogModal-hidden');
this._doneButton.element.parentElement.classList.remove('dialogModal-hidden');
}
}
}
@@ -225,7 +230,7 @@ export class WizardModal extends Modal {
public async done(validate: boolean = true): Promise<void> {
if (this._doneButton.enabled) {
if (validate && !await this._wizard.validateNavigation(undefined)) {
if (validate && !await this.validateNavigation(undefined)) {
return;
}
this._onDone.fire();
@@ -240,6 +245,20 @@ export class WizardModal extends Modal {
this.hide();
}
private async validateNavigation(newPage: number): Promise<boolean> {
let button = newPage === undefined ? this._doneButton : this._nextButton;
let buttonSpinnerHandler = setTimeout(() => {
button.enabled = false;
button.element.innerHTML = '&nbsp';
button.element.classList.add('validating');
}, 100);
let navigationValid = await this._wizard.validateNavigation(newPage);
clearTimeout(buttonSpinnerHandler);
button.element.classList.remove('validating');
this.updateButtonElement(button, newPage === undefined ? this._wizard.doneButton : this._wizard.nextButton, true);
return navigationValid;
}
protected hide(): void {
super.hide();
}

View File

@@ -8,9 +8,12 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ConnectionContextKey } from 'sql/parts/connection/common/connectionContextKey';
import { IBootstrapParams } from './bootstrapService';
import { Event } from 'vs/base/common/event';
export interface IQueryComponentParams extends IBootstrapParams {
dataService: DataService;
onSaveViewState: Event<void>;
onRestoreViewState: Event<void>;
}
export interface IEditDataComponentParams extends IBootstrapParams {

2
src/sql/sqlops.d.ts vendored
View File

@@ -777,7 +777,7 @@ declare module 'sqlops' {
export interface SyntaxParseResult {
parseable: boolean;
errorMessages: string[];
errors: string[];
}
// Query Batch Notification -----------------------------------------------------------------------

View File

@@ -23,6 +23,7 @@ declare module 'sqlops' {
checkBox(): ComponentBuilder<CheckBoxComponent>;
radioButton(): ComponentBuilder<RadioButtonComponent>;
webView(): ComponentBuilder<WebViewComponent>;
editor(): ComponentBuilder<EditorComponent>;
text(): ComponentBuilder<TextComponent>;
button(): ComponentBuilder<ButtonComponent>;
dropDown(): ComponentBuilder<DropDownComponent>;
@@ -375,6 +376,7 @@ declare module 'sqlops' {
export interface TableComponentProperties extends ComponentProperties {
data: any[][];
columns: string[] | TableColumn[];
fontSize?: number | string;
selectedRows?: number[];
}
@@ -390,7 +392,8 @@ declare module 'sqlops' {
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
boolean = 'boolean',
editableCategory = 'editableCategory'
}
export interface RadioButtonProperties {
@@ -434,9 +437,24 @@ declare module 'sqlops' {
html?: string;
}
/**
* Editor properties for the editor component
*/
export interface EditorProperties {
/**
* The content inside the text editor
*/
content?: string;
/**
* The languge mode for this text editor. The language mode is SQL by default.
*/
languageMode?: string
}
export interface ButtonProperties extends ComponentProperties, ComponentWithIcon {
label?: string;
isFile?: boolean;
fileContent?: string;
}
export interface LoadingComponentProperties {
@@ -502,7 +520,21 @@ declare module 'sqlops' {
onMessage: vscode.Event<any>;
}
export interface ButtonComponent extends Component {
/**
* Editor component for displaying the text code editor
*/
export interface EditorComponent extends Component {
/**
* The content inside the text editor
*/
content: string;
/**
* The languge mode for this text editor. The language mode is SQL by default.
*/
languageMode: string;
}
export interface ButtonComponent extends Component, ButtonProperties {
label: string;
iconPath: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
onDidClick: vscode.Event<any>;
@@ -1078,4 +1110,21 @@ declare module 'sqlops' {
export function startBackgroundOperation(operationInfo: BackgroundOperationInfo): void;
}
export namespace connection {
/**
* List the databases that can be accessed from the given connection
* @param {string} connectionId The ID of the connection
* @returns {string[]} An list of names of databases
*/
export function listDatabases(connectionId: string): Thenable<string[]>;
/**
* Get a URI corresponding to the given connection so that it can be used with data
* providers and other APIs that require a connection API.
* Note: If the given connection corresponds to multiple URIs this may return any of them
* @param connectionId The ID of the connection
*/
export function getUriForConnection(connectionId: string): Thenable<string>;
}
}

View File

@@ -146,7 +146,8 @@ export enum ModelComponentTypes {
Group,
Toolbar,
LoadingComponent,
FileBrowserTree
FileBrowserTree,
Editor
}
export interface IComponentShape {
@@ -272,7 +273,8 @@ export enum DataProviderType {
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
boolean = 'boolean',
editableCategory = 'editableCategory'
}
export enum CardType {

View File

@@ -8,7 +8,7 @@ import { ExtHostConnectionManagementShape, SqlMainContext, MainThreadConnectionM
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
import * as sqlops from 'sqlops';
export class ExtHostConnectionManagement extends ExtHostConnectionManagementShape {
export class ExtHostConnectionManagement extends ExtHostConnectionManagementShape {
private _proxy: MainThreadConnectionManagementShape;
@@ -27,7 +27,15 @@ export class ExtHostConnectionManagement extends ExtHostConnectionManagementShap
return this._proxy.$getCurrentConnection();
}
public $getCredentials(connectionId: string): Thenable<{ [name: string]: string}> {
public $getCredentials(connectionId: string): Thenable<{ [name: string]: string }> {
return this._proxy.$getCredentials(connectionId);
}
public $listDatabases(connectionId: string): Thenable<string[]> {
return this._proxy.$listDatabases(connectionId);
}
public $getUriForConnection(connectionId: string): Thenable<string> {
return this._proxy.$getUriForConnection(connectionId);
}
}

View File

@@ -101,6 +101,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
return builder;
}
editor(): sqlops.ComponentBuilder<sqlops.EditorComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.EditorComponent> = this.getComponentBuilder(new EditorWrapper(this._proxy, this._handle, id), id);
this._componentBuilders.set(id, builder);
return builder;
}
button(): sqlops.ComponentBuilder<sqlops.ButtonComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.ButtonComponent> = this.getComponentBuilder(new ButtonWrapper(this._proxy, this._handle, id), id);
@@ -740,6 +747,28 @@ class WebViewWrapper extends ComponentWrapper implements sqlops.WebViewComponent
}
}
class EditorWrapper extends ComponentWrapper implements sqlops.EditorComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
super(proxy, handle, ModelComponentTypes.Editor, id);
this.properties = {};
}
public get content(): string {
return this.properties['content'];
}
public set content(v: string) {
this.setProperty('content', v);
}
public get languageMode(): string {
return this.properties['languageMode'];
}
public set languageMode(v: string) {
this.setProperty('languageMode', v);
}
}
class RadioButtonWrapper extends ComponentWrapper implements sqlops.RadioButtonComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
@@ -818,6 +847,14 @@ class TableComponentWrapper extends ComponentWrapper implements sqlops.TableComp
this.setProperty('columns', v);
}
public get fontSize(): number | string {
return this.properties['fontSize'];
}
public set fontSize(size: number | string) {
this.setProperty('fontSize', size);
}
public get selectedRows(): number[] {
return this.properties['selectedRows'];
}

View File

@@ -213,12 +213,16 @@ class BackgroundOperationHandler {
}
public createOperation(): void {
if (!this._operationInfo) {
return;
}
if (!this._operationInfo.operationId) {
let uniqueId = generateUuid();
this._operationInfo.operationId = 'OperationId' + uniqueId + this._name;
}
if (this._operationInfo && this._operationInfo.operation) {
if (this._operationInfo.operation) {
this._extHostTaskManagement.$registerTask(this._operationInfo);
}
}

View File

@@ -49,6 +49,17 @@ export class MainThreadConnectionManagement implements MainThreadConnectionManag
return Promise.resolve(this._connectionManagementService.getActiveConnectionCredentials(connectionId));
}
public async $listDatabases(connectionId: string): Promise<string[]> {
let connection = this._connectionManagementService.getActiveConnections().find(profile => profile.id === connectionId);
let connectionUri = this._connectionManagementService.getConnectionUri(connection);
let result = await this._connectionManagementService.listDatabases(connectionUri);
return result.databaseNames;
}
public $getUriForConnection(connectionId: string): Thenable<string> {
return Promise.resolve(this._connectionManagementService.getConnectionUriFromId(connectionId));
}
private convertConnection(profile: IConnectionProfile): sqlops.connection.Connection {
if (!profile) {
return undefined;

View File

@@ -101,6 +101,12 @@ export function createApiFactory(
},
getCredentials(connectionId: string): Thenable<{ [name: string]: string }> {
return extHostConnectionManagement.$getCredentials(connectionId);
},
listDatabases(connectionId: string): Thenable<string[]> {
return extHostConnectionManagement.$listDatabases(connectionId);
},
getUriForConnection(connectionId: string): Thenable<string> {
return extHostConnectionManagement.$getUriForConnection(connectionId);
}
};

View File

@@ -473,6 +473,8 @@ export interface MainThreadConnectionManagementShape extends IDisposable {
$getActiveConnections(): Thenable<sqlops.connection.Connection[]>;
$getCurrentConnection(): Thenable<sqlops.connection.Connection>;
$getCredentials(connectionId: string): Thenable<{ [name: string]: string }>;
$listDatabases(connectionId: string): Thenable<string[]>;
$getUriForConnection(connectionId: string): Thenable<string>;
}
export interface MainThreadCredentialManagementShape extends IDisposable {

View File

@@ -108,7 +108,7 @@ export function GetScriptOperationName(operation: ScriptOperation) {
export function connectIfNotAlreadyConnected(connectionProfile: IConnectionProfile, connectionService: IConnectionManagementService): Promise<void> {
return new Promise<void>((resolve, reject) => {
let connectionID = connectionService.getConnectionId(connectionProfile);
let connectionID = connectionService.getConnectionUri(connectionProfile);
let uri: string = connectionService.getFormattedUri(connectionID, connectionProfile);
if (!connectionService.isConnected(uri)) {
let options: IConnectionCompletionOptions = {

View File

@@ -756,8 +756,8 @@ suite('SQL ConnectionManagementService tests', () => {
connect(ownerUri, undefined, false, connectionProfileWithoutDb).then(() => {
try {
// If I get the URI for the connection with or without a database from the connection management service
let actualUriWithDb = connectionManagementService.getConnectionId(connectionProfileWithDb);
let actualUriWithoutDb = connectionManagementService.getConnectionId(connectionProfileWithoutDb);
let actualUriWithDb = connectionManagementService.getConnectionUri(connectionProfileWithDb);
let actualUriWithoutDb = connectionManagementService.getConnectionUri(connectionProfileWithoutDb);
// Then the retrieved URIs should match the one on the connection
let expectedUri = Utils.generateUri(connectionProfileWithoutDb);
@@ -804,4 +804,33 @@ suite('SQL ConnectionManagementService tests', () => {
let credentials = connectionManagementService.getActiveConnectionCredentials(profile.id);
assert.equal(credentials['password'], profile.options['password']);
});
test('getConnectionUriFromId returns a URI of an active connection with the given id', () => {
let profile = Object.assign({}, connectionProfile);
profile.options = {password: profile.password};
profile.id = 'test_id';
let uri = 'test_initial_uri';
connectionStatusManager.addConnection(profile, uri);
(connectionManagementService as any)._connectionStatusManager = connectionStatusManager;
// If I call getConnectionUriFromId on the given connection
let foundUri = connectionManagementService.getConnectionUriFromId(profile.id);
// Then the returned URI matches the connection's
assert.equal(foundUri, Utils.generateUri(new ConnectionProfile(capabilitiesService, profile)));
});
test('getConectionUriFromId returns undefined if the given connection is not active', () => {
let profile = Object.assign({}, connectionProfile);
profile.options = {password: profile.password};
profile.id = 'test_id';
connectionStatusManager.addConnection(profile, Utils.generateUri(profile));
(connectionManagementService as any)._connectionStatusManager = connectionStatusManager;
// If I call getConnectionUriFromId with a different URI than the connection's
let foundUri = connectionManagementService.getConnectionUriFromId('different_id');
// Then undefined is returned
assert.equal(foundUri, undefined);
});
});

View File

@@ -115,7 +115,7 @@ export class TestConnectionManagementService implements IConnectionManagementSer
return [];
}
getConnectionId(connectionProfile: ConnectionProfile): string {
getConnectionUri(connectionProfile: ConnectionProfile): string {
return undefined;
}
@@ -123,6 +123,10 @@ export class TestConnectionManagementService implements IConnectionManagementSer
return undefined;
}
getConnectionUriFromId(connectionId: string): string {
return undefined;
}
isConnected(fileUri: string, connectionProfile?: ConnectionProfile): boolean {
return false;
}

View File

@@ -1149,7 +1149,7 @@ declare namespace Slick {
* @param row A row index.
* @param cell A column index.
**/
public setActiveCell(row: number, cell: number): void;
public setActiveCell(row: number, cell: number, opt_editMode?: boolean, preClickModeOn?: boolean, suppressActiveCellChangedEvent?: boolean): void;
/**
* Sets CSS classes to specific grid cells by calling removeCellCssStyles(key) followed by addCellCssStyles(key, hash). key is name for this set of styles so you can reference it later - to modify it or remove it, for example. hash is a per-row-index, per-column-name nested hash of CSS classes to apply.

View File

@@ -81,6 +81,11 @@ export interface IColumnDefinition {
formatter?: (row: number, cell: any, value: any, columnDef: any, dataContext: any) => string;
isEditable?: boolean;
}
export interface ISlickColumn<T> extends Slick.Column<T> {
isEditable?: boolean;
}
export interface IGridColumnDefinition {
id: string;
type: number;

View File

@@ -568,6 +568,8 @@ export class CodeMenu {
let cut: Electron.MenuItem;
let copy: Electron.MenuItem;
let paste: Electron.MenuItem;
// {{SQL CARBON EDIT}}
let selectAll: Electron.MenuItem;
if (isMacintosh) {
undo = this.createContextAwareMenuItem(nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), 'undo', {
@@ -581,12 +583,19 @@ export class CodeMenu {
cut = this.createRoleMenuItem(nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), 'editor.action.clipboardCutAction', 'cut');
copy = this.createRoleMenuItem(nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), 'editor.action.clipboardCopyAction', 'copy');
paste = this.createRoleMenuItem(nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), 'editor.action.clipboardPasteAction', 'paste');
// {{SQL CARBON EDIT}}
selectAll = this.createContextAwareMenuItem(nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), 'editor.action.selectAll', {
inDevTools: devTools => devTools.selectAll(),
inNoWindow: () => Menu.sendActionToFirstResponder('selectAll:')
});
} else {
undo = this.createMenuItem(nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), 'undo');
redo = this.createMenuItem(nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), 'redo');
cut = this.createMenuItem(nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), 'editor.action.clipboardCutAction');
copy = this.createMenuItem(nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), 'editor.action.clipboardCopyAction');
paste = this.createMenuItem(nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), 'editor.action.clipboardPasteAction');
// {{SQL CARBON EDIT}}
selectAll = this.createMenuItem(nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), 'editor.action.selectAll');
}
const find = this.createMenuItem(nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find"), 'actions.find');
@@ -614,6 +623,8 @@ export class CodeMenu {
findInFiles,
replaceInFiles,
// {{SQL CARBON EDIT}}
selectAll,
// {{SQL CARBON EDIT}}
// __separator__(),
// toggleLineComment,
// toggleBlockComment,

View File

@@ -6,7 +6,8 @@
import { IWordAtPosition } from 'vs/editor/common/model';
export const USUAL_WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?';
// {{SQL CARBON EDIT}}
export const USUAL_WORD_SEPARATORS = '`~!#$%^&*()-=+[{]}\\|;:\'",.<>/?';
/**
* Create a word definition regular expression based on default word separators.

View File

@@ -54,6 +54,8 @@ export interface IProductConfiguration {
releaseNotesUrl: string;
// {SQL CARBON EDIT}
gettingStartedUrl: string;
// {SQL CARBON EDIT}
vscodeVersion: string;
keyboardShortcutsUrlMac: string;
keyboardShortcutsUrlLinux: string;
keyboardShortcutsUrlWin: string;

View File

@@ -458,15 +458,17 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
this.logService.trace('windowsService#openAboutDialog');
const lastActiveWindow = this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow();
// {{SQL CARBON EDIT}}
const detail = nls.localize('aboutDetail',
"Version {0}\nCommit {1}\nDate {2}\nShell {3}\nRenderer {4}\nNode {5}\nArchitecture {6}",
"Version {0}\nCommit {1}\nDate {2}\nVS Code {7}\nShell {3}\nRenderer {4}\nNode {5}\nArchitecture {6}",
app.getVersion(),
product.commit || 'Unknown',
product.date || 'Unknown',
process.versions['electron'],
process.versions['chrome'],
process.versions['node'],
process.arch
process.arch,
product.vscodeVersion
);
const buttons = [nls.localize('okButton', "OK")];

View File

@@ -40,12 +40,12 @@ interface WatermarkEntry {
// {{SQL CARBON EDIT}}
const showServers: WatermarkEntry = {
text: 'Show Servers',
text: nls.localize('watermark.showServers', 'Show Servers'),
ids: [OpenConnectionsViewletAction.ID]
};
const newSqlFile: WatermarkEntry = {
text: 'New SQL File',
text: nls.localize('watermark.newSqlFile', 'New SQL File'),
ids: [GlobalNewUntitledFileAction.ID]
};

View File

@@ -124,9 +124,11 @@ acorn@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.2.1.tgz#317ac7821826c22c702d66189ab8359675f135d7"
agent-base@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-1.0.2.tgz#6890d3fb217004b62b70f8928e0fae5f8952a706"
agent-base@4, agent-base@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
dependencies:
es6-promisify "^5.0.0"
ajv-keywords@^1.0.0:
version "1.5.1"
@@ -172,9 +174,9 @@ angular2-grid@2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/angular2-grid/-/angular2-grid-2.0.6.tgz#01fe225dc13b2822370b6c61f9a6913b3a26f989"
"angular2-slickgrid@git://github.com/Microsoft/angular2-slickgrid.git#1.3.11":
version "1.3.10"
resolved "git://github.com/Microsoft/angular2-slickgrid.git#35f00750ef2f544b17744cc167c0ff7997c114b4"
"angular2-slickgrid@github:Microsoft/angular2-slickgrid#1.3.12":
version "1.3.12"
resolved "https://codeload.github.com/Microsoft/angular2-slickgrid/tar.gz/19aafe8888d2f2eb70aec858e4b86e3c1b7b3fc8"
ansi-colors@^1.0.1:
version "1.1.0"
@@ -1300,7 +1302,7 @@ debug-fabulous@0.0.X:
lazy-debug-legacy "0.0.X"
object-assign "4.1.0"
debug@2, debug@2.2.0, debug@~2.2.0:
debug@2.2.0, debug@~2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
dependencies:
@@ -1312,6 +1314,12 @@ debug@2.6.9, debug@2.X, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3:
dependencies:
ms "2.0.0"
debug@3.1.0, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -1630,6 +1638,16 @@ es6-map@^0.1.3:
es6-symbol "~3.1.1"
event-emitter "~0.3.5"
es6-promise@^4.0.3:
version "4.2.4"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
es6-promisify@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
dependencies:
es6-promise "^4.0.3"
es6-set@~0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1"
@@ -1916,10 +1934,6 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
assign-symbols "^1.0.0"
is-extendable "^1.0.1"
extend@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4"
extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
@@ -3057,13 +3071,12 @@ http-errors@1.6.2, http-errors@~1.6.2:
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
http-proxy-agent@0.2.7:
version "0.2.7"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-0.2.7.tgz#e17fda65f0902d952ce7921e62c7ff8862655a5e"
http-proxy-agent@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
dependencies:
agent-base "~1.0.1"
debug "2"
extend "3"
agent-base "4"
debug "3.1.0"
http-signature@~0.10.0:
version "0.10.1"
@@ -3089,13 +3102,12 @@ http-signature@~1.2.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
https-proxy-agent@0.3.6:
version "0.3.6"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-0.3.6.tgz#713fa38e5d353f50eb14a342febe29033ed1619b"
https-proxy-agent@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies:
agent-base "~1.0.1"
debug "2"
extend "3"
agent-base "^4.1.0"
debug "^3.1.0"
husky@^0.13.1:
version "0.13.4"
@@ -5889,9 +5901,9 @@ slice-ansi@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
"slickgrid@github:anthonydresser/SlickGrid#2.3.23":
version "2.3.23"
resolved "https://codeload.github.com/anthonydresser/SlickGrid/tar.gz/a88c0c25fd6cbe01be86b3018f523987a198a6b6"
"slickgrid@github:anthonydresser/SlickGrid#2.3.25":
version "2.3.25"
resolved "https://codeload.github.com/anthonydresser/SlickGrid/tar.gz/5fa498a3df7ed671958bedeac1863c41352c7825"
dependencies:
jquery ">=1.8.0"
jquery-ui ">=1.8.0"