Merge remote-tracking branch 'origin/master' into rebornix/notebook

This commit is contained in:
rebornix 2020-02-07 10:22:51 -08:00
commit c78a8696fc
591 changed files with 12305 additions and 8854 deletions

View file

@ -614,6 +614,17 @@
"*"
]
},
{
"target": "**/test/integration/**",
"restrictions": [
"**/test/integration/**",
"*"
]
},
{
"target": "{**/api/**.test.ts,}",
"restrictions": "{**/vs/**,assert,sinon,crypto,vscode}"
},
{
"target": "{**/**.test.ts,**/test/**}",
"restrictions": "{**/vs/**,assert,sinon,crypto}"

24
.vscode/launch.json vendored
View file

@ -79,6 +79,24 @@
"order": 6
}
},
{
"type": "extensionHost",
"request": "launch",
"name": "VS Code Git Tests",
"runtimeExecutable": "${execPath}",
"args": [
"/tmp/my4g9l",
"--extensionDevelopmentPath=${workspaceFolder}/extensions/git",
"--extensionTestsPath=${workspaceFolder}/extensions/git/out/test"
],
"outFiles": [
"${workspaceFolder}/extensions/git/out/**/*.js"
],
"presentation": {
"group": "5_tests",
"order": 6
}
},
{
"type": "extensionHost",
"request": "launch",
@ -167,7 +185,9 @@
"webRoot": "${workspaceFolder}",
// Settings for js-debug:
"pauseForSourceMap": false,
"outFiles": ["${workspaceFolder}/out/**/*.js"],
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
},
{
"type": "node",
@ -282,7 +302,7 @@
"type": "node",
"request": "launch",
"name": "Run Unit Tests",
"program": "${workspaceFolder}/test/electron/index.js",
"program": "${workspaceFolder}/test/unit/electron/index.js",
"runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Electron",
"windows": {
"runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.exe"

View file

@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron"
target "6.1.6"
target "7.1.11"
runtime "electron"

View file

@ -44,15 +44,16 @@ async function doesAssetExist(blobService: azure.BlobService, quality: string, b
return existsResult.exists;
}
async function uploadBlob(blobService: azure.BlobService, quality: string, blobName: string, file: string): Promise<void> {
async function uploadBlob(blobService: azure.BlobService, quality: string, blobName: string, filePath: string, fileName: string): Promise<void> {
const blobOptions: azure.BlobService.CreateBlockBlobRequestOptions = {
contentSettings: {
contentType: mime.lookup(file),
contentType: mime.lookup(filePath),
contentDisposition: `attachment; filename="${fileName}"`,
cacheControl: 'max-age=31536000, public'
}
};
await new Promise((c, e) => blobService.createBlockBlobFromLocalFile(quality, blobName, file, blobOptions, err => err ? e(err) : c()));
await new Promise((c, e) => blobService.createBlockBlobFromLocalFile(quality, blobName, filePath, blobOptions, err => err ? e(err) : c()));
}
function getEnv(name: string): string {
@ -66,24 +67,24 @@ function getEnv(name: string): string {
}
async function main(): Promise<void> {
const [, , platform, type, name, file] = process.argv;
const [, , platform, type, fileName, filePath] = process.argv;
const quality = getEnv('VSCODE_QUALITY');
const commit = getEnv('BUILD_SOURCEVERSION');
console.log('Creating asset...');
const stat = await new Promise<fs.Stats>((c, e) => fs.stat(file, (err, stat) => err ? e(err) : c(stat)));
const stat = await new Promise<fs.Stats>((c, e) => fs.stat(filePath, (err, stat) => err ? e(err) : c(stat)));
const size = stat.size;
console.log('Size:', size);
const stream = fs.createReadStream(file);
const stream = fs.createReadStream(filePath);
const [sha1hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]);
console.log('SHA1:', sha1hash);
console.log('SHA256:', sha256hash);
const blobName = commit + '/' + name;
const blobName = commit + '/' + fileName;
const storageAccount = process.env['AZURE_STORAGE_ACCOUNT_2']!;
const blobService = azure.createBlobService(storageAccount, process.env['AZURE_STORAGE_ACCESS_KEY_2']!)
@ -98,7 +99,7 @@ async function main(): Promise<void> {
console.log('Uploading blobs to Azure storage...');
await uploadBlob(blobService, quality, blobName, file);
await uploadBlob(blobService, quality, blobName, filePath, fileName);
console.log('Blobs successfully uploaded.');

View file

@ -41,6 +41,9 @@ steps:
- script: |
./scripts/test.sh --tfs "Unit Tests"
displayName: Run Unit Tests
- script: |
yarn test-browser --browser chromium --browser webkit
displayName: Run Unit Tests (Browsers)
- script: |
./scripts/test-integration.sh --tfs "Integration Tests"
displayName: Run Integration Tests

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

View file

@ -44,6 +44,13 @@ steps:
git config user.email "vscode@microsoft.com"
git config user.name "VSCode"
security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
security default-keychain -s $(agent.tempdirectory)/buildagent.keychain
security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12
security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain
displayName: Prepare tooling
- script: |
@ -127,7 +134,12 @@ steps:
- script: |
set -e
pushd ../VSCode-darwin && zip -r -X -y ../VSCode-darwin.zip * && popd
codesign -s 99FM488X57 --deep --force --options runtime --entitlements build/azure-pipelines/darwin/entitlements.plist $(agent.builddirectory)/VSCode-darwin/*.app
displayName: Set Hardened Entitlements
- script: |
set -e
pushd $(agent.builddirectory)/VSCode-darwin && zip -r -X -y $(agent.builddirectory)/VSCode-darwin.zip * && popd
displayName: Archive build
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
@ -141,13 +153,53 @@ steps:
{
"keyCode": "CP-401337-Apple",
"operationSetCode": "MacAppDeveloperSign",
"parameters": [ ],
"parameters": [
{
"parameterName": "Hardening",
"parameterValue": "--options=runtime"
}
],
"toolName": "sign",
"toolVersion": "1.0"
}
]
SessionTimeout: 60
displayName: Codesign
- script: |
zip -d $(agent.builddirectory)/VSCode-darwin.zip "*.pkg"
displayName: Clean Archive
- script: |
APP_ROOT=$(agent.builddirectory)/VSCode-darwin
APP_NAME="`ls $APP_ROOT | head -n 1`"
BUNDLE_IDENTIFIER=$(node -p "require(\"$APP_ROOT/$APP_NAME/Contents/Resources/app/product.json\").darwinBundleIdentifier")
echo "##vso[task.setvariable variable=BundleIdentifier]$BUNDLE_IDENTIFIER"
displayName: Export bundle identifier
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
inputs:
ConnectedServiceName: 'ESRP CodeSign'
FolderPath: '$(agent.builddirectory)'
Pattern: 'VSCode-darwin.zip'
signConfigType: inlineSignParams
inlineOperation: |
[
{
"keyCode": "CP-401337-Apple",
"operationSetCode": "MacAppNotarize",
"parameters": [
{
"parameterName": "BundleId",
"parameterValue": "$(BundleIdentifier)"
}
],
"toolName": "sign",
"toolVersion": "1.0"
}
]
SessionTimeout: 120
displayName: Codesign
displayName: Notarization
- script: |
set -e

View file

@ -1,9 +1,6 @@
#!/usr/bin/env bash
set -e
# remove pkg from archive
zip -d ../VSCode-darwin.zip "*.pkg"
# publish the build
node build/azure-pipelines/common/createAsset.js \
darwin \

View file

@ -49,6 +49,9 @@ steps:
- script: |
DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests"
displayName: Run Unit Tests
- script: |
DISPLAY=:10 yarn test-browser --browser chromium
displayName: Run Unit Tests (Browser)
- script: |
DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests"
displayName: Run Integration Tests

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EsrpClient" version="1.0.27" />
<package id="Microsoft.ESRPClient" version="1.2.25" />
</packages>

View file

@ -46,6 +46,9 @@ steps:
- powershell: |
.\scripts\test.bat --tfs "Unit Tests"
displayName: Run Unit Tests
- powershell: |
yarn test-browser --browser chromium --browser webkit
displayName: Run Unit Tests (Browser)
- powershell: |
.\scripts\test-integration.bat --tfs "Integration Tests"
displayName: Run Integration Tests

View file

@ -67,4 +67,4 @@ $Input = Create-TmpJson @{
$Output = [System.IO.Path]::GetTempFileName()
$ScriptPath = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
& "$ScriptPath\ESRPClient\packages\EsrpClient.1.0.27\tools\ESRPClient.exe" Sign -a $Auth -p $Policy -i $Input -o $Output
& "$ScriptPath\ESRPClient\packages\Microsoft.ESRPClient.1.2.25\tools\ESRPClient.exe" Sign -a $Auth -p $Policy -i $Input -o $Output

View file

@ -46,7 +46,7 @@
},
{
"name": "ms-vscode.js-debug-nightly",
"version": "2020.1.43263",
"version": "2020.2.517",
"forQualities": [
"insider"
],

View file

@ -227,8 +227,13 @@ function toExternalDTS(contents) {
if (line.indexOf('declare namespace monaco.') === 0) {
lines[i] = line.replace('declare namespace monaco.', 'export namespace ');
}
if (line.indexOf('declare let MonacoEnvironment') === 0) {
lines[i] = `declare global {\n let MonacoEnvironment: Environment | undefined;\n}`;
// lines[i] = line.replace('declare namespace monaco.', 'export namespace ');
}
}
return lines.join('\n');
return lines.join('\n').replace(/\n\n\n+/g, '\n\n');
}
function filterStream(testFunc) {

View file

@ -33,6 +33,7 @@ const all = [
'scripts/**/*',
'src/**/*',
'test/**/*',
'!test/**/out/**',
'!**/node_modules/**'
];
@ -53,7 +54,7 @@ const indentationFilter = [
'!src/vs/base/common/marked/marked.js',
'!src/vs/base/node/terminateProcess.sh',
'!src/vs/base/node/cpuUsage.sh',
'!test/assert.js',
'!test/unit/assert.js',
// except specific folders
'!test/automation/out/**',
@ -82,7 +83,7 @@ const indentationFilter = [
'!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,ico,icns}',
'!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}',
'!build/{lib,download}/**/*.js',
'!build/**/*.sh',
'!build/azure-pipelines/**/*.js',

View file

@ -146,6 +146,10 @@
"name": "vs/workbench/contrib/search",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/searchEditor",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/snippets",
"project": "vscode-workbench"

View file

@ -38,6 +38,7 @@ const CORE_TYPES = [
'group',
'groupEnd',
'table',
'assert',
'Error',
'String',
'throws',
@ -102,6 +103,14 @@ const RULES = [
'@types/node' // no node.js
]
},
// Browser (editor contrib)
{
target: '**/src/vs/editor/contrib/**',
allowedTypes: CORE_TYPES,
disallowedDefinitions: [
'@types/node' // no node.js
]
},
// node.js
{
target: '**/vs/**/node/**',

View file

@ -39,6 +39,7 @@ const CORE_TYPES = [
'group',
'groupEnd',
'table',
'assert',
'Error',
'String',
'throws',
@ -112,6 +113,15 @@ const RULES = [
]
},
// Browser (editor contrib)
{
target: '**/src/vs/editor/contrib/**',
allowedTypes: CORE_TYPES,
disallowedDefinitions: [
'@types/node' // no node.js
]
},
// node.js
{
target: '**/vs/**/node/**',

View file

@ -3,12 +3,18 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
declare let MonacoEnvironment: monaco.Environment | undefined;
declare namespace monaco {
// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.
export type Thenable<T> = PromiseLike<T>;
export interface Environment {
baseUrl?: string;
getWorker?(workerId: string, label: string): Worker;
getWorkerUrl?(workerId: string, label: string): string;
}
export interface IDisposable {
dispose(): void;
}

View file

@ -72,4 +72,5 @@ runtime "${runtime}"`;
yarnInstall(`build`); // node modules required for build
yarnInstall('test/automation'); // node modules required for smoketest
yarnInstall('test/smoke'); // node modules required for smoketest
yarnInstall('test/integration/browser'); // node modules required for integration
yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron

View file

@ -6,7 +6,7 @@
"git": {
"name": "chromium",
"repositoryUrl": "https://chromium.googlesource.com/chromium/src",
"commitHash": "91f08db83c2ce8c722ddf0911ead8f7c473bedfa"
"commitHash": "e4745133a1d3745f066e068b8033c6a269b59caf"
}
},
"licenseDetail": [
@ -40,7 +40,7 @@
"SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
],
"isOnlyProductionDependency": true,
"version": "76.0.3809.146"
"version": "78.0.3904.130"
},
{
"component": {
@ -48,11 +48,11 @@
"git": {
"name": "nodejs",
"repositoryUrl": "https://github.com/nodejs/node",
"commitHash": "64219741218aa87e259cf8257596073b8e747f0a"
"commitHash": "787378879acfb212ed4ff824bf9f767a24a5cb43a"
}
},
"isOnlyProductionDependency": true,
"version": "12.4.0"
"version": "12.8.1"
},
{
"component": {
@ -60,12 +60,12 @@
"git": {
"name": "electron",
"repositoryUrl": "https://github.com/electron/electron",
"commitHash": "19c705ab80cd6fdccca3d65803ec2c4addb9540a"
"commitHash": "d17dfabfcba7bd0bc994b8dac5f5d2000bef572c"
}
},
"isOnlyProductionDependency": true,
"license": "MIT",
"version": "6.1.6"
"version": "7.1.11"
},
{
"component": {

View file

@ -29,7 +29,9 @@
"type": "array",
"description": "Ports that are forwarded from the container to the local machine.",
"items": {
"type": "integer"
"type": "integer",
"maximum": 65535,
"minimum": 0
}
},
"remoteEnv": {

View file

@ -1780,6 +1780,9 @@
"@types/mocha": "2.2.43",
"@types/node": "^12.11.7",
"@types/which": "^1.0.28",
"mocha": "^3.2.0"
"mocha": "^3.2.0",
"mocha-junit-reporter": "^1.23.3",
"mocha-multi-reporters": "^1.1.7",
"vscode": "^1.1.36"
}
}

View file

@ -5,7 +5,7 @@
import { Model } from '../model';
import { Repository as BaseRepository, Resource } from '../repository';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState } from './git';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions } from './git';
import { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode';
import { mapEvent } from '../util';
import { toGitUri } from '../uri';
@ -202,6 +202,10 @@ export class ApiRepository implements Repository {
log(options?: LogOptions): Promise<Commit[]> {
return this._repository.log(options);
}
commit(message: string, opts?: CommitOptions): Promise<void> {
return this._repository.commit(message, opts);
}
}
export class ApiGit implements Git {

View file

@ -121,6 +121,14 @@ export interface LogOptions {
readonly maxEntries?: number;
}
export interface CommitOptions {
all?: boolean | 'tracked';
amend?: boolean;
signoff?: boolean;
signCommit?: boolean;
empty?: boolean;
}
export interface Repository {
readonly rootUri: Uri;
@ -176,6 +184,8 @@ export interface Repository {
blame(path: string): Promise<string>;
log(options?: LogOptions): Promise<Commit[]>;
commit(message: string, opts?: CommitOptions): Promise<void>;
}
export type APIState = 'uninitialized' | 'initialized';

View file

@ -9,8 +9,8 @@ import * as path from 'path';
import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder } from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry';
import * as nls from 'vscode-nls';
import { Branch, GitErrorCodes, Ref, RefType, Status } from './api/git';
import { CommitOptions, ForcePushMode, Git, Stash } from './git';
import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions } from './api/git';
import { ForcePushMode, Git, Stash } from './git';
import { Model } from './model';
import { Repository, Resource, ResourceGroupType } from './repository';
import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging';
@ -1620,7 +1620,7 @@ export class CommandCenter {
const rawBranchName = defaultName || await window.showInputBox({
placeHolder: localize('branch name', "Branch name"),
prompt: localize('provide branch name', "Please provide a branch name"),
prompt: localize('provide branch name', "Please provide a new branch name"),
value: initialValue,
ignoreFocusOut: true,
validateInput: (name: string) => {
@ -2332,14 +2332,20 @@ export class CommandCenter {
}
@command('git.openDiff', { repository: false })
async openDiff(uri: Uri, hash: string) {
async openDiff(uri: Uri, lhs: string, rhs: string) {
const basename = path.basename(uri.fsPath);
if (hash === '~') {
return commands.executeCommand('vscode.diff', toGitUri(uri, hash), toGitUri(uri, `HEAD`), `${basename} (Index)`);
let title;
if ((lhs === 'HEAD' || lhs === '~') && rhs === '') {
title = `${basename} (Working Tree)`;
}
else if (lhs === 'HEAD' && rhs === '~') {
title = `${basename} (Index)`;
} else {
title = `${basename} (${lhs.endsWith('^') ? `${lhs.substr(0, 8)}^` : lhs.substr(0, 8)}) \u27f7 ${basename} (${rhs.endsWith('^') ? `${rhs.substr(0, 8)}^` : rhs.substr(0, 8)})`;
}
return commands.executeCommand('vscode.diff', toGitUri(uri, `${hash}^`), toGitUri(uri, hash), `${basename} (${hash.substr(0, 8)}^) \u27f7 ${basename} (${hash.substr(0, 8)})`);
return commands.executeCommand('vscode.diff', toGitUri(uri, lhs), rhs === '' ? uri : toGitUri(uri, rhs), title);
}
private createCommand(id: string, key: string, method: Function, options: CommandOptions): (...args: any[]) => any {

View file

@ -9,6 +9,7 @@ import { fromGitUri, toGitUri } from './uri';
import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model';
import { filterEvent, eventToPromise, isDescendant, pathEquals, EmptyDisposable } from './util';
import { Repository } from './repository';
import { TextEncoder } from 'util';
interface CacheRow {
uri: Uri;

View file

@ -15,7 +15,7 @@ import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes,
import { CancellationToken, Progress, Uri } from 'vscode';
import { URI } from 'vscode-uri';
import { detectEncoding } from './encoding';
import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status } from './api/git';
import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status, CommitOptions } from './api/git';
import * as byline from 'byline';
import { StringDecoder } from 'string_decoder';
@ -725,14 +725,6 @@ export function parseLsFiles(raw: string): LsFilesElement[] {
.map(([, mode, object, stage, file]) => ({ mode, object, stage, file }));
}
export interface CommitOptions {
all?: boolean | 'tracked';
amend?: boolean;
signoff?: boolean;
signCommit?: boolean;
empty?: boolean;
}
export interface PullOptions {
unshallow?: boolean;
tags?: boolean;

View file

@ -7,10 +7,10 @@ import * as fs from 'fs';
import * as path from 'path';
import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, Decoration } from 'vscode';
import * as nls from 'vscode-nls';
import { Branch, Change, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status } from './api/git';
import { Branch, Change, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions } from './api/git';
import { AutoFetcher } from './autofetch';
import { debounce, memoize, throttle } from './decorators';
import { Commit, CommitOptions, ForcePushMode, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git';
import { Commit, ForcePushMode, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git';
import { StatusBarCommands } from './statusbar';
import { toGitUri } from './uri';
import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent } from './util';
@ -309,11 +309,17 @@ export const enum Operation {
function isReadOnly(operation: Operation): boolean {
switch (operation) {
case Operation.Show:
case Operation.GetCommitTemplate:
case Operation.Blame:
case Operation.CheckIgnore:
case Operation.Diff:
case Operation.FindTrackingBranches:
case Operation.GetBranch:
case Operation.GetCommitTemplate:
case Operation.GetObjectDetails:
case Operation.Log:
case Operation.LogFile:
case Operation.MergeBase:
case Operation.Show:
return true;
default:
return false;

View file

@ -189,7 +189,9 @@ suite('git', () => {
suite('parseGitCommit', () => {
test('single parent commit', function () {
const GIT_OUTPUT_SINGLE_PARENT = `52c293a05038d865604c2284aa8698bd087915a1
John Doe
john.doe@mail.com
1580811030
8e5a374372b8393906c7e380dbb09349c5385554
This is a commit message.\x00`;
@ -197,13 +199,17 @@ This is a commit message.\x00`;
hash: '52c293a05038d865604c2284aa8698bd087915a1',
message: 'This is a commit message.',
parents: ['8e5a374372b8393906c7e380dbb09349c5385554'],
authorDate: new Date(1580811030000),
authorName: 'John Doe',
authorEmail: 'john.doe@mail.com',
}]);
});
test('multiple parent commits', function () {
const GIT_OUTPUT_MULTIPLE_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1
John Doe
john.doe@mail.com
1580811030
8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217
This is a commit message.\x00`;
@ -211,13 +217,17 @@ This is a commit message.\x00`;
hash: '52c293a05038d865604c2284aa8698bd087915a1',
message: 'This is a commit message.',
parents: ['8e5a374372b8393906c7e380dbb09349c5385554', 'df27d8c75b129ab9b178b386077da2822101b217'],
authorDate: new Date(1580811030000),
authorName: 'John Doe',
authorEmail: 'john.doe@mail.com',
}]);
});
test('no parent commits', function () {
const GIT_OUTPUT_NO_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1
John Doe
john.doe@mail.com
1580811030
This is a commit message.\x00`;
@ -225,6 +235,8 @@ This is a commit message.\x00`;
hash: '52c293a05038d865604c2284aa8698bd087915a1',
message: 'This is a commit message.',
parents: [],
authorDate: new Date(1580811030000),
authorName: 'John Doe',
authorEmail: 'john.doe@mail.com',
}]);
});

View file

@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path = require('path');
const testRunner = require('vscode/lib/testrunner');
const suite = 'Integration Git Tests';
const options: any = {
ui: 'tdd',
useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'),
timeout: 60000
};
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
options.reporter = 'mocha-multi-reporters';
options.reporterOptions = {
reporterEnabled: 'spec, mocha-junit-reporter',
mochaJunitReporterReporterOptions: {
testsuitesTitle: `${suite} ${process.platform}`,
mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`)
}
};
}
testRunner.configure(options);
export = testRunner;

View file

@ -0,0 +1,127 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'mocha';
import * as assert from 'assert';
import { workspace, commands, window, Uri, WorkspaceEdit, Range, TextDocument, extensions } from 'vscode';
import * as cp from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { GitExtension, API, Repository, Status } from '../api/git';
import { eventToPromise } from '../util';
suite('git smoke test', function () {
const cwd = fs.realpathSync(workspace.workspaceFolders![0].uri.fsPath);
function file(relativePath: string) {
return path.join(cwd, relativePath);
}
function uri(relativePath: string) {
return Uri.file(file(relativePath));
}
async function open(relativePath: string) {
const doc = await workspace.openTextDocument(uri(relativePath));
await window.showTextDocument(doc);
return doc;
}
async function type(doc: TextDocument, text: string) {
const edit = new WorkspaceEdit();
const end = doc.lineAt(doc.lineCount - 1).range.end;
edit.replace(doc.uri, new Range(end, end), text);
await workspace.applyEdit(edit);
}
let git: API;
let repository: Repository;
suiteSetup(async function () {
fs.writeFileSync(file('app.js'), 'hello', 'utf8');
fs.writeFileSync(file('index.pug'), 'hello', 'utf8');
cp.execSync('git init', { cwd });
cp.execSync('git config user.name testuser', { cwd });
cp.execSync('git config user.email monacotools@microsoft.com', { cwd });
cp.execSync('git add .', { cwd });
cp.execSync('git commit -m "initial commit"', { cwd });
// make sure git is activated
const ext = extensions.getExtension<GitExtension>('vscode.git');
await ext?.activate();
git = ext!.exports.getAPI(1);
if (git.repositories.length === 0) {
await eventToPromise(git.onDidOpenRepository);
}
assert.equal(git.repositories.length, 1);
assert.equal(fs.realpathSync(git.repositories[0].rootUri.fsPath), cwd);
repository = git.repositories[0];
});
test('reflects working tree changes', async function () {
await commands.executeCommand('workbench.view.scm');
const appjs = await open('app.js');
await type(appjs, ' world');
await appjs.save();
await repository.status();
assert.equal(repository.state.workingTreeChanges.length, 1);
repository.state.workingTreeChanges.some(r => r.uri.path === appjs.uri.path && r.status === Status.MODIFIED);
fs.writeFileSync(file('newfile.txt'), '');
const newfile = await open('newfile.txt');
await type(newfile, 'hey there');
await newfile.save();
await repository.status();
assert.equal(repository.state.workingTreeChanges.length, 2);
repository.state.workingTreeChanges.some(r => r.uri.path === appjs.uri.path && r.status === Status.MODIFIED);
repository.state.workingTreeChanges.some(r => r.uri.path === newfile.uri.path && r.status === Status.UNTRACKED);
});
test('opens diff editor', async function () {
const appjs = uri('app.js');
await commands.executeCommand('git.openChange', appjs);
assert(window.activeTextEditor);
assert.equal(window.activeTextEditor!.document.uri.path, appjs.path);
// TODO: how do we really know this is a diff editor?
});
test('stages correctly', async function () {
const appjs = uri('app.js');
const newfile = uri('newfile.txt');
await commands.executeCommand('git.stage', appjs);
assert.equal(repository.state.workingTreeChanges.length, 1);
repository.state.workingTreeChanges.some(r => r.uri.path === newfile.path && r.status === Status.UNTRACKED);
assert.equal(repository.state.indexChanges.length, 1);
repository.state.indexChanges.some(r => r.uri.path === appjs.path && r.status === Status.INDEX_MODIFIED);
await commands.executeCommand('git.unstage', appjs);
assert.equal(repository.state.workingTreeChanges.length, 2);
repository.state.workingTreeChanges.some(r => r.uri.path === appjs.path && r.status === Status.MODIFIED);
repository.state.workingTreeChanges.some(r => r.uri.path === newfile.path && r.status === Status.UNTRACKED);
});
test('stages, commits changes and verifies outgoing change', async function () {
const appjs = uri('app.js');
const newfile = uri('newfile.txt');
await commands.executeCommand('git.stage', appjs);
await repository.commit('second commit');
assert.equal(repository.state.workingTreeChanges.length, 1);
repository.state.workingTreeChanges.some(r => r.uri.path === newfile.path && r.status === Status.UNTRACKED);
assert.equal(repository.state.indexChanges.length, 0);
await commands.executeCommand('git.stageAll', appjs);
await repository.commit('third commit');
assert.equal(repository.state.workingTreeChanges.length, 0);
assert.equal(repository.state.indexChanges.length, 0);
});
});

View file

@ -6,7 +6,7 @@
import * as dayjs from 'dayjs';
import * as advancedFormat from 'dayjs/plugin/advancedFormat';
import * as relativeTime from 'dayjs/plugin/relativeTime';
import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, TimelineItem, TimelineProvider, Uri, workspace } from 'vscode';
import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, TimelineItem, TimelineProvider, Uri, workspace, TimelineChangeEvent } from 'vscode';
import { Model } from './model';
import { Repository } from './repository';
import { debounce } from './decorators';
@ -19,13 +19,13 @@ dayjs.extend(relativeTime);
// TODO[ECA]: Localize or use a setting for date format
export class GitTimelineProvider implements TimelineProvider {
private _onDidChange = new EventEmitter<Uri | undefined>();
get onDidChange(): Event<Uri | undefined> {
private _onDidChange = new EventEmitter<TimelineChangeEvent>();
get onDidChange(): Event<TimelineChangeEvent> {
return this._onDidChange.event;
}
readonly source = 'git-history';
readonly sourceDescription = 'Git History';
readonly id = 'git-history';
readonly label = 'Git History';
private _disposable: Disposable;
@ -62,8 +62,8 @@ export class GitTimelineProvider implements TimelineProvider {
this._repo = repo;
this._repoStatusDate = new Date();
this._repoDisposable = Disposable.from(
repo.onDidChangeRepository(this.onRepositoryChanged, this),
repo.onDidRunGitStatus(this.onRepositoryStatusChanged, this)
repo.onDidChangeRepository(uri => this.onRepositoryChanged(repo, uri)),
repo.onDidRunGitStatus(() => this.onRepositoryStatusChanged(repo))
);
}
@ -82,19 +82,18 @@ export class GitTimelineProvider implements TimelineProvider {
dateFormatter = dayjs(c.authorDate);
return {
id: c.hash,
timestamp: c.authorDate?.getTime() ?? 0,
iconPath: new ThemeIcon('git-commit'),
label: message,
description: `${dateFormatter.fromNow()} \u2022 ${c.authorName}`,
detail: `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n\n${c.message}`,
command: {
title: 'Open Diff',
command: 'git.openDiff',
arguments: [uri, c.hash]
}
const item = new TimelineItem(message, c.authorDate?.getTime() ?? 0);
item.id = c.hash;
item.iconPath = new (ThemeIcon as any)('git-commit');
item.description = `${dateFormatter.fromNow()} \u2022 ${c.authorName}`;
item.detail = `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n\n${c.message}`;
item.command = {
title: 'Open Diff',
command: 'git.openDiff',
arguments: [uri, `${c.hash}^`, c.hash]
};
return item;
});
const index = repo.indexGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath);
@ -124,43 +123,91 @@ export class GitTimelineProvider implements TimelineProvider {
break;
}
const item = new TimelineItem('Staged Changes', date.getTime());
item.id = 'index';
// TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
item.iconPath = new (ThemeIcon as any)('git-commit');
item.description = `${dateFormatter.fromNow()} \u2022 You`;
item.detail = `You \u2014 Index\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n${status}`;
item.command = {
title: 'Open Comparison',
command: 'git.openDiff',
arguments: [uri, 'HEAD', '~']
};
items.push({
id: '~',
timestamp: date.getTime(),
// TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
iconPath: new ThemeIcon('git-commit'),
label: 'Staged Changes',
description: `${dateFormatter.fromNow()} \u2022 You`,
detail: `You \u2014 Index\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n${status}`,
command: {
title: 'Open Comparison',
command: 'git.openDiff',
arguments: [uri, '~']
}
});
items.push(item);
}
const working = repo.workingTreeGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath);
if (working) {
const date = new Date();
dateFormatter = dayjs(date);
let status;
switch (working.type) {
case Status.INDEX_MODIFIED:
status = 'Modified';
break;
case Status.INDEX_ADDED:
status = 'Added';
break;
case Status.INDEX_DELETED:
status = 'Deleted';
break;
case Status.INDEX_RENAMED:
status = 'Renamed';
break;
case Status.INDEX_COPIED:
status = 'Copied';
break;
default:
status = '';
break;
}
const item = new TimelineItem('Uncommited Changes', date.getTime());
item.id = 'working';
// TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
item.iconPath = new (ThemeIcon as any)('git-commit');
item.description = `${dateFormatter.fromNow()} \u2022 You`;
item.detail = `You \u2014 Working Tree\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n${status}`;
item.command = {
title: 'Open Comparison',
command: 'git.openDiff',
arguments: [uri, index ? '~' : 'HEAD', '']
};
items.push(item);
}
return items;
}
@debounce(500)
private onRepositoriesChanged(_repo: Repository) {
// console.log(`GitTimelineProvider.onRepositoriesChanged`);
// TODO[ECA]: Being naive for now and just always refreshing each time there is a new repository
this._onDidChange.fire();
this.fireChanged();
}
private onRepositoryChanged(_repo: Repository, _uri: Uri) {
// console.log(`GitTimelineProvider.onRepositoryChanged: uri=${uri.toString(true)}`);
this.fireChanged();
}
private onRepositoryStatusChanged(_repo: Repository) {
// console.log(`GitTimelineProvider.onRepositoryStatusChanged`);
// This is crappy, but for now just save the last time a status was run and use that as the timestamp for staged items
this._repoStatusDate = new Date();
this.fireChanged();
}
@debounce(500)
private onRepositoryChanged() {
// console.log(`GitTimelineProvider.onRepositoryChanged`);
private fireChanged() {
this._onDidChange.fire();
}
private onRepositoryStatusChanged() {
// This is crappy, but for now just save the last time a status was run and use that as the timestamp for staged items
this._repoStatusDate = new Date();
}
}

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vscode';
import { Event, Disposable } from 'vscode';
import { dirname, sep } from 'path';
import { Readable } from 'stream';
import { promises as fs, createReadStream } from 'fs';
@ -33,15 +33,15 @@ export function combinedDisposable(disposables: IDisposable[]): IDisposable {
export const EmptyDisposable = toDisposable(() => null);
export function fireEvent<T>(event: Event<T>): Event<T> {
return (listener, thisArgs = null, disposables?) => event(_ => (listener as any).call(thisArgs), null, disposables);
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => event(_ => (listener as any).call(thisArgs), null, disposables);
}
export function mapEvent<I, O>(event: Event<I>, map: (i: I) => O): Event<O> {
return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables);
return (listener: (e: O) => any, thisArgs?: any, disposables?: Disposable[]) => event(i => listener.call(thisArgs, map(i)), null, disposables);
}
export function filterEvent<T>(event: Event<T>, filter: (e: T) => boolean): Event<T> {
return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
}
export function latchEvent<T>(event: Event<T>): Event<T> {
@ -57,7 +57,7 @@ export function latchEvent<T>(event: Event<T>): Event<T> {
}
export function anyEvent<T>(...events: Event<T>[]): Event<T> {
return (listener, thisArgs = null, disposables?) => {
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => {
const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i))));
if (disposables) {
@ -73,7 +73,7 @@ export function done<T>(promise: Promise<T>): Promise<void> {
}
export function onceEvent<T>(event: Event<T>): Event<T> {
return (listener, thisArgs = null, disposables?) => {
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => {
const result = event(e => {
result.dispose();
return listener.call(thisArgs, e);
@ -84,7 +84,7 @@ export function onceEvent<T>(event: Event<T>): Event<T> {
}
export function debounceEvent<T>(event: Event<T>, delay: number): Event<T> {
return (listener, thisArgs = null, disposables?) => {
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => {
let timer: NodeJS.Timer;
return event(e => {
clearTimeout(timer);

View file

@ -36,6 +36,28 @@
resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6"
integrity sha1-AW44dim4gXvtZT/jLqtdESecjfY=
agent-base@4, agent-base@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
dependencies:
es6-promisify "^5.0.0"
ajv@^6.5.5:
version "6.11.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9"
integrity sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
applicationinsights@1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5"
@ -45,11 +67,45 @@ applicationinsights@1.0.8:
diagnostic-channel-publishers "0.2.1"
zone.js "0.7.6"
asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
dependencies:
safer-buffer "~2.1.0"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
aws4@^1.8.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
dependencies:
tweetnacl "^0.14.3"
brace-expansion@^1.1.7:
version "1.1.8"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
@ -63,11 +119,43 @@ browser-stdout@1.3.0:
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8=
browser-stdout@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
byline@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1"
integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
charenc@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
commander@2.15.1:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
commander@2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
@ -80,6 +168,23 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
core-util-is@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
crypt@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
dependencies:
assert-plus "^1.0.0"
dayjs@1.8.19:
version "1.8.19"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.19.tgz#5117dc390d8f8e586d53891dbff3fa308f51abfe"
@ -92,6 +197,32 @@ debug@2.6.8:
dependencies:
ms "2.0.0"
debug@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
dependencies:
ms "2.0.0"
debug@^2.2.0:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@^3.1.0:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies:
ms "^2.1.1"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
diagnostic-channel-publishers@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
@ -109,21 +240,92 @@ diff@3.2.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k=
diff@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
dependencies:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
es6-promise@^4.0.3:
version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
es6-promisify@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
dependencies:
es6-promise "^4.0.3"
escape-string-regexp@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
extsprintf@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
extsprintf@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
fast-deep-equal@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
file-type@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.2.0.tgz#113cfed52e1d6959ab80248906e2f25a8cdccb74"
integrity sha1-ETz+1S4daVmrgCSJBuLyWozcy3Q=
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
dependencies:
assert-plus "^1.0.0"
glob@7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
@ -136,26 +338,98 @@ glob@7.1.1:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.2:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
"graceful-readlink@>= 1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
growl@1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
growl@1.9.2:
version "1.9.2"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
har-validator@~5.1.0:
version "5.1.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
dependencies:
ajv "^6.5.5"
har-schema "^2.0.0"
has-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
he@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
http-proxy-agent@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
dependencies:
agent-base "4"
debug "3.1.0"
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
https-proxy-agent@^2.2.1:
version "2.2.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
dependencies:
agent-base "^4.3.0"
debug "^3.1.0"
iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -176,21 +450,66 @@ inherits@2:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
is-buffer@~1.1.1:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
jschardet@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184"
integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q==
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json3@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.2.3"
verror "1.10.0"
lodash._baseassign@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
@ -247,7 +566,33 @@ lodash.keys@^3.0.0:
lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0"
minimatch@^3.0.2:
lodash@^4.16.4:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
md5@^2.1.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=
dependencies:
charenc "~0.0.1"
crypt "~0.0.1"
is-buffer "~1.1.1"
mime-db@1.43.0:
version "1.43.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==
mime-types@^2.1.12, mime-types@~2.1.19:
version "2.1.26"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06"
integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==
dependencies:
mime-db "1.43.0"
minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@ -259,13 +604,32 @@ minimist@0.0.8:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
mkdirp@0.5.1:
mkdirp@0.5.1, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
dependencies:
minimist "0.0.8"
mocha-junit-reporter@^1.23.3:
version "1.23.3"
resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz#941e219dd759ed732f8641e165918aa8b167c981"
integrity sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA==
dependencies:
debug "^2.2.0"
md5 "^2.1.0"
mkdirp "~0.5.1"
strip-ansi "^4.0.0"
xml "^1.0.0"
mocha-multi-reporters@^1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82"
integrity sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI=
dependencies:
debug "^3.1.0"
lodash "^4.16.4"
mocha@^3.2.0:
version "3.5.3"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d"
@ -284,11 +648,38 @@ mocha@^3.2.0:
mkdirp "0.5.1"
supports-color "3.1.2"
mocha@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==
dependencies:
browser-stdout "1.3.1"
commander "2.15.1"
debug "3.1.0"
diff "3.5.0"
escape-string-regexp "1.0.5"
glob "7.1.2"
growl "1.10.5"
he "1.1.1"
minimatch "3.0.4"
mkdirp "0.5.1"
supports-color "5.4.0"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@ -301,7 +692,73 @@ path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
"safer-buffer@>= 2.1.2 < 3":
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
psl@^1.1.24:
version "1.7.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c"
integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==
punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
querystringify@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
request@^2.88.0:
version "2.88.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
caseless "~0.12.0"
combined-stream "~1.0.6"
extend "~3.0.2"
forever-agent "~0.6.1"
form-data "~2.3.2"
har-validator "~5.1.0"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.19"
oauth-sign "~0.9.0"
performance-now "^2.1.0"
qs "~6.5.2"
safe-buffer "^5.1.2"
tough-cookie "~2.4.3"
tunnel-agent "^0.6.0"
uuid "^3.3.2"
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
version "5.2.0"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@ -311,6 +768,46 @@ semver@^5.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
semver@^5.4.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
source-map-support@^0.5.0:
version "0.5.16"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map@^0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
sshpk@^1.7.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
bcrypt-pbkdf "^1.0.0"
dashdash "^1.12.0"
ecc-jsbn "~0.1.1"
getpass "^0.1.1"
jsbn "~0.1.0"
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
dependencies:
ansi-regex "^3.0.0"
supports-color@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
@ -318,6 +815,62 @@ supports-color@3.1.2:
dependencies:
has-flag "^1.0.0"
supports-color@5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==
dependencies:
has-flag "^3.0.0"
tough-cookie@~2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
dependencies:
psl "^1.1.24"
punycode "^1.4.1"
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
dependencies:
safe-buffer "^5.0.1"
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
uri-js@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
dependencies:
punycode "^2.1.0"
url-parse@^1.4.4:
version "1.4.7"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
uuid@^3.3.2:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
dependencies:
assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
vscode-extension-telemetry@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b"
@ -330,11 +883,32 @@ vscode-nls@^4.0.0:
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002"
integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw==
vscode-test@^0.4.1:
version "0.4.3"
resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8"
integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w==
dependencies:
http-proxy-agent "^2.1.0"
https-proxy-agent "^2.2.1"
vscode-uri@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542"
integrity sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw==
vscode@^1.1.36:
version "1.1.36"
resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6"
integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ==
dependencies:
glob "^7.1.2"
mocha "^5.2.0"
request "^2.88.0"
semver "^5.4.1"
source-map-support "^0.5.0"
url-parse "^1.4.4"
vscode-test "^0.4.1"
which@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
@ -347,6 +921,11 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
xml@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=
zone.js@0.7.6:
version "0.7.6"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"

View file

@ -329,7 +329,7 @@ documents.onDidClose(event => {
});
const pendingValidationRequests: { [uri: string]: NodeJS.Timer; } = {};
const validationDelayMs = 500;
const validationDelayMs = 300;
function cleanPendingValidation(textDocument: TextDocument): void {
const request = pendingValidationRequests[textDocument.uri];
@ -363,12 +363,12 @@ function validateTextDocument(textDocument: TextDocument, callback?: (diagnostic
const documentSettings: DocumentLanguageSettings = textDocument.languageId === 'jsonc' ? { comments: 'ignore', trailingCommas: 'warning' } : { comments: 'error', trailingCommas: 'error' };
languageService.doValidation(textDocument, jsonDocument, documentSettings).then(diagnostics => {
setTimeout(() => {
setImmediate(() => {
const currDocument = documents.get(textDocument.uri);
if (currDocument && currDocument.version === version) {
respond(diagnostics); // Send the computed diagnostics to VSCode.
}
}, 100);
});
}, error => {
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, error));
});

View file

@ -2,6 +2,11 @@
"extends": "../../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./dist/",
"jsx": "react"
"jsx": "react",
"lib": [
"es2018",
"DOM",
"DOM.Iterable"
]
}
}

View file

@ -115,7 +115,7 @@ mappings.forEach(([ext, scope, regexp]) =>
{
name: [scopes.resultBlock.result.meta, scopes.resultBlock.result.metaMultiLine].join(' '),
begin: "^ ((\\d+) )",
while: "^ ((\\d+)(:))|((\\d+) )",
while: "^ (?:((\\d+)(:))|((\\d+) ))",
beginCaptures: {
"0": { name: scopes.resultBlock.result.prefix.meta },
"1": { name: scopes.resultBlock.result.prefix.metaContext },
@ -215,7 +215,7 @@ const plainText = [
}
},
{
match: "^ ((\\d+)(:))|((\\d+)( ))(.*)",
match: "^ (?:((\\d+)(:))|((\\d+)( ))(.*))",
name: [scopes.resultBlock.meta, scopes.resultBlock.result.meta].join(' '),
captures: {
"1": { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaMatch].join(' ') },

View file

@ -253,7 +253,7 @@
}
},
{
"match": "^ ((\\d+)(:))|((\\d+)( ))(.*)",
"match": "^ (?:((\\d+)(:))|((\\d+)( ))(.*))",
"name": "meta.resultBlock.search meta.resultLine.search",
"captures": {
"1": {
@ -297,7 +297,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -383,7 +383,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -469,7 +469,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -555,7 +555,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -641,7 +641,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -727,7 +727,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -813,7 +813,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -899,7 +899,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -985,7 +985,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -1071,7 +1071,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -1157,7 +1157,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -1243,7 +1243,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -1329,7 +1329,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -1415,7 +1415,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -1501,7 +1501,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -1587,7 +1587,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -1673,7 +1673,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -1759,7 +1759,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -1845,7 +1845,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -1931,7 +1931,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2017,7 +2017,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2103,7 +2103,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2189,7 +2189,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2275,7 +2275,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2361,7 +2361,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2447,7 +2447,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2533,7 +2533,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2619,7 +2619,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2705,7 +2705,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2791,7 +2791,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2877,7 +2877,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -2963,7 +2963,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3049,7 +3049,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3135,7 +3135,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3221,7 +3221,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3307,7 +3307,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3393,7 +3393,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3479,7 +3479,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3565,7 +3565,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3651,7 +3651,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3737,7 +3737,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3823,7 +3823,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3909,7 +3909,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -3995,7 +3995,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -4081,7 +4081,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -4167,7 +4167,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -4253,7 +4253,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -4339,7 +4339,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -4425,7 +4425,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -4511,7 +4511,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -4597,7 +4597,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -4683,7 +4683,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"
@ -4769,7 +4769,7 @@
{
"name": "meta.resultLine.search meta.resultLine.multiLine.search",
"begin": "^ ((\\d+) )",
"while": "^ ((\\d+)(:))|((\\d+) )",
"while": "^ (?:((\\d+)(:))|((\\d+) ))",
"beginCaptures": {
"0": {
"name": "constant.numeric.integer meta.resultLinePrefix.search"

View file

@ -1,6 +1,9 @@
{
"compilerOptions": {
"target": "es2018",
"lib": [
"es2018"
],
"module": "commonjs",
"strict": true,
"alwaysStrict": true,
@ -9,4 +12,4 @@
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
}

View file

@ -730,6 +730,12 @@
"default": 3072,
"description": "%configuration.tsserver.maxTsServerMemory%",
"scope": "window"
},
"typescript.tsserver.experimental.enableProjectDiagnostics": {
"type": "boolean",
"default": false,
"description": "%configuration.tsserver.experimental.enableProjectDiagnostics%",
"scope": "window"
}
}
},
@ -882,10 +888,10 @@
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "^\\s*(?:message TS6032:|\\[?\\D*\\d{1,2}:\\d{1,2}:\\d{1,2}\\D*(?:\\]| -)) File change detected\\. Starting incremental compilation\\.\\.\\."
"regexp": "^\\s*(?:message TS6032:|\\[?\\D*\\d{1,2}[:.]\\d{1,2}[:.]\\d{1,2}\\D*(?:\\]| -)) File change detected\\. Starting incremental compilation\\.\\.\\."
},
"endsPattern": {
"regexp": "^\\s*(?:message TS6042:|\\[?\\D*\\d{1,2}:\\d{1,2}:\\d{1,2}\\D*(?:\\]| -)) (?:Compilation complete\\.|Found \\d+ errors?\\.) Watching for file changes\\."
"regexp": "^\\s*(?:message TS6042:|\\[?\\D*\\d{1,2}[:.]\\d{1,2}[:.]\\d{1,2}\\D*(?:\\]| -)) (?:Compilation complete\\.|Found \\d+ errors?\\.) Watching for file changes\\."
}
}
}

View file

@ -58,6 +58,7 @@
"configuration.suggest.paths": "Enable/disable suggestions for paths in import statements and require calls.",
"configuration.tsserver.useSeparateSyntaxServer": "Enable/disable spawning a separate TypeScript server that can more quickly respond to syntax related operations, such as calculating folding or computing document symbols. Requires using TypeScript 3.4.0 or newer in the workspace.",
"configuration.tsserver.maxTsServerMemory": "Set the maximum amount of memory (in MB) to allocate to the TypeScript server process",
"configuration.tsserver.experimental.enableProjectDiagnostics": "(Experimental) Enables project wide error reporting.",
"typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Requires using TypeScript 2.6.0 or newer in the workspace. Default of `null` uses VS Code's locale.",
"javascript.implicitProjectConfig.experimentalDecorators": "Enable/disable `experimentalDecorators` for JavaScript files that are not part of a project. Existing jsconfig.json or tsconfig.json files override this setting. Requires using TypeScript 2.3.1 or newer in the workspace.",
"configuration.suggest.autoImports": "Enable/disable auto import suggestions. Requires using TypeScript 2.6.1 or newer in the workspace.",

View file

@ -19,7 +19,6 @@ import LogDirectoryProvider from './utils/logDirectoryProvider';
import ManagedFileContextManager from './utils/managedFileContext';
import { PluginManager } from './utils/plugins';
import * as ProjectStatus from './utils/projectStatus';
import { Surveyor } from './utils/surveyor';
import TscTaskProvider from './features/task';
export function activate(
@ -70,8 +69,6 @@ function createLazyClientHost(
context.subscriptions.push(clientHost);
context.subscriptions.push(new Surveyor(context.globalState, clientHost.serviceClient));
clientHost.serviceClient.onReady(() => {
context.subscriptions.push(
ProjectStatus.create(

View file

@ -289,19 +289,25 @@ class GetErrRequest {
public readonly files: ResourceMap<void>,
onDone: () => void
) {
const args: Proto.GeterrRequestArgs = {
delay: 0,
files: coalesce(Array.from(files.entries).map(entry => client.normalizedPath(entry.resource)))
};
const allFiles = coalesce(Array.from(files.entries).map(entry => client.normalizedPath(entry.resource)));
if (!allFiles.length) {
this._done = true;
onDone();
} else {
const request = client.configuration.enableProjectDiagnostics
// Note that geterrForProject is almost certainly not the api we want here as it ends up computing far
// too many diagnostics
? client.executeAsync('geterrForProject', { delay: 0, file: allFiles[0] }, this._token.token)
: client.executeAsync('geterr', { delay: 0, files: allFiles }, this._token.token);
client.executeAsync('geterr', args, this._token.token)
.finally(() => {
request.finally(() => {
if (this._done) {
return;
}
this._done = true;
onDone();
});
}
}
public cancel(): any {
@ -329,7 +335,7 @@ export default class BufferSyncSupport extends Disposable {
constructor(
client: ITypeScriptServiceClient,
modeIds: string[]
modeIds: readonly string[]
) {
super();
this.client = client;
@ -454,7 +460,9 @@ export default class BufferSyncSupport extends Disposable {
}
public interuptGetErr<R>(f: () => R): R {
if (!this.pendingGetErr) {
if (!this.pendingGetErr
|| this.client.configuration.enableProjectDiagnostics // `geterr` happens on seperate server so no need to cancel it.
) {
return f();
}

View file

@ -66,17 +66,19 @@ class MyCompletionItem extends vscode.CompletionItem {
this.useCodeSnippet = useCodeSnippetsOnMethodSuggest && (this.kind === vscode.CompletionItemKind.Function || this.kind === vscode.CompletionItemKind.Method);
if (tsEntry.replacementSpan) {
this.range = typeConverters.Range.fromTextSpan(tsEntry.replacementSpan);
let replaceRange = typeConverters.Range.fromTextSpan(tsEntry.replacementSpan);
// Make sure we only replace a single line at most
if (!this.range.isSingleLine) {
this.range = new vscode.Range(this.range.start.line, this.range.start.character, this.range.start.line, line.length);
if (!replaceRange.isSingleLine) {
replaceRange = new vscode.Range(replaceRange.start.line, replaceRange.start.character, replaceRange.start.line, line.length);
}
this.range = {
inserting: new vscode.Range(replaceRange.start, position),
replacing: replaceRange,
};
}
this.insertText = tsEntry.insertText;
// Set filterText for intelliCode and bracket accessors , but not for `this.` completions since it results in
// them being overly prioritized. #74164
this.filterText = tsEntry.insertText && !/^this\./.test(tsEntry.insertText) ? tsEntry.insertText : undefined;
this.filterText = this.getFilterText(line, tsEntry.insertText);
if (completionContext.isMemberCompletion && completionContext.dotAccessorContext) {
this.filterText = completionContext.dotAccessorContext.text + (this.insertText || this.label);
@ -119,29 +121,68 @@ class MyCompletionItem extends vscode.CompletionItem {
this.resolveRange(line);
}
private getFilterText(line: string, insertText: string | undefined): string | undefined {
// Handle private field completions
if (this.tsEntry.name.startsWith('#')) {
const wordRange = this.document.getWordRangeAtPosition(this.position);
const wordStart = wordRange ? line.charAt(wordRange.start.character) : undefined;
if (insertText) {
if (insertText.startsWith('this.#')) {
return wordStart === '#' ? insertText : insertText.replace(/^this\.#/, '');
} else {
return insertText;
}
} else {
return wordStart === '#' ? undefined : this.tsEntry.name.replace(/^#/, '');
}
return undefined;
}
// For `this.` completions, generally don't set the filter text since we don't want them to be overly prioritized. #74164
if (insertText?.startsWith('this.')) {
return undefined;
}
// Handle the case:
// ```
// const xyz = { 'ab c': 1 };
// xyz.ab|
// ```
// In which case we want to insert a bracket accessor but should use `.abc` as the filter text instead of
// the bracketed insert text.
else if (insertText?.startsWith('[')) {
return insertText.replace(/^\[['"](.+)[['"]\]$/, '.$1');
}
// In all other cases, fallback to using the insertText
return insertText;
}
private resolveRange(line: string): void {
if (this.range) {
return;
}
const wordRange = this.document.getWordRangeAtPosition(this.position);
if (wordRange) {
// TODO: Reverted next line due to https://github.com/Microsoft/vscode/issues/66187
// this.range = wordRange;
}
let replaceRange = wordRange;
// Try getting longer, prefix based range for completions that span words
const text = line.slice(Math.max(0, this.position.character - this.label.length), this.position.character).toLowerCase();
const entryName = this.label.toLowerCase();
for (let i = entryName.length; i >= 0; --i) {
if (text.endsWith(entryName.substr(0, i)) && (!wordRange || wordRange.start.character > this.position.character - i)) {
this.range = new vscode.Range(
replaceRange = new vscode.Range(
new vscode.Position(this.position.line, Math.max(0, this.position.character - i)),
this.position);
break;
}
}
if (replaceRange) {
this.range = {
inserting: new vscode.Range(replaceRange.start, this.position),
replacing: replaceRange
};
}
}
private static convertKind(kind: string): vscode.CompletionItemKind {
@ -464,7 +505,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider
return this.client.apiVersion.gte(API.v310) && this.client.apiVersion.lt(API.v320) ? undefined : '@';
case '#': // Workaround for https://github.com/microsoft/TypeScript/issues/36367
return this.client.apiVersion.lt(API.v381) ? undefined : '#' as Proto.CompletionsTriggerCharacter;
return this.client.apiVersion.lt(API.v381) ? undefined : '#';
case '.':
case '"':

View file

@ -28,7 +28,7 @@ class JsDocCompletionItem extends vscode.CompletionItem {
const suffix = line.slice(position.character).match(/^\s*\**\//);
const start = position.translate(0, prefix ? -prefix[0].length : 0);
const range = new vscode.Range(start, position.translate(0, suffix ? suffix[0].length : 0));
this.range = <any>{ inserting: range, replacing: range };
this.range = { inserting: range, replacing: range };
}
}

View file

@ -17,7 +17,7 @@ const jsTsLanguageConfiguration: vscode.LanguageConfiguration = {
decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]].*$/,
increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/
},
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
onEnterRules: [
{
// e.g. /** | */

View file

@ -3,38 +3,23 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import 'mocha';
import * as vscode from 'vscode';
import { disposeAll } from '../utils/dispose';
import { createTestEditor, joinLines, wait } from './testUtils';
import { acceptFirstSuggestion, typeCommitCharacter } from './suggestTestHelpers';
import { assertEditorContents, Config, createTestEditor, joinLines, updateConfig, VsCodeConfiguration, wait, enumerateConfig } from './testUtils';
const testDocumentUri = vscode.Uri.parse('untitled:test.ts');
type VsCodeConfiguration = { [key: string]: any };
async function updateConfig(newConfig: VsCodeConfiguration): Promise<VsCodeConfiguration> {
const oldConfig: VsCodeConfiguration = {};
const config = vscode.workspace.getConfiguration(undefined, testDocumentUri);
for (const configKey of Object.keys(newConfig)) {
oldConfig[configKey] = config.get(configKey);
await new Promise((resolve, reject) =>
config.update(configKey, newConfig[configKey], vscode.ConfigurationTarget.Global)
.then(() => resolve(), reject));
}
return oldConfig;
}
namespace Config {
export const suggestSelection = 'editor.suggestSelection';
export const completeFunctionCalls = 'typescript.suggest.completeFunctionCalls';
}
const insertModes = Object.freeze(['insert', 'replace']);
suite('TypeScript Completions', () => {
const configDefaults: VsCodeConfiguration = Object.freeze({
[Config.suggestSelection]: 'first',
[Config.autoClosingBrackets]: 'always',
[Config.completeFunctionCalls]: false,
[Config.insertMode]: 'insert',
[Config.snippetSuggestions]: 'none',
[Config.suggestSelection]: 'first',
});
const _disposables: vscode.Disposable[] = [];
@ -44,76 +29,87 @@ suite('TypeScript Completions', () => {
await wait(100);
// Save off config and apply defaults
oldConfig = await updateConfig(configDefaults);
oldConfig = await updateConfig(testDocumentUri, configDefaults);
});
teardown(async () => {
disposeAll(_disposables);
// Restore config
await updateConfig(oldConfig);
await updateConfig(testDocumentUri, oldConfig);
return vscode.commands.executeCommand('workbench.action.closeAllEditors');
});
test('Basic var completion', async () => {
await createTestEditor(testDocumentUri,
`const abcdef = 123;`,
`ab$0;`
);
const document = await acceptFirstSuggestion(testDocumentUri, _disposables);
assert.strictEqual(
document.getText(),
joinLines(
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
`const abcdef = 123;`,
`abcdef;`
));
`ab$0;`
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`const abcdef = 123;`,
`abcdef;`
),
`config: ${config}`
);
});
});
test('Should treat period as commit character for var completions', async () => {
await createTestEditor(testDocumentUri,
`const abcdef = 123;`,
`ab$0;`
);
const document = await typeCommitCharacter(testDocumentUri, '.', _disposables);
assert.strictEqual(
document.getText(),
joinLines(
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
`const abcdef = 123;`,
`abcdef.;`
));
`ab$0;`
);
await typeCommitCharacter(testDocumentUri, '.', _disposables);
assertEditorContents(editor,
joinLines(
`const abcdef = 123;`,
`abcdef.;`
),
`config: ${config}`);
});
});
test('Should treat paren as commit character for function completions', async () => {
await createTestEditor(testDocumentUri,
`function abcdef() {};`,
`ab$0;`
);
const document = await typeCommitCharacter(testDocumentUri, '(', _disposables);
assert.strictEqual(
document.getText(),
joinLines(
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
`function abcdef() {};`,
`abcdef();`
));
`ab$0;`
);
await typeCommitCharacter(testDocumentUri, '(', _disposables);
assertEditorContents(editor,
joinLines(
`function abcdef() {};`,
`abcdef();`
), `config: ${config}`);
});
});
test('Should insert backets when completing dot properties with spaces in name', async () => {
await createTestEditor(testDocumentUri,
'const x = { "hello world": 1 };',
'x.$0'
);
const document = await acceptFirstSuggestion(testDocumentUri, _disposables);
assert.strictEqual(
document.getText(),
joinLines(
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
'const x = { "hello world": 1 };',
'x["hello world"]'
));
'x.$0'
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
'const x = { "hello world": 1 };',
'x["hello world"]'
), `config: ${config}`);
});
});
test('Should allow commit characters for backet completions', async () => {
@ -121,14 +117,14 @@ suite('TypeScript Completions', () => {
{ char: '.', insert: '.' },
{ char: '(', insert: '()' },
]) {
await createTestEditor(testDocumentUri,
const editor = await createTestEditor(testDocumentUri,
'const x = { "hello world2": 1 };',
'x.$0'
);
const document = await typeCommitCharacter(testDocumentUri, char, _disposables);
assert.strictEqual(
document.getText(),
await typeCommitCharacter(testDocumentUri, char, _disposables);
assertEditorContents(editor,
joinLines(
'const x = { "hello world2": 1 };',
`x["hello world2"]${insert}`
@ -140,23 +136,26 @@ suite('TypeScript Completions', () => {
});
test('Should not prioritize bracket accessor completions. #63100', async () => {
// 'a' should be first entry in completion list
await createTestEditor(testDocumentUri,
'const x = { "z-z": 1, a: 1 };',
'x.$0'
);
const document = await acceptFirstSuggestion(testDocumentUri, _disposables);
assert.strictEqual(
document.getText(),
joinLines(
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
// 'a' should be first entry in completion list
const editor = await createTestEditor(testDocumentUri,
'const x = { "z-z": 1, a: 1 };',
'x.a'
));
'x.$0'
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
'const x = { "z-z": 1, a: 1 };',
'x.a'
),
`config: ${config}`);
});
});
test('Accepting a string completion should replace the entire string. #53962', async () => {
await createTestEditor(testDocumentUri,
const editor = await createTestEditor(testDocumentUri,
'interface TFunction {',
` (_: 'abc.abc2', __ ?: {}): string;`,
` (_: 'abc.abc', __?: {}): string;`,
@ -165,9 +164,9 @@ suite('TypeScript Completions', () => {
`f('abc.abc$0')`
);
const document = await acceptFirstSuggestion(testDocumentUri, _disposables, { useLineRange: true });
assert.strictEqual(
document.getText(),
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
'interface TFunction {',
` (_: 'abc.abc2', __ ?: {}): string;`,
@ -178,35 +177,18 @@ suite('TypeScript Completions', () => {
));
});
test.skip('Accepting a member completion should result in valid code. #58597', async () => {
await createTestEditor(testDocumentUri,
`const abc = 123;`,
`ab$0c`
);
const document = await acceptFirstSuggestion(testDocumentUri, _disposables);
assert.strictEqual(
document.getText(),
joinLines(
`const abc = 123;`,
`abc`
));
});
test('completeFunctionCalls should complete function parameters when at end of word', async () => {
await updateConfig({
[Config.completeFunctionCalls]: true,
});
await updateConfig(testDocumentUri, { [Config.completeFunctionCalls]: true });
// Complete with-in word
await createTestEditor(testDocumentUri,
const editor = await createTestEditor(testDocumentUri,
`function abcdef(x, y, z) { }`,
`abcdef$0`
);
const document = await acceptFirstSuggestion(testDocumentUri, _disposables);
assert.strictEqual(
document.getText(),
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`function abcdef(x, y, z) { }`,
`abcdef(x, y, z)`
@ -214,18 +196,16 @@ suite('TypeScript Completions', () => {
});
test.skip('completeFunctionCalls should complete function parameters when within word', async () => {
await updateConfig({
[Config.completeFunctionCalls]: true,
});
await updateConfig(testDocumentUri, { [Config.completeFunctionCalls]: true });
await createTestEditor(testDocumentUri,
const editor = await createTestEditor(testDocumentUri,
`function abcdef(x, y, z) { }`,
`abcd$0ef`
);
const document = await acceptFirstSuggestion(testDocumentUri, _disposables);
assert.strictEqual(
document.getText(),
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`function abcdef(x, y, z) { }`,
`abcdef(x, y, z)`
@ -233,18 +213,16 @@ suite('TypeScript Completions', () => {
});
test('completeFunctionCalls should not complete function parameters at end of word if we are already in something that looks like a function call, #18131', async () => {
await updateConfig({
[Config.completeFunctionCalls]: true,
});
await updateConfig(testDocumentUri, { [Config.completeFunctionCalls]: true });
await createTestEditor(testDocumentUri,
const editor = await createTestEditor(testDocumentUri,
`function abcdef(x, y, z) { }`,
`abcdef$0(1, 2, 3)`
);
const document = await acceptFirstSuggestion(testDocumentUri, _disposables);
assert.strictEqual(
document.getText(),
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`function abcdef(x, y, z) { }`,
`abcdef(1, 2, 3)`
@ -252,45 +230,396 @@ suite('TypeScript Completions', () => {
});
test.skip('completeFunctionCalls should not complete function parameters within word if we are already in something that looks like a function call, #18131', async () => {
await updateConfig({
[Config.completeFunctionCalls]: true,
});
await updateConfig(testDocumentUri, { [Config.completeFunctionCalls]: true });
await createTestEditor(testDocumentUri,
const editor = await createTestEditor(testDocumentUri,
`function abcdef(x, y, z) { }`,
`abcd$0ef(1, 2, 3)`
);
const document = await acceptFirstSuggestion(testDocumentUri, _disposables);
assert.strictEqual(
document.getText(),
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`function abcdef(x, y, z) { }`,
`abcdef(1, 2, 3)`
));
});
test('should not de-prioritized this.member suggestion, #74164', async () => {
await createTestEditor(testDocumentUri,
`class A {`,
` private detail = '';`,
test('should not de-prioritize `this.member` suggestion, #74164', async () => {
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
`class A {`,
` private detail = '';`,
` foo() {`,
` det$0`,
` }`,
`}`,
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class A {`,
` private detail = '';`,
` foo() {`,
` this.detail`,
` }`,
`}`,
),
`Config: ${config}`);
});
});
test('Member completions for string property name should insert `this.` and use brackets', async () => {
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
`class A {`,
` ['xyz 123'] = 1`,
` foo() {`,
` xyz$0`,
` }`,
`}`,
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class A {`,
` ['xyz 123'] = 1`,
` foo() {`,
` this["xyz 123"]`,
` }`,
`}`,
),
`Config: ${config}`);
});
});
test('Member completions for string property name already using `this.` should add brackets', async () => {
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
`class A {`,
` ['xyz 123'] = 1`,
` foo() {`,
` this.xyz$0`,
` }`,
`}`,
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class A {`,
` ['xyz 123'] = 1`,
` foo() {`,
` this["xyz 123"]`,
` }`,
`}`,
),
`Config: ${config}`);
});
});
test('Accepting a completion in word using `insert` mode should insert', async () => {
await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' });
const editor = await createTestEditor(testDocumentUri,
`const abc = 123;`,
`ab$0c`
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`const abc = 123;`,
`abcc`
));
});
test('Accepting a completion in word using `replace` mode should replace', async () => {
await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' });
const editor = await createTestEditor(testDocumentUri,
`const abc = 123;`,
`ab$0c`
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`const abc = 123;`,
`abc`
));
});
test('Accepting a member completion in word using `insert` mode add `this.` and insert', async () => {
await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' });
const editor = await createTestEditor(testDocumentUri,
`class Foo {`,
` abc = 1;`,
` foo() {`,
` det$0`,
` ab$0c`,
` }`,
`}`,
);
const document = await acceptFirstSuggestion(testDocumentUri, _disposables);
assert.strictEqual(
document.getText(),
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class A {`,
` private detail = '';`,
`class Foo {`,
` abc = 1;`,
` foo() {`,
` this.detail`,
` this.abcc`,
` }`,
`}`,
));
});
});
test('Accepting a member completion in word using `replace` mode should add `this.` and replace', async () => {
await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' });
const editor = await createTestEditor(testDocumentUri,
`class Foo {`,
` abc = 1;`,
` foo() {`,
` ab$0c`,
` }`,
`}`,
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class Foo {`,
` abc = 1;`,
` foo() {`,
` this.abc`,
` }`,
`}`,
));
});
test('Accepting string completion inside string using `insert` mode should insert', async () => {
await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' });
const editor = await createTestEditor(testDocumentUri,
`const abc = { 'xy z': 123 }`,
`abc["x$0y w"]`
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`const abc = { 'xy z': 123 }`,
`abc["xy zy w"]`
));
});
// Waiting on https://github.com/microsoft/TypeScript/issues/35602
test.skip('Accepting string completion inside string using insert mode should insert', async () => {
await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' });
const editor = await createTestEditor(testDocumentUri,
`const abc = { 'xy z': 123 }`,
`abc["x$0y w"]`
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`const abc = { 'xy z': 123 }`,
`abc["xy w"]`
));
});
test('Private field completions on `this.#` should work', async () => {
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
`class A {`,
` #xyz = 1;`,
` foo() {`,
` this.#$0`,
` }`,
`}`,
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class A {`,
` #xyz = 1;`,
` foo() {`,
` this.#xyz`,
` }`,
`}`,
),
`Config: ${config}`);
});
});
test('Private field completions on `#` should insert `this.`', async () => {
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
`class A {`,
` #xyz = 1;`,
` foo() {`,
` #$0`,
` }`,
`}`,
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class A {`,
` #xyz = 1;`,
` foo() {`,
` this.#xyz`,
` }`,
`}`,
),
`Config: ${config}`);
});
});
test('Private field completions should not require strict prefix match (#89556)', async () => {
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
`class A {`,
` #xyz = 1;`,
` foo() {`,
` this.xyz$0`,
` }`,
`}`,
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class A {`,
` #xyz = 1;`,
` foo() {`,
` this.#xyz`,
` }`,
`}`,
),
`Config: ${config}`);
});
});
test('Private field completions without `this.` should not require strict prefix match (#89556)', async () => {
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
`class A {`,
` #xyz = 1;`,
` foo() {`,
` xyz$0`,
` }`,
`}`,
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class A {`,
` #xyz = 1;`,
` foo() {`,
` this.#xyz`,
` }`,
`}`,
),
`Config: ${config}`);
});
});
test('Accepting a completion for async property in `insert` mode should insert and add await', async () => {
await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' });
const editor = await createTestEditor(testDocumentUri,
`class A {`,
` xyz = Promise.resolve({ 'abc': 1 });`,
` async foo() {`,
` this.xyz.ab$0c`,
` }`,
`}`,
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class A {`,
` xyz = Promise.resolve({ 'abc': 1 });`,
` async foo() {`,
` (await this.xyz).abcc`,
` }`,
`}`,
));
});
test('Accepting a completion for async property in `replace` mode should replace and add await', async () => {
await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' });
const editor = await createTestEditor(testDocumentUri,
`class A {`,
` xyz = Promise.resolve({ 'abc': 1 });`,
` async foo() {`,
` this.xyz.ab$0c`,
` }`,
`}`,
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class A {`,
` xyz = Promise.resolve({ 'abc': 1 });`,
` async foo() {`,
` (await this.xyz).abc`,
` }`,
`}`,
));
});
test.skip('Accepting a completion for async string property should add await plus brackets', async () => {
await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
const editor = await createTestEditor(testDocumentUri,
`class A {`,
` xyz = Promise.resolve({ 'ab c': 1 });`,
` async foo() {`,
` this.xyz.ab$0`,
` }`,
`}`,
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`class A {`,
` xyz = Promise.resolve({ 'abc': 1 });`,
` async foo() {`,
` (await this.xyz)["ab c"]`,
` }`,
`}`,
),
`Config: ${config}`);
});
});
});

View file

@ -3,42 +3,59 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import 'mocha';
import * as vscode from 'vscode';
import { disposeAll } from '../utils/dispose';
import { createTestEditor, joinLines, wait } from './testUtils';
import { acceptFirstSuggestion } from './suggestTestHelpers';
import { assertEditorContents, Config, createTestEditor, CURSOR, enumerateConfig, insertModesValues, joinLines, updateConfig, VsCodeConfiguration, wait } from './testUtils';
const testDocumentUri = vscode.Uri.parse('untitled:test.ts');
suite('JSDoc Completions', () => {
const _disposables: vscode.Disposable[] = [];
const configDefaults: VsCodeConfiguration = Object.freeze({
[Config.snippetSuggestions]: 'inline',
});
let oldConfig: { [key: string]: any } = {};
setup(async () => {
await wait(100);
// Save off config and apply defaults
oldConfig = await updateConfig(testDocumentUri, configDefaults);
});
teardown(async () => {
disposeAll(_disposables);
// Restore config
await updateConfig(testDocumentUri, oldConfig);
return vscode.commands.executeCommand('workbench.action.closeAllEditors');
});
test('Should complete jsdoc inside single line comment', async () => {
await createTestEditor(testDocumentUri,
`/**$0 */`,
`function abcdef(x, y) { }`,
);
await enumerateConfig(testDocumentUri, Config.insertMode, insertModesValues, async config => {
const document = await acceptFirstSuggestion(testDocumentUri, _disposables, { useLineRange: true});
assert.strictEqual(
document.getText(),
joinLines(
`/**`,
` *`,
` * @param {*} x `,
` * @param {*} y `,
` */`,
const editor = await createTestEditor(testDocumentUri,
`/**$0 */`,
`function abcdef(x, y) { }`,
));
);
await acceptFirstSuggestion(testDocumentUri, _disposables);
assertEditorContents(editor,
joinLines(
`/**`,
` * `,
` * @param x ${CURSOR}`,
` * @param y `,
` */`,
`function abcdef(x, y) { }`,
),
`Config: ${config}`);
});
});
});

View file

@ -54,5 +54,15 @@ suite('typescript.previewer', () => {
'x {@link http://www.example.com/foo y [bar](http://www.example.com/bar) z');
});
test('Should support non-ascii characters in parameter name (#90108)', async () => {
assert.strictEqual(
tagsMarkdownPreview([
{
name: 'param',
text: 'parámetroConDiacríticos this will not'
}
]),
'*@param* `parámetroConDiacríticos` — this will not');
});
});

View file

@ -7,23 +7,18 @@ import 'mocha';
import * as vscode from 'vscode';
import { wait } from './testUtils';
export async function acceptFirstSuggestion(uri: vscode.Uri, _disposables: vscode.Disposable[], options?: { useLineRange?: boolean }) {
export async function acceptFirstSuggestion(uri: vscode.Uri, _disposables: vscode.Disposable[]) {
const didChangeDocument = onChangedDocument(uri, _disposables);
const didSuggest = onDidSuggest(_disposables, options);
await vscode.commands.executeCommand('editor.action.triggerSuggest');
await didSuggest;
// TODO: depends on reverting fix for https://github.com/Microsoft/vscode/issues/64257
// Make sure we have time to resolve the suggestion because `acceptSelectedSuggestion` doesn't
await wait(40);
await wait(1000); // Give time for suggestions to show
await vscode.commands.executeCommand('acceptSelectedSuggestion');
return await didChangeDocument;
return didChangeDocument;
}
export async function typeCommitCharacter(uri: vscode.Uri, character: string, _disposables: vscode.Disposable[]) {
const didChangeDocument = onChangedDocument(uri, _disposables);
const didSuggest = onDidSuggest(_disposables);
await vscode.commands.executeCommand('editor.action.triggerSuggest');
await didSuggest;
await wait(1000); // Give time for suggestions to show
await vscode.commands.executeCommand('type', { text: character });
return await didChangeDocument;
}
@ -35,29 +30,3 @@ export function onChangedDocument(documentUri: vscode.Uri, disposables: vscode.D
}
}, undefined, disposables));
}
function onDidSuggest(disposables: vscode.Disposable[], options?: { useLineRange?: boolean }) {
return new Promise(resolve =>
disposables.push(vscode.languages.registerCompletionItemProvider('typescript', new class implements vscode.CompletionItemProvider {
provideCompletionItems(doc: vscode.TextDocument, position: vscode.Position): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
// Return a fake item that will come first
const range = options && options.useLineRange
? new vscode.Range(new vscode.Position(position.line, 0), position)
: doc.getWordRangeAtPosition(position.translate({ characterDelta: -1 }));
return [{
label: '🦄',
insertText: doc.getText(range),
filterText: doc.getText(range),
preselect: true,
sortText: 'a',
range: range
}];
}
async resolveCompletionItem(item: vscode.CompletionItem) {
await vscode.commands.executeCommand('selectNextSuggestion');
resolve();
return item;
}
})));
}

View file

@ -3,10 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as assert from 'assert';
import * as fs from 'fs';
import * as os from 'os';
import { join } from 'path';
import * as vscode from 'vscode';
function rndName() {
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10);
@ -73,11 +74,62 @@ export const joinLines = (...args: string[]) => args.join('\n');
export async function createTestEditor(uri: vscode.Uri, ...lines: string[]) {
const document = await vscode.workspace.openTextDocument(uri);
await vscode.window.showTextDocument(document);
const activeEditor = vscode.window.activeTextEditor;
if (!activeEditor) {
throw new Error('no active editor');
}
await activeEditor.insertSnippet(new vscode.SnippetString(joinLines(...lines)), new vscode.Range(0, 0, 1000, 0));
const editor = await vscode.window.showTextDocument(document);
await editor.insertSnippet(new vscode.SnippetString(joinLines(...lines)), new vscode.Range(0, 0, 1000, 0));
return editor;
}
export function assertEditorContents(editor: vscode.TextEditor, expectedDocContent: string, message?: string): void {
const cursorIndex = expectedDocContent.indexOf(CURSOR);
assert.strictEqual(
editor.document.getText(),
expectedDocContent.replace(CURSOR, ''),
message);
if (cursorIndex >= 0) {
const expectedCursorPos = editor.document.positionAt(cursorIndex);
assert.deepEqual(
{ line: editor.selection.active.line, character: editor.selection.active.line },
{ line: expectedCursorPos.line, character: expectedCursorPos.line },
'Cursor position'
);
}
}
export type VsCodeConfiguration = { [key: string]: any };
export async function updateConfig(documentUri: vscode.Uri, newConfig: VsCodeConfiguration): Promise<VsCodeConfiguration> {
const oldConfig: VsCodeConfiguration = {};
const config = vscode.workspace.getConfiguration(undefined, documentUri);
for (const configKey of Object.keys(newConfig)) {
oldConfig[configKey] = config.get(configKey);
await new Promise((resolve, reject) =>
config.update(configKey, newConfig[configKey], vscode.ConfigurationTarget.Global)
.then(() => resolve(), reject));
}
return oldConfig;
}
export const Config = Object.freeze({
autoClosingBrackets: 'editor.autoClosingBrackets',
completeFunctionCalls: 'typescript.suggest.completeFunctionCalls',
insertMode: 'editor.suggest.insertMode',
snippetSuggestions: 'editor.snippetSuggestions',
suggestSelection: 'editor.suggestSelection',
} as const);
export const insertModesValues = Object.freeze(['insert', 'replace']);
export async function enumerateConfig(
documentUri: vscode.Uri,
configKey: string,
values: readonly string[],
f: (message: string) => Promise<void>
): Promise<void> {
for (const value of values) {
const newConfig = { [configKey]: value };
await updateConfig(documentUri, newConfig);
await f(JSON.stringify(newConfig));
}
}

View file

@ -297,22 +297,128 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
}
class RequestRouter {
private static readonly sharedCommands = new Set<keyof TypeScriptRequests>([
'change',
'close',
'open',
'updateOpen',
'configure',
'configurePlugin',
]);
constructor(
private readonly servers: ReadonlyArray<{ readonly server: ITypeScriptServer, readonly preferredCommands?: ReadonlySet<keyof TypeScriptRequests> }>,
private readonly delegate: TsServerDelegate,
) { }
public execute(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
if (RequestRouter.sharedCommands.has(command)) {
// Dispatch shared commands to all server but only return from first one one
const requestStates: RequestState.State[] = this.servers.map(() => RequestState.Unresolved);
// Also make sure we never cancel requests to just one server
let token: vscode.CancellationToken | undefined = undefined;
if (executeInfo.token) {
const source = new vscode.CancellationTokenSource();
executeInfo.token.onCancellationRequested(() => {
if (requestStates.some(state => state === RequestState.Resolved)) {
// Don't cancel.
// One of the servers completed this request so we don't want to leave the other
// in a different state.
return;
}
source.cancel();
});
token = source.token;
}
let firstRequest: Promise<ServerResponse.Response<Proto.Response>> | undefined;
for (let serverIndex = 0; serverIndex < this.servers.length; ++serverIndex) {
const server = this.servers[serverIndex].server;
const request = server.executeImpl(command, args, { ...executeInfo, token });
if (serverIndex === 0) {
firstRequest = request;
}
if (request) {
request
.then(result => {
requestStates[serverIndex] = RequestState.Resolved;
const erroredRequest = requestStates.find(state => state.type === RequestState.Type.Errored) as RequestState.Errored | undefined;
if (erroredRequest) {
// We've gone out of sync
this.delegate.onFatalError(command, erroredRequest.err);
}
return result;
}, err => {
requestStates[serverIndex] = new RequestState.Errored(err);
if (requestStates.some(state => state === RequestState.Resolved)) {
// We've gone out of sync
this.delegate.onFatalError(command, err);
}
throw err;
});
}
}
return firstRequest;
}
for (const { preferredCommands, server } of this.servers) {
if (!preferredCommands || preferredCommands.has(command)) {
return server.executeImpl(command, args, executeInfo);
}
}
throw new Error(`Could not find server for command: '${command}'`);
}
}
export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServer {
private static readonly syntaxCommands = new Set<keyof TypeScriptRequests>([
'navtree',
'getOutliningSpans',
'jsxClosingTag',
'selectionRange',
'format',
'formatonkey',
'docCommentTemplate',
]);
private readonly syntaxServer: ITypeScriptServer;
private readonly semanticServer: ITypeScriptServer;
private readonly router: RequestRouter;
public constructor(
private readonly syntaxServer: ITypeScriptServer,
private readonly semanticServer: ITypeScriptServer,
private readonly _delegate: TsServerDelegate,
servers: { syntax: ITypeScriptServer, semantic: ITypeScriptServer },
delegate: TsServerDelegate,
) {
super();
this._register(syntaxServer.onEvent(e => this._onEvent.fire(e)));
this._register(semanticServer.onEvent(e => this._onEvent.fire(e)));
this.syntaxServer = servers.syntax;
this.semanticServer = servers.semantic;
this._register(semanticServer.onExit(e => {
this.router = new RequestRouter(
[
{ server: this.syntaxServer, preferredCommands: SyntaxRoutingTsServer.syntaxCommands },
{ server: this.semanticServer, preferredCommands: undefined /* gets all other commands */ }
],
delegate);
this._register(this.syntaxServer.onEvent(e => this._onEvent.fire(e)));
this._register(this.semanticServer.onEvent(e => this._onEvent.fire(e)));
this._register(this.semanticServer.onExit(e => {
this._onExit.fire(e);
this.syntaxServer.kill();
}));
this._register(semanticServer.onError(e => this._onError.fire(e)));
this._register(this.semanticServer.onError(e => this._onError.fire(e)));
}
private readonly _onEvent = this._register(new vscode.EventEmitter<Proto.Event>());
@ -333,98 +439,91 @@ export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServ
this.semanticServer.kill();
}
private static readonly syntaxCommands = new Set<keyof TypeScriptRequests>([
'navtree',
'getOutliningSpans',
'jsxClosingTag',
'selectionRange',
'format',
'formatonkey',
'docCommentTemplate',
]);
private static readonly sharedCommands = new Set<keyof TypeScriptRequests>([
'change',
'close',
'open',
'updateOpen',
'configure',
'configurePlugin',
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
return this.router.execute(command, args, executeInfo);
}
}
export class GetErrRoutingTsServer extends Disposable implements ITypeScriptServer {
private static readonly diagnosticEvents = new Set([
'configFileDiag',
'syntaxDiag',
'semanticDiag',
'suggestionDiag'
]);
private readonly getErrServer: ITypeScriptServer;
private readonly mainServer: ITypeScriptServer;
private readonly router: RequestRouter;
public constructor(
servers: { getErr: ITypeScriptServer, primary: ITypeScriptServer },
delegate: TsServerDelegate,
) {
super();
this.getErrServer = servers.getErr;
this.mainServer = servers.primary;
this.router = new RequestRouter(
[
{ server: this.getErrServer, preferredCommands: new Set<keyof TypeScriptRequests>(['geterr', 'geterrForProject']) },
{ server: this.mainServer, preferredCommands: undefined /* gets all other commands */ }
],
delegate);
this._register(this.getErrServer.onEvent(e => {
if (GetErrRoutingTsServer.diagnosticEvents.has(e.event)) {
this._onEvent.fire(e);
}
// Ignore all other events
}));
this._register(this.mainServer.onEvent(e => {
if (!GetErrRoutingTsServer.diagnosticEvents.has(e.event)) {
this._onEvent.fire(e);
}
// Ignore all other events
}));
this._register(this.getErrServer.onError(e => this._onError.fire(e)));
this._register(this.mainServer.onError(e => this._onError.fire(e)));
this._register(this.mainServer.onExit(e => {
this._onExit.fire(e);
this.getErrServer.kill();
}));
}
private readonly _onEvent = this._register(new vscode.EventEmitter<Proto.Event>());
public readonly onEvent = this._onEvent.event;
private readonly _onExit = this._register(new vscode.EventEmitter<any>());
public readonly onExit = this._onExit.event;
private readonly _onError = this._register(new vscode.EventEmitter<any>());
public readonly onError = this._onError.event;
public get onReaderError() { return this.mainServer.onReaderError; }
public get tsServerLogFile() { return this.mainServer.tsServerLogFile; }
public kill(): void {
this.getErrServer.kill();
this.mainServer.kill();
}
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
if (SyntaxRoutingTsServer.syntaxCommands.has(command)) {
return this.syntaxServer.executeImpl(command, args, executeInfo);
} else if (SyntaxRoutingTsServer.sharedCommands.has(command)) {
// Dispatch to both server but only return from syntax one
let syntaxRequestState: RequestState.State = RequestState.Unresolved;
let semanticRequestState: RequestState.State = RequestState.Unresolved;
// Also make sure we never cancel requests to just one server
let token: vscode.CancellationToken | undefined = undefined;
if (executeInfo.token) {
const source = new vscode.CancellationTokenSource();
executeInfo.token.onCancellationRequested(() => {
if (syntaxRequestState !== RequestState.Unresolved && semanticRequestState === RequestState.Unresolved
|| syntaxRequestState === RequestState.Unresolved && semanticRequestState !== RequestState.Unresolved
) {
// Don't cancel.
// One of the servers completed this request so we don't want to leave the other
// in a different state
return;
}
source.cancel();
});
token = source.token;
}
const semanticRequest = this.semanticServer.executeImpl(command, args, { ...executeInfo, token });
if (semanticRequest) {
semanticRequest
.then(result => {
semanticRequestState = RequestState.Resolved;
if (syntaxRequestState.type === RequestState.Type.Errored) {
// We've gone out of sync
this._delegate.onFatalError(command, syntaxRequestState.err);
}
return result;
}, err => {
semanticRequestState = new RequestState.Errored(err);
if (syntaxRequestState === RequestState.Resolved) {
// We've gone out of sync
this._delegate.onFatalError(command, err);
}
throw err;
});
}
const syntaxRequest = this.syntaxServer.executeImpl(command, args, { ...executeInfo, token });
if (syntaxRequest) {
syntaxRequest
.then(result => {
syntaxRequestState = RequestState.Resolved;
if (semanticRequestState.type === RequestState.Type.Errored) {
// We've gone out of sync
this._delegate.onFatalError(command, semanticRequestState.err);
}
return result;
}, err => {
syntaxRequestState = new RequestState.Errored(err);
if (semanticRequestState === RequestState.Resolved) {
// We've gone out of sync
this._delegate.onFatalError(command, err);
}
throw err;
});
}
return syntaxRequest;
} else {
return this.semanticServer.executeImpl(command, args, executeInfo);
}
return this.router.execute(command, args, executeInfo);
}
}
namespace RequestState {
export const enum Type { Unresolved, Resolved, Errored }

View file

@ -18,9 +18,14 @@ import { PluginManager } from '../utils/plugins';
import { TelemetryReporter } from '../utils/telemetry';
import Tracer from '../utils/tracer';
import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider';
import { ITypeScriptServer, PipeRequestCanceller, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerProcess, TsServerDelegate } from './server';
import { ITypeScriptServer, PipeRequestCanceller, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerProcess, TsServerDelegate, GetErrRoutingTsServer } from './server';
type ServerKind = 'main' | 'syntax' | 'semantic';
const enum ServerKind {
Main = 'main',
Syntax = 'syntax',
Semantic = 'semantic',
Diagnostics = 'diagnostics'
}
export class TypeScriptServerSpawner {
public constructor(
@ -38,13 +43,24 @@ export class TypeScriptServerSpawner {
pluginManager: PluginManager,
delegate: TsServerDelegate,
): ITypeScriptServer {
let primaryServer: ITypeScriptServer;
if (this.shouldUseSeparateSyntaxServer(version, configuration)) {
const syntaxServer = this.spawnTsServer('syntax', version, configuration, pluginManager);
const semanticServer = this.spawnTsServer('semantic', version, configuration, pluginManager);
return new SyntaxRoutingTsServer(syntaxServer, semanticServer, delegate);
primaryServer = new SyntaxRoutingTsServer({
syntax: this.spawnTsServer(ServerKind.Syntax, version, configuration, pluginManager),
semantic: this.spawnTsServer(ServerKind.Semantic, version, configuration, pluginManager)
}, delegate);
} else {
primaryServer = this.spawnTsServer(ServerKind.Main, version, configuration, pluginManager);
}
return this.spawnTsServer('main', version, configuration, pluginManager);
if (this.shouldUseSeparateDiagnosticsServer(configuration)) {
return new GetErrRoutingTsServer({
getErr: this.spawnTsServer(ServerKind.Diagnostics, version, configuration, pluginManager),
primary: primaryServer,
}, delegate);
}
return primaryServer;
}
private shouldUseSeparateSyntaxServer(
@ -54,6 +70,12 @@ export class TypeScriptServerSpawner {
return configuration.useSeparateSyntaxServer && !!version.apiVersion && version.apiVersion.gte(API.v340);
}
private shouldUseSeparateDiagnosticsServer(
configuration: TypeScriptServiceConfiguration,
): boolean {
return configuration.enableProjectDiagnostics;
}
private spawnTsServer(
kind: ServerKind,
version: TypeScriptVersion,
@ -107,7 +129,7 @@ export class TypeScriptServerSpawner {
const args: string[] = [];
let tsServerLogFile: string | undefined;
if (kind === 'syntax') {
if (kind === ServerKind.Syntax) {
args.push('--syntaxOnly');
}
@ -117,11 +139,11 @@ export class TypeScriptServerSpawner {
args.push('--useSingleInferredProject');
}
if (configuration.disableAutomaticTypeAcquisition || kind === 'syntax') {
if (configuration.disableAutomaticTypeAcquisition || kind === ServerKind.Syntax || kind === ServerKind.Diagnostics) {
args.push('--disableAutomaticTypingAcquisition');
}
if (kind !== 'syntax') {
if (kind === ServerKind.Semantic || kind === ServerKind.Main) {
args.push('--enableTelemetry');
}

View file

@ -55,18 +55,6 @@ export default class TypeScriptServiceClientHost extends Disposable {
onCompletionAccepted: (item: vscode.CompletionItem) => void,
) {
super();
const handleProjectCreateOrDelete = () => {
this.triggerAllDiagnostics();
};
const handleProjectChange = () => {
setTimeout(() => {
this.triggerAllDiagnostics();
}, 1500);
};
const configFileWatcher = this._register(vscode.workspace.createFileSystemWatcher('**/[tj]sconfig.json'));
configFileWatcher.onDidCreate(handleProjectCreateOrDelete, this, this._disposables);
configFileWatcher.onDidDelete(handleProjectCreateOrDelete, this, this._disposables);
configFileWatcher.onDidChange(handleProjectChange, this, this._disposables);
const allModeIds = this.getAllModeIds(descriptions, pluginManager);
this.client = this._register(new TypeScriptServiceClient(

View file

@ -74,6 +74,7 @@ interface NoResponseTsServerRequests {
interface AsyncTsServerRequests {
'geterr': [Proto.GeterrRequestArgs, Proto.Response];
'geterrForProject': [Proto.GeterrForProjectRequestArgs, Proto.Response];
}
export type TypeScriptRequests = StandardTsServerRequests & NoResponseTsServerRequests & AsyncTsServerRequests;
@ -137,7 +138,11 @@ export interface ITypeScriptServiceClient {
args: NoResponseTsServerRequests[K][0]
): void;
executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<ServerResponse.Response<Proto.Response>>;
executeAsync<K extends keyof AsyncTsServerRequests>(
command: K,
args: AsyncTsServerRequests[K][0],
token: vscode.CancellationToken
): Promise<ServerResponse.Response<Proto.Response>>;
/**
* Cancel on going geterr requests and re-queue them after `f` has been evaluated.

View file

@ -121,7 +121,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
private readonly onDidChangeTypeScriptVersion: (version: TypeScriptVersion) => void,
public readonly pluginManager: PluginManager,
private readonly logDirectoryProvider: LogDirectoryProvider,
allModeIds: string[]
allModeIds: readonly string[]
) {
super();
this.pathSeparator = path.sep;

View file

@ -3,5 +3,4 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>

View file

@ -55,6 +55,7 @@ export class TypeScriptServiceConfiguration {
public readonly experimentalDecorators: boolean;
public readonly disableAutomaticTypeAcquisition: boolean;
public readonly useSeparateSyntaxServer: boolean;
public readonly enableProjectDiagnostics: boolean;
public readonly maxTsServerMemory: number;
public static loadFromWorkspace(): TypeScriptServiceConfiguration {
@ -74,6 +75,7 @@ export class TypeScriptServiceConfiguration {
this.experimentalDecorators = TypeScriptServiceConfiguration.readExperimentalDecorators(configuration);
this.disableAutomaticTypeAcquisition = TypeScriptServiceConfiguration.readDisableAutomaticTypeAcquisition(configuration);
this.useSeparateSyntaxServer = TypeScriptServiceConfiguration.readUseSeparateSyntaxServer(configuration);
this.enableProjectDiagnostics = TypeScriptServiceConfiguration.readEnableProjectDiagnostics(configuration);
this.maxTsServerMemory = TypeScriptServiceConfiguration.readMaxTsServerMemory(configuration);
}
@ -88,6 +90,7 @@ export class TypeScriptServiceConfiguration {
&& this.disableAutomaticTypeAcquisition === other.disableAutomaticTypeAcquisition
&& arrays.equals(this.tsServerPluginPaths, other.tsServerPluginPaths)
&& this.useSeparateSyntaxServer === other.useSeparateSyntaxServer
&& this.enableProjectDiagnostics === other.enableProjectDiagnostics
&& this.maxTsServerMemory === other.maxTsServerMemory;
}
@ -150,6 +153,10 @@ export class TypeScriptServiceConfiguration {
return configuration.get<boolean>('typescript.tsserver.useSeparateSyntaxServer', true);
}
private static readEnableProjectDiagnostics(configuration: vscode.WorkspaceConfiguration): boolean {
return configuration.get<boolean>('typescript.tsserver.experimental.enableProjectDiagnostics', false);
}
private static readMaxTsServerMemory(configuration: vscode.WorkspaceConfiguration): number {
const defaultMaxMemory = 3072;
const minimumMaxMemory = 128;

View file

@ -68,8 +68,8 @@ function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined {
case 'extends':
case 'param':
case 'template':
const body = (tag.text || '').split(/^([\w\.]+)\s*-?\s*/);
if (body && body.length === 3) {
const body = (tag.text || '').split(/^(\S+)\s*-?\s*/);
if (body?.length === 3) {
const param = body[1];
const doc = body[2];
const label = `*@${tag.name}* \`${param}\``;

View file

@ -1,214 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import TypeScriptServiceClient from '../typescriptServiceClient';
import { Disposable } from './dispose';
import { memoize } from './memoize';
const localize = nls.loadMessageBundle();
interface SurveyData {
/**
* Internal id of the survey. Comes from TypeScript.
*/
readonly id: string;
/**
* Text displayed to the user when survey is triggered.
*/
readonly prompt: string;
/**
* Number of times to trigger with `surveyReady` before showing the survey.
*
* This is cumulative and shared across workspaces.
*/
readonly globalTriggerThreshold: number;
/**
* Survey link.
*/
readonly url: vscode.Uri;
/**
* Milliseconds to wait after 'Remind later' is chosen before trying to prompt again.
*/
readonly remindLaterDelayInMilliseconds: number;
}
const allSurveys: ReadonlyArray<SurveyData> = [
{
id: 'checkJs',
prompt: localize('survey.checkJs.prompt', "Help improve VS Code's support for [checkJs](https://code.visualstudio.com/Docs/languages/javascript#_type-checking) in JavaScript! Since you have been using this feature, would you consider taking a short survey about your experience?"),
globalTriggerThreshold: 10,
url: vscode.Uri.parse('https://www.surveymonkey.com/r/FH8PZQ3'),
remindLaterDelayInMilliseconds: 3 * 24 * 60 * 60 * 1000 // 3 days
}
];
class Survey {
private _hasShownInThisSession = false;
public constructor(
private readonly data: SurveyData,
private readonly memento: vscode.Memento
) { }
public get id(): string { return this.data.id; }
public get prompt(): string { return this.data.prompt; }
public get isActive(): boolean {
return !this._hasShownInThisSession && !this.memento.get<boolean>(this.isCompletedMementoKey);
}
public open(): void {
this.markComplete();
vscode.commands.executeCommand<void>('vscode.open', this.data.url);
}
public remindLater(): void {
// Make sure we don't show again in this session (but don't persist as completed)
this._hasShownInThisSession = true;
// And save off prompt time.
this.memento.update(this.lastPromptTimeMementoKey, Date.now());
}
public trigger(): boolean {
const triggerCount = this.triggerCount + 1;
this.memento.update(this.triggerCountMementoKey, triggerCount);
if (triggerCount >= this.data.globalTriggerThreshold) {
const lastPromptTime = this.memento.get<number>(this.lastPromptTimeMementoKey);
if (!lastPromptTime || isNaN(+lastPromptTime)) {
return true;
}
return (lastPromptTime + this.data.remindLaterDelayInMilliseconds < Date.now());
}
return false;
}
public willShow() {
this._hasShownInThisSession = true;
}
public markComplete() {
this._hasShownInThisSession = true;
this.memento.update(this.isCompletedMementoKey, true);
}
private get triggerCount(): number {
const count = this.memento.get<number>(this.triggerCountMementoKey);
return !count || isNaN(+count) ? 0 : +count;
}
private getMementoKey(part: string): string {
return `survey.v0.${this.id}.${part}`;
}
private get isCompletedMementoKey(): string {
return this.getMementoKey('isComplete');
}
private get lastPromptTimeMementoKey(): string {
return this.getMementoKey('lastPromptTime');
}
private get triggerCountMementoKey(): string {
return this.getMementoKey('globalTriggerCount');
}
}
export class Surveyor extends Disposable {
public constructor(
private readonly memento: vscode.Memento,
serviceClient: TypeScriptServiceClient,
) {
super();
this._register(serviceClient.onSurveyReady(e => this.surveyReady(e.surveyId)));
}
@memoize
private get surveys(): Map<string, Survey> {
return new Map<string, Survey>(
allSurveys.map(data => [data.id, new Survey(data, this.memento)] as [string, Survey]));
}
private surveyReady(surveyId: string): void {
const survey = this.tryGetActiveSurvey(surveyId);
if (survey && survey.trigger()) {
survey.willShow();
this.showSurveyToUser(survey);
}
}
private async showSurveyToUser(survey: Survey): Promise<void> {
enum Choice {
GoToSurvey = 1,
RemindLater = 2,
NeverAgain = 3,
}
interface MessageItem extends vscode.MessageItem {
readonly choice: Choice;
}
const response = await vscode.window.showInformationMessage<MessageItem>(survey.prompt,
{
title: localize('takeShortSurvey', "Take Short Survey"),
choice: Choice.GoToSurvey
}, {
title: localize('remindLater', "Remind Me Later"),
choice: Choice.RemindLater
}, {
title: localize('neverAgain', "Disable JS/TS Surveys"),
choice: Choice.NeverAgain
});
switch (response && response.choice) {
case Choice.GoToSurvey:
survey.open();
break;
case Choice.NeverAgain:
survey.markComplete();
this.disableSurveys();
break;
case Choice.RemindLater:
default: // If user just closes the notification, treat this as a remind later.
survey.remindLater();
break;
}
}
private tryGetActiveSurvey(surveyId: string): Survey | undefined {
const survey = this.surveys.get(surveyId);
if (!survey) {
return undefined;
}
if (this.areSurveysEnabled() && survey.isActive) {
return survey;
}
return undefined;
}
private areSurveysEnabled() {
const config = vscode.workspace.getConfiguration('typescript');
return config.get<boolean>('surveys.enabled', true);
}
private disableSurveys() {
const config = vscode.workspace.getConfiguration('typescript');
config.update('surveys.enabled', false);
}
}

View file

@ -2,9 +2,16 @@
"extends": "../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./out",
"experimentalDecorators": true
"experimentalDecorators": true,
// https://github.com/microsoft/TypeScript/issues/31869#issuecomment-515167432
"baseUrl": "src/\u0000",
"paths": {
"vscode": [
"../../../../src/vs/vscode.d.ts"
]
}
},
"include": [
"src/**/*"
]
}
}

View file

@ -17,14 +17,13 @@
"main": "./out/extension.js",
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./"
"compile": "gulp compile-extension:vscode-account",
"watch": "gulp watch-extension:vscode-account"
},
"devDependencies": {
"typescript": "^3.7.4",
"tslint": "^5.12.1",
"@types/node": "^10.12.21",
"@types/keytar": "^4.0.1",
"@types/vscode": "^1.41.0"
"@types/keytar": "^4.0.1"
}
}

View file

@ -22,7 +22,7 @@ interface IToken {
accessToken: string;
refreshToken: string;
displayName: string;
accountName: string;
scope: string;
sessionId: string; // The account id + the scope
}
@ -33,6 +33,7 @@ interface ITokenClaims {
unique_name?: string;
oid?: string;
altsecid?: string;
ipd?: string;
scp: string;
}
@ -52,6 +53,8 @@ function parseQuery(uri: vscode.Uri) {
export const onDidChangeSessions = new vscode.EventEmitter<void>();
export const REFRESH_NETWORK_FAILURE = 'Network failure';
class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.UriHandler {
public handleUri(uri: vscode.Uri) {
this.fire(uri);
@ -90,7 +93,7 @@ export class AzureActiveDirectoryService {
try {
await this.refreshToken(session.refreshToken, session.scope);
} catch (e) {
await this.logout(session.id);
this.handleTokenRefreshFailure(e, session.id, session.refreshToken, session.scope, false);
}
});
@ -133,7 +136,7 @@ export class AzureActiveDirectoryService {
await this.refreshToken(session.refreshToken, session.scope);
didChange = true;
} catch (e) {
await this.logout(session.id);
this.handleTokenRefreshFailure(e, session.id, session.refreshToken, session.scope, false);
}
}
});
@ -169,11 +172,11 @@ export class AzureActiveDirectoryService {
}, 1000 * 30);
}
private convertToSession(token: IToken): vscode.Session {
private convertToSession(token: IToken): vscode.AuthenticationSession {
return {
id: token.sessionId,
accessToken: token.accessToken,
displayName: token.displayName,
accessToken: () => Promise.resolve(token.accessToken),
accountName: token.accountName,
scopes: token.scope.split(' ')
};
}
@ -187,7 +190,7 @@ export class AzureActiveDirectoryService {
}
}
get sessions(): vscode.Session[] {
get sessions(): vscode.AuthenticationSession[] {
return this._tokens.map(token => this.convertToSession(token));
}
@ -287,7 +290,7 @@ export class AzureActiveDirectoryService {
});
vscode.env.openExternal(uri);
const timeoutPromise = new Promise((resolve: (value: IToken) => void, reject) => {
const timeoutPromise = new Promise((_: (value: IToken) => void, reject) => {
const wait = setTimeout(() => {
clearTimeout(wait);
reject('Login timed out.');
@ -334,18 +337,14 @@ export class AzureActiveDirectoryService {
this._tokens.push(token);
}
const existingTimeout = this._refreshTimeouts.get(token.sessionId);
if (existingTimeout) {
clearTimeout(existingTimeout);
}
this.clearSessionTimeout(token.sessionId);
this._refreshTimeouts.set(token.sessionId, setTimeout(async () => {
try {
await this.refreshToken(token.refreshToken, scope);
} catch (e) {
await this.logout(token.sessionId);
} finally {
onDidChangeSessions.fire();
} catch (e) {
this.handleTokenRefreshFailure(e, token.sessionId, token.refreshToken, scope, true);
}
}, 1000 * (parseInt(token.expiresIn) - 30)));
@ -360,8 +359,8 @@ export class AzureActiveDirectoryService {
accessToken: json.access_token,
refreshToken: json.refresh_token,
scope,
sessionId: claims.tid + (claims.oid || claims.altsecid) + scope,
displayName: claims.email || claims.unique_name || 'user@example.com'
sessionId: `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}/${scope}`,
accountName: claims.email || claims.unique_name || 'user@example.com'
};
}
@ -459,29 +458,73 @@ export class AzureActiveDirectoryService {
post.end();
post.on('error', err => {
Logger.error(err.message);
reject(err);
reject(new Error(REFRESH_NETWORK_FAILURE));
});
});
}
public async logout(sessionId: string) {
Logger.info(`Logging out of session '${sessionId}'`);
private clearSessionTimeout(sessionId: string): void {
const timeout = this._refreshTimeouts.get(sessionId);
if (timeout) {
clearTimeout(timeout);
this._refreshTimeouts.delete(sessionId);
}
}
private removeInMemorySessionData(sessionId: string) {
const tokenIndex = this._tokens.findIndex(token => token.sessionId === sessionId);
if (tokenIndex > -1) {
this._tokens.splice(tokenIndex, 1);
}
this.clearSessionTimeout(sessionId);
}
private async handleTokenRefreshFailure(e: Error, sessionId: string, refreshToken: string, scope: string, sendChangeEvent: boolean): Promise<void> {
if (e.message === REFRESH_NETWORK_FAILURE) {
this.handleRefreshNetworkError(sessionId, refreshToken, scope);
} else {
await this.logout(sessionId);
if (sendChangeEvent) {
onDidChangeSessions.fire();
}
}
}
private handleRefreshNetworkError(sessionId: string, refreshToken: string, scope: string, attempts: number = 1) {
if (attempts === 5) {
Logger.error('Token refresh failed after 5 attempts');
return;
}
if (attempts === 1) {
this.removeInMemorySessionData(sessionId);
onDidChangeSessions.fire();
}
const delayBeforeRetry = 5 * attempts * attempts;
this.clearSessionTimeout(sessionId);
this._refreshTimeouts.set(sessionId, setTimeout(async () => {
try {
await this.refreshToken(refreshToken, scope);
onDidChangeSessions.fire();
} catch (e) {
await this.handleRefreshNetworkError(sessionId, refreshToken, scope, attempts + 1);
}
}, 1000 * delayBeforeRetry));
}
public async logout(sessionId: string) {
Logger.info(`Logging out of session '${sessionId}'`);
this.removeInMemorySessionData(sessionId);
if (this._tokens.length === 0) {
await keychain.deleteToken();
} else {
this.storeTokenData();
}
const timeout = this._refreshTimeouts.get(sessionId);
if (timeout) {
clearTimeout(timeout);
this._refreshTimeouts.delete(sessionId);
}
}
public async clearSessions() {

View file

@ -6,7 +6,7 @@
import * as vscode from 'vscode';
import { AzureActiveDirectoryService, onDidChangeSessions } from './AADHelper';
export async function activate(context: vscode.ExtensionContext) {
export async function activate(_: vscode.ExtensionContext) {
const loginService = new AzureActiveDirectoryService();

View file

@ -7,6 +7,7 @@
// how we load it
import * as keytarType from 'keytar';
import { env } from 'vscode';
import Logger from './logger';
function getKeytar(): Keytar | undefined {
try {
@ -44,22 +45,27 @@ export class Keychain {
return await this.keytar.setPassword(SERVICE_ID, ACCOUNT_ID, token);
} catch (e) {
// Ignore
Logger.error(`Setting token failed: ${e}`);
}
}
async getToken() {
async getToken(): Promise<string | null | undefined> {
try {
return await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID);
} catch (e) {
// Ignore
Logger.error(`Getting token failed: ${e}`);
return Promise.resolve(undefined);
}
}
async deleteToken() {
async deleteToken(): Promise<boolean | undefined> {
try {
return await this.keytar.deletePassword(SERVICE_ID, ACCOUNT_ID);
} catch (e) {
// Ignore
Logger.error(`Deleting token failed: ${e}`);
return Promise.resolve(undefined);
}
}
}

View file

@ -0,0 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>

View file

@ -1,61 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* This is the place for API experiments and proposals.
* These API are NOT stable and subject to change. They are only available in the Insiders
* distribution and CANNOT be used in published extensions.
*
* To test these API in local environment:
* - Use Insiders release of VS Code.
* - Add `"enableProposedApi": true` to your package.json.
* - Copy this file to your project.
*/
declare module 'vscode' {
export interface Session {
id: string;
accessToken: string;
displayName: string;
scopes: string[]
}
export interface AuthenticationProvider {
readonly id: string;
readonly displayName: string;
readonly onDidChangeSessions: Event<void>;
/**
* Returns an array of current sessions.
*/
getSessions(): Promise<ReadonlyArray<Session>>;
/**
* Prompts a user to login.
*/
login(scopes: string[]): Promise<Session>;
logout(sessionId: string): Promise<void>;
}
export namespace authentication {
export function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable;
/**
* Fires with the provider id that was registered or unregistered.
*/
export const onDidRegisterAuthenticationProvider: Event<string>;
export const onDidUnregisterAuthenticationProvider: Event<string>;
export const providers: ReadonlyArray<AuthenticationProvider>;
}
// #region Ben - extension auth flow (desktop+web)
export namespace env {
export function asExternalUri(target: Uri): Thenable<Uri>
}
}

View file

@ -1,24 +1,13 @@
{
"extends": "../shared.tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": [
"es6",
"es2016",
"dom"
],
"outDir": "./out",
"experimentalDecorators": true,
"typeRoots": [
"node_modules/@types",
"src/typings"
],
"sourceMap": true,
"rootDir": "src",
"strict": true,
"noImplicitAny": true
"./node_modules/@types"
]
},
"exclude": [
"node_modules",
".vscode-test"
"include": [
"src/**/*"
]
}

View file

@ -30,11 +30,6 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c"
integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==
"@types/vscode@^1.41.0":
version "1.41.0"
resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.41.0.tgz#b0d75920220f84e07093285e59180c0f11d336cd"
integrity sha512-7SfeY5u9jgiELwxyLB3z7l6l/GbN9CqpCQGkcRlB7tKRFBxzbz2PoBfGrLxI1vRfUCIq5+hg5vtDHExwq5j3+A==
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"

View file

@ -14,6 +14,11 @@
import * as vscode from 'vscode';
declare const window: unknown;
declare const TextEncoder: any;
declare const TextDecoder: any;
declare const URL: any;
const textEncoder = new TextEncoder();
const SCHEME = 'memfs';

View file

@ -105,7 +105,7 @@ suite('commands namespace tests', () => {
});
test('api-command: vscode.open', function () {
let uri = Uri.parse(workspace.workspaceFolders![0].uri.toString() + '/image.png');
let uri = Uri.parse(workspace.workspaceFolders![0].uri.toString() + '/far.js');
let a = commands.executeCommand('vscode.open', uri).then(() => assert.ok(true), () => assert.ok(false));
let b = commands.executeCommand('vscode.open', uri, ViewColumn.Two).then(() => assert.ok(true), () => assert.ok(false));
let c = commands.executeCommand('vscode.open').then(() => assert.ok(false), () => assert.ok(true));

View file

@ -0,0 +1,126 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { debug, workspace, Disposable, commands, window } from 'vscode';
import { disposeAll } from '../utils';
import { basename } from 'path';
suite('Debug', function () {
test('breakpoints', async function () {
assert.equal(debug.breakpoints.length, 0);
let onDidChangeBreakpointsCounter = 0;
const toDispose: Disposable[] = [];
toDispose.push(debug.onDidChangeBreakpoints(() => {
onDidChangeBreakpointsCounter++;
}));
debug.addBreakpoints([{ id: '1', enabled: true }, { id: '2', enabled: false, condition: '2 < 5' }]);
assert.equal(onDidChangeBreakpointsCounter, 1);
assert.equal(debug.breakpoints.length, 2);
assert.equal(debug.breakpoints[0].id, '1');
assert.equal(debug.breakpoints[1].id, '2');
assert.equal(debug.breakpoints[1].condition, '2 < 5');
debug.removeBreakpoints([{ id: '1', enabled: true }]);
assert.equal(onDidChangeBreakpointsCounter, 2);
assert.equal(debug.breakpoints.length, 1);
debug.removeBreakpoints([{ id: '2', enabled: false }]);
assert.equal(onDidChangeBreakpointsCounter, 3);
assert.equal(debug.breakpoints.length, 0);
disposeAll(toDispose);
});
test('start debugging', async function () {
assert.equal(debug.activeDebugSession, undefined);
let stoppedEvents = 0;
let variablesReceived: () => void;
let initializedReceived: () => void;
let configurationDoneReceived: () => void;
const firstVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve);
const toDispose: Disposable[] = [];
toDispose.push(debug.registerDebugAdapterTrackerFactory('node2', {
createDebugAdapterTracker: () => ({
onDidSendMessage: m => {
if (m.event === 'stopped') {
stoppedEvents++;
}
if (m.type === 'response' && m.command === 'variables') {
variablesReceived();
}
if (m.event === 'initialized') {
initializedReceived();
}
if (m.command === 'configurationDone') {
configurationDoneReceived();
}
}
})
}));
const initializedPromise = new Promise<void>(resolve => initializedReceived = resolve);
const configurationDonePromise = new Promise<void>(resolve => configurationDoneReceived = resolve);
// Do not await debug start to return due to https://github.com/microsoft/vscode/issues/90134
debug.startDebugging(workspace.workspaceFolders![0], 'Launch debug.js');
await initializedPromise;
await configurationDonePromise;
// Do not verify activeDebugSession due to same flakiness that sometimes start debugging does not return
// assert.notEqual(debug.activeDebugSession, undefined);
// assert.equal(debug.activeDebugSession?.name, 'Launch debug.js');
await firstVariablesRetrieved;
assert.equal(stoppedEvents, 1);
const secondVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve);
await commands.executeCommand('workbench.action.debug.stepOver');
await secondVariablesRetrieved;
assert.equal(stoppedEvents, 2);
const editor = window.activeTextEditor;
assert.notEqual(editor, undefined);
assert.equal(basename(editor!.document.fileName), 'debug.js');
const thirdVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve);
await commands.executeCommand('workbench.action.debug.stepOver');
await thirdVariablesRetrieved;
assert.equal(stoppedEvents, 3);
const fourthVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve);
await commands.executeCommand('workbench.action.debug.stepInto');
await fourthVariablesRetrieved;
assert.equal(stoppedEvents, 4);
const fifthVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve);
await commands.executeCommand('workbench.action.debug.stepOut');
await fifthVariablesRetrieved;
assert.equal(stoppedEvents, 5);
let sessionTerminated: () => void;
toDispose.push(debug.onDidTerminateDebugSession(() => {
sessionTerminated();
}));
const sessionTerminatedPromise = new Promise<void>(resolve => sessionTerminated = resolve);
await commands.executeCommand('workbench.action.debug.stop');
await sessionTerminatedPromise;
assert.equal(debug.activeDebugSession, undefined);
disposeAll(toDispose);
});
test('start debugging failure', async function () {
let errorCount = 0;
try {
await debug.startDebugging(workspace.workspaceFolders![0], 'non existent');
} catch (e) {
errorCount++;
}
assert.equal(errorCount, 1);
});
});

View file

@ -13,7 +13,8 @@ const webviewId = 'myWebview';
const testDocument = join(vscode.workspace.rootPath || '', './bower.json');
suite('Webview tests', () => {
// TODO: Re-enable after https://github.com/microsoft/vscode/issues/88415
('electron' in process.versions ? suite.skip : suite)('Webview tests', () => {
const disposables: vscode.Disposable[] = [];
function _register<T extends vscode.Disposable>(disposable: T) {

View file

@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch debug.js",
"stopOnEntry": true,
"program": "${workspaceFolder}/debug.js"
}
]
}

View file

@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
let y = 0;
for (let i = 0; i < 100; i++) {
console.log(y);
y = y + i;
}

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.43.0",
"distro": "efecc155935e7eb2f21b904b59991b07b0d58e58",
"distro": "b975cdab7c0c0e7705fa7eac4c8411ad8a790f4b",
"author": {
"name": "Microsoft Corporation"
},
@ -10,12 +10,13 @@
"private": true,
"scripts": {
"test": "mocha",
"test-browser": "node test/unit/browser/index.js",
"preinstall": "node build/npm/preinstall.js",
"postinstall": "node build/npm/postinstall.js",
"compile": "gulp compile --max_old_space_size=4095",
"watch": "gulp watch --max_old_space_size=4095",
"watch-client": "gulp watch-client --max_old_space_size=4095",
"monaco-editor-test": "mocha --only-monaco-editor",
"mocha": "mocha test/unit/node/all.js --delay",
"precommit": "node build/gulpfile.hygiene.js",
"gulp": "gulp --max_old_space_size=8192",
"electron": "node build/lib/electron",
@ -34,7 +35,7 @@
"dependencies": {
"applicationinsights": "1.0.8",
"chokidar": "3.2.3",
"graceful-fs": "4.1.11",
"graceful-fs": "4.2.3",
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.3",
"iconv-lite": "0.5.0",
@ -55,10 +56,11 @@
"vscode-ripgrep": "^1.5.7",
"vscode-sqlite3": "4.0.9",
"vscode-textmate": "4.4.0",
"xterm": "4.4.0-beta.15",
"xterm-addon-search": "0.4.0-beta4",
"xterm": "4.4.0",
"xterm-addon-search": "0.4.0",
"xterm-addon-unicode11": "0.1.1",
"xterm-addon-web-links": "0.2.1",
"xterm-addon-webgl": "0.5.0-beta.7",
"xterm-addon-webgl": "0.5.0",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},
@ -67,6 +69,7 @@
"@types/applicationinsights": "0.20.0",
"@types/chokidar": "2.1.3",
"@types/cookie": "^0.3.3",
"@types/debug": "^4.1.5",
"@types/graceful-fs": "4.1.2",
"@types/http-proxy-agent": "^2.0.1",
"@types/iconv-lite": "0.0.1",
@ -91,11 +94,10 @@
"coveralls": "^2.11.11",
"cson-parser": "^1.3.3",
"debounce": "^1.0.0",
"electron": "6.1.6",
"electron": "7.1.11",
"eslint": "6.8.0",
"eslint-plugin-jsdoc": "^19.1.0",
"event-stream": "3.3.4",
"express": "^4.13.1",
"fancy-log": "^1.3.3",
"fast-plist": "0.1.2",
"glob": "^5.0.13",
@ -137,6 +139,7 @@
"opn": "^6.0.0",
"optimist": "0.3.5",
"p-all": "^1.0.0",
"playwright": "^0.10.0",
"pump": "^1.0.1",
"queue": "3.0.6",
"rcedit": "^1.1.0",
@ -154,7 +157,8 @@
"vscode-nls-dev": "^3.3.1",
"webpack": "^4.16.5",
"webpack-cli": "^3.3.8",
"webpack-stream": "^5.1.1"
"webpack-stream": "^5.1.1",
"yaserver": "^0.2.0"
},
"repository": {
"type": "git",

View file

@ -5,7 +5,7 @@
"applicationinsights": "1.0.8",
"chokidar": "3.2.3",
"cookie": "^0.4.0",
"graceful-fs": "4.1.11",
"graceful-fs": "4.2.3",
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.3",
"iconv-lite": "0.5.0",
@ -20,10 +20,11 @@
"vscode-proxy-agent": "^0.5.2",
"vscode-ripgrep": "^1.5.7",
"vscode-textmate": "4.4.0",
"xterm": "4.4.0-beta.15",
"xterm-addon-search": "0.4.0-beta4",
"xterm": "4.4.0",
"xterm-addon-search": "0.4.0",
"xterm-addon-unicode11": "0.1.1",
"xterm-addon-web-links": "0.2.1",
"xterm-addon-webgl": "0.5.0-beta.7",
"xterm-addon-webgl": "0.5.0",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},

View file

@ -5,9 +5,10 @@
"onigasm-umd": "2.2.5",
"semver-umd": "^5.5.5",
"vscode-textmate": "4.4.0",
"xterm": "4.4.0-beta.15",
"xterm-addon-search": "0.4.0-beta4",
"xterm": "4.4.0",
"xterm-addon-search": "0.4.0",
"xterm-addon-unicode11": "0.1.1",
"xterm-addon-web-links": "0.2.1",
"xterm-addon-webgl": "0.5.0-beta.7"
"xterm-addon-webgl": "0.5.0"
}
}

View file

@ -31,22 +31,27 @@ vscode-textmate@4.4.0:
dependencies:
oniguruma "^7.2.0"
xterm-addon-search@0.4.0-beta4:
version "0.4.0-beta4"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.4.0-beta4.tgz#7762ea342c6b4f5e824d83466bd93793c9d7d779"
integrity sha512-TIbEBVhydGIxcyu/CfKJbD+BKHisMGbkAfaWlCPaWis2Xmw8yE7CKrCPn+lhZYl1MdjDVEmb8lQI6WetbC2OZA==
xterm-addon-search@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.4.0.tgz#a7beadb3caa7330eb31fb1f17d92de25537684a1"
integrity sha512-g07qb/Z4aSfrQ25e6Z6rz6KiExm2DvesQXkx+eA715VABBr5VM/9Jf0INoCiDSYy/nn7rpna+kXiGVJejIffKg==
xterm-addon-unicode11@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.1.1.tgz#b209ef137db38096f68636af4ef4d0c0acba85ad"
integrity sha512-z6vJTL+dpNljwAYzYoyDjJP8A2XjZuEosl0sRa+FGRf3jEyEVWquDM53MfUd1ztVdAPQ839qR6eYK1BXV04Bhw==
xterm-addon-web-links@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2"
integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ==
xterm-addon-webgl@0.5.0-beta.7:
version "0.5.0-beta.7"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0-beta.7.tgz#b7b95a362e942ad6f86fa286d7b7bd8ee3e7cf67"
integrity sha512-v6aCvhm1C6mvaurGwUYQfyhb2cAUyuVnzf3Ob/hy5ebtyzUj4wW0N9NbqDEJk67UeMi1lV2xZqrO5gNeTpVqFA==
xterm-addon-webgl@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0.tgz#c1031dc7599cce3509824643ab5f15361c928e3e"
integrity sha512-hQrvabKCnwXFaEZ+YtoJM9Pm0CIBXL5KSwoU+RiGStU3KYTAcqYP2GsH3dWdvKX6kTWhWLS81dtDsGkfbOciuA==
xterm@4.4.0-beta.15:
version "4.4.0-beta.15"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0-beta.15.tgz#5897bf79d29d1a2496ccd54665aded28c341b1cc"
integrity sha512-Dvz1CMCYKeoxPF7uIDznbRgUA2Mct49Bq93K2nnrDU0pDMM3Sf1t9fkEyz59wxSx5XEHVdLS80jywsz4sjXBjQ==
xterm@4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0.tgz#5915d3c4c8800fadbcf555a0a603c672ab9df589"
integrity sha512-JGIpigWM3EBWvnS3rtBuefkiToIILSK1HYMXy4BCsUpO+O4UeeV+/U1AdAXgCB6qJrnPNb7yLgBsVCQUNMteig==

View file

@ -155,12 +155,7 @@ glob-parent@~5.1.0:
dependencies:
is-glob "^4.0.1"
graceful-fs@4.1.11:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=
graceful-fs@^4.1.2, graceful-fs@^4.1.6:
graceful-fs@4.2.3, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
version "4.2.3"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
@ -418,25 +413,30 @@ vscode-windows-registry@1.0.2:
resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.2.tgz#b863e704a6a69c50b3098a55fbddbe595b0c124a"
integrity sha512-/CLLvuOSM2Vme2z6aNyB+4Omd7hDxpf4Thrt8ImxnXeQtxzel2bClJpFQvQqK/s4oaXlkBKS7LqVLeZM+uSVIA==
xterm-addon-search@0.4.0-beta4:
version "0.4.0-beta4"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.4.0-beta4.tgz#7762ea342c6b4f5e824d83466bd93793c9d7d779"
integrity sha512-TIbEBVhydGIxcyu/CfKJbD+BKHisMGbkAfaWlCPaWis2Xmw8yE7CKrCPn+lhZYl1MdjDVEmb8lQI6WetbC2OZA==
xterm-addon-search@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.4.0.tgz#a7beadb3caa7330eb31fb1f17d92de25537684a1"
integrity sha512-g07qb/Z4aSfrQ25e6Z6rz6KiExm2DvesQXkx+eA715VABBr5VM/9Jf0INoCiDSYy/nn7rpna+kXiGVJejIffKg==
xterm-addon-unicode11@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.1.1.tgz#b209ef137db38096f68636af4ef4d0c0acba85ad"
integrity sha512-z6vJTL+dpNljwAYzYoyDjJP8A2XjZuEosl0sRa+FGRf3jEyEVWquDM53MfUd1ztVdAPQ839qR6eYK1BXV04Bhw==
xterm-addon-web-links@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2"
integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ==
xterm-addon-webgl@0.5.0-beta.7:
version "0.5.0-beta.7"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0-beta.7.tgz#b7b95a362e942ad6f86fa286d7b7bd8ee3e7cf67"
integrity sha512-v6aCvhm1C6mvaurGwUYQfyhb2cAUyuVnzf3Ob/hy5ebtyzUj4wW0N9NbqDEJk67UeMi1lV2xZqrO5gNeTpVqFA==
xterm-addon-webgl@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0.tgz#c1031dc7599cce3509824643ab5f15361c928e3e"
integrity sha512-hQrvabKCnwXFaEZ+YtoJM9Pm0CIBXL5KSwoU+RiGStU3KYTAcqYP2GsH3dWdvKX6kTWhWLS81dtDsGkfbOciuA==
xterm@4.4.0-beta.15:
version "4.4.0-beta.15"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0-beta.15.tgz#5897bf79d29d1a2496ccd54665aded28c341b1cc"
integrity sha512-Dvz1CMCYKeoxPF7uIDznbRgUA2Mct49Bq93K2nnrDU0pDMM3Sf1t9fkEyz59wxSx5XEHVdLS80jywsz4sjXBjQ==
xterm@4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0.tgz#5915d3c4c8800fadbcf555a0a603c672ab9df589"
integrity sha512-JGIpigWM3EBWvnS3rtBuefkiToIILSK1HYMXy4BCsUpO+O4UeeV+/U1AdAXgCB6qJrnPNb7yLgBsVCQUNMteig==
yauzl@^2.9.2:
version "2.10.0"

View file

@ -3,7 +3,7 @@ setlocal
pushd %~dp0\..
set VSCODEUSERDATADIR=%TMP%\vscodeuserfolder-%RANDOM%-%TIME:~6,5%
set VSCODEUSERDATADIR=%TEMP%\vscodeuserfolder-%RANDOM%-%TIME:~6,2%
:: Figure out which Electron to use for running tests
if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" (
@ -12,7 +12,7 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" (
set INTEGRATION_TEST_ELECTRON_PATH=.\scripts\code.bat
set VSCODE_BUILD_BUILTIN_EXTENSIONS_SILENCE_PLEASE=1
echo "Running integration tests out of sources."
echo Running integration tests out of sources.
) else (
:: Run from a built: need to compile all test extensions
call yarn gulp compile-extension:vscode-api-tests
@ -22,13 +22,14 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" (
call yarn gulp compile-extension:css-language-features-server
call yarn gulp compile-extension:html-language-features-server
call yarn gulp compile-extension:json-language-features-server
call yarn gulp compile-extension:git
:: Configuration for more verbose output
set VSCODE_CLI=1
set ELECTRON_ENABLE_LOGGING=1
set ELECTRON_ENABLE_STACK_DUMPING=1
echo "Running integration tests with '%INTEGRATION_TEST_ELECTRON_PATH%' as build."
echo Running integration tests with '%INTEGRATION_TEST_ELECTRON_PATH%' as build.
)
:: Integration & performance tests in AMD
@ -49,6 +50,12 @@ if %errorlevel% neq 0 exit /b %errorlevel%
call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% .
if %errorlevel% neq 0 exit /b %errorlevel%
for /f "delims=" %%i in ('node -p "require('fs').realpathSync.native(require('os').tmpdir())"') do set TEMPDIR=%%i
set GITWORKSPACE=%TEMPDIR%\git-%RANDOM%
mkdir %GITWORKSPACE%
call "%INTEGRATION_TEST_ELECTRON_PATH%" %GITWORKSPACE% --extensionDevelopmentPath=%~dp0\..\extensions\git --extensionTestsPath=%~dp0\..\extensions\git\out\test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
:: Tests in commonJS (HTML, CSS, JSON language server tests...)
call .\scripts\node-electron.bat .\node_modules\mocha\bin\_mocha .\extensions\*\server\out\test\**\*.test.js
if %errorlevel% neq 0 exit /b %errorlevel%

View file

@ -4,13 +4,12 @@ set -e
if [[ "$OSTYPE" == "darwin"* ]]; then
realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; }
ROOT=$(dirname $(dirname $(realpath "$0")))
VSCODEUSERDATADIR=`mktemp -d -t 'myuserdatadir'`
else
ROOT=$(dirname $(dirname $(readlink -f $0)))
VSCODEUSERDATADIR=`mktemp -d 2>/dev/null`
LINUX_NO_SANDBOX="--no-sandbox" # Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox.
fi
VSCODEUSERDATADIR=`mktemp -d 2>/dev/null`
cd $ROOT
# Figure out which Electron to use for running tests
@ -29,6 +28,7 @@ else
yarn gulp compile-extension:css-language-features-server
yarn gulp compile-extension:html-language-features-server
yarn gulp compile-extension:json-language-features-server
yarn gulp compile-extension:git
# Configuration for more verbose output
export VSCODE_CLI=1
@ -46,6 +46,7 @@ fi
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $(mktemp -d 2>/dev/null) --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
mkdir -p $ROOT/extensions/emmet/test-fixtures
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR

View file

@ -17,7 +17,7 @@ if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron
:: Run tests
set ELECTRON_ENABLE_LOGGING=1
%CODE% .\test\electron\index.js %*
%CODE% .\test\unit\electron\index.js %*
popd

View file

@ -29,10 +29,10 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
cd $ROOT ; ulimit -n 4096 ; \
ELECTRON_ENABLE_LOGGING=1 \
"$CODE" \
test/electron/index.js "$@"
test/unit/electron/index.js "$@"
else
cd $ROOT ; \
ELECTRON_ENABLE_LOGGING=1 \
"$CODE" \
test/electron/index.js --no-sandbox "$@" # Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox.
test/unit/electron/index.js --no-sandbox "$@" # Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox.
fi

View file

@ -120,6 +120,6 @@ export const isWebKit = (userAgent.indexOf('AppleWebKit') >= 0);
export const isChrome = (userAgent.indexOf('Chrome') >= 0);
export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0));
export const isWebkitWebView = (!isChrome && !isSafari && isWebKit);
export const isIPad = (userAgent.indexOf('iPad') >= 0);
export const isIPad = (userAgent.indexOf('iPad') >= 0 || (isSafari && navigator.maxTouchPoints > 0));
export const isEdgeWebView = isEdge && (userAgent.indexOf('WebView/') >= 0);
export const isStandalone = (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches);

View file

@ -28,6 +28,7 @@ export class FastDomNode<T extends HTMLElement> {
private _backgroundColor: string;
private _layerHint: boolean;
private _contain: 'none' | 'strict' | 'content' | 'size' | 'layout' | 'style' | 'paint';
private _boxShadow: string;
constructor(domNode: T) {
this.domNode = domNode;
@ -51,6 +52,7 @@ export class FastDomNode<T extends HTMLElement> {
this._backgroundColor = '';
this._layerHint = false;
this._contain = 'none';
this._boxShadow = '';
}
public setMaxWidth(maxWidth: number): void {
@ -218,6 +220,14 @@ export class FastDomNode<T extends HTMLElement> {
this.domNode.style.transform = this._layerHint ? 'translate3d(0px, 0px, 0px)' : '';
}
public setBoxShadow(boxShadow: string): void {
if (this._boxShadow === boxShadow) {
return;
}
this._boxShadow = boxShadow;
this.domNode.style.boxShadow = boxShadow;
}
public setContain(contain: 'none' | 'strict' | 'content' | 'size' | 'layout' | 'style' | 'paint'): void {
if (this._contain === contain) {
return;

View file

@ -88,7 +88,7 @@ export class MenuBar extends Disposable {
private numMenusShown: number = 0;
private menuStyle: IMenuStyles | undefined;
private overflowLayoutScheduled: IDisposable | null = null;
private overflowLayoutScheduled: IDisposable | undefined = undefined;
constructor(private container: HTMLElement, private options: IMenuBarOptions = {}) {
super();
@ -419,9 +419,8 @@ export class MenuBar extends Disposable {
DOM.removeNode(this.overflowMenu.titleElement);
DOM.removeNode(this.overflowMenu.buttonElement);
if (this.overflowLayoutScheduled) {
this.overflowLayoutScheduled = dispose(this.overflowLayoutScheduled);
}
dispose(this.overflowLayoutScheduled);
this.overflowLayoutScheduled = undefined;
}
blur(): void {
@ -561,7 +560,7 @@ export class MenuBar extends Disposable {
if (!this.overflowLayoutScheduled) {
this.overflowLayoutScheduled = DOM.scheduleAtNextAnimationFrame(() => {
this.updateOverflowAction();
this.overflowLayoutScheduled = null;
this.overflowLayoutScheduled = undefined;
});
}

View file

@ -287,6 +287,7 @@ export abstract class AbstractScrollableElement extends Widget {
this._options.handleMouseWheel = massagedOptions.handleMouseWheel;
this._options.mouseWheelScrollSensitivity = massagedOptions.mouseWheelScrollSensitivity;
this._options.fastScrollSensitivity = massagedOptions.fastScrollSensitivity;
this._options.scrollPredominantAxis = massagedOptions.scrollPredominantAxis;
this._setListeningToMouseWheel(this._options.handleMouseWheel);
if (!this._options.lazyRender) {
@ -338,6 +339,14 @@ export abstract class AbstractScrollableElement extends Widget {
let deltaY = e.deltaY * this._options.mouseWheelScrollSensitivity;
let deltaX = e.deltaX * this._options.mouseWheelScrollSensitivity;
if (this._options.scrollPredominantAxis) {
if (Math.abs(deltaY) >= Math.abs(deltaX)) {
deltaX = 0;
} else {
deltaY = 0;
}
}
if (this._options.flipAxes) {
[deltaY, deltaX] = [deltaX, deltaY];
}
@ -557,6 +566,7 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme
scrollYToX: (typeof opts.scrollYToX !== 'undefined' ? opts.scrollYToX : false),
mouseWheelScrollSensitivity: (typeof opts.mouseWheelScrollSensitivity !== 'undefined' ? opts.mouseWheelScrollSensitivity : 1),
fastScrollSensitivity: (typeof opts.fastScrollSensitivity !== 'undefined' ? opts.fastScrollSensitivity : 5),
scrollPredominantAxis: (typeof opts.scrollPredominantAxis !== 'undefined' ? opts.scrollPredominantAxis : true),
mouseWheelSmoothScroll: (typeof opts.mouseWheelSmoothScroll !== 'undefined' ? opts.mouseWheelSmoothScroll : true),
arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11),

View file

@ -55,6 +55,13 @@ export interface ScrollableElementCreationOptions {
* Defaults to 5.
*/
fastScrollSensitivity?: number;
/**
* Whether the scrollable will only scroll along the predominant axis when scrolling both
* vertically and horizontally at the same time.
* Prevents horizontal drift when scrolling vertically on a trackpad.
* Defaults to true.
*/
scrollPredominantAxis?: boolean;
/**
* Height for vertical arrows (top/bottom) and width for horizontal arrows (left/right).
* Defaults to 11.
@ -113,6 +120,7 @@ export interface ScrollableElementChangeOptions {
handleMouseWheel?: boolean;
mouseWheelScrollSensitivity?: number;
fastScrollSensitivity: number;
scrollPredominantAxis: boolean;
}
export interface ScrollableElementResolvedOptions {
@ -125,6 +133,7 @@ export interface ScrollableElementResolvedOptions {
alwaysConsumeMouseWheel: boolean;
mouseWheelScrollSensitivity: number;
fastScrollSensitivity: number;
scrollPredominantAxis: boolean;
mouseWheelSmoothScroll: boolean;
arrowSize: number;
listenOnDomNode: HTMLElement | null;

View file

@ -585,6 +585,6 @@ export function asArray<T>(x: T | T[]): T[] {
return Array.isArray(x) ? x : [x];
}
export function getRandomElement<T>(arr: T[]): T {
export function getRandomElement<T>(arr: T[]): T | undefined {
return arr[Math.floor(Math.random() * arr.length)];
}

View file

@ -91,6 +91,9 @@ export function toDisposable(fn: () => void): IDisposable {
}
export class DisposableStore implements IDisposable {
static DISABLE_DISPOSED_WARNING = false;
private _toDispose = new Set<IDisposable>();
private _isDisposed = false;
@ -127,7 +130,9 @@ export class DisposableStore implements IDisposable {
markTracked(t);
if (this._isDisposed) {
console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack);
if (!DisposableStore.DISABLE_DISPOSED_WARNING) {
console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack);
}
} else {
this._toDispose.add(t);
}

View file

@ -227,7 +227,7 @@ export function relativePath(from: URI, to: URI, ignoreCase = hasToIgnoreCase(fr
return undefined;
}
if (from.scheme === Schemas.file) {
const relativePath = paths.relative(from.path, to.path);
const relativePath = paths.relative(originalFSPath(from), originalFSPath(to));
return isWindows ? extpath.toSlashes(relativePath) : relativePath;
}
let fromPath = from.path || '/', toPath = to.path || '/';

View file

@ -126,8 +126,8 @@ export class ConfigWatcher<T> extends Disposable implements IConfigWatcher<T> {
}
private async handleSymbolicLink(): Promise<void> {
const { stat, isSymbolicLink } = await statLink(this._path);
if (isSymbolicLink && !stat.isDirectory()) {
const { stat, symbolicLink } = await statLink(this._path);
if (symbolicLink && !stat.isDirectory()) {
const realPath = await realpath(this._path);
this.watch(realPath, false);

View file

@ -14,7 +14,18 @@ import { promisify } from 'util';
import { isRootOrDriveLetter } from 'vs/base/common/extpath';
import { generateUuid } from 'vs/base/common/uuid';
import { normalizeNFC } from 'vs/base/common/normalization';
import { encode, encodeStream } from 'vs/base/node/encoding';
import { encode } from 'vs/base/node/encoding';
// See https://github.com/Microsoft/vscode/issues/30180
const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB
const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB
// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149
const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB
const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB
export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE;
export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE;
export enum RimRafMode {
@ -178,30 +189,52 @@ export function stat(path: string): Promise<fs.Stats> {
}
export interface IStatAndLink {
// The stats of the file. If the file is a symbolic
// link, the stats will be of that target file and
// not the link itself.
// If the file is a symbolic link pointing to a non
// existing file, the stat will be of the link and
// the `dangling` flag will indicate this.
stat: fs.Stats;
isSymbolicLink: boolean;
// Will be provided if the resource is a symbolic link
// on disk. Use the `dangling` flag to find out if it
// points to a resource that does not exist on disk.
symbolicLink?: { dangling: boolean };
}
export async function statLink(path: string): Promise<IStatAndLink> {
// First stat the link
let linkStat: fs.Stats | undefined;
let linkStatError: NodeJS.ErrnoException | undefined;
let lstats: fs.Stats | undefined;
try {
linkStat = await lstat(path);
lstats = await lstat(path);
// Return early if the stat is not a symbolic link at all
if (!lstats.isSymbolicLink()) {
return { stat: lstats };
}
} catch (error) {
linkStatError = error;
/* ignore - use stat() instead */
}
// Then stat the target and return that
const isLink = !!(linkStat && linkStat.isSymbolicLink());
if (linkStatError || isLink) {
const fileStat = await stat(path);
// If the stat is a symbolic link or failed to stat, use fs.stat()
// which for symbolic links will stat the target they point to
try {
const stats = await stat(path);
return { stat: fileStat, isSymbolicLink: isLink };
return { stat: stats, symbolicLink: lstats?.isSymbolicLink() ? { dangling: false } : undefined };
} catch (error) {
// If the link points to a non-existing file we still want
// to return it as result while setting dangling: true flag
if (error.code === 'ENOENT' && lstats) {
return { stat: lstats, symbolicLink: { dangling: true } };
}
throw error;
}
return { stat: linkStat!, isSymbolicLink: false };
}
export function lstat(path: string): Promise<fs.Stats> {
@ -213,9 +246,7 @@ export function rename(oldPath: string, newPath: string): Promise<void> {
}
export function renameIgnoreError(oldPath: string, newPath: string): Promise<void> {
return new Promise(resolve => {
fs.rename(oldPath, newPath, () => resolve());
});
return new Promise(resolve => fs.rename(oldPath, newPath, () => resolve()));
}
export function unlink(path: string): Promise<void> {
@ -236,6 +267,10 @@ export function readFile(path: string, encoding?: string): Promise<Buffer | stri
return promisify(fs.readFile)(path, encoding);
}
export async function mkdirp(path: string, mode?: number): Promise<void> {
return promisify(fs.mkdir)(path, { mode, recursive: true });
}
// According to node.js docs (https://nodejs.org/docs/v6.5.0/api/fs.html#fs_fs_writefile_file_data_options_callback)
// it is not safe to call writeFile() on the same path multiple times without waiting for the callback to return.
// Therefor we use a Queue on the path that is given to us to sequentialize calls to the same path properly.
@ -244,12 +279,15 @@ const writeFilePathQueues: Map<string, Queue<void>> = new Map();
export function writeFile(path: string, data: string, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: Buffer, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: Uint8Array, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: NodeJS.ReadableStream, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options?: IWriteFileOptions): Promise<void> {
export function writeFile(path: string, data: string | Buffer | Uint8Array, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: string | Buffer | Uint8Array, options?: IWriteFileOptions): Promise<void> {
const queueKey = toQueueKey(path);
return ensureWriteFileQueue(queueKey).queue(() => writeFileAndFlush(path, data, options));
return ensureWriteFileQueue(queueKey).queue(() => {
const ensuredOptions = ensureWriteOptions(options);
return new Promise((resolve, reject) => doWriteFileAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve()));
});
}
function toQueueKey(path: string): string {
@ -294,103 +332,6 @@ interface IEnsuredWriteFileOptions extends IWriteFileOptions {
}
let canFlush = true;
function writeFileAndFlush(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options: IWriteFileOptions | undefined): Promise<void> {
const ensuredOptions = ensureWriteOptions(options);
return new Promise((resolve, reject) => {
if (typeof data === 'string' || Buffer.isBuffer(data) || data instanceof Uint8Array) {
doWriteFileAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve());
} else {
doWriteFileStreamAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve());
}
});
}
function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, options: IEnsuredWriteFileOptions, callback: (error?: Error) => void): void {
// finish only once
let finished = false;
const finish = (error?: Error) => {
if (!finished) {
finished = true;
// in error cases we need to manually close streams
// if the write stream was successfully opened
if (error) {
if (isOpen) {
writer.once('close', () => callback(error));
writer.destroy();
} else {
callback(error);
}
}
// otherwise just return without error
else {
callback();
}
}
};
// create writer to target. we set autoClose: false because we want to use the streams
// file descriptor to call fs.fdatasync to ensure the data is flushed to disk
const writer = fs.createWriteStream(path, { mode: options.mode, flags: options.flag, autoClose: false });
// Event: 'open'
// Purpose: save the fd for later use and start piping
// Notes: will not be called when there is an error opening the file descriptor!
let fd: number;
let isOpen: boolean;
writer.once('open', descriptor => {
fd = descriptor;
isOpen = true;
// if an encoding is provided, we need to pipe the stream through
// an encoder stream and forward the encoding related options
if (options.encoding) {
reader = reader.pipe(encodeStream(options.encoding.charset, { addBOM: options.encoding.addBOM }));
}
// start data piping only when we got a successful open. this ensures that we do
// not consume the stream when an error happens and helps to fix this issue:
// https://github.com/Microsoft/vscode/issues/42542
reader.pipe(writer);
});
// Event: 'error'
// Purpose: to return the error to the outside and to close the write stream (does not happen automatically)
reader.once('error', error => finish(error));
writer.once('error', error => finish(error));
// Event: 'finish'
// Purpose: use fs.fdatasync to flush the contents to disk
// Notes: event is called when the writer has finished writing to the underlying resource. we must call writer.close()
// because we have created the WriteStream with autoClose: false
writer.once('finish', () => {
// flush to disk
if (canFlush && isOpen) {
fs.fdatasync(fd, (syncError: Error) => {
// In some exotic setups it is well possible that node fails to sync
// In that case we disable flushing and warn to the console
if (syncError) {
console.warn('[node.js fs] fdatasync is now disabled for this session because it failed: ', syncError);
canFlush = false;
}
writer.destroy();
});
} else {
writer.destroy();
}
});
// Event: 'close'
// Purpose: signal we are done to the outside
// Notes: event is called when the writer's filedescriptor is closed
writer.once('close', () => finish());
}
// Calls fs.writeFile() followed by a fs.sync() call to flush the changes to disk
// We do this in cases where we want to make sure the data is really on disk and
@ -631,18 +572,3 @@ async function doCopyFile(source: string, target: string, mode: number): Promise
reader.pipe(writer);
});
}
export async function mkdirp(path: string, mode?: number): Promise<void> {
return promisify(fs.mkdir)(path, { mode, recursive: true });
}
// See https://github.com/Microsoft/vscode/issues/30180
const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB
const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB
// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149
const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB
const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB
export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE;
export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE;

View file

@ -10,6 +10,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
import * as errors from 'vs/base/common/errors';
import { VSBuffer } from 'vs/base/common/buffer';
import { getRandomElement } from 'vs/base/common/arrays';
import { isFunction } from 'vs/base/common/types';
/**
* An `IChannel` is an abstraction over a collection of commands.
@ -700,14 +701,34 @@ export class IPCServer<TContext = string> implements IChannelServer<TContext>, I
});
}
getChannel<T extends IChannel>(channelName: string, router?: IClientRouter<TContext>): T {
/**
* Get a channel from a remote client. When passed a router,
* one can specify which client it wants to call and listen to/from.
* Otherwise, when calling without a router, a random client will
* be selected and when listening without a router, every client
* will be listened to.
*/
getChannel<T extends IChannel>(channelName: string, router: IClientRouter<TContext>): T;
getChannel<T extends IChannel>(channelName: string, clientFilter: (client: Client<TContext>) => boolean): T;
getChannel<T extends IChannel>(channelName: string, routerOrClientFilter: IClientRouter<TContext> | ((client: Client<TContext>) => boolean)): T {
const that = this;
return {
call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise<T> {
const connectionPromise = router
? router.routeCall(that, command, arg)
: Promise.resolve(getRandomElement(that.connections));
let connectionPromise: Promise<Client<TContext>>;
if (isFunction(routerOrClientFilter)) {
// when no router is provided, we go random client picking
let connection = getRandomElement(that.connections.filter(routerOrClientFilter));
connectionPromise = connection
// if we found a client, let's call on it
? Promise.resolve(connection)
// else, let's wait for a client to come along
: Event.toPromise(Event.filter(that.onDidAddConnection, routerOrClientFilter));
} else {
connectionPromise = routerOrClientFilter.routeCall(that, command, arg);
}
const channelPromise = connectionPromise
.then(connection => (connection as Connection<TContext>).channelClient.getChannel(channelName));
@ -716,11 +737,11 @@ export class IPCServer<TContext = string> implements IChannelServer<TContext>, I
.call(command, arg, cancellationToken);
},
listen(event: string, arg: any): Event<T> {
if (!router) {
return that.getMulticastEvent(channelName, event, arg);
if (isFunction(routerOrClientFilter)) {
return that.getMulticastEvent(channelName, routerOrClientFilter, event, arg);
}
const channelPromise = router.routeEvent(that, event, arg)
const channelPromise = routerOrClientFilter.routeEvent(that, event, arg)
.then(connection => (connection as Connection<TContext>).channelClient.getChannel(channelName));
return getDelayedChannel(channelPromise)
@ -729,7 +750,7 @@ export class IPCServer<TContext = string> implements IChannelServer<TContext>, I
} as T;
}
private getMulticastEvent<T extends IChannel>(channelName: string, eventName: string, arg: any): Event<T> {
private getMulticastEvent<T extends IChannel>(channelName: string, clientFilter: (client: Client<TContext>) => boolean, eventName: string, arg: any): Event<T> {
const that = this;
let disposables = new DisposableStore();
@ -766,8 +787,8 @@ export class IPCServer<TContext = string> implements IChannelServer<TContext>, I
map.delete(connection);
};
that.connections.forEach(onDidAddConnection);
that.onDidAddConnection(onDidAddConnection, undefined, disposables);
that.connections.filter(clientFilter).forEach(onDidAddConnection);
Event.filter(that.onDidAddConnection, clientFilter)(onDidAddConnection, undefined, disposables);
that.onDidRemoveConnection(onDidRemoveConnection, undefined, disposables);
eventMultiplexer.event(emitter.fire, emitter, disposables);

View file

@ -454,7 +454,7 @@ suite('Base IPC', function () {
client1.registerChannel('channel', clientChannel1);
const pings: string[] = [];
const channel = server.getChannel('channel');
const channel = server.getChannel('channel', () => true);
const service = new TestChannelClient(channel);
service.onPong(msg => pings.push(msg));

View file

Before

Width:  |  Height:  |  Size: 594 B

After

Width:  |  Height:  |  Size: 594 B

View file

Before

Width:  |  Height:  |  Size: 594 B

After

Width:  |  Height:  |  Size: 594 B

Some files were not shown because too many files have changed in this diff Show more