mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c48320ce0 | ||
|
|
96ecf79d32 | ||
|
|
6d243e0ac8 | ||
|
|
8bf60f133f | ||
|
|
3b9a3aba87 | ||
|
|
f0c986858a | ||
|
|
7ee13b4afb | ||
|
|
bca4102542 | ||
|
|
31ac8d6acc | ||
|
|
e82b7615b3 | ||
|
|
6f10f7a21a | ||
|
|
46fb68214f | ||
|
|
c2381424bc | ||
|
|
31328e79be | ||
|
|
96657e300a | ||
|
|
a2e6aa9e64 | ||
|
|
8c2d79e9cf | ||
|
|
cd140b5527 | ||
|
|
a0456bf4f7 | ||
|
|
55e3947cf7 | ||
|
|
db5156e4cd | ||
|
|
eece0677a7 | ||
|
|
24e8c20511 | ||
|
|
886717d330 | ||
|
|
26b27a616a | ||
|
|
0c663e5555 | ||
|
|
0f087915f6 | ||
|
|
a78fa9c0f2 | ||
|
|
b1752ea635 | ||
|
|
ec150917c2 | ||
|
|
7a9a69c439 | ||
|
|
9e9862c6f0 | ||
|
|
7b76d929cd | ||
|
|
1811dfa423 | ||
|
|
3abbc8fd97 | ||
|
|
4b88b67bed | ||
|
|
a2734807ca | ||
|
|
d0d4df313e | ||
|
|
1efd5e6502 | ||
|
|
b12cac0ac3 | ||
|
|
578aa6ccd2 | ||
|
|
3b90530717 | ||
|
|
f199c7a63c | ||
|
|
50a2526d1f | ||
|
|
7fb8a28b59 | ||
|
|
30a825438e | ||
|
|
f63da13210 | ||
|
|
08d73675d4 | ||
|
|
f0f6c00a0e | ||
|
|
965458ca74 | ||
|
|
b30f7ee41c | ||
|
|
a2eb53ce0b | ||
|
|
619c816e7f | ||
|
|
3d6fb7a8fa | ||
|
|
891624c085 | ||
|
|
a7c4686980 | ||
|
|
93aa052856 | ||
|
|
61d05f6782 | ||
|
|
60ccae48f1 | ||
|
|
4b454581fd | ||
|
|
f0aadebd2c | ||
|
|
d3f0ac7954 | ||
|
|
134f76c17f | ||
|
|
b9c877a109 | ||
|
|
aa243a8aae | ||
|
|
e5c1c6f544 | ||
|
|
9e9f85079e | ||
|
|
ae8e2e1f89 | ||
|
|
b2c70e9301 | ||
|
|
e022f4a0d1 | ||
|
|
8bc32e6371 | ||
|
|
f739c47984 | ||
|
|
13fb9fdfd2 | ||
|
|
9a5f51bfbf | ||
|
|
06bab6a38c | ||
|
|
c35a14d8fd | ||
|
|
b914073147 | ||
|
|
04ae18143b | ||
|
|
efe8e81b6e | ||
|
|
c33ddfabf9 | ||
|
|
8ec5451e64 | ||
|
|
20853ddf7e | ||
|
|
091d4cb924 | ||
|
|
cd0210c88a | ||
|
|
ed10f984b6 | ||
|
|
087a6a0810 | ||
|
|
495b4ee7c2 | ||
|
|
7833c28b7a | ||
|
|
bbfb68b082 | ||
|
|
b32e7a777c | ||
|
|
a327889d05 | ||
|
|
5ac89e5a49 | ||
|
|
849653927a | ||
|
|
3545483fc1 | ||
|
|
e5a1896414 | ||
|
|
bec8e72688 | ||
|
|
86748e6d69 | ||
|
|
596f09f754 | ||
|
|
563e25f073 | ||
|
|
1800d0baaf | ||
|
|
2182658301 | ||
|
|
3990719054 | ||
|
|
071b510fba | ||
|
|
d2d2ade9f7 | ||
|
|
d97d2e5c91 | ||
|
|
a6ba44e435 | ||
|
|
4967e630fb | ||
|
|
2508464fde | ||
|
|
782623cba9 | ||
|
|
36045c5381 | ||
|
|
7e49167530 | ||
|
|
a680a2a7dc | ||
|
|
4734451f5f | ||
|
|
dafb780987 | ||
|
|
5fba3e31b4 | ||
|
|
0315020cf0 | ||
|
|
6dca896768 | ||
|
|
90454fbe87 | ||
|
|
f9b2136494 | ||
|
|
3d1c1aefb3 | ||
|
|
e6a65599fd | ||
|
|
32ac907403 | ||
|
|
3af05d62b2 | ||
|
|
b5d8dfa509 | ||
|
|
612987d1e5 | ||
|
|
916598e029 | ||
|
|
c45c634938 | ||
|
|
1bed0d0733 | ||
|
|
e0075b7633 | ||
|
|
0029767561 | ||
|
|
93f9828d04 | ||
|
|
c484af0901 | ||
|
|
b2a96bbe50 | ||
|
|
2414f43757 | ||
|
|
22c54a9917 | ||
|
|
a14c0351ba | ||
|
|
31f75a46c8 | ||
|
|
08d1e9cd73 | ||
|
|
0da83b5d4b | ||
|
|
37d7266751 | ||
|
|
ddfb61b46a | ||
|
|
9a2e8cffed | ||
|
|
e9299d1991 | ||
|
|
08d97cc795 |
9
.vscode/launch.json
vendored
9
.vscode/launch.json
vendored
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
@@ -9,7 +8,7 @@
|
||||
"program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js",
|
||||
"stopOnEntry": true,
|
||||
"args": [
|
||||
"watch-extension:json-client"
|
||||
"hygiene"
|
||||
],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
@@ -87,7 +86,11 @@
|
||||
"runtimeArgs": [
|
||||
"--inspect=5875"
|
||||
],
|
||||
"webRoot": "${workspaceFolder}"
|
||||
"skipFiles": [
|
||||
"**/winjs*.js"
|
||||
],
|
||||
"webRoot": "${workspaceFolder}",
|
||||
"timeout": 15000
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
|
||||
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -10,6 +10,9 @@
|
||||
"when": "$(basename).ts"
|
||||
}
|
||||
},
|
||||
"files.associations": {
|
||||
"OSSREADME.json": "jsonc"
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/bower_components": true,
|
||||
@@ -35,6 +38,5 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
|
||||
2
.yarnrc
2
.yarnrc
@@ -1,3 +1,3 @@
|
||||
disturl "https://atom.io/download/electron"
|
||||
target "1.7.11"
|
||||
target "1.7.12"
|
||||
runtime "electron"
|
||||
|
||||
30
CHANGELOG.md
30
CHANGELOG.md
@@ -1,5 +1,35 @@
|
||||
# Change Log
|
||||
|
||||
## Version 0.28.6
|
||||
* Release date: April 25, 2018
|
||||
* Release status: Public Preview
|
||||
|
||||
## What's new in this version
|
||||
The April Public Preview release contains some of the following highlights.
|
||||
|
||||
* Improvements to SQL Agent *Preview* extension
|
||||
* Accessibility improvements for keyboard navigation, screen reader support and high-contrast mode.
|
||||
* Improved large and protected file support for saving Admin protected and >256M files within SQL Ops Studio
|
||||
* Integrated Terminal splitting to work with multiple open terminals at once
|
||||
* Reduced installation on-disk file count foot print for faster installs and startup times
|
||||
* Improvements to Server Reports extension
|
||||
* Continue to fix GitHub issues
|
||||
|
||||
## Version 0.27.3
|
||||
* Release date: March 28, 2017
|
||||
* Release status: Public Preview
|
||||
|
||||
## What's new in this version
|
||||
The March Public Preview release enables some key aspects of the SQL Operations
|
||||
Studio extensibility story. Here are some highlights in this release.
|
||||
|
||||
* Enhance the Manage Dashboard extensibility model to support tabbed Insights and Configuration panes
|
||||
* Dashboard Insights extensions for `sp_whoisactive` from [whoisactive.com](http://whoisactive.com)
|
||||
* Extension Manager enables simple acquisition of 1st-party and 3rd-party extensions
|
||||
* Add additional Extensibility APIs for `connection` and `objectexplorer` management
|
||||
* Community Localization open for 10 languages
|
||||
* Continue to fix important customer impacting GitHub issues
|
||||
|
||||
## Version 0.26.7
|
||||
* Release date: February 16, 2017
|
||||
* Release status: Public Preview Hotfix 1
|
||||
|
||||
28
README.md
28
README.md
@@ -4,16 +4,16 @@
|
||||
|
||||
SQL Operations Studio is a data management tool that enables you to work with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
|
||||
|
||||
**Download SQL Operations Studio February Public Preview**
|
||||
**Download SQL Operations Studio March Public Preview**
|
||||
|
||||
Platform | Link
|
||||
-- | --
|
||||
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=867998
|
||||
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=867997
|
||||
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=867999
|
||||
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=868000
|
||||
Linux DEB | https://go.microsoft.com/fwlink/?linkid=868002
|
||||
Linux RPM | https://go.microsoft.com/fwlink/?linkid=868001
|
||||
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=872717
|
||||
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=872718
|
||||
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=872719
|
||||
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=872720
|
||||
Linux DEB | https://go.microsoft.com/fwlink/?linkid=872722
|
||||
Linux RPM | https://go.microsoft.com/fwlink/?linkid=872721
|
||||
|
||||
Go to our [download page](https://aka.ms/sqlopsstudio) for more specific instructions.
|
||||
|
||||
@@ -21,14 +21,6 @@ Try out the latest insiders build from `master` at https://github.com/Microsoft/
|
||||
|
||||
See the [change log](https://github.com/Microsoft/sqlopsstudio/blob/master/CHANGELOG.md) for additional details of what's in this release.
|
||||
|
||||
**Design Discussions**
|
||||
|
||||
The SQL Operations Studio team would like to incorporate community feedback earlier in the development process. To facilitate this, we'd like to share our designs while features are actively being built.
|
||||
|
||||
We're currently collecting input on the **SQL Agent** experience and enhancements to the Manage Dashboard that we're calling **"Command Center"**. We'll add additional design feedback requests below as we start work in new feature areas. Please leave comments on these issues to help us understand your requirements and shape feature development.
|
||||
|
||||
* [#750 Seeking community feedback on SQL Agent UX prototype](https://github.com/Microsoft/sqlopsstudio/issues/750)
|
||||
|
||||
**Feature Highlights**
|
||||
|
||||
- Cross-Platform DB management for Windows, macOS and Linux with simple XCopy deployment
|
||||
@@ -69,6 +61,8 @@ The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.micro
|
||||
## Contributions and "thank you"
|
||||
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
|
||||
|
||||
* westerncj for `Removed duplicate contribution from README.md (#753)`
|
||||
* ntovas for `Fix for duplicate extensions shown in "Save File" dialog. (#779)`
|
||||
* SebastianPfliegel for `Add cursor snippet (#475)`
|
||||
* mikaoelitiana for fix: `revert README and CONTRIBUTING after last VSCode merge (#574)`
|
||||
* alextercete for `Reinstate menu item to install from VSIX (#682)`
|
||||
@@ -79,6 +73,10 @@ We would like to thank all our users who raised issues, and in particular the fo
|
||||
* stebet for `Fix #153: Fixing sql snippets that failed on a DB with case-sensitive collation. (#152)`
|
||||
* SebastianPfliegel `Remove sqlExtensionHelp (#312)`
|
||||
* olljanat for `Implemented npm version check (#314)`
|
||||
* Adam Mechanic for helping with the `whoisactive` extension
|
||||
* All community localization contributors *(will get list of individuals next month)*
|
||||
|
||||
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)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ install:
|
||||
build_script:
|
||||
- yarn
|
||||
- .\node_modules\.bin\gulp electron
|
||||
- .\node_modules\.bin\tsc -p .\src\tsconfig.monaco.json --noEmit
|
||||
- npm run compile
|
||||
|
||||
test_script:
|
||||
|
||||
12
build/builtInExtensions.json
Normal file
12
build/builtInExtensions.json
Normal file
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"name": "ms-vscode.node-debug",
|
||||
"version": "1.21.8",
|
||||
"repo": "https://github.com/Microsoft/vscode-node-debug"
|
||||
},
|
||||
{
|
||||
"name": "ms-vscode.node-debug2",
|
||||
"version": "1.21.2",
|
||||
"repo": "https://github.com/Microsoft/vscode-node-debug2"
|
||||
}
|
||||
]
|
||||
20
build/builtin/.eslintrc
Normal file
20
build/builtin/.eslintrc
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true,
|
||||
"browser": true
|
||||
},
|
||||
"rules": {
|
||||
"no-console": 0,
|
||||
"no-cond-assign": 0,
|
||||
"no-unused-vars": 1,
|
||||
"no-extra-semi": "warn",
|
||||
"semi": "warn"
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true
|
||||
}
|
||||
}
|
||||
}
|
||||
126
build/builtin/browser-main.js
Normal file
126
build/builtin/browser-main.js
Normal file
@@ -0,0 +1,126 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
// @ts-ignore review
|
||||
const { remote } = require('electron');
|
||||
const dialog = remote.dialog;
|
||||
|
||||
const builtInExtensionsPath = path.join(__dirname, '..', 'builtInExtensions.json');
|
||||
const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json');
|
||||
|
||||
function readJson(filePath) {
|
||||
return JSON.parse(fs.readFileSync(filePath, { encoding: 'utf8' }));
|
||||
}
|
||||
|
||||
function writeJson(filePath, obj) {
|
||||
fs.writeFileSync(filePath, JSON.stringify(obj, null, 2));
|
||||
}
|
||||
|
||||
function renderOption(form, id, title, value, checked) {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'radio';
|
||||
input.id = id;
|
||||
input.name = 'choice';
|
||||
input.value = value;
|
||||
input.checked = !!checked;
|
||||
form.appendChild(input);
|
||||
|
||||
const label = document.createElement('label');
|
||||
label.setAttribute('for', id);
|
||||
label.textContent = title;
|
||||
form.appendChild(label);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
function render(el, state) {
|
||||
function setState(state) {
|
||||
try {
|
||||
writeJson(controlFilePath, state.control);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
el.innerHTML = '';
|
||||
render(el, state);
|
||||
}
|
||||
|
||||
const ul = document.createElement('ul');
|
||||
const { builtin, control } = state;
|
||||
|
||||
for (const ext of builtin) {
|
||||
const controlState = control[ext.name] || 'marketplace';
|
||||
|
||||
const li = document.createElement('li');
|
||||
ul.appendChild(li);
|
||||
|
||||
const name = document.createElement('code');
|
||||
name.textContent = ext.name;
|
||||
li.appendChild(name);
|
||||
|
||||
const form = document.createElement('form');
|
||||
li.appendChild(form);
|
||||
|
||||
const marketplaceInput = renderOption(form, `marketplace-${ext.name}`, 'Marketplace', 'marketplace', controlState === 'marketplace');
|
||||
marketplaceInput.onchange = function () {
|
||||
control[ext.name] = 'marketplace';
|
||||
setState({ builtin, control });
|
||||
};
|
||||
|
||||
const disabledInput = renderOption(form, `disabled-${ext.name}`, 'Disabled', 'disabled', controlState === 'disabled');
|
||||
disabledInput.onchange = function () {
|
||||
control[ext.name] = 'disabled';
|
||||
setState({ builtin, control });
|
||||
};
|
||||
|
||||
let local = undefined;
|
||||
|
||||
if (controlState !== 'marketplace' && controlState !== 'disabled') {
|
||||
local = controlState;
|
||||
}
|
||||
|
||||
const localInput = renderOption(form, `local-${ext.name}`, 'Local', 'local', !!local);
|
||||
localInput.onchange = function () {
|
||||
const result = dialog.showOpenDialog(remote.getCurrentWindow(), {
|
||||
title: 'Choose Folder',
|
||||
properties: ['openDirectory']
|
||||
});
|
||||
|
||||
if (result && result.length >= 1) {
|
||||
control[ext.name] = result[0];
|
||||
}
|
||||
|
||||
setState({ builtin, control });
|
||||
};
|
||||
|
||||
if (local) {
|
||||
const localSpan = document.createElement('code');
|
||||
localSpan.className = 'local';
|
||||
localSpan.textContent = local;
|
||||
form.appendChild(localSpan);
|
||||
}
|
||||
}
|
||||
|
||||
el.appendChild(ul);
|
||||
}
|
||||
|
||||
function main() {
|
||||
const el = document.getElementById('extensions');
|
||||
const builtin = readJson(builtInExtensionsPath);
|
||||
let control;
|
||||
|
||||
try {
|
||||
control = readJson(controlFilePath);
|
||||
} catch (err) {
|
||||
control = {};
|
||||
}
|
||||
|
||||
render(el, { builtin, control });
|
||||
}
|
||||
|
||||
window.onload = main;
|
||||
46
build/builtin/index.html
Normal file
46
build/builtin/index.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Manage Built-in Extensions</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="browser-main.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'Menlo', 'Courier New', 'Courier', monospace;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
padding: 0.3em 0;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
form {
|
||||
padding: 0.3em 0 0.3em 0.3em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Built-in Extensions</h1>
|
||||
<div id="extensions"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
20
build/builtin/main.js
Normal file
20
build/builtin/main.js
Normal file
@@ -0,0 +1,20 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
const url = require('url');
|
||||
const path = require('path');
|
||||
|
||||
let window = null;
|
||||
|
||||
app.once('ready', () => {
|
||||
window = new BrowserWindow({ width: 800, height: 600 });
|
||||
window.setMenuBarVisibility(false);
|
||||
window.loadURL(url.format({ pathname: path.join(__dirname, 'index.html'), protocol: 'file:', slashes: true }));
|
||||
// window.webContents.openDevTools();
|
||||
window.once('closed', () => window = null);
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => app.quit());
|
||||
5
build/builtin/package.json
Normal file
5
build/builtin/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "builtin",
|
||||
"version": "0.1.0",
|
||||
"main": "main.js"
|
||||
}
|
||||
@@ -12,6 +12,7 @@ var File = require('vinyl');
|
||||
|
||||
var root = path.dirname(__dirname);
|
||||
var sha1 = util.getVersion(root);
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
var semver = require('./monaco/package.json').version;
|
||||
var headerVersion = semver + '(' + sha1 + ')';
|
||||
|
||||
@@ -21,14 +22,14 @@ var editorEntryPoints = [
|
||||
{
|
||||
name: 'vs/editor/editor.main',
|
||||
include: [],
|
||||
exclude: [ 'vs/css', 'vs/nls' ],
|
||||
prepend: [ 'out-build/vs/css.js', 'out-build/vs/nls.js' ],
|
||||
exclude: ['vs/css', 'vs/nls'],
|
||||
prepend: ['out-build/vs/css.js', 'out-build/vs/nls.js'],
|
||||
},
|
||||
{
|
||||
name: 'vs/base/common/worker/simpleWorker',
|
||||
include: [ 'vs/editor/common/services/editorSimpleWorker' ],
|
||||
prepend: [ 'vs/loader.js' ],
|
||||
append: [ 'vs/base/worker/workerMain' ],
|
||||
include: ['vs/editor/common/services/editorSimpleWorker'],
|
||||
prepend: ['vs/loader.js'],
|
||||
append: ['vs/base/worker/workerMain'],
|
||||
dest: 'vs/base/worker/workerMain.js'
|
||||
}
|
||||
];
|
||||
@@ -79,14 +80,15 @@ gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-client-build'],
|
||||
bundleLoader: false,
|
||||
header: BUNDLED_FILE_HEADER,
|
||||
bundleInfo: true,
|
||||
out: 'out-editor'
|
||||
out: 'out-editor',
|
||||
languages: undefined
|
||||
}));
|
||||
|
||||
gulp.task('clean-minified-editor', util.rimraf('out-editor-min'));
|
||||
gulp.task('minify-editor', ['clean-minified-editor', 'optimize-editor'], common.minifyTask('out-editor'));
|
||||
|
||||
gulp.task('clean-editor-distro', util.rimraf('out-monaco-editor-core'));
|
||||
gulp.task('editor-distro', ['clean-editor-distro', 'minify-editor', 'optimize-editor'], function() {
|
||||
gulp.task('editor-distro', ['clean-editor-distro', 'minify-editor', 'optimize-editor'], function () {
|
||||
return es.merge(
|
||||
// other assets
|
||||
es.merge(
|
||||
@@ -97,17 +99,17 @@ gulp.task('editor-distro', ['clean-editor-distro', 'minify-editor', 'optimize-ed
|
||||
|
||||
// package.json
|
||||
gulp.src('build/monaco/package.json')
|
||||
.pipe(es.through(function(data) {
|
||||
.pipe(es.through(function (data) {
|
||||
var json = JSON.parse(data.contents.toString());
|
||||
json.private = false;
|
||||
data.contents = new Buffer(JSON.stringify(json, null, ' '));
|
||||
data.contents = Buffer.from(JSON.stringify(json, null, ' '));
|
||||
this.emit('data', data);
|
||||
}))
|
||||
.pipe(gulp.dest('out-monaco-editor-core')),
|
||||
|
||||
// README.md
|
||||
gulp.src('build/monaco/README-npm.md')
|
||||
.pipe(es.through(function(data) {
|
||||
.pipe(es.through(function (data) {
|
||||
this.emit('data', new File({
|
||||
path: data.path.replace(/README-npm\.md/, 'README.md'),
|
||||
base: data.base,
|
||||
@@ -124,10 +126,10 @@ gulp.task('editor-distro', ['clean-editor-distro', 'minify-editor', 'optimize-ed
|
||||
// min folder
|
||||
es.merge(
|
||||
gulp.src('out-editor-min/**/*')
|
||||
).pipe(filterStream(function(path) {
|
||||
).pipe(filterStream(function (path) {
|
||||
// no map files
|
||||
return !/(\.js\.map$)|(nls\.metadata\.json$)|(bundleInfo\.json$)/.test(path);
|
||||
})).pipe(es.through(function(data) {
|
||||
})).pipe(es.through(function (data) {
|
||||
// tweak the sourceMappingURL
|
||||
if (!/\.js$/.test(data.path)) {
|
||||
this.emit('data', data);
|
||||
@@ -140,49 +142,50 @@ gulp.task('editor-distro', ['clean-editor-distro', 'minify-editor', 'optimize-ed
|
||||
var newStr = '//# sourceMappingURL=' + relativePathToMap.replace(/\\/g, '/');
|
||||
strContents = strContents.replace(/\/\/\# sourceMappingURL=[^ ]+$/, newStr);
|
||||
|
||||
data.contents = new Buffer(strContents);
|
||||
data.contents = Buffer.from(strContents);
|
||||
this.emit('data', data);
|
||||
})).pipe(gulp.dest('out-monaco-editor-core/min')),
|
||||
|
||||
// min-maps folder
|
||||
es.merge(
|
||||
gulp.src('out-editor-min/**/*')
|
||||
).pipe(filterStream(function(path) {
|
||||
).pipe(filterStream(function (path) {
|
||||
// no map files
|
||||
return /\.js\.map$/.test(path);
|
||||
})).pipe(gulp.dest('out-monaco-editor-core/min-maps'))
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('analyze-editor-distro', function() {
|
||||
gulp.task('analyze-editor-distro', function () {
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
var bundleInfo = require('../out-editor/bundleInfo.json');
|
||||
var graph = bundleInfo.graph;
|
||||
var bundles = bundleInfo.bundles;
|
||||
|
||||
var inverseGraph = {};
|
||||
Object.keys(graph).forEach(function(module) {
|
||||
Object.keys(graph).forEach(function (module) {
|
||||
var dependencies = graph[module];
|
||||
dependencies.forEach(function(dep) {
|
||||
dependencies.forEach(function (dep) {
|
||||
inverseGraph[dep] = inverseGraph[dep] || [];
|
||||
inverseGraph[dep].push(module);
|
||||
});
|
||||
});
|
||||
|
||||
var detailed = {};
|
||||
Object.keys(bundles).forEach(function(entryPoint) {
|
||||
Object.keys(bundles).forEach(function (entryPoint) {
|
||||
var included = bundles[entryPoint];
|
||||
var includedMap = {};
|
||||
included.forEach(function(included) {
|
||||
included.forEach(function (included) {
|
||||
includedMap[included] = true;
|
||||
});
|
||||
|
||||
var explanation = [];
|
||||
included.map(function(included) {
|
||||
included.map(function (included) {
|
||||
if (included.indexOf('!') >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var reason = (inverseGraph[included]||[]).filter(function(mod) {
|
||||
var reason = (inverseGraph[included] || []).filter(function (mod) {
|
||||
return !!includedMap[mod];
|
||||
});
|
||||
explanation.push({
|
||||
@@ -198,7 +201,7 @@ gulp.task('analyze-editor-distro', function() {
|
||||
});
|
||||
|
||||
function filterStream(testFunc) {
|
||||
return es.through(function(data) {
|
||||
return es.through(function (data) {
|
||||
if (!testFunc(data.relative)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ const sourcemaps = require('gulp-sourcemaps');
|
||||
const nlsDev = require('vscode-nls-dev');
|
||||
const root = path.dirname(__dirname);
|
||||
const commit = util.getVersion(root);
|
||||
const i18n = require('./lib/i18n');
|
||||
|
||||
const extensionsPath = path.join(path.dirname(__dirname), 'extensions');
|
||||
|
||||
@@ -29,7 +30,8 @@ const compilations = glob.sync('**/tsconfig.json', {
|
||||
});
|
||||
|
||||
const getBaseUrl = out => `https://ticino.blob.core.windows.net/sourcemaps/${commit}/${out}`;
|
||||
const languages = ['chs', 'cht', 'jpn', 'kor', 'deu', 'fra', 'esn', 'rus', 'ita'];
|
||||
|
||||
const languages = i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []);
|
||||
|
||||
const tasks = compilations.map(function (tsconfigFile) {
|
||||
const absolutePath = path.join(extensionsPath, tsconfigFile);
|
||||
@@ -55,9 +57,19 @@ const tasks = compilations.map(function (tsconfigFile) {
|
||||
const srcBase = path.join(root, 'src');
|
||||
const src = path.join(srcBase, '**');
|
||||
const out = path.join(root, 'out');
|
||||
const i18n = path.join(__dirname, '..', 'i18n');
|
||||
const i18nPath = path.join(__dirname, '..', 'i18n');
|
||||
const baseUrl = getBaseUrl(out);
|
||||
|
||||
let headerId, headerOut;
|
||||
let index = relativeDirname.indexOf('/');
|
||||
if (index < 0) {
|
||||
headerId = 'vscode.' + relativeDirname;
|
||||
headerOut = 'out';
|
||||
} else {
|
||||
headerId = 'vscode.' + relativeDirname.substr(0, index);
|
||||
headerOut = relativeDirname.substr(index + 1) + '/out';
|
||||
}
|
||||
|
||||
function createPipeline(build, emitError) {
|
||||
const reporter = createReporter();
|
||||
|
||||
@@ -82,7 +94,9 @@ const tasks = compilations.map(function (tsconfigFile) {
|
||||
sourceRoot: '../src'
|
||||
}))
|
||||
.pipe(tsFilter.restore)
|
||||
.pipe(build ? nlsDev.createAdditionalLanguageFiles(languages, i18n, out) : es.through())
|
||||
.pipe(build ? nlsDev.createAdditionalLanguageFiles(languages, i18nPath, out) : es.through())
|
||||
.pipe(build ? nlsDev.bundleMetaDataFiles(headerId, headerOut) : es.through())
|
||||
.pipe(build ? nlsDev.bundleLanguageFiles() : es.through())
|
||||
.pipe(reporter.end(emitError));
|
||||
|
||||
return es.duplex(input, output);
|
||||
@@ -129,7 +143,7 @@ const tasks = compilations.map(function (tsconfigFile) {
|
||||
const watchInput = watcher(src, srcOpts);
|
||||
|
||||
return watchInput
|
||||
.pipe(util.incremental(() => pipeline(true), input))
|
||||
.pipe(util.incremental(() => pipeline(), input))
|
||||
.pipe(gulp.dest(out));
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,11 @@ const gulptslint = require('gulp-tslint');
|
||||
const gulpeslint = require('gulp-eslint');
|
||||
const tsfmt = require('typescript-formatter');
|
||||
const tslint = require('tslint');
|
||||
const VinylFile = require('vinyl');
|
||||
const vfs = require('vinyl-fs');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const pall = require('p-all');
|
||||
|
||||
/**
|
||||
* Hygiene works by creating cascading subsets of all our files and
|
||||
@@ -29,55 +33,56 @@ const all = [
|
||||
'extensions/**/*',
|
||||
'scripts/**/*',
|
||||
'src/**/*',
|
||||
'test/**/*'
|
||||
];
|
||||
|
||||
const eolFilter = [
|
||||
'**',
|
||||
'!ThirdPartyNotices.txt',
|
||||
'!LICENSE.txt',
|
||||
'!extensions/**/out/**',
|
||||
'!test/smoke/out/**',
|
||||
'!**/node_modules/**',
|
||||
'!**/fixtures/**',
|
||||
'!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot}',
|
||||
'!build/{lib,tslintRules}/**/*.js',
|
||||
'!build/monaco/**',
|
||||
'!build/win32/**',
|
||||
'!build/**/*.sh',
|
||||
'!build/tfs/**/*.js',
|
||||
'!**/Dockerfile'
|
||||
'test/**/*',
|
||||
'!**/node_modules/**'
|
||||
];
|
||||
|
||||
const indentationFilter = [
|
||||
'**',
|
||||
|
||||
// except specific files
|
||||
'!ThirdPartyNotices.txt',
|
||||
'!**/*.md',
|
||||
'!**/*.ps1',
|
||||
'!**/*.template',
|
||||
'!**/*.yaml',
|
||||
'!**/*.yml',
|
||||
'!**/yarn.lock',
|
||||
'!**/lib/**',
|
||||
'!extensions/**/*.d.ts',
|
||||
'!src/typings/**/*.d.ts',
|
||||
'!src/vs/*/**/*.d.ts',
|
||||
'!**/*.d.ts.recipe',
|
||||
'!LICENSE.txt',
|
||||
'!src/vs/nls.js',
|
||||
'!src/vs/css.js',
|
||||
'!src/vs/loader.js',
|
||||
'!src/vs/base/common/marked/raw.marked.js',
|
||||
'!src/vs/base/common/winjs.base.raw.js',
|
||||
'!src/vs/base/node/terminateProcess.sh',
|
||||
'!src/vs/base/node/ps-win.ps1',
|
||||
'!test/assert.js',
|
||||
|
||||
// except specific folders
|
||||
'!test/smoke/out/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace2/**',
|
||||
'!build/monaco/**',
|
||||
'!build/win32/**',
|
||||
|
||||
// except multiple specific files
|
||||
'!**/package.json',
|
||||
'!**/yarn.lock',
|
||||
|
||||
// except multiple specific folders
|
||||
'!**/octicons/**',
|
||||
'!**/vs/base/common/marked/raw.marked.js',
|
||||
'!**/vs/base/common/winjs.base.raw.js',
|
||||
'!**/vs/base/node/terminateProcess.sh',
|
||||
'!**/vs/base/node/ps-win.ps1',
|
||||
'!**/vs/nls.js',
|
||||
'!**/vs/css.js',
|
||||
'!**/vs/loader.js',
|
||||
'!**/fixtures/**',
|
||||
'!**/lib/**',
|
||||
'!extensions/**/out/**',
|
||||
'!extensions/**/snippets/**',
|
||||
'!extensions/**/syntaxes/**',
|
||||
'!extensions/**/themes/**',
|
||||
'!extensions/**/colorize-fixtures/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace/**'
|
||||
|
||||
// except specific file types
|
||||
'!src/vs/*/**/*.d.ts',
|
||||
'!src/typings/**/*.d.ts',
|
||||
'!extensions/**/*.d.ts',
|
||||
'!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe}',
|
||||
'!build/{lib,tslintRules}/**/*.js',
|
||||
'!build/**/*.sh',
|
||||
'!build/tfs/**/*.js',
|
||||
'!**/Dockerfile',
|
||||
'!extensions/markdown/media/*.js'
|
||||
];
|
||||
|
||||
const copyrightFilter = [
|
||||
@@ -95,6 +100,7 @@ const copyrightFilter = [
|
||||
'!**/*.xpm',
|
||||
'!**/*.opts',
|
||||
'!**/*.disabled',
|
||||
'!**/*.code-workspace',
|
||||
'!build/**/*.init',
|
||||
'!resources/linux/snap/snapcraft.yaml',
|
||||
'!resources/win32/bin/code.js',
|
||||
@@ -124,6 +130,7 @@ const tslintFilter = [
|
||||
'!**/node_modules/**',
|
||||
'!extensions/typescript/test/colorize-fixtures/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace2/**',
|
||||
'!extensions/**/*.test.ts',
|
||||
'!extensions/html/server/lib/jquery.d.ts'
|
||||
];
|
||||
@@ -144,31 +151,23 @@ gulp.task('eslint', () => {
|
||||
});
|
||||
|
||||
gulp.task('tslint', () => {
|
||||
// {{SQL CARBON EDIT}}
|
||||
const options = { emitError: false };
|
||||
|
||||
return vfs.src(all, { base: '.', follow: true, allowEmpty: true })
|
||||
.pipe(filter(tslintFilter))
|
||||
.pipe(gulptslint({ rulesDirectory: 'build/lib/tslint' }))
|
||||
.pipe(gulptslint.report(options));
|
||||
.pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint' }))
|
||||
.pipe(gulptslint.default.report(options));
|
||||
});
|
||||
|
||||
const hygiene = exports.hygiene = (some, options) => {
|
||||
options = options || {};
|
||||
function hygiene(some) {
|
||||
let errorCount = 0;
|
||||
|
||||
const eol = es.through(function (file) {
|
||||
if (/\r\n?/g.test(file.contents.toString('utf8'))) {
|
||||
console.error(file.relative + ': Bad EOL found');
|
||||
errorCount++;
|
||||
}
|
||||
|
||||
this.emit('data', file);
|
||||
});
|
||||
|
||||
const indentation = es.through(function (file) {
|
||||
file.contents
|
||||
.toString('utf8')
|
||||
.split(/\r\n|\r|\n/)
|
||||
const lines = file.contents.toString('utf8').split(/\r\n|\r|\n/);
|
||||
file.__lines = lines;
|
||||
|
||||
lines
|
||||
.forEach((line, i) => {
|
||||
if (/^\s*$/.test(line)) {
|
||||
// empty or whitespace lines are OK
|
||||
@@ -186,9 +185,14 @@ const hygiene = exports.hygiene = (some, options) => {
|
||||
});
|
||||
|
||||
const copyrights = es.through(function (file) {
|
||||
if (file.contents.toString('utf8').indexOf(copyrightHeader) !== 0) {
|
||||
console.error(file.relative + ': Missing or bad copyright statement');
|
||||
errorCount++;
|
||||
const lines = file.__lines;
|
||||
|
||||
for (let i = 0; i < copyrightHeaderLines.length; i++) {
|
||||
if (lines[i] !== copyrightHeaderLines[i]) {
|
||||
console.error(file.relative + ': Missing or bad copyright statement');
|
||||
errorCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.emit('data', file);
|
||||
@@ -196,12 +200,20 @@ const hygiene = exports.hygiene = (some, options) => {
|
||||
|
||||
const formatting = es.map(function (file, cb) {
|
||||
tsfmt.processString(file.path, file.contents.toString('utf8'), {
|
||||
verify: true,
|
||||
verify: false,
|
||||
tsfmt: true,
|
||||
// verbose: true
|
||||
// keep checkJS happy
|
||||
editorconfig: undefined,
|
||||
replace: undefined,
|
||||
tsconfig: undefined,
|
||||
tslint: undefined
|
||||
}).then(result => {
|
||||
if (result.error) {
|
||||
console.error(result.message);
|
||||
let original = result.src.replace(/\r\n/gm, '\n');
|
||||
let formatted = result.dest.replace(/\r\n/gm, '\n');
|
||||
|
||||
if (original !== formatted) {
|
||||
console.error('File not formatted:', file.relative);
|
||||
errorCount++;
|
||||
}
|
||||
cb(null, file);
|
||||
@@ -211,43 +223,31 @@ const hygiene = exports.hygiene = (some, options) => {
|
||||
});
|
||||
});
|
||||
|
||||
function reportFailures(failures) {
|
||||
failures.forEach(failure => {
|
||||
const name = failure.name || failure.fileName;
|
||||
const position = failure.startPosition;
|
||||
const line = position.lineAndCharacter ? position.lineAndCharacter.line : position.line;
|
||||
const character = position.lineAndCharacter ? position.lineAndCharacter.character : position.character;
|
||||
|
||||
// console.error(`${name}:${line + 1}:${character + 1}:${failure.failure}`);
|
||||
});
|
||||
}
|
||||
const tslintConfiguration = tslint.Configuration.findConfiguration('tslint.json', '.');
|
||||
const tslintOptions = { fix: false, formatter: 'json' };
|
||||
const tsLinter = new tslint.Linter(tslintOptions);
|
||||
|
||||
const tsl = es.through(function (file) {
|
||||
const configuration = tslint.Configuration.findConfiguration(null, '.');
|
||||
const options = { formatter: 'json', rulesDirectory: 'build/lib/tslint' };
|
||||
const contents = file.contents.toString('utf8');
|
||||
const linter = new tslint.Linter(options);
|
||||
linter.lint(file.relative, contents, configuration.results);
|
||||
const result = linter.getResult();
|
||||
|
||||
if (result.failures.length > 0) {
|
||||
reportFailures(result.failures);
|
||||
errorCount += result.failures.length;
|
||||
}
|
||||
|
||||
tsLinter.lint(file.relative, contents, tslintConfiguration.results);
|
||||
this.emit('data', file);
|
||||
});
|
||||
|
||||
const result = vfs.src(some || all, { base: '.', follow: true, allowEmpty: true })
|
||||
let input;
|
||||
|
||||
if (Array.isArray(some) || typeof some === 'string' || !some) {
|
||||
input = vfs.src(some || all, { base: '.', follow: true, allowEmpty: true });
|
||||
} else {
|
||||
input = some;
|
||||
}
|
||||
|
||||
const result = input
|
||||
.pipe(filter(f => !f.stat.isDirectory()))
|
||||
.pipe(filter(eolFilter))
|
||||
// {{SQL CARBON EDIT}}
|
||||
//.pipe(options.skipEOL ? es.through() : eol)
|
||||
.pipe(filter(indentationFilter))
|
||||
.pipe(indentation)
|
||||
.pipe(filter(copyrightFilter))
|
||||
.pipe(filter(copyrightFilter));
|
||||
// {{SQL CARBON EDIT}}
|
||||
//.pipe(copyrights);
|
||||
// .pipe(copyrights);
|
||||
|
||||
const typescript = result
|
||||
.pipe(filter(tslintFilter))
|
||||
@@ -257,23 +257,52 @@ const hygiene = exports.hygiene = (some, options) => {
|
||||
const javascript = result
|
||||
.pipe(filter(eslintFilter))
|
||||
.pipe(gulpeslint('src/.eslintrc'))
|
||||
.pipe(gulpeslint.formatEach('compact'))
|
||||
// {{SQL CARBON EDIT}}
|
||||
.pipe(gulpeslint.formatEach('compact'));
|
||||
// {{SQL CARBON EDIT}}
|
||||
// .pipe(gulpeslint.failAfterError());
|
||||
|
||||
return es.merge(typescript, javascript)
|
||||
.pipe(es.through(null, function () {
|
||||
// {{SQL CARBON EDIT}}
|
||||
// if (errorCount > 0) {
|
||||
// this.emit('error', 'Hygiene failed with ' + errorCount + ' errors. Check \'build/gulpfile.hygiene.js\'.');
|
||||
// } else {
|
||||
// this.emit('end');
|
||||
// }
|
||||
this.emit('end');
|
||||
}));
|
||||
};
|
||||
let count = 0;
|
||||
return es.merge(typescript, javascript)
|
||||
.pipe(es.through(function (data) {
|
||||
// {{SQL CARBON EDIT}}
|
||||
this.emit('end');
|
||||
}));
|
||||
}
|
||||
|
||||
gulp.task('hygiene', () => hygiene(''));
|
||||
function createGitIndexVinyls(paths) {
|
||||
const cp = require('child_process');
|
||||
const repositoryPath = process.cwd();
|
||||
|
||||
const fns = paths.map(relativePath => () => new Promise((c, e) => {
|
||||
const fullPath = path.join(repositoryPath, relativePath);
|
||||
|
||||
fs.stat(fullPath, (err, stat) => {
|
||||
if (err && err.code === 'ENOENT') { // ignore deletions
|
||||
return c(null);
|
||||
} else if (err) {
|
||||
return e(err);
|
||||
}
|
||||
|
||||
cp.exec(`git show :${relativePath}`, { maxBuffer: 2000 * 1024, encoding: 'buffer' }, (err, out) => {
|
||||
if (err) {
|
||||
return e(err);
|
||||
}
|
||||
|
||||
c(new VinylFile({
|
||||
path: fullPath,
|
||||
base: repositoryPath,
|
||||
contents: out,
|
||||
stat
|
||||
}));
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
return pall(fns, { concurrency: 4 })
|
||||
.then(r => r.filter(p => !!p));
|
||||
}
|
||||
|
||||
gulp.task('hygiene', () => hygiene());
|
||||
|
||||
// this allows us to run hygiene as a git pre-commit hook
|
||||
if (require.main === module) {
|
||||
@@ -284,22 +313,19 @@ if (require.main === module) {
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
cp.exec('git config core.autocrlf', (err, out) => {
|
||||
const skipEOL = out.trim() === 'true';
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
return hygiene(process.argv.slice(2), { skipEOL: skipEOL }).on('error', err => {
|
||||
console.error();
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
hygiene(process.argv.slice(2)).on('error', err => {
|
||||
console.error();
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
} else {
|
||||
cp.exec('git diff --cached --name-only', { maxBuffer: 2000 * 1024 }, (err, out) => {
|
||||
if (err) {
|
||||
console.error();
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
const some = out
|
||||
@@ -307,12 +333,18 @@ if (require.main === module) {
|
||||
.filter(l => !!l);
|
||||
|
||||
if (some.length > 0) {
|
||||
hygiene(some, { skipEOL: skipEOL }).on('error', err => {
|
||||
console.error();
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
console.log('Reading git index versions...');
|
||||
|
||||
createGitIndexVinyls(some)
|
||||
.then(vinyls => new Promise((c, e) => hygiene(es.readArray(vinyls))
|
||||
.on('end', () => c())
|
||||
.on('error', e)))
|
||||
.catch(err => {
|
||||
console.error();
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,21 @@
|
||||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
const json = require('gulp-json-editor');
|
||||
const buffer = require('gulp-buffer');
|
||||
const filter = require('gulp-filter');
|
||||
const es = require('event-stream');
|
||||
const util = require('./lib/util');
|
||||
const remote = require('gulp-remote-src');
|
||||
const zip = require('gulp-vinyl-zip');
|
||||
const assign = require('object-assign');
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
const jeditor = require('gulp-json-editor');
|
||||
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
const pkg = require('../package.json');
|
||||
|
||||
gulp.task('mixin', function () {
|
||||
// {{SQL CARBON EDIT}}
|
||||
const updateUrl = process.env['SQLOPS_UPDATEURL'];
|
||||
@@ -25,9 +37,9 @@ gulp.task('mixin', function () {
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
let serviceUrl = 'https://raw.githubusercontent.com/Microsoft/sqlopsstudio/release/extensions/extensionsGallery.json';
|
||||
let serviceUrl = 'https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery.json';
|
||||
if (quality === 'insider') {
|
||||
serviceUrl = `https://raw.githubusercontent.com/Microsoft/sqlopsstudio/release/extensions/extensionsGallery-${quality}.json`;
|
||||
serviceUrl = `https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery-${quality}.json`;
|
||||
}
|
||||
let newValues = {
|
||||
"updateUrl": updateUrl,
|
||||
|
||||
@@ -13,14 +13,6 @@ const filter = require('gulp-filter');
|
||||
|
||||
gulp.task('clean-mssql-extension', util.rimraf('extensions/mssql/node_modules'));
|
||||
gulp.task('clean-credentials-extension', util.rimraf('extensions/credentials/node_modules'));
|
||||
gulp.task('clean-extensions-modules', util.rimraf('extensions-modules/node_modules'));
|
||||
gulp.task('clean-protocol', ['clean-extensions-modules', 'clean-mssql-extension', 'clean-credentials-extension', 'clean-client', 'clean-jsonrpc', 'clean-server', 'clean-types']);
|
||||
|
||||
// Tasks to clean extensions modules
|
||||
gulp.task('clean-mssql-ext-mod', util.rimraf('extensions/mssql/node_modules/extensions-modules'));
|
||||
gulp.task('clean-credentials-ext-mod', util.rimraf('extensions/credentials/node_modules/extensions-modules'));
|
||||
gulp.task('clean-build-ext-mod', util.rimraf('build/node_modules/extensions-modules'));
|
||||
gulp.task('clean-ext-mod', ['clean-mssql-ext-mod', 'clean-credentials-ext-mod', 'clean-build-ext-mod', 'clean-extensions-modules']);
|
||||
|
||||
gulp.task('fmt', () => formatStagedFiles());
|
||||
const formatFiles = (some) => {
|
||||
|
||||
@@ -27,17 +27,22 @@ const common = require('./lib/optimize');
|
||||
const nlsDev = require('vscode-nls-dev');
|
||||
const root = path.dirname(__dirname);
|
||||
const commit = util.getVersion(root);
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
const packageJson = require('../package.json');
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
const product = require('../product.json');
|
||||
const crypto = require('crypto');
|
||||
const i18n = require('./lib/i18n');
|
||||
const serviceInstaller = require('../extensions-modules/lib/languageservice/serviceInstallerUtil');
|
||||
// {{SQL CARBON EDIT}}
|
||||
const serviceDownloader = require('service-downloader').ServiceDownloadProvider;
|
||||
const platformInfo = require('service-downloader/out/platform').PlatformInformation;
|
||||
const glob = require('glob');
|
||||
const deps = require('./dependencies');
|
||||
const getElectronVersion = require('./lib/electron').getElectronVersion;
|
||||
const createAsar = require('./lib/asar').createAsar;
|
||||
|
||||
const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname));
|
||||
|
||||
// @ts-ignore
|
||||
// {{SQL CARBON EDIT}}
|
||||
var del = require('del');
|
||||
const extensionsRoot = path.join(root, 'extensions');
|
||||
@@ -55,16 +60,16 @@ const nodeModules = [
|
||||
.concat(_.uniq(productionDependencies.map(d => d.name)))
|
||||
.concat(baseModules);
|
||||
|
||||
// Build
|
||||
|
||||
const builtInExtensions = [
|
||||
{ name: 'ms-vscode.node-debug', version: '1.19.8' },
|
||||
{ name: 'ms-vscode.node-debug2', version: '1.19.4' }
|
||||
];
|
||||
// Build
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
const builtInExtensions = require('./builtInExtensions.json');
|
||||
|
||||
const excludedExtensions = [
|
||||
'vscode-api-tests',
|
||||
'vscode-colorize-tests'
|
||||
'vscode-colorize-tests',
|
||||
'ms-vscode.node-debug',
|
||||
'ms-vscode.node-debug2',
|
||||
];
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
@@ -103,7 +108,8 @@ const vscodeResources = [
|
||||
'out-build/vs/workbench/parts/welcome/walkThrough/**/*.md',
|
||||
'out-build/vs/workbench/services/files/**/*.exe',
|
||||
'out-build/vs/workbench/services/files/**/*.md',
|
||||
'out-build/vs/code/electron-browser/sharedProcess.js',
|
||||
'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js',
|
||||
'out-build/vs/code/electron-browser/issue/issueReporter.js',
|
||||
// {{SQL CARBON EDIT}}
|
||||
'out-build/sql/workbench/electron-browser/splashscreen/*',
|
||||
'out-build/sql/**/*.{svg,png,cur,html}',
|
||||
@@ -133,10 +139,7 @@ const BUNDLED_FILE_HEADER = [
|
||||
' *--------------------------------------------------------*/'
|
||||
].join('\n');
|
||||
|
||||
var languages = ['chs', 'cht', 'jpn', 'kor', 'deu', 'fra', 'esn', 'rus', 'ita'];
|
||||
if (process.env.VSCODE_QUALITY !== 'stable') {
|
||||
languages = languages.concat(['ptb', 'hun', 'trk']); // Add languages requested by the community to non-stable builds
|
||||
}
|
||||
const languages = i18n.defaultLanguages.concat([]); // i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []);
|
||||
|
||||
gulp.task('clean-optimized-vscode', util.rimraf('out-vscode'));
|
||||
gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compile-extensions-build'], common.optimizeTask({
|
||||
@@ -146,7 +149,8 @@ gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compil
|
||||
loaderConfig: common.loaderConfig(nodeModules),
|
||||
header: BUNDLED_FILE_HEADER,
|
||||
out: 'out-vscode',
|
||||
languages: languages
|
||||
languages: languages,
|
||||
bundleInfo: undefined
|
||||
}));
|
||||
|
||||
|
||||
@@ -168,7 +172,7 @@ const config = {
|
||||
version: getElectronVersion(),
|
||||
productAppName: product.nameLong,
|
||||
companyName: 'Microsoft Corporation',
|
||||
copyright: 'Copyright (C) 2017 Microsoft. All rights reserved',
|
||||
copyright: 'Copyright (C) 2018 Microsoft. All rights reserved',
|
||||
darwinIcon: 'resources/darwin/code.icns',
|
||||
darwinBundleIdentifier: product.darwinBundleIdentifier,
|
||||
darwinApplicationCategoryType: 'public.app-category.developer-tools',
|
||||
@@ -187,7 +191,7 @@ const config = {
|
||||
name: product.nameLong,
|
||||
urlSchemes: [product.urlProtocol]
|
||||
}],
|
||||
darwinCredits: darwinCreditsTemplate ? new Buffer(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : void 0,
|
||||
darwinCredits: darwinCreditsTemplate ? Buffer.from(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : void 0,
|
||||
linuxExecutableName: product.applicationName,
|
||||
winIcon: 'resources/win32/code.ico',
|
||||
token: process.env['VSCODE_MIXIN_PASSWORD'] || process.env['GITHUB_TOKEN'] || void 0,
|
||||
@@ -325,16 +329,12 @@ function packageTask(platform, arch, opts) {
|
||||
const localExtensionDependencies = gulp.src(extensionDepsSrc, { base: '.', dot: true })
|
||||
.pipe(filter(['**', '!**/package-lock.json']))
|
||||
.pipe(util.cleanNodeModule('account-provider-azure', ['node_modules/date-utils/doc/**', 'node_modules/adal_node/node_modules/**'], undefined))
|
||||
.pipe(util.cleanNodeModule('dataprotocol-client', ['node_modules/**', 'src/*.js'], undefined))
|
||||
.pipe(util.cleanNodeModule('extensions-modules', ['node_modules/**', 'src/*.js'], undefined))
|
||||
.pipe(util.cleanNodeModule('typescript', ['**/**'], undefined));
|
||||
|
||||
|
||||
const sources = es.merge(src, localExtensions, localExtensionDependencies)
|
||||
.pipe(util.setExecutableBit(['**/*.sh']))
|
||||
.pipe(filter(['**',
|
||||
'!**/*.js.map',
|
||||
'!extensions/**/node_modules/**/{test, tests}/**',
|
||||
'!extensions/**/node_modules/**/test.js']));
|
||||
.pipe(filter(['**', '!**/*.js.map']));
|
||||
|
||||
let version = packageJson.version;
|
||||
const quality = product.quality;
|
||||
@@ -358,7 +358,7 @@ function packageTask(platform, arch, opts) {
|
||||
|
||||
// TODO the API should be copied to `out` during compile, not here
|
||||
const api = gulp.src('src/vs/vscode.d.ts').pipe(rename('out/vs/vscode.d.ts'));
|
||||
// {{SQL CARBON EDIT}}
|
||||
// {{SQL CARBON EDIT}}
|
||||
const dataApi = gulp.src('src/vs/data.d.ts').pipe(rename('out/sql/data.d.ts'));
|
||||
|
||||
const depsSrc = [
|
||||
@@ -372,6 +372,7 @@ function packageTask(platform, arch, opts) {
|
||||
.pipe(util.cleanNodeModule('oniguruma', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/*.js']))
|
||||
.pipe(util.cleanNodeModule('windows-mutex', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-is-elevated', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-watchdog', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('spdlog', ['binding.gyp', 'build/**', 'deps/**', 'src/**', 'test/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('jschardet', ['dist/**']))
|
||||
@@ -379,18 +380,27 @@ function packageTask(platform, arch, opts) {
|
||||
.pipe(util.cleanNodeModule('windows-process-tree', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('gc-signals', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/index.js']))
|
||||
.pipe(util.cleanNodeModule('keytar', ['binding.gyp', 'build/**', 'src/**', 'script/**', 'node_modules/**'], ['**/*.node']))
|
||||
|
||||
.pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'tools/**'], ['build/Release/*.exe', 'build/Release/*.dll', 'build/Release/*.node']))
|
||||
// {{SQL CARBON EDIT}}
|
||||
.pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'tools/**'], ['build/Release/*.node', 'build/Release/*.dll', 'build/Release/*.exe']))
|
||||
.pipe(util.cleanNodeModule('chart.js', ['node_modules/**'], undefined))
|
||||
.pipe(util.cleanNodeModule('emmet', ['node_modules/**'], undefined))
|
||||
.pipe(util.cleanNodeModule('pty.js', ['build/**'], ['build/Release/**']))
|
||||
.pipe(util.cleanNodeModule('jquery-ui', ['external/**', 'demos/**'], undefined))
|
||||
.pipe(util.cleanNodeModule('core-js', ['**/**'], undefined))
|
||||
.pipe(util.cleanNodeModule('slickgrid', ['node_modules/**', 'examples/**'], undefined))
|
||||
|
||||
.pipe(util.cleanNodeModule('nsfw', ['binding.gyp', 'build/**', 'src/**', 'openpa/**', 'includes/**'], ['**/*.node', '**/*.a']))
|
||||
.pipe(util.cleanNodeModule('vsda', ['binding.gyp', 'README.md', 'build/**', '*.bat', '*.sh', '*.cpp', '*.h'], ['build/Release/vsda.node']));
|
||||
.pipe(util.cleanNodeModule('vsda', ['binding.gyp', 'README.md', 'build/**', '*.bat', '*.sh', '*.cpp', '*.h'], ['build/Release/vsda.node']))
|
||||
.pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*'], 'app/node_modules.asar'));
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
let copiedModules = gulp.src([
|
||||
'node_modules/jquery/**/*.*',
|
||||
'node_modules/reflect-metadata/**/*.*',
|
||||
'node_modules/slickgrid/**/*.*',
|
||||
'node_modules/underscore/**/*.*',
|
||||
'node_modules/zone.js/**/*.*',
|
||||
'node_modules/chart.js/**/*.*'
|
||||
], { base: '.', dot: true });
|
||||
|
||||
let all = es.merge(
|
||||
packageJsonStream,
|
||||
@@ -398,7 +408,8 @@ function packageTask(platform, arch, opts) {
|
||||
license,
|
||||
watermark,
|
||||
api,
|
||||
// {{SQL CARBON EDIT}}
|
||||
// {{SQL CARBON EDIT}}
|
||||
copiedModules,
|
||||
dataApi,
|
||||
sources,
|
||||
deps
|
||||
@@ -469,25 +480,21 @@ gulp.task('vscode-linux-x64-min', ['minify-vscode', 'clean-vscode-linux-x64'], p
|
||||
gulp.task('vscode-linux-arm-min', ['minify-vscode', 'clean-vscode-linux-arm'], packageTask('linux', 'arm', { minified: true }));
|
||||
|
||||
// Transifex Localizations
|
||||
const vscodeLanguages = [
|
||||
'zh-hans',
|
||||
'zh-hant',
|
||||
'ja',
|
||||
'ko',
|
||||
'de',
|
||||
'fr',
|
||||
'es',
|
||||
'ru',
|
||||
'it',
|
||||
'pt-br',
|
||||
'hu',
|
||||
'tr'
|
||||
];
|
||||
const setupDefaultLanguages = [
|
||||
'zh-hans',
|
||||
'zh-hant',
|
||||
'ko'
|
||||
];
|
||||
|
||||
const innoSetupConfig = {
|
||||
'zh-cn': { codePage: 'CP936', defaultInfo: { name: 'Simplified Chinese', id: '$0804', } },
|
||||
'zh-tw': { codePage: 'CP950', defaultInfo: { name: 'Traditional Chinese', id: '$0404' } },
|
||||
'ko': { codePage: 'CP949', defaultInfo: { name: 'Korean', id: '$0412' } },
|
||||
'ja': { codePage: 'CP932' },
|
||||
'de': { codePage: 'CP1252' },
|
||||
'fr': { codePage: 'CP1252' },
|
||||
'es': { codePage: 'CP1252' },
|
||||
'ru': { codePage: 'CP1251' },
|
||||
'it': { codePage: 'CP1252' },
|
||||
'pt-br': { codePage: 'CP1252' },
|
||||
'hu': { codePage: 'CP1250' },
|
||||
'tr': { codePage: 'CP1254' }
|
||||
};
|
||||
|
||||
const apiHostname = process.env.TRANSIFEX_API_URL;
|
||||
const apiName = process.env.TRANSIFEX_API_NAME;
|
||||
@@ -495,27 +502,50 @@ const apiToken = process.env.TRANSIFEX_API_TOKEN;
|
||||
|
||||
gulp.task('vscode-translations-push', ['optimize-vscode'], function () {
|
||||
const pathToMetadata = './out-vscode/nls.metadata.json';
|
||||
const pathToExtensions = './extensions/**/*.nls.json';
|
||||
const pathToExtensions = './extensions/*';
|
||||
const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}';
|
||||
|
||||
return es.merge(
|
||||
gulp.src(pathToMetadata).pipe(i18n.prepareXlfFiles()),
|
||||
gulp.src(pathToSetup).pipe(i18n.prepareXlfFiles()),
|
||||
gulp.src(pathToExtensions).pipe(i18n.prepareXlfFiles('vscode-extensions'))
|
||||
gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()),
|
||||
gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
|
||||
gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
|
||||
).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken)
|
||||
).pipe(i18n.pushXlfFiles(apiHostname, apiName, apiToken));
|
||||
});
|
||||
|
||||
gulp.task('vscode-translations-pull', function () {
|
||||
gulp.task('vscode-translations-push-test', ['optimize-vscode'], function () {
|
||||
const pathToMetadata = './out-vscode/nls.metadata.json';
|
||||
const pathToExtensions = './extensions/*';
|
||||
const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}';
|
||||
|
||||
return es.merge(
|
||||
i18n.pullXlfFiles('vscode-editor', apiHostname, apiName, apiToken, vscodeLanguages),
|
||||
i18n.pullXlfFiles('vscode-workbench', apiHostname, apiName, apiToken, vscodeLanguages),
|
||||
i18n.pullXlfFiles('vscode-extensions', apiHostname, apiName, apiToken, vscodeLanguages),
|
||||
i18n.pullXlfFiles('vscode-setup', apiHostname, apiName, apiToken, setupDefaultLanguages)
|
||||
).pipe(vfs.dest('../vscode-localization'));
|
||||
gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()),
|
||||
gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
|
||||
gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
|
||||
).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken)
|
||||
).pipe(vfs.dest('../vscode-transifex-input'));
|
||||
});
|
||||
|
||||
gulp.task('vscode-translations-pull', function () {
|
||||
[...i18n.defaultLanguages, ...i18n.extraLanguages].forEach(language => {
|
||||
i18n.pullCoreAndExtensionsXlfFiles(apiHostname, apiName, apiToken, language).pipe(vfs.dest(`../vscode-localization/${language.id}/build`));
|
||||
|
||||
let includeDefault = !!innoSetupConfig[language.id].defaultInfo;
|
||||
i18n.pullSetupXlfFiles(apiHostname, apiName, apiToken, language, includeDefault).pipe(vfs.dest(`../vscode-localization/${language.id}/setup`));
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('vscode-translations-import', function () {
|
||||
return gulp.src('../vscode-localization/**/*.xlf').pipe(i18n.prepareJsonFiles()).pipe(vfs.dest('./i18n'));
|
||||
[...i18n.defaultLanguages, ...i18n.extraLanguages].forEach(language => {
|
||||
gulp.src(`../vscode-localization/${language.id}/build/*/*.xlf`)
|
||||
.pipe(i18n.prepareI18nFiles())
|
||||
.pipe(vfs.dest(`./i18n/${language.folderName}`));
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// gulp.src(`../vscode-localization/${language.id}/setup/*/*.xlf`)
|
||||
// .pipe(i18n.prepareIslFiles(language, innoSetupConfig[language.id]))
|
||||
// .pipe(vfs.dest(`./build/win32/i18n`));
|
||||
});
|
||||
});
|
||||
|
||||
// Sourcemaps
|
||||
@@ -541,7 +571,8 @@ gulp.task('upload-vscode-sourcemaps', ['minify-vscode'], () => {
|
||||
const allConfigDetailsPath = path.join(os.tmpdir(), 'configuration.json');
|
||||
gulp.task('upload-vscode-configuration', ['generate-vscode-configuration'], () => {
|
||||
const branch = process.env.BUILD_SOURCEBRANCH;
|
||||
if (!branch.endsWith('/master') && branch.indexOf('/release/') < 0) {
|
||||
|
||||
if (!/\/master$/.test(branch) && branch.indexOf('/release/') < 0) {
|
||||
console.log(`Only runs on master and release branches, not ${branch}`);
|
||||
return;
|
||||
}
|
||||
@@ -636,6 +667,7 @@ function versionStringToNumber(versionStr) {
|
||||
return parseInt(match[1], 10) * 1e4 + parseInt(match[2], 10) * 1e2 + parseInt(match[3], 10);
|
||||
}
|
||||
|
||||
// This task is only run for the MacOS build
|
||||
gulp.task('generate-vscode-configuration', () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const buildDir = process.env['AGENT_BUILDDIRECTORY'];
|
||||
@@ -645,7 +677,8 @@ gulp.task('generate-vscode-configuration', () => {
|
||||
|
||||
const userDataDir = path.join(os.tmpdir(), 'tmpuserdata');
|
||||
const extensionsDir = path.join(os.tmpdir(), 'tmpextdir');
|
||||
const appPath = path.join(buildDir, 'VSCode-darwin/Visual\\ Studio\\ Code\\ -\\ Insiders.app/Contents/Resources/app/bin/code');
|
||||
const appName = process.env.VSCODE_QUALITY === 'insider' ? 'Visual\\ Studio\\ Code\\ -\\ Insiders.app' : 'Visual\\ Studio\\ Code.app';
|
||||
const appPath = path.join(buildDir, `VSCode-darwin/${appName}/Contents/Resources/app/bin/code`);
|
||||
const codeProc = cp.exec(`${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`);
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
@@ -674,27 +707,25 @@ gulp.task('generate-vscode-configuration', () => {
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Install service locally before building carbon
|
||||
|
||||
function installService(extObj, path) {
|
||||
var installer = new serviceInstaller.ServiceInstaller(extObj, path);
|
||||
installer.getServiceInstallDirectoryRoot().then(serviceInstallFolder => {
|
||||
console.log('Cleaning up the install folder: ' + serviceInstallFolder);
|
||||
del(serviceInstallFolder + '/*').then(() => {
|
||||
console.log('Installing the service. Install folder: ' + serviceInstallFolder);
|
||||
installer.installService();
|
||||
}, delError => {
|
||||
console.log('failed to delete the install folder error: ' + delError);
|
||||
});
|
||||
}, getFolderPathError => {
|
||||
console.log('failed to call getServiceInstallDirectoryRoot error: ' + getFolderPathError);
|
||||
function installService() {
|
||||
let config = require('../extensions/mssql/src/config.json');
|
||||
return platformInfo.getCurrent().then(p => {
|
||||
let runtime = p.runtimeId;
|
||||
// fix path since it won't be correct
|
||||
config.installDirectory = path.join(__dirname, '../extensions/mssql/src', config.installDirectory);
|
||||
var installer = new serviceDownloader(config);
|
||||
let serviceInstallFolder = installer.getInstallDirectory(runtime);
|
||||
console.log('Cleaning up the install folder: ' + serviceInstallFolder);
|
||||
return del(serviceInstallFolder + '/*').then(() => {
|
||||
console.log('Installing the service. Install folder: ' + serviceInstallFolder);
|
||||
return installer.installService(runtime);
|
||||
}, delError => {
|
||||
console.log('failed to delete the install folder error: ' + delError);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
gulp.task('install-sqltoolsservice', () => {
|
||||
var mssqlExt = require('../extensions/mssql/client/out/models/constants');
|
||||
var extObj = new mssqlExt.Constants();
|
||||
var path = '../extensions/mssql/client/out/config.json';
|
||||
return installService(extObj, path);
|
||||
return installService();
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -12,9 +12,12 @@ const shell = require('gulp-shell');
|
||||
const es = require('event-stream');
|
||||
const vfs = require('vinyl-fs');
|
||||
const util = require('./lib/util');
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
const packageJson = require('../package.json');
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
const product = require('../product.json');
|
||||
const rpmDependencies = require('../resources/linux/rpm/dependencies');
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
const rpmDependencies = require('../resources/linux/rpm/dependencies.json');
|
||||
|
||||
const linuxPackageRevision = Math.floor(new Date().getTime() / 1000);
|
||||
|
||||
@@ -111,8 +114,7 @@ function buildDebPackage(arch) {
|
||||
return shell.task([
|
||||
'chmod 755 ' + product.applicationName + '-' + debArch + '/DEBIAN/postinst ' + product.applicationName + '-' + debArch + '/DEBIAN/prerm ' + product.applicationName + '-' + debArch + '/DEBIAN/postrm',
|
||||
'mkdir -p deb',
|
||||
'fakeroot dpkg-deb -b ' + product.applicationName + '-' + debArch + ' deb',
|
||||
'dpkg-scanpackages deb /dev/null > Packages'
|
||||
'fakeroot dpkg-deb -b ' + product.applicationName + '-' + debArch + ' deb'
|
||||
], { cwd: '.build/linux/deb/' + debArch });
|
||||
}
|
||||
|
||||
@@ -220,10 +222,10 @@ function prepareSnapPackage(arch) {
|
||||
|
||||
function buildSnapPackage(arch) {
|
||||
const snapBuildPath = getSnapBuildPath(arch);
|
||||
|
||||
const snapFilename = `${product.applicationName}-${packageJson.version}-${linuxPackageRevision}-${arch}.snap`;
|
||||
return shell.task([
|
||||
`chmod +x ${snapBuildPath}/electron-launch`,
|
||||
`cd ${snapBuildPath} && snapcraft snap`
|
||||
`cd ${snapBuildPath} && snapcraft snap --output ../${snapFilename}`
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,11 @@ const assert = require('assert');
|
||||
const cp = require('child_process');
|
||||
const _7z = require('7zip')['7z'];
|
||||
const util = require('./lib/util');
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
const pkg = require('../package.json');
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
const product = require('../product.json');
|
||||
const vfs = require('vinyl-fs');
|
||||
|
||||
const repoPath = path.dirname(__dirname);
|
||||
// {{SQL CARBON EDIT}}
|
||||
@@ -91,3 +94,13 @@ gulp.task('vscode-win32-ia32-archive', ['clean-vscode-win32-ia32-archive'], arch
|
||||
|
||||
gulp.task('clean-vscode-win32-x64-archive', util.rimraf(zipDir('x64')));
|
||||
gulp.task('vscode-win32-x64-archive', ['clean-vscode-win32-x64-archive'], archiveWin32Setup('x64'));
|
||||
|
||||
function copyInnoUpdater(arch) {
|
||||
return () => {
|
||||
return gulp.src('build/win32/{inno_updater.exe,vcruntime140.dll}', { base: 'build/win32' })
|
||||
.pipe(vfs.dest(path.join(buildPath(arch), 'tools')));
|
||||
};
|
||||
}
|
||||
|
||||
gulp.task('vscode-win32-ia32-copy-inno-updater', copyInnoUpdater('ia32'));
|
||||
gulp.task('vscode-win32-x64-copy-inno-updater', copyInnoUpdater('x64'));
|
||||
118
build/lib/asar.js
Normal file
118
build/lib/asar.js
Normal file
@@ -0,0 +1,118 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var path = require("path");
|
||||
var es = require("event-stream");
|
||||
var pickle = require("chromium-pickle-js");
|
||||
var Filesystem = require("asar/lib/filesystem");
|
||||
var VinylFile = require("vinyl");
|
||||
var minimatch = require("minimatch");
|
||||
function createAsar(folderPath, unpackGlobs, destFilename) {
|
||||
var shouldUnpackFile = function (file) {
|
||||
for (var i = 0; i < unpackGlobs.length; i++) {
|
||||
if (minimatch(file.relative, unpackGlobs[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
var filesystem = new Filesystem(folderPath);
|
||||
var out = [];
|
||||
// Keep track of pending inserts
|
||||
var pendingInserts = 0;
|
||||
var onFileInserted = function () { pendingInserts--; };
|
||||
// Do not insert twice the same directory
|
||||
var seenDir = {};
|
||||
var insertDirectoryRecursive = function (dir) {
|
||||
if (seenDir[dir]) {
|
||||
return;
|
||||
}
|
||||
var lastSlash = dir.lastIndexOf('/');
|
||||
if (lastSlash === -1) {
|
||||
lastSlash = dir.lastIndexOf('\\');
|
||||
}
|
||||
if (lastSlash !== -1) {
|
||||
insertDirectoryRecursive(dir.substring(0, lastSlash));
|
||||
}
|
||||
seenDir[dir] = true;
|
||||
filesystem.insertDirectory(dir);
|
||||
};
|
||||
var insertDirectoryForFile = function (file) {
|
||||
var lastSlash = file.lastIndexOf('/');
|
||||
if (lastSlash === -1) {
|
||||
lastSlash = file.lastIndexOf('\\');
|
||||
}
|
||||
if (lastSlash !== -1) {
|
||||
insertDirectoryRecursive(file.substring(0, lastSlash));
|
||||
}
|
||||
};
|
||||
var insertFile = function (relativePath, stat, shouldUnpack) {
|
||||
insertDirectoryForFile(relativePath);
|
||||
pendingInserts++;
|
||||
filesystem.insertFile(relativePath, shouldUnpack, { stat: stat }, {}, onFileInserted);
|
||||
};
|
||||
return es.through(function (file) {
|
||||
if (file.stat.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
if (!file.stat.isFile()) {
|
||||
throw new Error("unknown item in stream!");
|
||||
}
|
||||
var shouldUnpack = shouldUnpackFile(file);
|
||||
insertFile(file.relative, { size: file.contents.length, mode: file.stat.mode }, shouldUnpack);
|
||||
if (shouldUnpack) {
|
||||
// The file goes outside of xx.asar, in a folder xx.asar.unpacked
|
||||
var relative = path.relative(folderPath, file.path);
|
||||
this.queue(new VinylFile({
|
||||
cwd: folderPath,
|
||||
base: folderPath,
|
||||
path: path.join(destFilename + '.unpacked', relative),
|
||||
stat: file.stat,
|
||||
contents: file.contents
|
||||
}));
|
||||
}
|
||||
else {
|
||||
// The file goes inside of xx.asar
|
||||
out.push(file.contents);
|
||||
}
|
||||
}, function () {
|
||||
var _this = this;
|
||||
var finish = function () {
|
||||
{
|
||||
var headerPickle = pickle.createEmpty();
|
||||
headerPickle.writeString(JSON.stringify(filesystem.header));
|
||||
var headerBuf = headerPickle.toBuffer();
|
||||
var sizePickle = pickle.createEmpty();
|
||||
sizePickle.writeUInt32(headerBuf.length);
|
||||
var sizeBuf = sizePickle.toBuffer();
|
||||
out.unshift(headerBuf);
|
||||
out.unshift(sizeBuf);
|
||||
}
|
||||
var contents = Buffer.concat(out);
|
||||
out.length = 0;
|
||||
_this.queue(new VinylFile({
|
||||
cwd: folderPath,
|
||||
base: folderPath,
|
||||
path: destFilename,
|
||||
contents: contents
|
||||
}));
|
||||
_this.queue(null);
|
||||
};
|
||||
// Call finish() only when all file inserts have finished...
|
||||
if (pendingInserts === 0) {
|
||||
finish();
|
||||
}
|
||||
else {
|
||||
onFileInserted = function () {
|
||||
pendingInserts--;
|
||||
if (pendingInserts === 0) {
|
||||
finish();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.createAsar = createAsar;
|
||||
131
build/lib/asar.ts
Normal file
131
build/lib/asar.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as path from 'path';
|
||||
import * as es from 'event-stream';
|
||||
import * as pickle from 'chromium-pickle-js';
|
||||
import * as Filesystem from 'asar/lib/filesystem';
|
||||
import * as VinylFile from 'vinyl';
|
||||
import * as minimatch from 'minimatch';
|
||||
|
||||
export function createAsar(folderPath: string, unpackGlobs: string[], destFilename: string): NodeJS.ReadWriteStream {
|
||||
|
||||
const shouldUnpackFile = (file: VinylFile): boolean => {
|
||||
for (let i = 0; i < unpackGlobs.length; i++) {
|
||||
if (minimatch(file.relative, unpackGlobs[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const filesystem = new Filesystem(folderPath);
|
||||
const out: Buffer[] = [];
|
||||
|
||||
// Keep track of pending inserts
|
||||
let pendingInserts = 0;
|
||||
let onFileInserted = () => { pendingInserts--; };
|
||||
|
||||
// Do not insert twice the same directory
|
||||
const seenDir: { [key: string]: boolean; } = {};
|
||||
const insertDirectoryRecursive = (dir: string) => {
|
||||
if (seenDir[dir]) {
|
||||
return;
|
||||
}
|
||||
|
||||
let lastSlash = dir.lastIndexOf('/');
|
||||
if (lastSlash === -1) {
|
||||
lastSlash = dir.lastIndexOf('\\');
|
||||
}
|
||||
if (lastSlash !== -1) {
|
||||
insertDirectoryRecursive(dir.substring(0, lastSlash));
|
||||
}
|
||||
seenDir[dir] = true;
|
||||
filesystem.insertDirectory(dir);
|
||||
};
|
||||
|
||||
const insertDirectoryForFile = (file: string) => {
|
||||
let lastSlash = file.lastIndexOf('/');
|
||||
if (lastSlash === -1) {
|
||||
lastSlash = file.lastIndexOf('\\');
|
||||
}
|
||||
if (lastSlash !== -1) {
|
||||
insertDirectoryRecursive(file.substring(0, lastSlash));
|
||||
}
|
||||
};
|
||||
|
||||
const insertFile = (relativePath: string, stat: { size: number; mode: number; }, shouldUnpack: boolean) => {
|
||||
insertDirectoryForFile(relativePath);
|
||||
pendingInserts++;
|
||||
filesystem.insertFile(relativePath, shouldUnpack, { stat: stat }, {}, onFileInserted);
|
||||
};
|
||||
|
||||
return es.through(function (file) {
|
||||
if (file.stat.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
if (!file.stat.isFile()) {
|
||||
throw new Error(`unknown item in stream!`);
|
||||
}
|
||||
const shouldUnpack = shouldUnpackFile(file);
|
||||
insertFile(file.relative, { size: file.contents.length, mode: file.stat.mode }, shouldUnpack);
|
||||
|
||||
if (shouldUnpack) {
|
||||
// The file goes outside of xx.asar, in a folder xx.asar.unpacked
|
||||
const relative = path.relative(folderPath, file.path);
|
||||
this.queue(new VinylFile({
|
||||
cwd: folderPath,
|
||||
base: folderPath,
|
||||
path: path.join(destFilename + '.unpacked', relative),
|
||||
stat: file.stat,
|
||||
contents: file.contents
|
||||
}));
|
||||
} else {
|
||||
// The file goes inside of xx.asar
|
||||
out.push(file.contents);
|
||||
}
|
||||
}, function () {
|
||||
|
||||
let finish = () => {
|
||||
{
|
||||
const headerPickle = pickle.createEmpty();
|
||||
headerPickle.writeString(JSON.stringify(filesystem.header));
|
||||
const headerBuf = headerPickle.toBuffer();
|
||||
|
||||
const sizePickle = pickle.createEmpty();
|
||||
sizePickle.writeUInt32(headerBuf.length);
|
||||
const sizeBuf = sizePickle.toBuffer();
|
||||
|
||||
out.unshift(headerBuf);
|
||||
out.unshift(sizeBuf);
|
||||
}
|
||||
|
||||
const contents = Buffer.concat(out);
|
||||
out.length = 0;
|
||||
|
||||
this.queue(new VinylFile({
|
||||
cwd: folderPath,
|
||||
base: folderPath,
|
||||
path: destFilename,
|
||||
contents: contents
|
||||
}));
|
||||
this.queue(null);
|
||||
};
|
||||
|
||||
// Call finish() only when all file inserts have finished...
|
||||
if (pendingInserts === 0) {
|
||||
finish();
|
||||
} else {
|
||||
onFileInserted = () => {
|
||||
pendingInserts--;
|
||||
if (pendingInserts === 0) {
|
||||
finish();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
122
build/lib/builtInExtensions.js
Normal file
122
build/lib/builtInExtensions.js
Normal file
@@ -0,0 +1,122 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const mkdirp = require('mkdirp');
|
||||
const rimraf = require('rimraf');
|
||||
const es = require('event-stream');
|
||||
const rename = require('gulp-rename');
|
||||
const vfs = require('vinyl-fs');
|
||||
const ext = require('./extensions');
|
||||
const util = require('gulp-util');
|
||||
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
// @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file
|
||||
const builtInExtensions = require('../builtInExtensions.json');
|
||||
const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json');
|
||||
|
||||
function getExtensionPath(extension) {
|
||||
return path.join(root, '.build', 'builtInExtensions', extension.name);
|
||||
}
|
||||
|
||||
function isUpToDate(extension) {
|
||||
const packagePath = path.join(getExtensionPath(extension), 'package.json');
|
||||
|
||||
if (!fs.existsSync(packagePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const packageContents = fs.readFileSync(packagePath, { encoding: 'utf8' });
|
||||
|
||||
try {
|
||||
const diskVersion = JSON.parse(packageContents).version;
|
||||
return (diskVersion === extension.version);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function syncMarketplaceExtension(extension) {
|
||||
if (isUpToDate(extension)) {
|
||||
util.log(util.colors.blue('[marketplace]'), `${extension.name}@${extension.version}`, util.colors.green('✔︎'));
|
||||
return es.readArray([]);
|
||||
}
|
||||
|
||||
rimraf.sync(getExtensionPath(extension));
|
||||
|
||||
return ext.fromMarketplace(extension.name, extension.version)
|
||||
.pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`))
|
||||
.pipe(vfs.dest('.build/builtInExtensions'))
|
||||
.on('end', () => util.log(util.colors.blue('[marketplace]'), extension.name, util.colors.green('✔︎')));
|
||||
}
|
||||
|
||||
function syncExtension(extension, controlState) {
|
||||
switch (controlState) {
|
||||
case 'disabled':
|
||||
util.log(util.colors.blue('[disabled]'), util.colors.gray(extension.name));
|
||||
return es.readArray([]);
|
||||
|
||||
case 'marketplace':
|
||||
return syncMarketplaceExtension(extension);
|
||||
|
||||
default:
|
||||
if (!fs.existsSync(controlState)) {
|
||||
util.log(util.colors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`));
|
||||
return es.readArray([]);
|
||||
|
||||
} else if (!fs.existsSync(path.join(controlState, 'package.json'))) {
|
||||
util.log(util.colors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`));
|
||||
return es.readArray([]);
|
||||
}
|
||||
|
||||
util.log(util.colors.blue('[local]'), `${extension.name}: ${util.colors.cyan(controlState)}`, util.colors.green('✔︎'));
|
||||
return es.readArray([]);
|
||||
}
|
||||
}
|
||||
|
||||
function readControlFile() {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(controlFilePath, 'utf8'));
|
||||
} catch (err) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function writeControlFile(control) {
|
||||
mkdirp.sync(path.dirname(controlFilePath));
|
||||
fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2));
|
||||
}
|
||||
|
||||
function main() {
|
||||
util.log('Syncronizing built-in extensions...');
|
||||
util.log(`You can manage built-in extensions with the ${util.colors.cyan('--builtin')} flag`);
|
||||
|
||||
const control = readControlFile();
|
||||
const streams = [];
|
||||
|
||||
for (const extension of builtInExtensions) {
|
||||
let controlState = control[extension.name] || 'marketplace';
|
||||
control[extension.name] = controlState;
|
||||
|
||||
streams.push(syncExtension(extension, controlState));
|
||||
}
|
||||
|
||||
writeControlFile(control);
|
||||
|
||||
es.merge(streams)
|
||||
.on('error', err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
})
|
||||
.on('end', () => {
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -217,6 +217,7 @@ function removeDuplicateTSBoilerplate(destFiles) {
|
||||
{ start: /^var __metadata/, end: /^};$/ },
|
||||
{ start: /^var __param/, end: /^};$/ },
|
||||
{ start: /^var __awaiter/, end: /^};$/ },
|
||||
{ start: /^var __generator/, end: /^};$/ },
|
||||
];
|
||||
destFiles.forEach(function (destFile) {
|
||||
var SEEN_BOILERPLATE = [];
|
||||
|
||||
@@ -44,11 +44,11 @@ interface ILoaderPluginReqFunc {
|
||||
|
||||
export interface IEntryPoint {
|
||||
name: string;
|
||||
include: string[];
|
||||
exclude: string[];
|
||||
include?: string[];
|
||||
exclude?: string[];
|
||||
prepend: string[];
|
||||
append: string[];
|
||||
dest: string;
|
||||
append?: string[];
|
||||
dest?: string;
|
||||
}
|
||||
|
||||
interface IEntryPointMap {
|
||||
@@ -339,6 +339,7 @@ function removeDuplicateTSBoilerplate(destFiles: IConcatFile[]): IConcatFile[] {
|
||||
{ start: /^var __metadata/, end: /^};$/ },
|
||||
{ start: /^var __param/, end: /^};$/ },
|
||||
{ start: /^var __awaiter/, end: /^};$/ },
|
||||
{ start: /^var __generator/, end: /^};$/ },
|
||||
];
|
||||
|
||||
destFiles.forEach((destFile) => {
|
||||
|
||||
@@ -22,6 +22,9 @@ var rootDir = path.join(__dirname, '../../src');
|
||||
var options = require('../../src/tsconfig.json').compilerOptions;
|
||||
options.verbose = false;
|
||||
options.sourceMap = true;
|
||||
if (process.env['VSCODE_NO_SOURCEMAP']) {
|
||||
options.sourceMap = false;
|
||||
}
|
||||
options.rootDir = rootDir;
|
||||
options.sourceRoot = util.toFileUri(rootDir);
|
||||
function createCompile(build, emitError) {
|
||||
@@ -58,9 +61,13 @@ function compileTask(out, build) {
|
||||
return function () {
|
||||
var compile = createCompile(build, true);
|
||||
var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts'));
|
||||
// Do not write .d.ts files to disk, as they are not needed there.
|
||||
var dtsFilter = util.filter(function (data) { return !/\.d\.ts$/.test(data.path); });
|
||||
return src
|
||||
.pipe(compile())
|
||||
.pipe(dtsFilter)
|
||||
.pipe(gulp.dest(out))
|
||||
.pipe(dtsFilter.restore)
|
||||
.pipe(monacodtsTask(out, false));
|
||||
};
|
||||
}
|
||||
@@ -70,54 +77,19 @@ function watchTask(out, build) {
|
||||
var compile = createCompile(build);
|
||||
var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts'));
|
||||
var watchSrc = watch('src/**', { base: 'src' });
|
||||
// Do not write .d.ts files to disk, as they are not needed there.
|
||||
var dtsFilter = util.filter(function (data) { return !/\.d\.ts$/.test(data.path); });
|
||||
return watchSrc
|
||||
.pipe(util.incremental(compile, src, true))
|
||||
.pipe(dtsFilter)
|
||||
.pipe(gulp.dest(out))
|
||||
.pipe(dtsFilter.restore)
|
||||
.pipe(monacodtsTask(out, true));
|
||||
};
|
||||
}
|
||||
exports.watchTask = watchTask;
|
||||
function reloadTypeScriptNodeModule() {
|
||||
var util = require('gulp-util');
|
||||
function log(message) {
|
||||
var rest = [];
|
||||
for (var _i = 1; _i < arguments.length; _i++) {
|
||||
rest[_i - 1] = arguments[_i];
|
||||
}
|
||||
util.log.apply(util, [util.colors.cyan('[memory watch dog]'), message].concat(rest));
|
||||
}
|
||||
function heapUsed() {
|
||||
return (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2) + ' MB';
|
||||
}
|
||||
return es.through(function (data) {
|
||||
this.emit('data', data);
|
||||
}, function () {
|
||||
log('memory usage after compilation finished: ' + heapUsed());
|
||||
// It appears we are running into some variant of
|
||||
// https://bugs.chromium.org/p/v8/issues/detail?id=2073
|
||||
//
|
||||
// Even though all references are dropped, some
|
||||
// optimized methods in the TS compiler end up holding references
|
||||
// to the entire TypeScript language host (>600MB)
|
||||
//
|
||||
// The idea is to force v8 to drop references to these
|
||||
// optimized methods, by "reloading" the typescript node module
|
||||
log('Reloading typescript node module...');
|
||||
var resolvedName = require.resolve('typescript');
|
||||
var originalModule = require.cache[resolvedName];
|
||||
delete require.cache[resolvedName];
|
||||
var newExports = require('typescript');
|
||||
require.cache[resolvedName] = originalModule;
|
||||
for (var prop in newExports) {
|
||||
if (newExports.hasOwnProperty(prop)) {
|
||||
originalModule.exports[prop] = newExports[prop];
|
||||
}
|
||||
}
|
||||
log('typescript node module reloaded.');
|
||||
this.emit('end');
|
||||
});
|
||||
}
|
||||
function monacodtsTask(out, isWatch) {
|
||||
var basePath = path.resolve(process.cwd(), out);
|
||||
var neededFiles = {};
|
||||
monacodts.getFilesToWatch(out).forEach(function (filePath) {
|
||||
filePath = path.normalize(filePath);
|
||||
@@ -160,7 +132,7 @@ function monacodtsTask(out, isWatch) {
|
||||
}));
|
||||
}
|
||||
resultStream = es.through(function (data) {
|
||||
var filePath = path.normalize(data.path);
|
||||
var filePath = path.normalize(path.resolve(basePath, data.relative));
|
||||
if (neededFiles[filePath]) {
|
||||
setInputFile(filePath, data.contents.toString());
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ const rootDir = path.join(__dirname, '../../src');
|
||||
const options = require('../../src/tsconfig.json').compilerOptions;
|
||||
options.verbose = false;
|
||||
options.sourceMap = true;
|
||||
if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry
|
||||
options.sourceMap = false;
|
||||
}
|
||||
options.rootDir = rootDir;
|
||||
options.sourceRoot = util.toFileUri(rootDir);
|
||||
|
||||
@@ -49,7 +52,6 @@ function createCompile(build: boolean, emitError?: boolean): (token?: util.ICanc
|
||||
.pipe(tsFilter)
|
||||
.pipe(util.loadSourcemaps())
|
||||
.pipe(ts(token))
|
||||
// .pipe(build ? reloadTypeScriptNodeModule() : es.through())
|
||||
.pipe(noDeclarationsFilter)
|
||||
.pipe(build ? nls() : es.through())
|
||||
.pipe(noDeclarationsFilter.restore)
|
||||
@@ -75,9 +77,14 @@ export function compileTask(out: string, build: boolean): () => NodeJS.ReadWrite
|
||||
gulp.src('node_modules/typescript/lib/lib.d.ts'),
|
||||
);
|
||||
|
||||
// Do not write .d.ts files to disk, as they are not needed there.
|
||||
const dtsFilter = util.filter(data => !/\.d\.ts$/.test(data.path));
|
||||
|
||||
return src
|
||||
.pipe(compile())
|
||||
.pipe(dtsFilter)
|
||||
.pipe(gulp.dest(out))
|
||||
.pipe(dtsFilter.restore)
|
||||
.pipe(monacodtsTask(out, false));
|
||||
};
|
||||
}
|
||||
@@ -93,62 +100,22 @@ export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteSt
|
||||
);
|
||||
const watchSrc = watch('src/**', { base: 'src' });
|
||||
|
||||
// Do not write .d.ts files to disk, as they are not needed there.
|
||||
const dtsFilter = util.filter(data => !/\.d\.ts$/.test(data.path));
|
||||
|
||||
return watchSrc
|
||||
.pipe(util.incremental(compile, src, true))
|
||||
.pipe(dtsFilter)
|
||||
.pipe(gulp.dest(out))
|
||||
.pipe(dtsFilter.restore)
|
||||
.pipe(monacodtsTask(out, true));
|
||||
};
|
||||
}
|
||||
|
||||
function reloadTypeScriptNodeModule(): NodeJS.ReadWriteStream {
|
||||
var util = require('gulp-util');
|
||||
function log(message: any, ...rest: any[]): void {
|
||||
util.log(util.colors.cyan('[memory watch dog]'), message, ...rest);
|
||||
}
|
||||
|
||||
function heapUsed(): string {
|
||||
return (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2) + ' MB';
|
||||
}
|
||||
|
||||
return es.through(function (data) {
|
||||
this.emit('data', data);
|
||||
}, function () {
|
||||
|
||||
log('memory usage after compilation finished: ' + heapUsed());
|
||||
|
||||
// It appears we are running into some variant of
|
||||
// https://bugs.chromium.org/p/v8/issues/detail?id=2073
|
||||
//
|
||||
// Even though all references are dropped, some
|
||||
// optimized methods in the TS compiler end up holding references
|
||||
// to the entire TypeScript language host (>600MB)
|
||||
//
|
||||
// The idea is to force v8 to drop references to these
|
||||
// optimized methods, by "reloading" the typescript node module
|
||||
|
||||
log('Reloading typescript node module...');
|
||||
|
||||
var resolvedName = require.resolve('typescript');
|
||||
|
||||
var originalModule = require.cache[resolvedName];
|
||||
delete require.cache[resolvedName];
|
||||
var newExports = require('typescript');
|
||||
require.cache[resolvedName] = originalModule;
|
||||
|
||||
for (var prop in newExports) {
|
||||
if (newExports.hasOwnProperty(prop)) {
|
||||
originalModule.exports[prop] = newExports[prop];
|
||||
}
|
||||
}
|
||||
|
||||
log('typescript node module reloaded.');
|
||||
|
||||
this.emit('end');
|
||||
});
|
||||
}
|
||||
|
||||
function monacodtsTask(out: string, isWatch: boolean): NodeJS.ReadWriteStream {
|
||||
|
||||
const basePath = path.resolve(process.cwd(), out);
|
||||
|
||||
const neededFiles: { [file: string]: boolean; } = {};
|
||||
monacodts.getFilesToWatch(out).forEach(function (filePath) {
|
||||
filePath = path.normalize(filePath);
|
||||
@@ -196,7 +163,7 @@ function monacodtsTask(out: string, isWatch: boolean): NodeJS.ReadWriteStream {
|
||||
}
|
||||
|
||||
resultStream = es.through(function (data) {
|
||||
const filePath = path.normalize(data.path);
|
||||
const filePath = path.normalize(path.resolve(basePath, data.relative));
|
||||
if (neededFiles[filePath]) {
|
||||
setInputFile(filePath, data.contents.toString());
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,10 +46,6 @@
|
||||
"name": "vs/workbench/parts/execution",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/parts/explorers",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/parts/extensions",
|
||||
"project": "vscode-workbench"
|
||||
@@ -71,7 +67,11 @@
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/parts/nps",
|
||||
"name": "vs/workbench/parts/localizations",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/parts/logs",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
@@ -138,6 +138,10 @@
|
||||
"name": "vs/workbench/parts/welcome",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/actions",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/configuration",
|
||||
"project": "vscode-workbench"
|
||||
@@ -146,6 +150,10 @@
|
||||
"name": "vs/workbench/services/crashReporter",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/dialogs",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/editor",
|
||||
"project": "vscode-workbench"
|
||||
@@ -154,6 +162,10 @@
|
||||
"name": "vs/workbench/services/extensions",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/jsonschemas",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/files",
|
||||
"project": "vscode-workbench"
|
||||
@@ -162,10 +174,6 @@
|
||||
"name": "vs/workbench/services/keybinding",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/message",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/mode",
|
||||
"project": "vscode-workbench"
|
||||
@@ -193,10 +201,6 @@
|
||||
{
|
||||
"name": "vs/workbench/services/decorations",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "setup_messages",
|
||||
"project": "vscode-workbench"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
1030
build/lib/i18n.ts
1030
build/lib/i18n.ts
File diff suppressed because it is too large
Load Diff
@@ -79,7 +79,7 @@ function isImportNode(node) {
|
||||
function fileFrom(file, contents, path) {
|
||||
if (path === void 0) { path = file.path; }
|
||||
return new File({
|
||||
contents: new Buffer(contents),
|
||||
contents: Buffer.from(contents),
|
||||
base: file.base,
|
||||
cwd: file.cwd,
|
||||
path: path
|
||||
|
||||
@@ -131,7 +131,7 @@ module nls {
|
||||
|
||||
export function fileFrom(file: File, contents: string, path: string = file.path) {
|
||||
return new File({
|
||||
contents: new Buffer(contents),
|
||||
contents: Buffer.from(contents),
|
||||
base: file.base,
|
||||
cwd: file.cwd,
|
||||
path: path
|
||||
|
||||
@@ -59,7 +59,7 @@ function loader(bundledFileHeader, bundleLoader) {
|
||||
this.emit('data', new VinylFile({
|
||||
path: 'fake',
|
||||
base: '',
|
||||
contents: new Buffer(bundledFileHeader)
|
||||
contents: Buffer.from(bundledFileHeader)
|
||||
}));
|
||||
this.emit('data', data);
|
||||
}
|
||||
@@ -98,7 +98,7 @@ function toConcatStream(bundledFileHeader, sources, dest) {
|
||||
return new VinylFile({
|
||||
path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake',
|
||||
base: base,
|
||||
contents: new Buffer(source.contents)
|
||||
contents: Buffer.from(source.contents)
|
||||
});
|
||||
});
|
||||
return es.readArray(treatedSources)
|
||||
@@ -141,7 +141,7 @@ function optimizeTask(opts) {
|
||||
bundleInfoArray.push(new VinylFile({
|
||||
path: 'bundleInfo.json',
|
||||
base: '.',
|
||||
contents: new Buffer(JSON.stringify(result.bundleData, null, '\t'))
|
||||
contents: Buffer.from(JSON.stringify(result.bundleData, null, '\t'))
|
||||
}));
|
||||
}
|
||||
es.readArray(bundleInfoArray).pipe(bundleInfoStream);
|
||||
@@ -174,7 +174,6 @@ function optimizeTask(opts) {
|
||||
};
|
||||
}
|
||||
exports.optimizeTask = optimizeTask;
|
||||
;
|
||||
/**
|
||||
* Wrap around uglify and allow the preserveComments function
|
||||
* to have a file "context" to include our copyright only once per file.
|
||||
@@ -237,4 +236,3 @@ function minifyTask(src, sourceMapBaseUrl) {
|
||||
};
|
||||
}
|
||||
exports.minifyTask = minifyTask;
|
||||
;
|
||||
|
||||
@@ -31,7 +31,7 @@ function log(prefix: string, message: string): void {
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
export function loaderConfig(emptyPaths: string[]) {
|
||||
export function loaderConfig(emptyPaths?: string[]) {
|
||||
const result = {
|
||||
paths: {
|
||||
'vs': 'out-build/vs',
|
||||
@@ -73,7 +73,7 @@ function loader(bundledFileHeader: string, bundleLoader: boolean): NodeJS.ReadWr
|
||||
this.emit('data', new VinylFile({
|
||||
path: 'fake',
|
||||
base: '',
|
||||
contents: new Buffer(bundledFileHeader)
|
||||
contents: Buffer.from(bundledFileHeader)
|
||||
}));
|
||||
this.emit('data', data);
|
||||
} else {
|
||||
@@ -117,7 +117,7 @@ function toConcatStream(bundledFileHeader: string, sources: bundle.IFile[], dest
|
||||
return new VinylFile({
|
||||
path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake',
|
||||
base: base,
|
||||
contents: new Buffer(source.contents)
|
||||
contents: Buffer.from(source.contents)
|
||||
});
|
||||
});
|
||||
|
||||
@@ -165,7 +165,7 @@ export interface IOptimizeTaskOpts {
|
||||
/**
|
||||
* (languages to process)
|
||||
*/
|
||||
languages: string[];
|
||||
languages: i18n.Language[];
|
||||
}
|
||||
export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStream {
|
||||
const entryPoints = opts.entryPoints;
|
||||
@@ -201,7 +201,7 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr
|
||||
bundleInfoArray.push(new VinylFile({
|
||||
path: 'bundleInfo.json',
|
||||
base: '.',
|
||||
contents: new Buffer(JSON.stringify(result.bundleData, null, '\t'))
|
||||
contents: Buffer.from(JSON.stringify(result.bundleData, null, '\t'))
|
||||
}));
|
||||
}
|
||||
es.readArray(bundleInfoArray).pipe(bundleInfoStream);
|
||||
@@ -241,7 +241,7 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr
|
||||
}))
|
||||
.pipe(gulp.dest(out));
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
declare class FileWithCopyright extends VinylFile {
|
||||
public __hasOurCopyright: boolean;
|
||||
@@ -295,7 +295,7 @@ function uglifyWithCopyrights(): NodeJS.ReadWriteStream {
|
||||
return es.duplex(input, output);
|
||||
}
|
||||
|
||||
export function minifyTask(src: string, sourceMapBaseUrl: string): (cb: any) => void {
|
||||
export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) => void {
|
||||
const sourceMappingURL = sourceMapBaseUrl && (f => `${sourceMapBaseUrl}/${f.relative}.map`);
|
||||
|
||||
return cb => {
|
||||
@@ -326,4 +326,4 @@ export function minifyTask(src: string, sourceMapBaseUrl: string): (cb: any) =>
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
9
build/lib/typings/event-stream.d.ts
vendored
9
build/lib/typings/event-stream.d.ts
vendored
@@ -1,7 +1,14 @@
|
||||
declare module "event-stream" {
|
||||
import { Stream } from 'stream';
|
||||
import { ThroughStream } from 'through';
|
||||
import { ThroughStream as _ThroughStream} from 'through';
|
||||
import { MapStream } from 'map-stream';
|
||||
import * as File from 'vinyl';
|
||||
|
||||
export interface ThroughStream extends _ThroughStream {
|
||||
queue(data: File | null);
|
||||
push(data: File | null);
|
||||
paused: boolean;
|
||||
}
|
||||
|
||||
function merge(streams: Stream[]): ThroughStream;
|
||||
function merge(...streams: Stream[]): ThroughStream;
|
||||
|
||||
@@ -143,7 +143,7 @@ function loadSourcemaps() {
|
||||
cb(null, f);
|
||||
return;
|
||||
}
|
||||
f.contents = new Buffer(contents.replace(/\/\/# sourceMappingURL=(.*)$/g, ''), 'utf8');
|
||||
f.contents = Buffer.from(contents.replace(/\/\/# sourceMappingURL=(.*)$/g, ''), 'utf8');
|
||||
fs.readFile(path.join(path.dirname(f.path), lastMatch[1]), 'utf8', function (err, contents) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
@@ -160,7 +160,7 @@ function stripSourceMappingURL() {
|
||||
var output = input
|
||||
.pipe(es.mapSync(function (f) {
|
||||
var contents = f.contents.toString('utf8');
|
||||
f.contents = new Buffer(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8');
|
||||
f.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8');
|
||||
return f;
|
||||
}));
|
||||
return es.duplex(input, output);
|
||||
@@ -173,7 +173,6 @@ function rimraf(dir) {
|
||||
if (!err) {
|
||||
return cb();
|
||||
}
|
||||
;
|
||||
if (err.code === 'ENOTEMPTY' && ++retries < 5) {
|
||||
return setTimeout(function () { return retry(cb); }, 10);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export interface IStreamProvider {
|
||||
(cancellationToken?: ICancellationToken): NodeJS.ReadWriteStream;
|
||||
}
|
||||
|
||||
export function incremental(streamProvider: IStreamProvider, initial: NodeJS.ReadWriteStream, supportsCancellation: boolean): NodeJS.ReadWriteStream {
|
||||
export function incremental(streamProvider: IStreamProvider, initial: NodeJS.ReadWriteStream, supportsCancellation?: boolean): NodeJS.ReadWriteStream {
|
||||
const input = es.through();
|
||||
const output = es.through();
|
||||
let state = 'idle';
|
||||
@@ -129,7 +129,7 @@ export function skipDirectories(): NodeJS.ReadWriteStream {
|
||||
});
|
||||
}
|
||||
|
||||
export function cleanNodeModule(name: string, excludes: string[], includes: string[]): NodeJS.ReadWriteStream {
|
||||
export function cleanNodeModule(name: string, excludes: string[], includes?: string[]): NodeJS.ReadWriteStream {
|
||||
const toGlob = (path: string) => '**/node_modules/' + name + (path ? '/' + path : '');
|
||||
const negate = (str: string) => '!' + str;
|
||||
|
||||
@@ -190,7 +190,7 @@ export function loadSourcemaps(): NodeJS.ReadWriteStream {
|
||||
return;
|
||||
}
|
||||
|
||||
f.contents = new Buffer(contents.replace(/\/\/# sourceMappingURL=(.*)$/g, ''), 'utf8');
|
||||
f.contents = Buffer.from(contents.replace(/\/\/# sourceMappingURL=(.*)$/g, ''), 'utf8');
|
||||
|
||||
fs.readFile(path.join(path.dirname(f.path), lastMatch[1]), 'utf8', (err, contents) => {
|
||||
if (err) { return cb(err); }
|
||||
@@ -209,7 +209,7 @@ export function stripSourceMappingURL(): NodeJS.ReadWriteStream {
|
||||
const output = input
|
||||
.pipe(es.mapSync<VinylFile, VinylFile>(f => {
|
||||
const contents = (<Buffer>f.contents).toString('utf8');
|
||||
f.contents = new Buffer(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8');
|
||||
f.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8');
|
||||
return f;
|
||||
}));
|
||||
|
||||
@@ -223,7 +223,7 @@ export function rimraf(dir: string): (cb: any) => void {
|
||||
_rimraf(dir, { maxBusyTries: 1 }, (err: any) => {
|
||||
if (!err) {
|
||||
return cb();
|
||||
};
|
||||
}
|
||||
|
||||
if (err.code === 'ENOTEMPTY' && ++retries < 5) {
|
||||
return setTimeout(() => retry(cb), 10);
|
||||
|
||||
@@ -9,7 +9,7 @@ const es = require('event-stream');
|
||||
function handleDeletions() {
|
||||
return es.mapSync(f => {
|
||||
if (/\.ts$/.test(f.relative) && !f.contents) {
|
||||
f.contents = new Buffer('');
|
||||
f.contents = Buffer.from('');
|
||||
f.stat = { mtime: new Date() };
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,12 @@ function watch(root) {
|
||||
path: path,
|
||||
base: root
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
file.event = type;
|
||||
result.emit('data', file);
|
||||
}
|
||||
|
||||
nsfw(root, function(events) {
|
||||
nsfw(root, function (events) {
|
||||
for (var i = 0; i < events.length; i++) {
|
||||
var e = events[i];
|
||||
var changeType = e.action;
|
||||
@@ -47,16 +47,16 @@ function watch(root) {
|
||||
handleEvent(path.join(e.directory, e.file), toChangeType(changeType));
|
||||
}
|
||||
}
|
||||
}).then(function(watcher) {
|
||||
}).then(function (watcher) {
|
||||
watcher.start();
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
var cache = Object.create(null);
|
||||
|
||||
module.exports = function(pattern, options) {
|
||||
module.exports = function (pattern, options) {
|
||||
options = options || {};
|
||||
|
||||
var cwd = path.normalize(options.cwd || process.cwd());
|
||||
@@ -66,7 +66,7 @@ module.exports = function(pattern, options) {
|
||||
watcher = cache[cwd] = watch(cwd);
|
||||
}
|
||||
|
||||
var rebase = !options.base ? es.through() : es.mapSync(function(f) {
|
||||
var rebase = !options.base ? es.through() : es.mapSync(function (f) {
|
||||
f.base = options.base;
|
||||
return f;
|
||||
});
|
||||
@@ -74,13 +74,13 @@ module.exports = function(pattern, options) {
|
||||
return watcher
|
||||
.pipe(filter(['**', '!.git{,/**}'])) // ignore all things git
|
||||
.pipe(filter(pattern))
|
||||
.pipe(es.map(function(file, cb) {
|
||||
fs.stat(file.path, function(err, stat) {
|
||||
.pipe(es.map(function (file, cb) {
|
||||
fs.stat(file.path, function (err, stat) {
|
||||
if (err && err.code === 'ENOENT') { return cb(null, file); }
|
||||
if (err) { return cb(); }
|
||||
if (!stat.isFile()) { return cb(); }
|
||||
|
||||
fs.readFile(file.path, function(err, contents) {
|
||||
fs.readFile(file.path, function (err, contents) {
|
||||
if (err && err.code === 'ENOENT') { return cb(null, file); }
|
||||
if (err) { return cb(); }
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ function watch(root) {
|
||||
var result = es.through();
|
||||
var child = cp.spawn(watcherPath, [root]);
|
||||
|
||||
child.stdout.on('data', function(data) {
|
||||
child.stdout.on('data', function (data) {
|
||||
// @ts-ignore
|
||||
var lines = data.toString('utf8').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i].trim();
|
||||
@@ -46,17 +47,17 @@ function watch(root) {
|
||||
path: changePathFull,
|
||||
base: root
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
file.event = toChangeType(changeType);
|
||||
result.emit('data', file);
|
||||
}
|
||||
});
|
||||
|
||||
child.stderr.on('data', function(data) {
|
||||
child.stderr.on('data', function (data) {
|
||||
result.emit('error', data);
|
||||
});
|
||||
|
||||
child.on('exit', function(code) {
|
||||
child.on('exit', function (code) {
|
||||
result.emit('error', 'Watcher died with code ' + code);
|
||||
child = null;
|
||||
});
|
||||
@@ -70,7 +71,7 @@ function watch(root) {
|
||||
|
||||
var cache = Object.create(null);
|
||||
|
||||
module.exports = function(pattern, options) {
|
||||
module.exports = function (pattern, options) {
|
||||
options = options || {};
|
||||
|
||||
var cwd = path.normalize(options.cwd || process.cwd());
|
||||
|
||||
@@ -1 +1,21 @@
|
||||
See project root directory
|
||||
The Source EULA
|
||||
|
||||
Copyright (c) 2016 Microsoft Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -32,7 +32,7 @@ END OF winjs NOTICES AND INFORMATION
|
||||
|
||||
%% string_scorer version 0.1.20 (https://github.com/joshaven/string_score)
|
||||
=========================================
|
||||
This software is released under the MIT license:
|
||||
This software is released under the Source EULA:
|
||||
|
||||
Copyright (c) Joshaven Potter
|
||||
|
||||
@@ -60,7 +60,7 @@ END OF string_scorer NOTICES AND INFORMATION
|
||||
|
||||
%% chjj-marked NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
The MIT License (MIT)
|
||||
The Source EULA
|
||||
|
||||
Copyright (c) 2011-2014, Christopher Jeffrey (https://github.com/chjj/)
|
||||
|
||||
|
||||
@@ -62,19 +62,24 @@ export interface ICommandHandler {
|
||||
#include(vs/editor/standalone/browser/colorizer): IColorizerOptions, IColorizerElementOptions
|
||||
#include(vs/base/common/scrollable): ScrollbarVisibility
|
||||
#include(vs/platform/theme/common/themeService): ThemeColor
|
||||
#includeAll(vs/editor/common/editorCommon;IMode=>languages.IMode;LanguageIdentifier=>languages.LanguageIdentifier;editorOptions.=>): ISelection, IScrollEvent
|
||||
#includeAll(vs/editor/common/model;LanguageIdentifier=>languages.LanguageIdentifier): IScrollEvent
|
||||
#includeAll(vs/editor/common/editorCommon;editorOptions.=>): IScrollEvent
|
||||
#includeAll(vs/editor/common/model/textModelEvents):
|
||||
#includeAll(vs/editor/common/controller/cursorEvents):
|
||||
#includeAll(vs/editor/common/config/editorOptions):
|
||||
#includeAll(vs/editor/browser/editorBrowser;editorCommon.=>;editorOptions.=>):
|
||||
#include(vs/editor/common/config/fontInfo): FontInfo, BareFontInfo
|
||||
|
||||
//compatibility:
|
||||
export type IReadOnlyModel = ITextModel;
|
||||
export type IModel = ITextModel;
|
||||
}
|
||||
|
||||
declare module monaco.languages {
|
||||
|
||||
#includeAll(vs/editor/standalone/browser/standaloneLanguages;modes.=>;editorCommon.=>editor.;IMarkerData=>editor.IMarkerData):
|
||||
#includeAll(vs/editor/standalone/browser/standaloneLanguages;modes.=>;editorCommon.=>editor.;model.=>editor.;IMarkerData=>editor.IMarkerData):
|
||||
#includeAll(vs/editor/common/modes/languageConfiguration):
|
||||
#includeAll(vs/editor/common/modes;editorCommon.IRange=>IRange;editorCommon.IPosition=>IPosition;editorCommon.=>editor.;IMarkerData=>editor.IMarkerData):
|
||||
#includeAll(vs/editor/common/modes;editorCommon.IRange=>IRange;editorCommon.IPosition=>IPosition;editorCommon.=>editor.;IMarkerData=>editor.IMarkerData;model.=>editor.):
|
||||
#include(vs/editor/common/services/modeService): ILanguageExtensionPoint
|
||||
#includeAll(vs/editor/standalone/common/monarch/monarchTypes):
|
||||
|
||||
|
||||
@@ -21,16 +21,16 @@ function yarnInstall(location, opts) {
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
yarnInstall('extensions-modules');
|
||||
yarnInstall('extensions'); // node modules shared by all extensions
|
||||
|
||||
const extensions = [
|
||||
'vscode-colorize-tests',
|
||||
'json',
|
||||
'mssql',
|
||||
'mssql',
|
||||
'configuration-editing',
|
||||
'extension-editing',
|
||||
'markdown',
|
||||
'markdown-basics',
|
||||
'git',
|
||||
'merge-conflict',
|
||||
'insights-default',
|
||||
@@ -43,6 +43,7 @@ extensions.forEach(extension => yarnInstall(`extensions/${extension}`));
|
||||
function yarnInstallBuildDependencies() {
|
||||
// make sure we install the deps of build/lib/watch for the system installed
|
||||
// node, since that is the driver of gulp
|
||||
//@ts-ignore
|
||||
const env = Object.assign({}, process.env);
|
||||
const watchPath = path.join(path.dirname(__dirname), 'lib', 'watch');
|
||||
const yarnrcPath = path.join(watchPath, '.yarnrc');
|
||||
@@ -60,4 +61,5 @@ runtime "${runtime}"`;
|
||||
}
|
||||
|
||||
yarnInstall(`build`); // node modules required for build
|
||||
yarnInstall('test/smoke'); // node modules required for smoketest
|
||||
yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron
|
||||
@@ -4,9 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const cp = require('child_process');
|
||||
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function updateGrammar(location) {
|
||||
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
const result = cp.spawnSync(npm, ['run', 'update-grammar'], {
|
||||
cwd: location,
|
||||
stdio: 'inherit'
|
||||
@@ -17,50 +19,17 @@ function updateGrammar(location) {
|
||||
}
|
||||
}
|
||||
|
||||
const extensions = [
|
||||
'bat',
|
||||
'clojure',
|
||||
'coffeescript',
|
||||
'cpp',
|
||||
'csharp',
|
||||
'css',
|
||||
'diff',
|
||||
'docker',
|
||||
'fsharp',
|
||||
'gitsyntax',
|
||||
'go',
|
||||
'groovy',
|
||||
'handlebars',
|
||||
'hlsl',
|
||||
'html',
|
||||
'ini',
|
||||
'java',
|
||||
// 'javascript', updated through JavaScript
|
||||
'json',
|
||||
'less',
|
||||
'lua',
|
||||
'make',
|
||||
'markdown',
|
||||
'objective-c',
|
||||
'perl',
|
||||
'php',
|
||||
// 'powershell', grammar not ready yet, @daviwil will ping when ready
|
||||
'pug',
|
||||
'python',
|
||||
'r',
|
||||
'razor',
|
||||
'ruby',
|
||||
'rust',
|
||||
'scss',
|
||||
'shaderlab',
|
||||
'shellscript',
|
||||
'sql',
|
||||
'swift',
|
||||
'typescript',
|
||||
'vb',
|
||||
'xml',
|
||||
'yaml'
|
||||
];
|
||||
const allExtensionFolders = fs.readdirSync('extensions');
|
||||
const extensions = allExtensionFolders.filter(e => {
|
||||
try {
|
||||
let packageJSON = JSON.parse(fs.readFileSync(path.join('extensions', e, 'package.json')).toString());
|
||||
return packageJSON && packageJSON.scripts && packageJSON.scripts['update-grammar'];
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Updating ${extensions.length} grammars...`);
|
||||
|
||||
extensions.forEach(extension => updateGrammar(`extensions/${extension}`));
|
||||
|
||||
@@ -70,4 +39,5 @@ if (process.platform === 'win32') {
|
||||
cp.spawn('.\scripts\test-integration.bat', [], { env: process.env, stdio: 'inherit' });
|
||||
} else {
|
||||
cp.spawn('/bin/bash', ['./scripts/test-integration.sh'], { env: process.env, stdio: 'inherit' });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,19 @@ var url = require('url');
|
||||
|
||||
function getOptions(urlString) {
|
||||
var _url = url.parse(urlString);
|
||||
var headers = {
|
||||
'User-Agent': 'VSCode'
|
||||
};
|
||||
var token = process.env['GITHUB_TOKEN'];
|
||||
if (token) {
|
||||
headers['Authorization'] = 'token ' + token
|
||||
}
|
||||
return {
|
||||
protocol: _url.protocol,
|
||||
host: _url.host,
|
||||
port: _url.port,
|
||||
path: _url.path,
|
||||
headers: {
|
||||
'User-Agent': 'NodeJS'
|
||||
}
|
||||
headers: headers
|
||||
};
|
||||
}
|
||||
|
||||
@@ -32,12 +37,16 @@ function download(url, redirectCount) {
|
||||
response.on('data', function (data) {
|
||||
content += data.toString();
|
||||
}).on('end', function () {
|
||||
if (response.statusCode === 403 && response.headers['x-ratelimit-remaining'] === '0') {
|
||||
e('GitHub API rate exceeded. Set GITHUB_TOKEN environment variable to increase rate limit.');
|
||||
return;
|
||||
}
|
||||
let count = redirectCount || 0;
|
||||
if (count < 5 && response.statusCode >= 300 && response.statusCode <= 303 || response.statusCode === 307) {
|
||||
let location = response.headers['location'];
|
||||
if (location) {
|
||||
console.log("Redirected " + url + " to " + location);
|
||||
download(location, count+1).then(c, e);
|
||||
download(location, count + 1).then(c, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -59,17 +68,13 @@ function getCommitSha(repoId, repoPath) {
|
||||
commitDate: lastCommit.commit.author.date
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Failed extracting the SHA: " + content);
|
||||
return Promise.resolve(null);
|
||||
return Promise.reject(new Error("Failed extracting the SHA: " + content));
|
||||
}
|
||||
}, function () {
|
||||
console.error('Failed loading ' + commitInfo);
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
}
|
||||
|
||||
exports.update = function (repoId, repoPath, dest, modifyGrammar) {
|
||||
var contentPath = 'https://raw.githubusercontent.com/' + repoId + '/master/' + repoPath;
|
||||
exports.update = function (repoId, repoPath, dest, modifyGrammar, version = 'master') {
|
||||
var contentPath = 'https://raw.githubusercontent.com/' + repoId + `/${version}/` + repoPath;
|
||||
console.log('Reading from ' + contentPath);
|
||||
return download(contentPath).then(function (content) {
|
||||
var ext = path.extname(repoPath);
|
||||
@@ -81,8 +86,7 @@ exports.update = function (repoId, repoPath, dest, modifyGrammar) {
|
||||
} else if (ext === '.json') {
|
||||
grammar = JSON.parse(content);
|
||||
} else {
|
||||
console.error('Unknown file extension: ' + ext);
|
||||
return;
|
||||
return Promise.reject(new Error('Unknown file extension: ' + ext));
|
||||
}
|
||||
if (modifyGrammar) {
|
||||
modifyGrammar(grammar);
|
||||
@@ -99,8 +103,10 @@ exports.update = function (repoId, repoPath, dest, modifyGrammar) {
|
||||
if (info) {
|
||||
result.version = 'https://github.com/' + repoId + '/commit/' + info.commitSha;
|
||||
}
|
||||
for (let key in grammar) {
|
||||
if (!result.hasOwnProperty(key)) {
|
||||
|
||||
let keys = ['name', 'scopeName', 'comment', 'injections', 'patterns', 'repository'];
|
||||
for (let key of keys) {
|
||||
if (grammar.hasOwnProperty(key)) {
|
||||
result[key] = grammar[key];
|
||||
}
|
||||
}
|
||||
@@ -113,11 +119,14 @@ exports.update = function (repoId, repoPath, dest, modifyGrammar) {
|
||||
console.log('Updated ' + path.basename(dest));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
});
|
||||
|
||||
}, console.error);
|
||||
}, console.error).catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
};
|
||||
|
||||
if (path.basename(process.argv[1]) === 'update-grammar.js') {
|
||||
|
||||
69
build/npm/update-localization-extension.js
Normal file
69
build/npm/update-localization-extension.js
Normal file
@@ -0,0 +1,69 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let i18n = require("../lib/i18n");
|
||||
|
||||
let fs = require("fs");
|
||||
let path = require("path");
|
||||
let vfs = require("vinyl-fs");
|
||||
let rimraf = require('rimraf');
|
||||
|
||||
function update(idOrPath) {
|
||||
if (!idOrPath) {
|
||||
throw new Error('Argument must be the location of the localization extension.');
|
||||
}
|
||||
let locExtFolder = idOrPath;
|
||||
if (/^\w{2}(-\w+)?$/.test(idOrPath)) {
|
||||
locExtFolder = '../vscode-language-pack-' + idOrPath;
|
||||
}
|
||||
let locExtStat = fs.statSync(locExtFolder);
|
||||
if (!locExtStat || !locExtStat.isDirectory) {
|
||||
throw new Error('No directory found at ' + idOrPath);
|
||||
}
|
||||
let packageJSON = JSON.parse(fs.readFileSync(path.join(locExtFolder, 'package.json')).toString());
|
||||
let contributes = packageJSON['contributes'];
|
||||
if (!contributes) {
|
||||
throw new Error('The extension must define a "localizations" contribution in the "package.json"');
|
||||
}
|
||||
let localizations = contributes['localizations'];
|
||||
if (!localizations) {
|
||||
throw new Error('The extension must define a "localizations" contribution of type array in the "package.json"');
|
||||
}
|
||||
|
||||
localizations.forEach(function (localization) {
|
||||
if (!localization.languageId || !localization.languageName || !localization.localizedLanguageName) {
|
||||
throw new Error('Each localization contribution must define "languageId", "languageName" and "localizedLanguageName" properties.');
|
||||
}
|
||||
let server = localization.server || 'www.transifex.com';
|
||||
let userName = localization.userName || 'api';
|
||||
let apiToken = process.env.TRANSIFEX_API_TOKEN;
|
||||
let languageId = localization.transifexId || localization.languageId;
|
||||
let translationDataFolder = path.join(locExtFolder, 'translations');
|
||||
|
||||
if (fs.existsSync(translationDataFolder) && fs.existsSync(path.join(translationDataFolder, 'main.i18n.json'))) {
|
||||
console.log('Clearing \'' + translationDataFolder + '\'...');
|
||||
rimraf.sync(translationDataFolder);
|
||||
}
|
||||
|
||||
console.log('Downloading translations for \'' + languageId + '\' to \'' + translationDataFolder + '\'...');
|
||||
const translationPaths = [];
|
||||
i18n.pullI18nPackFiles(server, userName, apiToken, { id: languageId }, translationPaths)
|
||||
.pipe(vfs.dest(translationDataFolder)).on('end', function () {
|
||||
localization.translations = [];
|
||||
for (let tp of translationPaths) {
|
||||
localization.translations.push({ id: tp.id, path: `./translations/${tp.resourceName}`});
|
||||
}
|
||||
fs.writeFileSync(path.join(locExtFolder, 'package.json'), JSON.stringify(packageJSON, null, '\t'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
if (path.basename(process.argv[1]) === 'update-localization-extension.js') {
|
||||
update(process.argv[2]);
|
||||
}
|
||||
@@ -12,17 +12,18 @@
|
||||
"azure-storage": "^2.1.0",
|
||||
"decompress": "^4.2.0",
|
||||
"documentdb": "1.13.0",
|
||||
"extensions-modules": "file:../extensions-modules",
|
||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
|
||||
"fs-extra-promise": "^1.0.1",
|
||||
"mime": "^1.3.4",
|
||||
"minimist": "^1.2.0",
|
||||
"typescript": "2.6.1",
|
||||
"vscode": "^1.0.1",
|
||||
"vscode": "^1.0.1",
|
||||
"xml2js": "^0.4.17"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "tsc",
|
||||
"watch": "tsc --watch",
|
||||
"postinstall": "npm run compile"
|
||||
"compile": "tsc -p tsconfig.build.json",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"postinstall": "npm run compile",
|
||||
"npmCheckJs": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ set -e
|
||||
# setup nvm
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
export NVM_DIR=~/.nvm
|
||||
source $(brew --prefix nvm)/nvm.sh
|
||||
source $(brew --prefix nvm)/nvm.sh --no-use
|
||||
else
|
||||
source $NVM_DIR/nvm.sh
|
||||
source $NVM_DIR/nvm.sh --no-use
|
||||
fi
|
||||
|
||||
# install node
|
||||
|
||||
@@ -70,6 +70,7 @@ interface Asset {
|
||||
hash: string;
|
||||
sha256hash: string;
|
||||
size: number;
|
||||
supportsFastUpdate?: boolean;
|
||||
}
|
||||
|
||||
function createOrUpdate(commit: string, quality: string, platform: string, type: string, release: NewDocument, asset: Asset, isUpdate: boolean): Promise<void> {
|
||||
@@ -203,17 +204,30 @@ async function publish(commit: string, quality: string, platform: string, type:
|
||||
// mooncake is fussy and far away, this is needed!
|
||||
mooncakeBlobService.defaultClientRequestTimeoutInMs = 10 * 60 * 1000;
|
||||
|
||||
await assertContainer(mooncakeBlobService, quality);
|
||||
await Promise.all([
|
||||
assertContainer(blobService, quality),
|
||||
assertContainer(mooncakeBlobService, quality)
|
||||
]);
|
||||
|
||||
const mooncakeBlobExists = await doesAssetExist(mooncakeBlobService, quality, blobName);
|
||||
const [blobExists, moooncakeBlobExists] = await Promise.all([
|
||||
doesAssetExist(blobService, quality, blobName),
|
||||
doesAssetExist(mooncakeBlobService, quality, blobName)
|
||||
]);
|
||||
|
||||
if (!mooncakeBlobExists) {
|
||||
const promises = [];
|
||||
|
||||
if (!blobExists) {
|
||||
promises.push(uploadBlob(blobService, quality, blobName, file));
|
||||
}
|
||||
|
||||
if (!moooncakeBlobExists) {
|
||||
promises.push(uploadBlob(mooncakeBlobService, quality, blobName, file));
|
||||
}
|
||||
} else {
|
||||
console.log('Skipping Mooncake publishing.');
|
||||
}
|
||||
|
||||
|
||||
if (promises.length === 0) {
|
||||
console.log(`Blob ${quality}, ${blobName} already exists, not publishing again.`);
|
||||
return;
|
||||
@@ -240,6 +254,13 @@ async function publish(commit: string, quality: string, platform: string, type:
|
||||
size
|
||||
};
|
||||
|
||||
// Remove this if we ever need to rollback fast updates for windows
|
||||
if (/win32/.test(platform)) {
|
||||
asset.supportsFastUpdate = true;
|
||||
}
|
||||
|
||||
console.log('Asset:', JSON.stringify(asset, null, ' '));
|
||||
|
||||
const release = {
|
||||
id: commit,
|
||||
timestamp: (new Date()).getTime(),
|
||||
|
||||
@@ -19,6 +19,9 @@ step "Install dependencies" \
|
||||
step "Hygiene" \
|
||||
npm run gulp -- hygiene
|
||||
|
||||
step "Monaco Editor Check" \
|
||||
./node_modules/.bin/tsc -p ./src/tsconfig.monaco.json --noEmit
|
||||
|
||||
step "Mix in repository from vscode-distro" \
|
||||
npm run gulp -- mixin
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ step "Install dependencies" \
|
||||
step "Hygiene" \
|
||||
npm run gulp -- hygiene
|
||||
|
||||
step "Monaco Editor Check" \
|
||||
./node_modules/.bin/tsc -p ./src/tsconfig.monaco.json --noEmit
|
||||
|
||||
step "Mix in repository from vscode-distro" \
|
||||
npm run gulp -- mixin
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ step "Hygiene" {
|
||||
exec { & npm run gulp -- hygiene }
|
||||
}
|
||||
|
||||
step "Monaco Editor Check" {
|
||||
exec { & .\node_modules\.bin\tsc -p .\src\tsconfig.monaco.json --noEmit }
|
||||
}
|
||||
|
||||
$env:VSCODE_MIXIN_PASSWORD = $mixinPassword
|
||||
step "Mix in repository from vscode-distro" {
|
||||
exec { & npm run gulp -- mixin }
|
||||
@@ -41,6 +45,10 @@ step "Build minified" {
|
||||
exec { & npm run gulp -- "vscode-win32-$global:arch-min" }
|
||||
}
|
||||
|
||||
step "Copy Inno updater" {
|
||||
exec { & npm run gulp -- "vscode-win32-$global:arch-copy-inno-updater" }
|
||||
}
|
||||
|
||||
# step "Create loader snapshot" {
|
||||
# exec { & node build\lib\snapshotLoader.js --arch=$global:arch }
|
||||
# }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# install node
|
||||
$env:Path = $env:NVM_HOME + ";" + $env:NVM_SYMLINK + ";" + $env:Path
|
||||
$NodeVersion = "8.9.1"
|
||||
nvm install $NodeVersion
|
||||
nvm use $NodeVersion
|
||||
npm install -g yarn
|
||||
# nvm install $NodeVersion
|
||||
# nvm use $NodeVersion
|
||||
# npm install -g yarn
|
||||
$env:Path = $env:NVM_HOME + "\v" + $NodeVersion + ";" + $env:Path
|
||||
7
build/tsconfig.build.json
Normal file
7
build/tsconfig.build.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": false,
|
||||
"checkJs": false
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,12 @@
|
||||
"preserveConstEnums": true,
|
||||
"sourceMap": false,
|
||||
"experimentalDecorators": true,
|
||||
"newLine": "LF"
|
||||
"newLine": "LF",
|
||||
// enable JavaScript type checking for the language service
|
||||
// use the tsconfig.build.json for compiling wich disable JavaScript
|
||||
// type checking so that JavaScript file are not transpiled
|
||||
"allowJs": true,
|
||||
"checkJs": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules/**"
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
"always"
|
||||
],
|
||||
"triple-equals": true
|
||||
}
|
||||
},
|
||||
"defaultSeverity": "warning"
|
||||
}
|
||||
1794
build/win32/OSSREADME.json
Normal file
1794
build/win32/OSSREADME.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@ OutputDir={#OutputDir}
|
||||
OutputBaseFilename=SqlOpsStudioSetup
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
AppMutex={#AppMutex}
|
||||
AppMutex={code:GetAppMutex}
|
||||
SetupMutex={#AppMutex}setup
|
||||
WizardImageFile={#RepoDir}\resources\win32\inno-big.bmp
|
||||
WizardSmallImageFile={#RepoDir}\resources\win32\inno-small.bmp
|
||||
@@ -52,8 +52,13 @@ Type: filesandordirs; Name: "{app}\resources\app\out"; Check: IsNotUpdate
|
||||
Type: filesandordirs; Name: "{app}\resources\app\plugins"; Check: IsNotUpdate
|
||||
Type: filesandordirs; Name: "{app}\resources\app\extensions"; Check: IsNotUpdate
|
||||
Type: filesandordirs; Name: "{app}\resources\app\node_modules"; Check: IsNotUpdate
|
||||
Type: filesandordirs; Name: "{app}\resources\app\node_modules.asar.unpacked"; Check: IsNotUpdate
|
||||
Type: files; Name: "{app}\resources\app\node_modules.asar"; Check: IsNotUpdate
|
||||
Type: files; Name: "{app}\resources\app\Credits_45.0.2454.85.html"; Check: IsNotUpdate
|
||||
|
||||
[UninstallDelete]
|
||||
Type: filesandordirs; Name: "{app}\_"
|
||||
|
||||
[Tasks]
|
||||
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
|
||||
Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}"
|
||||
@@ -138,6 +143,48 @@ begin
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
function GetAppMutex(Value: string): string;
|
||||
begin
|
||||
if IsBackgroundUpdate() then
|
||||
Result := ''
|
||||
else
|
||||
Result := '{#AppMutex}';
|
||||
end;
|
||||
|
||||
function GetDestDir(Value: string): string;
|
||||
begin
|
||||
if IsBackgroundUpdate() then
|
||||
Result := ExpandConstant('{app}\_')
|
||||
else
|
||||
Result := ExpandConstant('{app}');
|
||||
end;
|
||||
|
||||
function BoolToStr(Value: Boolean): String;
|
||||
begin
|
||||
if Value then
|
||||
Result := 'true'
|
||||
else
|
||||
Result := 'false';
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
var
|
||||
UpdateResultCode: Integer;
|
||||
begin
|
||||
if IsBackgroundUpdate() and (CurStep = ssPostInstall) then
|
||||
begin
|
||||
CreateMutex('{#AppMutex}-ready');
|
||||
|
||||
while (CheckForMutexes('{#AppMutex}')) do
|
||||
begin
|
||||
Log('Application is still running, waiting');
|
||||
Sleep(1000);
|
||||
end;
|
||||
|
||||
Exec(ExpandConstant('{app}\tools\inno_updater.exe'), ExpandConstant('"{app}\{#ExeBasename}.exe" ' + BoolToStr(LockFileExists())), '', SW_SHOW, ewWaitUntilTerminated, UpdateResultCode);
|
||||
end;
|
||||
end;
|
||||
|
||||
// http://stackoverflow.com/a/23838239/261019
|
||||
procedure Explode(var Dest: TArrayOfString; Text: String; Separator: String);
|
||||
var
|
||||
|
||||
BIN
build/win32/inno_updater.exe
Normal file
BIN
build/win32/inno_updater.exe
Normal file
Binary file not shown.
BIN
build/win32/vcruntime140.dll
Normal file
BIN
build/win32/vcruntime140.dll
Normal file
Binary file not shown.
4339
build/yarn.lock
4339
build/yarn.lock
File diff suppressed because it is too large
Load Diff
3
extensions-modules/.gitignore
vendored
3
extensions-modules/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
node_modules
|
||||
lib
|
||||
*.log
|
||||
@@ -1,9 +0,0 @@
|
||||
.vscode/
|
||||
lib/test/
|
||||
lib/**/*.map
|
||||
src/
|
||||
test/
|
||||
.eslintrc
|
||||
.gitignore
|
||||
gulpfile.js
|
||||
tsd.json
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"name": "extensions-modules",
|
||||
"version": "0.1.0",
|
||||
"description": "Shared modules for SQL Operations Studio extensions",
|
||||
"dependencies": {
|
||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.5",
|
||||
"decompress": "^4.2.0",
|
||||
"fs-extra-promise": "^1.0.1",
|
||||
"http-proxy-agent": "^2.0.0",
|
||||
"https-proxy-agent": "^2.1.0",
|
||||
"opener": "^1.4.3",
|
||||
"tmp": "0.0.33",
|
||||
"vscode-extension-telemetry": "0.0.8",
|
||||
"vscode-languageclient": "3.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^6.0.61",
|
||||
"vscode": "^1.1.10",
|
||||
"sqlops": "github:anthonydresser/vscode-extension-vscode"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "node ./node_modules/vscode/bin/install && node ./node_modules/sqlops/bin/install && tsc -p ./src",
|
||||
"compile": "tsc -p ./src",
|
||||
"watch": "tsc -w -p ./src"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "*",
|
||||
"sqlops": "*"
|
||||
},
|
||||
"main": "./lib/main.js",
|
||||
"typings": "./lib/main"
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
const fs = require('fs');
|
||||
import * as path from 'path';
|
||||
import { IConfig } from '../languageservice/interfaces';
|
||||
import { Constants } from '../models/constants';
|
||||
|
||||
/*
|
||||
* Config class handles getting values from config.json.
|
||||
*/
|
||||
export default class Config implements IConfig {
|
||||
private _configJsonContent: any = undefined;
|
||||
|
||||
private _extensionConfigSectionName: string = undefined;
|
||||
private _fromBuild: boolean = undefined;
|
||||
|
||||
constructor(extensionConfigSectionName: string, private path: string, fromBuild?: boolean) {
|
||||
this._extensionConfigSectionName = extensionConfigSectionName;
|
||||
this._fromBuild = fromBuild;
|
||||
}
|
||||
|
||||
public get configJsonContent(): any {
|
||||
if (this._configJsonContent === undefined) {
|
||||
this._configJsonContent = this.loadConfig();
|
||||
}
|
||||
return this._configJsonContent;
|
||||
}
|
||||
|
||||
public getDownloadUrl(): string {
|
||||
return this.getConfigValue(Constants.downloadUrlConfigKey);
|
||||
}
|
||||
|
||||
public getInstallDirectory(): string {
|
||||
return this.getConfigValue(Constants.installDirConfigKey);
|
||||
}
|
||||
|
||||
public getExecutableFiles(): string[] {
|
||||
return this.getConfigValue(Constants.executableFilesConfigKey);
|
||||
}
|
||||
|
||||
public getPackageVersion(): string {
|
||||
return this.getConfigValue(Constants.versionConfigKey);
|
||||
}
|
||||
|
||||
public getConfigValue(configKey: string): any {
|
||||
let json = this.configJsonContent;
|
||||
let toolsConfig = json[Constants.serviceConfigKey];
|
||||
let configValue: string = undefined;
|
||||
if (toolsConfig !== undefined) {
|
||||
configValue = toolsConfig[configKey];
|
||||
}
|
||||
return configValue;
|
||||
}
|
||||
|
||||
public getExtensionConfig(key: string, defaultValue?: any): any {
|
||||
let json = this.configJsonContent;
|
||||
let extensionConfig = json[this._extensionConfigSectionName];
|
||||
let configValue = extensionConfig[key];
|
||||
if (!configValue) {
|
||||
configValue = defaultValue;
|
||||
}
|
||||
return configValue;
|
||||
}
|
||||
|
||||
public getWorkspaceConfig(key: string, defaultValue?: any): any {
|
||||
let json = this.configJsonContent;
|
||||
let configValue = json[key];
|
||||
if (!configValue) {
|
||||
configValue = defaultValue;
|
||||
}
|
||||
return configValue;
|
||||
}
|
||||
|
||||
public loadConfig(): any {
|
||||
let configContent = undefined;
|
||||
if (this._fromBuild) {
|
||||
let remainingPath = '../../../extensions/' + this._extensionConfigSectionName + '/client/out/config.json';
|
||||
configContent = fs.readFileSync(path.join(__dirname, remainingPath));
|
||||
}
|
||||
else {
|
||||
configContent = fs.readFileSync(this.path);
|
||||
}
|
||||
return JSON.parse(configContent);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 Config from './config';
|
||||
import { workspace, WorkspaceConfiguration } from 'vscode';
|
||||
import { IConfig } from '../languageservice/interfaces';
|
||||
import { Constants } from '../models/constants';
|
||||
|
||||
/*
|
||||
* ExtConfig class handles getting values from workspace config or config.json.
|
||||
*/
|
||||
export default class ExtConfig implements IConfig {
|
||||
|
||||
constructor(private _extensionConfigSectionName: string, private _config?: IConfig,
|
||||
path?: string,
|
||||
private _extensionConfig?: WorkspaceConfiguration,
|
||||
private _workspaceConfig?: WorkspaceConfiguration) {
|
||||
if (this._config === undefined) {
|
||||
this._config = new Config(_extensionConfigSectionName, path);
|
||||
}
|
||||
if (this._extensionConfig === undefined) {
|
||||
this._extensionConfig = workspace.getConfiguration(_extensionConfigSectionName);
|
||||
}
|
||||
if (this._workspaceConfig === undefined) {
|
||||
this._workspaceConfig = workspace.getConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
public getDownloadUrl(): string {
|
||||
return this.getConfigValue(Constants.downloadUrlConfigKey);
|
||||
}
|
||||
|
||||
public getInstallDirectory(): string {
|
||||
return this.getConfigValue(Constants.installDirConfigKey);
|
||||
}
|
||||
|
||||
public getExecutableFiles(): string[] {
|
||||
return this.getConfigValue(Constants.executableFilesConfigKey);
|
||||
}
|
||||
|
||||
public getPackageVersion(): string {
|
||||
return this.getConfigValue(Constants.versionConfigKey);
|
||||
}
|
||||
|
||||
public getConfigValue(configKey: string): any {
|
||||
let configValue: string = <string>this.getExtensionConfig(`${Constants.serviceConfigKey}.${configKey}`);
|
||||
if (!configValue) {
|
||||
configValue = this._config.getConfigValue(configKey);
|
||||
}
|
||||
return configValue;
|
||||
}
|
||||
|
||||
public getExtensionConfig(key: string, defaultValue?: any): any {
|
||||
let configValue = this._extensionConfig.get(key);
|
||||
if (configValue === undefined) {
|
||||
configValue = defaultValue;
|
||||
}
|
||||
return configValue;
|
||||
}
|
||||
|
||||
public getWorkspaceConfig(key: string, defaultValue?: any): any {
|
||||
let configValue = this._workspaceConfig.get(key);
|
||||
if (configValue === undefined) {
|
||||
configValue = defaultValue;
|
||||
}
|
||||
return configValue;
|
||||
}
|
||||
|
||||
public updateWorkspaceConfig(configKey: string, configValue: any) {
|
||||
this._workspaceConfig.update(configKey, configValue, true);
|
||||
}
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import vscode = require('vscode');
|
||||
import { IExtensionConstants } from '../models/contracts/contracts';
|
||||
|
||||
export class VscodeWrapper {
|
||||
private _extensionConstants: IExtensionConstants;
|
||||
/**
|
||||
* Output channel for logging. Shared among all instances.
|
||||
*/
|
||||
private static _outputChannel: vscode.OutputChannel;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public constructor(constants: IExtensionConstants) {
|
||||
this._extensionConstants = constants;
|
||||
if (typeof VscodeWrapper._outputChannel === 'undefined') {
|
||||
VscodeWrapper._outputChannel = this.createOutputChannel(this._extensionConstants.outputChannelName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current active text editor
|
||||
*/
|
||||
public get activeTextEditor(): vscode.TextEditor {
|
||||
return vscode.window.activeTextEditor;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the current textDocument; any that are open?
|
||||
*/
|
||||
public get textDocuments(): vscode.TextDocument[] {
|
||||
return vscode.workspace.textDocuments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse uri
|
||||
*/
|
||||
public parseUri(uri: string): vscode.Uri {
|
||||
return vscode.Uri.parse(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URI string for the current active text editor
|
||||
*/
|
||||
public get activeTextEditorUri(): string {
|
||||
if (typeof vscode.window.activeTextEditor !== 'undefined' &&
|
||||
typeof vscode.window.activeTextEditor.document !== 'undefined') {
|
||||
return vscode.window.activeTextEditor.document.uri.toString();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public get constants(): IExtensionConstants {
|
||||
return this._extensionConstants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an output channel in vscode.
|
||||
*/
|
||||
public createOutputChannel(channelName: string): vscode.OutputChannel {
|
||||
return vscode.window.createOutputChannel(channelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the command denoted by the given command identifier.
|
||||
*
|
||||
* When executing an editor command not all types are allowed to
|
||||
* be passed as arguments. Allowed are the primitive types `string`, `boolean`,
|
||||
* `number`, `undefined`, and `null`, as well as classes defined in this API.
|
||||
* There are no restrictions when executing commands that have been contributed
|
||||
* by extensions.
|
||||
*
|
||||
* @param command Identifier of the command to execute.
|
||||
* @param rest Parameters passed to the command function.
|
||||
* @return A thenable that resolves to the returned value of the given command. `undefined` when
|
||||
* the command handler function doesn't return anything.
|
||||
* @see vscode.commands.executeCommand
|
||||
*/
|
||||
public executeCommand<T>(command: string, ...rest: any[]): Thenable<T> {
|
||||
return vscode.commands.executeCommand<T>(command, ...rest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration for a extensionName; NOT YET IMPLEMENTED
|
||||
* @param extensionName The string name of the extension to get the configuration for
|
||||
*/
|
||||
public getConfiguration(extensionName: string): vscode.WorkspaceConfiguration {
|
||||
return vscode.workspace.getConfiguration(extensionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 'true' if the active editor window has a .sql file, false otherwise
|
||||
*/
|
||||
public get isEditingSqlFile(): boolean {
|
||||
let sqlFile = false;
|
||||
let editor = this.activeTextEditor;
|
||||
if (editor) {
|
||||
if (editor.document.languageId === this._extensionConstants.languageId) {
|
||||
sqlFile = true;
|
||||
}
|
||||
}
|
||||
return sqlFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* An event that is emitted when a [text document](#TextDocument) is disposed.
|
||||
*/
|
||||
public get onDidCloseTextDocument(): vscode.Event<vscode.TextDocument> {
|
||||
return vscode.workspace.onDidCloseTextDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* An event that is emitted when a [text document](#TextDocument) is opened.
|
||||
*/
|
||||
public get onDidOpenTextDocument(): vscode.Event<vscode.TextDocument> {
|
||||
return vscode.workspace.onDidOpenTextDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* An event that is emitted when a [text document](#TextDocument) is saved to disk.
|
||||
*/
|
||||
public get onDidSaveTextDocument(): vscode.Event<vscode.TextDocument> {
|
||||
return vscode.workspace.onDidSaveTextDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the denoted document from disk. Will return early if the
|
||||
* document is already open, otherwise the document is loaded and the
|
||||
* [open document](#workspace.onDidOpenTextDocument)-event fires.
|
||||
* The document to open is denoted by the [uri](#Uri). Two schemes are supported:
|
||||
*
|
||||
* file: A file on disk, will be rejected if the file does not exist or cannot be loaded, e.g. `file:///Users/frodo/r.ini`.
|
||||
* untitled: A new file that should be saved on disk, e.g. `untitled:c:\frodo\new.js`. The language will be derived from the file name.
|
||||
*
|
||||
* Uris with other schemes will make this method return a rejected promise.
|
||||
*
|
||||
* @param uri Identifies the resource to open.
|
||||
* @return A promise that resolves to a [document](#TextDocument).
|
||||
* @see vscode.workspace.openTextDocument
|
||||
*/
|
||||
public openTextDocument(uri: vscode.Uri): Thenable<vscode.TextDocument> {
|
||||
return vscode.workspace.openTextDocument(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to log messages to output channel.
|
||||
*/
|
||||
public logToOutputChannel(msg: any): void {
|
||||
let date: Date = new Date();
|
||||
if (msg instanceof Array) {
|
||||
msg.forEach(element => {
|
||||
VscodeWrapper._outputChannel.appendLine('[' + date.toLocaleTimeString() + '] ' + element.toString());
|
||||
});
|
||||
} else {
|
||||
VscodeWrapper._outputChannel.appendLine('[' + date.toLocaleTimeString() + '] ' + msg.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a vscode.Range object
|
||||
* @param start The start position for the range
|
||||
* @param end The end position for the range
|
||||
*/
|
||||
public range(start: vscode.Position, end: vscode.Position): vscode.Range {
|
||||
return new vscode.Range(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a vscode.Position object
|
||||
* @param line The line for the position
|
||||
* @param column The column for the position
|
||||
*/
|
||||
public position(line: number, column: number): vscode.Position {
|
||||
return new vscode.Position(line, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a vscode.Selection object
|
||||
* @param start The start postion of the selection
|
||||
* @param end The end position of the selection
|
||||
*/
|
||||
public selection(start: vscode.Position, end: vscode.Position): vscode.Selection {
|
||||
return new vscode.Selection(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats and shows a vscode error message
|
||||
*/
|
||||
public showErrorMessage(msg: string, ...items: string[]): Thenable<string> {
|
||||
return vscode.window.showErrorMessage(this._extensionConstants.extensionName + ': ' + msg, ...items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats and shows a vscode information message
|
||||
*/
|
||||
public showInformationMessage(msg: string, ...items: string[]): Thenable<string> {
|
||||
return vscode.window.showInformationMessage(this._extensionConstants.extensionName + ': ' + msg, ...items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a selection list.
|
||||
*
|
||||
* @param items An array of items, or a promise that resolves to an array of items.
|
||||
* @param options Configures the behavior of the selection list.
|
||||
* @return A promise that resolves to the selected item or undefined.
|
||||
*/
|
||||
public showQuickPick<T extends vscode.QuickPickItem>(items: T[] | Thenable<T[]>, options?: vscode.QuickPickOptions): Thenable<T> {
|
||||
return vscode.window.showQuickPick<T>(items, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the given document in a text editor. A [column](#ViewColumn) can be provided
|
||||
* to control where the editor is being shown. Might change the [active editor](#window.activeTextEditor).
|
||||
*
|
||||
* @param document A text document to be shown.
|
||||
* @param column A view column in which the editor should be shown. The default is the [one](#ViewColumn.One), other values
|
||||
* are adjusted to be __Min(column, columnCount + 1)__.
|
||||
* @param preserveFocus When `true` the editor will not take focus.
|
||||
* @return A promise that resolves to an [editor](#TextEditor).
|
||||
*/
|
||||
public showTextDocument(document: vscode.TextDocument, column?: vscode.ViewColumn, preserveFocus?: boolean): Thenable<vscode.TextEditor> {
|
||||
return vscode.window.showTextDocument(document, column, preserveFocus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats and shows a vscode warning message
|
||||
*/
|
||||
public showWarningMessage(msg: string): Thenable<string> {
|
||||
return vscode.window.showWarningMessage(this._extensionConstants.extensionName + ': ' + msg );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a array of the text editors currently visible in the window
|
||||
*/
|
||||
public get visibleEditors(): vscode.TextEditor[] {
|
||||
return vscode.window.visibleTextEditors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an URI from a file system path. The [scheme](#Uri.scheme)
|
||||
* will be `file`.
|
||||
*
|
||||
* @param path A file system or UNC path.
|
||||
* @return A new Uri instance.
|
||||
* @see vscode.Uri.file
|
||||
*/
|
||||
public uriFile(path: string): vscode.Uri {
|
||||
return vscode.Uri.file(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an URI from a string. Will throw if the given value is not
|
||||
* valid.
|
||||
*
|
||||
* @param value The string value of an Uri.
|
||||
* @return A new Uri instance.
|
||||
* @see vscode.Uri.parse
|
||||
*/
|
||||
public uriParse(value: string): vscode.Uri {
|
||||
return vscode.Uri.parse(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The folder that is open in VS Code. `undefined` when no folder
|
||||
* has been opened.
|
||||
*
|
||||
* @readonly
|
||||
* @see vscode.workspace.rootPath
|
||||
*/
|
||||
public get workspaceRootPath(): string {
|
||||
return vscode.workspace.rootPath;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IDecompressProvider, IPackage } from './interfaces';
|
||||
import { ILogger } from '../models/interfaces';
|
||||
const decompress = require('decompress');
|
||||
|
||||
export default class DecompressProvider implements IDecompressProvider {
|
||||
public decompress(pkg: IPackage, logger: ILogger): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
decompress(pkg.tmpFile.name, pkg.installPath).then(files => {
|
||||
logger.appendLine(`Done! ${files.length} files unpacked.\n`);
|
||||
resolve();
|
||||
}).catch(decompressErr => {
|
||||
logger.appendLine(`[ERROR] ${decompressErr}`);
|
||||
reject(decompressErr);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IPackage, IStatusView, PackageError, IHttpClient } from './interfaces';
|
||||
import { ILogger } from '../models/interfaces';
|
||||
import { parse as parseUrl, Url } from 'url';
|
||||
import * as https from 'https';
|
||||
import * as http from 'http';
|
||||
import { getProxyAgent } from './proxy';
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
/*
|
||||
* Http client class to handle downloading files using http or https urls
|
||||
*/
|
||||
export default class HttpClient implements IHttpClient {
|
||||
|
||||
/*
|
||||
* Downloads a file and stores the result in the temp file inside the package object
|
||||
*/
|
||||
public downloadFile(urlString: string, pkg: IPackage, logger: ILogger, statusView: IStatusView, proxy?: string, strictSSL?: boolean): Promise<void> {
|
||||
const url = parseUrl(urlString);
|
||||
let options = this.getHttpClientOptions(url, proxy, strictSSL);
|
||||
let clientRequest = url.protocol === 'http:' ? http.request : https.request;
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (!pkg.tmpFile || pkg.tmpFile.fd === 0) {
|
||||
return reject(new PackageError('Temporary package file unavailable', pkg));
|
||||
}
|
||||
|
||||
let request = clientRequest(options, response => {
|
||||
if (response.statusCode === 301 || response.statusCode === 302) {
|
||||
// Redirect - download from new location
|
||||
return resolve(this.downloadFile(response.headers.location, pkg, logger, statusView, proxy, strictSSL));
|
||||
}
|
||||
|
||||
if (response.statusCode !== 200) {
|
||||
// Download failed - print error message
|
||||
logger.appendLine(`failed (error code '${response.statusCode}')`);
|
||||
return reject(new PackageError(response.statusCode.toString(), pkg));
|
||||
}
|
||||
|
||||
// If status code is 200
|
||||
this.handleSuccessfulResponse(pkg, response, logger, statusView).then(_ => {
|
||||
resolve();
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
request.on('error', error => {
|
||||
// reject(new PackageError(`Request error: ${error.code || 'NONE'}`, pkg, error));
|
||||
reject(new PackageError(`Request error: ${error.name || 'NONE'}`, pkg, error));
|
||||
});
|
||||
|
||||
// Execute the request
|
||||
request.end();
|
||||
});
|
||||
}
|
||||
|
||||
private getHttpClientOptions(url: Url, proxy?: string, strictSSL?: boolean): any {
|
||||
const agent = getProxyAgent(url, proxy, strictSSL);
|
||||
|
||||
let options: http.RequestOptions = {
|
||||
host: url.hostname,
|
||||
path: url.path,
|
||||
agent: agent,
|
||||
port: +url.port
|
||||
};
|
||||
|
||||
if (url.protocol === 'https:') {
|
||||
let httpsOptions: https.RequestOptions = {
|
||||
host: url.hostname,
|
||||
path: url.path,
|
||||
agent: agent,
|
||||
port: +url.port
|
||||
};
|
||||
options = httpsOptions;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the download percentage and stores in the progress object
|
||||
*/
|
||||
public handleDataReceivedEvent(progress: IDownloadProgress, data: any, logger: ILogger, statusView: IStatusView): void {
|
||||
progress.downloadedBytes += data.length;
|
||||
|
||||
// Update status bar item with percentage
|
||||
if (progress.packageSize > 0) {
|
||||
let newPercentage = Math.ceil(100 * (progress.downloadedBytes / progress.packageSize));
|
||||
if (newPercentage !== progress.downloadPercentage) {
|
||||
statusView.updateServiceDownloadingProgress(progress.downloadPercentage);
|
||||
progress.downloadPercentage = newPercentage;
|
||||
}
|
||||
|
||||
// Update dots after package name in output console
|
||||
let newDots = Math.ceil(progress.downloadPercentage / 5);
|
||||
if (newDots > progress.dots) {
|
||||
logger.append('.'.repeat(newDots - progress.dots));
|
||||
progress.dots = newDots;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private handleSuccessfulResponse(pkg: IPackage, response: http.IncomingMessage, logger: ILogger, statusView: IStatusView): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
let progress: IDownloadProgress = {
|
||||
packageSize: parseInt(response.headers['content-length'], 10),
|
||||
dots: 0,
|
||||
downloadedBytes: 0,
|
||||
downloadPercentage: 0
|
||||
};
|
||||
logger.append(`(${Math.ceil(progress.packageSize / 1024)} KB) `);
|
||||
response.on('data', data => {
|
||||
this.handleDataReceivedEvent(progress, data, logger, statusView);
|
||||
});
|
||||
let tmpFile = fs.createWriteStream(undefined, { fd: pkg.tmpFile.fd });
|
||||
response.on('end', () => {
|
||||
resolve();
|
||||
});
|
||||
|
||||
response.on('error', err => {
|
||||
reject(new PackageError(`Response error: ${err.name || 'NONE'}`, pkg, err));
|
||||
});
|
||||
|
||||
// Begin piping data from the response to the package file
|
||||
response.pipe(tmpFile, { end: false });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Interface to store the values needed to calculate download percentage
|
||||
*/
|
||||
export interface IDownloadProgress {
|
||||
packageSize: number;
|
||||
downloadedBytes: number;
|
||||
downloadPercentage: number;
|
||||
dots: number;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as tmp from 'tmp';
|
||||
import { ILogger } from '../models/interfaces';
|
||||
|
||||
export interface IStatusView {
|
||||
installingService(): void;
|
||||
serviceInstalled(): void;
|
||||
serviceInstallationFailed(): void;
|
||||
updateServiceDownloadingProgress(downloadPercentage: number): void;
|
||||
}
|
||||
|
||||
export interface IConfig {
|
||||
getDownloadUrl(): string;
|
||||
getInstallDirectory(): string;
|
||||
getExecutableFiles(): string[];
|
||||
getPackageVersion(): string;
|
||||
getExtensionConfig(key: string, defaultValue?: any): any;
|
||||
getWorkspaceConfig(key: string, defaultValue?: any): any;
|
||||
getConfigValue(configKey: string): any;
|
||||
}
|
||||
|
||||
export interface IPackage {
|
||||
url: string;
|
||||
installPath?: string;
|
||||
tmpFile: tmp.SynchronousResult;
|
||||
}
|
||||
|
||||
export class PackageError extends Error {
|
||||
// Do not put PII (personally identifiable information) in the 'message' field as it will be logged to telemetry
|
||||
constructor(public message: string,
|
||||
public pkg: IPackage = undefined,
|
||||
public innerError: any = undefined) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IHttpClient {
|
||||
downloadFile(urlString: string, pkg: IPackage, logger: ILogger, statusView: IStatusView, proxy: string, strictSSL: boolean): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IDecompressProvider {
|
||||
decompress(pkg: IPackage, logger: ILogger): Promise<void>;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { Url, parse as parseUrl } from 'url';
|
||||
const HttpProxyAgent = require('http-proxy-agent');
|
||||
const HttpsProxyAgent = require('https-proxy-agent');
|
||||
|
||||
function getSystemProxyURL(requestURL: Url): string {
|
||||
if (requestURL.protocol === 'http:') {
|
||||
return process.env.HTTP_PROXY || process.env.http_proxy || undefined;
|
||||
} else if (requestURL.protocol === 'https:') {
|
||||
return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || undefined;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the proxy agent using the proxy url in the parameters or the system proxy. Returns null if no proxy found
|
||||
*/
|
||||
export function getProxyAgent(requestURL: Url, proxy?: string, strictSSL?: boolean): any {
|
||||
const proxyURL = proxy || getSystemProxyURL(requestURL);
|
||||
|
||||
if (!proxyURL) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const proxyEndpoint = parseUrl(proxyURL);
|
||||
|
||||
if (!/^https?:$/.test(proxyEndpoint.protocol)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
strictSSL = strictSSL || true;
|
||||
|
||||
const opts = {
|
||||
host: proxyEndpoint.hostname,
|
||||
port: Number(proxyEndpoint.port),
|
||||
auth: proxyEndpoint.auth,
|
||||
rejectUnauthorized: strictSSL
|
||||
};
|
||||
|
||||
return requestURL.protocol === 'http:' ? new HttpProxyAgent(opts) : new HttpsProxyAgent(opts);
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as path from 'path';
|
||||
import { Runtime } from '../models/platform';
|
||||
import ServiceDownloadProvider from './serviceDownloadProvider';
|
||||
import { IConfig, IStatusView } from './interfaces';
|
||||
const fs = require('fs-extra-promise');
|
||||
|
||||
|
||||
/*
|
||||
* Service Provider class finds the SQL tools service executable file or downloads it if doesn't exist.
|
||||
*/
|
||||
export default class ServerProvider {
|
||||
|
||||
constructor(private _downloadProvider: ServiceDownloadProvider,
|
||||
private _config: IConfig,
|
||||
private _statusView: IStatusView,
|
||||
private _extensionConfigSectionName: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Public get method for downloadProvider
|
||||
*/
|
||||
public get downloadProvider(): ServiceDownloadProvider {
|
||||
return this._downloadProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a file path, returns the path to the SQL Tools service file.
|
||||
*/
|
||||
public findServerPath(filePath: string, executableFiles: string[] = undefined): Promise<string> {
|
||||
return fs.lstatAsync(filePath).then(stats => {
|
||||
// If a file path was passed, assume its the launch file.
|
||||
if (stats.isFile()) {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
// Otherwise, search the specified folder.
|
||||
let candidate: string;
|
||||
|
||||
if (executableFiles === undefined && this._config !== undefined) {
|
||||
executableFiles = this._config.getExecutableFiles();
|
||||
}
|
||||
if (executableFiles !== undefined) {
|
||||
executableFiles.forEach(element => {
|
||||
let executableFile = path.join(filePath, element);
|
||||
if (candidate === undefined && fs.existsSync(executableFile)) {
|
||||
candidate = executableFile;
|
||||
return candidate;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return candidate;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the service if doesn't exist and returns the file path.
|
||||
*/
|
||||
public getOrDownloadServer(runtime: Runtime): Promise<string> {
|
||||
// Attempt to find launch file path first from options, and then from the default install location.
|
||||
// If SQL tools service can't be found, download it.
|
||||
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
return this.getServerPath(runtime).then(result => {
|
||||
if (result === undefined) {
|
||||
return this.downloadServerFiles(runtime).then ( downloadResult => {
|
||||
resolve(downloadResult);
|
||||
});
|
||||
} else {
|
||||
return resolve(result);
|
||||
}
|
||||
}).catch(err => {
|
||||
return reject(err);
|
||||
});
|
||||
}).catch(err => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of the installed service
|
||||
*/
|
||||
public getServerPath(runtime: Runtime): Promise<string> {
|
||||
const installDirectory = this._downloadProvider.getInstallDirectory(runtime, this._extensionConfigSectionName);
|
||||
return this.findServerPath(installDirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the service and returns the path of the installed service
|
||||
*/
|
||||
public downloadServerFiles(runtime: Runtime): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const installDirectory = this._downloadProvider.getInstallDirectory(runtime, this._extensionConfigSectionName);
|
||||
return this._downloadProvider.installService(runtime).then( _ => {
|
||||
return this.findServerPath(installDirectory).then ( result => {
|
||||
return resolve(result);
|
||||
});
|
||||
}).catch(err => {
|
||||
this._statusView.serviceInstallationFailed();
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IStatusView } from './interfaces';
|
||||
import vscode = require('vscode');
|
||||
import { IExtensionConstants } from '../models/contracts/contracts';
|
||||
import { Constants } from '../models/constants';
|
||||
|
||||
/*
|
||||
* The status class which includes the service initialization result.
|
||||
*/
|
||||
export class ServerInitializationResult {
|
||||
|
||||
public constructor(
|
||||
public installedBeforeInitializing: Boolean = false,
|
||||
public isRunning: Boolean = false,
|
||||
public serverPath: string = undefined
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
public Clone(): ServerInitializationResult {
|
||||
return new ServerInitializationResult(this.installedBeforeInitializing, this.isRunning, this.serverPath);
|
||||
}
|
||||
|
||||
public WithRunning(isRunning: Boolean): ServerInitializationResult {
|
||||
return new ServerInitializationResult(this.installedBeforeInitializing, isRunning, this.serverPath);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The status class shows service installing progress in UI
|
||||
*/
|
||||
export class ServerStatusView implements IStatusView, vscode.Disposable {
|
||||
private _numberOfSecondsBeforeHidingMessage = 5000;
|
||||
private _statusBarItem: vscode.StatusBarItem = undefined;
|
||||
private _progressTimerId: NodeJS.Timer;
|
||||
private _constants: IExtensionConstants;
|
||||
|
||||
constructor(constants: IExtensionConstants) {
|
||||
this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
|
||||
vscode.window.onDidChangeActiveTextEditor((params) => this.onDidChangeActiveTextEditor(params));
|
||||
vscode.workspace.onDidCloseTextDocument((params) => this.onDidCloseTextDocument(params));
|
||||
this._constants = constants;
|
||||
}
|
||||
|
||||
public installingService(): void {
|
||||
this._statusBarItem.command = undefined;
|
||||
this._statusBarItem.show();
|
||||
|
||||
this.showProgress('$(desktop-download) ' + Constants.serviceInstalling);
|
||||
}
|
||||
|
||||
public updateServiceDownloadingProgress(downloadPercentage: number): void {
|
||||
this._statusBarItem.text = '$(cloud-download) ' + `${Constants.serviceDownloading} ... ${downloadPercentage}%`;
|
||||
this._statusBarItem.show();
|
||||
}
|
||||
|
||||
public serviceInstalled(): void {
|
||||
|
||||
this._statusBarItem.command = undefined;
|
||||
this._statusBarItem.text = this._constants.serviceInstalled;
|
||||
this._statusBarItem.show();
|
||||
// Cleat the status bar after 2 seconds
|
||||
setTimeout(() => {
|
||||
this._statusBarItem.hide();
|
||||
}, this._numberOfSecondsBeforeHidingMessage);
|
||||
}
|
||||
|
||||
public serviceInstallationFailed(): void {
|
||||
this._statusBarItem.command = undefined;
|
||||
this._statusBarItem.text = this._constants.serviceInstallationFailed;
|
||||
this._statusBarItem.show();
|
||||
}
|
||||
|
||||
private showProgress(statusText: string): void {
|
||||
let index = 0;
|
||||
let progressTicks = [ '|', '/', '-', '\\'];
|
||||
|
||||
|
||||
this._progressTimerId = setInterval(() => {
|
||||
index++;
|
||||
if (index > 3) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
let progressTick = progressTicks[index];
|
||||
if (this._statusBarItem.text !== this._constants.serviceInstalled) {
|
||||
this._statusBarItem.text = statusText + ' ' + progressTick;
|
||||
this._statusBarItem.show();
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.destroyStatusBar();
|
||||
}
|
||||
|
||||
private hideLastShownStatusBar(): void {
|
||||
if (typeof this._statusBarItem !== 'undefined') {
|
||||
this._statusBarItem.hide();
|
||||
}
|
||||
}
|
||||
|
||||
private onDidChangeActiveTextEditor(editor: vscode.TextEditor): void {
|
||||
// Hide the most recently shown status bar
|
||||
this.hideLastShownStatusBar();
|
||||
}
|
||||
|
||||
private onDidCloseTextDocument(doc: vscode.TextDocument): void {
|
||||
// Remove the status bar associated with the document
|
||||
this.destroyStatusBar();
|
||||
}
|
||||
|
||||
private destroyStatusBar(): void {
|
||||
if (typeof this._statusBarItem !== 'undefined') {
|
||||
this._statusBarItem.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,535 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ExtensionContext, workspace, window, OutputChannel, languages } from 'vscode';
|
||||
import * as SqlopsClient from 'dataprotocol-client';
|
||||
import { CloseAction, ErrorAction, ServerOptions, NotificationHandler, NotificationType, RequestType, TransportKind } from 'vscode-languageclient';
|
||||
|
||||
import { VscodeWrapper } from '../controllers/vscodeWrapper';
|
||||
import { Telemetry } from '../models/telemetry';
|
||||
import { Utils } from '../models/utils';
|
||||
import { VersionRequest, IExtensionConstants } from '../models/contracts/contracts';
|
||||
import { Logger } from '../models/logger';
|
||||
import ServerProvider from './server';
|
||||
import ServiceDownloadProvider from './serviceDownloadProvider';
|
||||
import DecompressProvider from './decompressProvider';
|
||||
import HttpClient from './httpClient';
|
||||
import ExtConfig from '../configurations/extConfig';
|
||||
import { PlatformInformation, Runtime } from '../models/platform';
|
||||
import { ServerInitializationResult, ServerStatusView } from './serverStatus';
|
||||
import StatusView from '../views/statusView';
|
||||
import * as LanguageServiceContracts from '../models/contracts/languageService';
|
||||
import { Constants } from '../models/constants';
|
||||
import ServiceStatus from './serviceStatus';
|
||||
|
||||
const opener = require('opener');
|
||||
const path = require('path');
|
||||
let _channel: OutputChannel = undefined;
|
||||
|
||||
/**
|
||||
* @interface IMessage
|
||||
*/
|
||||
interface IMessage {
|
||||
jsonrpc: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Language Service client errors
|
||||
* @class LanguageClientErrorHandler
|
||||
*/
|
||||
class LanguageClientErrorHandler {
|
||||
|
||||
private vscodeWrapper: VscodeWrapper;
|
||||
|
||||
/**
|
||||
* Creates an instance of LanguageClientErrorHandler.
|
||||
* @memberOf LanguageClientErrorHandler
|
||||
*/
|
||||
constructor(constants: IExtensionConstants) {
|
||||
if (!this.vscodeWrapper) {
|
||||
this.vscodeWrapper = new VscodeWrapper(constants);
|
||||
}
|
||||
Telemetry.getRuntimeId = this.vscodeWrapper.constants.getRuntimeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an error message prompt with a link to known issues wiki page
|
||||
* @memberOf LanguageClientErrorHandler
|
||||
*/
|
||||
showOnErrorPrompt(): void {
|
||||
let extensionConstants = this.vscodeWrapper.constants;
|
||||
Telemetry.sendTelemetryEvent(extensionConstants.serviceName + 'Crash');
|
||||
this.vscodeWrapper.showErrorMessage(
|
||||
extensionConstants.serviceCrashMessage,
|
||||
Constants.serviceCrashButton).then(action => {
|
||||
if (action && action === Constants.serviceCrashButton) {
|
||||
opener(extensionConstants.serviceCrashLink);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for language service client error
|
||||
*
|
||||
* @param {Error} error
|
||||
* @param {Message} message
|
||||
* @param {number} count
|
||||
* @returns {ErrorAction}
|
||||
*
|
||||
* @memberOf LanguageClientErrorHandler
|
||||
*/
|
||||
error(error: Error, message: IMessage, count: number): ErrorAction {
|
||||
this.showOnErrorPrompt();
|
||||
|
||||
// we don't retry running the service since crashes leave the extension
|
||||
// in a bad, unrecovered state
|
||||
return ErrorAction.Shutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for language service client closed
|
||||
*
|
||||
* @returns {CloseAction}
|
||||
*
|
||||
* @memberOf LanguageClientErrorHandler
|
||||
*/
|
||||
closed(): CloseAction {
|
||||
this.showOnErrorPrompt();
|
||||
|
||||
// we don't retry running the service since crashes leave the extension
|
||||
// in a bad, unrecovered state
|
||||
return CloseAction.DoNotRestart;
|
||||
}
|
||||
}
|
||||
|
||||
// The Service Client class handles communication with the VS Code LanguageClient
|
||||
export class SqlToolsServiceClient {
|
||||
// singleton instance
|
||||
private static _instance: SqlToolsServiceClient = undefined;
|
||||
|
||||
private static _constants: IExtensionConstants = undefined;
|
||||
|
||||
public static get constants(): IExtensionConstants {
|
||||
return this._constants;
|
||||
}
|
||||
|
||||
public static set constants(constantsObject: IExtensionConstants) {
|
||||
this._constants = constantsObject;
|
||||
Telemetry.getRuntimeId = this._constants.getRuntimeId;
|
||||
}
|
||||
|
||||
private static _helper: LanguageServiceContracts.ILanguageClientHelper = undefined;
|
||||
|
||||
public static get helper(): LanguageServiceContracts.ILanguageClientHelper {
|
||||
return this._helper;
|
||||
}
|
||||
|
||||
public static set helper(helperObject: LanguageServiceContracts.ILanguageClientHelper) {
|
||||
this._helper = helperObject;
|
||||
}
|
||||
|
||||
// VS Code Language Client
|
||||
private _client: SqlopsClient.SqlOpsDataClient = undefined;
|
||||
|
||||
// getter method for the Language Client
|
||||
private get client(): SqlopsClient.SqlOpsDataClient {
|
||||
return this._client;
|
||||
}
|
||||
|
||||
private set client(client: SqlopsClient.SqlOpsDataClient) {
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
public installDirectory: string;
|
||||
private _downloadProvider: ServiceDownloadProvider;
|
||||
private _vscodeWrapper: VscodeWrapper;
|
||||
|
||||
private _serviceStatus: ServiceStatus;
|
||||
|
||||
private _languageClientStartTime: number = undefined;
|
||||
private _installationTime: number = undefined;
|
||||
|
||||
constructor(
|
||||
private _server: ServerProvider,
|
||||
private _logger: Logger,
|
||||
private _statusView: StatusView,
|
||||
private _config: ExtConfig) {
|
||||
this._downloadProvider = _server.downloadProvider;
|
||||
if (!this._vscodeWrapper) {
|
||||
this._vscodeWrapper = new VscodeWrapper(SqlToolsServiceClient.constants);
|
||||
}
|
||||
this._serviceStatus = new ServiceStatus(SqlToolsServiceClient._constants.serviceName);
|
||||
}
|
||||
|
||||
// gets or creates the singleton service client instance
|
||||
public static getInstance(path: string): SqlToolsServiceClient {
|
||||
if (this._instance === undefined) {
|
||||
let constants = this._constants;
|
||||
let config = new ExtConfig(constants.extensionConfigSectionName, undefined, path);
|
||||
_channel = window.createOutputChannel(constants.serviceInitializingOutputChannelName);
|
||||
let logger = new Logger(text => _channel.append(text), constants);
|
||||
let serverStatusView = new ServerStatusView(constants);
|
||||
let httpClient = new HttpClient();
|
||||
let decompressProvider = new DecompressProvider();
|
||||
let downloadProvider = new ServiceDownloadProvider(config, logger, serverStatusView, httpClient,
|
||||
decompressProvider, constants, false);
|
||||
let serviceProvider = new ServerProvider(downloadProvider, config, serverStatusView, constants.extensionConfigSectionName);
|
||||
let statusView = new StatusView();
|
||||
this._instance = new SqlToolsServiceClient(serviceProvider, logger, statusView, config);
|
||||
}
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
// initialize the Service Client instance by launching
|
||||
// out-of-proc server through the LanguageClient
|
||||
public initialize(context: ExtensionContext): Promise<any> {
|
||||
this._logger.appendLine(SqlToolsServiceClient._constants.serviceInitializing);
|
||||
this._languageClientStartTime = Date.now();
|
||||
return PlatformInformation.getCurrent(SqlToolsServiceClient._constants.getRuntimeId, SqlToolsServiceClient._constants.extensionName).then(platformInfo => {
|
||||
return this.initializeForPlatform(platformInfo, context);
|
||||
}).catch(err => {
|
||||
this._vscodeWrapper.showErrorMessage(err);
|
||||
});
|
||||
}
|
||||
|
||||
public initializeForPlatform(platformInfo: PlatformInformation, context: ExtensionContext): Promise<ServerInitializationResult> {
|
||||
return new Promise<ServerInitializationResult>((resolve, reject) => {
|
||||
this._logger.appendLine(SqlToolsServiceClient._constants.commandsNotAvailableWhileInstallingTheService);
|
||||
this._logger.appendLine();
|
||||
this._logger.append(`Platform: ${platformInfo.toString()}`);
|
||||
|
||||
if (!platformInfo.isValidRuntime()) {
|
||||
// if it's an unknown Linux distro then try generic Linux x64 and give a warning to the user
|
||||
if (platformInfo.isLinux()) {
|
||||
this._logger.appendLine(Constants.usingDefaultPlatformMessage);
|
||||
platformInfo.runtimeId = Runtime.Linux_64;
|
||||
}
|
||||
|
||||
let ignoreWarning: boolean = this._config.getWorkspaceConfig(Constants.ignorePlatformWarning, false);
|
||||
if (!ignoreWarning) {
|
||||
this._vscodeWrapper.showErrorMessage(
|
||||
Constants.unsupportedPlatformErrorMessage,
|
||||
Constants.neverShowAgain)
|
||||
.then(action => {
|
||||
if (action === Constants.neverShowAgain) {
|
||||
this._config.updateWorkspaceConfig(Constants.ignorePlatformWarning, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Telemetry.sendTelemetryEvent('UnsupportedPlatform', { platform: platformInfo.toString() });
|
||||
}
|
||||
|
||||
if (platformInfo.runtimeId) {
|
||||
this._logger.appendLine(` (${platformInfo.getRuntimeDisplayName()})`);
|
||||
} else {
|
||||
this._logger.appendLine();
|
||||
}
|
||||
|
||||
this._logger.appendLine();
|
||||
|
||||
this._server.getServerPath(platformInfo.runtimeId).then(serverPath => {
|
||||
if (serverPath === undefined) {
|
||||
// Check if the service already installed and if not open the output channel to show the logs
|
||||
if (_channel !== undefined) {
|
||||
_channel.show();
|
||||
}
|
||||
let installationStartTime = Date.now();
|
||||
this._server.downloadServerFiles(platformInfo.runtimeId).then(installedServerPath => {
|
||||
this._installationTime = Date.now() - installationStartTime;
|
||||
this.initializeLanguageClient(installedServerPath, context, platformInfo.runtimeId);
|
||||
resolve(new ServerInitializationResult(true, true, installedServerPath));
|
||||
}).catch(downloadErr => {
|
||||
reject(downloadErr);
|
||||
});
|
||||
} else {
|
||||
this.initializeLanguageClient(serverPath, context, platformInfo.runtimeId);
|
||||
resolve(new ServerInitializationResult(false, true, serverPath));
|
||||
}
|
||||
}).catch(err => {
|
||||
Utils.logDebug(SqlToolsServiceClient._constants.serviceLoadingFailed + ' ' + err, SqlToolsServiceClient._constants.extensionConfigSectionName);
|
||||
Utils.showErrorMsg(SqlToolsServiceClient._constants.serviceLoadingFailed, SqlToolsServiceClient._constants.extensionName);
|
||||
Telemetry.sendTelemetryEvent('ServiceInitializingFailed');
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the SQL language configuration
|
||||
*
|
||||
* @memberOf SqlToolsServiceClient
|
||||
*/
|
||||
private initializeLanguageConfiguration(): void {
|
||||
languages.setLanguageConfiguration('sql', {
|
||||
comments: {
|
||||
lineComment: '--',
|
||||
blockComment: ['/*', '*/']
|
||||
},
|
||||
|
||||
brackets: [
|
||||
['{', '}'],
|
||||
['[', ']'],
|
||||
['(', ')']
|
||||
],
|
||||
|
||||
__characterPairSupport: {
|
||||
autoClosingPairs: [
|
||||
{ open: '{', close: '}' },
|
||||
{ open: '[', close: ']' },
|
||||
{ open: '(', close: ')' },
|
||||
{ open: '"', close: '"', notIn: ['string'] },
|
||||
{ open: '\'', close: '\'', notIn: ['string', 'comment'] }
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private initializeLanguageClient(serverPath: string, context: ExtensionContext, runtimeId: Runtime): void {
|
||||
if (serverPath === undefined) {
|
||||
Utils.logDebug(SqlToolsServiceClient._constants.invalidServiceFilePath, SqlToolsServiceClient._constants.extensionConfigSectionName);
|
||||
throw new Error(SqlToolsServiceClient._constants.invalidServiceFilePath);
|
||||
} else {
|
||||
let self = this;
|
||||
|
||||
if (SqlToolsServiceClient._constants.languageId === 'sql') {
|
||||
self.initializeLanguageConfiguration();
|
||||
}
|
||||
|
||||
// Use default createServerOptions if one isn't specified
|
||||
let serverOptions: ServerOptions = SqlToolsServiceClient._helper ?
|
||||
SqlToolsServiceClient._helper.createServerOptions(serverPath, runtimeId) : self.createServerOptions(serverPath);
|
||||
this.client = this.createLanguageClient(serverOptions);
|
||||
this.installDirectory = this._downloadProvider.getInstallDirectory(runtimeId, SqlToolsServiceClient._constants.extensionConfigSectionName);
|
||||
|
||||
if (context !== undefined) {
|
||||
// Create the language client and start the client.
|
||||
let disposable = this.client.start();
|
||||
|
||||
// Push the disposable to the context's subscriptions so that the
|
||||
// client can be deactivated on extension deactivation
|
||||
|
||||
context.subscriptions.push(disposable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public createClient(context: ExtensionContext, runtimeId: Runtime, languageClientHelper: LanguageServiceContracts.ILanguageClientHelper, executableFiles: string[]): Promise<SqlopsClient.SqlOpsDataClient> {
|
||||
return new Promise<SqlopsClient.SqlOpsDataClient>((resolve, reject) => {
|
||||
let client: SqlopsClient.SqlOpsDataClient;
|
||||
this._server.findServerPath(this.installDirectory, executableFiles).then(serverPath => {
|
||||
if (serverPath === undefined) {
|
||||
reject(new Error(SqlToolsServiceClient._constants.invalidServiceFilePath));
|
||||
} else {
|
||||
|
||||
let serverOptions: ServerOptions = languageClientHelper ?
|
||||
languageClientHelper.createServerOptions(serverPath, runtimeId) : this.createServerOptions(serverPath);
|
||||
|
||||
// Options to control the language client
|
||||
let clientOptions: SqlopsClient.ClientOptions = {
|
||||
documentSelector: [SqlToolsServiceClient._constants.languageId],
|
||||
providerId: '',
|
||||
synchronize: {
|
||||
configurationSection: SqlToolsServiceClient._constants.extensionConfigSectionName
|
||||
},
|
||||
errorHandler: new LanguageClientErrorHandler(SqlToolsServiceClient._constants),
|
||||
outputChannel: {
|
||||
append: () => {
|
||||
},
|
||||
appendLine: () => {
|
||||
},
|
||||
dispose: () => {
|
||||
},
|
||||
clear: () => {
|
||||
},
|
||||
hide: () => {
|
||||
},
|
||||
name: '',
|
||||
show: () => {
|
||||
}
|
||||
},
|
||||
// pass in no features so we don't register features we don't want
|
||||
features: []
|
||||
};
|
||||
|
||||
this._serviceStatus.showServiceLoading();
|
||||
// cache the client instance for later use
|
||||
client = new SqlopsClient.SqlOpsDataClient(SqlToolsServiceClient._constants.serviceName, serverOptions, clientOptions);
|
||||
|
||||
if (context !== undefined) {
|
||||
// Create the language client and start the client.
|
||||
let disposable = client.start();
|
||||
|
||||
// Push the disposable to the context's subscriptions so that the
|
||||
// client can be deactivated on extension deactivation
|
||||
|
||||
context.subscriptions.push(disposable);
|
||||
}
|
||||
client.onReady().then(this._serviceStatus.showServiceLoaded);
|
||||
|
||||
resolve(client);
|
||||
}
|
||||
}, error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private createServerOptions(servicePath): ServerOptions {
|
||||
let serverArgs = [];
|
||||
let serverCommand: string = servicePath;
|
||||
if (servicePath.endsWith('.dll')) {
|
||||
serverArgs = [servicePath];
|
||||
serverCommand = 'dotnet';
|
||||
}
|
||||
|
||||
// Enable diagnostic logging in the service if it is configured
|
||||
let config = workspace.getConfiguration(SqlToolsServiceClient._constants.extensionConfigSectionName);
|
||||
if (config) {
|
||||
let logDebugInfo = config[Constants.configLogDebugInfo];
|
||||
if (logDebugInfo) {
|
||||
serverArgs.push('--enable-logging');
|
||||
}
|
||||
}
|
||||
serverArgs.push('--log-dir');
|
||||
let logFileLocation = path.join(Utils.getDefaultLogLocation(), SqlToolsServiceClient.constants.extensionName);
|
||||
serverArgs.push(logFileLocation);
|
||||
|
||||
// run the service host using dotnet.exe from the path
|
||||
let serverOptions: ServerOptions = { command: serverCommand, args: serverArgs, transport: TransportKind.stdio };
|
||||
return serverOptions;
|
||||
}
|
||||
|
||||
private createLanguageClient(serverOptions: ServerOptions): SqlopsClient.SqlOpsDataClient {
|
||||
// Options to control the language client
|
||||
let clientOptions: SqlopsClient.ClientOptions = {
|
||||
documentSelector: [SqlToolsServiceClient._constants.languageId],
|
||||
providerId: SqlToolsServiceClient._constants.providerId,
|
||||
synchronize: {
|
||||
configurationSection: SqlToolsServiceClient._constants.extensionConfigSectionName
|
||||
},
|
||||
errorHandler: new LanguageClientErrorHandler(SqlToolsServiceClient._constants),
|
||||
outputChannel: {
|
||||
append: () => {
|
||||
},
|
||||
appendLine: () => {
|
||||
},
|
||||
dispose: () => {
|
||||
},
|
||||
clear: () => {
|
||||
},
|
||||
hide: () => {
|
||||
},
|
||||
name: '',
|
||||
show: () => {
|
||||
}
|
||||
},
|
||||
features: [
|
||||
SqlopsClient.AdminServicesFeature,
|
||||
SqlopsClient.BackupFeature,
|
||||
SqlopsClient.CapabilitiesFeature,
|
||||
SqlopsClient.ConnectionFeature,
|
||||
SqlopsClient.FileBrowserFeature,
|
||||
SqlopsClient.MetadataFeature,
|
||||
SqlopsClient.ObjectExplorerFeature,
|
||||
SqlopsClient.ProfilerFeature,
|
||||
SqlopsClient.QueryFeature,
|
||||
SqlopsClient.RestoreFeature,
|
||||
SqlopsClient.ScriptingFeature,
|
||||
SqlopsClient.TaskServicesFeature,
|
||||
// heres the important bit
|
||||
LanguageServiceContracts.AgentServicesFeature
|
||||
]
|
||||
};
|
||||
|
||||
this._serviceStatus.showServiceLoading();
|
||||
// cache the client instance for later use
|
||||
let client = new SqlopsClient.SqlOpsDataClient(SqlToolsServiceClient._constants.serviceName, serverOptions, clientOptions);
|
||||
client.onReady().then(() => {
|
||||
this.checkServiceCompatibility();
|
||||
this._serviceStatus.showServiceLoaded();
|
||||
client.onNotification(LanguageServiceContracts.TelemetryNotification.type, this.handleLanguageServiceTelemetryNotification());
|
||||
client.onNotification(LanguageServiceContracts.StatusChangedNotification.type, this.handleLanguageServiceStatusNotification());
|
||||
|
||||
// Report the language client startup time
|
||||
let endTime = Date.now();
|
||||
let installationTime = this._installationTime || 0;
|
||||
let totalTime = endTime - this._languageClientStartTime;
|
||||
let processStartupTime = totalTime - installationTime;
|
||||
Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', {
|
||||
installationTime: String(installationTime),
|
||||
processStartupTime: String(processStartupTime),
|
||||
totalTime: String(totalTime),
|
||||
beginningTimestamp: String(this._languageClientStartTime)
|
||||
});
|
||||
this._languageClientStartTime = undefined;
|
||||
this._installationTime = undefined;
|
||||
});
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private handleLanguageServiceTelemetryNotification(): NotificationHandler<LanguageServiceContracts.TelemetryParams> {
|
||||
return (event: LanguageServiceContracts.TelemetryParams): void => {
|
||||
Telemetry.sendTelemetryEvent(event.params.eventName, event.params.properties, event.params.measures);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Public for testing purposes only.
|
||||
*/
|
||||
public handleLanguageServiceStatusNotification(): NotificationHandler<LanguageServiceContracts.StatusChangeParams> {
|
||||
return (event: LanguageServiceContracts.StatusChangeParams): void => {
|
||||
this._statusView.languageServiceStatusChanged(event.ownerUri, event.status);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a request to the service client
|
||||
* @param type The of the request to make
|
||||
* @param params The params to pass with the request
|
||||
* @returns A thenable object for when the request receives a response
|
||||
*/
|
||||
public sendRequest<P, R, E, RO>(type: RequestType<P, R, E, RO>, params?: P, client: SqlopsClient.SqlOpsDataClient = undefined): Thenable<R> {
|
||||
if (client === undefined) {
|
||||
client = this._client;
|
||||
}
|
||||
if (client !== undefined) {
|
||||
return client.sendRequest(type, params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler for a notification type
|
||||
* @param type The notification type to register the handler for
|
||||
* @param handler The handler to register
|
||||
*/
|
||||
public onNotification<P, RO>(type: NotificationType<P, RO>, handler: NotificationHandler<P>, client: SqlopsClient.SqlOpsDataClient = undefined): void {
|
||||
if (client === undefined) {
|
||||
client = this._client;
|
||||
}
|
||||
if (client !== undefined) {
|
||||
return client.onNotification(type, handler);
|
||||
}
|
||||
}
|
||||
|
||||
public checkServiceCompatibility(): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
this._client.sendRequest(VersionRequest.type, undefined).then((result) => {
|
||||
Utils.logDebug(SqlToolsServiceClient._constants.extensionName + ' service client version: ' + result, SqlToolsServiceClient._constants.extensionConfigSectionName);
|
||||
|
||||
if (result === undefined || !result.startsWith(SqlToolsServiceClient._constants.serviceCompatibleVersion)) {
|
||||
Utils.showErrorMsg(Constants.serviceNotCompatibleError, SqlToolsServiceClient._constants.extensionName);
|
||||
Utils.logDebug(Constants.serviceNotCompatibleError, SqlToolsServiceClient._constants.extensionConfigSectionName);
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { Runtime, getRuntimeDisplayName } from '../models/platform';
|
||||
import * as path from 'path';
|
||||
import { IConfig, IStatusView, IPackage, PackageError, IHttpClient, IDecompressProvider } from './interfaces';
|
||||
import { ILogger } from '../models/interfaces';
|
||||
import { Constants } from '../models/constants';
|
||||
import * as tmp from 'tmp';
|
||||
import { IExtensionConstants } from '../models/contracts/contracts';
|
||||
|
||||
const fse = require('fs-extra');
|
||||
|
||||
/*
|
||||
* Service Download Provider class which handles downloading the SQL Tools service.
|
||||
*/
|
||||
export default class ServiceDownloadProvider {
|
||||
|
||||
constructor(private _config: IConfig,
|
||||
private _logger: ILogger,
|
||||
private _statusView: IStatusView,
|
||||
private _httpClient: IHttpClient,
|
||||
private _decompressProvider: IDecompressProvider,
|
||||
private _extensionConstants: IExtensionConstants,
|
||||
private _fromBuild: boolean) {
|
||||
// Ensure our temp files get cleaned up in case of error.
|
||||
tmp.setGracefulCleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the download url for given platform
|
||||
*/
|
||||
public getDownloadFileName(platform: Runtime): string {
|
||||
let fileNamesJson = this._config.getConfigValue('downloadFileNames');
|
||||
console.info('Platform: ', platform.toString());
|
||||
|
||||
let fileName = fileNamesJson[platform.toString()];
|
||||
console.info('Filename: ', fileName);
|
||||
|
||||
if (fileName === undefined) {
|
||||
if (process.platform === 'linux') {
|
||||
throw new Error('Unsupported linux distribution');
|
||||
} else {
|
||||
throw new Error(`Unsupported platform: ${process.platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns SQL tools service installed folder.
|
||||
*/
|
||||
public getInstallDirectory(platform: Runtime, extensionConfigSectionName: string): string {
|
||||
let basePath = this.getInstallDirectoryRoot(platform, extensionConfigSectionName);
|
||||
let versionFromConfig = this._config.getPackageVersion();
|
||||
basePath = basePath.replace('{#version#}', versionFromConfig);
|
||||
basePath = basePath.replace('{#platform#}', getRuntimeDisplayName(platform));
|
||||
if (!fse.existsSync(basePath)) {
|
||||
fse.mkdirsSync(basePath);
|
||||
}
|
||||
|
||||
return basePath;
|
||||
}
|
||||
|
||||
private getLocalUserFolderPath(platform: Runtime): string {
|
||||
if (platform) {
|
||||
switch (platform) {
|
||||
case Runtime.Windows_64:
|
||||
case Runtime.Windows_86:
|
||||
return process.env.APPDATA;
|
||||
case Runtime.OSX:
|
||||
return process.env.HOME + '/Library/Preferences';
|
||||
default:
|
||||
return process.env.HOME;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns SQL tools service installed folder root.
|
||||
*/
|
||||
public getInstallDirectoryRoot(platform: Runtime, extensionConfigSectionName: string): string {
|
||||
let installDirFromConfig : string;
|
||||
installDirFromConfig = this._config.getInstallDirectory();
|
||||
if (!installDirFromConfig || installDirFromConfig === '') {
|
||||
let rootFolderName: string = '.sqlops';
|
||||
if (platform === Runtime.Windows_64 || platform === Runtime.Windows_86) {
|
||||
rootFolderName = 'sqlops';
|
||||
}
|
||||
installDirFromConfig = path.join(this.getLocalUserFolderPath(platform), `/${rootFolderName}/${this._extensionConstants.installFolderName}/{#version#}/{#platform#}`);
|
||||
}
|
||||
let basePath: string;
|
||||
if (path.isAbsolute(installDirFromConfig)) {
|
||||
basePath = installDirFromConfig;
|
||||
} else if (this._fromBuild) {
|
||||
basePath = path.join(__dirname, '../../../extensions/' + extensionConfigSectionName + '/' + installDirFromConfig);
|
||||
} else {
|
||||
// The path from config is relative to the out folder
|
||||
basePath = path.join(__dirname, '../../../../' + installDirFromConfig);
|
||||
}
|
||||
return basePath;
|
||||
}
|
||||
|
||||
private getGetDownloadUrl(fileName: string): string {
|
||||
let baseDownloadUrl = this._config.getDownloadUrl();
|
||||
let version = this._config.getPackageVersion();
|
||||
baseDownloadUrl = baseDownloadUrl.replace('{#version#}', version);
|
||||
baseDownloadUrl = baseDownloadUrl.replace('{#fileName#}', fileName);
|
||||
return baseDownloadUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the service and decompress it in the install folder.
|
||||
*/
|
||||
public installService(platform: Runtime): Promise<boolean> {
|
||||
const proxy = <string>this._config.getWorkspaceConfig('http.proxy');
|
||||
const strictSSL = this._config.getWorkspaceConfig('http.proxyStrictSSL', true);
|
||||
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
const fileName = this.getDownloadFileName(platform);
|
||||
const installDirectory = this.getInstallDirectory(platform, this._extensionConstants.extensionConfigSectionName);
|
||||
|
||||
this._logger.appendLine(`${this._extensionConstants.serviceInstallingTo} ${installDirectory}.`);
|
||||
const urlString = this.getGetDownloadUrl(fileName);
|
||||
|
||||
this._logger.appendLine(`${Constants.serviceDownloading} ${urlString}`);
|
||||
let pkg: IPackage = {
|
||||
installPath: installDirectory,
|
||||
url: urlString,
|
||||
tmpFile: undefined
|
||||
};
|
||||
this.createTempFile(pkg).then(tmpResult => {
|
||||
pkg.tmpFile = tmpResult;
|
||||
|
||||
this._httpClient.downloadFile(pkg.url, pkg, this._logger, this._statusView, proxy, strictSSL).then(_ => {
|
||||
|
||||
this._logger.logDebug(`Downloaded to ${pkg.tmpFile.name}...`);
|
||||
this._logger.appendLine(' Done!');
|
||||
this.install(pkg).then(result => {
|
||||
resolve(true);
|
||||
}).catch(installError => {
|
||||
reject(installError);
|
||||
});
|
||||
}).catch(downloadError => {
|
||||
this._logger.appendLine(`[ERROR] ${downloadError}`);
|
||||
reject(downloadError);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private createTempFile(pkg: IPackage): Promise<tmp.SynchronousResult> {
|
||||
return new Promise<tmp.SynchronousResult>((resolve, reject) => {
|
||||
tmp.file({ prefix: 'package-' }, (err, path, fd, cleanupCallback) => {
|
||||
if (err) {
|
||||
return reject(new PackageError('Error from tmp.file', pkg, err));
|
||||
}
|
||||
|
||||
resolve(<tmp.SynchronousResult>{ name: path, fd: fd, removeCallback: cleanupCallback });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private install(pkg: IPackage): Promise<void> {
|
||||
this._logger.appendLine('Installing ...');
|
||||
this._statusView.installingService();
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this._decompressProvider.decompress(pkg, this._logger).then(_ => {
|
||||
this._statusView.serviceInstalled();
|
||||
resolve();
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Runtime, PlatformInformation } from '../models/platform';
|
||||
import Config from '../configurations/config';
|
||||
import ServiceDownloadProvider from './serviceDownloadProvider';
|
||||
import DecompressProvider from './decompressProvider';
|
||||
import HttpClient from './httpClient';
|
||||
import ServerProvider from './server';
|
||||
import { IStatusView } from './interfaces';
|
||||
import { ILogger } from '../models/interfaces';
|
||||
import { IExtensionConstants } from '../models/contracts/contracts';
|
||||
|
||||
class StubStatusView implements IStatusView {
|
||||
installingService(): void {
|
||||
console.log('...');
|
||||
}
|
||||
serviceInstalled(): void {
|
||||
console.log('Service installed');
|
||||
}
|
||||
serviceInstallationFailed(): void {
|
||||
console.log('Service installation failed');
|
||||
}
|
||||
updateServiceDownloadingProgress(downloadPercentage: number): void {
|
||||
if (downloadPercentage === 100) {
|
||||
process.stdout.write('100%');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StubLogger implements ILogger {
|
||||
logDebug(message: string): void {
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
increaseIndent(): void {
|
||||
console.log('increaseIndent');
|
||||
}
|
||||
|
||||
decreaseIndent(): void {
|
||||
console.log('decreaseIndent');
|
||||
}
|
||||
|
||||
append(message?: string): void {
|
||||
process.stdout.write(message);
|
||||
}
|
||||
appendLine(message?: string): void {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
export class ServiceInstaller {
|
||||
private _config = undefined;
|
||||
private _logger = new StubLogger();
|
||||
private _statusView = new StubStatusView();
|
||||
private _httpClient = new HttpClient();
|
||||
private _decompressProvider = new DecompressProvider();
|
||||
private _downloadProvider = undefined;
|
||||
private _serverProvider = undefined;
|
||||
private _extensionConstants = undefined;
|
||||
|
||||
constructor(extensionConstants: IExtensionConstants, path?: string) {
|
||||
this._extensionConstants = extensionConstants;
|
||||
this._config = new Config(extensionConstants.extensionConfigSectionName, path, true);
|
||||
this._downloadProvider = new ServiceDownloadProvider(this._config, this._logger, this._statusView, this._httpClient, this._decompressProvider, extensionConstants, true);
|
||||
this._serverProvider = new ServerProvider(this._downloadProvider, this._config, this._statusView, extensionConstants.extensionConfigSectionName);
|
||||
}
|
||||
/*
|
||||
* Installs the service for the given platform if it's not already installed.
|
||||
*/
|
||||
public installService(): Promise<String> {
|
||||
return PlatformInformation.getCurrent(this._extensionConstants.getRuntimeId, this._extensionConstants.extensionName).then(platformInfo => {
|
||||
if (platformInfo.isValidRuntime()) {
|
||||
return this._serverProvider.getOrDownloadServer(platformInfo.runtimeId);
|
||||
} else {
|
||||
throw new Error('unsupported runtime');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the install folder path for given platform.
|
||||
*/
|
||||
public getServiceInstallDirectory(runtime: Runtime): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
if (runtime === undefined) {
|
||||
PlatformInformation.getCurrent(this._extensionConstants.getRuntimeId, this._extensionConstants.extensionName).then(platformInfo => {
|
||||
if (platformInfo.isValidRuntime()) {
|
||||
resolve(this._downloadProvider.getInstallDirectory(platformInfo.runtimeId));
|
||||
} else {
|
||||
reject('unsupported runtime');
|
||||
}
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
resolve(this._downloadProvider.getInstallDirectory(runtime));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the path to the root folder of service install location.
|
||||
*/
|
||||
public getServiceInstallDirectoryRoot(runtime: Runtime): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
if (runtime === undefined) {
|
||||
PlatformInformation.getCurrent(this._extensionConstants.getRuntimeId, this._extensionConstants.extensionName).then(platformInfo => {
|
||||
if (platformInfo.isValidRuntime()) {
|
||||
let directoryPath: string = this._downloadProvider.getInstallDirectoryRoot(platformInfo, this._extensionConstants.extensionName);
|
||||
directoryPath = directoryPath.replace('\\{#version#}', '');
|
||||
directoryPath = directoryPath.replace('\\{#platform#}', '');
|
||||
directoryPath = directoryPath.replace('/{#platform#}', '');
|
||||
directoryPath = directoryPath.replace('/{#version#}', '');
|
||||
resolve(directoryPath);
|
||||
} else {
|
||||
reject('unsupported runtime');
|
||||
}
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
resolve(this._downloadProvider.getInstallDirectory(runtime));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 vscode = require('vscode');
|
||||
|
||||
export default class ServiceStatus implements vscode.Disposable {
|
||||
|
||||
private _progressTimerId: NodeJS.Timer;
|
||||
|
||||
private _statusBarItem: vscode.StatusBarItem = undefined;
|
||||
|
||||
private durationStatusInMs: number = 1500;
|
||||
|
||||
// These need localization
|
||||
private _serviceStartingMessage: string = `Starting ${this._serviceName}`;
|
||||
private _serviceStartedMessage: string = `${this._serviceName} started`;
|
||||
|
||||
constructor(private _serviceName: string) {
|
||||
this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
|
||||
}
|
||||
|
||||
public showServiceLoading(): Promise<void> {
|
||||
return this === undefined ?
|
||||
Promise.resolve() :
|
||||
Promise.resolve(this.updateStatusView(this._serviceStartingMessage, true));
|
||||
}
|
||||
|
||||
public showServiceLoaded(): Promise<void> {
|
||||
return this === undefined ?
|
||||
Promise.resolve() :
|
||||
Promise.resolve(this.updateStatusView(this._serviceStartedMessage, false, this.durationStatusInMs));
|
||||
}
|
||||
|
||||
//TODO: This can be merged with the serverStatus code
|
||||
private showProgress(statusText: string): void {
|
||||
let index: number = 0;
|
||||
let progressTicks: string[] = ['.', '..', '...', '....'];
|
||||
|
||||
this._progressTimerId = setInterval(() => {
|
||||
index = (index + 1) % progressTicks.length;
|
||||
let progressTick = progressTicks[index];
|
||||
if (this._statusBarItem.text !== this._serviceStartedMessage) {
|
||||
this._statusBarItem.text = statusText + ' ' + progressTick;
|
||||
this._statusBarItem.show();
|
||||
}
|
||||
}, 400);
|
||||
}
|
||||
|
||||
private updateStatusView(message: string, showAsProgress: boolean = false, disposeAfter: number = -1): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (showAsProgress) {
|
||||
this.showProgress(message);
|
||||
}
|
||||
else {
|
||||
this._statusBarItem.text = message;
|
||||
this._statusBarItem.show();
|
||||
if (this._progressTimerId !== undefined) {
|
||||
clearInterval(this._progressTimerId);
|
||||
}
|
||||
}
|
||||
if (disposeAfter !== -1) {
|
||||
setInterval(() => {
|
||||
this._statusBarItem.hide();
|
||||
}, disposeAfter);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
if (this._progressTimerId !== undefined) {
|
||||
clearInterval(this._progressTimerId);
|
||||
}
|
||||
this._statusBarItem.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
export * from './controllers/vscodeWrapper';
|
||||
export * from './models/constants';
|
||||
export * from './models/utils';
|
||||
|
||||
export { SqlToolsServiceClient } from './languageservice/serviceClient';
|
||||
export { IExtensionConstants } from './models/contracts/contracts';
|
||||
export { ILanguageClientHelper } from './models/contracts/languageService';
|
||||
export { Runtime, PlatformInformation } from './models/platform';
|
||||
export { Telemetry } from './models/telemetry';
|
||||
export { LinuxDistribution } from './models/platform';
|
||||
export { ServiceInstaller } from './languageservice/serviceInstallerUtil';
|
||||
@@ -1,25 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export namespace Constants {
|
||||
//constants
|
||||
export const configLogDebugInfo: string = 'logDebugInfo';
|
||||
export const serviceNotCompatibleError: string = 'Client is not compatible with the service layer';
|
||||
export const serviceDownloading: string = 'Downloading';
|
||||
export const serviceInstalling: string = 'Installing';
|
||||
export const unsupportedPlatformErrorMessage: string = 'This platform is unsupported and application services may not function correctly';
|
||||
export const serviceConfigKey = 'service';
|
||||
export const executableFilesConfigKey = 'executableFiles';
|
||||
export const versionConfigKey = 'version';
|
||||
export const downloadUrlConfigKey = 'downloadUrl';
|
||||
export const installDirConfigKey = 'installDir';
|
||||
export const serviceCrashButton = 'View Known Issues';
|
||||
export const neverShowAgain = 'Do not show again';
|
||||
export const ignorePlatformWarning = 'ignorePlatformWarning';
|
||||
export const usingDefaultPlatformMessage = 'Unknown platform detected, defaulting to Linux_x64 platform';
|
||||
export const serverConnectionMetadata = 'serverConnectionMetadata';
|
||||
export const extensionDeactivated: string = 'de-activated.';
|
||||
export const extensionActivated: string = 'activated.';
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { RequestType } from 'vscode-languageclient';
|
||||
import { Runtime, LinuxDistribution } from '../platform';
|
||||
|
||||
// --------------------------------- < Version Request > -------------------------------------------------
|
||||
|
||||
// Version request message callback declaration
|
||||
export namespace VersionRequest {
|
||||
export const type = new RequestType<void, VersionResult, void, void>('version');
|
||||
}
|
||||
|
||||
// Version response format
|
||||
export type VersionResult = string;
|
||||
|
||||
// ------------------------------- </ Version Request > --------------------------------------------------
|
||||
|
||||
// Constants interface for each extension
|
||||
export interface IExtensionConstants {
|
||||
// TODO: Fill in interface
|
||||
|
||||
// Definitely dependent on the extension
|
||||
extensionName: string;
|
||||
invalidServiceFilePath: string;
|
||||
serviceName: string;
|
||||
extensionConfigSectionName: string;
|
||||
serviceCompatibleVersion: string;
|
||||
outputChannelName: string;
|
||||
languageId: string;
|
||||
serviceInstallingTo: string;
|
||||
serviceInitializing: string;
|
||||
serviceInstalled: string;
|
||||
serviceLoadingFailed: string;
|
||||
serviceInstallationFailed: string;
|
||||
serviceInitializingOutputChannelName: string;
|
||||
commandsNotAvailableWhileInstallingTheService: string;
|
||||
providerId: string;
|
||||
serviceCrashMessage: string;
|
||||
serviceCrashLink: string;
|
||||
installFolderName: string;
|
||||
telemetryExtensionName: string;
|
||||
|
||||
getRuntimeId(platform: string, architecture: string, distribution: LinuxDistribution): Runtime;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { NotificationType, ServerOptions, RequestType, RPCMessageType, ClientCapabilities, ServerCapabilities } from 'vscode-languageclient';
|
||||
import { ITelemetryEventProperties, ITelemetryEventMeasures } from '../telemetry';
|
||||
import { Runtime } from '../platform';
|
||||
import { SqlOpsFeature, SqlOpsDataClient } from 'dataprotocol-client';
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
||||
import { Disposable } from 'vscode';
|
||||
|
||||
// ------------------------------- < Telemetry Sent Event > ------------------------------------
|
||||
|
||||
/**
|
||||
* Event sent when the language service send a telemetry event
|
||||
*/
|
||||
export namespace TelemetryNotification {
|
||||
export const type = new NotificationType<TelemetryParams, void>('telemetry/sqlevent');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update event parameters
|
||||
*/
|
||||
export class TelemetryParams {
|
||||
public params: {
|
||||
eventName: string;
|
||||
properties: ITelemetryEventProperties;
|
||||
measures: ITelemetryEventMeasures;
|
||||
};
|
||||
}
|
||||
|
||||
// ------------------------------- </ Telemetry Sent Event > ----------------------------------
|
||||
|
||||
// ------------------------------- < Status Event > ------------------------------------
|
||||
|
||||
/**
|
||||
* Event sent when the language service send a status change event
|
||||
*/
|
||||
export namespace StatusChangedNotification {
|
||||
export const type = new NotificationType<StatusChangeParams, void>('textDocument/statusChanged');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update event parameters
|
||||
*/
|
||||
export class StatusChangeParams {
|
||||
/**
|
||||
* URI identifying the text document
|
||||
*/
|
||||
public ownerUri: string;
|
||||
|
||||
/**
|
||||
* The new status of the document
|
||||
*/
|
||||
public status: string;
|
||||
}
|
||||
|
||||
// ------------------------------- </ Status Sent Event > ----------------------------------
|
||||
|
||||
export interface ILanguageClientHelper {
|
||||
createServerOptions(servicePath: string, runtimeId?: Runtime): ServerOptions;
|
||||
}
|
||||
|
||||
// Job Management types
|
||||
export interface AgentJobsParams {
|
||||
ownerUri: string;
|
||||
jobId: string;
|
||||
}
|
||||
|
||||
export interface AgentJobsResult {
|
||||
succeeded: boolean;
|
||||
errorMessage: string;
|
||||
jobs: sqlops.AgentJobInfo[];
|
||||
}
|
||||
|
||||
export interface AgentJobHistoryParams {
|
||||
ownerUri: string;
|
||||
jobId: string;
|
||||
}
|
||||
|
||||
export interface AgentJobHistoryResult {
|
||||
succeeded: boolean;
|
||||
errorMessage: string;
|
||||
jobs: sqlops.AgentJobHistoryInfo[];
|
||||
}
|
||||
|
||||
export interface AgentJobActionParams {
|
||||
ownerUri: string;
|
||||
jobName: string;
|
||||
action: string;
|
||||
}
|
||||
|
||||
export interface AgentJobActionResult {
|
||||
succeeded: boolean;
|
||||
errorMessage: string;
|
||||
}
|
||||
|
||||
export namespace AgentJobsRequest {
|
||||
export const type = new RequestType<AgentJobsParams, AgentJobsResult, void, void>('agent/jobs');
|
||||
}
|
||||
|
||||
export namespace AgentJobHistoryRequest {
|
||||
export const type = new RequestType<AgentJobHistoryParams, AgentJobHistoryResult, void, void>('agent/jobhistory');
|
||||
}
|
||||
|
||||
|
||||
export namespace AgentJobActionRequest {
|
||||
export const type = new RequestType<AgentJobActionParams, AgentJobActionResult, void, void>('agent/jobaction');
|
||||
}
|
||||
|
||||
export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
||||
private static readonly messagesTypes: RPCMessageType[] = [
|
||||
AgentJobsRequest.type,
|
||||
AgentJobHistoryRequest.type,
|
||||
AgentJobActionRequest.type
|
||||
];
|
||||
|
||||
constructor(client: SqlOpsDataClient) {
|
||||
super(client, AgentServicesFeature.messagesTypes);
|
||||
}
|
||||
|
||||
public fillClientCapabilities(capabilities: ClientCapabilities): void {
|
||||
// this isn't explicitly necessary
|
||||
// ensure(ensure(capabilities, 'connection')!, 'agentServices')!.dynamicRegistration = true;
|
||||
}
|
||||
|
||||
public initialize(capabilities: ServerCapabilities): void {
|
||||
this.register(this.messages, {
|
||||
id: UUID.generateUuid(),
|
||||
registerOptions: undefined
|
||||
});
|
||||
}
|
||||
|
||||
protected registerProvider(options: undefined): Disposable {
|
||||
const client = this._client;
|
||||
|
||||
let getJobs = (ownerUri: string): Thenable<sqlops.AgentJobsResult> => {
|
||||
let params: AgentJobsParams = { ownerUri: ownerUri, jobId: null };
|
||||
return client.sendRequest(AgentJobsRequest.type, params).then(
|
||||
r => r,
|
||||
e => {
|
||||
client.logFailedRequest(AgentJobsRequest.type, e);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
let getJobHistory = (connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> => {
|
||||
let params: AgentJobHistoryParams = { ownerUri: connectionUri, jobId: jobID };
|
||||
|
||||
return client.sendRequest(AgentJobHistoryRequest.type, params).then(
|
||||
r => r,
|
||||
e => {
|
||||
client.logFailedRequest(AgentJobHistoryRequest.type, e);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
let jobAction = (connectionUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> => {
|
||||
let params: AgentJobActionParams = { ownerUri: connectionUri, jobName: jobName, action: action };
|
||||
return client.sendRequest(AgentJobActionRequest.type, params).then(
|
||||
r => r,
|
||||
e => {
|
||||
client.logFailedRequest(AgentJobActionRequest.type, e);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return sqlops.dataprotocol.registerAgentServicesProvider({
|
||||
providerId: client.providerId,
|
||||
getJobs,
|
||||
getJobHistory,
|
||||
jobAction
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as os from 'os';
|
||||
import { ILogger } from './interfaces';
|
||||
import { Utils } from './utils';
|
||||
import { IExtensionConstants } from './contracts/contracts';
|
||||
|
||||
/*
|
||||
* Logger class handles logging messages using the Util functions.
|
||||
*/
|
||||
export class Logger implements ILogger {
|
||||
private _writer: (message: string) => void;
|
||||
private _prefix: string;
|
||||
private _extensionConstants: IExtensionConstants;
|
||||
|
||||
private _indentLevel: number = 0;
|
||||
private _indentSize: number = 4;
|
||||
private _atLineStart: boolean = false;
|
||||
|
||||
constructor(writer: (message: string) => void, extensionConstants: IExtensionConstants, prefix?: string) {
|
||||
this._writer = writer;
|
||||
this._prefix = prefix;
|
||||
this._extensionConstants = extensionConstants;
|
||||
}
|
||||
|
||||
public logDebug(message: string): void {
|
||||
Utils.logDebug(message, this._extensionConstants.extensionConfigSectionName);
|
||||
}
|
||||
|
||||
private _appendCore(message: string): void {
|
||||
if (this._atLineStart) {
|
||||
if (this._indentLevel > 0) {
|
||||
const indent = ' '.repeat(this._indentLevel * this._indentSize);
|
||||
this._writer(indent);
|
||||
}
|
||||
|
||||
if (this._prefix) {
|
||||
this._writer(`[${this._prefix}] `);
|
||||
}
|
||||
|
||||
this._atLineStart = false;
|
||||
}
|
||||
|
||||
this._writer(message);
|
||||
}
|
||||
|
||||
public increaseIndent(): void {
|
||||
this._indentLevel += 1;
|
||||
}
|
||||
|
||||
public decreaseIndent(): void {
|
||||
if (this._indentLevel > 0) {
|
||||
this._indentLevel -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
public append(message?: string): void {
|
||||
message = message || '';
|
||||
this._appendCore(message);
|
||||
}
|
||||
|
||||
public appendLine(message?: string): void {
|
||||
message = message || '';
|
||||
this._appendCore(message + os.EOL);
|
||||
this._atLineStart = true;
|
||||
}
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as child_process from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
|
||||
const unknown = 'unknown';
|
||||
|
||||
export enum Runtime {
|
||||
UnknownRuntime = <any>'Unknown',
|
||||
UnknownVersion = <any>'Unknown',
|
||||
Windows_86 = <any>'Windows_86',
|
||||
Windows_64 = <any>'Windows_64',
|
||||
OSX = <any>'OSX',
|
||||
CentOS_7 = <any>'CentOS_7',
|
||||
Debian_8 = <any>'Debian_8',
|
||||
Fedora_23 = <any>'Fedora_23',
|
||||
OpenSUSE_13_2 = <any>'OpenSUSE_13_2',
|
||||
SLES_12_2 = <any>'SLES_12_2',
|
||||
RHEL_7 = <any>'RHEL_7',
|
||||
Ubuntu_14 = <any>'Ubuntu_14',
|
||||
Ubuntu_16 = <any>'Ubuntu_16',
|
||||
Linux_64 = <any>'Linux_64',
|
||||
Linux_86 = <any>'Linux-86'
|
||||
}
|
||||
|
||||
export function getRuntimeDisplayName(runtime: Runtime): string {
|
||||
switch (runtime) {
|
||||
case Runtime.Windows_64:
|
||||
return 'Windows';
|
||||
case Runtime.Windows_86:
|
||||
return 'Windows';
|
||||
case Runtime.OSX:
|
||||
return 'OSX';
|
||||
case Runtime.CentOS_7:
|
||||
return 'Linux';
|
||||
case Runtime.Debian_8:
|
||||
return 'Linux';
|
||||
case Runtime.Fedora_23:
|
||||
return 'Linux';
|
||||
case Runtime.OpenSUSE_13_2:
|
||||
return 'Linux';
|
||||
case Runtime.SLES_12_2:
|
||||
return 'Linux';
|
||||
case Runtime.RHEL_7:
|
||||
return 'Linux';
|
||||
case Runtime.Ubuntu_14:
|
||||
return 'Linux';
|
||||
case Runtime.Ubuntu_16:
|
||||
return 'Linux';
|
||||
case Runtime.Linux_64:
|
||||
return 'Linux';
|
||||
case Runtime.Linux_86:
|
||||
return 'Linux';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
export class PlatformInformation {
|
||||
public runtimeId: Runtime;
|
||||
|
||||
public constructor(
|
||||
public platform: string,
|
||||
public architecture: string,
|
||||
public distribution: LinuxDistribution = undefined,
|
||||
public getRuntimeId: (platform: string, architecture: string, distribution: LinuxDistribution) => Runtime) {
|
||||
try {
|
||||
this.runtimeId = this.getRuntimeId(platform, architecture, distribution);
|
||||
} catch (err) {
|
||||
this.runtimeId = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public isWindows(): boolean {
|
||||
return this.platform === 'win32';
|
||||
}
|
||||
|
||||
public isMacOS(): boolean {
|
||||
return this.platform === 'darwin';
|
||||
}
|
||||
|
||||
public isLinux(): boolean {
|
||||
return this.platform === 'linux';
|
||||
}
|
||||
|
||||
public isValidRuntime(): boolean {
|
||||
return this.runtimeId !== undefined && this.runtimeId !== Runtime.UnknownRuntime && this.runtimeId !== Runtime.UnknownVersion;
|
||||
}
|
||||
|
||||
public getRuntimeDisplayName(): string {
|
||||
return getRuntimeDisplayName(this.runtimeId);
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
let result = this.platform;
|
||||
|
||||
if (this.architecture) {
|
||||
if (result) {
|
||||
result += ', ';
|
||||
}
|
||||
|
||||
result += this.architecture;
|
||||
}
|
||||
|
||||
if (this.distribution) {
|
||||
if (result) {
|
||||
result += ', ';
|
||||
}
|
||||
|
||||
result += this.distribution.toString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static getCurrent(getRuntimeId: (platform: string, architecture: string, distribution: LinuxDistribution) => Runtime,
|
||||
extensionName: string): Promise<any> {
|
||||
let platform = os.platform();
|
||||
let architecturePromise: Promise<string>;
|
||||
let distributionPromise: Promise<LinuxDistribution>;
|
||||
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
architecturePromise = PlatformInformation.getWindowsArchitecture();
|
||||
distributionPromise = Promise.resolve(undefined);
|
||||
break;
|
||||
|
||||
case 'darwin':
|
||||
let osVersion = os.release();
|
||||
if (parseFloat(osVersion) < 16.0 && extensionName === 'mssql') {
|
||||
return Promise.reject('The current version of macOS is not supported. Only macOS Sierra and above (>= 10.12) are supported.');
|
||||
}
|
||||
architecturePromise = PlatformInformation.getUnixArchitecture();
|
||||
distributionPromise = Promise.resolve(undefined);
|
||||
break;
|
||||
|
||||
case 'linux':
|
||||
architecturePromise = PlatformInformation.getUnixArchitecture();
|
||||
distributionPromise = LinuxDistribution.getCurrent();
|
||||
break;
|
||||
|
||||
default:
|
||||
return Promise.reject(`Unsupported platform: ${platform}`);
|
||||
}
|
||||
|
||||
return architecturePromise.then(arch => {
|
||||
return distributionPromise.then(distro => {
|
||||
return new PlatformInformation(platform, arch, distro, getRuntimeId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static getWindowsArchitecture(): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
// try to get the architecture from WMIC
|
||||
PlatformInformation.getWindowsArchitectureWmic().then(architecture => {
|
||||
if (architecture && architecture !== unknown) {
|
||||
resolve(architecture);
|
||||
} else {
|
||||
// sometimes WMIC isn't available on the path so then try to parse the envvar
|
||||
PlatformInformation.getWindowsArchitectureEnv().then(architecture => {
|
||||
resolve(architecture);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static getWindowsArchitectureWmic(): Promise<string> {
|
||||
return this.execChildProcess('wmic os get osarchitecture')
|
||||
.then(architecture => {
|
||||
if (architecture) {
|
||||
let archArray: string[] = architecture.split(os.EOL);
|
||||
if (archArray.length >= 2) {
|
||||
let arch = archArray[1].trim();
|
||||
|
||||
// Note: This string can be localized. So, we'll just check to see if it contains 32 or 64.
|
||||
if (arch.indexOf('64') >= 0) {
|
||||
return 'x86_64';
|
||||
} else if (arch.indexOf('32') >= 0) {
|
||||
return 'x86';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unknown;
|
||||
}).catch((error) => {
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
private static getWindowsArchitectureEnv(): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
if (process.env.PROCESSOR_ARCHITECTURE === 'x86' && process.env.PROCESSOR_ARCHITEW6432 === undefined) {
|
||||
resolve('x86');
|
||||
}
|
||||
else {
|
||||
resolve('x86_64');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static getUnixArchitecture(): Promise<string> {
|
||||
return this.execChildProcess('uname -m')
|
||||
.then(architecture => {
|
||||
if (architecture) {
|
||||
return architecture.trim();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private static execChildProcess(process: string): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
child_process.exec(process, { maxBuffer: 500 * 1024 }, (error: Error, stdout: string, stderr: string) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stderr && stderr.length > 0) {
|
||||
reject(new Error(stderr));
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(stdout);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There is no standard way on Linux to find the distribution name and version.
|
||||
* Recently, systemd has pushed to standardize the os-release file. This has
|
||||
* seen adoption in "recent" versions of all major distributions.
|
||||
* https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||
*/
|
||||
export class LinuxDistribution {
|
||||
public constructor(
|
||||
public name: string,
|
||||
public version: string,
|
||||
public idLike?: string[]) { }
|
||||
|
||||
public static getCurrent(): Promise<LinuxDistribution> {
|
||||
// Try /etc/os-release and fallback to /usr/lib/os-release per the synopsis
|
||||
// at https://www.freedesktop.org/software/systemd/man/os-release.html.
|
||||
return LinuxDistribution.fromFilePath('/etc/os-release')
|
||||
.catch(() => LinuxDistribution.fromFilePath('/usr/lib/os-release'))
|
||||
.catch(() => Promise.resolve(new LinuxDistribution(unknown, unknown)));
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `name=${this.name}, version=${this.version}`;
|
||||
}
|
||||
|
||||
private static fromFilePath(filePath: string): Promise<LinuxDistribution> {
|
||||
return new Promise<LinuxDistribution>((resolve, reject) => {
|
||||
fs.readFile(filePath, 'utf8', (error, data) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(LinuxDistribution.fromReleaseInfo(data));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static fromReleaseInfo(releaseInfo: string, eol: string = os.EOL): LinuxDistribution {
|
||||
let name = unknown;
|
||||
let version = unknown;
|
||||
let idLike: string[] = undefined;
|
||||
|
||||
const lines = releaseInfo.split(eol);
|
||||
for (let line of lines) {
|
||||
line = line.trim();
|
||||
|
||||
let equalsIndex = line.indexOf('=');
|
||||
if (equalsIndex >= 0) {
|
||||
let key = line.substring(0, equalsIndex);
|
||||
let value = line.substring(equalsIndex + 1);
|
||||
|
||||
// Strip quotes if necessary
|
||||
if (value.length > 1 && value.startsWith('"') && value.endsWith('"')) {
|
||||
value = value.substring(1, value.length - 1);
|
||||
} else if (value.length > 1 && value.startsWith('\'') && value.endsWith('\'')) {
|
||||
value = value.substring(1, value.length - 1);
|
||||
}
|
||||
|
||||
if (key === 'ID') {
|
||||
name = value;
|
||||
} else if (key === 'VERSION_ID') {
|
||||
version = value;
|
||||
} else if (key === 'ID_LIKE') {
|
||||
idLike = value.split(' ');
|
||||
}
|
||||
|
||||
if (name !== unknown && version !== unknown && idLike !== undefined) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new LinuxDistribution(name, version, idLike);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as crypto from 'crypto';
|
||||
import * as os from 'os';
|
||||
import vscode = require('vscode');
|
||||
import { Constants } from './constants';
|
||||
import { ExtensionContext } from 'vscode';
|
||||
var path = require('path');
|
||||
|
||||
export namespace Utils {
|
||||
// INTERFACES /////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Interface for package.json information
|
||||
export interface IPackageInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
aiKey: string;
|
||||
}
|
||||
|
||||
// FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Get information from the extension's package.json file
|
||||
export function getPackageInfo(context: ExtensionContext): IPackageInfo {
|
||||
let extensionPackage = require(context.asAbsolutePath('./package.json'));
|
||||
if (extensionPackage) {
|
||||
return {
|
||||
name: extensionPackage.name,
|
||||
version: extensionPackage.version,
|
||||
aiKey: extensionPackage.aiKey
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a new GUID
|
||||
export function generateGuid(): string {
|
||||
let hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
|
||||
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
|
||||
let oct: string = '';
|
||||
let tmp: number;
|
||||
/* tslint:disable:no-bitwise */
|
||||
for (let a: number = 0; a < 4; a++) {
|
||||
tmp = (4294967296 * Math.random()) | 0;
|
||||
oct += hexValues[tmp & 0xF] +
|
||||
hexValues[tmp >> 4 & 0xF] +
|
||||
hexValues[tmp >> 8 & 0xF] +
|
||||
hexValues[tmp >> 12 & 0xF] +
|
||||
hexValues[tmp >> 16 & 0xF] +
|
||||
hexValues[tmp >> 20 & 0xF] +
|
||||
hexValues[tmp >> 24 & 0xF] +
|
||||
hexValues[tmp >> 28 & 0xF];
|
||||
}
|
||||
|
||||
// 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively'
|
||||
let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0];
|
||||
return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12);
|
||||
/* tslint:enable:no-bitwise */
|
||||
}
|
||||
|
||||
// Generate a unique, deterministic ID for the current user of the extension
|
||||
export function generateUserId(): Promise<string> {
|
||||
return new Promise<string>(resolve => {
|
||||
try {
|
||||
let interfaces = os.networkInterfaces();
|
||||
let mac;
|
||||
for (let key of Object.keys(interfaces)) {
|
||||
let item = interfaces[key][0];
|
||||
if (!item.internal) {
|
||||
mac = item.mac;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mac) {
|
||||
resolve(crypto.createHash('sha256').update(mac + os.homedir(), 'utf8').digest('hex'));
|
||||
} else {
|
||||
resolve(generateGuid());
|
||||
}
|
||||
} catch (err) {
|
||||
resolve(generateGuid()); // fallback
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Retrieve the URI for the currently open file if there is one; otherwise return the empty string
|
||||
export function getActiveTextEditorUri(): string {
|
||||
if (typeof vscode.window.activeTextEditor !== 'undefined' &&
|
||||
typeof vscode.window.activeTextEditor.document !== 'undefined') {
|
||||
return vscode.window.activeTextEditor.document.uri.toString();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// Helper to log debug messages
|
||||
export function logDebug(msg: any, extensionConfigSectionName: string): void {
|
||||
let config = vscode.workspace.getConfiguration(extensionConfigSectionName);
|
||||
let logDebugInfo = config[Constants.configLogDebugInfo];
|
||||
if (logDebugInfo === true) {
|
||||
let currentTime = new Date().toLocaleTimeString();
|
||||
let outputMsg = '[' + currentTime + ']: ' + msg ? msg.toString() : '';
|
||||
console.log(outputMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to show an error message
|
||||
export function showErrorMsg(msg: string, extensionName: string): void {
|
||||
vscode.window.showErrorMessage(extensionName + ': ' + msg);
|
||||
}
|
||||
|
||||
export function isEmpty(str: any): boolean {
|
||||
return (!str || '' === str);
|
||||
}
|
||||
|
||||
// The function is a duplicate of \src\paths.js. IT would be better to import path.js but it doesn't
|
||||
// work for now because the extension is running in different process.
|
||||
export function getAppDataPath() {
|
||||
var platform = process.platform;
|
||||
switch (platform) {
|
||||
case 'win32': return process.env['APPDATA'] || path.join(process.env['USERPROFILE'], 'AppData', 'Roaming');
|
||||
case 'darwin': return path.join(os.homedir(), 'Library', 'Application Support');
|
||||
case 'linux': return process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
|
||||
default: throw new Error('Platform not supported');
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultLogLocation() {
|
||||
return path.join(getAppDataPath(), 'sqlops');
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import vscode = require('vscode');
|
||||
import { Utils } from '../models/utils';
|
||||
|
||||
// Status bar element for each file in the editor
|
||||
class FileStatusBar {
|
||||
// Item for the connection status
|
||||
public statusConnection: vscode.StatusBarItem;
|
||||
|
||||
// Item for the query status
|
||||
public statusQuery: vscode.StatusBarItem;
|
||||
|
||||
// Item for language service status
|
||||
public statusLanguageService: vscode.StatusBarItem;
|
||||
|
||||
// Timer used for displaying a progress indicator on queries
|
||||
public progressTimerId: NodeJS.Timer;
|
||||
|
||||
public currentLanguageServiceStatus: string;
|
||||
}
|
||||
|
||||
export default class StatusView implements vscode.Disposable {
|
||||
private _statusBars: { [fileUri: string]: FileStatusBar };
|
||||
private _lastShownStatusBar: FileStatusBar;
|
||||
|
||||
constructor() {
|
||||
this._statusBars = {};
|
||||
vscode.window.onDidChangeActiveTextEditor((params) => this.onDidChangeActiveTextEditor(params));
|
||||
vscode.workspace.onDidCloseTextDocument((params) => this.onDidCloseTextDocument(params));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
for (let bar in this._statusBars) {
|
||||
if (this._statusBars.hasOwnProperty(bar)) {
|
||||
this._statusBars[bar].statusConnection.dispose();
|
||||
this._statusBars[bar].statusQuery.dispose();
|
||||
this._statusBars[bar].statusLanguageService.dispose();
|
||||
clearInterval(this._statusBars[bar].progressTimerId);
|
||||
delete this._statusBars[bar];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create status bar item if needed
|
||||
private createStatusBar(fileUri: string): void {
|
||||
let bar = new FileStatusBar();
|
||||
bar.statusConnection = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
|
||||
bar.statusQuery = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
|
||||
bar.statusLanguageService = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
|
||||
this._statusBars[fileUri] = bar;
|
||||
}
|
||||
|
||||
private destroyStatusBar(fileUri: string): void {
|
||||
let bar = this._statusBars[fileUri];
|
||||
if (bar) {
|
||||
if (bar.statusConnection) {
|
||||
bar.statusConnection.dispose();
|
||||
}
|
||||
if (bar.statusQuery) {
|
||||
bar.statusQuery.dispose();
|
||||
}
|
||||
if (bar.statusLanguageService) {
|
||||
bar.statusLanguageService.dispose();
|
||||
}
|
||||
if (bar.progressTimerId) {
|
||||
clearInterval(bar.progressTimerId);
|
||||
}
|
||||
|
||||
delete this._statusBars[fileUri];
|
||||
}
|
||||
}
|
||||
|
||||
private getStatusBar(fileUri: string): FileStatusBar {
|
||||
if (!(fileUri in this._statusBars)) {
|
||||
// Create it if it does not exist
|
||||
this.createStatusBar(fileUri);
|
||||
}
|
||||
|
||||
let bar = this._statusBars[fileUri];
|
||||
if (bar.progressTimerId) {
|
||||
clearInterval(bar.progressTimerId);
|
||||
}
|
||||
return bar;
|
||||
}
|
||||
|
||||
public languageServiceStatusChanged(fileUri: string, status: string): void {
|
||||
let bar = this.getStatusBar(fileUri);
|
||||
bar.currentLanguageServiceStatus = status;
|
||||
this.updateStatusMessage(status,
|
||||
() => { return bar.currentLanguageServiceStatus; }, (message) => {
|
||||
bar.statusLanguageService.text = message;
|
||||
this.showStatusBarItem(fileUri, bar.statusLanguageService);
|
||||
});
|
||||
}
|
||||
|
||||
public updateStatusMessage(
|
||||
newStatus: string,
|
||||
getCurrentStatus: () => string,
|
||||
updateMessage: (message: string) => void): void {
|
||||
}
|
||||
|
||||
private hideLastShownStatusBar(): void {
|
||||
if (typeof this._lastShownStatusBar !== 'undefined') {
|
||||
this._lastShownStatusBar.statusConnection.hide();
|
||||
this._lastShownStatusBar.statusQuery.hide();
|
||||
this._lastShownStatusBar.statusLanguageService.hide();
|
||||
}
|
||||
}
|
||||
|
||||
private onDidChangeActiveTextEditor(editor: vscode.TextEditor): void {
|
||||
// Hide the most recently shown status bar
|
||||
this.hideLastShownStatusBar();
|
||||
|
||||
// Change the status bar to match the open file
|
||||
if (typeof editor !== 'undefined') {
|
||||
const fileUri = editor.document.uri.toString();
|
||||
const bar = this._statusBars[fileUri];
|
||||
if (bar) {
|
||||
this.showStatusBarItem(fileUri, bar.statusConnection);
|
||||
this.showStatusBarItem(fileUri, bar.statusLanguageService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onDidCloseTextDocument(doc: vscode.TextDocument): void {
|
||||
// Remove the status bar associated with the document
|
||||
this.destroyStatusBar(doc.uri.toString());
|
||||
}
|
||||
|
||||
private showStatusBarItem(fileUri: string, statusBarItem: vscode.StatusBarItem): void {
|
||||
let currentOpenFile = Utils.getActiveTextEditorUri();
|
||||
|
||||
// Only show the status bar if it matches the currently open file and is not empty
|
||||
if (fileUri === currentOpenFile && !Utils.isEmpty(statusBarItem.text)) {
|
||||
statusBarItem.show();
|
||||
if (fileUri in this._statusBars) {
|
||||
this._lastShownStatusBar = this._statusBars[fileUri];
|
||||
}
|
||||
} else {
|
||||
statusBarItem.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"adal-node": "0.1.25",
|
||||
"request": "2.63.0",
|
||||
"vscode-nls": "2.0.2"
|
||||
"vscode-nls": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^8.0.24"
|
||||
@@ -19,8 +19,8 @@
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "extension.clearTokenCache",
|
||||
"title": "%extension.clearTokenCache%",
|
||||
"command": "accounts.clearTokenCache",
|
||||
"title": "%accounts.clearTokenCache%",
|
||||
"category": "Azure Accounts"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extension.clearTokenCache": "Clear Azure Account Token Cache",
|
||||
"accounts.clearTokenCache": "Clear Azure Account Token Cache",
|
||||
"config.enablePublicCloudDescription": "Should Azure public cloud integration be enabled",
|
||||
"config.enableUsGovCloudDescription": "Should US Government Azure cloud (Fairfax) integration be enabled",
|
||||
"config.enableChinaCloudDescription": "Should Azure China integration be enabled",
|
||||
|
||||
@@ -20,7 +20,7 @@ let localize = nls.loadMessageBundle();
|
||||
|
||||
export class AzureAccountProviderService implements vscode.Disposable {
|
||||
// CONSTANTS ///////////////////////////////////////////////////////////////
|
||||
private static CommandClearTokenCache = 'extension.clearTokenCache';
|
||||
private static CommandClearTokenCache = 'accounts.azure.clearTokenCache';
|
||||
private static ConfigurationSection = 'accounts.azure';
|
||||
private static CredentialNamespace = 'azureAccountProviderCredentials';
|
||||
|
||||
|
||||
@@ -591,9 +591,9 @@ verror@1.10.0:
|
||||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
vscode-nls@2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-2.0.2.tgz#808522380844b8ad153499af5c3b03921aea02da"
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.2.tgz#3817eca5b985c2393de325197cf4e15eb2aa5350"
|
||||
|
||||
"xmldom@>= 0.1.x":
|
||||
version "0.1.27"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "agent",
|
||||
"displayName": "SQL Server Agent",
|
||||
"description": "Manage and troubleshoot SQL Server Agent jobs (early preview)",
|
||||
"version": "0.27.1",
|
||||
"version": "0.28.1",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
||||
@@ -32,6 +32,7 @@
|
||||
"description": "Manage and troubleshoot SQL Agent jobs",
|
||||
"provider": "MSSQL",
|
||||
"title": "SQL Agent",
|
||||
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
|
||||
"container": {
|
||||
"controlhost-container": {
|
||||
"type": "agent"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"name": "bat",
|
||||
"version": "0.1.0",
|
||||
"displayName": "%displayName%",
|
||||
"description": "%description%",
|
||||
"version": "1.0.0",
|
||||
"publisher": "vscode",
|
||||
"engines": { "vscode": "*" },
|
||||
"scripts": {
|
||||
|
||||
4
extensions/bat/package.nls.json
Normal file
4
extensions/bat/package.nls.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"displayName": "Windows Bat Language Basics",
|
||||
"description": "Provides snippets, syntax highlighting, bracket matching and folding in Windows batch files."
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>uuid</key>
|
||||
<string>E07EC438-7B75-4437-8AA1-DA94C1E6EACC</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.command.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>\b(?i)(?:append|assoc|at|attrib|break|cacls|cd|chcp|chdir|chkdsk|chkntfs|cls|cmd|color|comp|compact|convert|copy|date|del|dir|diskcomp|diskcopy|doskey|echo|endlocal|erase|fc|find|findstr|format|ftype|graftabl|help|keyb|label|md|mkdir|mode|more|move|path|pause|popd|print|prompt|pushd|rd|recover|ren|rename|replace|restore|rmdir|set|setlocal|shift|sort|start|subst|time|title|tree|type|ver|verify|vol|xcopy)\b</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.control.statement.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>\b(?i)(?:goto|call|exit)\b</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.control.conditional.if.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>\b(?i)if\s+((not)\s+)(exist|defined|errorlevel|cmdextversion)\b</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.control.conditional.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>\b(?i)(?:if|else)\b</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.control.repeat.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>\b(?i)for\b</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>\b(?:EQU|NEQ|LSS|LEQ|GTR|GEQ)\b</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>comment.line.rem.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>\b(?i)rem(?:$|\s.*$)</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>comment.line.colons.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>\s*:\s*:.*$</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>variable.parameter.function.begin.shell</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>variable.parameter.function.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>(?i)(%)(~(?:f|d|p|n|x|s|a|t|z|\$[^:]*:)*)?\d</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>variable.other.parsetime.begin.shell</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>variable.other.parsetime.end.shell</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>variable.other.parsetime.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>(%)[^%]+(%)|(%%)[^%]+(%%)</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>variable.parameter.loop.begin.shell</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>variable.parameter.loop.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>(?i)(%%)(~(?:f|d|p|n|x|s|a|t|z|\$[^:]*:)*)?[a-z]</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>variable.other.delayed.begin.shell</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>variable.other.delayed.end.shell</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>variable.other.delayed.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>(!)[^!]+(!)</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>"</string>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.shell</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.shell</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>string.quoted.double.dosbatch</string>
|
||||
<key>end</key>
|
||||
<string>"|$</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.pipe.dosbatch</string>
|
||||
<key>match</key>
|
||||
<string>[|]</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.redirect.shell</string>
|
||||
<key>match</key>
|
||||
<string>&>|\d*>&\d*|\d*(>>|>|<)|\d*<&|\d*<></string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>name</key>
|
||||
<string>Batch File</string>
|
||||
<key>scopeName</key>
|
||||
<string>source.dosbatch</string>
|
||||
<key>fileTypes</key>
|
||||
<array>
|
||||
<string>bat</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -4,13 +4,9 @@
|
||||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/mmims/language-batchfile/commit/40b605c75db3967a24b7015f6d3a885360b84e28",
|
||||
"scopeName": "source.batchfile",
|
||||
"version": "https://github.com/mmims/language-batchfile/commit/3dd105c31484e5975144478dac1aa91d8f51e528",
|
||||
"name": "Batch File",
|
||||
"fileTypes": [
|
||||
"bat",
|
||||
"cmd"
|
||||
],
|
||||
"scopeName": "source.batchfile",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#commands"
|
||||
@@ -454,11 +450,15 @@
|
||||
{
|
||||
"begin": "\\(",
|
||||
"beginCaptures": {
|
||||
"0": "punctuation.section.group.begin.batchfile"
|
||||
"0": {
|
||||
"name": "punctuation.section.group.begin.batchfile"
|
||||
}
|
||||
},
|
||||
"end": "\\)",
|
||||
"endCaptures": {
|
||||
"0": "punctuation.section.group.end.batchfile"
|
||||
"0": {
|
||||
"name": "punctuation.section.group.end.batchfile"
|
||||
}
|
||||
},
|
||||
"name": "meta.group.batchfile",
|
||||
"patterns": [
|
||||
|
||||
15
extensions/configuration-editing/npm-shrinkwrap.json
generated
15
extensions/configuration-editing/npm-shrinkwrap.json
generated
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "configuration-editing",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"jsonc-parser": {
|
||||
"version": "0.3.1",
|
||||
"from": "jsonc-parser@0.3.1"
|
||||
},
|
||||
"vscode-nls": {
|
||||
"version": "2.0.2",
|
||||
"from": "vscode-nls@>=2.0.2 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"name": "configuration-editing",
|
||||
"version": "0.0.1",
|
||||
"displayName": "%displayName%",
|
||||
"description": "%description%",
|
||||
"version": "1.0.0",
|
||||
"publisher": "vscode",
|
||||
"engines": {
|
||||
"vscode": "^1.0.0"
|
||||
@@ -10,7 +12,8 @@
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:json", "onLanguage:jsonc"
|
||||
"onLanguage:json",
|
||||
"onLanguage:jsonc"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"scripts": {
|
||||
@@ -18,8 +21,8 @@
|
||||
"watch": "gulp watch-extension:configuration-editing"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonc-parser": "^0.3.1",
|
||||
"vscode-nls": "^2.0.1"
|
||||
"jsonc-parser": "^1.0.0",
|
||||
"vscode-nls": "^3.2.1"
|
||||
},
|
||||
"contributes": {
|
||||
"jsonValidation": [
|
||||
@@ -67,6 +70,10 @@
|
||||
"fileMatch": "%APP_SETTINGS_HOME%/snippets/*.json",
|
||||
"url": "vscode://schemas/snippets"
|
||||
},
|
||||
{
|
||||
"fileMatch": "**/*.code-snippets",
|
||||
"url": "vscode://schemas/global-snippets"
|
||||
},
|
||||
{
|
||||
"fileMatch": "/.vscode/extensions.json",
|
||||
"url": "vscode://schemas/extensions"
|
||||
@@ -76,4 +83,4 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "7.0.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
4
extensions/configuration-editing/package.nls.json
Normal file
4
extensions/configuration-editing/package.nls.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"displayName": "Configuration Editing",
|
||||
"description": "Provides capabilities (advanced IntelliSense, auto-fixing) in configuration files like settings, launch and extension recommendation files."
|
||||
}
|
||||
@@ -2,16 +2,14 @@
|
||||
* 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 * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
import * as vscode from 'vscode';
|
||||
import { getLocation, visit, parse } from 'jsonc-parser';
|
||||
import { getLocation, visit, parse, ParseErrorCode } from 'jsonc-parser';
|
||||
import * as path from 'path';
|
||||
import { SettingsDocument } from './settingsDocumentHelper';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
const decoration = vscode.window.createTextEditorDecorationType({
|
||||
color: '#9e9e9e'
|
||||
@@ -20,7 +18,6 @@ const decoration = vscode.window.createTextEditorDecorationType({
|
||||
let pendingLaunchJsonDecoration: NodeJS.Timer;
|
||||
|
||||
export function activate(context: vscode.ExtensionContext): void {
|
||||
|
||||
//keybindings.json command-suggestions
|
||||
context.subscriptions.push(registerKeybindingsCompletions());
|
||||
|
||||
@@ -41,6 +38,45 @@ export function activate(context: vscode.ExtensionContext): void {
|
||||
}
|
||||
}, null, context.subscriptions));
|
||||
updateLaunchJsonDecorations(vscode.window.activeTextEditor);
|
||||
|
||||
context.subscriptions.push(vscode.workspace.onWillSaveTextDocument(e => {
|
||||
if (!e.document.fileName.endsWith('/settings.json')) {
|
||||
return;
|
||||
}
|
||||
|
||||
autoFixSettingsJSON(e);
|
||||
}));
|
||||
}
|
||||
|
||||
function autoFixSettingsJSON(willSaveEvent: vscode.TextDocumentWillSaveEvent): void {
|
||||
const document = willSaveEvent.document;
|
||||
const text = document.getText();
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
|
||||
let lastEndOfSomething = -1;
|
||||
visit(text, {
|
||||
onArrayEnd(offset: number, length: number): void {
|
||||
lastEndOfSomething = offset + length;
|
||||
},
|
||||
|
||||
onLiteralValue(value: any, offset: number, length: number): void {
|
||||
lastEndOfSomething = offset + length;
|
||||
},
|
||||
|
||||
onObjectEnd(offset: number, length: number): void {
|
||||
lastEndOfSomething = offset + length;
|
||||
},
|
||||
|
||||
onError(error: ParseErrorCode, offset: number, length: number): void {
|
||||
if (error === ParseErrorCode.CommaExpected && lastEndOfSomething > -1) {
|
||||
const fixPosition = document.positionAt(lastEndOfSomething);
|
||||
edit.insert(document.uri, fixPosition, ',');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
willSaveEvent.waitUntil(
|
||||
vscode.workspace.applyEdit(edit));
|
||||
}
|
||||
|
||||
function registerKeybindingsCompletions(): vscode.Disposable {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user