Merge branch 'master' into link

This commit is contained in:
Daniel Imms 2019-10-25 03:44:58 -07:00 committed by GitHub
commit 9fce34075f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
78 changed files with 810 additions and 494 deletions

View file

@ -39,6 +39,7 @@
],
"typescript.tsdk": "node_modules/typescript/lib",
"npm.exclude": "**/extensions/**",
"npm.packageManager": "yarn",
"emmet.excludeLanguages": [],
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.quoteStyle": "single",

View file

@ -114,6 +114,7 @@ const copyrightFilter = [
'!**/*.opts',
'!**/*.disabled',
'!**/*.code-workspace',
'!**/*.js.map',
'!**/promise-polyfill/polyfill.js',
'!build/**/*.init',
'!resources/linux/snap/snapcraft.yaml',

View file

@ -42,7 +42,7 @@
"request": "^2.85.0",
"terser": "4.3.8",
"tslint": "^5.9.1",
"typescript": "3.7.0-dev.20191017",
"typescript": "3.7.1-rc",
"vsce": "1.48.0",
"vscode-telemetry-extractor": "^1.5.4",
"xml2js": "^0.4.17"

View file

@ -2297,10 +2297,10 @@ typed-rest-client@^0.9.0:
tunnel "0.0.4"
underscore "1.8.3"
typescript@3.7.0-dev.20191017:
version "3.7.0-dev.20191017"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.0-dev.20191017.tgz#e61440dd445edea6d7b9a699e7c5d5fbcd1906f2"
integrity sha512-Yi0lCPEN0cn9Gp8TEEkPpgKNR5SWAmx9Hmzzz+oEuivw6amURqRGynaLyFZkMA9iMsvYG5LLqhdlFO3uu5ZT/w==
typescript@3.7.1-rc:
version "3.7.1-rc"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.1-rc.tgz#2054b872d67f8dc732e36c1df397f9327f37ada0"
integrity sha512-2rMtWppLsaPvmpXsoIAXWDBQVnJMw1ITGGSnidMuayLj9iCmMRT69ncKZw/Mk5rXfJkilApKucWQZxproALoRw==
typescript@^3.0.1:
version "3.5.3"

View file

@ -98,7 +98,7 @@
"git": {
"name": "vscode-codicons",
"repositoryUrl": "https://github.com/microsoft/vscode-codicons",
"commitHash": "7f14c092f65f658cd520df3f13765efe828d0ba4"
"commitHash": "3e404025e1ce19702f5363ef6dd3339bfc6d9115"
}
},
"license": "MIT and Creative Commons Attribution 4.0",

View file

@ -401,6 +401,11 @@
"command": "git.stashApplyLatest",
"title": "%command.stashApplyLatest%",
"category": "Git"
},
{
"command": "git.stashDrop",
"title": "%command.stashDrop%",
"category": "Git"
}
],
"menus": {
@ -573,10 +578,6 @@
"command": "git.deleteTag",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.getTags",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.fetch",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
@ -664,6 +665,10 @@
{
"command": "git.stashApplyLatest",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.stashDrop",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
}
],
"scm/title": [
@ -817,6 +822,11 @@
"group": "6_stash",
"when": "scmProvider == git"
},
{
"command": "git.stashDrop",
"group": "6_stash",
"when": "scmProvider == git"
},
{
"command": "git.showOutput",
"group": "7_repository",

View file

@ -64,6 +64,7 @@
"command.stashPopLatest": "Pop Latest Stash",
"command.stashApply": "Apply Stash...",
"command.stashApplyLatest": "Apply Latest Stash",
"command.stashDrop": "Drop Stash...",
"config.enabled": "Whether git is enabled.",
"config.path": "Path and filename of the git executable, e.g. `C:\\Program Files\\Git\\bin\\git.exe` (Windows).",
"config.autoRepositoryDetection": "Configures when repositories should be automatically detected.",
@ -120,7 +121,7 @@
"config.scanRepositories": "List of paths to search for git repositories in.",
"config.showProgress": "Controls whether git actions should show progress.",
"config.rebaseWhenSync": "Force git to use rebase when running the sync command.",
"config.confirmEmptyCommits": "Always confirm the creation of empty commits.",
"config.confirmEmptyCommits": "Always confirm the creation of empty commits for the 'Git: Commit Empty' command.",
"config.fetchOnPull": "Fetch all branches when pulling or just the current one.",
"config.pullTags": "Fetch all tags when pulling.",
"config.autoStash": "Stash any changes before pulling and restore them after successful pull.",

View file

@ -2241,6 +2241,18 @@ export class CommandCenter {
await repository.applyStash();
}
@command('git.stashDrop', { repository: true })
async stashDrop(repository: Repository): Promise<void> {
const placeHolder = localize('pick stash to drop', "Pick a stash to drop");
const stash = await this.pickStash(repository, placeHolder);
if (!stash) {
return;
}
await repository.dropStash(stash.index);
}
private async pickStash(repository: Repository, placeHolder: string): Promise<Stash | undefined> {
const stashes = await repository.getStashes();

View file

@ -1597,6 +1597,24 @@ export class Repository {
}
}
async dropStash(index?: number): Promise<void> {
const args = ['stash', 'drop'];
if (typeof index === 'number') {
args.push(`stash@{${index}}`);
}
try {
await this.run(args);
} catch (err) {
if (/No stash found/.test(err.stderr || '')) {
err.gitErrorCode = GitErrorCodes.NoStashFound;
}
throw err;
}
}
getStatus(limit = 5000): Promise<{ status: IFileStatus[]; didHitLimit: boolean; }> {
return new Promise<{ status: IFileStatus[]; didHitLimit: boolean; }>((c, e) => {
const parser = new GitStatusParser();

View file

@ -1254,6 +1254,10 @@ export class Repository implements Disposable {
return await this.run(Operation.Stash, () => this.repository.popStash(index));
}
async dropStash(index?: number): Promise<void> {
return await this.run(Operation.Stash, () => this.repository.dropStash(index));
}
async applyStash(index?: number): Promise<void> {
return await this.run(Operation.Stash, () => this.repository.applyStash(index));
}

View file

@ -80,10 +80,16 @@
},
{
"scopeName": "documentation.injection",
"path": "./syntaxes/jsdoc.injection.tmLanguage.json",
"path": "./syntaxes/jsdoc.ts.injection.tmLanguage.json",
"injectTo": [
"source.ts",
"source.tsx",
"source.tsx"
]
},
{
"scopeName": "documentation.injection",
"path": "./syntaxes/jsdoc.js.injection.tmLanguage.json",
"injectTo": [
"source.js",
"source.js.jsx"
]

View file

@ -0,0 +1,54 @@
{
"injectionSelector": "L:comment.block.documentation",
"patterns": [
{
"include": "#jsdocbody"
}
],
"repository": {
"jsdocbody": {
"begin": "(?<=/\\*\\*)([^*]|\\*(?!/))*$",
"while": "(^|\\G)\\s*\\*(?!/)(?=([^*]|[*](?!/))*$)",
"patterns": [
{
"include": "text.html.markdown#fenced_code_block_js"
},
{
"include": "text.html.markdown#fenced_code_block_ts"
},
{
"include": "text.html.markdown#fenced_code_block_unknown"
},
{
"include": "#example"
},
{
"include": "source.ts#docblock"
},
{
"include": "text.html.markdown#inline"
}
]
},
"example": {
"begin": "((@)example)\\s+(?=([^*]|[*](?!/))*$).*$",
"while": "(^|\\G)\\s(?!@)(?=([^*]|[*](?!/))*$)",
"beginCaptures": {
"1": {
"name": "storage.type.class.jsdoc"
},
"2": {
"name": "punctuation.definition.block.tag.jsdoc"
}
},
"contentName": "meta.embedded.block.example.source.ts",
"patterns": [
{
"include": "source.js.jsx"
}
]
}
},
"scopeName": "documentation.injection"
}

View file

@ -63,26 +63,26 @@ class ChangeOperation {
*/
class BufferSynchronizer {
private readonly _pending = new Map<string, CloseOperation | OpenOperation | ChangeOperation>();
private readonly _pending = new ResourceMap<CloseOperation | OpenOperation | ChangeOperation>();
constructor(
private readonly client: ITypeScriptServiceClient
) { }
public open(args: Proto.OpenRequestArgs) {
public open(resource: vscode.Uri, args: Proto.OpenRequestArgs) {
if (this.supportsBatching) {
this.updatePending(args.file, pending => {
pending.set(args.file, new OpenOperation(args));
this.updatePending(resource, pending => {
pending.set(resource, new OpenOperation(args));
});
} else {
this.client.executeWithoutWaitingForResponse('open', args);
}
}
public close(filepath: string) {
public close(resource: vscode.Uri, filepath: string) {
if (this.supportsBatching) {
this.updatePending(filepath, pending => {
pending.set(filepath, new CloseOperation(filepath));
this.updatePending(resource, pending => {
pending.set(resource, new CloseOperation(filepath));
});
} else {
const args: Proto.FileRequestArgs = { file: filepath };
@ -90,14 +90,14 @@ class BufferSynchronizer {
}
}
public change(filepath: string, events: readonly vscode.TextDocumentContentChangeEvent[]) {
public change(resource: vscode.Uri, filepath: string, events: readonly vscode.TextDocumentContentChangeEvent[]) {
if (!events.length) {
return;
}
if (this.supportsBatching) {
this.updatePending(filepath, pending => {
pending.set(filepath, new ChangeOperation({
this.updatePending(resource, pending => {
pending.set(resource, new ChangeOperation({
fileName: filepath,
textChanges: events.map((change): Proto.CodeEdit => ({
newText: change.text,
@ -136,7 +136,7 @@ class BufferSynchronizer {
const closedFiles: string[] = [];
const openFiles: Proto.OpenRequestArgs[] = [];
const changedFiles: Proto.FileCodeEdits[] = [];
for (const change of this._pending.values()) {
for (const change of this._pending.values) {
switch (change.type) {
case 'change': changedFiles.push(change.args); break;
case 'open': openFiles.push(change.args); break;
@ -152,8 +152,8 @@ class BufferSynchronizer {
return this.client.apiVersion.gte(API.v340) && vscode.workspace.getConfiguration('typescript', null).get<boolean>('useBatchedBufferSync', true);
}
private updatePending(filepath: string, f: (pending: Map<string, CloseOperation | OpenOperation | ChangeOperation>) => void): void {
if (this._pending.has(filepath)) {
private updatePending(resource: vscode.Uri, f: (pending: ResourceMap<CloseOperation | OpenOperation | ChangeOperation>) => void): void {
if (this._pending.has(resource)) {
// we saw this file before, make sure we flush before working with it again
this.flush();
}
@ -193,7 +193,7 @@ class SyncedBuffer {
}
}
this.synchronizer.open(args);
this.synchronizer.open(this.resource, args);
this.state = BufferState.Open;
}
@ -219,7 +219,7 @@ class SyncedBuffer {
}
public close(): void {
this.synchronizer.close(this.filepath);
this.synchronizer.close(this.resource, this.filepath);
this.state = BufferState.Closed;
}
@ -228,7 +228,7 @@ class SyncedBuffer {
console.error(`Unexpected buffer state: ${this.state}`);
}
this.synchronizer.change(this.filepath, events);
this.synchronizer.change(this.resource, this.filepath, events);
}
}

View file

@ -55,7 +55,7 @@ class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider {
const start = range.start.line;
// workaround for #47240
const end = (range.end.character > 0 && new Set(['}', ']']).has(document.getText(new vscode.Range(range.end.translate(0, -1), range.end))))
const end = (range.end.character > 0 && ['}', ']'].includes(document.getText(new vscode.Range(range.end.translate(0, -1), range.end))))
? Math.max(range.end.line - 1, range.start.line)
: range.end.line;

View file

@ -9,13 +9,14 @@ import * as nls from 'vscode-nls';
import * as Proto from '../protocol';
import { ITypeScriptServiceClient } from '../typescriptService';
import API from '../utils/api';
import { Delayer } from '../utils/async';
import { nulToken } from '../utils/cancellation';
import { VersionDependentRegistration } from '../utils/dependentRegistration';
import { Disposable } from '../utils/dispose';
import * as fileSchemes from '../utils/fileSchemes';
import { doesResourceLookLikeATypeScriptFile } from '../utils/languageDescription';
import * as typeConverters from '../utils/typeConverters';
import FileConfigurationManager from './fileConfigurationManager';
import { Delayer } from '../utils/async';
const localize = nls.loadMessageBundle();
@ -43,10 +44,6 @@ interface RenameAction {
readonly jsTsFileThatIsBeingMoved: vscode.Uri;
}
function doesResourceLookLikeATypeScriptFile(resource: vscode.Uri): boolean {
return /\.tsx?$/i.test(resource.fsPath);
}
class UpdateImportsOnFileRenameHandler extends Disposable {
public static readonly minVersion = API.v300;

View file

@ -6,6 +6,8 @@
import * as vscode from 'vscode';
import * as Proto from '../protocol';
import { ITypeScriptServiceClient } from '../typescriptService';
import * as fileSchemes from '../utils/fileSchemes';
import { doesResourceLookLikeAJavaScriptFile, doesResourceLookLikeATypeScriptFile } from '../utils/languageDescription';
import * as typeConverters from '../utils/typeConverters';
function getSymbolKind(item: Proto.NavtoItem): vscode.SymbolKind {
@ -35,7 +37,7 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide
return [];
}
const filepath = this.client.toOpenedFilePath(document);
const filepath = await this.toOpenedFiledPath(document);
if (!filepath) {
return [];
}
@ -62,10 +64,25 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide
return result;
}
private async toOpenedFiledPath(document: vscode.TextDocument) {
if (document.uri.scheme === fileSchemes.git) {
try {
const path = vscode.Uri.file(JSON.parse(document.uri.query)?.path);
if (doesResourceLookLikeATypeScriptFile(path) || doesResourceLookLikeAJavaScriptFile(path)) {
const document = await vscode.workspace.openTextDocument(path);
return this.client.toOpenedFilePath(document);
}
} catch {
// noop
}
}
return this.client.toOpenedFilePath(document);
}
private static getLabel(item: Proto.NavtoItem) {
let label = item.name;
const label = item.name;
if (item.kind === 'method' || item.kind === 'function') {
label += '()';
return label + '()';
}
return label;
}
@ -75,17 +92,16 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide
// general questions so we check the active editor. If this
// doesn't match we take the first TS document.
const editor = vscode.window.activeTextEditor;
if (editor) {
const document = editor.document;
if (document && this.modeIds.indexOf(document.languageId) >= 0) {
return document;
const activeDocument = vscode.window.activeTextEditor?.document;
if (activeDocument) {
if (this.modeIds.includes(activeDocument.languageId)) {
return activeDocument;
}
}
const documents = vscode.workspace.textDocuments;
for (const document of documents) {
if (this.modeIds.indexOf(document.languageId) >= 0) {
if (this.modeIds.includes(document.languageId)) {
return document;
}
}
@ -98,4 +114,4 @@ export function register(
modeIds: string[],
) {
return vscode.languages.registerWorkspaceSymbolProvider(new TypeScriptWorkspaceSymbolProvider(client, modeIds));
}
}

View file

@ -4,9 +4,8 @@
*--------------------------------------------------------------------------------------------*/
export const file = 'file';
export const untitled = 'untitled';
export const git = 'git';
export const walkThroughSnippet = 'walkThroughSnippet';
export const supportedSchemes = [
@ -17,4 +16,4 @@ export const supportedSchemes = [
export function isSupportedScheme(scheme: string): boolean {
return supportedSchemes.indexOf(scheme) >= 0;
}
}

View file

@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { basename } from 'path';
import * as vscode from 'vscode';
import * as languageModeIds from './languageModeIds';
export const enum DiagnosticLanguage {
@ -48,3 +49,11 @@ export function isTsConfigFileName(fileName: string): boolean {
export function isJsConfigOrTsConfigFileName(fileName: string): boolean {
return /^[jt]sconfig\.(.+\.)?json$/i.test(basename(fileName));
}
export function doesResourceLookLikeATypeScriptFile(resource: vscode.Uri): boolean {
return /\.tsx?$/i.test(resource.fsPath);
}
export function doesResourceLookLikeAJavaScriptFile(resource: vscode.Uri): boolean {
return /\.jsx?$/i.test(resource.fsPath);
}

View file

@ -13,27 +13,25 @@ const localize = loadMessageBundle();
const typingsInstallTimeout = 30 * 1000;
export default class TypingsStatus extends Disposable {
private _acquiringTypings: { [eventId: string]: NodeJS.Timer } = Object.create({});
private _client: ITypeScriptServiceClient;
private _subscriptions: vscode.Disposable[] = [];
private readonly _acquiringTypings = new Map<number, NodeJS.Timer>();
private readonly _client: ITypeScriptServiceClient;
constructor(client: ITypeScriptServiceClient) {
super();
this._client = client;
this._subscriptions.push(
this._register(
this._client.onDidBeginInstallTypings(event => this.onBeginInstallTypings(event.eventId)));
this._subscriptions.push(
this._register(
this._client.onDidEndInstallTypings(event => this.onEndInstallTypings(event.eventId)));
}
public dispose(): void {
super.dispose();
this._subscriptions.forEach(x => x.dispose());
for (const eventId of Object.keys(this._acquiringTypings)) {
clearTimeout(this._acquiringTypings[eventId]);
for (const timeout of this._acquiringTypings.values()) {
clearTimeout(timeout);
}
}
@ -42,37 +40,36 @@ export default class TypingsStatus extends Disposable {
}
private onBeginInstallTypings(eventId: number): void {
if (this._acquiringTypings[eventId]) {
if (this._acquiringTypings.has(eventId)) {
return;
}
this._acquiringTypings[eventId] = setTimeout(() => {
this._acquiringTypings.set(eventId, setTimeout(() => {
this.onEndInstallTypings(eventId);
}, typingsInstallTimeout);
}, typingsInstallTimeout));
}
private onEndInstallTypings(eventId: number): void {
const timer = this._acquiringTypings[eventId];
const timer = this._acquiringTypings.get(eventId);
if (timer) {
clearTimeout(timer);
}
delete this._acquiringTypings[eventId];
this._acquiringTypings.delete(eventId);
}
}
export class AtaProgressReporter {
export class AtaProgressReporter extends Disposable {
private _promises = new Map<number, Function>();
private _disposable: vscode.Disposable;
private readonly _promises = new Map<number, Function>();
constructor(client: ITypeScriptServiceClient) {
this._disposable = vscode.Disposable.from(
client.onDidBeginInstallTypings(e => this._onBegin(e.eventId)),
client.onDidEndInstallTypings(e => this._onEndOrTimeout(e.eventId)),
client.onTypesInstallerInitializationFailed(_ => this.onTypesInstallerInitializationFailed()));
super();
this._register(client.onDidBeginInstallTypings(e => this._onBegin(e.eventId)));
this._register(client.onDidEndInstallTypings(e => this._onEndOrTimeout(e.eventId)));
this._register(client.onTypesInstallerInitializationFailed(_ => this.onTypesInstallerInitializationFailed()));
}
dispose(): void {
this._disposable.dispose();
super.dispose();
this._promises.forEach(value => value());
}

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.40.0",
"distro": "879a75db0f86c41ef432c8740266aefced54e9b7",
"distro": "2fd17a3cb58818370b0abc2de4728553ad812e7e",
"author": {
"name": "Microsoft Corporation"
},
@ -129,7 +129,7 @@
"source-map": "^0.4.4",
"ts-loader": "^4.4.2",
"tslint": "^5.16.0",
"typescript": "3.7.0-dev.20191017",
"typescript": "3.7.1-rc",
"typescript-formatter": "7.1.0",
"underscore": "^1.8.2",
"vinyl": "^2.0.0",

View file

@ -10,10 +10,10 @@ import { SubmenuAction } from 'vs/base/browser/ui/menu/menu';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
export interface IContextMenuEvent {
shiftKey?: boolean;
ctrlKey?: boolean;
altKey?: boolean;
metaKey?: boolean;
readonly shiftKey?: boolean;
readonly ctrlKey?: boolean;
readonly altKey?: boolean;
readonly metaKey?: boolean;
}
export class ContextSubMenu extends SubmenuAction {

View file

@ -5,7 +5,7 @@
@font-face {
font-family: "codicon";
src: url("./codicon.ttf?297296ccad95f5838f73ae67efd78fbc") format("truetype");
src: url("./codicon.ttf?0d7ce4078a0bf127a55edc6b4b1996eb") format("truetype");
}
.codicon[class*='codicon-'] {
@ -197,13 +197,13 @@
.codicon-color-mode:before { content: "\f130" }
.codicon-comment-discussion:before { content: "\f131" }
.codicon-compare-changes:before { content: "\f132" }
.codicon-continue:before { content: "\f133" }
.codicon-credit-card:before { content: "\f134" }
.codicon-current-and-breakpoint:before { content: "\f135" }
.codicon-current:before { content: "\f136" }
.codicon-dash:before { content: "\f137" }
.codicon-dashboard:before { content: "\f138" }
.codicon-database:before { content: "\f139" }
.codicon-credit-card:before { content: "\f133" }
.codicon-current-and-breakpoint:before { content: "\f134" }
.codicon-current:before { content: "\f135" }
.codicon-dash:before { content: "\f136" }
.codicon-dashboard:before { content: "\f137" }
.codicon-database:before { content: "\f138" }
.codicon-debug-continue:before { content: "\f139" }
.codicon-debug-disconnect:before { content: "\f13a" }
.codicon-debug-pause:before { content: "\f13b" }
.codicon-debug-restart:before { content: "\f13c" }

View file

@ -430,12 +430,16 @@ class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListRenderer
nodes.forEach(node => {
const ref = model.getNodeLocation(node);
const parentRef = model.getParentNodeLocation(ref);
try {
const parentRef = model.getParentNodeLocation(ref);
if (node.collapsible && node.children.length > 0 && !node.collapsed) {
set.add(node);
} else if (parentRef) {
set.add(model.getNode(parentRef));
if (node.collapsible && node.children.length > 0 && !node.collapsed) {
set.add(node);
} else if (parentRef) {
set.add(model.getNode(parentRef));
}
} catch {
// noop
}
});

View file

@ -257,7 +257,12 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
throw new TreeError(this.user, `Invalid getParentNodeLocation call`);
}
const node = this.nodes.get(element)!;
const node = this.nodes.get(element);
if (!node) {
throw new TreeError(this.user, `Tree element not found: ${element}`);
}
const location = this.model.getNodeLocation(node);
const parentLocation = this.model.getParentNodeLocation(location);
const parent = this.model.getNode(parentLocation);

View file

@ -137,6 +137,7 @@ export interface Location {
export interface ParseOptions {
disallowComments?: boolean;
allowTrailingComma?: boolean;
allowEmptyContent?: boolean;
}
export namespace ParseOptions {
@ -785,7 +786,7 @@ export function getLocation(text: string, position: number): Location {
if (position < offset) {
throw earlyReturnException;
}
setPreviousNode(value, offset, length, getLiteralNodeType(value));
setPreviousNode(value, offset, length, getNodeType(value));
if (position <= offset + length) {
throw earlyReturnException;
@ -848,7 +849,7 @@ export function parse(text: string, errors: ParseError[] = [], options: ParseOpt
function onValue(value: any) {
if (Array.isArray(currentParent)) {
(<any[]>currentParent).push(value);
} else if (currentProperty) {
} else if (currentProperty !== null) {
currentParent[currentProperty] = value;
}
}
@ -927,7 +928,7 @@ export function parseTree(text: string, errors: ParseError[] = [], options: Pars
ensurePropertyComplete(offset + length);
},
onLiteralValue: (value: any, offset: number, length: number) => {
onValue({ type: getLiteralNodeType(value), offset, length, parent: currentParent, value });
onValue({ type: getNodeType(value), offset, length, parent: currentParent, value });
ensurePropertyComplete(offset + length);
},
onSeparator: (sep: string, offset: number, length: number) => {
@ -1287,7 +1288,11 @@ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions
scanNext();
if (_scanner.getToken() === SyntaxKind.EOF) {
return true;
if (options.allowEmptyContent) {
return true;
}
handleError(ParseErrorCode.ValueExpected, [], []);
return false;
}
if (!parseValue()) {
handleError(ParseErrorCode.ValueExpected, [], []);
@ -1333,11 +1338,19 @@ export function stripComments(text: string, replaceCh?: string): string {
return parts.join('');
}
function getLiteralNodeType(value: any): NodeType {
export function getNodeType(value: any): NodeType {
switch (typeof value) {
case 'boolean': return 'boolean';
case 'number': return 'number';
case 'string': return 'string';
case 'object': {
if (!value) {
return 'null';
} else if (Array.isArray(value)) {
return 'array';
}
return 'object';
}
default: return 'null';
}
}

View file

@ -189,7 +189,7 @@ export const setImmediate: ISetImmediate = (function defineSetImmediate() {
id: myId,
callback: callback
});
globals.postMessage({ vscodeSetImmediateId: myId }, '*'); // TODO@alex please revisit
globals.postMessage({ vscodeSetImmediateId: myId }, '*');
};
}
if (typeof process !== 'undefined' && typeof process.nextTick === 'function') {

View file

@ -12,7 +12,6 @@ import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/v
import { DomReadingContext, ViewLine, ViewLineOptions } from 'vs/editor/browser/viewParts/lines/viewLine';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { IViewLines, LineVisibleRanges, VisibleRanges, HorizontalPosition } from 'vs/editor/common/view/renderingContext';
import { ViewContext } from 'vs/editor/common/view/viewContext';
@ -74,8 +73,8 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost<ViewLine>,
private _typicalHalfwidthCharacterWidth: number;
private _isViewportWrapping: boolean;
private _revealHorizontalRightPadding: number;
private _selections: Selection[];
private _cursorSurroundingLines: number;
private _cursorSurroundingLinesStyle: 'default' | 'all';
private _canUseLayerHinting: boolean;
private _viewLineOptions: ViewLineOptions;
@ -103,9 +102,9 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost<ViewLine>,
this._isViewportWrapping = wrappingInfo.isViewportWrapping;
this._revealHorizontalRightPadding = options.get(EditorOption.revealHorizontalRightPadding);
this._cursorSurroundingLines = options.get(EditorOption.cursorSurroundingLines);
this._cursorSurroundingLinesStyle = options.get(EditorOption.cursorSurroundingLinesStyle);
this._canUseLayerHinting = !options.get(EditorOption.disableLayerHinting);
this._viewLineOptions = new ViewLineOptions(conf, this._context.theme.type);
this._selections = [];
PartFingerprints.write(this.domNode, PartFingerprint.ViewLines);
this.domNode.setClassName('view-lines');
@ -156,6 +155,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost<ViewLine>,
this._isViewportWrapping = wrappingInfo.isViewportWrapping;
this._revealHorizontalRightPadding = options.get(EditorOption.revealHorizontalRightPadding);
this._cursorSurroundingLines = options.get(EditorOption.cursorSurroundingLines);
this._cursorSurroundingLinesStyle = options.get(EditorOption.cursorSurroundingLinesStyle);
this._canUseLayerHinting = !options.get(EditorOption.disableLayerHinting);
Configuration.applyFontInfo(this.domNode, fontInfo);
@ -186,7 +186,6 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost<ViewLine>,
return false;
}
public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean {
this._selections = e.selections;
const rendStartLineNumber = this._visibleLines.getStartLineNumber();
const rendEndLineNumber = this._visibleLines.getEndLineNumber();
let r = false;
@ -571,10 +570,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost<ViewLine>,
boxStartY = this._context.viewLayout.getVerticalOffsetForLineNumber(range.startLineNumber);
boxEndY = this._context.viewLayout.getVerticalOffsetForLineNumber(range.endLineNumber) + this._lineHeight;
const shouldIgnoreScrollOff = source === 'mouse' && (
this._selections.length > 1 // scroll off might trigger scrolling and mess up with multi cursor
|| (this._selections.length > 0 && this._selections[0].isEmpty()) // we don't want to single click triggering selection
);
const shouldIgnoreScrollOff = source === 'mouse' && this._cursorSurroundingLinesStyle === 'default';
if (!shouldIgnoreScrollOff) {
const context = Math.min((viewportHeight / this._lineHeight) / 2, this._cursorSurroundingLines);
@ -589,7 +585,10 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost<ViewLine>,
let newScrollTop: number;
if (verticalType === viewEvents.VerticalRevealType.Center || verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport) {
if (boxEndY - boxStartY > viewportHeight) {
// the box is larger than the viewport ... scroll to its top
newScrollTop = boxStartY;
} else if (verticalType === viewEvents.VerticalRevealType.Center || verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport) {
if (verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport && viewportStartY <= boxStartY && boxEndY <= viewportEndY) {
// Box is already in the viewport... do nothing
newScrollTop = viewportStartY;

View file

@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/editor';
import 'vs/css!./media/tokens';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';

View file

@ -1,9 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-editor .vs-whitespace {
display:inline-block;
}

View file

@ -71,6 +71,12 @@ export interface IEditorOptions {
* Defaults to 0.
*/
cursorSurroundingLines?: number;
/**
* Controls when `cursorSurroundingLines` should be enforced
* Defaults to `default`, `cursorSurroundingLines` is not enforced when cursor position is changed
* by mouse.
*/
cursorSurroundingLinesStyle?: 'default' | 'all';
/**
* Render last line number when the file ends with a newline.
* Defaults to true.
@ -2722,6 +2728,7 @@ export const enum EditorOption {
cursorSmoothCaretAnimation,
cursorStyle,
cursorSurroundingLines,
cursorSurroundingLinesStyle,
cursorWidth,
disableLayerHinting,
disableMonospaceOptimizations,
@ -2936,6 +2943,18 @@ export const EditorOptions = {
0, 0, Constants.MAX_SAFE_SMALL_INTEGER,
{ description: nls.localize('cursorSurroundingLines', "Controls the minimal number of visible leading and trailing lines surrounding the cursor. Known as 'scrollOff' or `scrollOffset` in some other editors.") }
)),
cursorSurroundingLinesStyle: register(new EditorStringEnumOption(
EditorOption.cursorSurroundingLinesStyle, 'cursorSurroundingLinesStyle',
'default' as 'default' | 'all',
['default', 'all'] as const,
{
enumDescriptions: [
nls.localize('cursorSurroundingLinesStyle.default', "`cursorSurroundingLines` is enforced only when triggered from keyboard and api"),
nls.localize('cursorSurroundingLinesStyle.all', "`cursorSurroundingLines` is enforced always.")
],
description: nls.localize('cursorSurroundingLinesStyle', "Controls when `cursorSurroundingLines` should be enforced")
}
)),
cursorWidth: register(new EditorIntOption(
EditorOption.cursorWidth, 'cursorWidth',
0, 0, Constants.MAX_SAFE_SMALL_INTEGER,

View file

@ -5,7 +5,7 @@
import { Event } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IScrollDimensions, IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable';
import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable';
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout';
@ -65,15 +65,23 @@ export class ViewLayout extends Disposable implements IViewLayout {
}
if (e.hasChanged(EditorOption.layoutInfo)) {
const layoutInfo = options.get(EditorOption.layoutInfo);
const width = layoutInfo.contentWidth;
const height = layoutInfo.contentHeight;
const scrollDimensions = this.scrollable.getScrollDimensions();
const scrollWidth = scrollDimensions.scrollWidth;
const scrollHeight = this._getTotalHeight(width, height, scrollWidth);
this.scrollable.setScrollDimensions({
width: layoutInfo.contentWidth,
height: layoutInfo.contentHeight
width: width,
height: height,
scrollHeight: scrollHeight
});
} else {
this._updateHeight();
}
if (e.hasChanged(EditorOption.smoothScrolling)) {
this._configureSmoothScrollDuration();
}
this._updateHeight();
}
public onFlushed(lineCount: number): void {
this._linesLayout.onFlushed(lineCount);
@ -87,37 +95,41 @@ export class ViewLayout extends Disposable implements IViewLayout {
// ---- end view event handlers
private _getHorizontalScrollbarHeight(scrollDimensions: IScrollDimensions): number {
private _getHorizontalScrollbarHeight(width: number, scrollWidth: number): number {
const options = this._configuration.options;
const scrollbar = options.get(EditorOption.scrollbar);
if (scrollbar.horizontal === ScrollbarVisibility.Hidden) {
// horizontal scrollbar not visible
return 0;
}
if (scrollDimensions.width >= scrollDimensions.scrollWidth) {
if (width >= scrollWidth) {
// horizontal scrollbar not visible
return 0;
}
return scrollbar.horizontalScrollbarSize;
}
private _getTotalHeight(): number {
private _getTotalHeight(width: number, height: number, scrollWidth: number): number {
const options = this._configuration.options;
const scrollDimensions = this.scrollable.getScrollDimensions();
let result = this._linesLayout.getLinesTotalHeight();
if (options.get(EditorOption.scrollBeyondLastLine)) {
result += scrollDimensions.height - options.get(EditorOption.lineHeight);
result += height - options.get(EditorOption.lineHeight);
} else {
result += this._getHorizontalScrollbarHeight(scrollDimensions);
result += this._getHorizontalScrollbarHeight(width, scrollWidth);
}
return Math.max(scrollDimensions.height, result);
return Math.max(height, result);
}
private _updateHeight(): void {
const scrollDimensions = this.scrollable.getScrollDimensions();
const width = scrollDimensions.width;
const height = scrollDimensions.height;
const scrollWidth = scrollDimensions.scrollWidth;
const scrollHeight = this._getTotalHeight(width, height, scrollWidth);
this.scrollable.setScrollDimensions({
scrollHeight: this._getTotalHeight()
scrollHeight: scrollHeight
});
}

View file

@ -783,7 +783,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
if (!fontIsMonospace) {
const partIsOnlyWhitespace = (partType === 'vs-whitespace');
if (partIsOnlyWhitespace || !containsForeignElements) {
sb.appendASCIIString(' style="width:');
sb.appendASCIIString(' style="display:inline-block;width:');
sb.appendASCIIString(String(spaceWidth * partContentCnt));
sb.appendASCIIString('px"');
}

View file

@ -489,7 +489,7 @@ export class StartFindWithSelectionAction extends EditorAction {
forceRevealReplace: false,
seedSearchStringFromSelection: true,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true,
updateSearchScope: false
});

View file

@ -189,8 +189,8 @@ suite('viewLineRenderer.renderLine', () => {
createPart(48, 12),
]);
let expectedOutput = [
'<span class="vs-whitespace" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtk2">export</span>',
'<span class="mtk3">\u00a0</span>',
'<span class="mtk4">class</span>',
@ -201,8 +201,8 @@ suite('viewLineRenderer.renderLine', () => {
'<span class="mtk9">\u00a0</span>',
'<span class="mtk10">//\u00a0</span>',
'<span class="mtk11">http://test.com</span>',
'<span class="vs-whitespace" style="width:20px">\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="width:30px">\u00b7\u00b7\u00b7</span>'
'<span class="vs-whitespace" style="display:inline-block;width:20px">\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:30px">\u00b7\u00b7\u00b7</span>'
].join('');
let expectedOffsetsArr = [
[0],
@ -867,10 +867,10 @@ suite('viewLineRenderer.renderLine 2', () => {
null,
[
'<span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'</span>',
].join('')
);
@ -889,12 +889,12 @@ suite('viewLineRenderer.renderLine 2', () => {
null,
[
'<span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'</span>',
].join('')
);
@ -913,11 +913,11 @@ suite('viewLineRenderer.renderLine 2', () => {
null,
[
'<span>',
'<span class="vs-whitespace" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="vs-whitespace" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'</span>',
].join('')
);
@ -936,15 +936,15 @@ suite('viewLineRenderer.renderLine 2', () => {
null,
[
'<span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u2192\u00a0</span>',
'<span class="vs-whitespace" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="width:20px">\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u2192\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:20px">\u00b7\u00b7</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="vs-whitespace" style="width:20px">\u00b7\uffeb</span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u2192\u00a0</span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u00b7\uffeb</span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:20px">\u00b7\uffeb</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u2192\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u00b7\uffeb</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'</span>',
].join('')
);
@ -965,13 +965,13 @@ suite('viewLineRenderer.renderLine 2', () => {
[
'<span>',
'<span class="">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="width:20px">\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:20px">\u00b7\u00b7</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="vs-whitespace" style="width:20px">\u00b7\uffeb</span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u2192\u00a0</span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u00b7\uffeb</span>',
'<span class="vs-whitespace" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:20px">\u00b7\uffeb</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u2192\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u00b7\uffeb</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'</span>',
].join('')
);
@ -1016,11 +1016,11 @@ suite('viewLineRenderer.renderLine 2', () => {
[
'<span>',
'<span class="mtk1">it</span>',
'<span class="vs-whitespace" style="width:20px">\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:20px">\u00b7\u00b7</span>',
'<span class="mtk1">it</span>',
'<span class="mtk2">\u00a0</span>',
'<span class="mtk3">it</span>',
'<span class="vs-whitespace" style="width:20px">\u00b7\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:20px">\u00b7\u00b7</span>',
'<span class="mtk3">it</span>',
'</span>',
].join('')
@ -1041,12 +1041,12 @@ suite('viewLineRenderer.renderLine 2', () => {
null,
[
'<span>',
'<span class="vs-whitespace" style="width:10px">\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:10px">\u00b7</span>',
'<span class="mtk0">Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="vs-whitespace" style="width:10px">\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:10px">\u00b7</span>',
'<span class="mtk2">world!</span>',
'<span class="vs-whitespace" style="width:30px">\u2192\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:30px">\u2192\u00a0\u00a0</span>',
'</span>',
].join('')
);
@ -1088,12 +1088,12 @@ suite('viewLineRenderer.renderLine 2', () => {
[new LineRange(0, 14)],
[
'<span>',
'<span class="vs-whitespace" style="width:10px">\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:10px">\u00b7</span>',
'<span class="mtk0">Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="vs-whitespace" style="width:10px">\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:10px">\u00b7</span>',
'<span class="mtk2">world!</span>',
'<span class="vs-whitespace" style="width:30px">\u2192\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:30px">\u2192\u00a0\u00a0</span>',
'</span>',
].join('')
);
@ -1113,7 +1113,7 @@ suite('viewLineRenderer.renderLine 2', () => {
[new LineRange(0, 5)],
[
'<span>',
'<span class="vs-whitespace" style="width:10px">\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:10px">\u00b7</span>',
'<span class="mtk0">Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtk2">\u00a0world!\u00a0\u00a0\u00a0</span>',
@ -1137,11 +1137,11 @@ suite('viewLineRenderer.renderLine 2', () => {
[new LineRange(0, 5), new LineRange(9, 14)],
[
'<span>',
'<span class="vs-whitespace" style="width:10px">\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:10px">\u00b7</span>',
'<span class="mtk0">Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtk2">\u00a0world!</span>',
'<span class="vs-whitespace" style="width:30px">\u2192\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:30px">\u2192\u00a0\u00a0</span>',
'</span>',
].join('')
);
@ -1162,11 +1162,11 @@ suite('viewLineRenderer.renderLine 2', () => {
[new LineRange(9, 14), new LineRange(0, 5)],
[
'<span>',
'<span class="vs-whitespace" style="width:10px">\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:10px">\u00b7</span>',
'<span class="mtk0">Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtk2">\u00a0world!</span>',
'<span class="vs-whitespace" style="width:30px">\u2192\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:30px">\u2192\u00a0\u00a0</span>',
'</span>',
].join('')
);
@ -1184,9 +1184,9 @@ suite('viewLineRenderer.renderLine 2', () => {
[new LineRange(0, 1), new LineRange(1, 2), new LineRange(2, 3)],
[
'<span>',
'<span class="vs-whitespace" style="width:10px">\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:10px">\u00b7</span>',
'<span class="mtk0">*</span>',
'<span class="vs-whitespace" style="width:10px">\u00b7</span>',
'<span class="vs-whitespace" style="display:inline-block;width:10px">\u00b7</span>',
'<span class="mtk0">S</span>',
'</span>',
].join('')
@ -1293,7 +1293,7 @@ suite('viewLineRenderer.renderLine 2', () => {
let expected = [
'<span>',
'<span class="vs-whitespace" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="vs-whitespace" style="display:inline-block;width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="mtk3 before">b</span>',
'<span class="mtk3">la</span>',
'</span>'

6
src/vs/monaco.d.ts vendored
View file

@ -2484,6 +2484,12 @@ declare namespace monaco.editor {
* Defaults to 0.
*/
cursorSurroundingLines?: number;
/**
* Controls when `cursorSurroundingLines` should be enforced
* Defaults to `default`, `cursorSurroundingLines` is not enforced when cursor position is changed
* by mouse.
*/
cursorSurroundingLinesStyle?: 'default' | 'all';
/**
* Render last line number when the file ends with a newline.
* Defaults to true.

View file

@ -7,7 +7,7 @@ import { virtualMachineHint } from 'vs/base/node/id';
import { IMachineInfo, WorkspaceStats, WorkspaceStatItem, PerformanceInfo, SystemInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError, isRemoteDiagnosticError, IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnostics';
import { readdir, stat, exists, readFile } from 'fs';
import { join, basename } from 'vs/base/common/path';
import { parse, ParseError } from 'vs/base/common/json';
import { parse, ParseError, getNodeType } from 'vs/base/common/json';
import { listProcesses } from 'vs/base/node/ps';
import product from 'vs/platform/product/common/product';
import { repeat, pad } from 'vs/base/common/strings';
@ -223,7 +223,7 @@ export function collectLaunchConfigs(folder: string): Promise<WorkspaceStatItem[
return resolve([]);
}
if (json['configurations']) {
if (getNodeType(json) === 'object' && json['configurations']) {
for (const each of json['configurations']) {
const type = each['type'];
if (type) {

View file

@ -188,7 +188,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
private hasErrors(content: string): boolean {
const parseErrors: ParseError[] = [];
parse(content, parseErrors);
parse(content, parseErrors, { allowEmptyContent: true, allowTrailingComma: true });
return parseErrors.length > 0;
}
@ -248,13 +248,23 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
}
private getIgnoredSettings(settingsContent?: string): string[] {
const value: string[] = (settingsContent ? parse(settingsContent)['configurationSync.settingsToIgnore'] : this.configurationService.getValue<string[]>('configurationSync.settingsToIgnore')) || [];
let value: string[] = [];
if (settingsContent) {
const setting = parse(settingsContent);
if (setting) {
value = setting['configurationSync.settingsToIgnore'];
}
} else {
value = this.configurationService.getValue<string[]>('configurationSync.settingsToIgnore');
}
const added: string[] = [], removed: string[] = [];
for (const key of value) {
if (startsWith(key, '-')) {
removed.push(key.substring(1));
} else {
added.push(key);
if (Array.isArray(value)) {
for (const key of value) {
if (startsWith(key, '-')) {
removed.push(key.substring(1));
} else {
added.push(key);
}
}
}
return [...DEFAULT_IGNORED_SETTINGS, ...added].filter(setting => removed.indexOf(setting) === -1);

View file

@ -285,12 +285,9 @@ function doParseStoredWorkspace(path: URI, contents: string): IStoredWorkspace {
let storedWorkspace: IStoredWorkspace = json.parse(contents); // use fault tolerant parser
// Filter out folders which do not have a path or uri set
if (Array.isArray(storedWorkspace.folders)) {
if (storedWorkspace && Array.isArray(storedWorkspace.folders)) {
storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder));
}
// Validate
if (!Array.isArray(storedWorkspace.folders)) {
} else {
throw new Error(`${path} looks like an invalid workspace file.`);
}

View file

@ -130,12 +130,9 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
let storedWorkspace: IStoredWorkspace = json.parse(contents); // use fault tolerant parser
// Filter out folders which do not have a path or uri set
if (Array.isArray(storedWorkspace.folders)) {
if (storedWorkspace && Array.isArray(storedWorkspace.folders)) {
storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder));
}
// Validate
if (!Array.isArray(storedWorkspace.folders)) {
} else {
throw new Error(`${path.toString()} looks like an invalid workspace file.`);
}

View file

@ -16,8 +16,12 @@
declare module 'vscode' {
//#region Joh - call hierarchy
//#region Joh - call hierarchy: https://github.com/microsoft/vscode/issues/70231
/**
* Represents programming constructs like functions or constructors in the context
* of call hierarchy.
*/
export class CallHierarchyItem {
/**
* The name of this item.
@ -50,7 +54,7 @@ declare module 'vscode' {
range: Range;
/**
* The range that should be selected and reveal when this symbol is being picked, e.g. the name of a function.
* The range that should be selected and revealed when this symbol is being picked, e.g. the name of a function.
* Must be contained by the [`range`](#CallHierarchyItem.range).
*/
selectionRange: Range;
@ -58,35 +62,112 @@ declare module 'vscode' {
constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range);
}
/**
* Represents an incoming call, e.g. a caller of a method or constructor.
*/
export class CallHierarchyIncomingCall {
/**
* The item that makes the call.
*/
from: CallHierarchyItem;
/**
* The range at which at which the calls appears. This is relative to the caller
* denoted by [`this.from`](#CallHierarchyIncomingCall.from).
*/
fromRanges: Range[];
/**
* Create a new call object.
*
* @param item The item making the call.
* @param fromRanges The ranges at which the calls appear.
*/
constructor(item: CallHierarchyItem, fromRanges: Range[]);
}
/**
* Represents an outgoing call, e.g. calling a getter from a method or a method from a constructor etc.
*/
export class CallHierarchyOutgoingCall {
fromRanges: Range[];
/**
* The item that is called.
*/
to: CallHierarchyItem;
/**
* The range at which this item is called. This is the range relative to the caller, e.g the item
* passed to [`provideCallHierarchyOutgoingCalls`](#CallHierarchyItemProvider.provideCallHierarchyOutgoingCalls)
* and not [`this.to`](#CallHierarchyOutgoingCall.to).
*/
fromRanges: Range[];
/**
* Create a new call object.
*
* @param item The item being called
* @param fromRanges The ranges at which the calls appear.
*/
constructor(item: CallHierarchyItem, fromRanges: Range[]);
}
export interface CallHierarchyItemProvider {
/**
* The call hierarchy provider interface describes the constract between extensions
* and the call hierarchy feature which allows to browse calls and caller of function,
* methods, constructor etc.
*/
export interface CallHierarchyProvider {
/**
* Bootstraps call hierarchy by returning the item that is denoted by the given document
* and position. This item will be used as entry into the call graph. Providers should
* return `undefined` or `null` when there is no item at the given location.
*
* @param document The document in which the command was invoked.
* @param position The position at which the command was invoked.
* @param token A cancellation token.
* @returns A call hierarchy item or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined` or `null`.
*/
prepareCallHierarchy(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<CallHierarchyItem>;
/**
* Provide a list of callers for the provided item, e.g. all function calling a function.
* Provide all incoming calls for an item, e.g all callers for a method. In graph terms this descibes directed
* and annotated edges inside the call graph, e.g the given item is the starting node and the result is the nodes
* that can be reached.
*
* @param item The hierarchy item for which incoming calls should be computed.
* @param token A cancellation token.
* @returns A set of incoming calls or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined` or `null`.
*/
provideCallHierarchyIncomingCalls(item: CallHierarchyItem, token: CancellationToken): ProviderResult<CallHierarchyIncomingCall[]>;
/**
* Provide a list of calls for the provided item, e.g. all functions call from a function.
* Provide all outgoing calls for an item, e.g call calls to functions, methods, or constructors from the given item. In
* graph terms this descibes directed and annotated edges inside the call graph, e.g the given item is the starting
* node and the result is the nodes that can be reached.
*
* @param item The hierarchy item for which outgoing calls should be computed.
* @param token A cancellation token.
* @returns A set of outgoing calls or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined` or `null`.
*/
provideCallHierarchyOutgoingCalls(item: CallHierarchyItem, token: CancellationToken): ProviderResult<CallHierarchyOutgoingCall[]>;
}
export namespace languages {
export function registerCallHierarchyProvider(selector: DocumentSelector, provider: CallHierarchyItemProvider): Disposable;
/**
* Register a call hierarchy provider.
*
* @param selector A selector that defines the documents this provider is applicable to.
* @param provider A call hierarchy provider.
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
*/
export function registerCallHierarchyProvider(selector: DocumentSelector, provider: CallHierarchyProvider): Disposable;
}
//#endregion

View file

@ -18,7 +18,7 @@ import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'v
import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService';
import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsPanel';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape } from '../common/extHost.protocol';
import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape, CommentThreadChanges } from '../common/extHost.protocol';
import { COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer';
@ -116,17 +116,15 @@ export class MainThreadCommentThread implements modes.CommentThread {
this._isDisposed = false;
}
batchUpdate(
range: IRange,
label: string,
contextValue: string | undefined,
comments: modes.Comment[],
collapsibleState: modes.CommentThreadCollapsibleState) {
this._range = range;
this._label = label;
this._contextValue = contextValue;
this._comments = comments;
this._collapsibleState = collapsibleState;
batchUpdate(changes: CommentThreadChanges) {
const modified = (value: keyof CommentThreadChanges): boolean =>
Object.prototype.hasOwnProperty.call(changes, value);
if (modified('range')) { this._range = changes.range!; }
if (modified('label')) { this._label = changes.label; }
if (modified('contextValue')) { this._contextValue = changes.contextValue; }
if (modified('comments')) { this._comments = changes.comments; }
if (modified('collapseState')) { this._collapsibleState = changes.collapseState; }
}
dispose() {
@ -228,13 +226,9 @@ export class MainThreadCommentController {
updateCommentThread(commentThreadHandle: number,
threadId: string,
resource: UriComponents,
range: IRange,
label: string,
contextValue: string | undefined,
comments: modes.Comment[],
collapsibleState: modes.CommentThreadCollapsibleState): void {
changes: CommentThreadChanges): void {
let thread = this.getKnownThread(commentThreadHandle);
thread.batchUpdate(range, label, contextValue, comments, collapsibleState);
thread.batchUpdate(changes);
this._commentService.updateComments(this._uniqueId, {
added: [],
@ -430,18 +424,14 @@ export class MainThreadComments extends Disposable implements MainThreadComments
commentThreadHandle: number,
threadId: string,
resource: UriComponents,
range: IRange,
label: string,
contextValue: string | undefined,
comments: modes.Comment[],
collapsibleState: modes.CommentThreadCollapsibleState): void {
changes: CommentThreadChanges): void {
let provider = this._commentControllers.get(handle);
if (!provider) {
return undefined;
}
return provider.updateCommentThread(commentThreadHandle, threadId, resource, range, label, contextValue, comments, collapsibleState);
return provider.updateCommentThread(commentThreadHandle, threadId, resource, changes);
}
$deleteCommentThread(handle: number, commentThreadHandle: number) {

View file

@ -369,7 +369,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerSelectionRangeProvider(selector: vscode.DocumentSelector, provider: vscode.SelectionRangeProvider): vscode.Disposable {
return extHostLanguageFeatures.registerSelectionRangeProvider(extension, selector, provider);
},
registerCallHierarchyProvider(selector: vscode.DocumentSelector, provider: vscode.CallHierarchyItemProvider): vscode.Disposable {
registerCallHierarchyProvider(selector: vscode.DocumentSelector, provider: vscode.CallHierarchyProvider): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerCallHierarchyProvider(extension, selector, provider);
},

View file

@ -131,12 +131,20 @@ export interface CommentProviderFeatures {
reactionHandler?: boolean;
}
export type CommentThreadChanges = Partial<{
range: IRange,
label: string,
contextValue: string,
comments: modes.Comment[],
collapseState: modes.CommentThreadCollapsibleState
}>;
export interface MainThreadCommentsShape extends IDisposable {
$registerCommentController(handle: number, id: string, label: string): void;
$unregisterCommentController(handle: number): void;
$updateCommentControllerFeatures(handle: number, features: CommentProviderFeatures): void;
$createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, extensionId: ExtensionIdentifier): modes.CommentThread | undefined;
$updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, label: string | undefined, contextValue: string | undefined, comments: modes.Comment[], collapseState: modes.CommentThreadCollapsibleState): void;
$updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, changes: CommentThreadChanges): void;
$deleteCommentThread(handle: number, commentThreadHandle: number): void;
$onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void;
}

View file

@ -8,7 +8,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
import * as vscode from 'vscode';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as types from 'vs/workbench/api/common/extHostTypes';
import { IRawColorInfo, IWorkspaceEditDto, ICallHierarchyItemDto } from 'vs/workbench/api/common/extHost.protocol';
import { IRawColorInfo, IWorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import * as search from 'vs/workbench/contrib/search/common/search';
@ -182,22 +182,6 @@ export class ExtHostApiCommands {
],
returns: 'A promise that resolves to an array of DocumentLink-instances.'
});
this._register('vscode.executeCallHierarchyProviderIncomingCalls', this._executeCallHierarchyIncomingCallsProvider, {
description: 'Execute call hierarchy provider for incoming calls',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
],
returns: 'A promise that resolves to an array of CallHierarchyIncomingCall-instances.'
});
this._register('vscode.executeCallHierarchyProviderOutgoingCalls', this._executeCallHierarchyOutgoingCallsProvider, {
description: 'Execute call hierarchy provider for outgoing calls',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
],
returns: 'A promise that resolves to an array of CallHierarchyOutgoingCall-instances.'
});
this._register('vscode.executeDocumentColorProvider', this._executeDocumentColorProvider, {
description: 'Execute document color provider.',
args: [
@ -573,36 +557,6 @@ export class ExtHostApiCommands {
return this._commands.executeCommand<modes.ILink[]>('_executeLinkProvider', resource)
.then(tryMapWith(typeConverters.DocumentLink.to));
}
private async _executeCallHierarchyIncomingCallsProvider(resource: URI, position: types.Position): Promise<vscode.CallHierarchyIncomingCall[]> {
type IncomingCallDto = {
from: ICallHierarchyItemDto;
fromRanges: IRange[];
};
const args = { resource, position: typeConverters.Position.from(position) };
const calls = await this._commands.executeCommand<IncomingCallDto[]>('_executeCallHierarchyIncomingCalls', args);
const result: vscode.CallHierarchyIncomingCall[] = [];
for (const call of calls) {
result.push(new types.CallHierarchyIncomingCall(typeConverters.CallHierarchyItem.to(call.from), <vscode.Range[]>call.fromRanges.map(typeConverters.Range.to)));
}
return result;
}
private async _executeCallHierarchyOutgoingCallsProvider(resource: URI, position: types.Position): Promise<vscode.CallHierarchyOutgoingCall[]> {
type OutgoingCallDto = {
fromRanges: IRange[];
to: ICallHierarchyItemDto;
};
const args = { resource, position: typeConverters.Position.from(position) };
const calls = await this._commands.executeCommand<OutgoingCallDto[]>('_executeCallHierarchyOutgoingCalls', args);
const result: vscode.CallHierarchyOutgoingCall[] = [];
for (const call of calls) {
result.push(new types.CallHierarchyOutgoingCall(typeConverters.CallHierarchyItem.to(call.to), <vscode.Range[]>call.fromRanges.map(typeConverters.Range.to)));
}
return result;
}
}
function tryMapWith<T, R>(f: (x: T) => R) {

View file

@ -16,7 +16,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import * as extHostTypeConverter from 'vs/workbench/api/common/extHostTypeConverters';
import * as types from 'vs/workbench/api/common/extHostTypes';
import * as vscode from 'vscode';
import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShape } from './extHost.protocol';
import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShape, CommentThreadChanges } from './extHost.protocol';
import { ExtHostCommands } from './extHostCommands';
type ProviderHandle = number;
@ -213,12 +213,21 @@ export class ExtHostComments implements ExtHostCommentsShape, IDisposable {
}
}
type CommentThreadModification = Partial<{
range: vscode.Range,
label: string | undefined,
contextValue: string | undefined,
comments: vscode.Comment[],
collapsibleState: vscode.CommentThreadCollapsibleState
}>;
export class ExtHostCommentThread implements vscode.CommentThread {
private static _handlePool: number = 0;
readonly handle = ExtHostCommentThread._handlePool++;
public commentHandle: number = 0;
private modifications: CommentThreadModification = Object.create(null);
set threadId(id: string) {
this._id = id;
}
@ -245,6 +254,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set range(range: vscode.Range) {
if (!range.isEqual(this._range)) {
this._range = range;
this.modifications.range = range;
this._onDidUpdateCommentThread.fire();
}
}
@ -261,6 +271,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set label(label: string | undefined) {
this._label = label;
this.modifications.label = label;
this._onDidUpdateCommentThread.fire();
}
@ -272,6 +283,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set contextValue(context: string | undefined) {
this._contextValue = context;
this.modifications.contextValue = context;
this._onDidUpdateCommentThread.fire();
}
@ -281,6 +293,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set comments(newComments: vscode.Comment[]) {
this._comments = newComments;
this.modifications.comments = newComments;
this._onDidUpdateCommentThread.fire();
}
@ -292,6 +305,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set collapsibleState(newState: vscode.CommentThreadCollapsibleState) {
this._collapseState = newState;
this.modifications.collapsibleState = newState;
this._onDidUpdateCommentThread.fire();
}
@ -353,22 +367,34 @@ export class ExtHostCommentThread implements vscode.CommentThread {
this._acceptInputDisposables.value = new DisposableStore();
}
const commentThreadRange = extHostTypeConverter.Range.from(this._range);
const label = this.label;
const contextValue = this.contextValue;
const comments = this._comments.map(cmt => { return convertToModeComment(this, this._commentController, cmt, this._commentsMap); });
const collapsibleState = convertToCollapsibleState(this._collapseState);
const modified = (value: keyof CommentThreadModification): boolean =>
Object.prototype.hasOwnProperty.call(this.modifications, value);
const formattedModifications: CommentThreadChanges = {};
if (modified('range')) {
formattedModifications.range = extHostTypeConverter.Range.from(this._range);
}
if (modified('label')) {
formattedModifications.label = this.label;
}
if (modified('contextValue')) {
formattedModifications.contextValue = this.contextValue;
}
if (modified('comments')) {
formattedModifications.comments =
this._comments.map(cmt => convertToModeComment(this, this._commentController, cmt, this._commentsMap));
}
if (modified('collapsibleState')) {
formattedModifications.collapseState = convertToCollapsibleState(this._collapseState);
}
this.modifications = {};
this._proxy.$updateCommentThread(
this._commentController.handle,
this.handle,
this._id!,
this._uri,
commentThreadRange,
label,
contextValue,
comments,
collapsibleState
formattedModifications
);
}

View file

@ -471,8 +471,8 @@ class OnTypeFormattingAdapter {
class NavigateTypeAdapter {
private readonly _symbolCache: { [id: number]: vscode.SymbolInformation } = Object.create(null);
private readonly _resultCache: { [id: number]: [number, number] } = Object.create(null);
private readonly _symbolCache = new Map<number, vscode.SymbolInformation>();
private readonly _resultCache = new Map<number, [number, number]>();
private readonly _provider: vscode.WorkspaceSymbolProvider;
constructor(provider: vscode.WorkspaceSymbolProvider) {
@ -493,40 +493,38 @@ class NavigateTypeAdapter {
continue;
}
const symbol = extHostProtocol.IdObject.mixin(typeConvert.WorkspaceSymbol.from(item));
this._symbolCache[symbol._id!] = item;
this._symbolCache.set(symbol._id!, item);
result.symbols.push(symbol);
}
}
}).then(() => {
if (result.symbols.length > 0) {
this._resultCache[result._id!] = [result.symbols[0]._id!, result.symbols[result.symbols.length - 1]._id!];
this._resultCache.set(result._id!, [result.symbols[0]._id!, result.symbols[result.symbols.length - 1]._id!]);
}
return result;
});
}
resolveWorkspaceSymbol(symbol: extHostProtocol.IWorkspaceSymbolDto, token: CancellationToken): Promise<extHostProtocol.IWorkspaceSymbolDto | undefined> {
async resolveWorkspaceSymbol(symbol: extHostProtocol.IWorkspaceSymbolDto, token: CancellationToken): Promise<extHostProtocol.IWorkspaceSymbolDto | undefined> {
if (typeof this._provider.resolveWorkspaceSymbol !== 'function') {
return Promise.resolve(symbol);
return symbol;
}
const item = this._symbolCache[symbol._id!];
const item = this._symbolCache.get(symbol._id!);
if (item) {
return asPromise(() => this._provider.resolveWorkspaceSymbol!(item, token)).then(value => {
return value && mixin(symbol, typeConvert.WorkspaceSymbol.from(value), true);
});
const value = await asPromise(() => this._provider.resolveWorkspaceSymbol!(item, token));
return value && mixin(symbol, typeConvert.WorkspaceSymbol.from(value), true);
}
return Promise.resolve(undefined);
return undefined;
}
releaseWorkspaceSymbols(id: number): any {
const range = this._resultCache[id];
const range = this._resultCache.get(id);
if (range) {
for (let [from, to] = range; from <= to; from++) {
delete this._symbolCache[from];
this._symbolCache.delete(from);
}
delete this._resultCache[id];
this._resultCache.delete(id);
}
}
}
@ -1039,7 +1037,7 @@ class CallHierarchyAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.CallHierarchyItemProvider
private readonly _provider: vscode.CallHierarchyProvider
) { }
async prepareSession(uri: URI, position: IPosition, token: CancellationToken): Promise<{ sessionId: string, root: extHostProtocol.ICallHierarchyItemDto } | undefined> {
@ -1080,11 +1078,12 @@ class CallHierarchyAdapter {
}
releaseSession(sessionId: string): void {
this._cache.delete(sessionId);
this._cache.delete(sessionId.charAt(0));
}
private _cacheAndConvertItem(sessionId: string, item: vscode.CallHierarchyItem): extHostProtocol.ICallHierarchyItemDto {
const array = this._cache.get(sessionId.charAt(0))!;
private _cacheAndConvertItem(itemOrSessionId: string, item: vscode.CallHierarchyItem): extHostProtocol.ICallHierarchyItemDto {
const sessionId = itemOrSessionId.charAt(0);
const array = this._cache.get(sessionId)!;
const dto: extHostProtocol.ICallHierarchyItemDto = {
id: sessionId + String.fromCharCode(array.length),
name: item.name,
@ -1534,7 +1533,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
// --- call hierarchy
registerCallHierarchyProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CallHierarchyItemProvider): vscode.Disposable {
registerCallHierarchyProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CallHierarchyProvider): vscode.Disposable {
const handle = this._addNewAdapter(new CallHierarchyAdapter(this._documents, provider), extension);
this._proxy.$registerCallHierarchyProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);

View file

@ -20,17 +20,16 @@ import { DataTransfers } from 'vs/base/browser/dnd';
import { DragMouseEvent } from 'vs/base/browser/mouseEvent';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { MIME_BINARY } from 'vs/base/common/mime';
import { isWindows, isLinux, isWeb } from 'vs/base/common/platform';
import { isWindows } from 'vs/base/common/platform';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorIdentifier, GroupIdentifier } from 'vs/workbench/common/editor';
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
import { Disposable } from 'vs/base/common/lifecycle';
import { addDisposableListener, EventType } from 'vs/base/browser/dom';
import { addDisposableListener, EventType, asDomUri } from 'vs/base/browser/dom';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
export interface IDraggedResource {
@ -330,15 +329,8 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources:
const lineDelimiter = isWindows ? '\r\n' : '\n';
event.dataTransfer.setData(DataTransfers.TEXT, sources.map(source => source.resource.scheme === Schemas.file ? normalize(normalizeDriveLetter(source.resource.fsPath)) : source.resource.toString()).join(lineDelimiter));
const envService = accessor.get(IWorkbenchEnvironmentService);
const hasRemote = !!envService.configuration.remoteAuthority;
if (
!(isLinux && hasRemote) && // Not supported on linux remote due to chrome limitation https://github.com/microsoft/vscode-remote-release/issues/849
!isWeb // Does not seem to work anymore when running from web, the file ends up being empty (and PWA crashes)
) {
// Download URL: enables support to drag a tab as file to desktop (only single file supported)
event.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, basename(firstSource.resource), firstSource.resource.toString()].join(':'));
}
// Download URL: enables support to drag a tab as file to desktop (only single file supported)
event.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, basename(firstSource.resource), asDomUri(firstSource.resource).toString()].join(':'));
// Resource URLs: allows to drop multiple resources to a target in VS Code (not directories)
const files = sources.filter(s => !s.isDirectory);

View file

@ -5,7 +5,7 @@
import { URI } from 'vs/base/common/uri';
import { suggestFilename } from 'vs/base/common/mime';
import { memoize } from 'vs/base/common/decorators';
import { createMemoizer } from 'vs/base/common/decorators';
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
import { basenameOrAuthority, dirname } from 'vs/base/common/resources';
import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult, Verbosity, IModeSupport } from 'vs/workbench/common/editor';
@ -22,6 +22,7 @@ import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverServ
export class UntitledEditorInput extends EditorInput implements IEncodingSupport, IModeSupport {
static readonly ID: string = 'workbench.editors.untitledEditorInput';
private static readonly MEMOIZER = createMemoizer();
private cachedModel: UntitledEditorModel | null = null;
private modelResolve: Promise<UntitledEditorModel & IResolvedTextEditorModel> | null = null;
@ -43,6 +44,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
@ILabelService private readonly labelService: ILabelService
) {
super();
this._register(this.labelService.onDidChangeFormatters(() => UntitledEditorInput.MEMOIZER.clear()));
}
get hasAssociatedFilePath(): boolean {
@ -61,17 +63,17 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
return this.hasAssociatedFilePath ? basenameOrAuthority(this.resource) : this.resource.path;
}
@memoize
@UntitledEditorInput.MEMOIZER
private get shortDescription(): string {
return this.labelService.getUriBasenameLabel(dirname(this.resource));
}
@memoize
@UntitledEditorInput.MEMOIZER
private get mediumDescription(): string {
return this.labelService.getUriLabel(dirname(this.resource), { relative: true });
}
@memoize
@UntitledEditorInput.MEMOIZER
private get longDescription(): string {
return this.labelService.getUriLabel(dirname(this.resource));
}
@ -92,17 +94,17 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
}
}
@memoize
@UntitledEditorInput.MEMOIZER
private get shortTitle(): string {
return this.getName();
}
@memoize
@UntitledEditorInput.MEMOIZER
private get mediumTitle(): string {
return this.labelService.getUriLabel(this.resource, { relative: true });
}
@memoize
@UntitledEditorInput.MEMOIZER
private get longTitle(): string {
return this.labelService.getUriLabel(this.resource);
}

View file

@ -38,6 +38,8 @@ class CallHierarchyController implements IEditorContribution {
private readonly _dispoables = new DisposableStore();
private readonly _sessionDisposables = new DisposableStore();
private _widget?: CallHierarchyTreePeekWidget;
constructor(
private readonly _editor: ICodeEditor,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@ -74,24 +76,24 @@ class CallHierarchyController implements IEditorContribution {
const direction = this._storageService.getNumber(CallHierarchyController._StorageDirection, StorageScope.GLOBAL, <number>CallHierarchyDirection.CallsFrom);
Event.any<any>(this._editor.onDidChangeModel, this._editor.onDidChangeModelLanguage)(this.endCallHierarchy, this, this._sessionDisposables);
const widget = this._instantiationService.createInstance(
this._widget = this._instantiationService.createInstance(
CallHierarchyTreePeekWidget,
this._editor,
position,
direction
);
widget.showLoading();
this._widget.showLoading();
this._ctxIsVisible.set(true);
const cts = new CancellationTokenSource();
this._sessionDisposables.add(widget.onDidClose(() => {
this._sessionDisposables.add(this._widget.onDidClose(() => {
this.endCallHierarchy();
this._storageService.store(CallHierarchyController._StorageDirection, widget.direction, StorageScope.GLOBAL);
this._storageService.store(CallHierarchyController._StorageDirection, this._widget!.direction, StorageScope.GLOBAL);
}));
this._sessionDisposables.add({ dispose() { cts.dispose(true); } });
this._sessionDisposables.add(widget);
this._sessionDisposables.add(this._widget);
try {
const model = await CallHierarchyModel.create(document, position, cts.token);
@ -99,16 +101,22 @@ class CallHierarchyController implements IEditorContribution {
return; // nothing
} else if (model) {
this._sessionDisposables.add(model);
widget.showItem(model);
this._widget.showModel(model);
} else {
widget.showMessage(localize('no.item', "No results"));
this._widget.showMessage(localize('no.item', "No results"));
}
} catch (e) {
widget.showMessage(localize('error', "Failed to show call hierarchy"));
this._widget.showMessage(localize('error', "Failed to show call hierarchy"));
console.error(e);
}
}
toggleCallHierarchyDirection(): void {
if (this._widget) {
this._widget.toggleDirection();
}
}
endCallHierarchy(): void {
this._sessionDisposables.clear();
this._ctxIsVisible.set(false);
@ -135,17 +143,38 @@ registerEditorAction(class extends EditorAction {
primary: KeyMod.Shift + KeyMod.Alt + KeyCode.KEY_H
},
precondition: ContextKeyExpr.and(
_ctxCallHierarchyVisible.negate(),
_ctxHasCompletionItemProvider,
PeekContext.notInPeekEditor
)
});
}
async run(_accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise<void> {
async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
return CallHierarchyController.get(editor).startCallHierarchy();
}
});
registerEditorAction(class extends EditorAction {
constructor() {
super({
id: 'editor.toggleCallHierarchy',
label: localize('title.toggle', "Toggle Call Hierarchy"),
alias: 'Toggle Call Hierarchy',
kbOpts: {
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyMod.Shift + KeyMod.Alt + KeyCode.KEY_H
},
precondition: _ctxCallHierarchyVisible
});
}
async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
return CallHierarchyController.get(editor).toggleCallHierarchyDirection();
}
});
registerEditorCommand(new class extends EditorCommand {

View file

@ -10,7 +10,6 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry';
import { URI } from 'vs/base/common/uri';
import { IPosition } from 'vs/editor/common/core/position';
import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
@ -105,14 +104,3 @@ export class CallHierarchyModel {
}
}
export async function provideIncomingCalls(model: ITextModel, position: IPosition, token: CancellationToken): Promise<IncomingCall[]> {
throw new Error();
}
export async function provideOutgoingCalls(model: ITextModel, position: IPosition, token: CancellationToken): Promise<OutgoingCall[]> {
throw new Error();
}
registerDefaultLanguageCommand('_executeCallHierarchyIncomingCalls', async (model, position) => provideIncomingCalls(model, position, CancellationToken.None));
registerDefaultLanguageCommand('_executeCallHierarchyOutgoingCalls', async (model, position) => provideOutgoingCalls(model, position, CancellationToken.None));

View file

@ -42,19 +42,14 @@ const enum State {
class ChangeHierarchyDirectionAction extends Action {
constructor(direction: CallHierarchyDirection, updateDirection: (direction: CallHierarchyDirection) => void) {
constructor(getDirection: () => CallHierarchyDirection, toggleDirection: () => void) {
super('', undefined, '', true, () => {
if (direction === CallHierarchyDirection.CallsTo) {
direction = CallHierarchyDirection.CallsFrom;
} else {
direction = CallHierarchyDirection.CallsTo;
}
updateDirection(direction);
toggleDirection();
update();
return Promise.resolve();
});
const update = () => {
if (direction === CallHierarchyDirection.CallsFrom) {
if (getDirection() === CallHierarchyDirection.CallsFrom) {
this.label = localize('toggle.from', "Showing Calls");
this.class = 'calls-from';
} else {
@ -198,6 +193,7 @@ export class CallHierarchyTreePeekWidget extends PeekViewWidget {
addClass(treeContainer, 'tree');
container.appendChild(treeContainer);
const options: IAsyncDataTreeOptions<callHTree.Call, FuzzyScore> = {
sorter: new callHTree.Sorter(),
identityProvider: new callHTree.IdentityProvider(() => this._direction),
ariaLabel: localize('tree.aria', "Call Hierarchy"),
expandOnlyOnTwistieClick: true,
@ -269,12 +265,8 @@ export class CallHierarchyTreePeekWidget extends PeekViewWidget {
let previewUri: URI;
if (this._direction === CallHierarchyDirection.CallsFrom) {
// outgoing calls: show caller and highlight focused calls
const parent = this._tree.getParentElement(element);
if (parent instanceof callHTree.Call) {
previewUri = parent.item.uri;
} else {
previewUri = parent.root.uri;
}
previewUri = element.parent ? element.parent.item.uri : element.model.root.uri;
} else {
// incoming calls: show caller and highlight focused calls
previewUri = element.item.uri;
@ -365,42 +357,47 @@ export class CallHierarchyTreePeekWidget extends PeekViewWidget {
this._message.focus();
}
async showItem(item: CallHierarchyModel): Promise<void> {
async showModel(model: CallHierarchyModel): Promise<void> {
this._show();
const viewState = this._treeViewStates.get(this._direction);
await this._tree.setInput(item, viewState);
await this._tree.setInput(model, viewState);
const root = <ITreeNode<callHTree.Call>>this._tree.getNode(item).children[0];
const root = <ITreeNode<callHTree.Call>>this._tree.getNode(model).children[0];
await this._tree.expand(root.element);
if (root.children.length === 0) {
//
this.showMessage(this._direction === CallHierarchyDirection.CallsFrom
? localize('empt.callsFrom', "No calls from '{0}'", item.root.name)
: localize('empt.callsTo', "No callers of '{0}'", item.root.name));
? localize('empt.callsFrom', "No calls from '{0}'", model.root.name)
: localize('empt.callsTo', "No callers of '{0}'", model.root.name));
} else {
this._parent.dataset['state'] = State.Data;
this._tree.domFocus();
this._tree.setFocus([root.children[0].element]);
if (!viewState) {
this._tree.setFocus([root.children[0].element]);
}
}
if (!this._changeDirectionAction) {
const changeDirection = async (newDirection: CallHierarchyDirection) => {
if (this._direction !== newDirection) {
this._treeViewStates.set(this._direction, this._tree.getViewState());
this._direction = newDirection;
await this.showItem(item);
}
};
this._changeDirectionAction = new ChangeHierarchyDirectionAction(this._direction, changeDirection);
this._changeDirectionAction = new ChangeHierarchyDirectionAction(() => this._direction, () => this.toggleDirection());
this._disposables.add(this._changeDirectionAction);
this._actionbarWidget!.push(this._changeDirectionAction, { icon: true, label: false });
}
}
async toggleDirection(): Promise<void> {
const model = this._tree.getInput();
if (model) {
const newDirection = this._direction === CallHierarchyDirection.CallsTo ? CallHierarchyDirection.CallsFrom : CallHierarchyDirection.CallsTo;
this._treeViewStates.set(this._direction, this._tree.getViewState());
this._direction = newDirection;
await this.showModel(model);
}
}
private _show() {
if (!this._isShowing) {
this.editor.revealLineInCenterIfOutsideViewport(this._where.lineNumber, ScrollType.Smooth);

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree';
import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeSorter } from 'vs/base/browser/ui/tree/tree';
import { CallHierarchyItem, CallHierarchyDirection, CallHierarchyModel, } from 'vs/workbench/contrib/callHierarchy/browser/callHierarchy';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
@ -11,13 +11,24 @@ import { FuzzyScore, createMatches } from 'vs/base/common/filters';
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { SymbolKinds, Location } from 'vs/editor/common/modes';
import * as dom from 'vs/base/browser/dom';
import { compare } from 'vs/base/common/strings';
import { Range } from 'vs/editor/common/core/range';
export class Call {
constructor(
readonly item: CallHierarchyItem,
readonly locations: Location[],
readonly model: CallHierarchyModel
readonly model: CallHierarchyModel,
readonly parent: Call | undefined
) { }
static compare(a: Call, b: Call): number {
let res = compare(a.item.uri.toString(), b.item.uri.toString());
if (res === 0) {
res = Range.compareRangesUsingStarts(a.item.range, b.item.range);
}
return res;
}
}
export class DataSource implements IAsyncDataSource<CallHierarchyModel, Call> {
@ -32,43 +43,40 @@ export class DataSource implements IAsyncDataSource<CallHierarchyModel, Call> {
async getChildren(element: CallHierarchyModel | Call): Promise<Call[]> {
if (element instanceof CallHierarchyModel) {
return [new Call(element.root, [], element)];
return [new Call(element.root, [], element, undefined)];
}
const results: Call[] = [];
const { model, item } = element;
if (this.getDirection() === CallHierarchyDirection.CallsFrom) {
await this._getOutgoingCalls(element.model, element.item, results);
return (await model.resolveOutgoingCalls(item, CancellationToken.None)).map(call => {
return new Call(
call.to,
call.fromRanges.map(range => ({ range, uri: item.uri })),
model,
element
);
});
} else {
await this._getIncomingCalls(element.model, element.item, results);
}
return results;
}
private async _getOutgoingCalls(model: CallHierarchyModel, item: CallHierarchyItem, bucket: Call[]): Promise<void> {
const outgoingCalls = await model.resolveOutgoingCalls(item, CancellationToken.None);
for (const call of outgoingCalls) {
bucket.push(new Call(
call.to,
call.fromRanges.map(range => ({ range, uri: item.uri })),
model
));
return (await model.resolveIncomingCalls(item, CancellationToken.None)).map(call => {
return new Call(
call.from,
call.fromRanges.map(range => ({ range, uri: call.from.uri })),
model,
element
);
});
}
}
private async _getIncomingCalls(model: CallHierarchyModel, item: CallHierarchyItem, bucket: Call[]): Promise<void> {
const incomingCalls = await model.resolveIncomingCalls(item, CancellationToken.None);
for (const call of incomingCalls) {
bucket.push(new Call(
call.from,
call.fromRanges.map(range => ({ range, uri: call.from.uri })),
model
));
}
}
}
export class Sorter implements ITreeSorter<Call> {
compare(element: Call, otherElement: Call): number {
return Call.compare(element, otherElement);
}
}
export class IdentityProvider implements IIdentityProvider<Call> {
@ -77,7 +85,11 @@ export class IdentityProvider implements IIdentityProvider<Call> {
) { }
getId(element: Call): { toString(): string; } {
return this.getDirection() + '->' + element.item.id;
let res = this.getDirection() + JSON.stringify(element.item.uri) + JSON.stringify(element.item.range);
if (element.parent) {
res += this.getId(element.parent);
}
return res;
}
}

View file

@ -6,4 +6,5 @@
.monaco-editor .accessibilityHelpWidget {
padding: 10px;
vertical-align: middle;
}
overflow: auto;
}

View file

@ -263,11 +263,13 @@ class AccessibilityHelpWidget extends Widget implements IOverlayWidget {
private _layout(): void {
let editorLayout = this._editor.getLayoutInfo();
let top = Math.round((editorLayout.height - AccessibilityHelpWidget.HEIGHT) / 2);
this._domNode.setTop(top);
const width = Math.min(editorLayout.width - 40, AccessibilityHelpWidget.WIDTH);
const height = Math.min(editorLayout.height - 40, AccessibilityHelpWidget.HEIGHT);
let left = Math.round((editorLayout.width - AccessibilityHelpWidget.WIDTH) / 2);
this._domNode.setLeft(left);
this._domNode.setTop(Math.round((editorLayout.height - height) / 2));
this._domNode.setLeft(Math.round((editorLayout.width - width) / 2));
this._domNode.setWidth(width);
this._domNode.setHeight(height);
}
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { ParseError, parse } from 'vs/base/common/json';
import { ParseError, parse, getNodeType } from 'vs/base/common/json';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import * as types from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
@ -91,10 +91,14 @@ export class LanguageConfigurationFileHandler {
private _handleConfigFile(languageIdentifier: LanguageIdentifier, configFileLocation: URI): void {
this._fileService.readFile(configFileLocation).then((contents) => {
const errors: ParseError[] = [];
const configuration = <ILanguageConfiguration>parse(contents.value.toString(), errors);
let configuration = <ILanguageConfiguration>parse(contents.value.toString(), errors);
if (errors.length) {
console.error(nls.localize('parseErrors', "Errors parsing {0}: {1}", configFileLocation.toString(), errors.map(e => (`[${e.offset}, ${e.length}] ${getParseErrorMessage(e.error)}`)).join('\n')));
}
if (getNodeType(configuration) !== 'object') {
console.error(nls.localize('formatError', "{0}: Invalid format, JSON object expected.", configFileLocation.toString()));
configuration = {};
}
this._handleConfig(languageIdentifier, configuration);
}, (err) => {
console.error(err);

View file

@ -57,7 +57,7 @@ export class CommentNode extends Disposable {
protected toolbar: ToolBar | undefined;
private _commentFormActions: CommentFormActions | null = null;
private readonly _onDidDelete = new Emitter<CommentNode>();
private readonly _onDidClick = new Emitter<CommentNode>();
public get domNode(): HTMLElement {
return this._domNode;
@ -89,7 +89,7 @@ export class CommentNode extends Disposable {
this._contextKeyService = contextKeyService.createScoped(this._domNode);
this._commentContextValue = this._contextKeyService.createKey('comment', comment.contextValue);
this._domNode.tabIndex = 0;
this._domNode.tabIndex = -1;
const avatar = dom.append(this._domNode, dom.$('div.avatar-container'));
if (comment.userIconPath) {
const img = <HTMLImageElement>dom.append(avatar, dom.$('img.avatar'));
@ -111,10 +111,12 @@ export class CommentNode extends Disposable {
this._domNode.setAttribute('aria-label', `${comment.userName}, ${comment.body.value}`);
this._domNode.setAttribute('role', 'treeitem');
this._clearTimeout = null;
this._register(dom.addDisposableListener(this._domNode, dom.EventType.CLICK, () => this.isEditing || this._onDidClick.fire(this)));
}
public get onDidDelete(): Event<CommentNode> {
return this._onDidDelete.event;
public get onDidClick(): Event<CommentNode> {
return this._onDidClick.event;
}
private createHeader(commentDetailsContainer: HTMLElement): void {
@ -430,19 +432,31 @@ export class CommentNode extends Disposable {
this._commentFormActions.setActions(menu);
}
setFocus(focused: boolean, visible: boolean = false) {
if (focused) {
this._domNode.focus();
this._actionsToolbarContainer.classList.remove('hidden');
this._actionsToolbarContainer.classList.add('tabfocused');
this._domNode.tabIndex = 0;
} else {
if (this._actionsToolbarContainer.classList.contains('tabfocused') && !this._actionsToolbarContainer.classList.contains('mouseover')) {
this._actionsToolbarContainer.classList.add('hidden');
this._domNode.tabIndex = -1;
}
this._actionsToolbarContainer.classList.remove('tabfocused');
}
}
private registerActionBarListeners(actionsContainer: HTMLElement): void {
this._register(dom.addDisposableListener(this._domNode, 'mouseenter', () => {
actionsContainer.classList.remove('hidden');
actionsContainer.classList.add('mouseover');
}));
this._register(dom.addDisposableListener(this._domNode, 'focus', () => {
actionsContainer.classList.remove('hidden');
}));
this._register(dom.addDisposableListener(this._domNode, 'mouseleave', () => {
if (!this._domNode.contains(document.activeElement)) {
if (actionsContainer.classList.contains('mouseover') && !actionsContainer.classList.contains('tabfocused')) {
actionsContainer.classList.add('hidden');
}
actionsContainer.classList.remove('mouseover');
}));
}

View file

@ -46,6 +46,8 @@ import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/comme
import { SimpleCommentEditor } from './simpleCommentEditor';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration';
const COLLAPSE_ACTION_CLASS = 'expand-review-action';
@ -84,6 +86,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
private _commentEditorIsEmpty!: IContextKey<boolean>;
private _commentFormActions!: CommentFormActions;
private _scopedInstatiationService: IInstantiationService;
private _focusedComment: number | undefined = undefined;
public get owner(): string {
return this._owner;
@ -268,6 +271,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}
public collapse(): Promise<void> {
this._commentThread.collapsibleState = modes.CommentThreadCollapsibleState.Collapsed;
if (this._commentThread.comments && this._commentThread.comments.length === 0) {
this.deleteCommentThread();
return Promise.resolve();
@ -286,11 +290,13 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
toggleExpand(lineNumber: number) {
if (this._isExpanded) {
this._commentThread.collapsibleState = modes.CommentThreadCollapsibleState.Collapsed;
this.hide();
if (!this._commentThread.comments || !this._commentThread.comments.length) {
this.deleteCommentThread();
}
} else {
this._commentThread.collapsibleState = modes.CommentThreadCollapsibleState.Expanded;
this.show({ lineNumber: lineNumber, column: 1 }, 2);
}
}
@ -379,6 +385,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
} else {
this._commentThreadContextValue.reset();
}
this.setFocusedComment(this._focusedComment);
}
protected _onWidth(widthInPixel: number): void {
@ -401,6 +409,21 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
this._commentsElement = dom.append(this._bodyElement, dom.$('div.comments-container'));
this._commentsElement.setAttribute('role', 'presentation');
this._commentsElement.tabIndex = 0;
this._disposables.add(dom.addDisposableListener(this._commentsElement, dom.EventType.KEY_DOWN, (e) => {
let event = new StandardKeyboardEvent(e as KeyboardEvent);
if (event.equals(KeyCode.UpArrow) || event.equals(KeyCode.DownArrow)) {
const moveFocusWithinBounds = (change: number): number => {
if (this._focusedComment === undefined && change >= 0) { return 0; }
if (this._focusedComment === undefined && change < 0) { return this._commentElements.length - 1; }
let newIndex = this._focusedComment! + change;
return Math.min(Math.max(0, newIndex), this._commentElements.length - 1);
};
this.setFocusedComment(event.equals(KeyCode.UpArrow) ? moveFocusWithinBounds(-1) : moveFocusWithinBounds(1));
}
}));
this._commentElements = [];
if (this._commentThread.comments) {
@ -565,6 +588,19 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}));
}
private setFocusedComment(value: number | undefined) {
if (this._focusedComment !== undefined) {
this._commentElements[this._focusedComment]?.setFocus(false);
}
if (this._commentElements.length === 0 || value === undefined) {
this._focusedComment = undefined;
} else {
this._focusedComment = Math.min(value, this._commentElements.length - 1);
this._commentElements[this._focusedComment].setFocus(true);
}
}
private getActiveComment(): CommentNode | ReviewZoneWidget {
return this._commentElements.filter(node => node.isEditing)[0] || this;
}
@ -613,25 +649,9 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
this._markdownRenderer);
this._disposables.add(newCommentNode);
this._disposables.add(newCommentNode.onDidDelete(deletedNode => {
const deletedNodeId = deletedNode.comment.uniqueIdInThread;
const deletedElementIndex = arrays.firstIndex(this._commentElements, commentNode => commentNode.comment.uniqueIdInThread === deletedNodeId);
if (deletedElementIndex > -1) {
this._commentElements.splice(deletedElementIndex, 1);
}
const deletedCommentIndex = arrays.firstIndex(this._commentThread.comments!, comment => comment.uniqueIdInThread === deletedNodeId);
if (deletedCommentIndex > -1) {
this._commentThread.comments!.splice(deletedCommentIndex, 1);
}
this._commentsElement.removeChild(deletedNode.domNode);
deletedNode.dispose();
if (this._commentThread.comments!.length === 0) {
this.dispose();
}
}));
this._disposables.add(newCommentNode.onDidClick(clickedNode =>
this.setFocusedComment(arrays.firstIndex(this._commentElements, commentNode => commentNode.comment.uniqueIdInThread === clickedNode.comment.uniqueIdInThread))
));
return newCommentNode;
}

View file

@ -2101,7 +2101,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
protected getWorkspaceFolderExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> {
return Promise.resolve(this.fileService.readFile(extensionsFileResource))
.then(content => {
return (<IExtensionsConfigContent>json.parse(content.value.toString()));
return (<IExtensionsConfigContent>json.parse(content.value.toString()) || {});
}, err => ({ recommendations: [], unwantedRecommendations: [] }));
}

View file

@ -172,7 +172,7 @@ class HelpItem implements IHelpItem {
const action = await this.quickInputService.pick(actions, { placeHolder: nls.localize('pickRemoteExtension', "Select url to open") });
if (action) {
await this.openerService.open(URI.parse(action.label));
await this.openerService.open(URI.parse(action.description));
}
} else {
await this.openerService.open(URI.parse(this.values[0].url));

View file

@ -114,7 +114,11 @@ export class SCMStatusController implements IWorkbenchContribution {
this.disposables.push(disposable);
if (!this.focusedRepository) {
this.onDidFocusRepository(repository);
if (this.editorService.activeEditor) {
this.onDidActiveEditorChange();
} else {
this.onDidFocusRepository(repository);
}
}
}

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { parse as jsonParse } from 'vs/base/common/json';
import { parse as jsonParse, getNodeType } from 'vs/base/common/json';
import { forEach } from 'vs/base/common/collections';
import { localize } from 'vs/nls';
import { extname, basename } from 'vs/base/common/path';
@ -203,7 +203,7 @@ export class SnippetFile {
if (!this._loadPromise) {
this._loadPromise = Promise.resolve(this._fileService.readFile(this.location)).then(content => {
const data = <JsonSerializedSnippets>jsonParse(content.value.toString());
if (typeof data === 'object') {
if (getNodeType(data) === 'object') {
forEach(data, entry => {
const { key: name, value: scopeOrTemplate } = entry;
if (isJsonSerializedSnippet(scopeOrTemplate)) {

View file

@ -14,6 +14,7 @@ import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/termina
const SHELL_EXECUTABLES = [
'cmd.exe',
'powershell.exe',
'pwsh.exe',
'bash.exe',
'wsl.exe',
'ubuntu.exe',

View file

@ -8,14 +8,21 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution } from 'vs/workbench/contrib/update/browser/update';
import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, CheckForVSCodeUpdateAction, CONTEXT_UPDATE_STATE } from 'vs/workbench/contrib/update/browser/update';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import product from 'vs/platform/product/common/product';
import { StateType } from 'vs/platform/update/common/update';
const workbench = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbench.registerWorkbenchContribution(ProductContribution, LifecyclePhase.Restored);
workbench.registerWorkbenchContribution(UpdateContribution, LifecyclePhase.Restored);
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
// Editor
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions)
.registerWorkbenchAction(new SyncActionDescriptor(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), 'Show Release Notes');
actionRegistry
.registerWorkbenchAction(new SyncActionDescriptor(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), `${product.nameShort}: Show Release Notes`, product.nameShort);
actionRegistry
.registerWorkbenchAction(new SyncActionDescriptor(CheckForVSCodeUpdateAction, CheckForVSCodeUpdateAction.ID, CheckForVSCodeUpdateAction.LABEL), `${product.nameShort}: Check for Update`, product.nameShort, CONTEXT_UPDATE_STATE.isEqualTo(StateType.Idle));

View file

@ -26,11 +26,11 @@ import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/cont
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { FalseContext } from 'vs/platform/contextkey/common/contextkeys';
import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update';
import { ShowCurrentReleaseNotesActionId, CheckForVSCodeUpdateActionId } from 'vs/workbench/contrib/update/common/update';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IProductService } from 'vs/platform/product/common/productService';
const CONTEXT_UPDATE_STATE = new RawContextKey<string>('updateState', StateType.Uninitialized);
export const CONTEXT_UPDATE_STATE = new RawContextKey<string>('updateState', StateType.Idle);
let releaseNotesManager: ReleaseNotesManager | undefined = undefined;
@ -469,3 +469,23 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
});
}
}
export class CheckForVSCodeUpdateAction extends Action {
static readonly ID = CheckForVSCodeUpdateActionId;
static LABEL = nls.localize('checkForUpdates', "Check for Updates...");
constructor(
id: string,
label: string,
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IUpdateService private readonly updateService: IUpdateService,
) {
super(id, label, undefined, true);
}
run(): Promise<void> {
return this.updateService.checkForUpdates(this.workbenchEnvironmentService.configuration.sessionId);
}
}

View file

@ -3,4 +3,5 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const ShowCurrentReleaseNotesActionId = 'update.showCurrentReleaseNotes';
export const ShowCurrentReleaseNotesActionId = 'update.showCurrentReleaseNotes';
export const CheckForVSCodeUpdateActionId = 'update.checkForVSCodeUpdate';

View file

@ -121,7 +121,7 @@ export class TrustedDomainsFileSystemProvider implements IFileSystemProvider, IW
this.storageService.store('http.linkProtectionTrustedDomainsContent', trustedDomainsContent, StorageScope.GLOBAL);
this.storageService.store(
'http.linkProtectionTrustedDomains',
JSON.stringify(trustedDomains),
JSON.stringify(trustedDomains) || '',
StorageScope.GLOBAL
);
} catch (err) { }

View file

@ -409,7 +409,7 @@ export class ConfigurationEditingService {
return false;
}
const parseErrors: json.ParseError[] = [];
json.parse(model.getValue(), parseErrors);
json.parse(model.getValue(), parseErrors, { allowTrailingComma: true, allowEmptyContent: true });
return parseErrors.length > 0;
}

View file

@ -98,7 +98,7 @@ export class JSONEditingService implements IJSONEditingService {
private hasParseErrors(model: ITextModel): boolean {
const parseErrors: json.ParseError[] = [];
json.parse(model.getValue(), parseErrors);
json.parse(model.getValue(), parseErrors, { allowTrailingComma: true, allowEmptyContent: true });
return parseErrors.length > 0;
}

View file

@ -175,7 +175,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
@memoize
get webviewExternalEndpoint(): string {
// TODO: get fallback from product.json
return (this.options.webviewEndpoint || 'https://{{uuid}}.vscode-webview-test.com/{{commit}}').replace('{{commit}}', product.commit || 'c58aaab8a1cc22a7139b761166a0d4f37d41e998');
return (this.options.webviewEndpoint || 'https://{{uuid}}.vscode-webview-test.com/{{commit}}').replace('{{commit}}', product.commit || 'b53811e67e65c6a564a80e1c412ca2b13de02907');
}
@memoize

View file

@ -21,7 +21,7 @@ export class NativeWorkbenchEnvironmentService extends EnvironmentService implem
get webviewExternalEndpoint(): string {
const baseEndpoint = 'https://{{uuid}}.vscode-webview-test.com/{{commit}}';
return baseEndpoint.replace('{{commit}}', product.commit || 'c58aaab8a1cc22a7139b761166a0d4f37d41e998');
return baseEndpoint.replace('{{commit}}', product.commit || 'b53811e67e65c6a564a80e1c412ca2b13de02907');
}
@memoize

View file

@ -51,7 +51,7 @@ class ExtensionManifestParser extends ExtensionManifestHandler {
return pfs.readFile(this._absoluteManifestPath).then((manifestContents) => {
const errors: json.ParseError[] = [];
const manifest = json.parse(manifestContents.toString(), errors);
if (!!manifest && errors.length === 0) {
if (errors.length === 0 && json.getNodeType(manifest) === 'object') {
if (manifest.__metadata) {
manifest.uuid = manifest.__metadata.id;
}
@ -104,6 +104,9 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
this._log.error(this._absoluteFolderPath, nls.localize('jsonsParseReportErrors', "Failed to parse {0}: {1}.", localized, getParseErrorMessage(error.error)));
});
};
const reportInvalidFormat = (localized: string | null): void => {
this._log.error(this._absoluteFolderPath, nls.localize('jsonInvalidFormat', "Invalid format {0}: JSON object expected.", localized));
};
let extension = path.extname(this._absoluteManifestPath);
let basename = this._absoluteManifestPath.substr(0, this._absoluteManifestPath.length - extension.length);
@ -118,6 +121,9 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
if (errors.length > 0) {
reportErrors(translationPath, errors);
return { values: undefined, default: `${basename}.nls.json` };
} else if (json.getNodeType(translationBundle) !== 'object') {
reportInvalidFormat(translationPath);
return { values: undefined, default: `${basename}.nls.json` };
} else {
let values = translationBundle.contents ? translationBundle.contents.package : undefined;
return { values: values, default: `${basename}.nls.json` };
@ -140,6 +146,9 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
if (errors.length > 0) {
reportErrors(messageBundle.localized, errors);
return { values: undefined, default: messageBundle.original };
} else if (json.getNodeType(messages) !== 'object') {
reportInvalidFormat(messageBundle.localized);
return { values: undefined, default: messageBundle.original };
}
return { values: messages, default: messageBundle.original };
}, (err) => {
@ -161,6 +170,9 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
if (errors.length > 0) {
reportErrors(localizedMessages.default, errors);
return extensionDescription;
} else if (json.getNodeType(localizedMessages) !== 'object') {
reportInvalidFormat(localizedMessages.default);
return extensionDescription;
}
const localized = localizedMessages.values || Object.create(null);
ExtensionManifestNLSReplacer._replaceNLStrings(this._nlsConfig, extensionDescription, localized, defaults, this._log, this._absoluteFolderPath);

View file

@ -19,7 +19,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { RunOnceScheduler } from 'vs/base/common/async';
import { parse } from 'vs/base/common/json';
import { parse, getNodeType } from 'vs/base/common/json';
import * as objects from 'vs/base/common/objects';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { Registry } from 'vs/platform/registry/common/platform';
@ -482,9 +482,13 @@ class UserKeyboardLayout extends Disposable {
try {
const content = await this.fileService.readFile(this.keyboardLayoutResource);
const value = parse(content.value.toString());
const layoutInfo = value.layout;
const mappings = value.rawMapping;
this._keyboardLayout = KeymapInfo.createKeyboardLayoutFromDebugInfo(layoutInfo, mappings, true);
if (getNodeType(value) === 'object') {
const layoutInfo = value.layout;
const mappings = value.rawMapping;
this._keyboardLayout = KeymapInfo.createKeyboardLayoutFromDebugInfo(layoutInfo, mappings, true);
} else {
this._keyboardLayout = null;
}
} catch (e) {
this._keyboardLayout = null;
}

View file

@ -189,8 +189,10 @@ function _loadIconThemeDocument(fileService: IFileService, location: URI): Promi
return fileService.readFile(location).then((content) => {
let errors: Json.ParseError[] = [];
let contentValue = Json.parse(content.value.toString(), errors);
if (errors.length > 0 || !contentValue) {
if (errors.length > 0) {
return Promise.reject(new Error(nls.localize('error.cannotparseicontheme', "Problems parsing file icons file: {0}", errors.map(e => getParseErrorMessage(e.error)).join(', '))));
} else if (Json.getNodeType(contentValue) !== 'object') {
return Promise.reject(new Error(nls.localize('error.invalidformat', "Invalid format for file icons theme file: Object expected.")));
}
return Promise.resolve(contentValue);
});

View file

@ -302,6 +302,8 @@ function _loadColorTheme(fileService: IFileService, themeLocation: URI, resultRu
let contentValue = Json.parse(content.value.toString(), errors);
if (errors.length > 0) {
return Promise.reject(new Error(nls.localize('error.cannotparsejson', "Problems parsing JSON theme file: {0}", errors.map(e => getParseErrorMessage(e.error)).join(', '))));
} else if (Json.getNodeType(contentValue) !== 'object') {
return Promise.reject(new Error(nls.localize('error.invalidformat', "Invalid format for JSON theme file: Object expected.")));
}
let includeCompletes: Promise<any> = Promise.resolve(null);
if (contentValue.include) {

View file

@ -856,37 +856,4 @@ suite('ExtHostLanguageFeatureCommands', function () {
assert.equal(value.length, 1);
assert.ok(value[0].parent);
});
// --- call hierarcht
test('Call Hierarchy, back and forth', async function () {
disposables.push(extHost.registerCallHierarchyProvider(nullExtensionDescription, defaultSelector, new class implements vscode.CallHierarchyItemProvider {
prepareCallHierarchy(document: vscode.TextDocument) {
return new types.CallHierarchyItem(types.SymbolKind.Array, 'ROOT', '', document.uri, new types.Range(0, 0, 2, 0), new types.Range(0, 0, 2, 0));
}
provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CallHierarchyIncomingCall[]> {
return [
new types.CallHierarchyIncomingCall(new types.CallHierarchyItem(types.SymbolKind.Array, 'IN', '', item.uri, new types.Range(0, 0, 2, 0), new types.Range(0, 0, 2, 0)), [new types.Range(0, 0, 0, 0)]),
];
}
provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CallHierarchyOutgoingCall[]> {
return [
new types.CallHierarchyOutgoingCall(new types.CallHierarchyItem(types.SymbolKind.Array, 'OUT', '', item.uri, new types.Range(0, 0, 2, 0), new types.Range(0, 0, 2, 0)), [new types.Range(0, 0, 0, 0)]),
];
}
}));
await rpcProtocol.sync();
// let incoming = await commands.executeCommand<vscode.CallHierarchyIncomingCall[]>('vscode.executeCallHierarchyProviderIncomingCalls', model.uri, new types.Position(0, 10));
// assert.equal(incoming.length, 1);
// assert.ok(incoming[0].from instanceof types.CallHierarchyItem);
// assert.equal(incoming[0].from.name, 'IN');
// let outgoing = await commands.executeCommand<vscode.CallHierarchyOutgoingCall[]>('vscode.executeCallHierarchyProviderOutgoingCalls', model.uri, new types.Position(0, 10));
// assert.equal(outgoing.length, 1);
// assert.ok(outgoing[0].to instanceof types.CallHierarchyItem);
// assert.equal(outgoing[0].to.name, 'OUT');
});
});

View file

@ -8510,10 +8510,10 @@ typescript-formatter@7.1.0:
commandpost "^1.0.0"
editorconfig "^0.15.0"
typescript@3.7.0-dev.20191017:
version "3.7.0-dev.20191017"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.0-dev.20191017.tgz#e61440dd445edea6d7b9a699e7c5d5fbcd1906f2"
integrity sha512-Yi0lCPEN0cn9Gp8TEEkPpgKNR5SWAmx9Hmzzz+oEuivw6amURqRGynaLyFZkMA9iMsvYG5LLqhdlFO3uu5ZT/w==
typescript@3.7.1-rc:
version "3.7.1-rc"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.1-rc.tgz#2054b872d67f8dc732e36c1df397f9327f37ada0"
integrity sha512-2rMtWppLsaPvmpXsoIAXWDBQVnJMw1ITGGSnidMuayLj9iCmMRT69ncKZw/Mk5rXfJkilApKucWQZxproALoRw==
typescript@^2.6.2:
version "2.6.2"