mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
Merge branch 'master' into bepvte/contributions-rename
This commit is contained in:
@@ -614,6 +614,10 @@
|
||||
"*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "{**/api/**.test.ts,}",
|
||||
"restrictions": "{**/vs/**,assert,sinon,crypto,vscode}"
|
||||
},
|
||||
{
|
||||
"target": "{**/**.test.ts,**/test/**}",
|
||||
"restrictions": "{**/vs/**,assert,sinon,crypto}"
|
||||
|
||||
Vendored
+21
-1
@@ -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",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
disturl "https://atom.io/download/electron"
|
||||
target "6.1.6"
|
||||
target "7.1.11"
|
||||
runtime "electron"
|
||||
|
||||
@@ -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.');
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
},
|
||||
{
|
||||
"name": "ms-vscode.js-debug-nightly",
|
||||
"version": "2020.1.43263",
|
||||
"version": "2020.2.408",
|
||||
"forQualities": [
|
||||
"insider"
|
||||
],
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -142,6 +142,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"
|
||||
|
||||
@@ -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/**',
|
||||
|
||||
@@ -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/**',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+6
-6
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Vendored
+10
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
}]);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
+582
-3
@@ -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"
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
"extends": "../../shared.tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"jsx": "react"
|
||||
"jsx": "react",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.0.1",
|
||||
"description": "Dependencies shared by all extensions",
|
||||
"dependencies": {
|
||||
"typescript": "3.7.5"
|
||||
"typescript": "^3.8.0-dev.20200201"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "node ./postinstall"
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2018",
|
||||
"lib": [
|
||||
"es2018"
|
||||
],
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"alwaysStrict": true,
|
||||
@@ -9,4 +12,4 @@
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,10 +882,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\\."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 '"':
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import type * as Proto from '../protocol';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import { tagsMarkdownPreview } from '../utils/previewer';
|
||||
import { markdownDocumentation } from '../utils/previewer';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
|
||||
|
||||
@@ -45,9 +45,7 @@ class TypeScriptHoverProvider implements vscode.HoverProvider {
|
||||
if (data.displayString) {
|
||||
parts.push({ language: 'typescript', value: data.displayString });
|
||||
}
|
||||
|
||||
const tags = tagsMarkdownPreview(data.tags);
|
||||
parts.push(data.documentation + (tags ? '\n\n' + tags : ''));
|
||||
parts.push(markdownDocumentation(data.documentation, data.tags));
|
||||
return parts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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. /** | */
|
||||
|
||||
@@ -8,54 +8,4 @@ declare module "typescript/lib/protocol" {
|
||||
updateGraphDurationMs?: number;
|
||||
}
|
||||
}
|
||||
|
||||
const enum CommandTypes {
|
||||
PrepareCallHierarchy = "prepareCallHierarchy",
|
||||
ProvideCallHierarchyIncomingCalls = "provideCallHierarchyIncomingCalls",
|
||||
ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls",
|
||||
}
|
||||
|
||||
interface CallHierarchyItem {
|
||||
name: string;
|
||||
kind: ScriptElementKind;
|
||||
file: string;
|
||||
span: TextSpan;
|
||||
selectionSpan: TextSpan;
|
||||
}
|
||||
|
||||
interface CallHierarchyIncomingCall {
|
||||
from: CallHierarchyItem;
|
||||
fromSpans: TextSpan[];
|
||||
}
|
||||
|
||||
interface CallHierarchyOutgoingCall {
|
||||
to: CallHierarchyItem;
|
||||
fromSpans: TextSpan[];
|
||||
}
|
||||
|
||||
interface PrepareCallHierarchyRequest extends FileLocationRequest {
|
||||
command: CommandTypes.PrepareCallHierarchy;
|
||||
}
|
||||
|
||||
interface PrepareCallHierarchyResponse extends Response {
|
||||
readonly body: CallHierarchyItem | CallHierarchyItem[];
|
||||
}
|
||||
|
||||
interface ProvideCallHierarchyIncomingCallsRequest extends FileLocationRequest {
|
||||
command: CommandTypes.ProvideCallHierarchyIncomingCalls;
|
||||
kind: ScriptElementKind;
|
||||
}
|
||||
|
||||
interface ProvideCallHierarchyIncomingCallsResponse extends Response {
|
||||
readonly body: CallHierarchyIncomingCall[];
|
||||
}
|
||||
|
||||
interface ProvideCallHierarchyOutgoingCallsRequest extends FileLocationRequest {
|
||||
command: CommandTypes.ProvideCallHierarchyOutgoingCalls;
|
||||
kind: ScriptElementKind;
|
||||
}
|
||||
|
||||
interface ProvideCallHierarchyOutgoingCallsResponse extends Response {
|
||||
readonly body: CallHierarchyOutgoingCall[];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import 'mocha';
|
||||
import { tagsMarkdownPreview } from '../utils/previewer';
|
||||
import { tagsMarkdownPreview, markdownDocumentation } from '../utils/previewer';
|
||||
|
||||
suite('typescript.previewer', () => {
|
||||
test('Should ignore hyphens after a param tag', async () => {
|
||||
@@ -18,5 +18,51 @@ suite('typescript.previewer', () => {
|
||||
]),
|
||||
'*@param* `a` — b');
|
||||
});
|
||||
|
||||
test('Should parse url jsdoc @link', async () => {
|
||||
assert.strictEqual(
|
||||
markdownDocumentation('x {@link http://www.example.com/foo} y {@link https://api.jquery.com/bind/#bind-eventType-eventData-handler} z', []).value,
|
||||
'x [http://www.example.com/foo](http://www.example.com/foo) y [https://api.jquery.com/bind/#bind-eventType-eventData-handler](https://api.jquery.com/bind/#bind-eventType-eventData-handler) z');
|
||||
});
|
||||
|
||||
test('Should parse url jsdoc @link with text', async () => {
|
||||
assert.strictEqual(
|
||||
markdownDocumentation('x {@link http://www.example.com/foo abc xyz} y {@link http://www.example.com/bar|b a z} z', []).value,
|
||||
'x [abc xyz](http://www.example.com/foo) y [b a z](http://www.example.com/bar) z');
|
||||
});
|
||||
|
||||
test('Should treat @linkcode jsdocs links as monospace', async () => {
|
||||
assert.strictEqual(
|
||||
markdownDocumentation('x {@linkcode http://www.example.com/foo} y {@linkplain http://www.example.com/bar} z', []).value,
|
||||
'x [`http://www.example.com/foo`](http://www.example.com/foo) y [http://www.example.com/bar](http://www.example.com/bar) z');
|
||||
});
|
||||
|
||||
test('Should parse url jsdoc @link in param tag', async () => {
|
||||
assert.strictEqual(
|
||||
tagsMarkdownPreview([
|
||||
{
|
||||
name: 'param',
|
||||
text: 'a x {@link http://www.example.com/foo abc xyz} y {@link http://www.example.com/bar|b a z} z'
|
||||
}
|
||||
]),
|
||||
'*@param* `a` — x [abc xyz](http://www.example.com/foo) y [b a z](http://www.example.com/bar) z');
|
||||
});
|
||||
|
||||
test('Should ignore unclosed jsdocs @link', async () => {
|
||||
assert.strictEqual(
|
||||
markdownDocumentation('x {@link http://www.example.com/foo y {@link http://www.example.com/bar bar} z', []).value,
|
||||
'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');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
})));
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'/>
|
||||
|
||||
@@ -6,6 +6,24 @@
|
||||
import * as vscode from 'vscode';
|
||||
import type * as Proto from '../protocol';
|
||||
|
||||
function replaceLinks(text: string): string {
|
||||
return text
|
||||
// Http(s) links
|
||||
.replace(/\{@(link|linkplain|linkcode) (https?:\/\/[^ |}]+?)(?:[| ]([^{}\n]+?))?\}/gi, (_, tag: string, link: string, text?: string) => {
|
||||
switch (tag) {
|
||||
case 'linkcode':
|
||||
return `[\`${text ? text.trim() : link}\`](${link})`;
|
||||
|
||||
default:
|
||||
return `[${text ? text.trim() : link}](${link})`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function processInlineTags(text: string): string {
|
||||
return replaceLinks(text);
|
||||
}
|
||||
|
||||
function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined {
|
||||
if (!tag.text) {
|
||||
return undefined;
|
||||
@@ -41,7 +59,7 @@ function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined {
|
||||
return makeCodeblock(tag.text);
|
||||
}
|
||||
|
||||
return tag.text;
|
||||
return processInlineTags(tag.text);
|
||||
}
|
||||
|
||||
function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined {
|
||||
@@ -50,15 +68,15 @@ 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}\``;
|
||||
if (!doc) {
|
||||
return label;
|
||||
}
|
||||
return label + (doc.match(/\r\n|\n/g) ? ' \n' + doc : ` — ${doc}`);
|
||||
return label + (doc.match(/\r\n|\n/g) ? ' \n' + processInlineTags(doc) : ` — ${processInlineTags(doc)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,21 +89,19 @@ function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined {
|
||||
return label + (text.match(/\r\n|\n/g) ? ' \n' + text : ` — ${text}`);
|
||||
}
|
||||
|
||||
export function plain(parts: Proto.SymbolDisplayPart[]): string {
|
||||
if (!parts) {
|
||||
return '';
|
||||
}
|
||||
return parts.map(part => part.text).join('');
|
||||
export function plain(parts: Proto.SymbolDisplayPart[] | string): string {
|
||||
return processInlineTags(
|
||||
typeof parts === 'string'
|
||||
? parts
|
||||
: parts.map(part => part.text).join(''));
|
||||
}
|
||||
|
||||
export function tagsMarkdownPreview(tags: Proto.JSDocTagInfo[]): string {
|
||||
return (tags || [])
|
||||
.map(getTagDocumentation)
|
||||
.join(' \n\n');
|
||||
return tags.map(getTagDocumentation).join(' \n\n');
|
||||
}
|
||||
|
||||
export function markdownDocumentation(
|
||||
documentation: Proto.SymbolDisplayPart[],
|
||||
documentation: Proto.SymbolDisplayPart[] | string,
|
||||
tags: Proto.JSDocTagInfo[]
|
||||
): vscode.MarkdownString {
|
||||
const out = new vscode.MarkdownString();
|
||||
@@ -95,7 +111,7 @@ export function markdownDocumentation(
|
||||
|
||||
export function addMarkdownDocumentation(
|
||||
out: vscode.MarkdownString,
|
||||
documentation: Proto.SymbolDisplayPart[] | undefined,
|
||||
documentation: Proto.SymbolDisplayPart[] | string | undefined,
|
||||
tags: Proto.JSDocTagInfo[] | undefined
|
||||
): vscode.MarkdownString {
|
||||
if (documentation) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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/**/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -169,11 +170,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,
|
||||
accountName: token.accountName,
|
||||
scopes: token.scope.split(' ')
|
||||
};
|
||||
}
|
||||
@@ -187,7 +188,7 @@ export class AzureActiveDirectoryService {
|
||||
}
|
||||
}
|
||||
|
||||
get sessions(): vscode.Session[] {
|
||||
get sessions(): vscode.AuthenticationSession[] {
|
||||
return this._tokens.map(token => this.convertToSession(token));
|
||||
}
|
||||
|
||||
@@ -287,7 +288,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.');
|
||||
@@ -360,8 +361,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'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'/>
|
||||
@@ -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>
|
||||
}
|
||||
}
|
||||
@@ -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/**/*"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -13,10 +13,14 @@
|
||||
//
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { URL } from 'url';
|
||||
import { TextEncoder, TextDecoder } from 'util';
|
||||
|
||||
const textEncoder = new TextEncoder();
|
||||
const SCHEME = 'memfs';
|
||||
|
||||
declare const window: unknown;
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
if (typeof window !== 'undefined') { // do not run under node.js
|
||||
const memFs = enableFs(context);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 capabilitiesReceived: () => 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 === 'capabilities') {
|
||||
capabilitiesReceived();
|
||||
}
|
||||
if (m.event === 'initialized') {
|
||||
initializedReceived();
|
||||
}
|
||||
if (m.command === 'configurationDone') {
|
||||
configurationDoneReceived();
|
||||
}
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
||||
const capabilitiesPromise = new Promise<void>(resolve => capabilitiesReceived = resolve);
|
||||
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 capabilitiesPromise;
|
||||
await initializedPromise;
|
||||
await configurationDonePromise;
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
suite.skip('Webview tests', () => {
|
||||
const disposables: vscode.Disposable[] = [];
|
||||
|
||||
function _register<T extends vscode.Disposable>(disposable: T) {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
typescript@3.7.5:
|
||||
version "3.7.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
|
||||
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
|
||||
typescript@^3.8.0-dev.20200201:
|
||||
version "3.8.0-dev.20200201"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.0-dev.20200201.tgz#e03210f5c7915f89cb77bd38793d12a69933a1a6"
|
||||
integrity sha512-KhaK6J3dIg6p9t24UVhBLcBIav4MpMExlD6nf18cVwp9LELuqhWu0NDmnLdSN0ovsrbcIJNmobQ1xC7vLN21eg==
|
||||
|
||||
+8
-6
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.43.0",
|
||||
"distro": "37cc2dc2461194a03cadbd9de5030ac3b6eb1109",
|
||||
"distro": "b975cdab7c0c0e7705fa7eac4c8411ad8a790f4b",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
@@ -34,7 +34,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 +55,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"
|
||||
},
|
||||
@@ -91,7 +92,7 @@
|
||||
"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",
|
||||
@@ -137,6 +138,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",
|
||||
|
||||
+5
-4
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
+17
-12
@@ -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==
|
||||
|
||||
+18
-18
@@ -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"
|
||||
|
||||
@@ -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%
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
@@ -334,6 +335,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];
|
||||
}
|
||||
@@ -553,6 +562,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),
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -584,3 +584,7 @@ export function mapArrayOrNot<T, U>(items: T | T[], fn: (_: T) => U): U | U[] {
|
||||
export function asArray<T>(x: T | T[]): T[] {
|
||||
return Array.isArray(x) ? x : [x];
|
||||
}
|
||||
|
||||
export function getRandomElement<T>(arr: T[]): T | undefined {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 || '/';
|
||||
|
||||
@@ -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);
|
||||
|
||||
+58
-132
@@ -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;
|
||||
|
||||
@@ -9,6 +9,8 @@ import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/com
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
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.
|
||||
@@ -117,8 +119,7 @@ export interface IClientRouter<TContext = string> {
|
||||
* order to pick the right one.
|
||||
*/
|
||||
export interface IRoutingChannelClient<TContext = string> {
|
||||
getChannel<T extends IChannel>(channelName: string, router: IClientRouter<TContext>): T;
|
||||
getBroadcastChannel<T extends IChannel>(channelName: string): T;
|
||||
getChannel<T extends IChannel>(channelName: string, router?: IClientRouter<TContext>): T;
|
||||
}
|
||||
|
||||
interface IReader {
|
||||
@@ -700,19 +701,47 @@ 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 channelPromise = router.routeCall(that, command, arg)
|
||||
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));
|
||||
|
||||
return getDelayedChannel(channelPromise)
|
||||
.call(command, arg, cancellationToken);
|
||||
},
|
||||
listen(event: string, arg: any): Event<T> {
|
||||
const channelPromise = router.routeEvent(that, event, arg)
|
||||
if (isFunction(routerOrClientFilter)) {
|
||||
return that.getMulticastEvent(channelName, routerOrClientFilter, event, arg);
|
||||
}
|
||||
|
||||
const channelPromise = routerOrClientFilter.routeEvent(that, event, arg)
|
||||
.then(connection => (connection as Connection<TContext>).channelClient.getChannel(channelName));
|
||||
|
||||
return getDelayedChannel(channelPromise)
|
||||
@@ -721,64 +750,56 @@ export class IPCServer<TContext = string> implements IChannelServer<TContext>, I
|
||||
} as T;
|
||||
}
|
||||
|
||||
getBroadcastChannel<T extends IChannel>(channelName: string): 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();
|
||||
|
||||
return {
|
||||
call(_): Promise<T> {
|
||||
throw new Error('IPC broadcast channels are not supported for calls');
|
||||
},
|
||||
listen(eventName: string, arg: any): Event<T> {
|
||||
let disposables = new DisposableStore();
|
||||
// Create an emitter which hooks up to all clients
|
||||
// as soon as first listener is added. It also
|
||||
// disconnects from all clients as soon as the last listener
|
||||
// is removed.
|
||||
const emitter = new Emitter<T>({
|
||||
onFirstListenerAdd: () => {
|
||||
disposables = new DisposableStore();
|
||||
|
||||
// Create an emitter which hooks up to all clients
|
||||
// as soon as first listener is added. It also
|
||||
// disconnects from all clients as soon as the last listener
|
||||
// is removed.
|
||||
const emitter = new Emitter<T>({
|
||||
onFirstListenerAdd: () => {
|
||||
disposables = new DisposableStore();
|
||||
// The event multiplexer is useful since the active
|
||||
// client list is dynamic. We need to hook up and disconnection
|
||||
// to/from clients as they come and go.
|
||||
const eventMultiplexer = new EventMultiplexer<T>();
|
||||
const map = new Map<Connection<TContext>, IDisposable>();
|
||||
|
||||
// The event multiplexer is useful since the active
|
||||
// client list is dynamic. We need to hook up and disconnection
|
||||
// to/from clients as they come and go.
|
||||
const eventMultiplexer = new EventMultiplexer<T>();
|
||||
const map = new Map<Connection<TContext>, IDisposable>();
|
||||
const onDidAddConnection = (connection: Connection<TContext>) => {
|
||||
const channel = connection.channelClient.getChannel(channelName);
|
||||
const event = channel.listen<T>(eventName, arg);
|
||||
const disposable = eventMultiplexer.add(event);
|
||||
|
||||
const onDidAddConnection = (connection: Connection<TContext>) => {
|
||||
const channel = connection.channelClient.getChannel(channelName);
|
||||
const event = channel.listen<T>(eventName, arg);
|
||||
const disposable = eventMultiplexer.add(event);
|
||||
map.set(connection, disposable);
|
||||
};
|
||||
|
||||
map.set(connection, disposable);
|
||||
};
|
||||
const onDidRemoveConnection = (connection: Connection<TContext>) => {
|
||||
const disposable = map.get(connection);
|
||||
|
||||
const onDidRemoveConnection = (connection: Connection<TContext>) => {
|
||||
const disposable = map.get(connection);
|
||||
|
||||
if (!disposable) {
|
||||
return;
|
||||
}
|
||||
|
||||
disposable.dispose();
|
||||
map.delete(connection);
|
||||
};
|
||||
|
||||
that.connections.forEach(onDidAddConnection);
|
||||
that.onDidAddConnection(onDidAddConnection, undefined, disposables);
|
||||
that.onDidRemoveConnection(onDidRemoveConnection, undefined, disposables);
|
||||
eventMultiplexer.event(emitter.fire, emitter, disposables);
|
||||
|
||||
disposables.add(eventMultiplexer);
|
||||
},
|
||||
onLastListenerRemove: () => {
|
||||
disposables.dispose();
|
||||
if (!disposable) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
return emitter.event;
|
||||
disposable.dispose();
|
||||
map.delete(connection);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
disposables.add(eventMultiplexer);
|
||||
},
|
||||
onLastListenerRemove: () => {
|
||||
disposables.dispose();
|
||||
}
|
||||
} as T;
|
||||
});
|
||||
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
registerChannel(channelName: string, channel: IServerChannel<TContext>): void {
|
||||
|
||||
@@ -454,9 +454,9 @@ suite('Base IPC', function () {
|
||||
client1.registerChannel('channel', clientChannel1);
|
||||
|
||||
const pings: string[] = [];
|
||||
const broadcastChannel = server.getBroadcastChannel('channel');
|
||||
const broadcastService = new TestChannelClient(broadcastChannel);
|
||||
broadcastService.onPong(msg => pings.push(msg));
|
||||
const channel = server.getChannel('channel', () => true);
|
||||
const service = new TestChannelClient(channel);
|
||||
service.onPong(msg => pings.push(msg));
|
||||
|
||||
await timeout(1);
|
||||
clientService1.ping('hello 1');
|
||||
|
||||
@@ -26,35 +26,32 @@ export class DeferredPromise<T> {
|
||||
|
||||
public complete(value: T) {
|
||||
return new Promise(resolve => {
|
||||
process.nextTick(() => {
|
||||
this.completeCallback(value);
|
||||
resolve();
|
||||
});
|
||||
this.completeCallback(value);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
public error(err: any) {
|
||||
return new Promise(resolve => {
|
||||
process.nextTick(() => {
|
||||
this.errorCallback(err);
|
||||
resolve();
|
||||
});
|
||||
this.errorCallback(err);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
process.nextTick(() => {
|
||||
new Promise(resolve => {
|
||||
this.errorCallback(canceled());
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function toResource(this: any, path: string) {
|
||||
if (isWindows) {
|
||||
return URI.file(join('C:\\', Buffer.from(this.test.fullTitle()).toString('base64'), path));
|
||||
return URI.file(join('C:\\', btoa(this.test.fullTitle()), path));
|
||||
}
|
||||
|
||||
return URI.file(join('/', Buffer.from(this.test.fullTitle()).toString('base64'), path));
|
||||
return URI.file(join('/', btoa(this.test.fullTitle()), path));
|
||||
}
|
||||
|
||||
export function suiteRepeat(n: number, description: string, callback: (this: any) => void): void {
|
||||
|
||||
@@ -176,8 +176,8 @@ suite('Paths (Node Implementation)', () => {
|
||||
});
|
||||
|
||||
test('dirname', () => {
|
||||
assert.strictEqual(path.dirname(path.normalize(__filename)).substr(-11),
|
||||
isWindows ? 'test\\common' : 'test/common');
|
||||
assert.strictEqual(path.dirname(path.normalize(__filename)).substr(-9),
|
||||
isWindows ? 'test\\node' : 'test/node');
|
||||
|
||||
assert.strictEqual(path.posix.dirname('/a/b/'), '/a');
|
||||
assert.strictEqual(path.posix.dirname('/a/b'), '/a');
|
||||
@@ -7,47 +7,14 @@ import * as assert from 'assert';
|
||||
import * as os from 'os';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as fs from 'fs';
|
||||
import { Readable } from 'stream';
|
||||
import * as uuid from 'vs/base/common/uuid';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { canNormalize } from 'vs/base/common/normalization';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
|
||||
const chunkSize = 64 * 1024;
|
||||
const readError = 'Error while reading';
|
||||
function toReadable(value: string, throwError?: boolean): Readable {
|
||||
const totalChunks = Math.ceil(value.length / chunkSize);
|
||||
const stringChunks: string[] = [];
|
||||
|
||||
for (let i = 0, j = 0; i < totalChunks; ++i, j += chunkSize) {
|
||||
stringChunks[i] = value.substr(j, chunkSize);
|
||||
}
|
||||
|
||||
let counter = 0;
|
||||
return new Readable({
|
||||
read: function () {
|
||||
if (throwError) {
|
||||
this.emit('error', new Error(readError));
|
||||
}
|
||||
|
||||
let res!: string;
|
||||
let canPush = true;
|
||||
while (canPush && (res = stringChunks[counter++])) {
|
||||
canPush = this.push(res);
|
||||
}
|
||||
|
||||
// EOS
|
||||
if (!res) {
|
||||
this.push(null);
|
||||
}
|
||||
},
|
||||
encoding: 'utf8'
|
||||
});
|
||||
}
|
||||
|
||||
suite('PFS', function () {
|
||||
|
||||
// Given issues such as https://github.com/microsoft/vscode/issues/84066
|
||||
@@ -334,7 +301,7 @@ suite('PFS', function () {
|
||||
|
||||
test('stat link', async () => {
|
||||
if (isWindows) {
|
||||
return Promise.resolve(); // Symlinks are not the same on win, and we can not create them programitically without admin privileges
|
||||
return; // Symlinks are not the same on win, and we can not create them programitically without admin privileges
|
||||
}
|
||||
|
||||
const id1 = uuid.generateUuid();
|
||||
@@ -349,14 +316,38 @@ suite('PFS', function () {
|
||||
fs.symlinkSync(directory, symbolicLink);
|
||||
|
||||
let statAndIsLink = await pfs.statLink(directory);
|
||||
assert.ok(!statAndIsLink!.isSymbolicLink);
|
||||
assert.ok(!statAndIsLink?.symbolicLink);
|
||||
|
||||
statAndIsLink = await pfs.statLink(symbolicLink);
|
||||
assert.ok(statAndIsLink!.isSymbolicLink);
|
||||
assert.ok(statAndIsLink?.symbolicLink);
|
||||
assert.ok(!statAndIsLink?.symbolicLink?.dangling);
|
||||
|
||||
pfs.rimrafSync(directory);
|
||||
});
|
||||
|
||||
test('stat link (non existing target)', async () => {
|
||||
if (isWindows) {
|
||||
return; // Symlinks are not the same on win, and we can not create them programitically without admin privileges
|
||||
}
|
||||
|
||||
const id1 = uuid.generateUuid();
|
||||
const parentDir = path.join(os.tmpdir(), 'vsctests', id1);
|
||||
const directory = path.join(parentDir, 'pfs', id1);
|
||||
|
||||
const id2 = uuid.generateUuid();
|
||||
const symbolicLink = path.join(parentDir, 'pfs', id2);
|
||||
|
||||
await pfs.mkdirp(directory, 493);
|
||||
|
||||
fs.symlinkSync(directory, symbolicLink);
|
||||
|
||||
pfs.rimrafSync(directory);
|
||||
|
||||
const statAndIsLink = await pfs.statLink(symbolicLink);
|
||||
assert.ok(statAndIsLink?.symbolicLink);
|
||||
assert.ok(statAndIsLink?.symbolicLink?.dangling);
|
||||
});
|
||||
|
||||
test('readdir', async () => {
|
||||
if (canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) {
|
||||
const id = uuid.generateUuid();
|
||||
@@ -420,17 +411,10 @@ suite('PFS', function () {
|
||||
return testWriteFileAndFlush(VSBuffer.fromString(smallData).buffer, smallData, VSBuffer.fromString(bigData).buffer, bigData);
|
||||
});
|
||||
|
||||
test('writeFile (stream)', async () => {
|
||||
const smallData = 'Hello World';
|
||||
const bigData = (new Array(100 * 1024)).join('Large String\n');
|
||||
|
||||
return testWriteFileAndFlush(toReadable(smallData), smallData, toReadable(bigData), bigData);
|
||||
});
|
||||
|
||||
async function testWriteFileAndFlush(
|
||||
smallData: string | Buffer | NodeJS.ReadableStream | Uint8Array,
|
||||
smallData: string | Buffer | Uint8Array,
|
||||
smallDataValue: string,
|
||||
bigData: string | Buffer | NodeJS.ReadableStream | Uint8Array,
|
||||
bigData: string | Buffer | Uint8Array,
|
||||
bigDataValue: string
|
||||
): Promise<void> {
|
||||
const id = uuid.generateUuid();
|
||||
@@ -450,22 +434,6 @@ suite('PFS', function () {
|
||||
await pfs.rimraf(parentDir);
|
||||
}
|
||||
|
||||
test('writeFile (file stream)', async () => {
|
||||
const id = uuid.generateUuid();
|
||||
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
|
||||
const sourceFile = getPathFromAmdModule(require, './fixtures/index.html');
|
||||
const newDir = path.join(parentDir, 'pfs', id);
|
||||
const testFile = path.join(newDir, 'flushed.txt');
|
||||
|
||||
await pfs.mkdirp(newDir, 493);
|
||||
assert.ok(fs.existsSync(newDir));
|
||||
|
||||
await pfs.writeFile(testFile, fs.createReadStream(sourceFile));
|
||||
assert.equal(fs.readFileSync(testFile).toString(), fs.readFileSync(sourceFile).toString());
|
||||
|
||||
await pfs.rimraf(parentDir);
|
||||
});
|
||||
|
||||
test('writeFile (string, error handling)', async () => {
|
||||
const id = uuid.generateUuid();
|
||||
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
|
||||
@@ -490,118 +458,6 @@ suite('PFS', function () {
|
||||
await pfs.rimraf(parentDir);
|
||||
});
|
||||
|
||||
test('writeFile (stream, error handling EISDIR)', async () => {
|
||||
const id = uuid.generateUuid();
|
||||
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
|
||||
const newDir = path.join(parentDir, 'pfs', id);
|
||||
const testFile = path.join(newDir, 'flushed.txt');
|
||||
|
||||
await pfs.mkdirp(newDir, 493);
|
||||
|
||||
assert.ok(fs.existsSync(newDir));
|
||||
|
||||
fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory!
|
||||
|
||||
const readable = toReadable('Hello World');
|
||||
|
||||
let expectedError: Error | undefined;
|
||||
try {
|
||||
await pfs.writeFile(testFile, readable);
|
||||
} catch (error) {
|
||||
expectedError = error;
|
||||
}
|
||||
|
||||
if (!expectedError || (<any>expectedError).code !== 'EISDIR') {
|
||||
throw new Error('Expected EISDIR error for writing to folder but got: ' + (expectedError ? (<any>expectedError).code : 'no error'));
|
||||
}
|
||||
|
||||
// verify that the stream is still consumable (for https://github.com/Microsoft/vscode/issues/42542)
|
||||
assert.equal(readable.read(), 'Hello World');
|
||||
|
||||
await pfs.rimraf(parentDir);
|
||||
});
|
||||
|
||||
test('writeFile (stream, error handling READERROR)', async () => {
|
||||
const id = uuid.generateUuid();
|
||||
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
|
||||
const newDir = path.join(parentDir, 'pfs', id);
|
||||
const testFile = path.join(newDir, 'flushed.txt');
|
||||
|
||||
await pfs.mkdirp(newDir, 493);
|
||||
assert.ok(fs.existsSync(newDir));
|
||||
|
||||
let expectedError: Error | undefined;
|
||||
try {
|
||||
await pfs.writeFile(testFile, toReadable('Hello World', true /* throw error */));
|
||||
} catch (error) {
|
||||
expectedError = error;
|
||||
}
|
||||
|
||||
if (!expectedError || expectedError.message !== readError) {
|
||||
throw new Error('Expected error for writing to folder');
|
||||
}
|
||||
|
||||
await pfs.rimraf(parentDir);
|
||||
});
|
||||
|
||||
test('writeFile (stream, error handling EACCES)', async () => {
|
||||
if (isLinux) {
|
||||
return Promise.resolve(); // somehow this test fails on Linux in our TFS builds
|
||||
}
|
||||
|
||||
const id = uuid.generateUuid();
|
||||
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
|
||||
const newDir = path.join(parentDir, 'pfs', id);
|
||||
const testFile = path.join(newDir, 'flushed.txt');
|
||||
|
||||
await pfs.mkdirp(newDir, 493);
|
||||
|
||||
assert.ok(fs.existsSync(newDir));
|
||||
|
||||
fs.writeFileSync(testFile, '');
|
||||
fs.chmodSync(testFile, 33060); // make readonly
|
||||
|
||||
let expectedError: Error | undefined;
|
||||
try {
|
||||
await pfs.writeFile(testFile, toReadable('Hello World'));
|
||||
} catch (error) {
|
||||
expectedError = error;
|
||||
}
|
||||
|
||||
if (!expectedError || !((<any>expectedError).code !== 'EACCES' || (<any>expectedError).code !== 'EPERM')) {
|
||||
throw new Error('Expected EACCES/EPERM error for writing to folder but got: ' + (expectedError ? (<any>expectedError).code : 'no error'));
|
||||
}
|
||||
|
||||
await pfs.rimraf(parentDir);
|
||||
});
|
||||
|
||||
test('writeFile (file stream, error handling)', async () => {
|
||||
const id = uuid.generateUuid();
|
||||
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
|
||||
const sourceFile = getPathFromAmdModule(require, './fixtures/index.html');
|
||||
const newDir = path.join(parentDir, 'pfs', id);
|
||||
const testFile = path.join(newDir, 'flushed.txt');
|
||||
|
||||
await pfs.mkdirp(newDir, 493);
|
||||
|
||||
assert.ok(fs.existsSync(newDir));
|
||||
|
||||
fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory!
|
||||
|
||||
let expectedError: Error | undefined;
|
||||
try {
|
||||
await pfs.writeFile(testFile, fs.createReadStream(sourceFile));
|
||||
} catch (error) {
|
||||
expectedError = error;
|
||||
}
|
||||
|
||||
if (!expectedError) {
|
||||
throw new Error('Expected error for writing to folder');
|
||||
}
|
||||
|
||||
await pfs.rimraf(parentDir);
|
||||
});
|
||||
|
||||
test('writeFileSync', async () => {
|
||||
const id = uuid.generateUuid();
|
||||
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
'onigasm-umd': `${window.location.origin}/static/remote/web/node_modules/onigasm-umd/release/main`,
|
||||
'xterm': `${window.location.origin}/static/remote/web/node_modules/xterm/lib/xterm.js`,
|
||||
'xterm-addon-search': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
|
||||
'xterm-addon-unicode11': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
|
||||
'xterm-addon-web-links': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
|
||||
'xterm-addon-webgl': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
|
||||
'semver-umd': `${window.location.origin}/static/remote/web/node_modules/semver-umd/lib/semver-umd.js`,
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
'onigasm-umd': `${window.location.origin}/static/node_modules/onigasm-umd/release/main`,
|
||||
'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`,
|
||||
'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
|
||||
'xterm-addon-unicode11': `${window.location.origin}/static/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
|
||||
'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
|
||||
'xterm-addon-webgl': `${window.location.origin}/static/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
|
||||
'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`,
|
||||
|
||||
@@ -26,7 +26,6 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper
|
||||
import { TelemetryAppenderChannel } from 'vs/platform/telemetry/node/telemetryIpc';
|
||||
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
|
||||
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
|
||||
import { ActiveWindowManager } from 'vs/code/node/activeWindowTracker';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { ILogService, LogLevel, ILoggerService } from 'vs/platform/log/common/log';
|
||||
import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
|
||||
@@ -131,9 +130,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
|
||||
const electronService = createChannelSender<IElectronService>(mainProcessService.getChannel('electron'), { context: configuration.windowId });
|
||||
services.set(IElectronService, electronService);
|
||||
|
||||
const activeWindowManager = new ActiveWindowManager(electronService);
|
||||
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
|
||||
|
||||
// Files
|
||||
const fileService = new FileService(logService);
|
||||
services.set(IFileService, fileService);
|
||||
@@ -184,8 +180,8 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
|
||||
services.set(ICredentialsService, new SyncDescriptor(KeytarCredentialsService));
|
||||
services.set(IUserDataAuthTokenService, new SyncDescriptor(UserDataAuthTokenService));
|
||||
services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService));
|
||||
services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', activeWindowRouter)));
|
||||
services.set(IGlobalExtensionEnablementService, new GlobalExtensionEnablementServiceClient(server.getChannel('globalExtensionEnablement', activeWindowRouter)));
|
||||
services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main')));
|
||||
services.set(IGlobalExtensionEnablementService, new GlobalExtensionEnablementServiceClient(server.getChannel('globalExtensionEnablement', client => client.ctx !== 'main')));
|
||||
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
|
||||
services.set(ISettingsSyncService, new SyncDescriptor(SettingsSynchroniser));
|
||||
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
|
||||
|
||||
@@ -171,7 +171,7 @@ export class CodeApplication extends Disposable {
|
||||
app.on('web-contents-created', (_event: Event, contents) => {
|
||||
contents.on('will-attach-webview', (event: Event, webPreferences, params) => {
|
||||
|
||||
const isValidWebviewSource = (source: string): boolean => {
|
||||
const isValidWebviewSource = (source: string | undefined): boolean => {
|
||||
if (!source) {
|
||||
return false;
|
||||
}
|
||||
@@ -191,11 +191,12 @@ export class CodeApplication extends Disposable {
|
||||
webPreferences.nodeIntegration = false;
|
||||
|
||||
// Verify URLs being loaded
|
||||
if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) {
|
||||
// https://github.com/electron/electron/issues/21553
|
||||
if (isValidWebviewSource(params.src) && isValidWebviewSource((webPreferences as { preloadURL: string }).preloadURL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete webPreferences.preloadUrl;
|
||||
delete (webPreferences as { preloadURL: string }).preloadURL; // https://github.com/electron/electron/issues/21553
|
||||
|
||||
// Otherwise prevent loading
|
||||
this.logService.error('webContents#web-contents-created: Prevented webview attach');
|
||||
@@ -497,27 +498,27 @@ export class CodeApplication extends Disposable {
|
||||
this.logService.info(`Tracing: waiting for windows to get ready...`);
|
||||
|
||||
let recordingStopped = false;
|
||||
const stopRecording = (timeout: boolean) => {
|
||||
const stopRecording = async (timeout: boolean) => {
|
||||
if (recordingStopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
recordingStopped = true; // only once
|
||||
|
||||
contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), path => {
|
||||
if (!timeout) {
|
||||
if (this.dialogMainService) {
|
||||
this.dialogMainService.showMessageBox({
|
||||
type: 'info',
|
||||
message: localize('trace.message', "Successfully created trace."),
|
||||
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
|
||||
buttons: [localize('trace.ok', "Ok")]
|
||||
}, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
|
||||
}
|
||||
} else {
|
||||
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
|
||||
const path = await contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`));
|
||||
|
||||
if (!timeout) {
|
||||
if (this.dialogMainService) {
|
||||
this.dialogMainService.showMessageBox({
|
||||
type: 'info',
|
||||
message: localize('trace.message', "Successfully created trace."),
|
||||
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
|
||||
buttons: [localize('trace.ok', "Ok")]
|
||||
}, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Wait up to 30s before creating the trace anyways
|
||||
@@ -594,8 +595,6 @@ export class CodeApplication extends Disposable {
|
||||
// Catch file URLs
|
||||
if (uri.authority === Schemas.file && !!uri.path) {
|
||||
const cli = assign(Object.create(null), environmentService.args);
|
||||
|
||||
// hey Ben, we need to convert this `code://file` URI into a `file://` URI
|
||||
const urisToOpen = [{ fileUri: URI.file(uri.fsPath) }];
|
||||
|
||||
windowsMainService.open({ context: OpenContext.API, cli, urisToOpen, gotoLineMode: true });
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment } from 'electron';
|
||||
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme } from 'electron';
|
||||
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -347,9 +347,9 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
});
|
||||
|
||||
this._win.webContents.session.webRequest.onHeadersReceived(null!, (details, callback) => {
|
||||
const responseHeaders = details.responseHeaders as { [key: string]: string[] };
|
||||
const responseHeaders = details.responseHeaders as Record<string, (string) | (string[])>;
|
||||
|
||||
const contentType: string[] = (responseHeaders['content-type'] || responseHeaders['Content-Type']);
|
||||
const contentType = (responseHeaders['content-type'] || responseHeaders['Content-Type']);
|
||||
if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) {
|
||||
return callback({ cancel: true });
|
||||
}
|
||||
@@ -441,7 +441,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
// Inject headers when requests are incoming
|
||||
const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*'];
|
||||
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) =>
|
||||
this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined } })));
|
||||
this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as Record<string, string> })));
|
||||
}
|
||||
|
||||
private onWindowError(error: WindowError): void {
|
||||
@@ -648,7 +648,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
if (windowConfig?.autoDetectHighContrast === false) {
|
||||
autoDetectHighContrast = false;
|
||||
}
|
||||
windowConfiguration.highContrast = isWindows && autoDetectHighContrast && systemPreferences.isInvertedColorScheme();
|
||||
windowConfiguration.highContrast = isWindows && autoDetectHighContrast && nativeTheme.shouldUseInvertedColorScheme;
|
||||
windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled;
|
||||
|
||||
// Title style related
|
||||
@@ -1007,22 +1007,22 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
switch (visibility) {
|
||||
case ('default'):
|
||||
this._win.setMenuBarVisibility(!isFullscreen);
|
||||
this._win.setAutoHideMenuBar(isFullscreen);
|
||||
this._win.autoHideMenuBar = isFullscreen;
|
||||
break;
|
||||
|
||||
case ('visible'):
|
||||
this._win.setMenuBarVisibility(true);
|
||||
this._win.setAutoHideMenuBar(false);
|
||||
this._win.autoHideMenuBar = false;
|
||||
break;
|
||||
|
||||
case ('toggle'):
|
||||
this._win.setMenuBarVisibility(false);
|
||||
this._win.setAutoHideMenuBar(true);
|
||||
this._win.autoHideMenuBar = true;
|
||||
break;
|
||||
|
||||
case ('hidden'):
|
||||
this._win.setMenuBarVisibility(false);
|
||||
this._win.setAutoHideMenuBar(false);
|
||||
this._win.autoHideMenuBar = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ export class EditorScrollbar extends ViewPart {
|
||||
const scrollbar = options.get(EditorOption.scrollbar);
|
||||
const mouseWheelScrollSensitivity = options.get(EditorOption.mouseWheelScrollSensitivity);
|
||||
const fastScrollSensitivity = options.get(EditorOption.fastScrollSensitivity);
|
||||
const scrollPredominantAxis = options.get(EditorOption.scrollPredominantAxis);
|
||||
|
||||
const scrollbarOptions: ScrollableElementCreationOptions = {
|
||||
listenOnDomNode: viewDomNode.domNode,
|
||||
@@ -54,6 +55,7 @@ export class EditorScrollbar extends ViewPart {
|
||||
arrowSize: scrollbar.arrowSize,
|
||||
mouseWheelScrollSensitivity: mouseWheelScrollSensitivity,
|
||||
fastScrollSensitivity: fastScrollSensitivity,
|
||||
scrollPredominantAxis: scrollPredominantAxis,
|
||||
};
|
||||
|
||||
this.scrollbar = this._register(new SmoothScrollableElement(linesContent.domNode, scrollbarOptions, this._context.viewLayout.getScrollable()));
|
||||
@@ -140,10 +142,12 @@ export class EditorScrollbar extends ViewPart {
|
||||
const scrollbar = options.get(EditorOption.scrollbar);
|
||||
const mouseWheelScrollSensitivity = options.get(EditorOption.mouseWheelScrollSensitivity);
|
||||
const fastScrollSensitivity = options.get(EditorOption.fastScrollSensitivity);
|
||||
const scrollPredominantAxis = options.get(EditorOption.scrollPredominantAxis);
|
||||
const newOpts: ScrollableElementChangeOptions = {
|
||||
handleMouseWheel: scrollbar.handleMouseWheel,
|
||||
mouseWheelScrollSensitivity: mouseWheelScrollSensitivity,
|
||||
fastScrollSensitivity: fastScrollSensitivity
|
||||
fastScrollSensitivity: fastScrollSensitivity,
|
||||
scrollPredominantAxis: scrollPredominantAxis
|
||||
};
|
||||
this.scrollbar.updateOptions(newOpts);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-editor .margin-view-overlays .line-numbers {
|
||||
font-variant-numeric: tabular-nums;
|
||||
position: absolute;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
|
||||
@@ -589,6 +589,14 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost<ViewLine>,
|
||||
if (boxEndY - boxStartY > viewportHeight) {
|
||||
// the box is larger than the viewport ... scroll to its top
|
||||
newScrollTop = boxStartY;
|
||||
} else if (verticalType === viewEvents.VerticalRevealType.NearTop) {
|
||||
// We want a gap that is 20% of the viewport, but with a minimum of 5 lines
|
||||
const desiredGapAbove = Math.max(5 * this._lineHeight, viewportHeight * 0.2);
|
||||
// Try to scroll just above the box with the desired gap
|
||||
const desiredScrollTop = boxStartY - desiredGapAbove;
|
||||
// But ensure that the box is not pushed out of viewport
|
||||
const minScrollTop = boxEndY - viewportHeight;
|
||||
newScrollTop = Math.max(minScrollTop, desiredScrollTop);
|
||||
} else if (verticalType === viewEvents.VerticalRevealType.Center || verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport) {
|
||||
if (verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport && viewportStartY <= boxStartY && boxEndY <= viewportEndY) {
|
||||
// Box is already in the viewport... do nothing
|
||||
|
||||
@@ -25,8 +25,8 @@ import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/v
|
||||
import { ViewContext } from 'vs/editor/common/view/viewContext';
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { ViewLineData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapSelection } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { minimapSelection, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerThemingParticipant, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
@@ -107,7 +107,9 @@ class MinimapOptions {
|
||||
*/
|
||||
public readonly canvasOuterHeight: number;
|
||||
|
||||
constructor(configuration: IConfiguration) {
|
||||
public readonly backgroundColor: RGBA8;
|
||||
|
||||
constructor(configuration: IConfiguration, theme: ITheme, tokensColorTracker: MinimapTokensColorTracker) {
|
||||
const options = configuration.options;
|
||||
const pixelRatio = options.get(EditorOption.pixelRatio);
|
||||
const layoutInfo = options.get(EditorOption.layoutInfo);
|
||||
@@ -131,6 +133,16 @@ class MinimapOptions {
|
||||
|
||||
this.canvasOuterWidth = this.canvasInnerWidth / pixelRatio;
|
||||
this.canvasOuterHeight = this.canvasInnerHeight / pixelRatio;
|
||||
|
||||
this.backgroundColor = MinimapOptions._getMinimapBackground(theme, tokensColorTracker);
|
||||
}
|
||||
|
||||
private static _getMinimapBackground(theme: ITheme, tokensColorTracker: MinimapTokensColorTracker): RGBA8 {
|
||||
const themeColor = theme.getColor(minimapBackground);
|
||||
if (themeColor) {
|
||||
return new RGBA8(themeColor.rgba.r, themeColor.rgba.g, themeColor.rgba.b, themeColor.rgba.a);
|
||||
}
|
||||
return tokensColorTracker.getColor(ColorId.DefaultBackground);
|
||||
}
|
||||
|
||||
public equals(other: MinimapOptions): boolean {
|
||||
@@ -148,6 +160,7 @@ class MinimapOptions {
|
||||
&& this.canvasInnerHeight === other.canvasInnerHeight
|
||||
&& this.canvasOuterWidth === other.canvasOuterWidth
|
||||
&& this.canvasOuterHeight === other.canvasOuterHeight
|
||||
&& this.backgroundColor.equals(other.backgroundColor)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -447,13 +460,13 @@ class MinimapBuffers {
|
||||
|
||||
export class Minimap extends ViewPart {
|
||||
|
||||
private readonly _tokensColorTracker: MinimapTokensColorTracker;
|
||||
private readonly _domNode: FastDomNode<HTMLElement>;
|
||||
private readonly _shadow: FastDomNode<HTMLElement>;
|
||||
private readonly _canvas: FastDomNode<HTMLCanvasElement>;
|
||||
private readonly _decorationsCanvas: FastDomNode<HTMLCanvasElement>;
|
||||
private readonly _slider: FastDomNode<HTMLElement>;
|
||||
private readonly _sliderHorizontal: FastDomNode<HTMLElement>;
|
||||
private readonly _tokensColorTracker: MinimapTokensColorTracker;
|
||||
private readonly _mouseDownListener: IDisposable;
|
||||
private readonly _sliderMouseMoveMonitor: GlobalMouseMoveMonitor<IStandardMouseMoveEventData>;
|
||||
private readonly _sliderMouseDownListener: IDisposable;
|
||||
@@ -473,7 +486,8 @@ export class Minimap extends ViewPart {
|
||||
constructor(context: ViewContext) {
|
||||
super(context);
|
||||
|
||||
this._options = new MinimapOptions(this._context.configuration);
|
||||
this._tokensColorTracker = MinimapTokensColorTracker.getInstance();
|
||||
this._options = new MinimapOptions(this._context.configuration, this._context.theme, this._tokensColorTracker);
|
||||
this._lastRenderData = null;
|
||||
this._buffers = null;
|
||||
this._selectionColor = this._context.theme.getColor(minimapSelection);
|
||||
@@ -512,8 +526,6 @@ export class Minimap extends ViewPart {
|
||||
this._sliderHorizontal.setClassName('minimap-slider-horizontal');
|
||||
this._slider.appendChild(this._sliderHorizontal);
|
||||
|
||||
this._tokensColorTracker = MinimapTokensColorTracker.getInstance();
|
||||
|
||||
this._applyLayout();
|
||||
|
||||
this._mouseDownListener = dom.addStandardDisposableListener(this._domNode.domNode, 'mousedown', (e) => {
|
||||
@@ -664,7 +676,7 @@ export class Minimap extends ViewPart {
|
||||
this._canvas.domNode.getContext('2d')!,
|
||||
this._options.canvasInnerWidth,
|
||||
this._options.canvasInnerHeight,
|
||||
this._tokensColorTracker.getColor(ColorId.DefaultBackground)
|
||||
this._options.backgroundColor
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -672,7 +684,7 @@ export class Minimap extends ViewPart {
|
||||
}
|
||||
|
||||
private _onOptionsMaybeChanged(): boolean {
|
||||
const opts = new MinimapOptions(this._context.configuration);
|
||||
const opts = new MinimapOptions(this._context.configuration, this._context.theme, this._tokensColorTracker);
|
||||
if (this._options.equals(opts)) {
|
||||
return false;
|
||||
}
|
||||
@@ -745,6 +757,7 @@ export class Minimap extends ViewPart {
|
||||
this._context.model.invalidateMinimapColorCache();
|
||||
this._selectionColor = this._context.theme.getColor(minimapSelection);
|
||||
this._renderDecorations = true;
|
||||
this._onOptionsMaybeChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -942,7 +955,7 @@ export class Minimap extends ViewPart {
|
||||
// Fetch rendering info from view model for rest of lines that need rendering.
|
||||
const lineInfo = this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed);
|
||||
const tabSize = lineInfo.tabSize;
|
||||
const background = this._tokensColorTracker.getColor(ColorId.DefaultBackground);
|
||||
const background = this._options.backgroundColor;
|
||||
const useLighterFont = this._tokensColorTracker.backgroundIsLight();
|
||||
|
||||
// Render the rest of lines
|
||||
@@ -1141,6 +1154,10 @@ export class Minimap extends ViewPart {
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const minimapBackgroundValue = theme.getColor(minimapBackground);
|
||||
if (minimapBackgroundValue) {
|
||||
collector.addRule(`.monaco-editor .minimap > canvas { opacity: ${minimapBackgroundValue.rgba.a}; will-change: opacity; }`);
|
||||
}
|
||||
const sliderBackground = theme.getColor(scrollbarSliderBackground);
|
||||
if (sliderBackground) {
|
||||
const halfSliderBackground = sliderBackground.transparent(0.5);
|
||||
|
||||
@@ -11,13 +11,13 @@ import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/v
|
||||
import { ViewContext } from 'vs/editor/common/view/viewContext';
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { EditorOption, IRulerOption } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
export class Rulers extends ViewPart {
|
||||
|
||||
public domNode: FastDomNode<HTMLElement>;
|
||||
private readonly _renderedRulers: FastDomNode<HTMLElement>[];
|
||||
private _rulers: number[];
|
||||
private _rulers: IRulerOption[];
|
||||
private _typicalHalfwidthCharacterWidth: number;
|
||||
|
||||
constructor(context: ViewContext) {
|
||||
@@ -92,9 +92,11 @@ export class Rulers extends ViewPart {
|
||||
|
||||
for (let i = 0, len = this._rulers.length; i < len; i++) {
|
||||
const node = this._renderedRulers[i];
|
||||
const ruler = this._rulers[i];
|
||||
|
||||
node.setBoxShadow(ruler.color ? `1px 0 0 0 ${ruler.color} inset` : ``);
|
||||
node.setHeight(Math.min(ctx.scrollHeight, 1000000));
|
||||
node.setLeft(this._rulers[i] * this._typicalHalfwidthCharacterWidth);
|
||||
node.setLeft(ruler.column * this._typicalHalfwidthCharacterWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,6 +557,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
this._revealLine(lineNumber, VerticalRevealType.CenterIfOutsideViewport, scrollType);
|
||||
}
|
||||
|
||||
public revealLineNearTop(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
||||
this._revealLine(lineNumber, VerticalRevealType.NearTop, scrollType);
|
||||
}
|
||||
|
||||
private _revealLine(lineNumber: number, revealType: VerticalRevealType, scrollType: editorCommon.ScrollType): void {
|
||||
if (typeof lineNumber !== 'number') {
|
||||
throw new Error('Invalid arguments');
|
||||
@@ -597,6 +601,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
);
|
||||
}
|
||||
|
||||
public revealPositionNearTop(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
||||
this._revealPosition(
|
||||
position,
|
||||
VerticalRevealType.NearTop,
|
||||
true,
|
||||
scrollType
|
||||
);
|
||||
}
|
||||
|
||||
private _revealPosition(position: IPosition, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
|
||||
if (!Position.isIPosition(position)) {
|
||||
throw new Error('Invalid arguments');
|
||||
@@ -685,6 +698,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
);
|
||||
}
|
||||
|
||||
public revealLinesNearTop(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
||||
this._revealLines(
|
||||
startLineNumber,
|
||||
endLineNumber,
|
||||
VerticalRevealType.NearTop,
|
||||
scrollType
|
||||
);
|
||||
}
|
||||
|
||||
private _revealLines(startLineNumber: number, endLineNumber: number, verticalType: VerticalRevealType, scrollType: editorCommon.ScrollType): void {
|
||||
if (typeof startLineNumber !== 'number' || typeof endLineNumber !== 'number') {
|
||||
throw new Error('Invalid arguments');
|
||||
@@ -725,6 +747,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
);
|
||||
}
|
||||
|
||||
public revealRangeNearTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
||||
this._revealRange(
|
||||
range,
|
||||
VerticalRevealType.NearTop,
|
||||
true,
|
||||
scrollType
|
||||
);
|
||||
}
|
||||
|
||||
public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
||||
this._revealRange(
|
||||
range,
|
||||
|
||||
@@ -38,7 +38,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin';
|
||||
@@ -763,6 +763,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
this.modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType);
|
||||
}
|
||||
|
||||
public revealLineNearTop(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
||||
this.modifiedEditor.revealLineNearTop(lineNumber, scrollType);
|
||||
}
|
||||
|
||||
public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
||||
this.modifiedEditor.revealPosition(position, scrollType);
|
||||
}
|
||||
@@ -775,6 +779,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
this.modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType);
|
||||
}
|
||||
|
||||
public revealPositionNearTop(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
||||
this.modifiedEditor.revealPositionNearTop(position, scrollType);
|
||||
}
|
||||
|
||||
public getSelection(): Selection | null {
|
||||
return this.modifiedEditor.getSelection();
|
||||
}
|
||||
@@ -807,6 +815,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
this.modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType);
|
||||
}
|
||||
|
||||
public revealLinesNearTop(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
||||
this.modifiedEditor.revealLinesNearTop(startLineNumber, endLineNumber, scrollType);
|
||||
}
|
||||
|
||||
public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void {
|
||||
this.modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal);
|
||||
}
|
||||
@@ -819,6 +831,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
this.modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType);
|
||||
}
|
||||
|
||||
public revealRangeNearTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
||||
this.modifiedEditor.revealRangeNearTop(range, scrollType);
|
||||
}
|
||||
|
||||
public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
||||
this.modifiedEditor.revealRangeAtTop(range, scrollType);
|
||||
}
|
||||
@@ -2209,4 +2225,32 @@ registerThemingParticipant((theme, collector) => {
|
||||
if (border) {
|
||||
collector.addRule(`.monaco-diff-editor.side-by-side .editor.modified { border-left: 1px solid ${border}; }`);
|
||||
}
|
||||
|
||||
const scrollbarSliderBackgroundColor = theme.getColor(scrollbarSliderBackground);
|
||||
if (scrollbarSliderBackgroundColor) {
|
||||
collector.addRule(`
|
||||
.monaco-diff-editor .diffViewport {
|
||||
background: ${scrollbarSliderBackgroundColor};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const scrollbarSliderHoverBackgroundColor = theme.getColor(scrollbarSliderHoverBackground);
|
||||
if (scrollbarSliderHoverBackgroundColor) {
|
||||
collector.addRule(`
|
||||
.monaco-diff-editor .diffViewport:hover {
|
||||
background: ${scrollbarSliderHoverBackgroundColor};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const scrollbarSliderActiveBackgroundColor = theme.getColor(scrollbarSliderActiveBackground);
|
||||
if (scrollbarSliderActiveBackgroundColor) {
|
||||
collector.addRule(`
|
||||
.monaco-diff-editor .diffViewport:active {
|
||||
background: ${scrollbarSliderActiveBackgroundColor};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -8,19 +8,14 @@
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.monaco-diff-editor .diffOverview .diffViewport {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* colors not externalized: using transparancy on background */
|
||||
.monaco-diff-editor.vs .diffOverview { background: rgba(0, 0, 0, 0.03); }
|
||||
.monaco-diff-editor.vs-dark .diffOverview { background: rgba(255, 255, 255, 0.01); }
|
||||
|
||||
.monaco-diff-editor .diffViewport {
|
||||
box-shadow: inset 0px 0px 1px 0px #B9B9B9;
|
||||
background: rgba(0, 0, 0, 0.10);
|
||||
}
|
||||
|
||||
.monaco-diff-editor.vs-dark .diffViewport,
|
||||
.monaco-diff-editor.hc-black .diffViewport {
|
||||
background: rgba(255, 255, 255, 0.10);
|
||||
}
|
||||
.monaco-scrollable-element.modified-in-monaco-diff-editor.vs .scrollbar { background: rgba(0,0,0,0); }
|
||||
.monaco-scrollable-element.modified-in-monaco-diff-editor.vs-dark .scrollbar { background: rgba(0,0,0,0); }
|
||||
.monaco-scrollable-element.modified-in-monaco-diff-editor.hc-black .scrollbar { background: none; }
|
||||
|
||||
@@ -484,6 +484,11 @@ const editorConfiguration: IConfigurationNode = {
|
||||
default: true,
|
||||
description: nls.localize('wordBasedSuggestions', "Controls whether completions should be computed based on words in the document.")
|
||||
},
|
||||
'editor.semanticHighlighting.enabled': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: nls.localize('semanticHighlighting.enabled', "Controls whether the semanticHighlighting is shown for the languages that support it.")
|
||||
},
|
||||
'editor.stablePeek': {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
|
||||
@@ -58,7 +58,7 @@ export interface IEditorOptions {
|
||||
* Render vertical lines at the specified columns.
|
||||
* Defaults to empty array.
|
||||
*/
|
||||
rulers?: number[];
|
||||
rulers?: (number | IRulerOption)[];
|
||||
/**
|
||||
* A string containing the word separators used when doing word navigation.
|
||||
* Defaults to `~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?
|
||||
@@ -319,6 +319,11 @@ export interface IEditorOptions {
|
||||
* Defaults to 5.
|
||||
*/
|
||||
fastScrollSensitivity?: number;
|
||||
/**
|
||||
* Enable that the editor scrolls only the predominant axis. Prevents horizontal drift when scrolling vertically on a trackpad.
|
||||
* Defaults to true.
|
||||
*/
|
||||
scrollPredominantAxis?: boolean;
|
||||
/**
|
||||
* The modifier to be used to add multiple cursors with the mouse.
|
||||
* Defaults to 'alt'
|
||||
@@ -555,6 +560,11 @@ export interface IEditorOptions {
|
||||
* Defaults to false.
|
||||
*/
|
||||
peekWidgetDefaultFocus?: 'tree' | 'editor';
|
||||
/**
|
||||
* Controls whether the definition link opens element in the peek widget.
|
||||
* Defaults to false.
|
||||
*/
|
||||
definitionLinkOpensInPeek?: boolean;
|
||||
}
|
||||
|
||||
export interface IEditorConstructionOptions extends IEditorOptions {
|
||||
@@ -619,9 +629,6 @@ export class ConfigurationChangedEvent {
|
||||
constructor(values: boolean[]) {
|
||||
this._values = values;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public hasChanged(id: EditorOption): boolean {
|
||||
return this._values[id];
|
||||
}
|
||||
@@ -1586,55 +1593,6 @@ class EditorHover extends BaseEditorOption<EditorOption.hover, EditorHoverOption
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region semantic highlighting
|
||||
|
||||
/**
|
||||
* Configuration options for semantic highlighting
|
||||
*/
|
||||
export interface IEditorSemanticHighlightingOptions {
|
||||
/**
|
||||
* Enable semantic highlighting.
|
||||
* Defaults to true.
|
||||
*/
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type EditorSemanticHighlightingOptions = Readonly<Required<IEditorSemanticHighlightingOptions>>;
|
||||
|
||||
class EditorSemanticHighlighting extends BaseEditorOption<EditorOption.semanticHighlighting, EditorSemanticHighlightingOptions> {
|
||||
|
||||
constructor() {
|
||||
const defaults: EditorSemanticHighlightingOptions = {
|
||||
enabled: true
|
||||
};
|
||||
super(
|
||||
EditorOption.semanticHighlighting, 'semanticHighlighting', defaults,
|
||||
{
|
||||
'editor.semanticHighlighting.enabled': {
|
||||
type: 'boolean',
|
||||
default: defaults.enabled,
|
||||
description: nls.localize('semanticHighlighting.enabled', "Controls whether the semanticHighlighting is shown for the languages that support it.")
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public validate(_input: any): EditorSemanticHighlightingOptions {
|
||||
if (typeof _input !== 'object') {
|
||||
return this.defaultValue;
|
||||
}
|
||||
const input = _input as IEditorSemanticHighlightingOptions;
|
||||
return {
|
||||
enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region layoutInfo
|
||||
|
||||
/**
|
||||
@@ -2356,16 +2314,37 @@ export function filterValidationDecorations(options: IComputedEditorOptions): bo
|
||||
|
||||
//#region rulers
|
||||
|
||||
class EditorRulers extends SimpleEditorOption<EditorOption.rulers, number[]> {
|
||||
export interface IRulerOption {
|
||||
readonly column: number;
|
||||
readonly color: string | null;
|
||||
}
|
||||
|
||||
class EditorRulers extends BaseEditorOption<EditorOption.rulers, IRulerOption[]> {
|
||||
|
||||
constructor() {
|
||||
const defaults: number[] = [];
|
||||
const defaults: IRulerOption[] = [];
|
||||
const columnSchema: IJSONSchema = { type: 'number', description: nls.localize('rulers.size', "Number of monospace characters at which this editor ruler will render.") };
|
||||
super(
|
||||
EditorOption.rulers, 'rulers', defaults,
|
||||
{
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'number'
|
||||
anyOf: [
|
||||
columnSchema,
|
||||
{
|
||||
type: [
|
||||
'object'
|
||||
],
|
||||
properties: {
|
||||
column: columnSchema,
|
||||
color: {
|
||||
type: 'string',
|
||||
description: nls.localize('rulers.color', "Color of this editor ruler."),
|
||||
format: 'color-hex'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
default: defaults,
|
||||
description: nls.localize('rulers', "Render vertical rulers after a certain number of monospace characters. Use multiple values for multiple rulers. No rulers are drawn if array is empty.")
|
||||
@@ -2373,13 +2352,24 @@ class EditorRulers extends SimpleEditorOption<EditorOption.rulers, number[]> {
|
||||
);
|
||||
}
|
||||
|
||||
public validate(input: any): number[] {
|
||||
public validate(input: any): IRulerOption[] {
|
||||
if (Array.isArray(input)) {
|
||||
let rulers: number[] = [];
|
||||
for (let value of input) {
|
||||
rulers.push(EditorIntOption.clampedInt(value, 0, 0, 10000));
|
||||
let rulers: IRulerOption[] = [];
|
||||
for (let _element of input) {
|
||||
if (typeof _element === 'number') {
|
||||
rulers.push({
|
||||
column: EditorIntOption.clampedInt(_element, 0, 0, 10000),
|
||||
color: null
|
||||
});
|
||||
} else if (typeof _element === 'object') {
|
||||
const element = _element as IRulerOption;
|
||||
rulers.push({
|
||||
column: EditorIntOption.clampedInt(element.column, 0, 0, 10000),
|
||||
color: element.color
|
||||
});
|
||||
}
|
||||
}
|
||||
rulers.sort((a, b) => a - b);
|
||||
rulers.sort((a, b) => a.column - b.column);
|
||||
return rulers;
|
||||
}
|
||||
return this.defaultValue;
|
||||
@@ -2681,7 +2671,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
constructor() {
|
||||
const defaults: InternalSuggestOptions = {
|
||||
insertMode: 'insert',
|
||||
insertHighlight: false,
|
||||
insertHighlight: true,
|
||||
filterGraceful: true,
|
||||
snippetsPreventQuickSuggestions: true,
|
||||
localityBonus: false,
|
||||
@@ -3192,6 +3182,7 @@ export const enum EditorOption {
|
||||
overviewRulerLanes,
|
||||
parameterHints,
|
||||
peekWidgetDefaultFocus,
|
||||
definitionLinkOpensInPeek,
|
||||
quickSuggestions,
|
||||
quickSuggestionsDelay,
|
||||
readOnly,
|
||||
@@ -3207,10 +3198,10 @@ export const enum EditorOption {
|
||||
scrollbar,
|
||||
scrollBeyondLastColumn,
|
||||
scrollBeyondLastLine,
|
||||
scrollPredominantAxis,
|
||||
selectionClipboard,
|
||||
selectionHighlight,
|
||||
selectOnLineNumbers,
|
||||
semanticHighlighting,
|
||||
showFoldingControls,
|
||||
showUnused,
|
||||
snippetSuggestions,
|
||||
@@ -3440,7 +3431,13 @@ export const EditorOptions = {
|
||||
EditorOption.foldingStrategy, 'foldingStrategy',
|
||||
'auto' as 'auto' | 'indentation',
|
||||
['auto', 'indentation'] as const,
|
||||
{ markdownDescription: nls.localize('foldingStrategy', "Controls the strategy for computing folding ranges. `auto` uses a language specific folding strategy, if available. `indentation` uses the indentation based folding strategy.") }
|
||||
{
|
||||
enumDescriptions: [
|
||||
nls.localize('foldingStrategy.auto', "Use a language-specific folding strategy if available, else the indentation-based one."),
|
||||
nls.localize('foldingStrategy.indentation', "Use the indentation-based folding strategy."),
|
||||
],
|
||||
description: nls.localize('foldingStrategy', "Controls the strategy for computing folding ranges.")
|
||||
}
|
||||
)),
|
||||
foldingHighlight: register(new EditorBooleanOption(
|
||||
EditorOption.foldingHighlight, 'foldingHighlight', true,
|
||||
@@ -3583,6 +3580,10 @@ export const EditorOptions = {
|
||||
description: nls.localize('peekWidgetDefaultFocus', "Controls whether to focus the inline editor or the tree in the peek widget.")
|
||||
}
|
||||
)),
|
||||
definitionLinkOpensInPeek: register(new EditorBooleanOption(
|
||||
EditorOption.definitionLinkOpensInPeek, 'definitionLinkOpensInPeek', false,
|
||||
{ description: nls.localize('definitionLinkOpensInPeek', "Controls whether the definition link opens element in the peek widget.") }
|
||||
)),
|
||||
quickSuggestions: register(new EditorQuickSuggestions()),
|
||||
quickSuggestionsDelay: register(new EditorIntOption(
|
||||
EditorOption.quickSuggestionsDelay, 'quickSuggestionsDelay',
|
||||
@@ -3656,6 +3657,10 @@ export const EditorOptions = {
|
||||
EditorOption.scrollBeyondLastLine, 'scrollBeyondLastLine', true,
|
||||
{ description: nls.localize('scrollBeyondLastLine', "Controls whether the editor will scroll beyond the last line.") }
|
||||
)),
|
||||
scrollPredominantAxis: register(new EditorBooleanOption(
|
||||
EditorOption.scrollPredominantAxis, 'scrollPredominantAxis', true,
|
||||
{ description: nls.localize('scrollPredominantAxis', "Scroll only along the predominant axis when scrolling both vertically and horizontally at the same time. Prevents horizontal drift when scrolling vertically on a trackpad.") }
|
||||
)),
|
||||
selectionClipboard: register(new EditorBooleanOption(
|
||||
EditorOption.selectionClipboard, 'selectionClipboard', true,
|
||||
{
|
||||
@@ -3670,12 +3675,17 @@ export const EditorOptions = {
|
||||
selectOnLineNumbers: register(new EditorBooleanOption(
|
||||
EditorOption.selectOnLineNumbers, 'selectOnLineNumbers', true,
|
||||
)),
|
||||
semanticHighlighting: register(new EditorSemanticHighlighting()),
|
||||
showFoldingControls: register(new EditorStringEnumOption(
|
||||
EditorOption.showFoldingControls, 'showFoldingControls',
|
||||
'mouseover' as 'always' | 'mouseover',
|
||||
['always', 'mouseover'] as const,
|
||||
{ description: nls.localize('showFoldingControls', "Controls whether the fold controls on the gutter are automatically hidden.") }
|
||||
{
|
||||
enumDescriptions: [
|
||||
nls.localize('showFoldingControls.always', "Always show the folding controls."),
|
||||
nls.localize('showFoldingControls.mouseover', "Only show the folding controls when the mouse is over the gutter."),
|
||||
],
|
||||
description: nls.localize('showFoldingControls', "Controls when the folding controls on the gutter are shown.")
|
||||
}
|
||||
)),
|
||||
showUnused: register(new EditorBooleanOption(
|
||||
EditorOption.showUnused, 'showUnused', true,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { CursorColumns, CursorConfiguration, ICursorSimpleModel, SingleCursorSta
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
|
||||
export class CursorPosition {
|
||||
_cursorPositionBrand: void;
|
||||
@@ -214,7 +215,7 @@ export class MoveOperations {
|
||||
public static moveToEndOfLine(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState {
|
||||
let lineNumber = cursor.position.lineNumber;
|
||||
let maxColumn = model.getLineMaxColumn(lineNumber);
|
||||
return cursor.move(inSelectionMode, lineNumber, maxColumn, 0);
|
||||
return cursor.move(inSelectionMode, lineNumber, maxColumn, Constants.MAX_SAFE_SMALL_INTEGER - maxColumn);
|
||||
}
|
||||
|
||||
public static moveToBeginningOfBuffer(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState {
|
||||
|
||||
@@ -36,6 +36,15 @@ export class RGBA8 {
|
||||
this.a = RGBA8._clamp(a);
|
||||
}
|
||||
|
||||
public equals(other: RGBA8): boolean {
|
||||
return (
|
||||
this.r === other.r
|
||||
&& this.g === other.g
|
||||
&& this.b === other.b
|
||||
&& this.a === other.a
|
||||
);
|
||||
}
|
||||
|
||||
private static _clamp(c: number): number {
|
||||
if (c < 0) {
|
||||
return 0;
|
||||
|
||||
@@ -355,6 +355,12 @@ export interface IEditor {
|
||||
*/
|
||||
revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType?: ScrollType): void;
|
||||
|
||||
/**
|
||||
* Scroll vertically as necessary and reveal a line close to the top of the viewport,
|
||||
* optimized for viewing a code definition.
|
||||
*/
|
||||
revealLineNearTop(lineNumber: number, scrollType?: ScrollType): void;
|
||||
|
||||
/**
|
||||
* Scroll vertically or horizontally as necessary and reveal a position.
|
||||
*/
|
||||
@@ -370,6 +376,12 @@ export interface IEditor {
|
||||
*/
|
||||
revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType?: ScrollType): void;
|
||||
|
||||
/**
|
||||
* Scroll vertically or horizontally as necessary and reveal a position close to the top of the viewport,
|
||||
* optimized for viewing a code definition.
|
||||
*/
|
||||
revealPositionNearTop(position: IPosition, scrollType?: ScrollType): void;
|
||||
|
||||
/**
|
||||
* Returns the primary selection of the editor.
|
||||
*/
|
||||
@@ -422,6 +434,12 @@ export interface IEditor {
|
||||
*/
|
||||
revealLinesInCenterIfOutsideViewport(lineNumber: number, endLineNumber: number, scrollType?: ScrollType): void;
|
||||
|
||||
/**
|
||||
* Scroll vertically as necessary and reveal lines close to the top of the viewport,
|
||||
* optimized for viewing a code definition.
|
||||
*/
|
||||
revealLinesNearTop(lineNumber: number, endLineNumber: number, scrollType?: ScrollType): void;
|
||||
|
||||
/**
|
||||
* Scroll vertically or horizontally as necessary and reveal a range.
|
||||
*/
|
||||
@@ -442,6 +460,12 @@ export interface IEditor {
|
||||
*/
|
||||
revealRangeInCenterIfOutsideViewport(range: IRange, scrollType?: ScrollType): void;
|
||||
|
||||
/**
|
||||
* Scroll vertically or horizontally as necessary and reveal a range close to the top of the viewport,
|
||||
* optimized for viewing a code definition.
|
||||
*/
|
||||
revealRangeNearTop(range: IRange, scrollType?: ScrollType): void;
|
||||
|
||||
/**
|
||||
* Directly trigger a handler or an editor action.
|
||||
* @param source The source of the call.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user