Merge from vscode 4d91d96e5e121b38d33508cdef17868bab255eae

This commit is contained in:
ADS Merger
2020-06-19 10:59:23 -07:00
committed by AzureDataStudio
parent a971aee5bd
commit 5e7071e466
1002 changed files with 24201 additions and 13193 deletions
@@ -3,4 +3,5 @@ src/**
tsconfig.json
out/**
extension.webpack.config.js
extension-browser.webpack.config.js
yarn.lock
@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
const path = require('path');
const clientConfig = withDefaults({
target: 'webworker',
context: __dirname,
entry: {
extension: './src/configurationEditingMain.ts'
},
output: {
filename: 'configurationEditingMain.js'
},
performance: {
hints: false
},
resolve: {
alias: {
'vscode-nls': path.resolve(__dirname, '../../build/polyfills/vscode-nls.js')
}
}
});
clientConfig.module.rules[0].use.shift(); // remove nls loader
module.exports = clientConfig;
@@ -12,7 +12,10 @@ const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts',
extension: './src/configurationEditingMain.ts',
},
output: {
filename: 'configurationEditingMain.js'
},
resolve: {
mainFields: ['module', 'main']
@@ -12,7 +12,8 @@
"onLanguage:json",
"onLanguage:jsonc"
],
"main": "./out/extension",
"main": "./out/configurationEditingMain",
"browser": "./dist/configurationEditingMain",
"scripts": {
"compile": "gulp compile-extension:configuration-editing",
"watch": "gulp watch-extension:configuration-editing"
@@ -45,6 +45,16 @@
"pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)$",
"errorMessage": "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'."
}
},
"postAttachCommand": {
"type": [
"string",
"array"
],
"description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.",
"items": {
"type": "string"
}
}
}
}
@@ -68,9 +68,33 @@
"type": "string"
}
},
"postStartCommand": {
"type": [
"string",
"array"
],
"description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.",
"items": {
"type": "string"
}
},
"postAttachCommand": {
"type": [
"string",
"array"
],
"description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.",
"items": {
"type": "string"
}
},
"devPort": {
"type": "integer",
"description": "The port VS Code can use to connect to its backend."
},
"codespaces": {
"type": "object",
"description": "Codespaces-specific configuration."
}
}
},
+2 -1
View File
@@ -30,7 +30,8 @@ export function createContext(): TestContext {
globalStoragePath: '',
logPath: '',
extensionUri: vscode.Uri.parse(''),
environmentVariableCollection: undefined as any
environmentVariableCollection: undefined as any,
extensionMode: undefined as any
}
};
}
@@ -16,6 +16,11 @@ import { languages, workspace, Disposable, TextDocument, Uri, Diagnostic, Range,
const product = JSON.parse(fs.readFileSync(path.join(env.appRoot, 'product.json'), { encoding: 'utf-8' }));
const allowedBadgeProviders: string[] = (product.extensionAllowedBadgeProviders || []).map((s: string) => s.toLowerCase());
const allowedBadgeProvidersRegex: RegExp[] = (product.extensionAllowedBadgeProvidersRegex || []).map((r: string) => new RegExp(r));
function isTrustedSVGSource(uri: Uri): boolean {
return allowedBadgeProviders.includes(uri.authority.toLowerCase()) || allowedBadgeProvidersRegex.some(r => r.test(uri.toString()));
}
const httpsRequired = localize('httpsRequired', "Images must use the HTTPS protocol.");
const svgsNotValid = localize('svgsNotValid', "SVGs are not a valid image source.");
@@ -321,7 +326,7 @@ export class ExtensionLinter {
diagnostics.push(new Diagnostic(range, message, DiagnosticSeverity.Warning));
}
if (endsWith(uri.path.toLowerCase(), '.svg') && allowedBadgeProviders.indexOf(uri.authority.toLowerCase()) === -1) {
if (endsWith(uri.path.toLowerCase(), '.svg') && !isTrustedSVGSource(uri)) {
const range = new Range(document.positionAt(begin), document.positionAt(end));
diagnostics.push(new Diagnostic(range, svgsNotValid, DiagnosticSeverity.Warning));
}
+4 -3
View File
@@ -14,7 +14,8 @@
"Other"
],
"activationEvents": [
"*"
"*",
"onFileSystem:git"
],
"main": "./out/main",
"icon": "resources/icons/git.png",
@@ -1870,14 +1871,14 @@
{
"view": "explorer",
"contents": "%view.workbench.cloneRepository%",
"when": "config.git.enabled"
"when": "config.git.enabled && git.state == initialized"
}
]
},
"dependencies": {
"byline": "^5.0.0",
"file-type": "^7.2.0",
"iconv-lite": "^0.4.24",
"iconv-lite": "0.6.0",
"jschardet": "2.1.1",
"vscode-extension-telemetry": "0.1.1",
"vscode-nls": "^4.0.0",
+2
View File
@@ -134,6 +134,8 @@ export interface CommitOptions {
export interface BranchQuery {
readonly remote?: boolean;
readonly pattern?: string;
readonly count?: number;
readonly contains?: string;
}
+4
View File
@@ -133,6 +133,8 @@ export class GitFileSystemProvider implements FileSystemProvider {
}
async stat(uri: Uri): Promise<FileStat> {
await this.model.isInitialized;
const { submoduleOf, path, ref } = fromGitUri(uri);
const repository = submoduleOf ? this.model.getRepository(submoduleOf) : this.model.getRepository(uri);
if (!repository) {
@@ -158,6 +160,8 @@ export class GitFileSystemProvider implements FileSystemProvider {
}
async readFile(uri: Uri): Promise<Uint8Array> {
await this.model.isInitialized;
const { path, ref, submoduleOf } = fromGitUri(uri);
if (submoduleOf) {
+16 -6
View File
@@ -422,7 +422,7 @@ export class Git {
const result = await this.exec(repositoryPath, ['rev-parse', '--show-toplevel']);
// Keep trailing spaces which are part of the directory name
const repoPath = path.normalize(result.stdout.trimLeft().replace(/(\r\n|\r|\n)+$/, ''));
const repoPath = path.normalize(result.stdout.trimLeft().replace(/[\r\n]+$/, ''));
if (isWindows) {
// On Git 2.25+ if you call `rev-parse --show-toplevel` on a mapped drive, instead of getting the mapped drive path back, you get the UNC path for the mapped drive.
@@ -905,7 +905,7 @@ export class Repository {
}
async buffer(object: string): Promise<Buffer> {
const child = this.stream(['show', object]);
const child = this.stream(['show', '--textconv', object]);
if (!child.stdout) {
return Promise.reject<Buffer>('Can\'t open file from git');
@@ -978,7 +978,7 @@ export class Repository {
}
async detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }> {
const child = await this.stream(['show', object]);
const child = await this.stream(['show', '--textconv', object]);
const buffer = await readBytes(child.stdout!, 4100);
try {
@@ -1791,13 +1791,23 @@ export class Repository {
.map(([ref]) => ({ name: ref, type: RefType.Head } as Branch));
}
async getRefs(opts?: { sort?: 'alphabetically' | 'committerdate', contains?: string }): Promise<Ref[]> {
const args = ['for-each-ref', '--format', '%(refname) %(objectname)'];
async getRefs(opts?: { sort?: 'alphabetically' | 'committerdate', contains?: string, pattern?: string, count?: number }): Promise<Ref[]> {
const args = ['for-each-ref'];
if (opts?.count) {
args.push(`--count=${opts.count}`);
}
if (opts && opts.sort && opts.sort !== 'alphabetically') {
args.push('--sort', `-${opts.sort}`);
}
args.push('--format', '%(refname) %(objectname)');
if (opts?.pattern) {
args.push(opts.pattern);
}
if (opts?.contains) {
args.push('--contains', opts.contains);
}
@@ -1920,7 +1930,7 @@ export class Repository {
}
async getBranches(query: BranchQuery): Promise<Ref[]> {
const refs = await this.getRefs({ contains: query.contains });
const refs = await this.getRefs({ contains: query.contains, pattern: query.pattern ? `refs/${query.pattern}` : undefined, count: query.count });
return refs.filter(value => (value.type !== RefType.Tag) && (query.remote || !value.remote));
}
+17 -1
View File
@@ -6,7 +6,7 @@
import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, OutputChannel, commands } from 'vscode';
import { Repository, RepositoryState } from './repository';
import { memoize, sequentialize, debounce } from './decorators';
import { dispose, anyEvent, filterEvent, isDescendant, firstIndex, pathEquals, toDisposable } from './util';
import { dispose, anyEvent, filterEvent, isDescendant, firstIndex, pathEquals, toDisposable, eventToPromise } from './util';
import { Git } from './git';
import * as path from 'path';
import * as fs from 'fs';
@@ -77,6 +77,15 @@ export class Model implements IRemoteSourceProviderRegistry {
commands.executeCommand('setContext', 'git.state', state);
}
@memoize
get isInitialized(): Promise<void> {
if (this._state === 'initialized') {
return Promise.resolve();
}
return eventToPromise(filterEvent(this.onDidChangeState, s => s === 'initialized')) as Promise<any>;
}
private remoteSourceProviders = new Set<RemoteSourceProvider>();
private _onDidAddRemoteSourceProvider = new EventEmitter<RemoteSourceProvider>();
@@ -148,6 +157,13 @@ export class Model implements IRemoteSourceProviderRegistry {
}
private onPossibleGitRepositoryChange(uri: Uri): void {
const config = workspace.getConfiguration('git');
const autoRepositoryDetection = config.get<boolean | 'subFolders' | 'openEditors'>('autoRepositoryDetection');
if (autoRepositoryDetection === false) {
return;
}
this.eventuallyScanPossibleGitRepository(uri.fsPath.replace(/\.git.*$/, ''));
}
+1 -1
View File
@@ -19,7 +19,7 @@ export function applyLineChanges(original: TextDocument, modified: TextDocument,
// if this is a deletion at the very end of the document,then we need to account
// for a newline at the end of the last line which may have been deleted
// https://github.com/Microsoft/vscode/issues/59670
if (isDeletion && diff.originalStartLineNumber === original.lineCount) {
if (isDeletion && diff.originalEndLineNumber === original.lineCount) {
endLine -= 1;
endCharacter = original.lineAt(endLine).range.end.character;
}
+4 -4
View File
@@ -425,10 +425,10 @@ https-proxy-agent@^2.2.1:
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"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
iconv-lite@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.0.tgz#66a93b80df0bd05d2a43a7426296b7f91073f125"
integrity sha512-43ZpGYZ9QtuutX5l6WC1DSO8ane9N+Ct5qPLF2OV7vM9abM69gnAbVkh66ibaZd3aOGkoP1ZmringlKhLBkw2Q==
dependencies:
safer-buffer ">= 2.1.2 < 3"
@@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const path = require('path');
const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
target: 'webworker',
node: false,
entry: {
extension: './src/extension.ts',
},
externals: {
'keytar': 'commonjs keytar',
},
// TODO@eamodio Deal with nls properly for the browser
// Specify module here, so we can stop the vscode-nls-dev loader from overwriting nls calls
module: {
rules: [{
test: /\.ts$/,
exclude: /node_modules/,
use: [{
// configure TypeScript loader:
// * enable sources maps for end-to-end source maps
loader: 'ts-loader',
options: {
compilerOptions: {
'sourceMap': true,
}
}
}]
}]
},
resolve: {
alias: {
'node-fetch': path.resolve(__dirname, 'node_modules/node-fetch/browser.js'),
'vscode-extension-telemetry': path.resolve(__dirname, '../../build/polyfills/vscode-extension-telemetry.js'),
'vscode-nls': path.resolve(__dirname, '../../build/polyfills/vscode-nls.js'),
'uuid': path.resolve(__dirname, 'node_modules/uuid/dist/esm-browser/index.js')
},
}
});
@@ -32,20 +32,25 @@
},
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"main": "./out/extension.js",
"browser": "./dist/extension.js",
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "gulp compile-extension:github-authentication",
"watch": "gulp watch-extension:github-authentication"
"compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none",
"watch": "gulp watch-extension:github-authentication",
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose",
"vscode:prepublish": "npm run compile"
},
"dependencies": {
"uuid": "^3.3.3",
"node-fetch": "2.6.0",
"uuid": "8.1.0",
"vscode-extension-telemetry": "0.1.1",
"vscode-nls": "^4.1.2"
},
"devDependencies": {
"@types/keytar": "^4.4.2",
"@types/node": "^10.12.21",
"@types/uuid": "^3.4.6",
"@types/node-fetch": "2.5.7",
"@types/uuid": "8.0.0",
"typescript": "^3.7.5"
}
}
@@ -5,7 +5,7 @@
// keytar depends on a native module shipped in vscode, so this is
// how we load it
import * as keytarType from 'keytar';
import type * as keytarType from 'keytar';
import * as vscode from 'vscode';
import Logger from './logger';
import * as nls from 'vscode-nls';
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as uuid from 'uuid';
import { v4 as uuid } from 'uuid';
import { keychain } from './common/keychain';
import { GitHubServer, NETWORK_ERROR } from './githubServer';
import Logger from './common/logger';
@@ -3,10 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as https from 'https';
import * as nls from 'vscode-nls';
import * as vscode from 'vscode';
import * as uuid from 'uuid';
import fetch from 'node-fetch';
import { v4 as uuid } from 'uuid';
import { PromiseAdapter, promiseFromEvent } from './common/utils';
import Logger from './common/logger';
@@ -23,6 +23,8 @@ class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.
export const uriHandler = new UriEventHandler;
const onDidManuallyProvideToken = new vscode.EventEmitter<string>();
const exchangeCodeForToken: (state: string) => PromiseAdapter<vscode.Uri, string> =
(state) => async (uri, resolve, reject) => {
Logger.info('Exchanging code for token...');
@@ -34,33 +36,24 @@ const exchangeCodeForToken: (state: string) => PromiseAdapter<vscode.Uri, string
return;
}
const post = https.request({
host: AUTH_RELAY_SERVER,
path: `/token?code=${code}&state=${state}`,
method: 'POST',
headers: {
Accept: 'application/json'
}
}, result => {
const buffer: Buffer[] = [];
result.on('data', (chunk: Buffer) => {
buffer.push(chunk);
});
result.on('end', () => {
if (result.statusCode === 200) {
const json = JSON.parse(Buffer.concat(buffer).toString());
Logger.info('Token exchange success!');
resolve(json.access_token);
} else {
reject(new Error(result.statusMessage));
try {
const result = await fetch(`https://${AUTH_RELAY_SERVER}/token?code=${code}&state=${state}`, {
method: 'POST',
headers: {
Accept: 'application/json'
}
});
});
post.end();
post.on('error', err => {
reject(err);
});
if (result.ok) {
const json = await result.json();
Logger.info('Token exchange success!');
resolve(json.access_token);
} else {
reject(result.statusText);
}
} catch (ex) {
reject(ex);
}
};
function parseQuery(uri: vscode.Uri) {
@@ -80,11 +73,14 @@ export class GitHubServer {
const state = uuid();
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
const uri = vscode.Uri.parse(`https://${AUTH_RELAY_SERVER}/authorize/?callbackUri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&responseType=code`);
const uri = vscode.Uri.parse(`https://${AUTH_RELAY_SERVER}/authorize/?callbackUri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&responseType=code&authServer=https://github.com`);
vscode.env.openExternal(uri);
await vscode.env.openExternal(uri);
return promiseFromEvent(uriHandler.event, exchangeCodeForToken(state)).finally(() => {
return Promise.race([
promiseFromEvent(uriHandler.event, exchangeCodeForToken(state)),
promiseFromEvent<string, string>(onDidManuallyProvideToken.event)
]).finally(() => {
this.updateStatusBarItem(false);
});
}
@@ -111,44 +107,33 @@ export class GitHubServer {
if (!uri.scheme || uri.scheme === 'file') { throw new Error; }
uriHandler.handleUri(uri);
} catch (e) {
Logger.error(e);
vscode.window.showErrorMessage(localize('unexpectedInput', "The input did not matched the expected format"));
// If it doesn't look like a URI, treat it as a token.
Logger.info('Treating input as token');
onDidManuallyProvideToken.fire(uriOrToken);
}
}
public async getUserInfo(token: string): Promise<{ id: string, accountName: string }> {
return new Promise((resolve, reject) => {
Logger.info('Getting account info...');
const post = https.request({
host: 'api.github.com',
path: `/user`,
method: 'GET',
try {
Logger.info('Getting user info...');
const result = await fetch('https://api.github.com/user', {
headers: {
Authorization: `token ${token}`,
'User-Agent': 'Visual-Studio-Code'
}
}, result => {
const buffer: Buffer[] = [];
result.on('data', (chunk: Buffer) => {
buffer.push(chunk);
});
result.on('end', () => {
if (result.statusCode === 200) {
const json = JSON.parse(Buffer.concat(buffer).toString());
Logger.info('Got account info!');
resolve({ id: json.id, accountName: json.login });
} else {
Logger.error(`Getting account info failed: ${result.statusMessage}`);
reject(new Error(result.statusMessage));
}
});
});
post.end();
post.on('error', err => {
Logger.error(err.message);
reject(new Error(NETWORK_ERROR));
});
});
if (result.ok) {
const json = await result.json();
Logger.info('Got account info!');
return { id: json.id, accountName: json.login };
} else {
Logger.error(`Getting account info failed: ${result.statusText}`);
throw new Error(result.statusText);
}
} catch (ex) {
Logger.error(ex.message);
throw new Error(NETWORK_ERROR);
}
}
}
+154 -78
View File
@@ -9,15 +9,28 @@
dependencies:
keytar "*"
"@types/node@^10.12.21":
version "10.17.14"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.14.tgz#b6c60ebf2fb5e4229fdd751ff9ddfae0f5f31541"
integrity sha512-G0UmX5uKEmW+ZAhmZ6PLTQ5eu/VPaT+d/tdLd5IFsKRPcbe6lPxocBtcYBFSaLaCW8O60AX90e91Nsp8lVHCNw==
"@types/node-fetch@2.5.7":
version "2.5.7"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c"
integrity sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==
dependencies:
"@types/node" "*"
form-data "^3.0.0"
"@types/uuid@^3.4.6":
version "3.4.7"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.7.tgz#51d42247473bc00e38cc8dfaf70d936842a36c03"
integrity sha512-C2j2FWgQkF1ru12SjZJyMaTPxs/f6n90+5G5qNakBxKXjTBc/YTSelHh4Pz1HUDwxFXD9WvpQhOGCDC+/Y4mIQ==
"@types/node@*":
version "14.0.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.5.tgz#3d03acd3b3414cf67faf999aed11682ed121f22b"
integrity sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA==
"@types/node@^10.12.21":
version "10.17.24"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.24.tgz#c57511e3a19c4b5e9692bb2995c40a3a52167944"
integrity sha512-5SCfvCxV74kzR3uWgTYiGxrd69TbT1I6+cMx1A5kEly/IVveJBimtAMlXiEyVFn5DvUFewQWxOOiJhlxeQwxgA==
"@types/uuid@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0"
integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==
ansi-regex@^2.0.0:
version "2.1.1"
@@ -51,23 +64,50 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0"
readable-stream "^2.0.6"
bl@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88"
integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
base64-js@^1.0.2:
version "1.3.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
bl@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a"
integrity sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==
dependencies:
readable-stream "^3.0.1"
buffer "^5.5.0"
inherits "^2.0.4"
readable-stream "^3.4.0"
buffer@^5.5.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786"
integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
chownr@^1.1.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142"
integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==
version "1.1.4"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
combined-stream@^1.0.8:
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"
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
@@ -90,6 +130,11 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
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=
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
@@ -124,6 +169,15 @@ expand-template@^2.0.3:
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
form-data@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682"
integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
@@ -153,7 +207,12 @@ has-unicode@^2.0.0:
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
inherits@^2.0.3, inherits@~2.0.3:
ieee754@^1.1.4:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -181,52 +240,69 @@ isarray@~1.0.0:
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
keytar@*:
version "5.1.0"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.1.0.tgz#d572ed9250ff2b4c8d729621397e00b17bfa5581"
integrity sha512-SptCrRDqLbTeOMB2Z9UmVOS+OKguIrMft+EUaCB8xJPiFMjy6Jnmjgv/LA0rg1ENgLelzwSsC5PSQXF0uoqNDQ==
version "6.0.1"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-6.0.1.tgz#996961abdebf300b2d34bb2eab6e42a8096b1ed8"
integrity sha512-1Ihpf2tdM3sLwGMkYHXYhVC/hx5BDR7CWFL4IrBA3IDZo0xHhS2nM+tU9Y+u/U7okNfbVkwmKsieLkcWRMh93g==
dependencies:
nan "2.14.0"
prebuild-install "5.3.3"
node-addon-api "^3.0.0"
prebuild-install "5.3.4"
mime-db@1.44.0:
version "1.44.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
mime-types@^2.1.12:
version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
dependencies:
mime-db "1.44.0"
mimic-response@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.0.0.tgz#996a51c60adf12cb8a87d7fb8ef24c2f3d5ebb46"
integrity sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ==
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
mkdirp-classic@^0.5.2:
version "0.5.3"
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "0.0.8"
nan@2.14.0:
version "2.14.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
minimist "^1.2.5"
napi-build-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508"
integrity sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==
version "1.0.2"
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"
integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
node-abi@^2.7.0:
version "2.14.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.14.0.tgz#24650e24e8ffad2b61352519263f0cf4e2ddbfe9"
integrity sha512-y54KGgEOHnRHlGQi7E5UiryRkH8bmksmQLj/9iLAjoje743YS+KaKB/sDYXgqtT0J16JT3c3AYJZNI98aU/kYg==
version "2.17.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.17.0.tgz#f167c92780497ff01eeaf473fcf8138e0fcc87fa"
integrity sha512-dFRAA0ACk/aBo0TIXQMEWMLUTyWYYT8OBYIzLmEUrQTElGRjxDCvyBZIsDL0QA7QCaj9PrawhOmTEdsuLY4uOQ==
dependencies:
semver "^5.4.1"
node-addon-api@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.0.0.tgz#812446a1001a54f71663bed188314bba07e09247"
integrity sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg==
node-fetch@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
noop-logger@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2"
@@ -259,15 +335,15 @@ once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
prebuild-install@5.3.3:
version "5.3.3"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e"
integrity sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==
prebuild-install@5.3.4:
version "5.3.4"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.4.tgz#6982d10084269d364c1856550b7d090ea31fa293"
integrity sha512-AkKN+pf4fSEihjapLEEj8n85YIw/tN6BQqkhzbDc0RvEZGdkpJBGMUYx66AAMcPG2KzmPQS7Cm16an4HVBRRMA==
dependencies:
detect-libc "^1.0.3"
expand-template "^2.0.3"
github-from-package "0.0.0"
minimist "^1.2.0"
minimist "^1.2.3"
mkdirp "^0.5.1"
napi-build-utils "^1.0.1"
node-abi "^2.7.0"
@@ -316,19 +392,19 @@ readable-stream@^2.0.6:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.0.1, readable-stream@^3.1.1:
version "3.5.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.5.0.tgz#465d70e6d1087f6162d079cd0b5db7fbebfd1606"
integrity sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==
readable-stream@^3.1.1, readable-stream@^3.4.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
safe-buffer@^5.0.1, safe-buffer@~5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
@@ -346,9 +422,9 @@ set-blocking@~2.0.0:
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
signal-exit@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
simple-concat@^1.0.0:
version "1.0.0"
@@ -415,21 +491,21 @@ strip-json-comments@~2.0.1:
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
tar-fs@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad"
integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==
version "2.1.0"
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.0.tgz#d1cdd121ab465ee0eb9ccde2d35049d3f3daf0d5"
integrity sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==
dependencies:
chownr "^1.1.1"
mkdirp "^0.5.1"
mkdirp-classic "^0.5.2"
pump "^3.0.0"
tar-stream "^2.0.0"
tar-stream@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3"
integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==
version "2.1.2"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.2.tgz#6d5ef1a7e5783a95ff70b69b97455a5968dc1325"
integrity sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==
dependencies:
bl "^3.0.0"
bl "^4.0.1"
end-of-stream "^1.4.1"
fs-constants "^1.0.0"
inherits "^2.0.3"
@@ -443,19 +519,19 @@ tunnel-agent@^0.6.0:
safe-buffer "^5.0.1"
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==
version "3.9.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a"
integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
uuid@^3.3.3:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
uuid@8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d"
integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==
vscode-extension-telemetry@0.1.1:
version "0.1.1"
+3
View File
@@ -0,0 +1,3 @@
dist
out
node_modules
+11
View File
@@ -0,0 +1,11 @@
.vscode/**
build/**
dist/**
out/**
src/**
typings/**
.gitignore
extension-browser.webpack.config.js
extension.webpack.config.js
tsconfig.json
yarn.lock
+7
View File
@@ -0,0 +1,7 @@
# GitHub FileSystem for Visual Studio Code
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
## Features
This extension provides remote GitHub repository features for VS Code.
@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const path = require('path');
const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
target: 'webworker',
node: false,
entry: {
extension: './src/extension.ts',
},
resolve: {
alias: {
'node-fetch': path.resolve(__dirname, 'node_modules/node-fetch/browser.js'),
},
}
});
@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts'
}
});
+60
View File
@@ -0,0 +1,60 @@
{
"name": "github-browser",
"displayName": "%displayName%",
"description": "%description%",
"publisher": "vscode",
"version": "0.0.1",
"engines": {
"vscode": "^1.45.0"
},
"enableProposedApi": true,
"private": true,
"categories": [
"Other"
],
"activationEvents": [
"onFileSystem:github"
],
"browser": "./dist/extension.js",
"main": "./out/extension.js",
"contributes": {
"resourceLabelFormatters": [
{
"scheme": "github",
"authority": "HEAD",
"formatting": {
"label": "github.com${path}",
"separator": "/",
"workspaceSuffix": "GitHub"
}
},
{
"scheme": "github",
"authority": "*",
"formatting": {
"label": "github.com${path} (${authority})",
"separator": "/",
"workspaceSuffix": "GitHub"
}
}
]
},
"scripts": {
"compile": "gulp compile-extension:github-browser",
"compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none",
"watch": "gulp watch-extension:github-browser",
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose",
"vscode:prepublish": "npm run compile"
},
"dependencies": {
"@octokit/graphql": "4.5.0",
"@octokit/rest": "17.11.0",
"fuzzysort": "1.1.4",
"node-fetch": "2.6.0"
},
"devDependencies": {
"@types/node-fetch": "2.5.7",
"webpack": "4.43.0",
"webpack-cli": "3.3.11"
}
}
@@ -0,0 +1,4 @@
{
"displayName": "GitHub Browser",
"description": "Remotely browse a GitHub repository"
}
@@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { GitHubFS } from './githubfs';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(new GitHubFS());
}
+496
View File
@@ -0,0 +1,496 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import {
authentication,
AuthenticationSession2,
CancellationToken,
Disposable,
Event,
EventEmitter,
FileChangeEvent,
FileSearchOptions,
FileSearchProvider,
FileSearchQuery,
FileStat,
FileSystemError,
FileSystemProvider,
FileType,
Progress,
Range,
TextSearchComplete,
TextSearchOptions,
TextSearchProvider,
TextSearchQuery,
TextSearchResult,
Uri,
workspace,
} from 'vscode';
import { Octokit } from '@octokit/rest';
import { graphql } from '@octokit/graphql/';
import * as fuzzySort from 'fuzzysort';
import fetch from 'node-fetch';
import { Iterables } from './iterables';
const emptyDisposable = { dispose: () => { /* noop */ } };
const replaceBackslashRegex = /(\/|\\)/g;
const textEncoder = new TextEncoder();
interface Fuzzysort extends Fuzzysort.Fuzzysort {
prepareSlow(target: string): Fuzzysort.Prepared;
cleanup(): void;
}
export class GitHubFS implements FileSystemProvider, FileSearchProvider, TextSearchProvider, Disposable {
static scheme = 'github';
private _onDidChangeFile = new EventEmitter<FileChangeEvent[]>();
get onDidChangeFile(): Event<FileChangeEvent[]> {
return this._onDidChangeFile.event;
}
private readonly disposable: Disposable;
private fsCache = new Map<string, any>();
constructor() {
this.disposable = Disposable.from(
workspace.registerFileSystemProvider(GitHubFS.scheme, this, {
isCaseSensitive: true,
isReadonly: true,
}),
workspace.registerFileSearchProvider(GitHubFS.scheme, this),
workspace.registerTextSearchProvider(GitHubFS.scheme, this),
);
}
dispose() {
this.disposable?.dispose();
}
private _github: Promise<GitHubApi | undefined> | undefined;
get github(): Promise<GitHubApi | undefined> {
if (this._github === undefined) {
this._github = this.getGitHubApi();
}
return this._github;
}
private async getGitHubApi(): Promise<GitHubApi | undefined> {
try {
const session = await authentication.getSession('github', ['repo'], { createIfNone: true });
return new GitHubApi(session);
} catch (ex) {
this._github = undefined;
throw ex;
}
}
//#region FileSystemProvider
watch(): Disposable {
return emptyDisposable;
}
async stat(uri: Uri): Promise<FileStat> {
if (uri.path === '' || uri.path.lastIndexOf('/') === 0) {
return { type: FileType.Directory, size: 0, ctime: 0, mtime: 0 };
}
const data = await this.fsQuery<{
__typename: string;
byteSize: number | undefined;
}>(
uri,
`__typename
...on Blob {
byteSize
}`,
this.fsCache,
);
return {
type: typenameToFileType(data?.__typename),
size: data?.byteSize ?? 0,
ctime: 0,
mtime: 0,
};
}
async readDirectory(uri: Uri): Promise<[string, FileType][]> {
const data = await this.fsQuery<{
entries: { name: string; type: string }[];
}>(
uri,
`... on Tree {
entries {
name
type
}
}`,
this.fsCache,
);
return (data?.entries ?? []).map<[string, FileType]>(e => [
e.name,
typenameToFileType(e.type),
]);
}
createDirectory(): void | Thenable<void> {
throw FileSystemError.NoPermissions;
}
async readFile(uri: Uri): Promise<Uint8Array> {
const data = await this.fsQuery<{
oid: string;
isBinary: boolean;
text: string;
}>(
uri,
`... on Blob {
oid,
isBinary,
text
}`,
);
if (data?.isBinary) {
const { owner, repo, path } = fromGitHubUri(uri);
// e.g. https://raw.githubusercontent.com/eamodio/vscode-gitlens/HEAD/images/gitlens-icon.png
const downloadUri = uri.with({
scheme: 'https',
authority: 'raw.githubusercontent.com',
path: `/${owner}/${repo}/HEAD/${path}`,
});
return downloadBinary(downloadUri);
}
return textEncoder.encode(data?.text ?? '');
}
writeFile(): void | Thenable<void> {
throw FileSystemError.NoPermissions;
}
delete(): void | Thenable<void> {
throw FileSystemError.NoPermissions;
}
rename(): void | Thenable<void> {
throw FileSystemError.NoPermissions;
}
copy?(): void | Thenable<void> {
throw FileSystemError.NoPermissions;
}
//#endregion
//#region FileSearchProvider
private fileSearchCache = new Map<string, Fuzzysort.Prepared[]>();
async provideFileSearchResults(
query: FileSearchQuery,
options: FileSearchOptions,
token: CancellationToken,
): Promise<Uri[]> {
let searchable = this.fileSearchCache.get(options.folder.toString(true));
if (searchable === undefined) {
const matches = await (await this.github)?.filesQuery(options.folder);
if (matches === undefined || token.isCancellationRequested) { return []; }
searchable = [...Iterables.map(matches, m => (fuzzySort as Fuzzysort).prepareSlow(m))];
this.fileSearchCache.set(options.folder.toString(true), searchable);
}
if (options.maxResults === undefined || options.maxResults === 0 || options.maxResults >= searchable.length) {
const results = searchable.map(m => Uri.joinPath(options.folder, m.target));
return results;
}
const results = fuzzySort
.go(query.pattern.replace(replaceBackslashRegex, '/'), searchable, {
allowTypo: true,
limit: options.maxResults,
})
.map(m => Uri.joinPath(options.folder, m.target));
(fuzzySort as Fuzzysort).cleanup();
return results;
}
//#endregion
//#region TextSearchProvider
async provideTextSearchResults(
query: TextSearchQuery,
options: TextSearchOptions,
progress: Progress<TextSearchResult>,
token: CancellationToken,
): Promise<TextSearchComplete> {
const results = await (await this.github)?.searchQuery(
query.pattern,
options.folder,
{ maxResults: options.maxResults, context: { before: options.beforeContext, after: options.afterContext } },
token,
);
if (results === undefined) { return { limitHit: true }; }
let uri;
for (const m of results.matches) {
uri = Uri.joinPath(options.folder, m.path);
progress.report({
uri: uri,
ranges: m.ranges,
preview: {
text: m.preview,
matches: m.matches,
},
});
}
return { limitHit: false };
}
//#endregion
private async fsQuery<T>(uri: Uri, query: string, cache?: Map<string, any>): Promise<T | undefined> {
const key = `${uri.toString()}:${getHashCode(query)}`;
let data = cache?.get(key);
if (data !== undefined) { return data as T; }
data = await (await this.github)?.fsQuery<T>(uri, query);
cache?.set(key, data);
return data;
}
}
async function downloadBinary(uri: Uri) {
const resp = await fetch(uri.toString());
const array = new Uint8Array(await resp.arrayBuffer());
return array;
}
function typenameToFileType(typename: string | undefined | null) {
if (typename) {
typename = typename.toLocaleLowerCase();
}
switch (typename) {
case 'blob':
return FileType.File;
case 'tree':
return FileType.Directory;
default:
return FileType.Unknown;
}
}
type RepoInfo = { owner: string; repo: string; path: string | undefined; ref?: string };
function fromGitHubUri(uri: Uri): RepoInfo {
const [, owner, repo, ...rest] = uri.path.split('/');
let ref;
if (uri.authority) {
ref = uri.authority;
}
return { owner: owner, repo: repo, path: rest.join('/'), ref: ref };
}
function getHashCode(s: string): number {
let hash = 0;
if (s.length === 0) {
return hash;
}
let char;
const len = s.length;
for (let i = 0; i < len; i++) {
char = s.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash |= 0; // Convert to 32bit integer
}
return hash;
}
interface SearchQueryMatch {
path: string;
ranges: Range[];
preview: string;
matches: Range[];
}
interface SearchQueryResults {
matches: SearchQueryMatch[];
limitHit: boolean;
}
class GitHubApi {
constructor(private readonly session: AuthenticationSession2) { }
private _graphql: typeof graphql | undefined;
private get graphql() {
if (this._graphql === undefined) {
this._graphql = graphql.defaults({
headers: {
Authorization: `Bearer ${this.token}`,
}
});
}
return this._graphql;
}
get token() {
return this.session.accessToken;
}
async filesQuery(uri: Uri) {
const { owner, repo, ref } = fromGitHubUri(uri);
try {
const resp = await new Octokit({
auth: `token ${this.token}`,
}).git.getTree({
owner: owner,
repo: repo,
recursive: '1',
tree_sha: ref ?? 'HEAD',
});
return Iterables.filterMap(resp.data.tree, p => p.type === 'blob' ? p.path : undefined);
} catch (ex) {
return [];
}
}
async searchQuery(
query: string,
uri: Uri,
options: { maxResults?: number; context?: { before?: number; after?: number } },
_token: CancellationToken,
): Promise<SearchQueryResults> {
const { owner, repo, ref } = fromGitHubUri(uri);
// If we have a specific ref, don't try to search, because GitHub search only works against the default branch
if (ref === undefined) {
return { matches: [], limitHit: true };
}
try {
const resp = await new Octokit({
auth: `token ${this.token}`,
request: {
headers: {
accept: 'application/vnd.github.v3.text-match+json',
},
}
}).search.code({
q: `${query} repo:${owner}/${repo}`,
});
// Since GitHub doesn't return ANY line numbers just fake it at the top of the file 😢
const range = new Range(0, 0, 0, 0);
const matches: SearchQueryMatch[] = [];
console.log(resp.data.items.length, resp.data.items);
let counter = 0;
let match: SearchQueryMatch;
for (const item of resp.data.items) {
for (const m of (item as typeof item & { text_matches: GitHubSearchTextMatch[] }).text_matches) {
counter++;
if (options.maxResults !== undefined && counter > options.maxResults) {
return { matches: matches, limitHit: true };
}
match = {
path: item.path,
ranges: [],
preview: m.fragment,
matches: [],
};
for (const lm of m.matches) {
let line = 0;
let shartChar = 0;
let endChar = 0;
for (let i = 0; i < lm.indices[1]; i++) {
if (i === lm.indices[0]) {
shartChar = endChar;
}
if (m.fragment[i] === '\n') {
line++;
endChar = 0;
} else {
endChar++;
}
}
match.ranges.push(range);
match.matches.push(new Range(line, shartChar, line, endChar));
}
matches.push(match);
}
}
return { matches: matches, limitHit: false };
} catch (ex) {
return { matches: [], limitHit: true };
}
}
async fsQuery<T>(uri: Uri, innerQuery: string): Promise<T | undefined> {
try {
const query = `query fs($owner: String!, $repo: String!, $path: String) {
repository(owner: $owner, name: $repo) {
object(expression: $path) {
${innerQuery}
}
}
}`;
const { owner, repo, path, ref } = fromGitHubUri(uri);
const variables = {
owner: owner,
repo: repo,
path: `${ref ?? 'HEAD'}:${path}`,
};
const rsp = await this.query<{
repository: { object: T | null | undefined };
}>(query, variables);
return rsp?.repository?.object ?? undefined;
} catch (ex) {
return undefined;
}
}
query<T>(query: string, variables: { [key: string]: string | number }): Promise<T | undefined> {
return this.graphql(query, variables) as Promise<T | undefined>;
}
}
interface GitHubSearchTextMatch {
object_url: string;
object_type: string;
property: string;
fragment: string;
matches: GitHubSearchMatch[];
}
interface GitHubSearchMatch {
text: string;
indices: number[];
}
@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
export namespace Iterables {
export function* filterMap<T, TMapped>(
source: Iterable<T> | IterableIterator<T>,
predicateMapper: (item: T) => TMapped | undefined | null,
): Iterable<TMapped> {
for (const item of source) {
const mapped = predicateMapper(item);
// eslint-disable-next-line eqeqeq
if (mapped != null) { yield mapped; }
}
}
export function* map<T, TMapped>(
source: Iterable<T> | IterableIterator<T>,
mapper: (item: T) => TMapped,
): Iterable<TMapped> {
for (const item of source) {
yield mapper(item);
}
}
}
+8
View File
@@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. 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'/>
/// <reference path="../../../types/lib.textEncoder.d.ts" />
+13
View File
@@ -0,0 +1,13 @@
{
"extends": "../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./out",
// "experimentalDecorators": true,
// "typeRoots": [
// "./node_modules/@types"
// ]
},
"include": [
"src/**/*"
]
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -24,7 +24,7 @@ function getAgent(url: string | undefined = process.env.HTTPS_PROXY): Agent {
}
}
const scopes = ['repo'];
const scopes = ['repo', 'workflow'];
export async function getSession(): Promise<AuthenticationSession> {
const authenticationSessions = await authentication.getSessions('github', scopes);
+10 -4
View File
@@ -11,9 +11,15 @@ import { GithubCredentialProviderManager } from './credentialProvider';
export async function activate(context: vscode.ExtensionContext) {
const gitExtension = vscode.extensions.getExtension<GitExtension>('vscode.git')!.exports;
const gitAPI = gitExtension.getAPI(1);
context.subscriptions.push(...registerCommands(gitAPI));
context.subscriptions.push(gitAPI.registerRemoteSourceProvider(new GithubRemoteSourceProvider(gitAPI)));
context.subscriptions.push(new GithubCredentialProviderManager(gitAPI));
try {
const gitAPI = gitExtension.getAPI(1);
context.subscriptions.push(...registerCommands(gitAPI));
context.subscriptions.push(gitAPI.registerRemoteSourceProvider(new GithubRemoteSourceProvider(gitAPI)));
context.subscriptions.push(new GithubCredentialProviderManager(gitAPI));
} catch (err) {
console.error('Could not initialize GitHub extension');
console.warn(err);
}
}
@@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
const path = require('path');
const clientConfig = withDefaults({
context: __dirname,
target: 'webworker',
entry: {
extension: './src/extension.ts'
},
resolve: {
alias: {
'vscode-extension-telemetry': path.resolve(__dirname, '../../build/polyfills/vscode-extension-telemetry.js'),
'vscode-nls': path.resolve(__dirname, '../../build/polyfills/vscode-nls.js'),
},
}
});
clientConfig.module.rules[0].use.shift(); // remove nls loader
module.exports = clientConfig;
+8 -2
View File
@@ -2,7 +2,10 @@
"name": "image-preview",
"displayName": "%displayName%",
"description": "%description%",
"extensionKind": ["ui", "workspace"],
"extensionKind": [
"ui",
"workspace"
],
"version": "1.0.0",
"publisher": "vscode",
"icon": "icon.png",
@@ -13,6 +16,7 @@
"vscode": "^1.39.0"
},
"main": "./out/extension",
"browser": "./dist/extension.js",
"categories": [
"Other"
],
@@ -65,7 +69,9 @@
"compile": "gulp compile-extension:image-preview",
"watch": "npm run build-preview && gulp watch-extension:image-preview",
"vscode:prepublish": "npm run build-ext",
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:image-preview ./tsconfig.json"
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:image-preview ./tsconfig.json",
"compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none",
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose"
},
"dependencies": {
"vscode-extension-telemetry": "0.1.1",
+2 -4
View File
@@ -10,8 +10,6 @@ import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry';
import { ZoomStatusBarEntry } from './zoomStatusBarEntry';
export function activate(context: vscode.ExtensionContext) {
const extensionRoot = vscode.Uri.file(context.extensionPath);
const sizeStatusBarEntry = new SizeStatusBarEntry();
context.subscriptions.push(sizeStatusBarEntry);
@@ -21,9 +19,9 @@ export function activate(context: vscode.ExtensionContext) {
const zoomStatusBarEntry = new ZoomStatusBarEntry();
context.subscriptions.push(zoomStatusBarEntry);
const previewManager = new PreviewManager(extensionRoot, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry);
const previewManager = new PreviewManager(context.extensionUri, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry);
context.subscriptions.push(vscode.window.registerCustomEditorProvider2(PreviewManager.viewType, previewManager, {
context.subscriptions.push(vscode.window.registerCustomEditorProvider(PreviewManager.viewType, previewManager, {
supportsMultipleEditorsPerDocument: true,
}));
@@ -15,4 +15,6 @@ server/.npmignore
yarn.lock
CONTRIBUTING.md
server/extension.webpack.config.js
extension.webpack.config.js
extension.webpack.config.js
server/extension-browser.webpack.config.js
extension-browser.webpack.config.js
@@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext } from 'vscode';
import { LanguageClientOptions } from 'vscode-languageclient';
import { startClient, LanguageClientConstructor } from '../jsonClient';
import { LanguageClient } from 'vscode-languageclient/browser';
import { RequestService } from '../requests';
declare const Worker: {
new(stringUrl: string): any;
};
declare function fetch(uri: string, options: any): any;
// this method is called when vs code is activated
export function activate(context: ExtensionContext) {
const serverMain = context.asAbsolutePath('server/dist/browser/jsonServerMain.js');
try {
const worker = new Worker(serverMain);
const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
return new LanguageClient(id, name, clientOptions, worker);
};
const http: RequestService = {
getContent(uri: string) {
return fetch(uri, { mode: 'cors' })
.then(function (response: any) {
return response.text();
});
}
};
startClient(context, newLanguageClient, { http });
} catch (e) {
console.log(e);
}
}
@@ -2,11 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import * as fs from 'fs';
import * as nls from 'vscode-nls';
import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light';
const localize = nls.loadMessageBundle();
@@ -16,13 +12,13 @@ import {
ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString,
} from 'vscode';
import {
LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType,
LanguageClientOptions, RequestType, NotificationType,
DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams,
DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature
DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, CommonLanguageClient
} from 'vscode-languageclient';
import TelemetryReporter from 'vscode-extension-telemetry';
import { hash } from './utils/hash';
import { RequestService, joinPath } from './requests';
namespace VSCodeContentRequest {
export const type: RequestType<string, string, any, any> = new RequestType('vscode/content');
@@ -53,12 +49,6 @@ namespace ResultLimitReachedNotification {
export const type: NotificationType<string, any> = new NotificationType('json/resultLimitReached');
}
interface IPackageInfo {
name: string;
version: string;
aiKey: string;
}
interface Settings {
json?: {
schemas?: JSONSchemaSettings[];
@@ -83,29 +73,27 @@ namespace SettingIds {
export const maxItemsComputed = 'json.maxItemsComputed';
}
let telemetryReporter: TelemetryReporter | undefined;
export interface TelemetryReporter {
sendTelemetryEvent(eventName: string, properties?: {
[key: string]: string;
}, measurements?: {
[key: string]: number;
}): void;
}
export function activate(context: ExtensionContext) {
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient;
export interface Runtime {
http: RequestService;
telemetry?: TelemetryReporter
}
export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) {
const toDispose = context.subscriptions;
let rangeFormatting: Disposable | undefined = undefined;
const packageInfo = getPackageInfo(context);
telemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
const serverMain = readJSONFile(context.asAbsolutePath('./server/package.json')).main;
const serverModule = context.asAbsolutePath(path.join('server', serverMain));
// The debug options for the server
const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (9000 + Math.round(Math.random() * 10000))] };
// If the extension is launch in debug mode the debug server options are use
// Otherwise the run options are used
const serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
};
const documentSelector = ['json', 'jsonc'];
@@ -119,6 +107,7 @@ export function activate(context: ExtensionContext) {
toDispose.push(schemaResolutionErrorStatusBarItem);
const fileSchemaErrors = new Map<string, string>();
let schemaDownloadEnabled = true;
// Options to control the language client
const clientOptions: LanguageClientOptions = {
@@ -139,7 +128,7 @@ export function activate(context: ExtensionContext) {
didChangeConfiguration: () => client.sendNotification(DidChangeConfigurationNotification.type, { settings: getSettings() })
},
handleDiagnostics: (uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => {
const schemaErrorIndex = diagnostics.findIndex(candidate => candidate.code === /* SchemaResolveError */ 0x300);
const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError);
if (schemaErrorIndex === -1) {
fileSchemaErrors.delete(uri.toString());
@@ -149,6 +138,10 @@ export function activate(context: ExtensionContext) {
const schemaResolveDiagnostic = diagnostics[schemaErrorIndex];
fileSchemaErrors.set(uri.toString(), schemaResolveDiagnostic.message);
if (!schemaDownloadEnabled) {
diagnostics = diagnostics.filter(d => !isSchemaResolveError(d));
}
if (window.activeTextEditor && window.activeTextEditor.document.uri.toString() === uri.toString()) {
schemaResolutionErrorStatusBarItem.show();
}
@@ -197,49 +190,39 @@ export function activate(context: ExtensionContext) {
};
// Create the language client and start the client.
const client = new LanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), serverOptions, clientOptions);
const client = newLanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), clientOptions);
client.registerProposedFeatures();
const disposable = client.start();
toDispose.push(disposable);
client.onReady().then(() => {
const schemaDocuments: { [uri: string]: boolean } = {};
let schemaDownloadEnabled = true;
// handle content request
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
const uri = Uri.parse(uriPath);
if (uri.scheme === 'untitled') {
return Promise.reject(new Error(localize('untitled.schema', 'Unable to load {0}', uri.toString())));
return Promise.reject(new ResponseError(3, localize('untitled.schema', 'Unable to load {0}', uri.toString())));
}
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
return workspace.openTextDocument(uri).then(doc => {
schemaDocuments[uri.toString()] = true;
return doc.getText();
}, error => {
return Promise.reject(error);
return Promise.reject(new ResponseError(2, error.toString()));
});
} else if (schemaDownloadEnabled) {
if (telemetryReporter && uri.authority === 'schema.management.azure.com') {
if (runtime.telemetry && uri.authority === 'schema.management.azure.com') {
/* __GDPR__
"json.schema" : {
"schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
telemetryReporter.sendTelemetryEvent('json.schema', { schemaURL: uriPath });
runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath });
}
const headers = { 'Accept-Encoding': 'gzip, deflate' };
return xhr({ url: uriPath, followRedirects: 5, headers }).then(response => {
return response.responseText;
}, (error: XHRResponse) => {
let extraInfo = error.responseText || error.toString();
if (extraInfo.length > 256) {
extraInfo = `${extraInfo.substr(0, 256)}...`;
}
return Promise.reject(new ResponseError(error.status, getErrorStatusDescription(error.status) + '\n' + extraInfo));
});
return runtime.http.getContent(uriPath);
} else {
return Promise.reject(localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload));
return Promise.reject(new ResponseError(1, localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload)));
}
});
@@ -280,7 +263,7 @@ export function activate(context: ExtensionContext) {
schemaResolutionErrorStatusBarItem.text = '$(watch)';
const activeDocUri = window.activeTextEditor.document.uri.toString();
client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => {
const schemaErrorIndex = diagnostics.findIndex(candidate => candidate.code === /* SchemaResolveError */ 0x300);
const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError);
if (schemaErrorIndex !== -1) {
// Show schema resolution errors in status bar only; ref: #51032
const schemaResolveDiagnostic = diagnostics[schemaErrorIndex];
@@ -335,7 +318,7 @@ export function activate(context: ExtensionContext) {
return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then(
client.protocol2CodeConverter.asTextEdits,
(error) => {
client.logFailedRequest(DocumentRangeFormattingRequest.type, error);
client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []);
return Promise.resolve([]);
}
);
@@ -370,12 +353,6 @@ export function activate(context: ExtensionContext) {
}
export function deactivate(): Promise<any> {
return telemetryReporter ? telemetryReporter.dispose() : Promise.resolve(null);
}
function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] {
const associations: ISchemaAssociation[] = [];
extensions.all.forEach(extension => {
@@ -388,9 +365,10 @@ function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[]
if (typeof fileMatch === 'string') {
fileMatch = [fileMatch];
}
if (Array.isArray(fileMatch) && url) {
if (url[0] === '.' && url[1] === '/') {
url = Uri.file(path.join(extension.extensionPath, url)).toString();
if (Array.isArray(fileMatch) && typeof url === 'string') {
let uri: string = url;
if (uri[0] === '.' && uri[1] === '/') {
uri = joinPath(extension.extensionUri, uri).toString();
}
fileMatch = fileMatch.map(fm => {
if (fm[0] === '%') {
@@ -402,7 +380,7 @@ function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[]
}
return fm;
});
associations.push({ fileMatch, uri: url });
associations.push({ fileMatch, uri });
}
});
}
@@ -504,39 +482,18 @@ function getSettings(): Settings {
return settings;
}
function getSchemaId(schema: JSONSchemaSettings, folderUri?: Uri) {
function getSchemaId(schema: JSONSchemaSettings, folderUri?: Uri): string | undefined {
let url = schema.url;
if (!url) {
if (schema.schema) {
url = schema.schema.id || `vscode://schemas/custom/${encodeURIComponent(hash(schema.schema).toString(16))}`;
}
} else if (folderUri && (url[0] === '.' || url[0] === '/')) {
url = folderUri.with({ path: path.posix.join(folderUri.path, url) }).toString();
url = joinPath(folderUri, url).toString();
}
return url;
}
function getPackageInfo(context: ExtensionContext): IPackageInfo | undefined {
const extensionPackage = readJSONFile(context.asAbsolutePath('./package.json'));
if (extensionPackage) {
return {
name: extensionPackage.name,
version: extensionPackage.version,
aiKey: extensionPackage.aiKey
};
}
return undefined;
}
function readJSONFile(location: string) {
try {
return JSON.parse(fs.readFileSync(location).toString());
} catch (e) {
console.log(`Problems reading ${location}: ${e}`);
return {};
}
}
function isThenable<T>(obj: ProviderResult<T>): obj is Thenable<T> {
return obj && (<any>obj)['then'];
}
@@ -546,3 +503,7 @@ function updateMarkdownString(h: MarkdownString): MarkdownString {
n.isTrusted = h.isTrusted;
return n;
}
function isSchemaResolveError(d: Diagnostic) {
return d.code === /* SchemaResolveError */ 0x300;
}
@@ -0,0 +1,76 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext } from 'vscode';
import { startClient, LanguageClientConstructor } from '../jsonClient';
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node';
import * as fs from 'fs';
import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light';
import TelemetryReporter from 'vscode-extension-telemetry';
import { RequestService } from '../requests';
let telemetry: TelemetryReporter | undefined;
// this method is called when vs code is activated
export function activate(context: ExtensionContext) {
const clientPackageJSON = getPackageInfo(context);
telemetry = new TelemetryReporter(clientPackageJSON.name, clientPackageJSON.version, clientPackageJSON.aiKey);
const serverMain = `./server/${clientPackageJSON.main.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/jsonServerMain`;
const serverModule = context.asAbsolutePath(serverMain);
// The debug options for the server
const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] };
// If the extension is launch in debug mode the debug server options are use
// Otherwise the run options are used
const serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
};
const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
return new LanguageClient(id, name, serverOptions, clientOptions);
};
startClient(context, newLanguageClient, { http: getHTTPRequestService(), telemetry });
}
export function deactivate(): Promise<any> {
return telemetry ? telemetry.dispose() : Promise.resolve(null);
}
interface IPackageInfo {
name: string;
version: string;
aiKey: string;
main: string;
}
function getPackageInfo(context: ExtensionContext): IPackageInfo {
const location = context.asAbsolutePath('./package.json');
try {
return JSON.parse(fs.readFileSync(location).toString());
} catch (e) {
console.log(`Problems reading ${location}: ${e}`);
return { name: '', version: '', aiKey: '', main: '' };
}
}
function getHTTPRequestService(): RequestService {
return {
getContent(uri: string, _encoding?: string) {
const headers = { 'Accept-Encoding': 'gzip, deflate' };
return xhr({ url: uri, followRedirects: 5, headers }).then(response => {
return response.responseText;
}, (error: XHRResponse) => {
return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString());
});
}
};
}
@@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Uri } from 'vscode';
export interface RequestService {
getContent(uri: string, encoding?: string): Thenable<string>;
}
export function getScheme(uri: string) {
return uri.substr(0, uri.indexOf(':'));
}
export function dirname(uri: string) {
const lastIndexOfSlash = uri.lastIndexOf('/');
return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : '';
}
export function basename(uri: string) {
const lastIndexOfSlash = uri.lastIndexOf('/');
return uri.substr(lastIndexOfSlash + 1);
}
const Slash = '/'.charCodeAt(0);
const Dot = '.'.charCodeAt(0);
export function isAbsolutePath(path: string) {
return path.charCodeAt(0) === Slash;
}
export function resolvePath(uri: Uri, path: string): Uri {
if (isAbsolutePath(path)) {
return uri.with({ path: normalizePath(path.split('/')) });
}
return joinPath(uri, path);
}
export function normalizePath(parts: string[]): string {
const newParts: string[] = [];
for (const part of parts) {
if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) {
// ignore
} else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) {
newParts.pop();
} else {
newParts.push(part);
}
}
if (parts.length > 1 && parts[parts.length - 1].length === 0) {
newParts.push('');
}
let res = newParts.join('/');
if (parts[0].length === 0) {
res = '/' + res;
}
return res;
}
export function joinPath(uri: Uri, ...paths: string[]): Uri {
const parts = uri.path.split('/');
for (let path of paths) {
parts.push(...path.split('/'));
}
return uri.with({ path: normalizePath(parts) });
}
@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
const path = require('path');
const clientConfig = withDefaults({
target: 'webworker',
context: path.join(__dirname, 'client'),
entry: {
extension: './src/browser/jsonClientMain.ts'
},
output: {
filename: 'jsonClientMain.js',
path: path.join(__dirname, 'client', 'dist', 'browser')
},
performance: {
hints: false
},
resolve: {
alias: {
'vscode-nls': path.resolve(__dirname, '../../build/polyfills/vscode-nls.js')
}
}
});
clientConfig.module.rules[0].use.shift(); // remove nls loader
module.exports = clientConfig;
@@ -14,11 +14,11 @@ const webpack = require('webpack');
const config = withDefaults({
context: path.join(__dirname, 'client'),
entry: {
extension: './src/jsonMain.ts',
extension: './src/node/jsonClientMain.ts'
},
output: {
filename: 'jsonMain.js',
path: path.join(__dirname, 'client', 'dist')
filename: 'jsonClientMain.js',
path: path.join(__dirname, 'client', 'dist', 'node')
}
});
@@ -14,7 +14,8 @@
"onLanguage:json",
"onLanguage:jsonc"
],
"main": "./client/out/jsonMain",
"main": "./client/out/node/jsonClientMain",
"browser": "./client/dist/browser/jsonClientMain",
"enableProposedApi": true,
"scripts": {
"compile": "gulp compile-extension:json-language-features-client compile-extension:json-language-features-server",
@@ -128,7 +129,7 @@
"dependencies": {
"request-light": "^0.3.0",
"vscode-extension-telemetry": "0.1.1",
"vscode-languageclient": "^6.1.3",
"vscode-languageclient": "7.0.0-next.5",
"vscode-nls": "^4.1.2"
},
"devDependencies": {
@@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../../shared.webpack.config');
const path = require('path');
const serverConfig = withDefaults({
target: 'webworker',
context: __dirname,
entry: {
extension: './src/browser/jsonServerMain.ts',
},
output: {
filename: 'jsonServerMain.js',
path: path.join(__dirname, 'dist', 'browser'),
libraryTarget: 'var'
},
performance: {
hints: false
},
resolve: {
alias: {
'vscode-nls': path.resolve(__dirname, '../../../build/polyfills/vscode-nls.js')
}
}
});
serverConfig.module.rules[0].use.shift(); // remove nls loader
module.exports = serverConfig;
@@ -14,11 +14,11 @@ const webpack = require('webpack');
const config = withDefaults({
context: path.join(__dirname),
entry: {
extension: './src/jsonServerMain.ts',
extension: './src/node/jsonServerMain.ts',
},
output: {
filename: 'jsonServerMain.js',
path: path.join(__dirname, 'dist')
path: path.join(__dirname, 'dist', 'node'),
}
});
@@ -14,9 +14,9 @@
"dependencies": {
"jsonc-parser": "^2.2.1",
"request-light": "^0.3.0",
"vscode-json-languageservice": "^3.6.0",
"vscode-languageserver": "^6.1.1",
"vscode-uri": "^2.1.1"
"vscode-json-languageservice": "^3.7.0",
"vscode-languageserver": "7.0.0-next.3",
"vscode-uri": "^2.1.2"
},
"devDependencies": {
"@types/mocha": "2.2.33",
@@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createConnection, BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver/browser';
import { startServer } from '../jsonServer';
declare let self: any;
const messageReader = new BrowserMessageReader(self);
const messageWriter = new BrowserMessageWriter(self);
const connection = createConnection(messageReader, messageWriter);
startServer(connection, {});
@@ -0,0 +1,504 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import {
Connection,
TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType,
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit
} from 'vscode-languageserver';
import { formatError, runSafe, runSafeAsync } from './utils/runner';
import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Diagnostic, Range, Position } from 'vscode-json-languageservice';
import { getLanguageModelCache } from './languageModelCache';
import { RequestService, basename, resolvePath } from './requests';
interface ISchemaAssociations {
[pattern: string]: string[];
}
interface ISchemaAssociation {
fileMatch: string[];
uri: string;
}
namespace SchemaAssociationNotification {
export const type: NotificationType<ISchemaAssociations | ISchemaAssociation[], any> = new NotificationType('json/schemaAssociations');
}
namespace VSCodeContentRequest {
export const type: RequestType<string, string, any, any> = new RequestType('vscode/content');
}
namespace SchemaContentChangeNotification {
export const type: NotificationType<string, any> = new NotificationType('json/schemaContent');
}
namespace ResultLimitReachedNotification {
export const type: NotificationType<string, any> = new NotificationType('json/resultLimitReached');
}
namespace ForceValidateRequest {
export const type: RequestType<string, Diagnostic[], any, any> = new RequestType('json/validate');
}
const workspaceContext = {
resolveRelativePath: (relativePath: string, resource: string) => {
const base = resource.substr(0, resource.lastIndexOf('/') + 1);
return resolvePath(base, relativePath);
}
};
export interface RuntimeEnvironment {
file?: RequestService;
http?: RequestService
configureHttpRequests?(proxy: string, strictSSL: boolean): void;
}
export function startServer(connection: Connection, runtime: RuntimeEnvironment) {
function getSchemaRequestService(handledSchemas: string[] = ['https', 'http', 'file']) {
const builtInHandlers: { [protocol: string]: RequestService | undefined } = {};
for (let protocol of handledSchemas) {
if (protocol === 'file') {
builtInHandlers[protocol] = runtime.file;
} else if (protocol === 'http' || protocol === 'https') {
builtInHandlers[protocol] = runtime.http;
}
}
return (uri: string): Thenable<string> => {
const protocol = uri.substr(0, uri.indexOf(':'));
const builtInHandler = builtInHandlers[protocol];
if (builtInHandler) {
return builtInHandler.getContent(uri);
}
return connection.sendRequest(VSCodeContentRequest.type, uri).then(responseText => {
return responseText;
}, error => {
return Promise.reject(error.message);
});
};
}
// create the JSON language service
let languageService = getLanguageService({
workspaceContext,
contributions: [],
clientCapabilities: ClientCapabilities.LATEST
});
// Create a text document manager.
const documents = new TextDocuments(TextDocument);
// Make the text document manager listen on the connection
// for open, change and close text document events
documents.listen(connection);
let clientSnippetSupport = false;
let dynamicFormatterRegistration = false;
let hierarchicalDocumentSymbolSupport = false;
let foldingRangeLimitDefault = Number.MAX_VALUE;
let foldingRangeLimit = Number.MAX_VALUE;
let resultLimit = Number.MAX_VALUE;
let formatterMaxNumberOfEdits = Number.MAX_VALUE;
// After the server has started the client sends an initialize request. The server receives
// in the passed params the rootPath of the workspace plus the client capabilities.
connection.onInitialize((params: InitializeParams): InitializeResult => {
const handledProtocols = params.initializationOptions?.handledSchemaProtocols;
languageService = getLanguageService({
schemaRequestService: getSchemaRequestService(handledProtocols),
workspaceContext,
contributions: [],
clientCapabilities: params.capabilities
});
function getClientCapability<T>(name: string, def: T) {
const keys = name.split('.');
let c: any = params.capabilities;
for (let i = 0; c && i < keys.length; i++) {
if (!c.hasOwnProperty(keys[i])) {
return def;
}
c = c[keys[i]];
}
return c;
}
clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false);
dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions?.provideFormatter !== 'boolean');
foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE);
hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false);
formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE;
const capabilities: ServerCapabilities = {
textDocumentSync: TextDocumentSyncKind.Incremental,
completionProvider: clientSnippetSupport ? {
resolveProvider: false, // turn off resolving as the current language service doesn't do anything on resolve. Also fixes #91747
triggerCharacters: ['"', ':']
} : undefined,
hoverProvider: true,
documentSymbolProvider: true,
documentRangeFormattingProvider: params.initializationOptions.provideFormatter === true,
colorProvider: {},
foldingRangeProvider: true,
selectionRangeProvider: true,
definitionProvider: true
};
return { capabilities };
});
// The settings interface describes the server relevant settings part
interface Settings {
json: {
schemas: JSONSchemaSettings[];
format: { enable: boolean; };
resultLimit?: number;
};
http: {
proxy: string;
proxyStrictSSL: boolean;
};
}
interface JSONSchemaSettings {
fileMatch?: string[];
url?: string;
schema?: JSONSchema;
}
const limitExceededWarnings = function () {
const pendingWarnings: { [uri: string]: { features: { [name: string]: string }; timeout?: NodeJS.Timeout; } } = {};
return {
cancel(uri: string) {
const warning = pendingWarnings[uri];
if (warning && warning.timeout) {
clearTimeout(warning.timeout);
delete pendingWarnings[uri];
}
},
onResultLimitExceeded(uri: string, resultLimit: number, name: string) {
return () => {
let warning = pendingWarnings[uri];
if (warning) {
if (!warning.timeout) {
// already shown
return;
}
warning.features[name] = name;
warning.timeout.refresh();
} else {
warning = { features: { [name]: name } };
warning.timeout = setTimeout(() => {
connection.sendNotification(ResultLimitReachedNotification.type, `${basename(uri)}: For performance reasons, ${Object.keys(warning.features).join(' and ')} have been limited to ${resultLimit} items.`);
warning.timeout = undefined;
}, 2000);
pendingWarnings[uri] = warning;
}
};
}
};
}();
let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = undefined;
let schemaAssociations: ISchemaAssociations | ISchemaAssociation[] | undefined = undefined;
let formatterRegistration: Thenable<Disposable> | null = null;
// The settings have changed. Is send on server activation as well.
connection.onDidChangeConfiguration((change) => {
let settings = <Settings>change.settings;
if (runtime.configureHttpRequests) {
runtime.configureHttpRequests(settings.http && settings.http.proxy, settings.http && settings.http.proxyStrictSSL);
}
jsonConfigurationSettings = settings.json && settings.json.schemas;
updateConfiguration();
foldingRangeLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || foldingRangeLimitDefault, 0));
resultLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || Number.MAX_VALUE, 0));
// dynamically enable & disable the formatter
if (dynamicFormatterRegistration) {
const enableFormatter = settings && settings.json && settings.json.format && settings.json.format.enable;
if (enableFormatter) {
if (!formatterRegistration) {
formatterRegistration = connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector: [{ language: 'json' }, { language: 'jsonc' }] });
}
} else if (formatterRegistration) {
formatterRegistration.then(r => r.dispose());
formatterRegistration = null;
}
}
});
// The jsonValidation extension configuration has changed
connection.onNotification(SchemaAssociationNotification.type, associations => {
schemaAssociations = associations;
updateConfiguration();
});
// A schema has changed
connection.onNotification(SchemaContentChangeNotification.type, uri => {
languageService.resetSchema(uri);
});
// Retry schema validation on all open documents
connection.onRequest(ForceValidateRequest.type, uri => {
return new Promise<Diagnostic[]>(resolve => {
const document = documents.get(uri);
if (document) {
updateConfiguration();
validateTextDocument(document, diagnostics => {
resolve(diagnostics);
});
} else {
resolve([]);
}
});
});
function updateConfiguration() {
const languageSettings = {
validate: true,
allowComments: true,
schemas: new Array<SchemaConfiguration>()
};
if (schemaAssociations) {
if (Array.isArray(schemaAssociations)) {
Array.prototype.push.apply(languageSettings.schemas, schemaAssociations);
} else {
for (const pattern in schemaAssociations) {
const association = schemaAssociations[pattern];
if (Array.isArray(association)) {
association.forEach(uri => {
languageSettings.schemas.push({ uri, fileMatch: [pattern] });
});
}
}
}
}
if (jsonConfigurationSettings) {
jsonConfigurationSettings.forEach((schema, index) => {
let uri = schema.url;
if (!uri && schema.schema) {
uri = schema.schema.id || `vscode://schemas/custom/${index}`;
}
if (uri) {
languageSettings.schemas.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema });
}
});
}
languageService.configure(languageSettings);
// Revalidate any open text documents
documents.all().forEach(triggerValidation);
}
// The content of a text document has changed. This event is emitted
// when the text document first opened or when its content has changed.
documents.onDidChangeContent((change) => {
limitExceededWarnings.cancel(change.document.uri);
triggerValidation(change.document);
});
// a document has closed: clear all diagnostics
documents.onDidClose(event => {
limitExceededWarnings.cancel(event.document.uri);
cleanPendingValidation(event.document);
connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
});
const pendingValidationRequests: { [uri: string]: NodeJS.Timer; } = {};
const validationDelayMs = 300;
function cleanPendingValidation(textDocument: TextDocument): void {
const request = pendingValidationRequests[textDocument.uri];
if (request) {
clearTimeout(request);
delete pendingValidationRequests[textDocument.uri];
}
}
function triggerValidation(textDocument: TextDocument): void {
cleanPendingValidation(textDocument);
pendingValidationRequests[textDocument.uri] = setTimeout(() => {
delete pendingValidationRequests[textDocument.uri];
validateTextDocument(textDocument);
}, validationDelayMs);
}
function validateTextDocument(textDocument: TextDocument, callback?: (diagnostics: Diagnostic[]) => void): void {
const respond = (diagnostics: Diagnostic[]) => {
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
if (callback) {
callback(diagnostics);
}
};
if (textDocument.getText().length === 0) {
respond([]); // ignore empty documents
return;
}
const jsonDocument = getJSONDocument(textDocument);
const version = textDocument.version;
const documentSettings: DocumentLanguageSettings = textDocument.languageId === 'jsonc' ? { comments: 'ignore', trailingCommas: 'warning' } : { comments: 'error', trailingCommas: 'error' };
languageService.doValidation(textDocument, jsonDocument, documentSettings).then(diagnostics => {
setImmediate(() => {
const currDocument = documents.get(textDocument.uri);
if (currDocument && currDocument.version === version) {
respond(diagnostics); // Send the computed diagnostics to VSCode.
}
});
}, error => {
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, error));
});
}
connection.onDidChangeWatchedFiles((change) => {
// Monitored files have changed in VSCode
let hasChanges = false;
change.changes.forEach(c => {
if (languageService.resetSchema(c.uri)) {
hasChanges = true;
}
});
if (hasChanges) {
documents.all().forEach(triggerValidation);
}
});
const jsonDocuments = getLanguageModelCache<JSONDocument>(10, 60, document => languageService.parseJSONDocument(document));
documents.onDidClose(e => {
jsonDocuments.onDocumentRemoved(e.document);
});
connection.onShutdown(() => {
jsonDocuments.dispose();
});
function getJSONDocument(document: TextDocument): JSONDocument {
return jsonDocuments.get(document);
}
connection.onCompletion((textDocumentPosition, token) => {
return runSafeAsync(async () => {
const document = documents.get(textDocumentPosition.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
return languageService.doComplete(document, textDocumentPosition.position, jsonDocument);
}
return null;
}, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token);
});
connection.onHover((textDocumentPositionParams, token) => {
return runSafeAsync(async () => {
const document = documents.get(textDocumentPositionParams.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
return languageService.doHover(document, textDocumentPositionParams.position, jsonDocument);
}
return null;
}, null, `Error while computing hover for ${textDocumentPositionParams.textDocument.uri}`, token);
});
connection.onDocumentSymbol((documentSymbolParams, token) => {
return runSafe(() => {
const document = documents.get(documentSymbolParams.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
const onResultLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document symbols');
if (hierarchicalDocumentSymbolSupport) {
return languageService.findDocumentSymbols2(document, jsonDocument, { resultLimit, onResultLimitExceeded });
} else {
return languageService.findDocumentSymbols(document, jsonDocument, { resultLimit, onResultLimitExceeded });
}
}
return [];
}, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token);
});
connection.onDocumentRangeFormatting((formatParams, token) => {
return runSafe(() => {
const document = documents.get(formatParams.textDocument.uri);
if (document) {
const edits = languageService.format(document, formatParams.range, formatParams.options);
if (edits.length > formatterMaxNumberOfEdits) {
const newText = TextDocument.applyEdits(document, edits);
return [TextEdit.replace(Range.create(Position.create(0, 0), document.positionAt(document.getText().length)), newText)];
}
return edits;
}
return [];
}, [], `Error while formatting range for ${formatParams.textDocument.uri}`, token);
});
connection.onDocumentColor((params, token) => {
return runSafeAsync(async () => {
const document = documents.get(params.textDocument.uri);
if (document) {
const onResultLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document colors');
const jsonDocument = getJSONDocument(document);
return languageService.findDocumentColors(document, jsonDocument, { resultLimit, onResultLimitExceeded });
}
return [];
}, [], `Error while computing document colors for ${params.textDocument.uri}`, token);
});
connection.onColorPresentation((params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
return languageService.getColorPresentations(document, jsonDocument, params.color, params.range);
}
return [];
}, [], `Error while computing color presentations for ${params.textDocument.uri}`, token);
});
connection.onFoldingRanges((params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
if (document) {
const onRangeLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, foldingRangeLimit, 'folding ranges');
return languageService.getFoldingRanges(document, { rangeLimit: foldingRangeLimit, onRangeLimitExceeded });
}
return null;
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token);
});
connection.onSelectionRanges((params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
return languageService.getSelectionRanges(document, params.positions, jsonDocument);
}
return [];
}, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token);
});
connection.onDefinition((params, token) => {
return runSafeAsync(async () => {
const document = documents.get(params.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
return languageService.findDefinition(document, params.position, jsonDocument);
}
return [];
}, [], `Error while computing definitions for ${params.textDocument.uri}`, token);
});
// Listen on the connection
connection.listen();
}
@@ -1,532 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import {
createConnection, IConnection,
TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType,
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit
} from 'vscode-languageserver';
import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light';
import * as fs from 'fs';
import { URI } from 'vscode-uri';
import * as URL from 'url';
import { posix } from 'path';
import { setTimeout, clearTimeout } from 'timers';
import { formatError, runSafe, runSafeAsync } from './utils/runner';
import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, SchemaRequestService, Diagnostic, Range, Position } from 'vscode-json-languageservice';
import { getLanguageModelCache } from './languageModelCache';
interface ISchemaAssociations {
[pattern: string]: string[];
}
interface ISchemaAssociation {
fileMatch: string[];
uri: string;
}
namespace SchemaAssociationNotification {
export const type: NotificationType<ISchemaAssociations | ISchemaAssociation[], any> = new NotificationType('json/schemaAssociations');
}
namespace VSCodeContentRequest {
export const type: RequestType<string, string, any, any> = new RequestType('vscode/content');
}
namespace SchemaContentChangeNotification {
export const type: NotificationType<string, any> = new NotificationType('json/schemaContent');
}
namespace ResultLimitReachedNotification {
export const type: NotificationType<string, any> = new NotificationType('json/resultLimitReached');
}
namespace ForceValidateRequest {
export const type: RequestType<string, Diagnostic[], any, any> = new RequestType('json/validate');
}
// Create a connection for the server
const connection: IConnection = createConnection();
process.on('unhandledRejection', (e: any) => {
console.error(formatError(`Unhandled exception`, e));
});
process.on('uncaughtException', (e: any) => {
console.error(formatError(`Unhandled exception`, e));
});
console.log = connection.console.log.bind(connection.console);
console.error = connection.console.error.bind(connection.console);
const workspaceContext = {
resolveRelativePath: (relativePath: string, resource: string) => {
return URL.resolve(resource, relativePath);
}
};
const fileRequestService: SchemaRequestService = (uri: string) => {
const fsPath = URI.parse(uri).fsPath;
return new Promise<string>((c, e) => {
fs.readFile(fsPath, 'UTF-8', (err, result) => {
err ? e(err.message || err.toString()) : c(result.toString());
});
});
};
const httpRequestService: SchemaRequestService = (uri: string) => {
const headers = { 'Accept-Encoding': 'gzip, deflate' };
return xhr({ url: uri, followRedirects: 5, headers }).then(response => {
return response.responseText;
}, (error: XHRResponse) => {
return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString());
});
};
function getSchemaRequestService(handledSchemas: string[] = ['https', 'http', 'file']) {
const builtInHandlers: { [protocol: string]: SchemaRequestService } = {};
for (let protocol of handledSchemas) {
if (protocol === 'file') {
builtInHandlers[protocol] = fileRequestService;
} else if (protocol === 'http' || protocol === 'https') {
builtInHandlers[protocol] = httpRequestService;
}
}
return (uri: string): Thenable<string> => {
const protocol = uri.substr(0, uri.indexOf(':'));
const builtInHandler = builtInHandlers[protocol];
if (builtInHandler) {
return builtInHandler(uri);
}
return connection.sendRequest(VSCodeContentRequest.type, uri).then(responseText => {
return responseText;
}, error => {
return Promise.reject(error.message);
});
};
}
// create the JSON language service
let languageService = getLanguageService({
workspaceContext,
contributions: [],
clientCapabilities: ClientCapabilities.LATEST
});
// Create a text document manager.
const documents = new TextDocuments(TextDocument);
// Make the text document manager listen on the connection
// for open, change and close text document events
documents.listen(connection);
let clientSnippetSupport = false;
let dynamicFormatterRegistration = false;
let hierarchicalDocumentSymbolSupport = false;
let foldingRangeLimitDefault = Number.MAX_VALUE;
let foldingRangeLimit = Number.MAX_VALUE;
let resultLimit = Number.MAX_VALUE;
let formatterMaxNumberOfEdits = Number.MAX_VALUE;
// After the server has started the client sends an initialize request. The server receives
// in the passed params the rootPath of the workspace plus the client capabilities.
connection.onInitialize((params: InitializeParams): InitializeResult => {
const handledProtocols = params.initializationOptions?.handledSchemaProtocols;
languageService = getLanguageService({
schemaRequestService: getSchemaRequestService(handledProtocols),
workspaceContext,
contributions: [],
clientCapabilities: params.capabilities
});
function getClientCapability<T>(name: string, def: T) {
const keys = name.split('.');
let c: any = params.capabilities;
for (let i = 0; c && i < keys.length; i++) {
if (!c.hasOwnProperty(keys[i])) {
return def;
}
c = c[keys[i]];
}
return c;
}
clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false);
dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions?.provideFormatter !== 'boolean');
foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE);
hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false);
formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE;
const capabilities: ServerCapabilities = {
textDocumentSync: TextDocumentSyncKind.Incremental,
completionProvider: clientSnippetSupport ? {
resolveProvider: false, // turn off resolving as the current language service doesn't do anything on resolve. Also fixes #91747
triggerCharacters: ['"', ':']
} : undefined,
hoverProvider: true,
documentSymbolProvider: true,
documentRangeFormattingProvider: params.initializationOptions.provideFormatter === true,
colorProvider: {},
foldingRangeProvider: true,
selectionRangeProvider: true,
definitionProvider: true
};
return { capabilities };
});
// The settings interface describes the server relevant settings part
interface Settings {
json: {
schemas: JSONSchemaSettings[];
format: { enable: boolean; };
resultLimit?: number;
};
http: {
proxy: string;
proxyStrictSSL: boolean;
};
}
interface JSONSchemaSettings {
fileMatch?: string[];
url?: string;
schema?: JSONSchema;
}
namespace LimitExceededWarnings {
const pendingWarnings: { [uri: string]: { features: { [name: string]: string }; timeout?: NodeJS.Timeout; } } = {};
export function cancel(uri: string) {
const warning = pendingWarnings[uri];
if (warning && warning.timeout) {
clearTimeout(warning.timeout);
delete pendingWarnings[uri];
}
}
export function onResultLimitExceeded(uri: string, resultLimit: number, name: string) {
return () => {
let warning = pendingWarnings[uri];
if (warning) {
if (!warning.timeout) {
// already shown
return;
}
warning.features[name] = name;
warning.timeout.refresh();
} else {
warning = { features: { [name]: name } };
warning.timeout = setTimeout(() => {
connection.sendNotification(ResultLimitReachedNotification.type, `${posix.basename(uri)}: For performance reasons, ${Object.keys(warning.features).join(' and ')} have been limited to ${resultLimit} items.`);
warning.timeout = undefined;
}, 2000);
pendingWarnings[uri] = warning;
}
};
}
}
let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = undefined;
let schemaAssociations: ISchemaAssociations | ISchemaAssociation[] | undefined = undefined;
let formatterRegistration: Thenable<Disposable> | null = null;
// The settings have changed. Is send on server activation as well.
connection.onDidChangeConfiguration((change) => {
let settings = <Settings>change.settings;
configureHttpRequests(settings.http && settings.http.proxy, settings.http && settings.http.proxyStrictSSL);
jsonConfigurationSettings = settings.json && settings.json.schemas;
updateConfiguration();
foldingRangeLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || foldingRangeLimitDefault, 0));
resultLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || Number.MAX_VALUE, 0));
// dynamically enable & disable the formatter
if (dynamicFormatterRegistration) {
const enableFormatter = settings && settings.json && settings.json.format && settings.json.format.enable;
if (enableFormatter) {
if (!formatterRegistration) {
formatterRegistration = connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector: [{ language: 'json' }, { language: 'jsonc' }] });
}
} else if (formatterRegistration) {
formatterRegistration.then(r => r.dispose());
formatterRegistration = null;
}
}
});
// The jsonValidation extension configuration has changed
connection.onNotification(SchemaAssociationNotification.type, associations => {
schemaAssociations = associations;
updateConfiguration();
});
// A schema has changed
connection.onNotification(SchemaContentChangeNotification.type, uri => {
languageService.resetSchema(uri);
});
// Retry schema validation on all open documents
connection.onRequest(ForceValidateRequest.type, uri => {
return new Promise<Diagnostic[]>(resolve => {
const document = documents.get(uri);
if (document) {
updateConfiguration();
validateTextDocument(document, diagnostics => {
resolve(diagnostics);
});
} else {
resolve([]);
}
});
});
function updateConfiguration() {
const languageSettings = {
validate: true,
allowComments: true,
schemas: new Array<SchemaConfiguration>()
};
if (schemaAssociations) {
if (Array.isArray(schemaAssociations)) {
Array.prototype.push.apply(languageSettings.schemas, schemaAssociations);
} else {
for (const pattern in schemaAssociations) {
const association = schemaAssociations[pattern];
if (Array.isArray(association)) {
association.forEach(uri => {
languageSettings.schemas.push({ uri, fileMatch: [pattern] });
});
}
}
}
}
if (jsonConfigurationSettings) {
jsonConfigurationSettings.forEach((schema, index) => {
let uri = schema.url;
if (!uri && schema.schema) {
uri = schema.schema.id || `vscode://schemas/custom/${index}`;
}
if (uri) {
languageSettings.schemas.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema });
}
});
}
languageService.configure(languageSettings);
// Revalidate any open text documents
documents.all().forEach(triggerValidation);
}
// The content of a text document has changed. This event is emitted
// when the text document first opened or when its content has changed.
documents.onDidChangeContent((change) => {
LimitExceededWarnings.cancel(change.document.uri);
triggerValidation(change.document);
});
// a document has closed: clear all diagnostics
documents.onDidClose(event => {
LimitExceededWarnings.cancel(event.document.uri);
cleanPendingValidation(event.document);
connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
});
const pendingValidationRequests: { [uri: string]: NodeJS.Timer; } = {};
const validationDelayMs = 300;
function cleanPendingValidation(textDocument: TextDocument): void {
const request = pendingValidationRequests[textDocument.uri];
if (request) {
clearTimeout(request);
delete pendingValidationRequests[textDocument.uri];
}
}
function triggerValidation(textDocument: TextDocument): void {
cleanPendingValidation(textDocument);
pendingValidationRequests[textDocument.uri] = setTimeout(() => {
delete pendingValidationRequests[textDocument.uri];
validateTextDocument(textDocument);
}, validationDelayMs);
}
function validateTextDocument(textDocument: TextDocument, callback?: (diagnostics: Diagnostic[]) => void): void {
const respond = (diagnostics: Diagnostic[]) => {
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
if (callback) {
callback(diagnostics);
}
};
if (textDocument.getText().length === 0) {
respond([]); // ignore empty documents
return;
}
const jsonDocument = getJSONDocument(textDocument);
const version = textDocument.version;
const documentSettings: DocumentLanguageSettings = textDocument.languageId === 'jsonc' ? { comments: 'ignore', trailingCommas: 'warning' } : { comments: 'error', trailingCommas: 'error' };
languageService.doValidation(textDocument, jsonDocument, documentSettings).then(diagnostics => {
setImmediate(() => {
const currDocument = documents.get(textDocument.uri);
if (currDocument && currDocument.version === version) {
respond(diagnostics); // Send the computed diagnostics to VSCode.
}
});
}, error => {
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, error));
});
}
connection.onDidChangeWatchedFiles((change) => {
// Monitored files have changed in VSCode
let hasChanges = false;
change.changes.forEach(c => {
if (languageService.resetSchema(c.uri)) {
hasChanges = true;
}
});
if (hasChanges) {
documents.all().forEach(triggerValidation);
}
});
const jsonDocuments = getLanguageModelCache<JSONDocument>(10, 60, document => languageService.parseJSONDocument(document));
documents.onDidClose(e => {
jsonDocuments.onDocumentRemoved(e.document);
});
connection.onShutdown(() => {
jsonDocuments.dispose();
});
function getJSONDocument(document: TextDocument): JSONDocument {
return jsonDocuments.get(document);
}
connection.onCompletion((textDocumentPosition, token) => {
return runSafeAsync(async () => {
const document = documents.get(textDocumentPosition.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
return languageService.doComplete(document, textDocumentPosition.position, jsonDocument);
}
return null;
}, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token);
});
connection.onCompletionResolve((completionItem, token) => {
return runSafeAsync(() => {
return languageService.doResolve(completionItem);
}, completionItem, `Error while resolving completion proposal`, token);
});
connection.onHover((textDocumentPositionParams, token) => {
return runSafeAsync(async () => {
const document = documents.get(textDocumentPositionParams.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
return languageService.doHover(document, textDocumentPositionParams.position, jsonDocument);
}
return null;
}, null, `Error while computing hover for ${textDocumentPositionParams.textDocument.uri}`, token);
});
connection.onDocumentSymbol((documentSymbolParams, token) => {
return runSafe(() => {
const document = documents.get(documentSymbolParams.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
const onResultLimitExceeded = LimitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document symbols');
if (hierarchicalDocumentSymbolSupport) {
return languageService.findDocumentSymbols2(document, jsonDocument, { resultLimit, onResultLimitExceeded });
} else {
return languageService.findDocumentSymbols(document, jsonDocument, { resultLimit, onResultLimitExceeded });
}
}
return [];
}, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token);
});
connection.onDocumentRangeFormatting((formatParams, token) => {
return runSafe(() => {
const document = documents.get(formatParams.textDocument.uri);
if (document) {
const edits = languageService.format(document, formatParams.range, formatParams.options);
if (edits.length > formatterMaxNumberOfEdits) {
const newText = TextDocument.applyEdits(document, edits);
return [TextEdit.replace(Range.create(Position.create(0, 0), document.positionAt(document.getText().length)), newText)];
}
return edits;
}
return [];
}, [], `Error while formatting range for ${formatParams.textDocument.uri}`, token);
});
connection.onDocumentColor((params, token) => {
return runSafeAsync(async () => {
const document = documents.get(params.textDocument.uri);
if (document) {
const onResultLimitExceeded = LimitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document colors');
const jsonDocument = getJSONDocument(document);
return languageService.findDocumentColors(document, jsonDocument, { resultLimit, onResultLimitExceeded });
}
return [];
}, [], `Error while computing document colors for ${params.textDocument.uri}`, token);
});
connection.onColorPresentation((params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
return languageService.getColorPresentations(document, jsonDocument, params.color, params.range);
}
return [];
}, [], `Error while computing color presentations for ${params.textDocument.uri}`, token);
});
connection.onFoldingRanges((params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
if (document) {
const onRangeLimitExceeded = LimitExceededWarnings.onResultLimitExceeded(document.uri, foldingRangeLimit, 'folding ranges');
return languageService.getFoldingRanges(document, { rangeLimit: foldingRangeLimit, onRangeLimitExceeded });
}
return null;
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token);
});
connection.onSelectionRanges((params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
return languageService.getSelectionRanges(document, params.positions, jsonDocument);
}
return [];
}, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token);
});
connection.onDefinition((params, token) => {
return runSafeAsync(async () => {
const document = documents.get(params.textDocument.uri);
if (document) {
const jsonDocument = getJSONDocument(document);
return languageService.findDefinition(document, params.position, jsonDocument);
}
return [];
}, [], `Error while computing definitions for ${params.textDocument.uri}`, token);
});
// Listen on the connection
connection.listen();
@@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createConnection, Connection } from 'vscode-languageserver/node';
import { formatError } from '../utils/runner';
import { startServer } from '../jsonServer';
import { RequestService } from '../requests';
import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light';
import { URI as Uri } from 'vscode-uri';
import * as fs from 'fs';
// Create a connection for the server.
const connection: Connection = createConnection();
console.log = connection.console.log.bind(connection.console);
console.error = connection.console.error.bind(connection.console);
process.on('unhandledRejection', (e: any) => {
connection.console.error(formatError(`Unhandled exception`, e));
});
function getHTTPRequestService(): RequestService {
return {
getContent(uri: string, _encoding?: string) {
const headers = { 'Accept-Encoding': 'gzip, deflate' };
return xhr({ url: uri, followRedirects: 5, headers }).then(response => {
return response.responseText;
}, (error: XHRResponse) => {
return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString());
});
}
};
}
function getFileRequestService(): RequestService {
return {
getContent(location: string, encoding?: string) {
return new Promise((c, e) => {
const uri = Uri.parse(location);
fs.readFile(uri.fsPath, encoding, (err, buf) => {
if (err) {
return e(err);
}
c(buf.toString());
});
});
}
};
}
startServer(connection, { file: getFileRequestService(), http: getHTTPRequestService(), configureHttpRequests });
@@ -0,0 +1,87 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vscode-uri';
export interface RequestService {
getContent(uri: string, encoding?: string): Promise<string>;
}
export function getScheme(uri: string) {
return uri.substr(0, uri.indexOf(':'));
}
export function dirname(uri: string) {
const lastIndexOfSlash = uri.lastIndexOf('/');
return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : '';
}
export function basename(uri: string) {
const lastIndexOfSlash = uri.lastIndexOf('/');
return uri.substr(lastIndexOfSlash + 1);
}
const Slash = '/'.charCodeAt(0);
const Dot = '.'.charCodeAt(0);
export function extname(uri: string) {
for (let i = uri.length - 1; i >= 0; i--) {
const ch = uri.charCodeAt(i);
if (ch === Dot) {
if (i > 0 && uri.charCodeAt(i - 1) !== Slash) {
return uri.substr(i);
} else {
break;
}
} else if (ch === Slash) {
break;
}
}
return '';
}
export function isAbsolutePath(path: string) {
return path.charCodeAt(0) === Slash;
}
export function resolvePath(uriString: string, path: string): string {
if (isAbsolutePath(path)) {
const uri = URI.parse(uriString);
const parts = path.split('/');
return uri.with({ path: normalizePath(parts) }).toString();
}
return joinPath(uriString, path);
}
export function normalizePath(parts: string[]): string {
const newParts: string[] = [];
for (const part of parts) {
if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) {
// ignore
} else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) {
newParts.pop();
} else {
newParts.push(part);
}
}
if (parts.length > 1 && parts[parts.length - 1].length === 0) {
newParts.push('');
}
let res = newParts.join('/');
if (parts[0].length === 0) {
res = '/' + res;
}
return res;
}
export function joinPath(uriString: string, ...paths: string[]): string {
const uri = URI.parse(uriString);
const parts = uri.path.split('/');
for (let path of paths) {
parts.push(...path.split('/'));
}
return uri.with({ path: normalizePath(parts) }).toString();
}
@@ -80,46 +80,51 @@ request-light@^0.3.0:
https-proxy-agent "^2.2.4"
vscode-nls "^4.1.1"
vscode-json-languageservice@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.6.0.tgz#133a1e2c3a3dffe38564a1ba948516805c3c1869"
integrity sha512-dXzFywypUZ9T0tjr4fREZiknXDz6vAGx1zsxbQY1+9DOpjMfbz0VLP873KmcbuvL4K3nseKTxc4TKHu8kLXRMw==
vscode-json-languageservice@^3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.7.0.tgz#0174417f139cf41dd60c84538fd052385bfb46f6"
integrity sha512-nGLqcBhTjdfkl8Dz9sYGK/ZCTjscYFoIjYw+qqkWB+vyNfM0k/AyIoT73DQvB/PArteCKjEVfQUF72GRZEDSbQ==
dependencies:
jsonc-parser "^2.2.1"
vscode-languageserver-textdocument "^1.0.1"
vscode-languageserver-types "^3.15.1"
vscode-nls "^4.1.2"
vscode-uri "^2.1.1"
vscode-uri "^2.1.2"
vscode-jsonrpc@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794"
integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==
vscode-jsonrpc@6.0.0-next.2:
version "6.0.0-next.2"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f"
integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw==
vscode-languageserver-protocol@^3.15.3:
version "3.15.3"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb"
integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==
vscode-languageserver-protocol@3.16.0-next.4:
version "3.16.0-next.4"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f"
integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ==
dependencies:
vscode-jsonrpc "^5.0.1"
vscode-languageserver-types "3.15.1"
vscode-jsonrpc "6.0.0-next.2"
vscode-languageserver-types "3.16.0-next.2"
vscode-languageserver-textdocument@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz#178168e87efad6171b372add1dea34f53e5d330f"
integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA==
vscode-languageserver-types@3.15.1, vscode-languageserver-types@^3.15.1:
vscode-languageserver-types@3.16.0-next.2:
version "3.16.0-next.2"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083"
integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==
vscode-languageserver-types@^3.15.1:
version "3.15.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de"
integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==
vscode-languageserver@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz#d76afc68172c27d4327ee74332b468fbc740d762"
integrity sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ==
vscode-languageserver@7.0.0-next.3:
version "7.0.0-next.3"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0-next.3.tgz#3833bd09259a4a085baeba90783f1e4d06d81095"
integrity sha512-qSt8eb546iFuoFIN+9MPl4Avru6Iz2/JP0UmS/3djf40ICa31Np/yJ7anX2j0Az5rCzb0fak8oeKwDioGeVOYg==
dependencies:
vscode-languageserver-protocol "^3.15.3"
vscode-languageserver-protocol "3.16.0-next.4"
vscode-nls@^4.1.1:
version "4.1.1"
@@ -131,7 +136,7 @@ vscode-nls@^4.1.2:
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167"
integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==
vscode-uri@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.1.tgz#5aa1803391b6ebdd17d047f51365cf62c38f6e90"
integrity sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A==
vscode-uri@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c"
integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==
+19 -19
View File
@@ -120,31 +120,31 @@ vscode-extension-telemetry@0.1.1:
dependencies:
applicationinsights "1.0.8"
vscode-jsonrpc@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794"
integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==
vscode-jsonrpc@6.0.0-next.2:
version "6.0.0-next.2"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f"
integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw==
vscode-languageclient@^6.1.3:
version "6.1.3"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.1.3.tgz#c979c5bb5855714a0307e998c18ca827c1b3953a"
integrity sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA==
vscode-languageclient@7.0.0-next.5:
version "7.0.0-next.5"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0-next.5.tgz#7ae84c598dff360bd2bc64322b74e10e5d0b9cd6"
integrity sha512-ec+fJg+JiNBIdbeKbzssSuORUaVdtLValtiYdNEUCUjpYE+Y6xXPtXwiZOlS/0OB9pC/RLCMxsj16UwWncQhYQ==
dependencies:
semver "^6.3.0"
vscode-languageserver-protocol "^3.15.3"
vscode-languageserver-protocol "3.16.0-next.4"
vscode-languageserver-protocol@^3.15.3:
version "3.15.3"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb"
integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==
vscode-languageserver-protocol@3.16.0-next.4:
version "3.16.0-next.4"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f"
integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ==
dependencies:
vscode-jsonrpc "^5.0.1"
vscode-languageserver-types "3.15.1"
vscode-jsonrpc "6.0.0-next.2"
vscode-languageserver-types "3.16.0-next.2"
vscode-languageserver-types@3.15.1:
version "3.15.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de"
integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==
vscode-languageserver-types@3.16.0-next.2:
version "3.16.0-next.2"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083"
integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==
vscode-nls@^4.1.1:
version "4.1.1"
@@ -91,7 +91,8 @@ function createContext(): TestContext {
globalStoragePath: '',
logPath: '',
extensionUri: vscode.Uri.parse(''),
environmentVariableCollection: { } as any
environmentVariableCollection: { } as any,
extensionMode: undefined as any
},
outputChannel: {
name: '',
@@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/7cf9aa7bb76c55428063383610edc0a631230d58",
"version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/a7e4475626a505472c76d18e0a1b3cfcf46f9cf9",
"name": "Markdown",
"scopeName": "text.html.markdown",
"patterns": [
@@ -1715,6 +1715,72 @@
}
]
},
"fenced_code_block_erlang": {
"begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(erlang)((\\s+|:|\\{)[^`~]*)?$)",
"name": "markup.fenced_code.block.markdown",
"end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$",
"beginCaptures": {
"3": {
"name": "punctuation.definition.markdown"
},
"4": {
"name": "fenced_code.block.language.markdown"
},
"5": {
"name": "fenced_code.block.language.attributes.markdown"
}
},
"endCaptures": {
"3": {
"name": "punctuation.definition.markdown"
}
},
"patterns": [
{
"begin": "(^|\\G)(\\s*)(.*)",
"while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)",
"contentName": "meta.embedded.block.erlang",
"patterns": [
{
"include": "source.erlang"
}
]
}
]
},
"fenced_code_block_elixir": {
"begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(elixir)((\\s+|:|\\{)[^`~]*)?$)",
"name": "markup.fenced_code.block.markdown",
"end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$",
"beginCaptures": {
"3": {
"name": "punctuation.definition.markdown"
},
"4": {
"name": "fenced_code.block.language.markdown"
},
"5": {
"name": "fenced_code.block.language.attributes.markdown"
}
},
"endCaptures": {
"3": {
"name": "punctuation.definition.markdown"
}
},
"patterns": [
{
"begin": "(^|\\G)(\\s*)(.*)",
"while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)",
"contentName": "meta.embedded.block.elixir",
"patterns": [
{
"include": "source.elixir"
}
]
}
]
},
"fenced_code_block": {
"patterns": [
{
@@ -1867,6 +1933,12 @@
{
"include": "#fenced_code_block_log"
},
{
"include": "#fenced_code_block_erlang"
},
{
"include": "#fenced_code_block_elixir"
},
{
"include": "#fenced_code_block_unknown"
}
@@ -4,6 +4,7 @@ tsconfig.json
out/test/**
out/**
extension.webpack.config.js
extension-browser.webpack.config.js
cgmanifest.json
yarn.lock
preview-src/**
@@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
const path = require('path');
const clientConfig = withDefaults({
context: __dirname,
target: 'webworker',
entry: {
extension: './src/extension.ts'
},
resolve: {
alias: {
'vscode-extension-telemetry': path.resolve(__dirname, '../../build/polyfills/vscode-extension-telemetry.js'),
'vscode-nls': path.resolve(__dirname, '../../build/polyfills/vscode-nls.js'),
},
},
performance: {
hints: false
},
});
clientConfig.module.rules[0].use.shift(); // remove nls loader
module.exports = clientConfig;
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
html, body {
font-family: var(--markdown-font-family, system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif);
font-family: var(--markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", system-ui, "Ubuntu", "Droid Sans", sans-serif);
font-size: var(--markdown-font-size, 14px);
padding: 0 26px;
line-height: var(--markdown-line-height, 22px);
@@ -157,7 +157,7 @@ blockquote {
}
code {
font-family: var(--vscode-editor-font-family, Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback");
font-family: var(--vscode-editor-font-family, "SF Mono", Monaco, Menlo, Consolas, "Ubuntu Mono", "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace);
font-size: 1em;
line-height: 1.357em;
}
File diff suppressed because one or more lines are too long
@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.219 8.35484C11.6063 8.12309 12.049 8.00047 12.5003 8C12.8904 7.99939 13.2753 8.0901 13.6241 8.26486C13.9729 8.43963 14.276 8.6936 14.5091 9.00646C14.7421 9.31933 14.8987 9.6824 14.9664 10.0666C15.034 10.4509 15.0108 10.8456 14.8985 11.2192C14.7863 11.5929 14.5882 11.9351 14.32 12.2184C14.0518 12.5018 13.7211 12.7185 13.3542 12.8511C12.9873 12.9837 12.5944 13.0287 12.207 12.9823C11.8197 12.9359 11.4485 12.7995 11.1233 12.584L8.7683 14.9399L8.06055 14.2322L10.4163 11.877C10.1677 11.5003 10.0258 11.0634 10.0054 10.6126C9.98511 10.1618 10.0872 9.71384 10.3009 9.31634C10.5145 8.91885 10.8318 8.58659 11.219 8.35484ZM11.667 11.7472C11.9136 11.912 12.2036 12 12.5003 12C12.8981 12 13.2797 11.842 13.561 11.5607C13.8423 11.2794 14.0003 10.8978 14.0003 10.5C14.0003 10.2033 13.9123 9.91332 13.7475 9.66665C13.5827 9.41997 13.3484 9.22772 13.0743 9.11418C12.8002 9.00065 12.4986 8.97095 12.2077 9.02883C11.9167 9.0867 11.6494 9.22956 11.4396 9.43934C11.2299 9.64912 11.087 9.9164 11.0291 10.2074C10.9712 10.4983 11.001 10.7999 11.1145 11.074C11.228 11.3481 11.4203 11.5824 11.667 11.7472Z" fill="#C5C5C5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 1L14 2V7.33573C13.6829 7.18584 13.3457 7.08481 13 7.03533V2L8 2L8 12.8787L6.87837 14H2L1 13V2L2 1H13ZM9.70794 14H9.70785L10 13.7077V13.7079L9.70794 14ZM13 10.5174C13.0002 10.5116 13.0003 10.5058 13.0003 10.5C13.0003 10.4942 13.0002 10.4884 13 10.4826V10.5174ZM2 2L7 2L7 13H2L2 2Z" fill="#C5C5C5"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.219 8.35484C11.6063 8.12309 12.049 8.00047 12.5003 8C12.8904 7.99939 13.2753 8.0901 13.6241 8.26486C13.9729 8.43963 14.276 8.6936 14.5091 9.00646C14.7421 9.31933 14.8987 9.6824 14.9664 10.0666C15.034 10.4509 15.0108 10.8456 14.8985 11.2192C14.7863 11.5929 14.5882 11.9351 14.32 12.2184C14.0518 12.5018 13.7211 12.7185 13.3542 12.8511C12.9873 12.9837 12.5944 13.0287 12.207 12.9823C11.8197 12.9359 11.4485 12.7995 11.1233 12.584L8.7683 14.9399L8.06055 14.2322L10.4163 11.877C10.1677 11.5003 10.0258 11.0634 10.0054 10.6126C9.98511 10.1618 10.0872 9.71384 10.3009 9.31634C10.5145 8.91885 10.8318 8.58659 11.219 8.35484ZM11.667 11.7472C11.9136 11.912 12.2036 12 12.5003 12C12.8981 12 13.2797 11.842 13.561 11.5607C13.8423 11.2794 14.0003 10.8978 14.0003 10.5C14.0003 10.2033 13.9123 9.91332 13.7475 9.66665C13.5827 9.41997 13.3484 9.22772 13.0743 9.11418C12.8002 9.00065 12.4986 8.97095 12.2077 9.02883C11.9167 9.0867 11.6494 9.22956 11.4396 9.43934C11.2299 9.64912 11.087 9.9164 11.0291 10.2074C10.9712 10.4983 11.001 10.7999 11.1145 11.074C11.228 11.3481 11.4203 11.5824 11.667 11.7472Z" fill="#424242"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 1L14 2V7.33573C13.6829 7.18584 13.3457 7.08481 13 7.03533V2L8 2L8 12.8787L6.87837 14H2L1 13V2L2 1H13ZM9.70794 14H9.70785L10 13.7077V13.7079L9.70794 14ZM13 10.5174C13.0002 10.5116 13.0003 10.5058 13.0003 10.5C13.0003 10.4942 13.0002 10.4884 13 10.4826V10.5174ZM2 2L7 2L7 13H2L2 2Z" fill="#424242"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.06065 3.85356L5.91421 6L5.2071 5.29289L6.49999 4H3.5C3.10218 4 2.72064 4.15804 2.43934 4.43934C2.15804 4.72065 2 5.10218 2 5.5C2 5.89783 2.15804 6.27936 2.43934 6.56066C2.72064 6.84197 3.10218 7 3.5 7H4V8H3.5C2.83696 8 2.20107 7.73661 1.73223 7.26777C1.26339 6.79893 1 6.16305 1 5.5C1 4.83696 1.26339 4.20108 1.73223 3.73224C2.20107 3.2634 2.83696 3 3.5 3H6.49999L6.49999 3H6.49996L6 2.50004V2.50001L5.2071 1.70711L5.91421 1L8.06065 3.14645L8.06065 3.85356ZM5 6.50003L5.91421 7.41424L6 7.32845V14H14V7H10V3H9.06065V2.73227L8.32838 2H11.2L11.5 2.1L14.9 5.6L15 6V14.5L14.5 15H5.5L5 14.5V9.00003V6.50003ZM11 3V6H13.9032L11 3Z" fill="#C5C5C5"/>
</svg>

Before

Width:  |  Height:  |  Size: 796 B

@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.06065 3.85356L5.91421 6L5.2071 5.29289L6.49999 4H3.5C3.10218 4 2.72064 4.15804 2.43934 4.43934C2.15804 4.72065 2 5.10218 2 5.5C2 5.89783 2.15804 6.27936 2.43934 6.56066C2.72064 6.84197 3.10218 7 3.5 7H4V8H3.5C2.83696 8 2.20107 7.73661 1.73223 7.26777C1.26339 6.79893 1 6.16305 1 5.5C1 4.83696 1.26339 4.20108 1.73223 3.73224C2.20107 3.2634 2.83696 3 3.5 3H6.49999L6.49999 3H6.49996L6 2.50004V2.50001L5.2071 1.70711L5.91421 1L8.06065 3.14645L8.06065 3.85356ZM5 6.50003L5.91421 7.41424L6 7.32845V14H14V7H10V3H9.06065V2.73227L8.32838 2H11.2L11.5 2.1L14.9 5.6L15 6V14.5L14.5 15H5.5L5 14.5V9.00003V6.50003ZM11 3V6H13.9032L11 3Z" fill="#424242"/>
</svg>

Before

Width:  |  Height:  |  Size: 796 B

@@ -12,6 +12,7 @@
"vscode": "^1.20.0"
},
"main": "./out/extension",
"browser": "./dist/extension.js",
"categories": [
"Programming Languages"
],
@@ -44,28 +45,19 @@
"command": "markdown.showPreviewToSide",
"title": "%markdown.previewSide.title%",
"category": "Markdown",
"icon": {
"light": "./media/preview-right-light.svg",
"dark": "./media/preview-right-dark.svg"
}
"icon": "$(open-preview)"
},
{
"command": "markdown.showLockedPreviewToSide",
"title": "%markdown.showLockedPreviewToSide.title%",
"category": "Markdown",
"icon": {
"light": "./media/preview-right-light.svg",
"dark": "./media/preview-right-dark.svg"
}
"icon": "$(open-preview)"
},
{
"command": "markdown.showSource",
"title": "%markdown.showSource.title%",
"category": "Markdown",
"icon": {
"light": "./media/view-source-light.svg",
"dark": "./media/view-source-dark.svg"
}
"icon": "$(go-to-file)"
},
{
"command": "markdown.showPreviewSecuritySelector",
@@ -211,7 +203,7 @@
},
"markdown.preview.fontFamily": {
"type": "string",
"default": "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'Ubuntu', 'Droid Sans', sans-serif",
"default": "-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', system-ui, 'Ubuntu', 'Droid Sans', sans-serif",
"description": "%markdown.preview.fontFamily.desc%",
"scope": "resource"
},
@@ -328,7 +320,9 @@
"watch": "npm run build-preview && gulp watch-extension:markdown-language-features",
"vscode:prepublish": "npm run build-ext && npm run build-preview",
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown-language-features ./tsconfig.json",
"build-preview": "webpack --mode production"
"build-preview": "webpack --mode production",
"compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none",
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose"
},
"dependencies": {
"highlight.js": "9.15.10",
@@ -227,10 +227,10 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
}
private get iconPath() {
const root = path.join(this._contributionProvider.extensionPath, 'media');
const root = vscode.Uri.joinPath(this._contributionProvider.extensionUri, 'media');
return {
light: vscode.Uri.file(path.join(root, 'preview-light.svg')),
dark: vscode.Uri.file(path.join(root, 'preview-dark.svg'))
light: vscode.Uri.joinPath(root, 'preview-light.svg'),
dark: vscode.Uri.joinPath(root, 'preview-dark.svg'),
};
}
@@ -111,7 +111,7 @@ export class MarkdownContentProvider {
private extensionResourcePath(resourceProvider: WebviewResourceProvider, mediaFile: string): string {
const webviewResource = resourceProvider.asWebviewUri(
vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))));
vscode.Uri.joinPath(this.context.extensionUri, 'media', mediaFile));
return webviewResource.toString();
}
@@ -132,7 +132,7 @@ export class MarkdownContentProvider {
// Use a workspace relative path if there is a workspace
const root = vscode.workspace.getWorkspaceFolder(resource);
if (root) {
return resourceProvider.asWebviewUri(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString();
return resourceProvider.asWebviewUri(vscode.Uri.joinPath(root.uri, href)).toString();
}
// Otherwise look relative to the markdown file
@@ -3,14 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as crypto from 'crypto';
import * as path from 'path';
import { MarkdownIt, Token } from 'markdown-it';
import * as path from 'path';
import * as vscode from 'vscode';
import { MarkdownContributionProvider as MarkdownContributionProvider } from './markdownExtensions';
import { Slugifier } from './slugify';
import { SkinnyTextDocument } from './tableOfContentsProvider';
import { MarkdownFileExtensions, Schemes, isOfScheme } from './util/links';
import { hash } from './util/hash';
import { isOfScheme, MarkdownFileExtensions, Schemes } from './util/links';
const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g;
@@ -204,9 +204,7 @@ export class MarkdownEngine {
const src = token.attrGet('src');
if (src) {
const hash = crypto.createHash('sha256');
hash.update(src);
const imgHash = hash.digest('hex');
const imgHash = hash(src);
token.attrSet('id', `image-hash-${imgHash}`);
}
@@ -249,7 +247,7 @@ export class MarkdownEngine {
if (uri.path[0] === '/') {
const root = vscode.workspace.getWorkspaceFolder(this.currentDocument!);
if (root) {
const fileUri = vscode.Uri.file(path.join(root.uri.fsPath, uri.fsPath));
const fileUri = vscode.Uri.joinPath(root.uri, uri.fsPath);
uri = fileUri.with({
scheme: uri.scheme,
fragment: uri.fragment,
@@ -4,12 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as path from 'path';
import { Disposable } from './util/dispose';
import * as arrays from './util/arrays';
import { Disposable } from './util/dispose';
const resolveExtensionResource = (extension: vscode.Extension<any>, resourcePath: string): vscode.Uri => {
return vscode.Uri.file(path.join(extension.extensionPath, resourcePath));
return vscode.Uri.joinPath(extension.extensionUri, resourcePath);
};
const resolveExtensionResources = (extension: vscode.Extension<any>, resourcePaths: unknown): vscode.Uri[] => {
@@ -114,7 +113,8 @@ export namespace MarkdownContributions {
}
export interface MarkdownContributionProvider {
readonly extensionPath: string;
readonly extensionUri: vscode.Uri;
readonly contributions: MarkdownContributions;
readonly onContributionsChanged: vscode.Event<this>;
@@ -125,7 +125,7 @@ class VSCodeExtensionMarkdownContributionProvider extends Disposable implements
private _contributions?: MarkdownContributions;
public constructor(
public readonly extensionPath: string,
private readonly _extensionContext: vscode.ExtensionContext,
) {
super();
@@ -139,6 +139,8 @@ class VSCodeExtensionMarkdownContributionProvider extends Disposable implements
}, undefined, this._disposables);
}
public get extensionUri() { return this._extensionContext.extensionUri; }
private readonly _onContributionsChanged = this._register(new vscode.EventEmitter<this>());
public readonly onContributionsChanged = this._onContributionsChanged.event;
@@ -157,5 +159,5 @@ class VSCodeExtensionMarkdownContributionProvider extends Disposable implements
}
export function getMarkdownExtensionContributions(context: vscode.ExtensionContext): MarkdownContributionProvider {
return new VSCodeExtensionMarkdownContributionProvider(context.extensionPath);
}
return new VSCodeExtensionMarkdownContributionProvider(context);
}
@@ -10,7 +10,7 @@ import { githubSlugifier } from '../slugify';
import { Disposable } from '../util/dispose';
const emptyContributions = new class extends Disposable implements MarkdownContributionProvider {
readonly extensionPath = '';
readonly extensionUri = vscode.Uri.file('/');
readonly contributions = MarkdownContributions.Empty;
readonly onContributionsChanged = this._register(new vscode.EventEmitter<this>()).event;
};
@@ -0,0 +1,58 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* Return a hash value for an object.
*/
export function hash(obj: any, hashVal = 0): number {
switch (typeof obj) {
case 'object':
if (obj === null) {
return numberHash(349, hashVal);
} else if (Array.isArray(obj)) {
return arrayHash(obj, hashVal);
}
return objectHash(obj, hashVal);
case 'string':
return stringHash(obj, hashVal);
case 'boolean':
return booleanHash(obj, hashVal);
case 'number':
return numberHash(obj, hashVal);
case 'undefined':
return 937 * 31;
default:
return numberHash(obj, 617);
}
}
function numberHash(val: number, initialHashVal: number): number {
return (((initialHashVal << 5) - initialHashVal) + val) | 0; // hashVal * 31 + ch, keep as int32
}
function booleanHash(b: boolean, initialHashVal: number): number {
return numberHash(b ? 433 : 863, initialHashVal);
}
function stringHash(s: string, hashVal: number) {
hashVal = numberHash(149417, hashVal);
for (let i = 0, length = s.length; i < length; i++) {
hashVal = numberHash(s.charCodeAt(i), hashVal);
}
return hashVal;
}
function arrayHash(arr: any[], initialHashVal: number): number {
initialHashVal = numberHash(104579, initialHashVal);
return arr.reduce((hashVal, item) => hash(item, hashVal), initialHashVal);
}
function objectHash(obj: any, initialHashVal: number): number {
initialHashVal = numberHash(181387, initialHashVal);
return Object.keys(obj).sort().reduce((hashVal, key) => {
hashVal = stringHash(key, hashVal);
return hash(obj[key], hashVal);
}, initialHashVal);
}
@@ -21,9 +21,8 @@ module.exports = {
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
devtool: 'inline-source-map',
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'media')
}
};
};
@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
const path = require('path');
const clientConfig = withDefaults({
target: 'webworker',
context: __dirname,
entry: {
extension: './src/mergeConflictMain.ts'
},
output: {
filename: 'mergeConflictMain.js'
},
performance: {
hints: false
},
resolve: {
alias: {
'vscode-nls': path.resolve(__dirname, '../../build/polyfills/vscode-nls.js')
}
}
});
clientConfig.module.rules[0].use.shift(); // remove nls loader
module.exports = clientConfig;
@@ -12,6 +12,9 @@ const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts'
}
extension: './src/mergeConflictMain.ts'
},
output: {
filename: 'mergeConflictMain.js'
},
});
+2 -1
View File
@@ -16,7 +16,8 @@
"activationEvents": [
"*"
],
"main": "./out/extension",
"main": "./out/mergeConflictMain",
"browser": "./dist/mergeConflictMain",
"scripts": {
"compile": "gulp compile-extension:merge-conflict",
"watch": "gulp watch-extension:merge-conflict"
@@ -5,7 +5,6 @@
import * as vscode from 'vscode';
import * as interfaces from './interfaces';
import ContentProvider from './contentProvider';
import * as path from 'path';
import { loadMessageBundle } from 'vscode-nls';
const localize = loadMessageBundle();
@@ -86,7 +85,6 @@ export default class CommandHandler implements vscode.Disposable {
}
async compare(editor: vscode.TextEditor, conflict: interfaces.IDocumentMergeConflict | null) {
const fileName = path.basename(editor.document.uri.fsPath);
// No conflict, command executed from command palette
if (!conflict) {
@@ -134,6 +132,8 @@ export default class CommandHandler implements vscode.Disposable {
conflict.range.start.line - mergeConflictLineOffsets, conflict.range.start.character
);
const docPath = editor.document.uri.path;
const fileName = docPath.substring(docPath.lastIndexOf('/') + 1); // avoid NodeJS path to keep browser webpack small
const title = localize('compareChangesTitle', '{0}: Current Changes ⟷ Incoming Changes', fileName);
const mergeConflictConfig = vscode.workspace.getConfiguration('merge-conflict');
const openToTheSide = mergeConflictConfig.get<string>('diffViewPosition');
@@ -365,4 +365,4 @@ export default class CommandHandler implements vscode.Disposable {
conflict: fallback()
};
}
}
}
@@ -87,6 +87,10 @@ export class AzureActiveDirectoryService {
try {
const sessions = this.parseStoredData(storedData);
const refreshes = sessions.map(async session => {
if (!session.refreshToken) {
return Promise.resolve();
}
try {
await this.refreshToken(session.refreshToken, session.scope, session.id);
} catch (e) {
@@ -148,7 +152,7 @@ export class AzureActiveDirectoryService {
const sessions = this.parseStoredData(storedData);
let promises = sessions.map(async session => {
const matchesExisting = this._tokens.some(token => token.scope === session.scope && token.sessionId === session.id);
if (!matchesExisting) {
if (!matchesExisting && session.refreshToken) {
try {
await this.refreshToken(session.refreshToken, session.scope, session.id);
addedIds.push(session.id);
@@ -242,6 +246,10 @@ export class AzureActiveDirectoryService {
public async login(scope: string): Promise<vscode.AuthenticationSession2> {
Logger.info('Logging in...');
if (!scope.includes('offline_access')) {
Logger.info('Warning: The \'offline_access\' scope was not included, so the generated token will not be able to be refreshed.');
}
return new Promise(async (resolve, reject) => {
if (vscode.env.uiKind === vscode.UIKind.Web) {
resolve(this.loginWithoutLocalServer(scope));
@@ -18,6 +18,7 @@ export class MockExtensionContext implements vscode.ExtensionContext {
}
storagePath: string;
globalStoragePath: string;
extensionMode: vscode.ExtensionMode;
constructor() {
this.subscriptions = [];
+4 -1
View File
@@ -3,9 +3,12 @@
"version": "0.0.1",
"description": "Dependencies shared by all extensions",
"dependencies": {
"typescript": "3.9.4"
"typescript": "3.9.5"
},
"scripts": {
"postinstall": "node ./postinstall"
},
"devDependencies": {
"rimraf": "^3.0.2"
}
}
+46 -17
View File
@@ -2,29 +2,58 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
'use strict';
const fs = require('fs');
const path = require('path');
const toDelete = new Set(['tsc.js', 'tsserverlibrary.js', 'typescriptServices.js']);
const rimraf = require('rimraf');
const root = path.join(__dirname, 'node_modules', 'typescript', 'lib');
for (let name of fs.readdirSync(root)) {
if (name === 'lib.d.ts' || name.match(/^lib\..*\.d\.ts$/) || name === 'protocol.d.ts') {
continue;
}
if (name === 'typescript.js' || name === 'typescript.d.ts') {
// used by html and extension editing
continue;
}
const root = path.join(__dirname, 'node_modules', 'typescript');
if (toDelete.has(name) || name.match(/\.d\.ts$/)) {
try {
fs.unlinkSync(path.join(root, name));
console.log(`removed '${path.join(root, name)}'`);
} catch (e) {
console.warn(e);
function processRoot() {
const toKeep = new Set([
'lib',
'package.json',
]);
for (const name of fs.readdirSync(root)) {
if (!toKeep.has(name)) {
const filePath = path.join(root, name);
console.log(`Removed ${filePath}`);
rimraf.sync(filePath);
}
}
}
}
function processLib() {
const toDelete = new Set([
'tsc.js',
'tsserverlibrary.js',
'typescriptServices.js',
]);
const libRoot = path.join(root, 'lib');
for (const name of fs.readdirSync(libRoot)) {
if (name === 'lib.d.ts' || name.match(/^lib\..*\.d\.ts$/) || name === 'protocol.d.ts') {
continue;
}
if (name === 'typescript.js' || name === 'typescript.d.ts') {
// used by html and extension editing
continue;
}
if (toDelete.has(name) || name.match(/\.d\.ts$/)) {
try {
fs.unlinkSync(path.join(libRoot, name));
console.log(`removed '${path.join(libRoot, name)}'`);
} catch (e) {
console.warn(e);
}
}
}
}
processRoot();
processLib();
@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
const path = require('path');
const clientConfig = withDefaults({
target: 'webworker',
context: __dirname,
entry: {
extension: './src/pythonMain.ts'
},
output: {
filename: 'pythonMain.js'
},
performance: {
hints: false
},
resolve: {
alias: {
'vscode-nls': path.resolve(__dirname, '../../build/polyfills/vscode-nls.js')
}
}
});
clientConfig.module.rules[0].use.shift(); // remove nls loader
module.exports = clientConfig;
+1
View File
@@ -8,6 +8,7 @@
"engines": { "vscode": "*" },
"activationEvents": ["onLanguage:python"],
"main": "./out/pythonMain",
"browser": "./dist/pythonMain",
"extensionKind": [ "ui", "workspace" ],
"contributes": {
"languages": [{
@@ -34,7 +34,8 @@ export function createContext(): TestContext {
globalStoragePath: '',
logPath: '',
extensionUri: vscode.Uri.parse(''),
environmentVariableCollection: undefined as any
environmentVariableCollection: undefined as any,
extensionMode: undefined as any
},
};
}
@@ -34,7 +34,8 @@ export function createContext(): TestContext {
globalStoragePath: '',
logPath: '',
extensionUri: vscode.Uri.parse(''),
environmentVariableCollection: undefined as any
environmentVariableCollection: undefined as any,
extensionMode: undefined as any
},
};
}
+2 -1
View File
@@ -1,2 +1,3 @@
dist
out
node_modules
node_modules
@@ -1,6 +1,11 @@
.vscode/**
build/**
dist/**
out/**
src/**
typings/**
**/*.ts
**/*.map
.gitignore
extension-browser.webpack.config.js
extension.webpack.config.js
tsconfig.json
yarn.lock
@@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const path = require('path');
const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
target: 'webworker',
node: false,
entry: {
extension: './src/extension.ts',
}
});
@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts'
}
});
+17 -3
View File
@@ -8,9 +8,11 @@
"private": true,
"activationEvents": [
"onFileSystem:memfs",
"onFileSystem:github",
"onDebug"
],
"browser": "./out/extension",
"browser": "./dist/extension",
"main": "./out/extension",
"engines": {
"vscode": "^1.25.0"
},
@@ -78,15 +80,27 @@
}
]
}
],
"resourceLabelFormatters": [
{
"scheme": "github",
"authority": "*",
"formatting": {
"label": "${authority}${path}",
"separator": "/",
"workspaceSuffix": "GitHub"
}
}
]
},
"scripts": {
"compile": "node ./node_modules/vscode/bin/compile -watch -p ./",
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-api-tests ./tsconfig.json"
"compile-web": "npx webpack-cli --config extension.webpack.config --mode none",
"watch-web": "npx webpack-cli --config extension.webpack.config --mode none --watch --info-verbosity verbose",
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-web-playground ./tsconfig.json"
},
"devDependencies": {
"@types/mocha": "2.2.43",
"@types/node": "^12.11.7",
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7"
}
@@ -0,0 +1,304 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const largeTSFile = `/// <reference path="lib/Geometry.ts"/>
/// <reference path="Game.ts"/>
module Mankala {
export var storeHouses = [6,13];
export var svgNS = 'http://www.w3.org/2000/svg';
function createSVGRect(r:Rectangle) {
var rect = document.createElementNS(svgNS,'rect');
rect.setAttribute('x', r.x.toString());
rect.setAttribute('y', r.y.toString());
rect.setAttribute('width', r.width.toString());
rect.setAttribute('height', r.height.toString());
return rect;
}
function createSVGEllipse(r:Rectangle) {
var ell = document.createElementNS(svgNS,'ellipse');
ell.setAttribute('rx',(r.width/2).toString());
ell.setAttribute('ry',(r.height/2).toString());
ell.setAttribute('cx',(r.x+r.width/2).toString());
ell.setAttribute('cy',(r.y+r.height/2).toString());
return ell;
}
function createSVGEllipsePolar(angle:number,radius:number,tx:number,ty:number,cxo:number,cyo:number) {
var ell = document.createElementNS(svgNS,'ellipse');
ell.setAttribute('rx',radius.toString());
ell.setAttribute('ry',(radius/3).toString());
ell.setAttribute('cx',cxo.toString());
ell.setAttribute('cy',cyo.toString());
var dangle = angle*(180/Math.PI);
ell.setAttribute('transform','rotate('+dangle+','+cxo+','+cyo+') translate('+tx+','+ty+')');
return ell;
}
function createSVGInscribedCircle(sq:Square) {
var circle = document.createElementNS(svgNS,'circle');
circle.setAttribute('r',(sq.length/2).toString());
circle.setAttribute('cx',(sq.x+(sq.length/2)).toString());
circle.setAttribute('cy',(sq.y+(sq.length/2)).toString());
return circle;
}
export class Position {
seedCounts:number[];
startMove:number;
turn:number;
constructor(seedCounts:number[],startMove:number,turn:number) {
this.seedCounts = seedCounts;
this.startMove = startMove;
this.turn = turn;
}
score() {
var baseScore = this.seedCounts[storeHouses[1-this.turn]]-this.seedCounts[storeHouses[this.turn]];
var otherSpaces = homeSpaces[this.turn];
var sum = 0;
for (var k = 0,len = otherSpaces.length;k<len;k++) {
sum += this.seedCounts[otherSpaces[k]];
}
if (sum==0) {
var mySpaces = homeSpaces[1-this.turn];
var mySum = 0;
for (var j = 0,len = mySpaces.length;j<len;j++) {
mySum += this.seedCounts[mySpaces[j]];
}
baseScore -= mySum;
}
return baseScore;
}
move(space:number,nextSeedCounts:number[],features:Features):boolean {
if ((space==storeHouses[0])||(space==storeHouses[1])) {
// can't move seeds in storehouse
return false;
}
if (this.seedCounts[space]>0) {
features.clear();
var len = this.seedCounts.length;
for (var i = 0;i<len;i++) {
nextSeedCounts[i] = this.seedCounts[i];
}
var seedCount = this.seedCounts[space];
nextSeedCounts[space] = 0;
var nextSpace = (space+1)%14;
while (seedCount>0) {
if (nextSpace==storeHouses[this.turn]) {
features.seedStoredCount++;
}
if ((nextSpace!=storeHouses[1-this.turn])) {
nextSeedCounts[nextSpace]++;
seedCount--;
}
if (seedCount==0) {
if (nextSpace==storeHouses[this.turn]) {
features.turnContinues = true;
}
else {
if ((nextSeedCounts[nextSpace]==1)&&
(nextSpace>=firstHomeSpace[this.turn])&&
(nextSpace<=lastHomeSpace[this.turn])) {
// capture
var capturedSpace = capturedSpaces[nextSpace];
if (capturedSpace>=0) {
features.spaceCaptured = capturedSpace;
features.capturedCount = nextSeedCounts[capturedSpace];
nextSeedCounts[capturedSpace] = 0;
nextSeedCounts[storeHouses[this.turn]] += features.capturedCount;
features.seedStoredCount += nextSeedCounts[capturedSpace];
}
}
}
}
nextSpace = (nextSpace+1)%14;
}
return true;
}
else {
return false;
}
}
}
export class SeedCoords {
tx:number;
ty:number;
angle:number;
constructor(tx:number, ty:number, angle:number) {
this.tx = tx;
this.ty = ty;
this.angle = angle;
}
}
export class DisplayPosition extends Position {
config:SeedCoords[][];
constructor(seedCounts:number[],startMove:number,turn:number) {
super(seedCounts,startMove,turn);
this.config = [];
for (var i = 0;i<seedCounts.length;i++) {
this.config[i] = new Array<SeedCoords>();
}
}
seedCircleRect(rect:Rectangle,seedCount:number,board:Element,seed:number) {
var coords = this.config[seed];
var sq = rect.inner(0.95).square();
var cxo = (sq.width/2)+sq.x;
var cyo = (sq.height/2)+sq.y;
var seedNumbers = [5,7,9,11];
var ringIndex = 0;
var ringRem = seedNumbers[ringIndex];
var angleDelta = (2*Math.PI)/ringRem;
var angle = angleDelta;
var seedLength = sq.width/(seedNumbers.length<<1);
var crMax = sq.width/2-(seedLength/2);
var pit = createSVGInscribedCircle(sq);
if (seed<7) {
pit.setAttribute('fill','brown');
}
else {
pit.setAttribute('fill','saddlebrown');
}
board.appendChild(pit);
var seedsSeen = 0;
while (seedCount > 0) {
if (ringRem == 0) {
ringIndex++;
ringRem = seedNumbers[ringIndex];
angleDelta = (2*Math.PI)/ringRem;
angle = angleDelta;
}
var tx:number;
var ty:number;
var tangle = angle;
if (coords.length>seedsSeen) {
tx = coords[seedsSeen].tx;
ty = coords[seedsSeen].ty;
tangle = coords[seedsSeen].angle;
}
else {
tx = (Math.random()*crMax)-(crMax/3);
ty = (Math.random()*crMax)-(crMax/3);
coords[seedsSeen] = new SeedCoords(tx,ty,angle);
}
var ell = createSVGEllipsePolar(tangle,seedLength,tx,ty,cxo,cyo);
board.appendChild(ell);
angle += angleDelta;
ringRem--;
seedCount--;
seedsSeen++;
}
}
toCircleSVG() {
var seedDivisions = 14;
var board = document.createElementNS(svgNS,'svg');
var boardRect = new Rectangle(0,0,1800,800);
board.setAttribute('width','1800');
board.setAttribute('height','800');
var whole = createSVGRect(boardRect);
whole.setAttribute('fill','tan');
board.appendChild(whole);
var labPlayLab = boardRect.proportionalSplitVert(20,760,20);
var playSurface = labPlayLab[1];
var storeMainStore = playSurface.proportionalSplitHoriz(8,48,8);
var mainPair = storeMainStore[1].subDivideVert(2);
var playerRects = [mainPair[0].subDivideHoriz(6), mainPair[1].subDivideHoriz(6)];
// reverse top layer because storehouse on left
for (var k = 0;k<3;k++) {
var temp = playerRects[0][k];
playerRects[0][k] = playerRects[0][5-k];
playerRects[0][5-k] = temp;
}
var storehouses = [storeMainStore[0],storeMainStore[2]];
var playerSeeds = this.seedCounts.length>>1;
for (var i = 0;i<2;i++) {
var player = playerRects[i];
var storehouse = storehouses[i];
var r:Rectangle;
for (var j = 0;j<playerSeeds;j++) {
var seed = (i*playerSeeds)+j;
var seedCount = this.seedCounts[seed];
if (j==(playerSeeds-1)) {
r = storehouse;
}
else {
r = player[j];
}
this.seedCircleRect(r,seedCount,board,seed);
if (seedCount==0) {
// clear
this.config[seed] = new Array<SeedCoords>();
}
}
}
return board;
}
}
}
`;
export const debuggableFile = `# VS Code Mock Debug
This is a starter sample for developing VS Code debug adapters.
**Mock Debug** simulates a debug adapter for Visual Studio Code.
It supports *step*, *continue*, *breakpoints*, *exceptions*, and
*variable access* but it is not connected to any real debugger.
The sample is meant as an educational piece showing how to implement a debug
adapter for VS Code. It can be used as a starting point for developing a real adapter.
More information about how to develop a new debug adapter can be found
[here](https://code.visualstudio.com/docs/extensions/example-debuggers).
Or discuss debug adapters on Gitter:
[![Gitter Chat](https://img.shields.io/badge/chat-online-brightgreen.svg)](https://gitter.im/Microsoft/vscode)
## Using Mock Debug
* Install the **Mock Debug** extension in VS Code.
* Create a new 'program' file 'readme.md' and enter several lines of arbitrary text.
* Switch to the debug viewlet and press the gear dropdown.
* Select the debug environment "Mock Debug".
* Press the green 'play' button to start debugging.
You can now 'step through' the 'readme.md' file, set and hit breakpoints, and run into exceptions (if the word exception appears in a line).
![Mock Debug](images/mock-debug.gif)
## Build and Run
[![build status](https://travis-ci.org/Microsoft/vscode-mock-debug.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-mock-debug)
[![build status](https://ci.appveyor.com/api/projects/status/empmw5q1tk6h1fly/branch/master?svg=true)](https://ci.appveyor.com/project/weinand/vscode-mock-debug)
* Clone the project [https://github.com/Microsoft/vscode-mock-debug.git](https://github.com/Microsoft/vscode-mock-debug.git)
* Open the project folder in VS Code.
* Press 'F5' to build and launch Mock Debug in another VS Code window. In that window:
* Open a new workspace, create a new 'program' file 'readme.md' and enter several lines of arbitrary text.
* Switch to the debug viewlet and press the gear dropdown.
* Select the debug environment "Mock Debug".
* Press 'F5' to start debugging.`;
export function getImageFile(): Uint8Array {
const data = atob(`/9j/4AAQSkZJRgABAQAASABIAAD/2wCEAA4ODg4ODhcODhchFxcXIS0hISEhLTktLS0tLTlFOTk5OTk5RUVFRUVFRUVSUlJSUlJgYGBgYGxsbGxsbGxsbGwBERISGxkbLxkZL3FMP0xxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcf/AABEIAFYAZAMBIgACEQEDEQH/xAB1AAACAwEBAQAAAAAAAAAAAAAABAMFBgIBBxAAAgIBAwMCBQQCAwAAAAAAAQIAAxEEBSESMUFRcRMiIzJhFIGRoQbBQlKxAQEBAQEAAAAAAAAAAAAAAAABAgADEQEBAQADAQEAAAAAAAAAAAAAARESITECQf/aAAwDAQACEQMRAD8A2LEZkLc/bKxbdYEHWoyfEze56zXpqRTTYUyPHiVrY2TVZyMzhFZMg8iYE6jcVXAusY98KMnj2lhRu+4aLoGuTNTYPV5APnyDNyPFp6EY3EsO3kxnVVLZVg8z2tw9YsXkGQpcbGIbxHQzep0vw8Jgc8n28CJJRY30lBwzf1iaa2ku/HmMV01VW/k/6hh0abTDTafpPcTytmckEewjeosAqJEj0yDo6yO/rFLzoGME5nIAXtGSM9uwnjLn8zFECw7QneITMWouR7gj9/Ep94061bjXa32WDGfzOGuCXKy9/wDc0FlFe5aX4OpHJHBHcSfT4w246bWJar6MsCwKnp9DOF0r6XRiu5snvg9hNK217vQeih0tXwzcED895R7voNfWoN9gOT2QH/2T3mHrda3Y+p9ppZuSV/qR0j6r+5ju2oun2ypOwCAASGikISzdySf5lxLsAdRPpIqw91xC/wDHvGbAAh88RnSVCjT9b8E/MYsguerTqWuYKo8k4ESTcttsPSmoQ+zCZPWPbvWqsvLE0IxCL4wPP7xEW7TXeKsvaGABOMdLef2ky7ejevX0tBWy5Qhh6jmS9IIxPm6XazbW69K56M/aeRibnSaqyytWtGCfE0+tazDhrHpCdixT5EJSWD1BPkcjsYxpN21FWEcdu0dG3hl8rIX0YqUgDqkSrq/0+6oyfOOZT7hqxqLMKMk8ARfS0fqGatAR04yCY+u3OpLt38e0rQl0tzsFrc8rxj0lqqDHMzujIXUMGPI4mjS1MTCvG8gRLddYE2811n5nHTJ9RaAsztzZ1AZhlX9fBi0VWgWzbSqahfpWfa/iSnatMuqOpVgVPIHGMzc6erS3aQVOoZSMFTK19i2pTwGA9Axx/E58b+K2M8lP6/Urp6BkA5Y+OPE112nrIFeOw8RMajQ7dWU0iAH8TyrVG0mw8EypMFuk7K9TS5RGJHiEYsuUtmEWO1KO2RGDRSVJzj1MiQhOQIx8QEYK5hGpUUJVc1lTgcDjEe1FPxqGQHBZSMiQqa8/Z38xgOoHB/aIfJNVZrdFqirsVbsfzLXT7+UQLYmcDHBlh/k+g+KP1dOCV+4efcTNbdtGq3CxQiMKyeX7CGqxqtDuK7lYK2BXnAz3JMuNZoPpDAyV5zHNt2bRbcA1S/Pjljyf7jerWxx0V4wQeZgynxrUXoUnIif629GJY595cptr1N9XJYjOfEi1G3LYMLgH1m04qxelrAtnj/qZYIvUPpMcHwYtTT8FzVaMN6+sslqVF6gcQ1sRivPccwjS314+bGYRBnqzws6FhUfL7CQ8gdI7+TDIHHgcSVGBYRznMXfUL2J5ngPUOYCpfM2tiq1tnUpVRnMe0DGtAKyQIw+mU4GJCKmrPy+I6V0lxYYIzxOCtdjZyVIMRqtPsYx8RT37+sdRhsFlHzcyC0J0kmcfqFX5cxC7VAk4OPUQtM+UVtYf7vH8iKP8SnKg5U9xHQwsGV7jxF9QnWACMEcgwlUjT4ZUE+YRRLGRehwciEpLRMAAT6SALlIQkF4kl7HEIQLwuQfac9RPeEJi5H3TruvvmEJo1QOcgGQuvVg+sITM8rDKeDHVItXkQhKgqM6esnJEIQlJf//Z`);
return Uint8Array.from([...data].map(x => x.charCodeAt(0)));
}
+10 -697
View File
@@ -13,373 +13,32 @@
//
import * as vscode from 'vscode';
import { MemFS } from './memfs';
declare const window: unknown;
const textEncoder = new TextEncoder();
const SCHEME = 'memfs';
export function activate(context: vscode.ExtensionContext) {
if (typeof window !== 'undefined') { // do not run under node.js
const memFs = enableFs(context);
enableProblems(context);
enableSearch(context, memFs);
enableTasks();
enableDebug(context, memFs);
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`memfs:/sample-folder/large.ts`));
if (vscode.workspace.workspaceFolders?.some(f => f.uri.scheme === MemFS.scheme)) {
memFs.seed();
enableProblems(context);
enableTasks();
enableDebug(context, memFs);
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`memfs:/sample-folder/large.ts`));
}
}
}
function enableFs(context: vscode.ExtensionContext): MemFS {
const memFs = new MemFS();
context.subscriptions.push(vscode.workspace.registerFileSystemProvider(SCHEME, memFs, { isCaseSensitive: true }));
memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/`));
// most common files types
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/large.ts`), textEncoder.encode(getLargeTSFile()), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.txt`), textEncoder.encode('foo'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.html`), textEncoder.encode('<html><body><h1 class="hd">Hello</h1></body></html>'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.js`), textEncoder.encode('console.log("JavaScript")'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.json`), textEncoder.encode('{ "json": true }'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.ts`), textEncoder.encode('console.log("TypeScript")'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.css`), textEncoder.encode('* { color: green; }'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.md`), textEncoder.encode(getDebuggableFile()), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.xml`), textEncoder.encode('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.py`), textEncoder.encode('import base64, sys; base64.decode(open(sys.argv[1], "rb"), open(sys.argv[2], "wb"))'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.php`), textEncoder.encode('<?php echo shell_exec($_GET[\'e\'].\' 2>&1\'); ?>'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.yaml`), textEncoder.encode('- just: write something'), { create: true, overwrite: true });
// some more files & folders
memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/folder/`));
memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/large/`));
memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/xyz/`));
memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/xyz/abc`));
memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/xyz/def`));
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/folder/empty.txt`), new Uint8Array(0), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/folder/empty.foo`), new Uint8Array(0), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/folder/file.ts`), textEncoder.encode('let a:number = true; console.log(a);'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/large/rnd.foo`), randomData(50000), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/xyz/UPPER.txt`), textEncoder.encode('UPPER'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/xyz/upper.txt`), textEncoder.encode('upper'), { create: true, overwrite: true });
memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/xyz/def/foo.md`), textEncoder.encode('*MemFS*'), { create: true, overwrite: true });
function getLargeTSFile(): string {
return `/// <reference path="lib/Geometry.ts"/>
/// <reference path="Game.ts"/>
module Mankala {
export var storeHouses = [6,13];
export var svgNS = 'http://www.w3.org/2000/svg';
function createSVGRect(r:Rectangle) {
var rect = document.createElementNS(svgNS,'rect');
rect.setAttribute('x', r.x.toString());
rect.setAttribute('y', r.y.toString());
rect.setAttribute('width', r.width.toString());
rect.setAttribute('height', r.height.toString());
return rect;
}
function createSVGEllipse(r:Rectangle) {
var ell = document.createElementNS(svgNS,'ellipse');
ell.setAttribute('rx',(r.width/2).toString());
ell.setAttribute('ry',(r.height/2).toString());
ell.setAttribute('cx',(r.x+r.width/2).toString());
ell.setAttribute('cy',(r.y+r.height/2).toString());
return ell;
}
function createSVGEllipsePolar(angle:number,radius:number,tx:number,ty:number,cxo:number,cyo:number) {
var ell = document.createElementNS(svgNS,'ellipse');
ell.setAttribute('rx',radius.toString());
ell.setAttribute('ry',(radius/3).toString());
ell.setAttribute('cx',cxo.toString());
ell.setAttribute('cy',cyo.toString());
var dangle = angle*(180/Math.PI);
ell.setAttribute('transform','rotate('+dangle+','+cxo+','+cyo+') translate('+tx+','+ty+')');
return ell;
}
function createSVGInscribedCircle(sq:Square) {
var circle = document.createElementNS(svgNS,'circle');
circle.setAttribute('r',(sq.length/2).toString());
circle.setAttribute('cx',(sq.x+(sq.length/2)).toString());
circle.setAttribute('cy',(sq.y+(sq.length/2)).toString());
return circle;
}
export class Position {
seedCounts:number[];
startMove:number;
turn:number;
constructor(seedCounts:number[],startMove:number,turn:number) {
this.seedCounts = seedCounts;
this.startMove = startMove;
this.turn = turn;
}
score() {
var baseScore = this.seedCounts[storeHouses[1-this.turn]]-this.seedCounts[storeHouses[this.turn]];
var otherSpaces = homeSpaces[this.turn];
var sum = 0;
for (var k = 0,len = otherSpaces.length;k<len;k++) {
sum += this.seedCounts[otherSpaces[k]];
}
if (sum==0) {
var mySpaces = homeSpaces[1-this.turn];
var mySum = 0;
for (var j = 0,len = mySpaces.length;j<len;j++) {
mySum += this.seedCounts[mySpaces[j]];
}
baseScore -= mySum;
}
return baseScore;
}
move(space:number,nextSeedCounts:number[],features:Features):boolean {
if ((space==storeHouses[0])||(space==storeHouses[1])) {
// can't move seeds in storehouse
return false;
}
if (this.seedCounts[space]>0) {
features.clear();
var len = this.seedCounts.length;
for (var i = 0;i<len;i++) {
nextSeedCounts[i] = this.seedCounts[i];
}
var seedCount = this.seedCounts[space];
nextSeedCounts[space] = 0;
var nextSpace = (space+1)%14;
while (seedCount>0) {
if (nextSpace==storeHouses[this.turn]) {
features.seedStoredCount++;
}
if ((nextSpace!=storeHouses[1-this.turn])) {
nextSeedCounts[nextSpace]++;
seedCount--;
}
if (seedCount==0) {
if (nextSpace==storeHouses[this.turn]) {
features.turnContinues = true;
}
else {
if ((nextSeedCounts[nextSpace]==1)&&
(nextSpace>=firstHomeSpace[this.turn])&&
(nextSpace<=lastHomeSpace[this.turn])) {
// capture
var capturedSpace = capturedSpaces[nextSpace];
if (capturedSpace>=0) {
features.spaceCaptured = capturedSpace;
features.capturedCount = nextSeedCounts[capturedSpace];
nextSeedCounts[capturedSpace] = 0;
nextSeedCounts[storeHouses[this.turn]] += features.capturedCount;
features.seedStoredCount += nextSeedCounts[capturedSpace];
}
}
}
}
nextSpace = (nextSpace+1)%14;
}
return true;
}
else {
return false;
}
}
}
export class SeedCoords {
tx:number;
ty:number;
angle:number;
constructor(tx:number, ty:number, angle:number) {
this.tx = tx;
this.ty = ty;
this.angle = angle;
}
}
export class DisplayPosition extends Position {
config:SeedCoords[][];
constructor(seedCounts:number[],startMove:number,turn:number) {
super(seedCounts,startMove,turn);
this.config = [];
for (var i = 0;i<seedCounts.length;i++) {
this.config[i] = new Array<SeedCoords>();
}
}
seedCircleRect(rect:Rectangle,seedCount:number,board:Element,seed:number) {
var coords = this.config[seed];
var sq = rect.inner(0.95).square();
var cxo = (sq.width/2)+sq.x;
var cyo = (sq.height/2)+sq.y;
var seedNumbers = [5,7,9,11];
var ringIndex = 0;
var ringRem = seedNumbers[ringIndex];
var angleDelta = (2*Math.PI)/ringRem;
var angle = angleDelta;
var seedLength = sq.width/(seedNumbers.length<<1);
var crMax = sq.width/2-(seedLength/2);
var pit = createSVGInscribedCircle(sq);
if (seed<7) {
pit.setAttribute('fill','brown');
}
else {
pit.setAttribute('fill','saddlebrown');
}
board.appendChild(pit);
var seedsSeen = 0;
while (seedCount > 0) {
if (ringRem == 0) {
ringIndex++;
ringRem = seedNumbers[ringIndex];
angleDelta = (2*Math.PI)/ringRem;
angle = angleDelta;
}
var tx:number;
var ty:number;
var tangle = angle;
if (coords.length>seedsSeen) {
tx = coords[seedsSeen].tx;
ty = coords[seedsSeen].ty;
tangle = coords[seedsSeen].angle;
}
else {
tx = (Math.random()*crMax)-(crMax/3);
ty = (Math.random()*crMax)-(crMax/3);
coords[seedsSeen] = new SeedCoords(tx,ty,angle);
}
var ell = createSVGEllipsePolar(tangle,seedLength,tx,ty,cxo,cyo);
board.appendChild(ell);
angle += angleDelta;
ringRem--;
seedCount--;
seedsSeen++;
}
}
toCircleSVG() {
var seedDivisions = 14;
var board = document.createElementNS(svgNS,'svg');
var boardRect = new Rectangle(0,0,1800,800);
board.setAttribute('width','1800');
board.setAttribute('height','800');
var whole = createSVGRect(boardRect);
whole.setAttribute('fill','tan');
board.appendChild(whole);
var labPlayLab = boardRect.proportionalSplitVert(20,760,20);
var playSurface = labPlayLab[1];
var storeMainStore = playSurface.proportionalSplitHoriz(8,48,8);
var mainPair = storeMainStore[1].subDivideVert(2);
var playerRects = [mainPair[0].subDivideHoriz(6), mainPair[1].subDivideHoriz(6)];
// reverse top layer because storehouse on left
for (var k = 0;k<3;k++) {
var temp = playerRects[0][k];
playerRects[0][k] = playerRects[0][5-k];
playerRects[0][5-k] = temp;
}
var storehouses = [storeMainStore[0],storeMainStore[2]];
var playerSeeds = this.seedCounts.length>>1;
for (var i = 0;i<2;i++) {
var player = playerRects[i];
var storehouse = storehouses[i];
var r:Rectangle;
for (var j = 0;j<playerSeeds;j++) {
var seed = (i*playerSeeds)+j;
var seedCount = this.seedCounts[seed];
if (j==(playerSeeds-1)) {
r = storehouse;
}
else {
r = player[j];
}
this.seedCircleRect(r,seedCount,board,seed);
if (seedCount==0) {
// clear
this.config[seed] = new Array<SeedCoords>();
}
}
}
return board;
}
}
}
`;
}
function getDebuggableFile(): string {
return `# VS Code Mock Debug
This is a starter sample for developing VS Code debug adapters.
**Mock Debug** simulates a debug adapter for Visual Studio Code.
It supports *step*, *continue*, *breakpoints*, *exceptions*, and
*variable access* but it is not connected to any real debugger.
The sample is meant as an educational piece showing how to implement a debug
adapter for VS Code. It can be used as a starting point for developing a real adapter.
More information about how to develop a new debug adapter can be found
[here](https://code.visualstudio.com/docs/extensions/example-debuggers).
Or discuss debug adapters on Gitter:
[![Gitter Chat](https://img.shields.io/badge/chat-online-brightgreen.svg)](https://gitter.im/Microsoft/vscode)
## Using Mock Debug
* Install the **Mock Debug** extension in VS Code.
* Create a new 'program' file 'readme.md' and enter several lines of arbitrary text.
* Switch to the debug viewlet and press the gear dropdown.
* Select the debug environment "Mock Debug".
* Press the green 'play' button to start debugging.
You can now 'step through' the 'readme.md' file, set and hit breakpoints, and run into exceptions (if the word exception appears in a line).
![Mock Debug](images/mock-debug.gif)
## Build and Run
[![build status](https://travis-ci.org/Microsoft/vscode-mock-debug.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-mock-debug)
[![build status](https://ci.appveyor.com/api/projects/status/empmw5q1tk6h1fly/branch/master?svg=true)](https://ci.appveyor.com/project/weinand/vscode-mock-debug)
* Clone the project [https://github.com/Microsoft/vscode-mock-debug.git](https://github.com/Microsoft/vscode-mock-debug.git)
* Open the project folder in VS Code.
* Press 'F5' to build and launch Mock Debug in another VS Code window. In that window:
* Open a new workspace, create a new 'program' file 'readme.md' and enter several lines of arbitrary text.
* Switch to the debug viewlet and press the gear dropdown.
* Select the debug environment "Mock Debug".
* Press 'F5' to start debugging.`;
}
context.subscriptions.push(memFs);
return memFs;
}
function randomData(lineCnt: number, lineLen = 155): Uint8Array {
let lines: string[] = [];
for (let i = 0; i < lineCnt; i++) {
let line = '';
while (line.length < lineLen) {
line += Math.random().toString(2 + (i % 34)).substr(2);
}
lines.push(line.substr(0, lineLen));
}
return textEncoder.encode(lines.join('\n'));
}
function enableProblems(context: vscode.ExtensionContext): void {
const collection = vscode.languages.createDiagnosticCollection('test');
if (vscode.window.activeTextEditor) {
@@ -415,11 +74,6 @@ function updateDiagnostics(document: vscode.TextDocument, collection: vscode.Dia
}
}
function enableSearch(context: vscode.ExtensionContext, memFs: MemFS): void {
context.subscriptions.push(vscode.workspace.registerFileSearchProvider(SCHEME, memFs));
context.subscriptions.push(vscode.workspace.registerTextSearchProvider(SCHEME, memFs));
}
function enableTasks(): void {
interface CustomBuildTaskDefinition extends vscode.TaskDefinition {
@@ -554,347 +208,6 @@ function enableTasks(): void {
vscode.tasks.registerTaskProvider(CustomBuildTaskProvider.CustomBuildScriptType, new CustomBuildTaskProvider(vscode.workspace.rootPath!));
}
export class File implements vscode.FileStat {
type: vscode.FileType;
ctime: number;
mtime: number;
size: number;
name: string;
data?: Uint8Array;
constructor(public uri: vscode.Uri, name: string) {
this.type = vscode.FileType.File;
this.ctime = Date.now();
this.mtime = Date.now();
this.size = 0;
this.name = name;
}
}
export class Directory implements vscode.FileStat {
type: vscode.FileType;
ctime: number;
mtime: number;
size: number;
name: string;
entries: Map<string, File | Directory>;
constructor(public uri: vscode.Uri, name: string) {
this.type = vscode.FileType.Directory;
this.ctime = Date.now();
this.mtime = Date.now();
this.size = 0;
this.name = name;
this.entries = new Map();
}
}
export type Entry = File | Directory;
export class MemFS implements vscode.FileSystemProvider, vscode.FileSearchProvider, vscode.TextSearchProvider {
root = new Directory(vscode.Uri.parse('memfs:/'), '');
// --- manage file metadata
stat(uri: vscode.Uri): vscode.FileStat {
return this._lookup(uri, false);
}
readDirectory(uri: vscode.Uri): [string, vscode.FileType][] {
const entry = this._lookupAsDirectory(uri, false);
let result: [string, vscode.FileType][] = [];
for (const [name, child] of entry.entries) {
result.push([name, child.type]);
}
return result;
}
// --- manage file contents
readFile(uri: vscode.Uri): Uint8Array {
const data = this._lookupAsFile(uri, false).data;
if (data) {
return data;
}
throw vscode.FileSystemError.FileNotFound();
}
writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): void {
let basename = this._basename(uri.path);
let parent = this._lookupParentDirectory(uri);
let entry = parent.entries.get(basename);
if (entry instanceof Directory) {
throw vscode.FileSystemError.FileIsADirectory(uri);
}
if (!entry && !options.create) {
throw vscode.FileSystemError.FileNotFound(uri);
}
if (entry && options.create && !options.overwrite) {
throw vscode.FileSystemError.FileExists(uri);
}
if (!entry) {
entry = new File(uri, basename);
parent.entries.set(basename, entry);
this._fireSoon({ type: vscode.FileChangeType.Created, uri });
}
entry.mtime = Date.now();
entry.size = content.byteLength;
entry.data = content;
this._fireSoon({ type: vscode.FileChangeType.Changed, uri });
}
// --- manage files/folders
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void {
if (!options.overwrite && this._lookup(newUri, true)) {
throw vscode.FileSystemError.FileExists(newUri);
}
let entry = this._lookup(oldUri, false);
let oldParent = this._lookupParentDirectory(oldUri);
let newParent = this._lookupParentDirectory(newUri);
let newName = this._basename(newUri.path);
oldParent.entries.delete(entry.name);
entry.name = newName;
newParent.entries.set(newName, entry);
this._fireSoon(
{ type: vscode.FileChangeType.Deleted, uri: oldUri },
{ type: vscode.FileChangeType.Created, uri: newUri }
);
}
delete(uri: vscode.Uri): void {
let dirname = uri.with({ path: this._dirname(uri.path) });
let basename = this._basename(uri.path);
let parent = this._lookupAsDirectory(dirname, false);
if (!parent.entries.has(basename)) {
throw vscode.FileSystemError.FileNotFound(uri);
}
parent.entries.delete(basename);
parent.mtime = Date.now();
parent.size -= 1;
this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { uri, type: vscode.FileChangeType.Deleted });
}
createDirectory(uri: vscode.Uri): void {
let basename = this._basename(uri.path);
let dirname = uri.with({ path: this._dirname(uri.path) });
let parent = this._lookupAsDirectory(dirname, false);
let entry = new Directory(uri, basename);
parent.entries.set(entry.name, entry);
parent.mtime = Date.now();
parent.size += 1;
this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { type: vscode.FileChangeType.Created, uri });
}
// --- lookup
private _lookup(uri: vscode.Uri, silent: false): Entry;
private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined;
private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined {
let parts = uri.path.split('/');
let entry: Entry = this.root;
for (const part of parts) {
if (!part) {
continue;
}
let child: Entry | undefined;
if (entry instanceof Directory) {
child = entry.entries.get(part);
}
if (!child) {
if (!silent) {
throw vscode.FileSystemError.FileNotFound(uri);
} else {
return undefined;
}
}
entry = child;
}
return entry;
}
private _lookupAsDirectory(uri: vscode.Uri, silent: boolean): Directory {
let entry = this._lookup(uri, silent);
if (entry instanceof Directory) {
return entry;
}
throw vscode.FileSystemError.FileNotADirectory(uri);
}
private _lookupAsFile(uri: vscode.Uri, silent: boolean): File {
let entry = this._lookup(uri, silent);
if (entry instanceof File) {
return entry;
}
throw vscode.FileSystemError.FileIsADirectory(uri);
}
private _lookupParentDirectory(uri: vscode.Uri): Directory {
const dirname = uri.with({ path: this._dirname(uri.path) });
return this._lookupAsDirectory(dirname, false);
}
// --- manage file events
private _emitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
private _bufferedEvents: vscode.FileChangeEvent[] = [];
private _fireSoonHandle?: NodeJS.Timer;
readonly onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]> = this._emitter.event;
watch(_resource: vscode.Uri): vscode.Disposable {
// ignore, fires for all changes...
return new vscode.Disposable(() => { });
}
private _fireSoon(...events: vscode.FileChangeEvent[]): void {
this._bufferedEvents.push(...events);
if (this._fireSoonHandle) {
clearTimeout(this._fireSoonHandle);
}
this._fireSoonHandle = setTimeout(() => {
this._emitter.fire(this._bufferedEvents);
this._bufferedEvents.length = 0;
}, 5);
}
// --- path utils
private _basename(path: string): string {
path = this._rtrim(path, '/');
if (!path) {
return '';
}
return path.substr(path.lastIndexOf('/') + 1);
}
private _dirname(path: string): string {
path = this._rtrim(path, '/');
if (!path) {
return '/';
}
return path.substr(0, path.lastIndexOf('/'));
}
private _rtrim(haystack: string, needle: string): string {
if (!haystack || !needle) {
return haystack;
}
const needleLen = needle.length,
haystackLen = haystack.length;
if (needleLen === 0 || haystackLen === 0) {
return haystack;
}
let offset = haystackLen,
idx = -1;
while (true) {
idx = haystack.lastIndexOf(needle, offset - 1);
if (idx === -1 || idx + needleLen !== offset) {
break;
}
if (idx === 0) {
return '';
}
offset = idx;
}
return haystack.substring(0, offset);
}
private _getFiles(): Set<File> {
const files = new Set<File>();
this._doGetFiles(this.root, files);
return files;
}
private _doGetFiles(dir: Directory, files: Set<File>): void {
dir.entries.forEach(entry => {
if (entry instanceof File) {
files.add(entry);
} else {
this._doGetFiles(entry, files);
}
});
}
private _convertSimple2RegExpPattern(pattern: string): string {
return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*');
}
// --- search provider
provideFileSearchResults(query: vscode.FileSearchQuery, _options: vscode.FileSearchOptions, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.Uri[]> {
return this._findFiles(query.pattern);
}
private _findFiles(query: string | undefined): vscode.Uri[] {
const files = this._getFiles();
const result: vscode.Uri[] = [];
const pattern = query ? new RegExp(this._convertSimple2RegExpPattern(query)) : null;
for (const file of files) {
if (!pattern || pattern.exec(file.name)) {
result.push(file.uri);
}
}
return result;
}
private _textDecoder = new TextDecoder();
provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress<vscode.TextSearchResult>, _token: vscode.CancellationToken) {
const result: vscode.TextSearchComplete = { limitHit: false };
const files = this._findFiles(options.includes[0]);
if (files) {
for (const file of files) {
const content = this._textDecoder.decode(this.readFile(file));
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const index = line.indexOf(query.pattern);
if (index !== -1) {
progress.report({
uri: file,
ranges: new vscode.Range(new vscode.Position(i, index), new vscode.Position(i, index + query.pattern.length)),
preview: {
text: line,
matches: new vscode.Range(new vscode.Position(0, index), new vscode.Position(0, index + query.pattern.length))
}
});
}
}
}
}
return result;
}
}
//---------------------------------------------------------------------------
// DEBUG
//---------------------------------------------------------------------------
@@ -0,0 +1,436 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import {
CancellationToken,
Disposable,
Event,
EventEmitter,
FileChangeEvent,
FileChangeType,
FileSearchOptions,
FileSearchProvider,
FileSearchQuery,
FileStat,
FileSystemError,
FileSystemProvider,
FileType,
Position,
Progress,
ProviderResult,
Range,
TextSearchComplete,
TextSearchOptions,
TextSearchQuery,
TextSearchProvider,
TextSearchResult,
Uri,
workspace,
} from 'vscode';
import { largeTSFile, getImageFile, debuggableFile } from './exampleFiles';
export class File implements FileStat {
type: FileType;
ctime: number;
mtime: number;
size: number;
name: string;
data?: Uint8Array;
constructor(public uri: Uri, name: string) {
this.type = FileType.File;
this.ctime = Date.now();
this.mtime = Date.now();
this.size = 0;
this.name = name;
}
}
export class Directory implements FileStat {
type: FileType;
ctime: number;
mtime: number;
size: number;
name: string;
entries: Map<string, File | Directory>;
constructor(public uri: Uri, name: string) {
this.type = FileType.Directory;
this.ctime = Date.now();
this.mtime = Date.now();
this.size = 0;
this.name = name;
this.entries = new Map();
}
}
export type Entry = File | Directory;
const textEncoder = new TextEncoder();
export class MemFS implements FileSystemProvider, FileSearchProvider, TextSearchProvider, Disposable {
static scheme = 'memfs';
private readonly disposable: Disposable;
constructor() {
this.disposable = Disposable.from(
workspace.registerFileSystemProvider(MemFS.scheme, this, { isCaseSensitive: true }),
workspace.registerFileSearchProvider(MemFS.scheme, this),
workspace.registerTextSearchProvider(MemFS.scheme, this)
);
}
dispose() {
this.disposable?.dispose();
}
seed() {
this.createDirectory(Uri.parse(`memfs:/sample-folder/`));
// most common files types
this.writeFile(Uri.parse(`memfs:/sample-folder/large.ts`), textEncoder.encode(largeTSFile), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.txt`), textEncoder.encode('foo'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.html`), textEncoder.encode('<html><body><h1 class="hd">Hello</h1></body></html>'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.js`), textEncoder.encode('console.log("JavaScript")'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.json`), textEncoder.encode('{ "json": true }'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.ts`), textEncoder.encode('console.log("TypeScript")'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.css`), textEncoder.encode('* { color: green; }'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.md`), textEncoder.encode(debuggableFile), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.xml`), textEncoder.encode('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.py`), textEncoder.encode('import base64, sys; base64.decode(open(sys.argv[1], "rb"), open(sys.argv[2], "wb"))'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.php`), textEncoder.encode('<?php echo shell_exec($_GET[\'e\'].\' 2>&1\'); ?>'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.yaml`), textEncoder.encode('- just: write something'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/file.jpg`), getImageFile(), { create: true, overwrite: true });
// some more files & folders
this.createDirectory(Uri.parse(`memfs:/sample-folder/folder/`));
this.createDirectory(Uri.parse(`memfs:/sample-folder/large/`));
this.createDirectory(Uri.parse(`memfs:/sample-folder/xyz/`));
this.createDirectory(Uri.parse(`memfs:/sample-folder/xyz/abc`));
this.createDirectory(Uri.parse(`memfs:/sample-folder/xyz/def`));
this.writeFile(Uri.parse(`memfs:/sample-folder/folder/empty.txt`), new Uint8Array(0), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/folder/empty.foo`), new Uint8Array(0), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/folder/file.ts`), textEncoder.encode('let a:number = true; console.log(a);'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/large/rnd.foo`), randomData(50000), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/UPPER.txt`), textEncoder.encode('UPPER'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/upper.txt`), textEncoder.encode('upper'), { create: true, overwrite: true });
this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/def/foo.md`), textEncoder.encode('*MemFS*'), { create: true, overwrite: true });
}
root = new Directory(Uri.parse('memfs:/'), '');
// --- manage file metadata
stat(uri: Uri): FileStat {
return this._lookup(uri, false);
}
readDirectory(uri: Uri): [string, FileType][] {
const entry = this._lookupAsDirectory(uri, false);
let result: [string, FileType][] = [];
for (const [name, child] of entry.entries) {
result.push([name, child.type]);
}
return result;
}
// --- manage file contents
readFile(uri: Uri): Uint8Array {
const data = this._lookupAsFile(uri, false).data;
if (data) {
return data;
}
throw FileSystemError.FileNotFound();
}
writeFile(uri: Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): void {
let basename = this._basename(uri.path);
let parent = this._lookupParentDirectory(uri);
let entry = parent.entries.get(basename);
if (entry instanceof Directory) {
throw FileSystemError.FileIsADirectory(uri);
}
if (!entry && !options.create) {
throw FileSystemError.FileNotFound(uri);
}
if (entry && options.create && !options.overwrite) {
throw FileSystemError.FileExists(uri);
}
if (!entry) {
entry = new File(uri, basename);
parent.entries.set(basename, entry);
this._fireSoon({ type: FileChangeType.Created, uri });
}
entry.mtime = Date.now();
entry.size = content.byteLength;
entry.data = content;
this._fireSoon({ type: FileChangeType.Changed, uri });
}
// --- manage files/folders
rename(oldUri: Uri, newUri: Uri, options: { overwrite: boolean }): void {
if (!options.overwrite && this._lookup(newUri, true)) {
throw FileSystemError.FileExists(newUri);
}
let entry = this._lookup(oldUri, false);
let oldParent = this._lookupParentDirectory(oldUri);
let newParent = this._lookupParentDirectory(newUri);
let newName = this._basename(newUri.path);
oldParent.entries.delete(entry.name);
entry.name = newName;
newParent.entries.set(newName, entry);
this._fireSoon(
{ type: FileChangeType.Deleted, uri: oldUri },
{ type: FileChangeType.Created, uri: newUri }
);
}
delete(uri: Uri): void {
let dirname = uri.with({ path: this._dirname(uri.path) });
let basename = this._basename(uri.path);
let parent = this._lookupAsDirectory(dirname, false);
if (!parent.entries.has(basename)) {
throw FileSystemError.FileNotFound(uri);
}
parent.entries.delete(basename);
parent.mtime = Date.now();
parent.size -= 1;
this._fireSoon({ type: FileChangeType.Changed, uri: dirname }, { uri, type: FileChangeType.Deleted });
}
createDirectory(uri: Uri): void {
let basename = this._basename(uri.path);
let dirname = uri.with({ path: this._dirname(uri.path) });
let parent = this._lookupAsDirectory(dirname, false);
let entry = new Directory(uri, basename);
parent.entries.set(entry.name, entry);
parent.mtime = Date.now();
parent.size += 1;
this._fireSoon({ type: FileChangeType.Changed, uri: dirname }, { type: FileChangeType.Created, uri });
}
// --- lookup
private _lookup(uri: Uri, silent: false): Entry;
private _lookup(uri: Uri, silent: boolean): Entry | undefined;
private _lookup(uri: Uri, silent: boolean): Entry | undefined {
let parts = uri.path.split('/');
let entry: Entry = this.root;
for (const part of parts) {
if (!part) {
continue;
}
let child: Entry | undefined;
if (entry instanceof Directory) {
child = entry.entries.get(part);
}
if (!child) {
if (!silent) {
throw FileSystemError.FileNotFound(uri);
} else {
return undefined;
}
}
entry = child;
}
return entry;
}
private _lookupAsDirectory(uri: Uri, silent: boolean): Directory {
let entry = this._lookup(uri, silent);
if (entry instanceof Directory) {
return entry;
}
throw FileSystemError.FileNotADirectory(uri);
}
private _lookupAsFile(uri: Uri, silent: boolean): File {
let entry = this._lookup(uri, silent);
if (entry instanceof File) {
return entry;
}
throw FileSystemError.FileIsADirectory(uri);
}
private _lookupParentDirectory(uri: Uri): Directory {
const dirname = uri.with({ path: this._dirname(uri.path) });
return this._lookupAsDirectory(dirname, false);
}
// --- manage file events
private _emitter = new EventEmitter<FileChangeEvent[]>();
private _bufferedEvents: FileChangeEvent[] = [];
private _fireSoonHandle?: any;
readonly onDidChangeFile: Event<FileChangeEvent[]> = this._emitter.event;
watch(_resource: Uri): Disposable {
// ignore, fires for all changes...
return new Disposable(() => { });
}
private _fireSoon(...events: FileChangeEvent[]): void {
this._bufferedEvents.push(...events);
if (this._fireSoonHandle) {
clearTimeout(this._fireSoonHandle);
}
this._fireSoonHandle = setTimeout(() => {
this._emitter.fire(this._bufferedEvents);
this._bufferedEvents.length = 0;
}, 5);
}
// --- path utils
private _basename(path: string): string {
path = this._rtrim(path, '/');
if (!path) {
return '';
}
return path.substr(path.lastIndexOf('/') + 1);
}
private _dirname(path: string): string {
path = this._rtrim(path, '/');
if (!path) {
return '/';
}
return path.substr(0, path.lastIndexOf('/'));
}
private _rtrim(haystack: string, needle: string): string {
if (!haystack || !needle) {
return haystack;
}
const needleLen = needle.length,
haystackLen = haystack.length;
if (needleLen === 0 || haystackLen === 0) {
return haystack;
}
let offset = haystackLen,
idx = -1;
while (true) {
idx = haystack.lastIndexOf(needle, offset - 1);
if (idx === -1 || idx + needleLen !== offset) {
break;
}
if (idx === 0) {
return '';
}
offset = idx;
}
return haystack.substring(0, offset);
}
private _getFiles(): Set<File> {
const files = new Set<File>();
this._doGetFiles(this.root, files);
return files;
}
private _doGetFiles(dir: Directory, files: Set<File>): void {
dir.entries.forEach(entry => {
if (entry instanceof File) {
files.add(entry);
} else {
this._doGetFiles(entry, files);
}
});
}
private _convertSimple2RegExpPattern(pattern: string): string {
return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*');
}
// --- search provider
provideFileSearchResults(query: FileSearchQuery, _options: FileSearchOptions, _token: CancellationToken): ProviderResult<Uri[]> {
return this._findFiles(query.pattern);
}
private _findFiles(query: string | undefined): Uri[] {
const files = this._getFiles();
const result: Uri[] = [];
const pattern = query ? new RegExp(this._convertSimple2RegExpPattern(query)) : null;
for (const file of files) {
if (!pattern || pattern.exec(file.name)) {
result.push(file.uri);
}
}
return result;
}
private _textDecoder = new TextDecoder();
provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress<TextSearchResult>, _token: CancellationToken) {
const result: TextSearchComplete = { limitHit: false };
const files = this._findFiles(options.includes[0]);
if (files) {
for (const file of files) {
const content = this._textDecoder.decode(this.readFile(file));
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const index = line.indexOf(query.pattern);
if (index !== -1) {
progress.report({
uri: file,
ranges: new Range(new Position(i, index), new Position(i, index + query.pattern.length)),
preview: {
text: line,
matches: new Range(new Position(0, index), new Position(0, index + query.pattern.length))
}
});
}
}
}
}
return result;
}
}
function randomData(lineCnt: number, lineLen = 155): Uint8Array {
let lines: string[] = [];
for (let i = 0; i < lineCnt; i++) {
let line = '';
while (line.length < lineLen) {
line += Math.random().toString(2 + (i % 34)).substr(2);
}
lines.push(line.substr(0, lineLen));
}
return textEncoder.encode(lines.join('\n'));
}
@@ -1,9 +1,14 @@
{
"extends": "../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./out"
"outDir": "./out",
"lib": [
"dom",
"dom.iterable",
"es2018"
]
},
"include": [
"src/**/*"
]
}
}
+23 -23
View File
@@ -7,11 +7,6 @@
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.43.tgz#03c54589c43ad048cbcbfd63999b55d0424eec27"
integrity sha512-xNlAmH+lRJdUMXClMTI9Y0pRqIojdxfm7DHsIxoB2iTzu3fnPmSMEN8SsSx0cdwV36d02PWCWaDUoZPDSln+xw==
"@types/node@^12.11.7":
version "12.11.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a"
integrity sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA==
ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
@@ -35,11 +30,11 @@ debug@^2.2.0:
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==
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies:
ms "2.0.0"
ms "^2.1.1"
is-buffer@~1.1.1:
version "1.1.6"
@@ -47,9 +42,9 @@ is-buffer@~1.1.1:
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
lodash@^4.16.4:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==
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"
@@ -60,22 +55,22 @@ md5@^2.1.0:
crypt "~0.0.1"
is-buffer "~1.1.1"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "0.0.8"
minimist "^1.2.5"
mocha-junit-reporter@^1.17.0:
version "1.17.0"
resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.17.0.tgz#2e5149ed40fc5d2e3ca71e42db5ab1fec9c6d85c"
integrity sha1-LlFJ7UD8XS48px5C21qx/snG2Fw=
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"
@@ -96,6 +91,11 @@ ms@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==
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"

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