mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-30 05:26:46 +01:00
Merge branch 'master' into ben/workspace-api
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"name": "ms-vscode.node-debug",
|
||||
"version": "1.20.3",
|
||||
"repo": "https://github.com/Microsoft/vscode-node-debug"
|
||||
},
|
||||
{
|
||||
"name": "ms-vscode.node-debug2",
|
||||
"version": "1.20.1",
|
||||
"repo": "https://github.com/Microsoft/vscode-node-debug2"
|
||||
}
|
||||
]
|
||||
@@ -198,7 +198,7 @@ const hygiene = exports.hygiene = (some, options) => {
|
||||
tsfmt.processString(file.path, file.contents.toString('utf8'), {
|
||||
verify: true,
|
||||
tsfmt: true,
|
||||
// verbose: true
|
||||
verbose: true
|
||||
}).then(result => {
|
||||
if (result.error) {
|
||||
console.error(result.message);
|
||||
|
||||
@@ -44,14 +44,13 @@ const nodeModules = ['electron', 'original-fs']
|
||||
|
||||
// Build
|
||||
|
||||
const builtInExtensions = [
|
||||
{ name: 'ms-vscode.node-debug', version: '1.20.3' },
|
||||
{ name: 'ms-vscode.node-debug2', version: '1.20.1' }
|
||||
];
|
||||
const builtInExtensions = require('./builtInExtensions');
|
||||
|
||||
const excludedExtensions = [
|
||||
'vscode-api-tests',
|
||||
'vscode-colorize-tests'
|
||||
'vscode-colorize-tests',
|
||||
'ms-vscode.node-debug',
|
||||
'ms-vscode.node-debug2',
|
||||
];
|
||||
|
||||
const vscodeEntryPoints = _.flatten([
|
||||
@@ -584,3 +583,17 @@ gulp.task('generate-vscode-configuration', () => {
|
||||
console.error(e.toString());
|
||||
});
|
||||
});
|
||||
|
||||
//#region Built-In Extensions
|
||||
gulp.task('clean-builtin-extensions', util.rimraf('.build/builtInExtensions'));
|
||||
gulp.task('download-builtin-extensions', ['clean-builtin-extensions'], function () {
|
||||
const marketplaceExtensions = es.merge(...builtInExtensions.map(extension => {
|
||||
return ext.fromMarketplace(extension.name, extension.version)
|
||||
.pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`));
|
||||
}));
|
||||
|
||||
return marketplaceExtensions
|
||||
.pipe(util.setExecutableBit(['**/*.sh']))
|
||||
.pipe(vfs.dest('.build/builtInExtensions'));
|
||||
});
|
||||
//#endregion
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
|
||||
function isUpToDate(extension) {
|
||||
const packagePath = path.join(root, '.build', 'builtInExtensions', extension.name, 'package.json');
|
||||
if (!fs.existsSync(packagePath)) {
|
||||
return false;
|
||||
}
|
||||
const packageContents = fs.readFileSync(packagePath);
|
||||
try {
|
||||
const diskVersion = JSON.parse(packageContents).version;
|
||||
return (diskVersion === extension.version);
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const builtInExtensions = require('../builtInExtensions');
|
||||
builtInExtensions.forEach((extension) => {
|
||||
if (!isUpToDate(extension)) {
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
process.exit(0);
|
||||
Binary file not shown.
-7087
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": "node-debug-placeholder",
|
||||
"version": "1.6.0",
|
||||
"publisher": "vscode",
|
||||
"engines": {
|
||||
"vscode": "1.6.x"
|
||||
}
|
||||
}
|
||||
-5106
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": "node-debug2-placeholder",
|
||||
"version": "0.0.3",
|
||||
"publisher": "vscode",
|
||||
"engines": {
|
||||
"vscode": "1.6.x"
|
||||
}
|
||||
}
|
||||
@@ -1,133 +1,175 @@
|
||||
{
|
||||
"print(\"...\")": {
|
||||
"prefix": "pr",
|
||||
"body": "print(\"$1\")$0"
|
||||
"print": {
|
||||
"prefix": "print",
|
||||
"body": "print(\"$1\")\n$0",
|
||||
"description": "print(\"...\")"
|
||||
},
|
||||
"print(\"\\(...)\")": {
|
||||
"prefix": "po",
|
||||
"body": "print(\"\\($1)\")$0"
|
||||
"print value": {
|
||||
"prefix": "printv",
|
||||
"body": "print(\"\\($1)\")\n$0",
|
||||
"description": "print(\"\\(...)\")"
|
||||
},
|
||||
"repeat...while loop": {
|
||||
"while": {
|
||||
"prefix": "while",
|
||||
"body": [
|
||||
"while ${1:condition} {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "while statement"
|
||||
},
|
||||
"repeat-while": {
|
||||
"prefix": "repeat",
|
||||
"body": [
|
||||
"repeat {",
|
||||
"\t$0",
|
||||
"} while ${1:true}"
|
||||
"} while ${1:condition}"
|
||||
],
|
||||
"description": "repeat...while loop"
|
||||
"description": "repeat-while statement"
|
||||
},
|
||||
"While loop": {
|
||||
"prefix": "while",
|
||||
"body": [
|
||||
"while ${1:true} {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "While loop"
|
||||
},
|
||||
"For-In statement": {
|
||||
"prefix": "forin",
|
||||
"for": {
|
||||
"prefix": "for",
|
||||
"body": [
|
||||
"for ${1:item} in ${2:collection} {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "For-In statement"
|
||||
"description": "for-in statement"
|
||||
},
|
||||
"Reverse for loop": {
|
||||
"prefix": "forr",
|
||||
"body": [
|
||||
"for var ${1:i} = ${2:length} - 1; ${1:i} >= 0; ${1:i}-- {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "Reverse for loop"
|
||||
},
|
||||
"for loop": {
|
||||
"prefix": "for",
|
||||
"body": [
|
||||
"for var ${1:i} = 0; ${1:i} < ${2:length}; ${1:i}++ {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "for loop"
|
||||
},
|
||||
"if statement": {
|
||||
"if": {
|
||||
"prefix": "if",
|
||||
"body": [
|
||||
"if ${1:true} {",
|
||||
"if ${1:condition} {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "if statement"
|
||||
},
|
||||
"else-if statement": {
|
||||
"else if": {
|
||||
"prefix": "elif",
|
||||
"body": [
|
||||
"else if ${1:true} {",
|
||||
"else if ${1:condition} {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "if statement"
|
||||
"description": "else clause with a nested if statement"
|
||||
},
|
||||
"Else statement": {
|
||||
"else": {
|
||||
"prefix": "else",
|
||||
"body": [
|
||||
"else {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "Else statement"
|
||||
"description": "else clause"
|
||||
},
|
||||
"Guard statement": {
|
||||
"if let": {
|
||||
"prefix": "iflet",
|
||||
"body": [
|
||||
"if let ${1:value} = ${2:optional} {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "if statement with optional binding"
|
||||
},
|
||||
"guard": {
|
||||
"prefix": "guard",
|
||||
"body": [
|
||||
"guard let ${1:a} = ${2:optional} else {",
|
||||
"guard ${1:condition} else {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "Guard statement"
|
||||
"description": "guard statement"
|
||||
},
|
||||
"Optional Binding statement": {
|
||||
"prefix": "ifnil",
|
||||
"guard let": {
|
||||
"prefix": "guardlet",
|
||||
"body": [
|
||||
"if let ${1:a} = ${2:optional} {",
|
||||
"guard let ${1:value} = ${2:optional} else {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "Optional Binding statement"
|
||||
"description": "guard statement with optional binding"
|
||||
},
|
||||
"Switch statement": {
|
||||
"switch": {
|
||||
"prefix": "switch",
|
||||
"body": [
|
||||
"switch ${1:switch_on} {",
|
||||
"case ${2:a}:",
|
||||
"switch ${1:value} {",
|
||||
"case ${2:pattern}:",
|
||||
"\t$0",
|
||||
"default:",
|
||||
"\t$1",
|
||||
"\t",
|
||||
"}"
|
||||
],
|
||||
"description": "Switch statement"
|
||||
"description": "switch statement"
|
||||
},
|
||||
"Do catch": {
|
||||
"prefix": "docatch",
|
||||
"do": {
|
||||
"prefix": "do",
|
||||
"body": [
|
||||
"do {",
|
||||
"\ttry ${1:function that throws}",
|
||||
"} catch ${2:pattern} {",
|
||||
"\t$0",
|
||||
"} catch ${1:error} {",
|
||||
"\t$2",
|
||||
"}"
|
||||
],
|
||||
"description": "do statement"
|
||||
},
|
||||
"func": {
|
||||
"prefix": "func",
|
||||
"body": [
|
||||
"func ${1:name}(${2:parameters}) -> ${3:Type} {",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "Try catch"
|
||||
"description": "function declaration"
|
||||
},
|
||||
"Enum": {
|
||||
"struct": {
|
||||
"prefix": "struct",
|
||||
"body": [
|
||||
"struct ${1:Name} {",
|
||||
"",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "struct declaration"
|
||||
},
|
||||
"enum": {
|
||||
"prefix": "enum",
|
||||
"body": [
|
||||
"enum ${1:Name} {",
|
||||
"",
|
||||
"\tcase $0",
|
||||
"}"
|
||||
],
|
||||
"description": "Enum"
|
||||
"description": "enum declaration"
|
||||
},
|
||||
"class": {
|
||||
"prefix": "class",
|
||||
"body": [
|
||||
"class ${1:Name} {",
|
||||
"",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "class declaration"
|
||||
},
|
||||
"protocol": {
|
||||
"prefix": "protocol",
|
||||
"body": [
|
||||
"protocol ${1:Name} {",
|
||||
"",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "protocol declaration"
|
||||
},
|
||||
"extension": {
|
||||
"prefix": "extension",
|
||||
"body": [
|
||||
"extension ${1:Type} {",
|
||||
"",
|
||||
"\t$0",
|
||||
"}"
|
||||
],
|
||||
"description": "extension declaration"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,10 +48,7 @@ class SyncedBuffer {
|
||||
}
|
||||
|
||||
if (this.client.apiVersion.has230Features()) {
|
||||
const root = this.client.getWorkspaceRootForResource(this.document.uri);
|
||||
if (root) {
|
||||
args.projectRootPath = root;
|
||||
}
|
||||
args.projectRootPath = this.client.getWorkspaceRootForResource(this.document.uri);
|
||||
}
|
||||
|
||||
if (this.client.apiVersion.has240Features()) {
|
||||
|
||||
@@ -578,12 +578,12 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
|
||||
}
|
||||
|
||||
public normalizePath(resource: Uri): string | null {
|
||||
if (resource.scheme === fileSchemes.walkThroughSnippet) {
|
||||
return resource.toString();
|
||||
}
|
||||
|
||||
if (resource.scheme === fileSchemes.untitled && this._apiVersion.has213Features()) {
|
||||
return resource.toString();
|
||||
if (this._apiVersion.has213Features()) {
|
||||
if (resource.scheme === fileSchemes.walkThroughSnippet || resource.scheme === fileSchemes.untitled) {
|
||||
const dirName = path.dirname(resource.path);
|
||||
const fileName = this.inMemoryResourcePrefix + path.basename(resource.path);
|
||||
return resource.with({ path: path.join(dirName, fileName) }).toString(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (resource.scheme !== fileSchemes.file) {
|
||||
@@ -599,11 +599,24 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
|
||||
return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/');
|
||||
}
|
||||
|
||||
private get inMemoryResourcePrefix(): string {
|
||||
return this._apiVersion.has270Features() ? '^' : '';
|
||||
}
|
||||
|
||||
public asUrl(filepath: string): Uri {
|
||||
if (filepath.startsWith(TypeScriptServiceClient.WALK_THROUGH_SNIPPET_SCHEME_COLON)
|
||||
|| (filepath.startsWith(fileSchemes.untitled + ':') && this._apiVersion.has213Features())
|
||||
) {
|
||||
return Uri.parse(filepath);
|
||||
if (this._apiVersion.has213Features()) {
|
||||
if (filepath.startsWith(TypeScriptServiceClient.WALK_THROUGH_SNIPPET_SCHEME_COLON) || (filepath.startsWith(fileSchemes.untitled + ':'))
|
||||
) {
|
||||
let resource = Uri.parse(filepath);
|
||||
if (this.inMemoryResourcePrefix) {
|
||||
const dirName = path.dirname(resource.path);
|
||||
const fileName = path.basename(resource.path);
|
||||
if (fileName.startsWith(this.inMemoryResourcePrefix)) {
|
||||
resource = resource.with({ path: path.join(dirName, fileName.slice(this.inMemoryResourcePrefix.length)) });
|
||||
}
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
return Uri.file(filepath);
|
||||
}
|
||||
@@ -620,8 +633,10 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
|
||||
return root.uri.fsPath;
|
||||
}
|
||||
}
|
||||
return roots[0].uri.fsPath;
|
||||
}
|
||||
return roots[0].uri.fsPath;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public execute(command: string, args: any, expectsResultOrToken?: boolean | CancellationToken): Promise<any> {
|
||||
|
||||
@@ -10,6 +10,7 @@ import * as vscode from 'vscode';
|
||||
import { createRandomFile, deleteFile, closeAllEditors, pathEquals } from './utils';
|
||||
import { join, basename } from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { Uri } from 'vscode';
|
||||
|
||||
suite('workspace-namespace', () => {
|
||||
|
||||
@@ -505,4 +506,44 @@ suite('workspace-namespace', () => {
|
||||
return vscode.workspace.applyEdit(edit);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('applyEdit should fail when editing deleted resource', async () => {
|
||||
const resource = await createRandomFile();
|
||||
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
edit.deleteResource(resource);
|
||||
try {
|
||||
edit.insert(resource, new vscode.Position(0, 0), '');
|
||||
assert.fail(false, 'Should disallow edit of deleted resource');
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
});
|
||||
|
||||
test('applyEdit should fail when renaming deleted resource', async () => {
|
||||
const resource = await createRandomFile();
|
||||
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
edit.deleteResource(resource);
|
||||
try {
|
||||
edit.renameResource(resource, resource);
|
||||
assert.fail(false, 'Should disallow rename of deleted resource');
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
});
|
||||
|
||||
test('applyEdit should fail when editing renamed from resource', async () => {
|
||||
const resource = await createRandomFile();
|
||||
const newResource = Uri.parse(resource.fsPath + '.1');
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
edit.renameResource(resource, newResource);
|
||||
try {
|
||||
edit.insert(resource, new vscode.Position(0, 0), '');
|
||||
assert.fail(false, 'Should disallow editing renamed file');
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.20.0",
|
||||
"distro": "e14a5a3afaae557ff651b344462ed39e776435a2",
|
||||
"distro": "d41bd1b8193403ffe9849b49fc37153e3d5b5a49",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
@@ -45,7 +45,7 @@
|
||||
"vscode-debugprotocol": "1.25.0",
|
||||
"vscode-ripgrep": "^0.7.1-patch.0",
|
||||
"vscode-textmate": "^3.2.0",
|
||||
"vscode-xterm": "3.1.0-beta7",
|
||||
"vscode-xterm": "3.1.0-beta10",
|
||||
"yauzl": "2.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -129,4 +129,4 @@
|
||||
"windows-mutex": "^0.2.0",
|
||||
"windows-process-tree": "0.1.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ set CODE=".build\electron\%NAMESHORT%"
|
||||
node build\lib\electron.js
|
||||
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron
|
||||
|
||||
:: Get built-in extensions
|
||||
node build\lib\builtInExtensions.js
|
||||
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js download-builtin-extensions
|
||||
|
||||
:: Build
|
||||
if not exist out node .\node_modules\gulp\bin\gulp.js compile
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ function code() {
|
||||
# Get electron
|
||||
node build/lib/electron.js || ./node_modules/.bin/gulp electron
|
||||
|
||||
# Get built-in extensions
|
||||
node build/lib/builtInExtensions.js || ./node_modules/.bin/gulp download-builtin-extensions
|
||||
|
||||
# Build
|
||||
test -d out || ./node_modules/.bin/gulp compile
|
||||
|
||||
|
||||
Vendored
+15
@@ -8,6 +8,11 @@
|
||||
*/
|
||||
|
||||
declare module 'vscode-xterm' {
|
||||
/**
|
||||
* A string representing text font weight.
|
||||
*/
|
||||
export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
|
||||
|
||||
/**
|
||||
* An object containing start up options for the terminal.
|
||||
*/
|
||||
@@ -57,6 +62,16 @@ declare module 'vscode-xterm' {
|
||||
*/
|
||||
fontFamily?: string;
|
||||
|
||||
/**
|
||||
* The font weight used to render non-bold text.
|
||||
*/
|
||||
fontWeight?: FontWeight;
|
||||
|
||||
/**
|
||||
* The font weight used to render bold text.
|
||||
*/
|
||||
fontWeightBold?: FontWeight;
|
||||
|
||||
/**
|
||||
* The spacing in whole pixels between characters..
|
||||
*/
|
||||
|
||||
@@ -156,6 +156,7 @@ export class ResourceViewer {
|
||||
|
||||
class ImageView {
|
||||
private static readonly MAX_IMAGE_SIZE = BinarySize.MB; // showing images inline is memory intense, so we have a limit
|
||||
private static readonly BASE64_MARKER = 'base64,';
|
||||
|
||||
public static create(
|
||||
container: Builder,
|
||||
@@ -177,9 +178,8 @@ class ImageView {
|
||||
|
||||
// Data URI
|
||||
if (descriptor.resource.scheme === Schemas.data) {
|
||||
const BASE64_MARKER = 'base64,';
|
||||
const base64MarkerIndex = descriptor.resource.path.indexOf(BASE64_MARKER);
|
||||
const hasData = base64MarkerIndex >= 0 && descriptor.resource.path.substring(base64MarkerIndex + BASE64_MARKER.length).length > 0;
|
||||
const base64MarkerIndex = descriptor.resource.path.indexOf(ImageView.BASE64_MARKER);
|
||||
const hasData = base64MarkerIndex >= 0 && descriptor.resource.path.substring(base64MarkerIndex + ImageView.BASE64_MARKER.length).length > 0;
|
||||
|
||||
skipInlineImage = !hasData || descriptor.size > ImageView.MAX_IMAGE_SIZE || descriptor.resource.path.length > ImageView.MAX_IMAGE_SIZE;
|
||||
}
|
||||
@@ -241,7 +241,11 @@ class InlineImageView {
|
||||
private static readonly SCALE_FACTOR = 1.5;
|
||||
private static readonly MAX_SCALE = 20;
|
||||
private static readonly MIN_SCALE = 0.1;
|
||||
private static readonly PIXELATION_THRESHOLD = 64; // enable image-rendering: pixelated for images less than this
|
||||
|
||||
/**
|
||||
* Enable image-rendering: pixelated for images scaled by more than this.
|
||||
*/
|
||||
private static readonly PIXELATION_THRESHOLD = 3;
|
||||
|
||||
/**
|
||||
* Chrome is caching images very aggressively and so we use the ETag information to find out if
|
||||
@@ -279,16 +283,17 @@ class InlineImageView {
|
||||
img.removeClass('untouched');
|
||||
updateScale(scale);
|
||||
}
|
||||
if (imgElement.naturalWidth < InlineImageView.PIXELATION_THRESHOLD
|
||||
|| imgElement.naturalHeight < InlineImageView.PIXELATION_THRESHOLD) {
|
||||
img.addClass('pixelated');
|
||||
}
|
||||
function setImageWidth(width) {
|
||||
img.style('width', `${width}px`);
|
||||
img.style('height', 'auto');
|
||||
}
|
||||
function updateScale(newScale) {
|
||||
scale = clamp(newScale, InlineImageView.MIN_SCALE, InlineImageView.MAX_SCALE);
|
||||
if (scale >= InlineImageView.PIXELATION_THRESHOLD) {
|
||||
img.addClass('pixelated');
|
||||
} else {
|
||||
img.removeClass('pixelated');
|
||||
}
|
||||
setImageWidth(Math.floor(imgElement.naturalWidth * scale));
|
||||
InlineImageView.IMAGE_SCALE_CACHE.set(cacheKey, scale);
|
||||
scrollbar.scanDomNode();
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
.monaco-resource-viewer img.untouched {
|
||||
max-width: 100%;
|
||||
object-fit: contain;
|
||||
image-rendering: auto;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer img {
|
||||
|
||||
@@ -38,8 +38,13 @@ export function isEqual(first: uri, second: uri, ignoreCase?: boolean): boolean
|
||||
}
|
||||
|
||||
export function dirname(resource: uri): uri {
|
||||
const dirname = paths.dirname(resource.path);
|
||||
if (resource.authority && dirname && !paths.isAbsolute(dirname)) {
|
||||
return null; // If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character
|
||||
}
|
||||
|
||||
return resource.with({
|
||||
path: paths.dirname(resource.path)
|
||||
path: dirname
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { distinctParents } from 'vs/base/common/resources';
|
||||
import { distinctParents, dirname } from 'vs/base/common/resources';
|
||||
import { normalize } from 'vs/base/common/paths';
|
||||
|
||||
suite('Resources', () => {
|
||||
|
||||
@@ -40,4 +41,14 @@ suite('Resources', () => {
|
||||
assert.equal(distinct[1].toString(), resources[3].toString());
|
||||
assert.equal(distinct[2].toString(), resources[4].toString());
|
||||
});
|
||||
|
||||
test('dirname', (done) => {
|
||||
const f = URI.file('/some/file/test.txt');
|
||||
const d = dirname(f);
|
||||
assert.equal(d.fsPath, normalize('/some/file', true));
|
||||
|
||||
// does not explode (https://github.com/Microsoft/vscode/issues/41987)
|
||||
URI.from({ scheme: 'file', authority: '/users/someone/portal.h' });
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -1,102 +1,17 @@
|
||||
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data:; media-src 'none'; child-src 'self'; object-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https:;">
|
||||
</head>
|
||||
<body>
|
||||
<div id="issue-reporter">
|
||||
|
||||
<div class="input-group">
|
||||
<label for="issue-type">I want to submit a</label>
|
||||
<select id="issue-type" class="form-control">
|
||||
<option value="0">Bug Report</option>
|
||||
<option value="1">Performance Issue</option>
|
||||
<option value="2">Feature Request</option>
|
||||
</select>
|
||||
</div>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data:; media-src 'none'; child-src 'self'; object-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https:;">
|
||||
<style>body{display: none}</style>
|
||||
</head>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="issue-title">Title <span class="required-input">*</span></label>
|
||||
<div id="issue-title-validation-error" class="validation-error hidden">Please enter a title.</div>
|
||||
<input id="issue-title" type="text" required>
|
||||
<small id="similar-issues">
|
||||
<!-- To be dynamically filled -->
|
||||
</small>
|
||||
</div>
|
||||
<body>
|
||||
</body>
|
||||
|
||||
<div class="input-group">
|
||||
<div class="two-col">
|
||||
<label for="vscode-version">VS Code Version <span class="required-input">*</span></label>
|
||||
<input id="vscode-version" type="text" value="Loading..." disabled/>
|
||||
</div>
|
||||
<div class="two-col">
|
||||
<label for="os">OS Version <span class="required-input">*</span></label>
|
||||
<input id="os" type="text" value="Loading..." disabled/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Startup via issueReporter.js -->
|
||||
<script src="issueReporter.js"></script>
|
||||
|
||||
<div id="block-container">
|
||||
<div class="block block-system">
|
||||
<details>
|
||||
<summary>My System Info
|
||||
<input type="checkbox" id="includeSystemInfo" checked>
|
||||
<label class="caption" for="includeSystemInfo">Send my data</label>
|
||||
</input>
|
||||
</summary>
|
||||
<div class="block-info">
|
||||
<!-- To be dynamically filled -->
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<div class="block block-process">
|
||||
<details>
|
||||
<summary>Currently Running Processes
|
||||
<input type="checkbox" id="includeProcessInfo" checked>
|
||||
<label class="caption" for="includeProcessInfo">Send my data</label>
|
||||
</input>
|
||||
</summary>
|
||||
<div class="block-info">
|
||||
<!-- To be dynamically filled -->
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<div class="block block-workspace">
|
||||
<details>
|
||||
<summary>My Workspace Stats
|
||||
<input type="checkbox" id="includeWorkspaceInfo" checked>
|
||||
<label class="caption" for="includeWorkspaceInfo">Send my data</label>
|
||||
</input>
|
||||
</summary>
|
||||
<pre class="block-info">
|
||||
<code>
|
||||
<!-- To be dynamically filled -->
|
||||
</code>
|
||||
</pre>
|
||||
</details>
|
||||
</div>
|
||||
<div class="block block-description">
|
||||
<label class="block-title">
|
||||
<!-- To be dynamically filled -->
|
||||
</label>
|
||||
<small class="block-subtitle">
|
||||
<!-- To be dynamically filled -->
|
||||
</small>
|
||||
<div class="block-info-text">
|
||||
<small>
|
||||
We support GitHub-flavored Markdown.
|
||||
You will still be able to edit your issue when we preview it on GitHub.
|
||||
</small>
|
||||
<div id="description-validation-error" class="validation-error hidden">Please enter a description.</div>
|
||||
<textarea name="description" id="description" cols="100" rows="15" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="github-submit-btn" disabled>Loading data...</button>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="issueReporter.js"></script>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -65,6 +65,25 @@ function main() {
|
||||
// Load the loader and start loading the workbench
|
||||
const rootUrl = uriFromPath(configuration.appRoot) + '/out';
|
||||
|
||||
// Get the nls configuration into the process.env as early as possible.
|
||||
var nlsConfig = { availableLanguages: {} };
|
||||
const config = process.env['VSCODE_NLS_CONFIG'];
|
||||
if (config) {
|
||||
process.env['VSCODE_NLS_CONFIG'] = config;
|
||||
try {
|
||||
nlsConfig = JSON.parse(config);
|
||||
} catch (e) { /*noop*/ }
|
||||
}
|
||||
|
||||
var locale = nlsConfig.availableLanguages['*'] || 'en';
|
||||
if (locale === 'zh-tw') {
|
||||
locale = 'zh-Hant';
|
||||
} else if (locale === 'zh-cn') {
|
||||
locale = 'zh-Hans';
|
||||
}
|
||||
|
||||
window.document.documentElement.setAttribute('lang', locale);
|
||||
|
||||
// In the bundled version the nls plugin is packaged with the loader so the NLS Plugins
|
||||
// loads as soon as the loader loads. To be able to have pseudo translation
|
||||
createScript(rootUrl + '/vs/loader.js', function () {
|
||||
@@ -72,7 +91,6 @@ function main() {
|
||||
|
||||
window.MonacoEnvironment = {};
|
||||
|
||||
var nlsConfig = { availableLanguages: {} };
|
||||
require.config({
|
||||
baseUrl: rootUrl,
|
||||
'vs/nls': nlsConfig,
|
||||
@@ -80,6 +98,12 @@ function main() {
|
||||
nodeModules: [/*BUILD->INSERT_NODE_MODULES*/]
|
||||
});
|
||||
|
||||
if (nlsConfig.pseudo) {
|
||||
require(['vs/nls'], function (nlsPlugin) {
|
||||
nlsPlugin.setPseudoTranslation(nlsConfig.pseudo);
|
||||
});
|
||||
}
|
||||
|
||||
require(['vs/code/electron-browser/issue/issueReporterMain'], (issueReporter) => {
|
||||
issueReporter.startup(configuration);
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import 'vs/css!./media/issueReporter';
|
||||
import { shell, ipcRenderer, webFrame, remote } from 'electron';
|
||||
import { localize } from 'vs/nls';
|
||||
import { $ } from 'vs/base/browser/dom';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import product from 'vs/platform/node/product';
|
||||
@@ -28,10 +29,17 @@ import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc';
|
||||
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||
import { IssueReporterModel, IssueReporterData } from 'vs/code/electron-browser/issue/issueReporterModel';
|
||||
import { IssueReporterStyles } from 'vs/platform/issue/common/issue';
|
||||
import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage';
|
||||
|
||||
export function startup(configuration: IWindowConfiguration) {
|
||||
document.body.innerHTML = BaseHtml();
|
||||
const issueReporter = new IssueReporter(configuration);
|
||||
issueReporter.render();
|
||||
|
||||
// workaround for flickering on page load as css is applied
|
||||
setTimeout(() => {
|
||||
issueReporter.render();
|
||||
document.body.style.display = 'block';
|
||||
}, 10);
|
||||
}
|
||||
|
||||
export class IssueReporter extends Disposable {
|
||||
@@ -61,7 +69,7 @@ export class IssueReporter extends Disposable {
|
||||
|
||||
const submitButton = <HTMLButtonElement>document.getElementById('github-submit-btn');
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = 'Preview on GitHub';
|
||||
submitButton.textContent = localize('previewOnGitHub', "Preview on GitHub");
|
||||
});
|
||||
|
||||
ipcRenderer.send('issueInfoRequest');
|
||||
@@ -69,6 +77,10 @@ export class IssueReporter extends Disposable {
|
||||
|
||||
this.initServices(configuration);
|
||||
this.setEventHandlers();
|
||||
|
||||
if (window.document.documentElement.lang !== 'en') {
|
||||
show(document.getElementById('english'));
|
||||
}
|
||||
}
|
||||
|
||||
render(): void {
|
||||
@@ -103,7 +115,8 @@ export class IssueReporter extends Disposable {
|
||||
}
|
||||
|
||||
if (styles.inputErrorBorder) {
|
||||
content.push(`.invalid-input, .invalid-input:focus { border: 1px solid ${styles.inputErrorBorder}; }`);
|
||||
content.push(`.invalid-input, .invalid-input:focus { border: 1px solid ${styles.inputErrorBorder} !important; }`);
|
||||
content.push(`.validation-error { color: ${styles.inputErrorBorder}; }`);
|
||||
}
|
||||
|
||||
if (styles.inputActiveBorder) {
|
||||
@@ -132,8 +145,6 @@ export class IssueReporter extends Disposable {
|
||||
|
||||
styleTag.innerHTML = content.join('\n');
|
||||
document.head.appendChild(styleTag);
|
||||
|
||||
document.body.style.backgroundColor = styles.backgroundColor;
|
||||
document.body.style.color = styles.color;
|
||||
}
|
||||
|
||||
@@ -215,7 +226,7 @@ export class IssueReporter extends Disposable {
|
||||
if (result.items.length) {
|
||||
const issues = $('ul');
|
||||
const issuesText = $('div.list-title');
|
||||
issuesText.textContent = 'Similar issues:';
|
||||
issuesText.textContent = localize('similarIssues', "Similar issues");
|
||||
addIssuesToList(issues, result.items);
|
||||
similarIssues.appendChild(issuesText);
|
||||
similarIssues.appendChild(issues);
|
||||
@@ -246,8 +257,8 @@ export class IssueReporter extends Disposable {
|
||||
const processBlock = document.querySelector('.block-process');
|
||||
const workspaceBlock = document.querySelector('.block-workspace');
|
||||
|
||||
const descriptionTitle = document.querySelector('.block-description .block-title');
|
||||
const descriptionSubtitle = document.querySelector('.block-description .block-subtitle');
|
||||
const descriptionTitle = document.getElementById('issue-description-label');
|
||||
const descriptionSubtitle = document.getElementById('issue-description-subtitle');
|
||||
|
||||
// 1 - Bug
|
||||
if (issueType === 0) {
|
||||
@@ -255,9 +266,9 @@ export class IssueReporter extends Disposable {
|
||||
hide(processBlock);
|
||||
hide(workspaceBlock);
|
||||
|
||||
descriptionTitle.innerHTML = 'Steps to Reproduce <span class="required-input">*</span>';
|
||||
descriptionTitle.innerHTML = `${localize('stepsToReproduce', "Steps to Reproduce")} <span class="required-input">*</span>`;
|
||||
show(descriptionSubtitle);
|
||||
descriptionSubtitle.innerHTML = 'How did you encounter this problem? Please provide clear steps to reproduce the problem during our investigation. What did you expect to happen and what actually did happen?';
|
||||
descriptionSubtitle.innerHTML = localize('bugDescription', "How did you encounter this problem? Please provide clear steps to reproduce the problem during our investigation. What did you expect to happen and what actually did happen?");
|
||||
}
|
||||
// 2 - Perf Issue
|
||||
else if (issueType === 1) {
|
||||
@@ -265,9 +276,9 @@ export class IssueReporter extends Disposable {
|
||||
show(processBlock);
|
||||
show(workspaceBlock);
|
||||
|
||||
descriptionTitle.innerHTML = 'Steps to Reproduce <span class="required-input">*</span>';
|
||||
descriptionTitle.innerHTML = `${localize('stepsToReproduce', "Steps to Reproduce")} <span class="required-input">*</span>`;
|
||||
show(descriptionSubtitle);
|
||||
descriptionSubtitle.innerHTML = 'When did this performance issue happen? For example, does it occur on startup or after a specific series of actions? Any details you can provide help our investigation.';
|
||||
descriptionSubtitle.innerHTML = localize('performanceIssueDesciption', "When did this performance issue happen? For example, does it occur on startup or after a specific series of actions? Any details you can provide help our investigation.");
|
||||
}
|
||||
// 3 - Feature Request
|
||||
else {
|
||||
@@ -275,7 +286,7 @@ export class IssueReporter extends Disposable {
|
||||
hide(processBlock);
|
||||
hide(workspaceBlock);
|
||||
|
||||
descriptionTitle.innerHTML = 'Description <span class="required-input">*</span>';
|
||||
descriptionTitle.innerHTML = `${localize('description', "Description")} <span class="required-input">*</span>`;
|
||||
hide(descriptionSubtitle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export default (): string => `
|
||||
<div id="issue-reporter">
|
||||
<div id="english" class="input-group hidden">${escape(localize('completeInEnglish', "Please complete the form in English."))}</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="issue-type">${escape(localize('issueTypeLabel', "I want to submit a"))}</label>
|
||||
<select id="issue-type" class="form-control">
|
||||
<option value="0">${escape(localize('bugReporter', "Bug Report"))}</option>
|
||||
<option value="1">${escape(localize('performanceIssue', "Performance Issue"))}</option>
|
||||
<option value="2">${escape(localize('featureRequest', "Feature Request"))}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="issue-title">${escape(localize('issueTitleLabel', "Title"))} <span class="required-input">*</span></label>
|
||||
<div id="issue-title-validation-error" class="validation-error hidden">${escape(localize('issueTitleRequired', "Please enter a title."))}</div>
|
||||
<input id="issue-title" type="text" required>
|
||||
<small id="similar-issues">
|
||||
<!-- To be dynamically filled -->
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<div class="two-col">
|
||||
<label for="vscode-version">${escape(localize('vscodeVersion', "VS Code Version"))} <span class="required-input">*</span></label>
|
||||
<input id="vscode-version" type="text" value="${escape(localize('loading', "Loading..."))}" disabled/>
|
||||
</div>
|
||||
<div class="two-col">
|
||||
<label for="os">${escape(localize('osVersion', "OS Version"))} <span class="required-input">*</span></label>
|
||||
<input id="os" type="text" value="${escape(localize('loading', "Loading..."))}" disabled/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="block-container" class="input-group">
|
||||
<div class="block block-system">
|
||||
<details>
|
||||
<summary>${escape(localize('systemInfo', "My System Info"))}
|
||||
<input type="checkbox" id="includeSystemInfo" checked>
|
||||
<label class="caption" for="includeSystemInfo">${escape(localize('sendData', "Send my data"))}</label>
|
||||
</input>
|
||||
</summary>
|
||||
<div class="block-info">
|
||||
<!-- To be dynamically filled -->
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<div class="block block-process">
|
||||
<details>
|
||||
<summary>${escape(localize('processes', "Currently Running Processes"))}
|
||||
<input type="checkbox" id="includeProcessInfo" checked>
|
||||
<label class="caption" for="includeProcessInfo">${escape(localize('sendData', "Send my data"))}</label>
|
||||
</input>
|
||||
</summary>
|
||||
<div class="block-info">
|
||||
<!-- To be dynamically filled -->
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<div class="block block-workspace">
|
||||
<details>
|
||||
<summary>${escape(localize('workspaceStats', "My Workspace Stats"))}
|
||||
<input type="checkbox" id="includeWorkspaceInfo" checked>
|
||||
<label class="caption" for="includeWorkspaceInfo">${escape(localize('sendData', "Send my data"))}</label>
|
||||
</input>
|
||||
</summary>
|
||||
<pre class="block-info">
|
||||
<code>
|
||||
<!-- To be dynamically filled -->
|
||||
</code>
|
||||
</pre>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="description" id="issue-description-label">
|
||||
<!-- To be dynamically filled -->
|
||||
</label>
|
||||
<small id="issue-description-subtitle">
|
||||
<!-- To be dynamically filled -->
|
||||
</small>
|
||||
<div class="block-info-text">
|
||||
<small>${escape(localize('githubMarkdown', "We support GitHub-flavored Markdown. You will still be able to edit your issue when we preview it on GitHub."))}</small>
|
||||
<div id="description-validation-error" class="validation-error hidden">${escape(localize('issueDescriptionRequired', "Please enter a description."))}</div>
|
||||
<textarea name="description" id="description" cols="100" rows="15" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="github-submit-btn" disabled>${escape(localize('loadingData', "Loading data..."))}</button>
|
||||
</div>`;
|
||||
@@ -97,7 +97,6 @@ html {
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #1E1E1E;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
@@ -105,14 +104,7 @@ body {
|
||||
}
|
||||
|
||||
#block-container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.block {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.block summary {
|
||||
margin-bottom: 16px;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.block .block-info {
|
||||
@@ -171,23 +163,29 @@ select, input, textarea {
|
||||
|
||||
summary {
|
||||
border: 1px solid transparent;
|
||||
padding: 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.validation-error {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.caption {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
margin-left: 1em;
|
||||
height: 18px;
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
margin-top: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
@@ -214,7 +212,7 @@ a {
|
||||
border: 1px solid #be1100;
|
||||
}
|
||||
|
||||
.required-input {
|
||||
.required-input, .validation-error {
|
||||
color: #be1100;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ import { ipcRenderer } from 'electron';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { createSharedProcessContributions } from 'vs/code/electron-browser/sharedProcess/contrib/contributions';
|
||||
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
|
||||
|
||||
export interface ISharedProcessConfiguration {
|
||||
readonly machineId: string;
|
||||
@@ -51,6 +52,7 @@ export function startup(configuration: ISharedProcessConfiguration) {
|
||||
interface ISharedProcessInitData {
|
||||
sharedIPCHandle: string;
|
||||
args: ParsedArgs;
|
||||
logLevel: LogLevel;
|
||||
}
|
||||
|
||||
class ActiveWindowManager implements IDisposable {
|
||||
@@ -81,7 +83,8 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
|
||||
const services = new ServiceCollection();
|
||||
|
||||
const environmentService = new EnvironmentService(initData.args, process.execPath);
|
||||
const logService = createSpdLogService('sharedprocess', environmentService);
|
||||
const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', { route: () => 'main' }));
|
||||
const logService = new FollowerLogService(logLevelClient, createSpdLogService('sharedprocess', initData.logLevel, environmentService.logsPath));
|
||||
process.once('exit', () => logService.dispose());
|
||||
|
||||
logService.info('main', JSON.stringify(configuration));
|
||||
|
||||
@@ -57,6 +57,7 @@ import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateServ
|
||||
import { IIssueService } from 'vs/platform/issue/common/issue';
|
||||
import { IssueChannel } from 'vs/platform/issue/common/issueIpc';
|
||||
import { IssueService } from 'vs/platform/issue/electron-main/issueService';
|
||||
import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc';
|
||||
|
||||
export class CodeApplication {
|
||||
|
||||
@@ -272,7 +273,7 @@ export class CodeApplication {
|
||||
this.logService.trace(`Resolved machine identifier: ${machineId}`);
|
||||
|
||||
// Spawn shared process
|
||||
this.sharedProcess = new SharedProcess(this.environmentService, machineId, this.userEnv);
|
||||
this.sharedProcess = new SharedProcess(this.environmentService, machineId, this.userEnv, this.logService);
|
||||
this.toDispose.push(this.sharedProcess);
|
||||
this.sharedProcessClient = this.sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
|
||||
|
||||
@@ -378,6 +379,11 @@ export class CodeApplication {
|
||||
this.electronIpcServer.registerChannel('windows', windowsChannel);
|
||||
this.sharedProcessClient.done(client => client.registerChannel('windows', windowsChannel));
|
||||
|
||||
// Log level management
|
||||
const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
|
||||
this.electronIpcServer.registerChannel('loglevel', logLevelChannel);
|
||||
this.sharedProcessClient.done(client => client.registerChannel('loglevel', logLevelChannel));
|
||||
|
||||
// Lifecycle
|
||||
this.lifecycleService.ready();
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import * as os from 'os';
|
||||
import * as cp from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as readline from 'readline';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { ILaunchChannel } from 'vs/code/electron-main/launch';
|
||||
@@ -17,6 +16,8 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { IRequestService } from 'vs/platform/request/node/request';
|
||||
import { IRequestContext } from 'vs/base/node/request';
|
||||
import { IChoiceService } from 'vs/platform/message/common/message';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
||||
interface PostResult {
|
||||
readonly blob_id: string;
|
||||
@@ -35,7 +36,8 @@ class Endpoint {
|
||||
|
||||
export async function uploadLogs(
|
||||
channel: ILaunchChannel,
|
||||
requestService: IRequestService
|
||||
requestService: IRequestService,
|
||||
choiceService: IChoiceService
|
||||
): TPromise<any> {
|
||||
const endpoint = Endpoint.getFromProduct();
|
||||
if (!endpoint) {
|
||||
@@ -45,7 +47,8 @@ export async function uploadLogs(
|
||||
|
||||
const logsPath = await channel.call('get-logs-path', null);
|
||||
|
||||
if (await promptUserToConfirmLogUpload(logsPath)) {
|
||||
if (await promptUserToConfirmLogUpload(logsPath, choiceService)) {
|
||||
console.log(localize('beginUploading', 'Uploading...'));
|
||||
const outZip = await zipLogs(logsPath);
|
||||
const result = await postLogs(endpoint, outZip, requestService);
|
||||
console.log(localize('didUploadLogs', 'Uploaded logs ID: {0}', result.blob_id));
|
||||
@@ -55,22 +58,18 @@ export async function uploadLogs(
|
||||
}
|
||||
|
||||
async function promptUserToConfirmLogUpload(
|
||||
logsPath: string
|
||||
logsPath: string,
|
||||
choiceService: IChoiceService
|
||||
): Promise<boolean> {
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
return new TPromise<boolean>(resolve =>
|
||||
rl.question(
|
||||
localize('logUploadPromptHeader', 'Upload session logs to secure endpoint?')
|
||||
+ '\n\n' + localize('logUploadPromptBody', 'Please review your log files: \'{0}\'', logsPath)
|
||||
+ '\n\n' + localize('logUploadPromptKey', 'Enter \'y\' to confirm upload...'),
|
||||
(answer: string) => {
|
||||
rl.close();
|
||||
resolve(answer && answer.trim()[0].toLowerCase() === 'y');
|
||||
}));
|
||||
const message = localize('logUploadPromptHeader', 'Upload session logs to secure endpoint?')
|
||||
+ '\n\n' + localize('logUploadPromptBody', 'Please review your log files here: \'{0}\'', logsPath)
|
||||
+ '\n\n' + localize('logUploadPromptBodyDetails', 'Logs may contain personal information such as full paths and file contents.')
|
||||
+ '\n\n';
|
||||
const choice = await choiceService.choose(Severity.Info, message, [
|
||||
localize('logUploadPromptKey', 'I have reviewed my logs. Proceed with upload...'),
|
||||
localize('logUploadPromptCancel', 'Cancel'),
|
||||
], 1);
|
||||
return choice === 0;
|
||||
}
|
||||
|
||||
async function postLogs(
|
||||
@@ -101,8 +100,14 @@ async function postLogs(
|
||||
|
||||
result.stream.on('end', () => {
|
||||
try {
|
||||
const result = Buffer.concat(parts).toString('utf-8');
|
||||
res(JSON.parse(result));
|
||||
const response = Buffer.concat(parts).toString('utf-8');
|
||||
if (result.res.statusCode === 200) {
|
||||
res(JSON.parse(response));
|
||||
} else {
|
||||
const errorMessage = localize('responseError', 'Error posting logs. Got {0}', result.res.statusCode);
|
||||
console.log(errorMessage);
|
||||
reject(new Error(errorMessage));
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(localize('parseError', 'Error parsing response'));
|
||||
reject(e);
|
||||
|
||||
@@ -21,7 +21,7 @@ import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiati
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { ILogService, ConsoleLogMainService, MultiplexLogService } from 'vs/platform/log/common/log';
|
||||
import { ILogService, ConsoleLogMainService, MultiplexLogService, getLogLevel } from 'vs/platform/log/common/log';
|
||||
import { StateService } from 'vs/platform/state/node/stateService';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
import { IBackupMainService } from 'vs/platform/backup/common/backup';
|
||||
@@ -46,12 +46,14 @@ import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { printDiagnostics } from 'vs/code/electron-main/diagnostics';
|
||||
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
|
||||
import { uploadLogs } from 'vs/code/electron-main/logUploader';
|
||||
import { IChoiceService } from 'vs/platform/message/common/message';
|
||||
import { ChoiceCliService } from 'vs/platform/message/node/messageCli';
|
||||
|
||||
function createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
const environmentService = new EnvironmentService(args, process.execPath);
|
||||
const consoleLogService = new ConsoleLogMainService(environmentService);
|
||||
const consoleLogService = new ConsoleLogMainService(getLogLevel(environmentService));
|
||||
const logService = new MultiplexLogService([consoleLogService, bufferLogService]);
|
||||
|
||||
process.once('exit', () => logService.dispose());
|
||||
@@ -69,6 +71,7 @@ function createServices(args: ParsedArgs, bufferLogService: BufferLogService): I
|
||||
services.set(IRequestService, new SyncDescriptor(RequestService));
|
||||
services.set(IURLService, new SyncDescriptor(URLService, args['open-url'] ? args._urls : []));
|
||||
services.set(IBackupMainService, new SyncDescriptor(BackupMainService));
|
||||
services.set(IChoiceService, new SyncDescriptor(ChoiceCliService));
|
||||
|
||||
return new InstantiationService(services, true);
|
||||
}
|
||||
@@ -106,6 +109,7 @@ function setupIPC(accessor: ServicesAccessor): TPromise<Server> {
|
||||
const logService = accessor.get(ILogService);
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
const requestService = accessor.get(IRequestService);
|
||||
const choiceService = accessor.get(IChoiceService);
|
||||
|
||||
function allowSetForegroundWindow(service: LaunchChannelClient): TPromise<void> {
|
||||
let promise = TPromise.wrap<void>(void 0);
|
||||
@@ -199,7 +203,7 @@ function setupIPC(accessor: ServicesAccessor): TPromise<Server> {
|
||||
|
||||
// Log uploader
|
||||
if (environmentService.args['upload-logs']) {
|
||||
return uploadLogs(channel, requestService)
|
||||
return uploadLogs(channel, requestService, choiceService)
|
||||
.then(() => TPromise.wrapError(new ExpectedError()));
|
||||
}
|
||||
|
||||
@@ -320,7 +324,7 @@ function main() {
|
||||
return instantiationService.invokeFunction(a => createPaths(a.get(IEnvironmentService)))
|
||||
.then(() => instantiationService.invokeFunction(setupIPC))
|
||||
.then(mainIpcServer => {
|
||||
bufferLogService.logger = createSpdLogService('main', environmentService);
|
||||
bufferLogService.logger = createSpdLogService('main', bufferLogService.getLevel(), environmentService.logsPath);
|
||||
return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnv).startup();
|
||||
});
|
||||
}).done(null, err => instantiationService.invokeFunction(quit, err));
|
||||
|
||||
@@ -683,6 +683,7 @@ export class CodeMenu {
|
||||
}
|
||||
|
||||
const commands = this.createMenuItem(nls.localize({ key: 'miCommandPalette', comment: ['&& denotes a mnemonic'] }, "&&Command Palette..."), 'workbench.action.showCommands');
|
||||
const openView = this.createMenuItem(nls.localize({ key: 'miOpenView', comment: ['&& denotes a mnemonic'] }, "&&Open View..."), 'workbench.action.openView');
|
||||
|
||||
const fullscreen = new MenuItem(this.withKeybinding('workbench.action.toggleFullScreen', { label: this.mnemonicLabel(nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen")), click: () => this.windowsMainService.getLastActiveWindow().toggleFullScreen(), enabled: this.windowsMainService.getWindowCount() > 0 }));
|
||||
const toggleZenMode = this.createMenuItem(nls.localize('miToggleZenMode', "Toggle Zen Mode"), 'workbench.action.toggleZenMode');
|
||||
@@ -729,6 +730,7 @@ export class CodeMenu {
|
||||
|
||||
arrays.coalesce([
|
||||
commands,
|
||||
openView,
|
||||
__separator__(),
|
||||
explorer,
|
||||
search,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { BrowserWindow, ipcMain } from 'electron';
|
||||
import { ISharedProcess } from 'vs/platform/windows/electron-main/windows';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class SharedProcess implements ISharedProcess {
|
||||
|
||||
@@ -23,7 +24,8 @@ export class SharedProcess implements ISharedProcess {
|
||||
constructor(
|
||||
private environmentService: IEnvironmentService,
|
||||
private readonly machineId: string,
|
||||
private readonly userEnv: IProcessEnvironment
|
||||
private readonly userEnv: IProcessEnvironment,
|
||||
private readonly logService: ILogService
|
||||
) { }
|
||||
|
||||
@memoize
|
||||
@@ -75,7 +77,8 @@ export class SharedProcess implements ISharedProcess {
|
||||
ipcMain.once('handshake:hello', ({ sender }: { sender: any }) => {
|
||||
sender.send('handshake:hey there', {
|
||||
sharedIPCHandle: this.environmentService.sharedIPCHandle,
|
||||
args: this.environmentService.args
|
||||
args: this.environmentService.args,
|
||||
logLevel: this.logService.getLevel()
|
||||
});
|
||||
|
||||
ipcMain.once('handshake:im ready', () => c(null));
|
||||
|
||||
@@ -565,6 +565,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
|
||||
// Set window ID
|
||||
windowConfiguration.windowId = this._win.id;
|
||||
windowConfiguration.logLevel = this.logService.getLevel();
|
||||
|
||||
// Set zoomlevel
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
@@ -593,7 +594,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
const environment = parseArgs(process.argv);
|
||||
const config = objects.assign(environment, windowConfiguration);
|
||||
for (let key in config) {
|
||||
if (!config[key]) {
|
||||
if (config[key] === void 0 || config[key] === null || config[key] === '') {
|
||||
delete config[key]; // only send over properties that have a true value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import { getBaseLabel } from 'vs/base/common/labels';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
import { StateService } from 'vs/platform/state/node/stateService';
|
||||
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ILogService, getLogLevel } from 'vs/platform/log/common/log';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
|
||||
const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id);
|
||||
@@ -196,7 +196,7 @@ export function main(argv: ParsedArgs): TPromise<void> {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
const environmentService = new EnvironmentService(argv, process.execPath);
|
||||
const logService = createSpdLogService('cli', environmentService);
|
||||
const logService = createSpdLogService('cli', getLogLevel(environmentService), environmentService.logsPath);
|
||||
process.once('exit', () => logService.dispose());
|
||||
|
||||
logService.info('main', argv);
|
||||
|
||||
@@ -5,68 +5,48 @@
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { IStringDictionary, forEach, values, groupBy, size } from 'vs/base/common/collections';
|
||||
import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
import { IFileService, IFileChange } from 'vs/platform/files/common/files';
|
||||
import { IFileService, FileChangeType } from 'vs/platform/files/common/files';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Range, IRange } from 'vs/editor/common/core/range';
|
||||
import { Selection, ISelection } from 'vs/editor/common/core/selection';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { IIdentifiedSingleEditOperation, ITextModel, EndOfLineSequence } from 'vs/editor/common/model';
|
||||
import { IProgressRunner } from 'vs/platform/progress/common/progress';
|
||||
import { IProgressRunner, emptyProgressRunner, IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { optional } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ResourceTextEdit, ResourceFileEdit, isResourceFileEdit, isResourceTextEdit } from 'vs/editor/common/modes';
|
||||
import { getPathLabel } from 'vs/base/common/labels';
|
||||
|
||||
export interface IResourceEdit {
|
||||
resource: URI;
|
||||
range?: IRange;
|
||||
newText: string;
|
||||
newEol?: EndOfLineSequence;
|
||||
}
|
||||
|
||||
interface IRecording {
|
||||
stop(): void;
|
||||
hasChanged(resource: URI): boolean;
|
||||
allChanges(): IFileChange[];
|
||||
}
|
||||
abstract class IRecording {
|
||||
|
||||
class ChangeRecorder {
|
||||
|
||||
private _fileService: IFileService;
|
||||
|
||||
constructor(fileService?: IFileService) {
|
||||
this._fileService = fileService;
|
||||
}
|
||||
|
||||
public start(): IRecording {
|
||||
|
||||
const changes: IStringDictionary<IFileChange[]> = Object.create(null);
|
||||
static start(fileService: IFileService): IRecording {
|
||||
|
||||
const _changes = new Set<string>();
|
||||
let stop: IDisposable;
|
||||
if (this._fileService) {
|
||||
stop = this._fileService.onFileChanges((event) => {
|
||||
event.changes.forEach(change => {
|
||||
|
||||
const key = String(change.resource);
|
||||
let array = changes[key];
|
||||
|
||||
if (!array) {
|
||||
changes[key] = array = [];
|
||||
if (fileService) {
|
||||
// watch only when there is a fileservice available
|
||||
stop = fileService.onFileChanges(event => {
|
||||
for (const change of event.changes) {
|
||||
if (change.type === FileChangeType.UPDATED) {
|
||||
_changes.add(change.resource.toString());
|
||||
}
|
||||
|
||||
array.push(change);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
stop: () => { return stop && stop.dispose(); },
|
||||
hasChanged: (resource: URI) => !!changes[resource.toString()],
|
||||
allChanges: () => flatten(values(changes))
|
||||
stop() { return dispose(stop); },
|
||||
hasChanged(resource) { return _changes.has(resource.toString()); }
|
||||
};
|
||||
}
|
||||
|
||||
abstract stop(): void;
|
||||
abstract hasChanged(resource: URI): boolean;
|
||||
}
|
||||
|
||||
class EditTask implements IDisposable {
|
||||
@@ -84,26 +64,34 @@ class EditTask implements IDisposable {
|
||||
this._edits = [];
|
||||
}
|
||||
|
||||
public addEdit(edit: IResourceEdit): void {
|
||||
|
||||
if (typeof edit.newEol === 'number') {
|
||||
// honor eol-change
|
||||
this._newEol = edit.newEol;
|
||||
}
|
||||
|
||||
if (edit.range || edit.newText) {
|
||||
// create edit operation
|
||||
let range: Range;
|
||||
if (!edit.range) {
|
||||
range = this._model.getFullModelRange();
|
||||
} else {
|
||||
range = Range.lift(edit.range);
|
||||
}
|
||||
this._edits.push(EditOperation.replaceMove(range, edit.newText));
|
||||
dispose() {
|
||||
if (this._model) {
|
||||
this._modelReference.dispose();
|
||||
this._modelReference = null;
|
||||
}
|
||||
}
|
||||
|
||||
public apply(): void {
|
||||
addEdit(resourceEdit: ResourceTextEdit): void {
|
||||
|
||||
for (const edit of resourceEdit.edits) {
|
||||
if (typeof edit.eol === 'number') {
|
||||
// honor eol-change
|
||||
this._newEol = edit.eol;
|
||||
}
|
||||
if (edit.range || edit.text) {
|
||||
// create edit operation
|
||||
let range: Range;
|
||||
if (!edit.range) {
|
||||
range = this._model.getFullModelRange();
|
||||
} else {
|
||||
range = Range.lift(edit.range);
|
||||
}
|
||||
this._edits.push(EditOperation.replaceMove(range, edit.text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply(): void {
|
||||
if (this._edits.length > 0) {
|
||||
|
||||
this._edits = this._edits.map((value, index) => ({ value, index })).sort((a, b) => {
|
||||
@@ -160,16 +148,10 @@ class EditTask implements IDisposable {
|
||||
return [this._endCursorSelection];
|
||||
}
|
||||
|
||||
public getEndCursorSelection(): Selection {
|
||||
getEndCursorSelection(): Selection {
|
||||
return this._endCursorSelection;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
if (this._model) {
|
||||
this._modelReference.dispose();
|
||||
this._modelReference = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SourceModelEditTask extends EditTask {
|
||||
@@ -189,34 +171,42 @@ class SourceModelEditTask extends EditTask {
|
||||
class BulkEditModel implements IDisposable {
|
||||
|
||||
private _textModelResolverService: ITextModelService;
|
||||
private _numberOfResourcesToModify: number = 0;
|
||||
private _edits: IStringDictionary<IResourceEdit[]> = Object.create(null);
|
||||
private _edits = new Map<string, ResourceTextEdit[]>();
|
||||
private _tasks: EditTask[];
|
||||
private _sourceModel: URI;
|
||||
private _sourceSelections: Selection[];
|
||||
private _sourceModelTask: SourceModelEditTask;
|
||||
private _progress: IProgress<void>;
|
||||
|
||||
constructor(textModelResolverService: ITextModelService, sourceModel: URI, sourceSelections: Selection[], edits: IResourceEdit[], private progress: IProgressRunner = null) {
|
||||
constructor(
|
||||
textModelResolverService: ITextModelService,
|
||||
editor: ICodeEditor,
|
||||
edits: ResourceTextEdit[],
|
||||
progress: IProgress<void>
|
||||
) {
|
||||
this._textModelResolverService = textModelResolverService;
|
||||
this._sourceModel = sourceModel;
|
||||
this._sourceSelections = sourceSelections;
|
||||
this._sourceModelTask = null;
|
||||
this._sourceModel = editor ? editor.getModel().uri : undefined;
|
||||
this._sourceSelections = editor ? editor.getSelections() : undefined;
|
||||
this._sourceModelTask = undefined;
|
||||
this._progress = progress;
|
||||
|
||||
for (let edit of edits) {
|
||||
this._addEdit(edit);
|
||||
}
|
||||
edits.forEach(this.addEdit, this);
|
||||
}
|
||||
|
||||
private _addEdit(edit: IResourceEdit): void {
|
||||
let array = this._edits[edit.resource.toString()];
|
||||
dispose(): void {
|
||||
this._tasks = dispose(this._tasks);
|
||||
}
|
||||
|
||||
addEdit(edit: ResourceTextEdit): void {
|
||||
let array = this._edits.get(edit.resource.toString());
|
||||
if (!array) {
|
||||
this._edits[edit.resource.toString()] = array = [];
|
||||
this._numberOfResourcesToModify += 1;
|
||||
array = [];
|
||||
this._edits.set(edit.resource.toString(), array);
|
||||
}
|
||||
array.push(edit);
|
||||
}
|
||||
|
||||
public prepare(): TPromise<BulkEditModel> {
|
||||
async prepare(): TPromise<BulkEditModel> {
|
||||
|
||||
if (this._tasks) {
|
||||
throw new Error('illegal state - already prepared');
|
||||
@@ -225,145 +215,80 @@ class BulkEditModel implements IDisposable {
|
||||
this._tasks = [];
|
||||
const promises: TPromise<any>[] = [];
|
||||
|
||||
if (this.progress) {
|
||||
this.progress.total(this._numberOfResourcesToModify * 2);
|
||||
}
|
||||
|
||||
forEach(this._edits, entry => {
|
||||
const promise = this._textModelResolverService.createModelReference(URI.parse(entry.key)).then(ref => {
|
||||
this._edits.forEach((value, key) => {
|
||||
const promise = this._textModelResolverService.createModelReference(URI.parse(key)).then(ref => {
|
||||
const model = ref.object;
|
||||
|
||||
if (!model || !model.textEditorModel) {
|
||||
throw new Error(`Cannot load file ${entry.key}`);
|
||||
throw new Error(`Cannot load file ${key}`);
|
||||
}
|
||||
|
||||
const textEditorModel = model.textEditorModel;
|
||||
let task: EditTask;
|
||||
|
||||
if (this._sourceModel && textEditorModel.uri.toString() === this._sourceModel.toString()) {
|
||||
if (this._sourceModel && model.textEditorModel.uri.toString() === this._sourceModel.toString()) {
|
||||
this._sourceModelTask = new SourceModelEditTask(ref, this._sourceSelections);
|
||||
task = this._sourceModelTask;
|
||||
} else {
|
||||
task = new EditTask(ref);
|
||||
}
|
||||
|
||||
entry.value.forEach(edit => task.addEdit(edit));
|
||||
value.forEach(edit => task.addEdit(edit));
|
||||
this._tasks.push(task);
|
||||
if (this.progress) {
|
||||
this.progress.worked(1);
|
||||
}
|
||||
this._progress.report(undefined);
|
||||
});
|
||||
promises.push(promise);
|
||||
});
|
||||
|
||||
await TPromise.join(promises);
|
||||
|
||||
return TPromise.join(promises).then(_ => this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public apply(): Selection {
|
||||
this._tasks.forEach(task => this.applyTask(task));
|
||||
let r: Selection = null;
|
||||
if (this._sourceModelTask) {
|
||||
r = this._sourceModelTask.getEndCursorSelection();
|
||||
apply(): Selection {
|
||||
for (const task of this._tasks) {
|
||||
task.apply();
|
||||
this._progress.report(undefined);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private applyTask(task: EditTask): void {
|
||||
task.apply();
|
||||
if (this.progress) {
|
||||
this.progress.worked(1);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._tasks = dispose(this._tasks);
|
||||
return this._sourceModelTask
|
||||
? this._sourceModelTask.getEndCursorSelection()
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export interface BulkEdit {
|
||||
progress(progress: IProgressRunner): void;
|
||||
add(edit: IResourceEdit[]): void;
|
||||
finish(): TPromise<ISelection>;
|
||||
ariaMessage(): string;
|
||||
}
|
||||
export type Edit = ResourceFileEdit | ResourceTextEdit;
|
||||
|
||||
export function bulkEdit(textModelResolverService: ITextModelService, editor: ICodeEditor, edits: IResourceEdit[], fileService?: IFileService, progress: IProgressRunner = null): TPromise<any> {
|
||||
let bulk = createBulkEdit(textModelResolverService, editor, fileService);
|
||||
bulk.add(edits);
|
||||
bulk.progress(progress);
|
||||
return bulk.finish();
|
||||
}
|
||||
export class BulkEdit {
|
||||
|
||||
export function createBulkEdit(textModelResolverService: ITextModelService, editor?: ICodeEditor, fileService?: IFileService): BulkEdit {
|
||||
|
||||
let all: IResourceEdit[] = [];
|
||||
let recording = new ChangeRecorder(fileService).start();
|
||||
let progressRunner: IProgressRunner;
|
||||
|
||||
function progress(progress: IProgressRunner) {
|
||||
progressRunner = progress;
|
||||
static perform(edits: Edit[], textModelService: ITextModelService, fileService: IFileService, editor: ICodeEditor): TPromise<any> {
|
||||
const edit = new BulkEdit(editor, null, textModelService, fileService);
|
||||
edit.add(edits);
|
||||
return edit.perform();
|
||||
}
|
||||
|
||||
function add(edits: IResourceEdit[]): void {
|
||||
all.push(...edits);
|
||||
private _edits: Edit[] = [];
|
||||
private _editor: ICodeEditor;
|
||||
private _progress: IProgressRunner;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
progress: IProgressRunner,
|
||||
@ITextModelService private _textModelService: ITextModelService,
|
||||
@optional(IFileService) private _fileService: IFileService
|
||||
) {
|
||||
this._editor = editor;
|
||||
this._progress = progress || emptyProgressRunner;
|
||||
}
|
||||
|
||||
function getConcurrentEdits() {
|
||||
let names: string[];
|
||||
for (let edit of all) {
|
||||
if (recording.hasChanged(edit.resource)) {
|
||||
if (!names) {
|
||||
names = [];
|
||||
}
|
||||
names.push(edit.resource.fsPath);
|
||||
}
|
||||
add(edits: Edit[] | Edit): void {
|
||||
if (Array.isArray(edits)) {
|
||||
this._edits.push(...edits);
|
||||
} else {
|
||||
this._edits.push(edits);
|
||||
}
|
||||
if (names) {
|
||||
return nls.localize('conflict', "These files have changed in the meantime: {0}", names.join(', '));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function finish(): TPromise<ISelection> {
|
||||
|
||||
if (all.length === 0) {
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
|
||||
let concurrentEdits = getConcurrentEdits();
|
||||
if (concurrentEdits) {
|
||||
return TPromise.wrapError<ISelection>(new Error(concurrentEdits));
|
||||
}
|
||||
|
||||
let uri: URI;
|
||||
let selections: Selection[];
|
||||
|
||||
if (editor && editor.getModel()) {
|
||||
uri = editor.getModel().uri;
|
||||
selections = editor.getSelections();
|
||||
}
|
||||
|
||||
const model = new BulkEditModel(textModelResolverService, uri, selections, all, progressRunner);
|
||||
|
||||
return model.prepare().then(_ => {
|
||||
|
||||
let concurrentEdits = getConcurrentEdits();
|
||||
if (concurrentEdits) {
|
||||
throw new Error(concurrentEdits);
|
||||
}
|
||||
|
||||
recording.stop();
|
||||
|
||||
const result = model.apply();
|
||||
model.dispose();
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function ariaMessage(): string {
|
||||
let editCount = all.length;
|
||||
let resourceCount = size(groupBy(all, edit => edit.resource.toString()));
|
||||
ariaMessage(): string {
|
||||
const editCount = this._edits.reduce((prev, cur) => isResourceFileEdit(cur) ? prev : prev + cur.edits.length, 0);
|
||||
const resourceCount = this._edits.length;
|
||||
if (editCount === 0) {
|
||||
return nls.localize('summary.0', "Made no edits");
|
||||
} else if (editCount > 1 && resourceCount > 1) {
|
||||
@@ -373,10 +298,84 @@ export function createBulkEdit(textModelResolverService: ITextModelService, edit
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
progress,
|
||||
add,
|
||||
finish,
|
||||
ariaMessage
|
||||
};
|
||||
async perform(): TPromise<Selection> {
|
||||
|
||||
let seen = new Set<string>();
|
||||
let total = 0;
|
||||
|
||||
const groups: Edit[][] = [];
|
||||
let group: Edit[];
|
||||
for (const edit of this._edits) {
|
||||
if (!group
|
||||
|| (isResourceFileEdit(group[0]) && !isResourceFileEdit(edit))
|
||||
|| (isResourceTextEdit(group[0]) && !isResourceTextEdit(edit))
|
||||
) {
|
||||
group = [];
|
||||
groups.push(group);
|
||||
}
|
||||
group.push(edit);
|
||||
|
||||
if (isResourceFileEdit(edit)) {
|
||||
total += 1;
|
||||
} else if (!seen.has(edit.resource.toString())) {
|
||||
seen.add(edit.resource.toString());
|
||||
total += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// define total work and progress callback
|
||||
// for child operations
|
||||
this._progress.total(total);
|
||||
let progress: IProgress<void> = { report: _ => this._progress.worked(1) };
|
||||
|
||||
// do it. return the last selection computed
|
||||
// by a text change (can be undefined then)
|
||||
let res: Selection = undefined;
|
||||
for (const group of groups) {
|
||||
if (isResourceFileEdit(group[0])) {
|
||||
await this._performFileEdits(<ResourceFileEdit[]>group, progress);
|
||||
} else {
|
||||
res = await this._performTextEdits(<ResourceTextEdit[]>group, progress) || res;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private async _performFileEdits(edits: ResourceFileEdit[], progress: IProgress<void>) {
|
||||
for (const edit of edits) {
|
||||
|
||||
progress.report(undefined);
|
||||
|
||||
if (edit.newUri && edit.oldUri) {
|
||||
await this._fileService.moveFile(edit.oldUri, edit.newUri, false);
|
||||
} else if (!edit.newUri && edit.oldUri) {
|
||||
await this._fileService.del(edit.oldUri, true);
|
||||
} else if (edit.newUri && !edit.oldUri) {
|
||||
await this._fileService.createFile(edit.newUri, undefined, { overwrite: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _performTextEdits(edits: ResourceTextEdit[], progress: IProgress<void>): TPromise<Selection> {
|
||||
|
||||
const recording = IRecording.start(this._fileService);
|
||||
const model = new BulkEditModel(this._textModelService, this._editor, edits, progress);
|
||||
|
||||
await model.prepare();
|
||||
|
||||
const conflicts = edits
|
||||
.filter(edit => recording.hasChanged(edit.resource))
|
||||
.map(edit => getPathLabel(edit.resource));
|
||||
|
||||
recording.stop();
|
||||
|
||||
if (conflicts.length > 0) {
|
||||
model.dispose();
|
||||
throw new Error(nls.localize('conflict', "These files have changed in the meantime: {0}", conflicts.join(', ')));
|
||||
}
|
||||
|
||||
const selection = await model.apply();
|
||||
model.dispose();
|
||||
return selection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,12 +57,16 @@ export function createTextBufferFactory(text: string): model.ITextBufferFactory
|
||||
return builder.finish();
|
||||
}
|
||||
|
||||
export function createTextBufferFactoryFromStream(stream: IStringStream): TPromise<model.ITextBufferFactory> {
|
||||
export function createTextBufferFactoryFromStream(stream: IStringStream, filter?: (chunk: string) => string): TPromise<model.ITextBufferFactory> {
|
||||
return new TPromise<model.ITextBufferFactory>((c, e, p) => {
|
||||
let done = false;
|
||||
let builder = createTextBufferBuilder();
|
||||
|
||||
stream.on('data', (chunk) => {
|
||||
if (filter) {
|
||||
chunk = filter(chunk);
|
||||
}
|
||||
|
||||
builder.acceptChunk(chunk);
|
||||
});
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationReg
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import * as model from 'vs/editor/common/model';
|
||||
import { isObject } from 'vs/base/common/types';
|
||||
|
||||
/**
|
||||
* Open ended enum at runtime
|
||||
@@ -825,15 +826,36 @@ export interface DocumentColorProvider {
|
||||
provideColorPresentations(model: model.ITextModel, colorInfo: IColorInformation, token: CancellationToken): IColorPresentation[] | Thenable<IColorPresentation[]>;
|
||||
}
|
||||
|
||||
export interface IResourceEdit {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function isResourceFileEdit(thing: any): thing is ResourceFileEdit {
|
||||
return isObject(thing) && (Boolean((<ResourceFileEdit>thing).newUri) || Boolean((<ResourceFileEdit>thing).oldUri));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function isResourceTextEdit(thing: any): thing is ResourceTextEdit {
|
||||
return isObject(thing) && (<ResourceTextEdit>thing).resource && Array.isArray((<ResourceTextEdit>thing).edits);
|
||||
}
|
||||
|
||||
export interface ResourceFileEdit {
|
||||
oldUri: URI;
|
||||
newUri: URI;
|
||||
}
|
||||
|
||||
export interface ResourceTextEdit {
|
||||
resource: URI;
|
||||
range: IRange;
|
||||
newText: string;
|
||||
modelVersionId?: number;
|
||||
edits: TextEdit[];
|
||||
}
|
||||
|
||||
export interface WorkspaceEdit {
|
||||
edits: IResourceEdit[];
|
||||
rejectReason?: string;
|
||||
edits: Array<ResourceTextEdit | ResourceFileEdit>;
|
||||
rejectReason?: string; // TODO@joh, move to rename
|
||||
}
|
||||
|
||||
export interface RenameProvider {
|
||||
provideRenameEdits(model: model.ITextModel, position: Position, newName: string, token: CancellationToken): WorkspaceEdit | Thenable<WorkspaceEdit>;
|
||||
}
|
||||
|
||||
@@ -16,11 +16,14 @@ import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { EditorAction, registerEditorAction, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||
import { editorBracketMatchBackground, editorBracketMatchBorder } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { TrackedRangeStickiness, IModelDeltaDecoration } from 'vs/editor/common/model';
|
||||
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { TrackedRangeStickiness, IModelDeltaDecoration, OverviewRulerLane } from 'vs/editor/common/model';
|
||||
|
||||
const overviewRulerBracketMatchForeground = registerColor('editorOverviewRuler.bracketMatchForeground', { dark: '#A0A0A0', light: '#A0A0A0', hc: '#A0A0A0' }, nls.localize('overviewRulerBracketMatchForeground', 'Overview ruler marker color for matching brackets.'));
|
||||
|
||||
class JumpToBracketAction extends EditorAction {
|
||||
constructor() {
|
||||
@@ -214,7 +217,12 @@ export class BracketMatchingController extends Disposable implements editorCommo
|
||||
|
||||
private static readonly _DECORATION_OPTIONS = ModelDecorationOptions.register({
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
className: 'bracket-match'
|
||||
className: 'bracket-match',
|
||||
overviewRuler: {
|
||||
color: themeColorFromId(overviewRulerBracketMatchForeground),
|
||||
darkColor: themeColorFromId(overviewRulerBracketMatchForeground),
|
||||
position: OverviewRulerLane.Center
|
||||
}
|
||||
});
|
||||
|
||||
private _updateBrackets(): void {
|
||||
|
||||
@@ -396,7 +396,7 @@ export class FindModelBoundToEditorModel {
|
||||
const findScope = this._decorations.getFindScope();
|
||||
|
||||
if (findScope === null && this._state.matchesCount >= MATCHES_LIMIT) {
|
||||
// Doing a replace on the entire file that is over 1k matches
|
||||
// Doing a replace on the entire file that is over ${MATCHES_LIMIT} matches
|
||||
this._largeReplaceAll();
|
||||
} else {
|
||||
this._regularReplaceAll(findScope);
|
||||
|
||||
@@ -23,7 +23,7 @@ import { QuickFixModel, QuickFixComputeEvent } from './quickFixModel';
|
||||
import { CodeActionKind, CodeActionAutoApply } from './codeActionTrigger';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { CodeAction } from 'vs/editor/common/modes';
|
||||
import { createBulkEdit } from 'vs/editor/browser/services/bulkEdit';
|
||||
import { BulkEdit } from 'vs/editor/browser/services/bulkEdit';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
|
||||
@@ -129,9 +129,7 @@ export class QuickFixController implements IEditorContribution {
|
||||
|
||||
private async _onApplyCodeAction(action: CodeAction): TPromise<void> {
|
||||
if (action.edit) {
|
||||
const edit = createBulkEdit(this._textModelService, this._editor, this._fileService);
|
||||
edit.add(action.edit.edits);
|
||||
await edit.finish();
|
||||
await BulkEdit.perform(action.edit.edits, this._textModelService, this._fileService, this._editor);
|
||||
}
|
||||
|
||||
if (action.command) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as assert from 'assert';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { CodeActionProviderRegistry, LanguageIdentifier, CodeActionProvider, Command, WorkspaceEdit, IResourceEdit, CodeAction, CodeActionContext } from 'vs/editor/common/modes';
|
||||
import { CodeActionProviderRegistry, LanguageIdentifier, CodeActionProvider, Command, WorkspaceEdit, ResourceTextEdit, CodeAction, CodeActionContext } from 'vs/editor/common/modes';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { getCodeActions } from 'vs/editor/contrib/quickFix/quickFix';
|
||||
@@ -58,7 +58,7 @@ suite('QuickFix', () => {
|
||||
bcd: {
|
||||
diagnostics: [],
|
||||
edit: new class implements WorkspaceEdit {
|
||||
edits: IResourceEdit[];
|
||||
edits: ResourceTextEdit[];
|
||||
},
|
||||
title: 'abc'
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { registerEditorAction, registerEditorContribution, ServicesAccessor, Edi
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { createBulkEdit } from 'vs/editor/browser/services/bulkEdit';
|
||||
import { BulkEdit } from 'vs/editor/browser/services/bulkEdit';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import RenameInputField from './renameInputField';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
@@ -147,9 +147,7 @@ class RenameController implements IEditorContribution {
|
||||
this._renameInputVisible.reset();
|
||||
this.editor.focus();
|
||||
|
||||
// start recording of file changes so that we can figure out if a file that
|
||||
// is to be renamed conflicts with another (concurrent) modification
|
||||
const edit = createBulkEdit(this._textModelResolverService, <ICodeEditor>this.editor, this._fileService);
|
||||
const edit = new BulkEdit(this.editor, null, this._textModelResolverService, this._fileService);
|
||||
const state = new EditorState(this.editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection | CodeEditorStateFlag.Scroll);
|
||||
|
||||
const renameOperation = rename(this.editor.getModel(), this.editor.getPosition(), newName).then(result => {
|
||||
@@ -163,7 +161,7 @@ class RenameController implements IEditorContribution {
|
||||
}
|
||||
edit.add(result.edits);
|
||||
|
||||
return edit.finish().then(selection => {
|
||||
return edit.perform().then(selection => {
|
||||
if (selection) {
|
||||
this.editor.setSelection(selection);
|
||||
}
|
||||
|
||||
@@ -42,38 +42,66 @@ for (let fileSize of fileSizes) {
|
||||
iterations: 10
|
||||
});
|
||||
|
||||
for (let i of [10, 100, 1000]) {
|
||||
editsSuite.add({
|
||||
name: `apply ${i} edits`,
|
||||
buildBuffer: (textBufferBuilder: ITextBufferBuilder) => {
|
||||
chunks.forEach(ck => textBufferBuilder.acceptChunk(ck));
|
||||
return textBufferBuilder.finish();
|
||||
},
|
||||
preCycle: (textBuffer) => {
|
||||
return textBuffer;
|
||||
},
|
||||
fn: (textBuffer) => {
|
||||
// for line model, this loop doesn't reflect the real situation.
|
||||
for (let k = 0; k < edits.length && k < i; k++) {
|
||||
textBuffer.applyEdits([edits[k]], false);
|
||||
}
|
||||
editsSuite.add({
|
||||
name: `apply 1000 edits`,
|
||||
buildBuffer: (textBufferBuilder: ITextBufferBuilder) => {
|
||||
chunks.forEach(ck => textBufferBuilder.acceptChunk(ck));
|
||||
return textBufferBuilder.finish();
|
||||
},
|
||||
preCycle: (textBuffer) => {
|
||||
return textBuffer;
|
||||
},
|
||||
fn: (textBuffer) => {
|
||||
// for line model, this loop doesn't reflect the real situation.
|
||||
for (let k = 0; k < edits.length; k++) {
|
||||
textBuffer.applyEdits([edits[k]], false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
editsSuite.add({
|
||||
name: `Read all lines after ${i} edits`,
|
||||
buildBuffer: (textBufferBuilder: ITextBufferBuilder) => {
|
||||
chunks.forEach(ck => textBufferBuilder.acceptChunk(ck));
|
||||
return textBufferBuilder.finish();
|
||||
},
|
||||
preCycle: (textBuffer) => {
|
||||
for (let k = 0; k < edits.length && k < i; k++) {
|
||||
textBuffer.applyEdits([edits[k]], false);
|
||||
}
|
||||
return textBuffer;
|
||||
},
|
||||
fn: (textBuffer) => {
|
||||
for (let j = 0, len = textBuffer.getLineCount(); j < len; j++) {
|
||||
editsSuite.add({
|
||||
name: `Read all lines after 1000 edits`,
|
||||
buildBuffer: (textBufferBuilder: ITextBufferBuilder) => {
|
||||
chunks.forEach(ck => textBufferBuilder.acceptChunk(ck));
|
||||
return textBufferBuilder.finish();
|
||||
},
|
||||
preCycle: (textBuffer) => {
|
||||
for (let k = 0; k < edits.length; k++) {
|
||||
textBuffer.applyEdits([edits[k]], false);
|
||||
}
|
||||
return textBuffer;
|
||||
},
|
||||
fn: (textBuffer) => {
|
||||
for (let j = 0, len = textBuffer.getLineCount(); j < len; j++) {
|
||||
var str = textBuffer.getLineContent(j + 1);
|
||||
let firstChar = str.charCodeAt(0);
|
||||
let lastChar = str.charCodeAt(str.length - 1);
|
||||
firstChar = firstChar - lastChar;
|
||||
lastChar = firstChar + lastChar;
|
||||
firstChar = lastChar - firstChar;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
editsSuite.add({
|
||||
name: `Read 10 random windows after 1000 edits`,
|
||||
buildBuffer: (textBufferBuilder: ITextBufferBuilder) => {
|
||||
chunks.forEach(ck => textBufferBuilder.acceptChunk(ck));
|
||||
return textBufferBuilder.finish();
|
||||
},
|
||||
preCycle: (textBuffer) => {
|
||||
for (let k = 0; k < edits.length; k++) {
|
||||
textBuffer.applyEdits([edits[k]], false);
|
||||
}
|
||||
return textBuffer;
|
||||
},
|
||||
fn: (textBuffer) => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
let minLine = 1;
|
||||
let maxLine = textBuffer.getLineCount();
|
||||
let startLine = getRandomInt(minLine, Math.max(minLine, maxLine - 100));
|
||||
let endLine = Math.min(maxLine, startLine + 100);
|
||||
for (let j = startLine; j < endLine; j++) {
|
||||
var str = textBuffer.getLineContent(j + 1);
|
||||
let firstChar = str.charCodeAt(0);
|
||||
let lastChar = str.charCodeAt(str.length - 1);
|
||||
@@ -82,57 +110,27 @@ for (let fileSize of fileSizes) {
|
||||
firstChar = lastChar - firstChar;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
editsSuite.add({
|
||||
name: `Read 10 random windows after ${i} edits`,
|
||||
buildBuffer: (textBufferBuilder: ITextBufferBuilder) => {
|
||||
chunks.forEach(ck => textBufferBuilder.acceptChunk(ck));
|
||||
return textBufferBuilder.finish();
|
||||
},
|
||||
preCycle: (textBuffer) => {
|
||||
for (let k = 0; k < edits.length && k < i; k++) {
|
||||
textBuffer.applyEdits([edits[k]], false);
|
||||
}
|
||||
return textBuffer;
|
||||
},
|
||||
fn: (textBuffer) => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
let minLine = 1;
|
||||
let maxLine = textBuffer.getLineCount();
|
||||
let startLine = getRandomInt(minLine, Math.max(minLine, maxLine - 100));
|
||||
let endLine = Math.min(maxLine, startLine + 100);
|
||||
for (let j = startLine; j < endLine; j++) {
|
||||
var str = textBuffer.getLineContent(j + 1);
|
||||
let firstChar = str.charCodeAt(0);
|
||||
let lastChar = str.charCodeAt(str.length - 1);
|
||||
firstChar = firstChar - lastChar;
|
||||
lastChar = firstChar + lastChar;
|
||||
firstChar = lastChar - firstChar;
|
||||
}
|
||||
}
|
||||
editsSuite.add({
|
||||
name: `save file after 1000 edits`,
|
||||
buildBuffer: (textBufferBuilder: ITextBufferBuilder) => {
|
||||
chunks.forEach(ck => textBufferBuilder.acceptChunk(ck));
|
||||
return textBufferBuilder.finish();
|
||||
},
|
||||
preCycle: (textBuffer) => {
|
||||
for (let k = 0; k < edits.length; k++) {
|
||||
textBuffer.applyEdits([edits[k]], false);
|
||||
}
|
||||
});
|
||||
|
||||
editsSuite.add({
|
||||
name: `save file after ${i} edits`,
|
||||
buildBuffer: (textBufferBuilder: ITextBufferBuilder) => {
|
||||
chunks.forEach(ck => textBufferBuilder.acceptChunk(ck));
|
||||
return textBufferBuilder.finish();
|
||||
},
|
||||
preCycle: (textBuffer) => {
|
||||
for (let k = 0; k < edits.length && k < i; k++) {
|
||||
textBuffer.applyEdits([edits[k]], false);
|
||||
}
|
||||
return textBuffer;
|
||||
},
|
||||
fn: (textBuffer) => {
|
||||
const lineCount = textBuffer.getLineCount();
|
||||
const fullModelRange = new Range(1, 1, lineCount, textBuffer.getLineLength(lineCount) + 1);
|
||||
textBuffer.getValueInRange(fullModelRange, EndOfLinePreference.LF);
|
||||
}
|
||||
});
|
||||
}
|
||||
return textBuffer;
|
||||
},
|
||||
fn: (textBuffer) => {
|
||||
const lineCount = textBuffer.getLineCount();
|
||||
const fullModelRange = new Range(1, 1, lineCount, textBuffer.getLineLength(lineCount) + 1);
|
||||
textBuffer.getValueInRange(fullModelRange, EndOfLinePreference.LF);
|
||||
}
|
||||
});
|
||||
|
||||
editsSuite.run();
|
||||
}
|
||||
|
||||
Vendored
+9
-4
@@ -4913,14 +4913,19 @@ declare module monaco.languages {
|
||||
provideColorPresentations(model: editor.ITextModel, colorInfo: IColorInformation, token: CancellationToken): IColorPresentation[] | Thenable<IColorPresentation[]>;
|
||||
}
|
||||
|
||||
export interface IResourceEdit {
|
||||
export interface ResourceFileEdit {
|
||||
oldUri: Uri;
|
||||
newUri: Uri;
|
||||
}
|
||||
|
||||
export interface ResourceTextEdit {
|
||||
resource: Uri;
|
||||
range: IRange;
|
||||
newText: string;
|
||||
modelVersionId?: number;
|
||||
edits: TextEdit[];
|
||||
}
|
||||
|
||||
export interface WorkspaceEdit {
|
||||
edits: IResourceEdit[];
|
||||
edits: Array<ResourceTextEdit | ResourceFileEdit>;
|
||||
rejectReason?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ suite('BackupMainService', () => {
|
||||
class TestBackupMainService extends BackupMainService {
|
||||
|
||||
constructor(backupHome: string, backupWorkspacesPath: string, configService: TestConfigurationService) {
|
||||
super(environmentService, configService, new ConsoleLogMainService(environmentService));
|
||||
super(environmentService, configService, new ConsoleLogMainService());
|
||||
|
||||
this.backupHome = backupHome;
|
||||
this.workspacesJsonPath = backupWorkspacesPath;
|
||||
|
||||
@@ -143,7 +143,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
|
||||
}
|
||||
|
||||
public notifyConfigurationSchemaUpdated(configuration: IConfigurationNode) {
|
||||
contributionRegistry.registerSchema(editorConfigurationSchemaId, this.editorConfigurationSchema);
|
||||
contributionRegistry.notifySchemaChanged(editorConfigurationSchemaId);
|
||||
}
|
||||
|
||||
public registerOverrideIdentifiers(overrideIdentifiers: string[]): void {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { LogLevel } from 'vs/platform/log/common/log';
|
||||
|
||||
export interface ParsedArgs {
|
||||
[arg: string]: any;
|
||||
@@ -117,7 +116,6 @@ export interface IEnvironmentService {
|
||||
// logging
|
||||
logsPath: string;
|
||||
verbose: boolean;
|
||||
logLevel: LogLevel;
|
||||
|
||||
skipGettingStarted: boolean | undefined;
|
||||
skipReleaseNotes: boolean | undefined;
|
||||
|
||||
@@ -12,7 +12,6 @@ import URI from 'vs/base/common/uri';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { LogLevel } from 'vs/platform/log/common/log';
|
||||
import { toLocalISOString } from 'vs/base/common/date';
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
|
||||
@@ -152,33 +151,6 @@ export class EnvironmentService implements IEnvironmentService {
|
||||
get isBuilt(): boolean { return !process.env['VSCODE_DEV']; }
|
||||
get verbose(): boolean { return this._args.verbose; }
|
||||
|
||||
@memoize
|
||||
get logLevel(): LogLevel {
|
||||
if (this.verbose) {
|
||||
return LogLevel.Trace;
|
||||
}
|
||||
if (typeof this._args.log === 'string') {
|
||||
const logLevel = this._args.log.toLowerCase();
|
||||
switch (logLevel) {
|
||||
case 'trace':
|
||||
return LogLevel.Trace;
|
||||
case 'debug':
|
||||
return LogLevel.Debug;
|
||||
case 'info':
|
||||
return LogLevel.Info;
|
||||
case 'warn':
|
||||
return LogLevel.Warning;
|
||||
case 'error':
|
||||
return LogLevel.Error;
|
||||
case 'critical':
|
||||
return LogLevel.Critical;
|
||||
case 'off':
|
||||
return LogLevel.Off;
|
||||
}
|
||||
}
|
||||
return LogLevel.Info;
|
||||
}
|
||||
|
||||
get wait(): boolean { return this._args.wait; }
|
||||
get logExtensionHostCommunication(): boolean { return this._args.logExtensionHostCommunication; }
|
||||
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
'use strict';
|
||||
|
||||
import { TPromise, Promise } from 'vs/base/common/winjs.base';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IIssueService, IssueReporterStyles } from 'vs/platform/issue/common/issue';
|
||||
import { BrowserWindow, ipcMain } from 'electron';
|
||||
import { ILaunchService } from 'vs/code/electron-main/launch';
|
||||
import { buildDiagnostics, DiagnosticInfo } from 'vs/code/electron-main/diagnostics';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
||||
const DEFAULT_BACKGROUND_COLOR = '#1E1E1E';
|
||||
|
||||
export class IssueService implements IIssueService {
|
||||
_serviceBrand: any;
|
||||
_issueWindow: BrowserWindow;
|
||||
@@ -39,8 +42,9 @@ export class IssueService implements IIssueService {
|
||||
this._issueWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 900,
|
||||
title: 'Issue Reporter',
|
||||
alwaysOnTop: true
|
||||
title: localize('issueReporter', "Issue Reporter"),
|
||||
parent: BrowserWindow.getFocusedWindow(),
|
||||
backgroundColor: theme && theme.backgroundColor || DEFAULT_BACKGROUND_COLOR
|
||||
});
|
||||
|
||||
this._issueWindow.setMenuBarVisibility(false); // workaround for now, until a menu is implemented
|
||||
|
||||
@@ -25,6 +25,13 @@ export interface IJSONContributionRegistry {
|
||||
*/
|
||||
registerSchema(uri: string, unresolvedSchemaContent: IJSONSchema): void;
|
||||
|
||||
|
||||
/**
|
||||
* Notifies all listeneres that the content of the given schema has changed.
|
||||
* @param uri The id of the schema
|
||||
*/
|
||||
notifySchemaChanged(uri: string): void;
|
||||
|
||||
/**
|
||||
* Get all schemas
|
||||
*/
|
||||
@@ -60,6 +67,10 @@ class JSONContributionRegistry implements IJSONContributionRegistry {
|
||||
this._onDidChangeSchema.fire(uri);
|
||||
}
|
||||
|
||||
public notifySchemaChanged(uri: string): void {
|
||||
this._onDidChangeSchema.fire(uri);
|
||||
}
|
||||
|
||||
public getSchemaContributions(): ISchemaContributions {
|
||||
return {
|
||||
schemas: this.schemasById,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
interface ILog {
|
||||
level: LogLevel;
|
||||
@@ -24,17 +24,12 @@ function getLogFunction(logger: ILogService, level: LogLevel): Function {
|
||||
}
|
||||
}
|
||||
|
||||
export class BufferLogService implements ILogService {
|
||||
export class BufferLogService extends AbstractLogService implements ILogService {
|
||||
|
||||
_serviceBrand: any;
|
||||
private buffer: ILog[] = [];
|
||||
private _logger: ILogService | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
private level: LogLevel = LogLevel.Error
|
||||
) {
|
||||
}
|
||||
|
||||
set logger(logger: ILogService) {
|
||||
this._logger = logger;
|
||||
|
||||
@@ -46,19 +41,11 @@ export class BufferLogService implements ILogService {
|
||||
this.buffer = [];
|
||||
}
|
||||
|
||||
setLevel(logLevel: LogLevel): void {
|
||||
this.level = logLevel;
|
||||
}
|
||||
|
||||
getLevel(): LogLevel {
|
||||
return this.level;
|
||||
}
|
||||
|
||||
private _log(level: LogLevel, args: IArguments): void {
|
||||
if (this._logger) {
|
||||
const fn = getLogFunction(this._logger, level);
|
||||
fn.apply(this._logger, args);
|
||||
} else if (this.level <= level) {
|
||||
} else if (this.getLevel() <= level) {
|
||||
this.buffer.push({ level, args });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { createDecorator as createServiceDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
||||
export const ILogService = createServiceDecorator<ILogService>('logService');
|
||||
|
||||
@@ -24,9 +25,10 @@ export enum LogLevel {
|
||||
|
||||
export interface ILogService extends IDisposable {
|
||||
_serviceBrand: any;
|
||||
onDidChangeLogLevel: Event<LogLevel>;
|
||||
|
||||
setLevel(level: LogLevel): void;
|
||||
getLevel(): LogLevel;
|
||||
setLevel(level: LogLevel): void;
|
||||
trace(message: string, ...args: any[]): void;
|
||||
debug(message: string, ...args: any[]): void;
|
||||
info(message: string, ...args: any[]): void;
|
||||
@@ -35,27 +37,37 @@ export interface ILogService extends IDisposable {
|
||||
critical(message: string | Error, ...args: any[]): void;
|
||||
}
|
||||
|
||||
export class ConsoleLogMainService implements ILogService {
|
||||
export abstract class AbstractLogService extends Disposable {
|
||||
|
||||
_serviceBrand: any;
|
||||
private level: LogLevel = LogLevel.Error;
|
||||
private useColors: boolean;
|
||||
|
||||
constructor( @IEnvironmentService environmentService: IEnvironmentService) {
|
||||
this.setLevel(environmentService.logLevel);
|
||||
this.useColors = !isWindows;
|
||||
}
|
||||
private readonly _onDidChangeLogLevel: Emitter<LogLevel> = this._register(new Emitter<LogLevel>());
|
||||
readonly onDidChangeLogLevel: Event<LogLevel> = this._onDidChangeLogLevel.event;
|
||||
|
||||
setLevel(level: LogLevel): void {
|
||||
this.level = level;
|
||||
if (this.level !== level) {
|
||||
this.level = level;
|
||||
this._onDidChangeLogLevel.fire(this.level);
|
||||
}
|
||||
}
|
||||
|
||||
getLevel(): LogLevel {
|
||||
return this.level;
|
||||
}
|
||||
}
|
||||
|
||||
export class ConsoleLogMainService extends AbstractLogService implements ILogService {
|
||||
|
||||
_serviceBrand: any;
|
||||
private useColors: boolean;
|
||||
|
||||
constructor(logLevel: LogLevel = LogLevel.Error) {
|
||||
super();
|
||||
this.setLevel(logLevel);
|
||||
this.useColors = !isWindows;
|
||||
}
|
||||
|
||||
trace(message: string, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Trace) {
|
||||
if (this.getLevel() <= LogLevel.Trace) {
|
||||
if (this.useColors) {
|
||||
console.log(`\x1b[90m[main ${new Date().toLocaleTimeString()}]\x1b[0m`, message, ...args);
|
||||
} else {
|
||||
@@ -65,7 +77,7 @@ export class ConsoleLogMainService implements ILogService {
|
||||
}
|
||||
|
||||
debug(message: string, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Debug) {
|
||||
if (this.getLevel() <= LogLevel.Debug) {
|
||||
if (this.useColors) {
|
||||
console.log(`\x1b[90m[main ${new Date().toLocaleTimeString()}]\x1b[0m`, message, ...args);
|
||||
} else {
|
||||
@@ -75,7 +87,7 @@ export class ConsoleLogMainService implements ILogService {
|
||||
}
|
||||
|
||||
info(message: string, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Info) {
|
||||
if (this.getLevel() <= LogLevel.Info) {
|
||||
if (this.useColors) {
|
||||
console.log(`\x1b[90m[main ${new Date().toLocaleTimeString()}]\x1b[0m`, message, ...args);
|
||||
} else {
|
||||
@@ -85,7 +97,7 @@ export class ConsoleLogMainService implements ILogService {
|
||||
}
|
||||
|
||||
warn(message: string | Error, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Warning) {
|
||||
if (this.getLevel() <= LogLevel.Warning) {
|
||||
if (this.useColors) {
|
||||
console.warn(`\x1b[93m[main ${new Date().toLocaleTimeString()}]\x1b[0m`, message, ...args);
|
||||
} else {
|
||||
@@ -95,7 +107,7 @@ export class ConsoleLogMainService implements ILogService {
|
||||
}
|
||||
|
||||
error(message: string, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Error) {
|
||||
if (this.getLevel() <= LogLevel.Error) {
|
||||
if (this.useColors) {
|
||||
console.error(`\x1b[91m[main ${new Date().toLocaleTimeString()}]\x1b[0m`, message, ...args);
|
||||
} else {
|
||||
@@ -105,7 +117,7 @@ export class ConsoleLogMainService implements ILogService {
|
||||
}
|
||||
|
||||
critical(message: string, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Critical) {
|
||||
if (this.getLevel() <= LogLevel.Critical) {
|
||||
if (this.useColors) {
|
||||
console.error(`\x1b[90m[main ${new Date().toLocaleTimeString()}]\x1b[0m`, message, ...args);
|
||||
} else {
|
||||
@@ -119,55 +131,47 @@ export class ConsoleLogMainService implements ILogService {
|
||||
}
|
||||
}
|
||||
|
||||
export class ConsoleLogService implements ILogService {
|
||||
export class ConsoleLogService extends AbstractLogService implements ILogService {
|
||||
|
||||
_serviceBrand: any;
|
||||
private level: LogLevel = LogLevel.Error;
|
||||
|
||||
constructor( @IEnvironmentService environmentService: IEnvironmentService) {
|
||||
this.setLevel(environmentService.logLevel);
|
||||
}
|
||||
|
||||
setLevel(level: LogLevel): void {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
getLevel(): LogLevel {
|
||||
return this.level;
|
||||
constructor(logLevel: LogLevel = LogLevel.Error) {
|
||||
super();
|
||||
this.setLevel(logLevel);
|
||||
}
|
||||
|
||||
trace(message: string, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Trace) {
|
||||
if (this.getLevel() <= LogLevel.Trace) {
|
||||
console.log('%cTRACE', 'color: #888', message, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
debug(message: string, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Debug) {
|
||||
if (this.getLevel() <= LogLevel.Debug) {
|
||||
console.log('%cDEBUG', 'background: #eee; color: #888', message, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
info(message: string, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Info) {
|
||||
if (this.getLevel() <= LogLevel.Info) {
|
||||
console.log('%c INFO', 'color: #33f', message, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
warn(message: string | Error, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Warning) {
|
||||
if (this.getLevel() <= LogLevel.Warning) {
|
||||
console.log('%c WARN', 'color: #993', message, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
error(message: string, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Error) {
|
||||
if (this.getLevel() <= LogLevel.Error) {
|
||||
console.log('%c ERR', 'color: #f33', message, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
critical(message: string, ...args: any[]): void {
|
||||
if (this.level <= LogLevel.Critical) {
|
||||
if (this.getLevel() <= LogLevel.Critical) {
|
||||
console.log('%cCRITI', 'background: #f33; color: white', message, ...args);
|
||||
}
|
||||
}
|
||||
@@ -175,22 +179,21 @@ export class ConsoleLogService implements ILogService {
|
||||
dispose(): void { }
|
||||
}
|
||||
|
||||
export class MultiplexLogService implements ILogService {
|
||||
export class MultiplexLogService extends AbstractLogService implements ILogService {
|
||||
_serviceBrand: any;
|
||||
|
||||
constructor(private logServices: ILogService[]) { }
|
||||
constructor(private logServices: ILogService[]) {
|
||||
super();
|
||||
if (logServices.length) {
|
||||
this.setLevel(logServices[0].getLevel());
|
||||
}
|
||||
}
|
||||
|
||||
setLevel(level: LogLevel): void {
|
||||
for (const logService of this.logServices) {
|
||||
logService.setLevel(level);
|
||||
}
|
||||
}
|
||||
|
||||
getLevel(): LogLevel {
|
||||
for (const logService of this.logServices) {
|
||||
return logService.getLevel();
|
||||
}
|
||||
return LogLevel.Info;
|
||||
super.setLevel(level);
|
||||
}
|
||||
|
||||
trace(message: string, ...args: any[]): void {
|
||||
@@ -236,8 +239,54 @@ export class MultiplexLogService implements ILogService {
|
||||
}
|
||||
}
|
||||
|
||||
export class DelegatedLogService extends Disposable implements ILogService {
|
||||
_serviceBrand: any;
|
||||
|
||||
constructor(private logService: ILogService) {
|
||||
super();
|
||||
this._register(logService);
|
||||
}
|
||||
|
||||
get onDidChangeLogLevel(): Event<LogLevel> {
|
||||
return this.logService.onDidChangeLogLevel;
|
||||
}
|
||||
|
||||
setLevel(level: LogLevel): void {
|
||||
this.logService.setLevel(level);
|
||||
}
|
||||
|
||||
getLevel(): LogLevel {
|
||||
return this.logService.getLevel();
|
||||
}
|
||||
|
||||
trace(message: string, ...args: any[]): void {
|
||||
this.logService.trace(message, ...args);
|
||||
}
|
||||
|
||||
debug(message: string, ...args: any[]): void {
|
||||
this.logService.debug(message, ...args);
|
||||
}
|
||||
|
||||
info(message: string, ...args: any[]): void {
|
||||
this.logService.info(message, ...args);
|
||||
}
|
||||
|
||||
warn(message: string, ...args: any[]): void {
|
||||
this.logService.warn(message, ...args);
|
||||
}
|
||||
|
||||
error(message: string | Error, ...args: any[]): void {
|
||||
this.logService.error(message, ...args);
|
||||
}
|
||||
|
||||
critical(message: string | Error, ...args: any[]): void {
|
||||
this.logService.critical(message, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
export class NullLogService implements ILogService {
|
||||
_serviceBrand: any;
|
||||
readonly onDidChangeLogLevel: Event<LogLevel> = new Emitter<LogLevel>().event;
|
||||
setLevel(level: LogLevel): void { }
|
||||
getLevel(): LogLevel { return LogLevel.Info; }
|
||||
trace(message: string, ...args: any[]): void { }
|
||||
@@ -248,3 +297,30 @@ export class NullLogService implements ILogService {
|
||||
critical(message: string | Error, ...args: any[]): void { }
|
||||
dispose(): void { }
|
||||
}
|
||||
|
||||
|
||||
export function getLogLevel(environmentService: IEnvironmentService): LogLevel {
|
||||
if (environmentService.verbose) {
|
||||
return LogLevel.Trace;
|
||||
}
|
||||
if (typeof environmentService.args.log === 'string') {
|
||||
const logLevel = environmentService.args.log.toLowerCase();
|
||||
switch (logLevel) {
|
||||
case 'trace':
|
||||
return LogLevel.Trace;
|
||||
case 'debug':
|
||||
return LogLevel.Debug;
|
||||
case 'info':
|
||||
return LogLevel.Info;
|
||||
case 'warn':
|
||||
return LogLevel.Warning;
|
||||
case 'error':
|
||||
return LogLevel.Error;
|
||||
case 'critical':
|
||||
return LogLevel.Critical;
|
||||
case 'off':
|
||||
return LogLevel.Off;
|
||||
}
|
||||
}
|
||||
return LogLevel.Info;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IChannel, eventToCall, eventFromCall } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { LogLevel, ILogService, DelegatedLogService } from 'vs/platform/log/common/log';
|
||||
import Event, { buffer } from 'vs/base/common/event';
|
||||
|
||||
export interface ILogLevelSetterChannel extends IChannel {
|
||||
call(command: 'event:onDidChangeLogLevel'): TPromise<LogLevel>;
|
||||
call(command: 'setLevel', logLevel: LogLevel): TPromise<void>;
|
||||
}
|
||||
|
||||
export class LogLevelSetterChannel implements ILogLevelSetterChannel {
|
||||
|
||||
onDidChangeLogLevel: Event<LogLevel>;
|
||||
|
||||
constructor(private service: ILogService) {
|
||||
this.onDidChangeLogLevel = buffer(service.onDidChangeLogLevel, true);
|
||||
}
|
||||
|
||||
call(command: string, arg?: any): TPromise<any> {
|
||||
switch (command) {
|
||||
case 'event:onDidChangeLogLevel': return eventToCall(this.onDidChangeLogLevel);
|
||||
case 'setLevel': this.service.setLevel(arg); return TPromise.as(null);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class LogLevelSetterChannelClient {
|
||||
|
||||
constructor(private channel: ILogLevelSetterChannel) { }
|
||||
|
||||
private _onDidChangeLogLevel = eventFromCall<LogLevel>(this.channel, 'event:onDidChangeLogLevel');
|
||||
get onDidChangeLogLevel(): Event<LogLevel> { return this._onDidChangeLogLevel; }
|
||||
|
||||
setLevel(level: LogLevel): TPromise<void> {
|
||||
return this.channel.call('setLevel', level);
|
||||
}
|
||||
}
|
||||
|
||||
export class FollowerLogService extends DelegatedLogService implements ILogService {
|
||||
_serviceBrand: any;
|
||||
|
||||
constructor(private master: LogLevelSetterChannelClient, logService: ILogService) {
|
||||
super(logService);
|
||||
this._register(master.onDidChangeLogLevel(level => logService.setLevel(level)));
|
||||
}
|
||||
|
||||
setLevel(level: LogLevel): void {
|
||||
this.master.setLevel(level);
|
||||
}
|
||||
}
|
||||
@@ -6,69 +6,62 @@
|
||||
'use strict';
|
||||
|
||||
import * as path from 'path';
|
||||
import { ILogService, LogLevel, NullLogService } from 'vs/platform/log/common/log';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ILogService, LogLevel, NullLogService, AbstractLogService } from 'vs/platform/log/common/log';
|
||||
import { RotatingLogger, setAsyncMode } from 'spdlog';
|
||||
|
||||
export function createSpdLogService(processName: string, environmentService: IEnvironmentService, logsSubfolder?: string): ILogService {
|
||||
export function createSpdLogService(processName: string, logLevel: LogLevel, logsFolder: string, logsSubfolder?: string): ILogService {
|
||||
try {
|
||||
setAsyncMode(8192, 2000);
|
||||
const logsDirPath = logsSubfolder ? path.join(environmentService.logsPath, logsSubfolder) : environmentService.logsPath;
|
||||
const logsDirPath = logsSubfolder ? path.join(logsFolder, logsSubfolder) : logsFolder;
|
||||
const logfilePath = path.join(logsDirPath, `${processName}.log`);
|
||||
const logger = new RotatingLogger(processName, logfilePath, 1024 * 1024 * 5, 6);
|
||||
logger.setLevel(0);
|
||||
|
||||
return new SpdLogService(logger, environmentService.logLevel);
|
||||
return new SpdLogService(logger, logLevel);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return new NullLogService();
|
||||
}
|
||||
|
||||
class SpdLogService implements ILogService {
|
||||
class SpdLogService extends AbstractLogService implements ILogService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
constructor(
|
||||
private readonly logger: RotatingLogger,
|
||||
private level: LogLevel = LogLevel.Error
|
||||
level: LogLevel = LogLevel.Error
|
||||
) {
|
||||
}
|
||||
|
||||
setLevel(logLevel: LogLevel): void {
|
||||
this.level = logLevel;
|
||||
}
|
||||
|
||||
getLevel(): LogLevel {
|
||||
return this.level;
|
||||
super();
|
||||
this.setLevel(level);
|
||||
}
|
||||
|
||||
trace(): void {
|
||||
if (this.level <= LogLevel.Trace) {
|
||||
if (this.getLevel() <= LogLevel.Trace) {
|
||||
this.logger.trace(this.format(arguments));
|
||||
}
|
||||
}
|
||||
|
||||
debug(): void {
|
||||
if (this.level <= LogLevel.Debug) {
|
||||
if (this.getLevel() <= LogLevel.Debug) {
|
||||
this.logger.debug(this.format(arguments));
|
||||
}
|
||||
}
|
||||
|
||||
info(): void {
|
||||
if (this.level <= LogLevel.Info) {
|
||||
if (this.getLevel() <= LogLevel.Info) {
|
||||
this.logger.info(this.format(arguments));
|
||||
}
|
||||
}
|
||||
|
||||
warn(): void {
|
||||
if (this.level <= LogLevel.Warning) {
|
||||
if (this.getLevel() <= LogLevel.Warning) {
|
||||
this.logger.warn(this.format(arguments));
|
||||
}
|
||||
}
|
||||
|
||||
error(): void {
|
||||
if (this.level <= LogLevel.Error) {
|
||||
if (this.getLevel() <= LogLevel.Error) {
|
||||
const arg = arguments[0];
|
||||
|
||||
if (arg instanceof Error) {
|
||||
@@ -82,7 +75,7 @@ class SpdLogService implements ILogService {
|
||||
}
|
||||
|
||||
critical(): void {
|
||||
if (this.level <= LogLevel.Critical) {
|
||||
if (this.getLevel() <= LogLevel.Critical) {
|
||||
this.logger.critical(this.format(arguments));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,12 @@ export interface IProgressRunner {
|
||||
done(): void;
|
||||
}
|
||||
|
||||
export const emptyProgressRunner: IProgressRunner = Object.freeze({
|
||||
total() { },
|
||||
worked() { },
|
||||
done() { }
|
||||
});
|
||||
|
||||
export interface IProgress<T> {
|
||||
report(item: T): void;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ export function resolveCommonProperties(commit: string, version: string, machine
|
||||
result['commitHash'] = commit;
|
||||
// __GDPR__COMMON__ "version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
result['version'] = version;
|
||||
// __GDPR__COMMON__ "common.platformVersion" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
result['common.platformVersion'] = (os.release() || '').replace(/^(\d+)(\.\d+)?(\.\d+)?(.*)/, '$1$2$3');
|
||||
// __GDPR__COMMON__ "common.osVersion" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
result['common.osVersion'] = os.release();
|
||||
result['common.osVersion'] = result['common.platformVersion']; // TODO: Drop this after the move to Nova
|
||||
// __GDPR__COMMON__ "common.platform" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
result['common.platform'] = Platform.Platform[Platform.platform];
|
||||
// __GDPR__COMMON__ "common.nodePlatform" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as os from 'os';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as uuid from 'vs/base/common/uuid';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
@@ -15,8 +14,6 @@ export function resolveWorkbenchCommonProperties(storageService: IStorageService
|
||||
result['common.version.shell'] = process.versions && (<any>process).versions['electron'];
|
||||
// __GDPR__COMMON__ "common.version.renderer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
result['common.version.renderer'] = process.versions && (<any>process).versions['chrome'];
|
||||
// __GDPR__COMMON__ "common.osVersion" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
result['common.osVersion'] = os.release();
|
||||
|
||||
const lastSessionDate = storageService.get('telemetry.lastSessionDate');
|
||||
const firstSessionDate = storageService.get('telemetry.firstSessionDate') || new Date().toUTCString();
|
||||
|
||||
@@ -49,6 +49,7 @@ suite('Telemetry - common properties', function () {
|
||||
// assert.ok('common.version.shell' in first.data); // only when running on electron
|
||||
// assert.ok('common.version.renderer' in first.data);
|
||||
assert.ok('common.osVersion' in props, 'osVersion');
|
||||
assert.ok('common.platformVersion' in props, 'platformVersion');
|
||||
assert.ok('version' in props);
|
||||
assert.equal(props['common.source'], 'my.install.source');
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import { IWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/
|
||||
import { IRecentlyOpened } from 'vs/platform/history/common/history';
|
||||
import { ICommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { PerformanceEntry } from 'vs/base/common/performance';
|
||||
import { LogLevel } from 'vs/platform/log/common/log';
|
||||
|
||||
export const IWindowsService = createDecorator<IWindowsService>('windowsService');
|
||||
|
||||
@@ -297,6 +298,7 @@ export interface IAddFoldersRequest {
|
||||
export interface IWindowConfiguration extends ParsedArgs, IOpenFileRequest {
|
||||
machineId: string;
|
||||
windowId: number;
|
||||
logLevel: LogLevel;
|
||||
|
||||
appRoot: string;
|
||||
execPath: string;
|
||||
|
||||
@@ -198,7 +198,7 @@ export class Workspace implements IWorkspace {
|
||||
}
|
||||
|
||||
public toJSON(): IWorkspace {
|
||||
return { id: this.id, folders: this.folders, name: this.name };
|
||||
return { id: this.id, folders: this.folders, name: this.name, configuration: this.configuration };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ suite('WorkspacesMainService', () => {
|
||||
}
|
||||
|
||||
const environmentService = new TestEnvironmentService(parseArgs(process.argv), process.execPath);
|
||||
const logService = new ConsoleLogMainService(environmentService);
|
||||
const logService = new ConsoleLogMainService();
|
||||
|
||||
let service: TestWorkspacesMainService;
|
||||
|
||||
|
||||
Vendored
+43
-2
@@ -2495,7 +2495,8 @@ declare module 'vscode' {
|
||||
}
|
||||
|
||||
/**
|
||||
* A workspace edit represents textual changes for many documents.
|
||||
* A workspace edit represents textual and files changes for
|
||||
* multiple resources and documents.
|
||||
*/
|
||||
export class WorkspaceEdit {
|
||||
|
||||
@@ -2556,9 +2557,49 @@ declare module 'vscode' {
|
||||
/**
|
||||
* Get all text edits grouped by resource.
|
||||
*
|
||||
* @return An array of `[Uri, TextEdit[]]`-tuples.
|
||||
* @return A shallow copy of `[Uri, TextEdit[]]`-tuples.
|
||||
*/
|
||||
entries(): [Uri, TextEdit[]][];
|
||||
|
||||
/**
|
||||
* Renames a given resource in the workspace.
|
||||
*
|
||||
* @param from Uri of current resource.
|
||||
* @param to Uri of renamed resource.
|
||||
*/
|
||||
renameResource(from: Uri, to: Uri): void;
|
||||
|
||||
/**
|
||||
* Create a new resource in the workspace.
|
||||
*
|
||||
* @param uri Uri of resource to create.
|
||||
*/
|
||||
createResource(uri: Uri): void;
|
||||
|
||||
/**
|
||||
* Delete a given resource in the workspace.
|
||||
*
|
||||
* @param uri Uri of resource to delete.
|
||||
*/
|
||||
deleteResource(uri: Uri): void;
|
||||
|
||||
/**
|
||||
* Get the resource edits for this workspace edit.
|
||||
*
|
||||
* @returns A shallow copy of uri-tuples in which a rename-edit
|
||||
* is represented as `[from, to]`, a delete-operation as `[from, null]`,
|
||||
* and a create-operation as `[null, to]`;
|
||||
*/
|
||||
resourceEdits(): [Uri, Uri][];
|
||||
|
||||
/**
|
||||
* Get all edits, textual changes and file changes. The order is the order
|
||||
* in which edits have been added to this workspace edits. Textuals edits
|
||||
* are grouped and the first textual edit for a resource matters.
|
||||
*
|
||||
* @returns A shallow copy of all changes.
|
||||
*/
|
||||
allEntries(): ([Uri, TextEdit[]] | [Uri, Uri])[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,7 @@ import './mainThreadTask';
|
||||
import './mainThreadTelemetry';
|
||||
import './mainThreadTerminalService';
|
||||
import './mainThreadTreeViews';
|
||||
import './mainThreadLogService';
|
||||
import './mainThreadWindow';
|
||||
import './mainThreadWorkspace';
|
||||
|
||||
|
||||
@@ -15,17 +15,18 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { Position as EditorPosition, ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { MainThreadTextEditor } from './mainThreadEditor';
|
||||
import { ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions, WorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors';
|
||||
import { equals as objectEquals } from 'vs/base/common/objects';
|
||||
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext, IWorkspaceResourceEdit } from '../node/extHost.protocol';
|
||||
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { bulkEdit, IResourceEdit } from 'vs/editor/browser/services/bulkEdit';
|
||||
import { BulkEdit } from 'vs/editor/browser/services/bulkEdit';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { isCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { isResourceFileEdit } from 'vs/editor/common/modes';
|
||||
|
||||
export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
|
||||
@@ -210,40 +211,22 @@ export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
return TPromise.as(this._documentsAndEditors.getEditor(id).applyEdits(modelVersionId, edits, opts));
|
||||
}
|
||||
|
||||
$tryApplyWorkspaceEdit(workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean> {
|
||||
$tryApplyWorkspaceEdit(dto: WorkspaceEditDto): TPromise<boolean> {
|
||||
|
||||
const { edits } = reviveWorkspaceEditDto(dto);
|
||||
|
||||
// First check if loaded models were not changed in the meantime
|
||||
for (let i = 0, len = workspaceResourceEdits.length; i < len; i++) {
|
||||
const workspaceResourceEdit = workspaceResourceEdits[i];
|
||||
if (workspaceResourceEdit.modelVersionId) {
|
||||
const uri = URI.revive(workspaceResourceEdit.resource);
|
||||
let model = this._modelService.getModel(uri);
|
||||
if (model && model.getVersionId() !== workspaceResourceEdit.modelVersionId) {
|
||||
for (let i = 0, len = edits.length; i < len; i++) {
|
||||
const edit = edits[i];
|
||||
if (!isResourceFileEdit(edit) && edit.modelVersionId) {
|
||||
let model = this._modelService.getModel(edit.resource);
|
||||
if (model && model.getVersionId() !== edit.modelVersionId) {
|
||||
// model changed in the meantime
|
||||
return TPromise.as(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to shape expected by bulkEdit below
|
||||
let resourceEdits: IResourceEdit[] = [];
|
||||
for (let i = 0, len = workspaceResourceEdits.length; i < len; i++) {
|
||||
const workspaceResourceEdit = workspaceResourceEdits[i];
|
||||
const uri = URI.revive(workspaceResourceEdit.resource);
|
||||
const edits = workspaceResourceEdit.edits;
|
||||
|
||||
for (let j = 0, lenJ = edits.length; j < lenJ; j++) {
|
||||
const edit = edits[j];
|
||||
|
||||
resourceEdits.push({
|
||||
resource: uri,
|
||||
newText: edit.newText,
|
||||
newEol: edit.newEol,
|
||||
range: edit.range
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let codeEditor: ICodeEditor;
|
||||
let editor = this._workbenchEditorService.getActiveEditor();
|
||||
if (editor) {
|
||||
@@ -253,8 +236,7 @@ export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
}
|
||||
}
|
||||
|
||||
return bulkEdit(this._textModelResolverService, codeEditor, resourceEdits, this._fileService)
|
||||
.then(() => true);
|
||||
return BulkEdit.perform(edits, this._textModelResolverService, this._fileService, codeEditor).then(() => true);
|
||||
}
|
||||
|
||||
$tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): TPromise<boolean> {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { wireCancellationToken } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Position as EditorPosition } from 'vs/editor/common/core/position';
|
||||
import { Range as EditorRange } from 'vs/editor/common/core/range';
|
||||
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, SymbolInformationDto, WorkspaceEditDto, ResourceEditDto, CodeActionDto } from '../node/extHost.protocol';
|
||||
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, SymbolInformationDto, CodeActionDto, reviveWorkspaceEditDto } from '../node/extHost.protocol';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { IHeapService } from './mainThreadHeapService';
|
||||
@@ -86,21 +86,9 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
}
|
||||
}
|
||||
|
||||
private static _reviveResourceEditDto(data: ResourceEditDto): modes.IResourceEdit {
|
||||
data.resource = URI.revive(data.resource);
|
||||
return <modes.IResourceEdit>data;
|
||||
}
|
||||
|
||||
private static _reviveWorkspaceEditDto(data: WorkspaceEditDto): modes.WorkspaceEdit {
|
||||
if (data && data.edits) {
|
||||
data.edits.forEach(MainThreadLanguageFeatures._reviveResourceEditDto);
|
||||
}
|
||||
return <modes.WorkspaceEdit>data;
|
||||
}
|
||||
|
||||
private static _reviveCodeActionDto(data: CodeActionDto[]): modes.CodeAction[] {
|
||||
if (data) {
|
||||
data.forEach(code => MainThreadLanguageFeatures._reviveWorkspaceEditDto(code.edit));
|
||||
data.forEach(code => reviveWorkspaceEditDto(code.edit));
|
||||
}
|
||||
return <modes.CodeAction[]>data;
|
||||
}
|
||||
@@ -266,7 +254,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector): void {
|
||||
this._registrations[handle] = modes.RenameProviderRegistry.register(toLanguageSelector(selector), <modes.RenameProvider>{
|
||||
provideRenameEdits: (model: ITextModel, position: EditorPosition, newName: string, token: CancellationToken): Thenable<modes.WorkspaceEdit> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideRenameEdits(handle, model.uri, position, newName)).then(MainThreadLanguageFeatures._reviveWorkspaceEditDto);
|
||||
return wireCancellationToken(token, this._proxy.$provideRenameEdits(handle, model.uri, position, newName)).then(reviveWorkspaceEditDto);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtHostContext, ExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
|
||||
@extHostCustomer
|
||||
export class MainThreadLogService extends Disposable {
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@ILogService logService: ILogService,
|
||||
) {
|
||||
super();
|
||||
this._register(logService.onDidChangeLogLevel(level => extHostContext.getProxy(ExtHostContext.ExtHostLogService).$setLevel(level)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { localize } from 'vs/nls';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export interface ISaveParticipantParticipant extends ISaveParticipant {
|
||||
// progressMessage: string;
|
||||
@@ -279,8 +280,9 @@ export class SaveParticipant implements ISaveParticipant {
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IProgressService2 private _progressService: IProgressService2,
|
||||
@IInstantiationService instantiationService: IInstantiationService
|
||||
@ILogService private _logService: ILogService
|
||||
) {
|
||||
this._saveParticipants = [
|
||||
instantiationService.createInstance(TrimWhitespaceParticipant),
|
||||
@@ -303,7 +305,7 @@ export class SaveParticipant implements ISaveParticipant {
|
||||
const promiseFactory = this._saveParticipants.map(p => () => {
|
||||
return Promise.resolve(p.participate(model, env));
|
||||
});
|
||||
return sequence(promiseFactory).then(() => { });
|
||||
return sequence(promiseFactory).then(() => { }, err => this._logService.error(err));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations';
|
||||
import { toGlobPattern, toLanguageSelector } from 'vs/workbench/api/node/extHostTypeConverters';
|
||||
import { ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { OverviewRulerLane } from 'vs/editor/common/model';
|
||||
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
(extension: IExtensionDescription): typeof vscode;
|
||||
@@ -89,18 +89,19 @@ export function createApiFactory(
|
||||
extHostWorkspace: ExtHostWorkspace,
|
||||
extHostConfiguration: ExtHostConfiguration,
|
||||
extensionService: ExtHostExtensionService,
|
||||
logService: ILogService
|
||||
extHostLogService: ExtHostLogService
|
||||
): IExtensionApiFactory {
|
||||
|
||||
// Addressable instances
|
||||
rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService);
|
||||
const extHostHeapService = rpcProtocol.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService());
|
||||
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, new ExtHostDecorations(rpcProtocol));
|
||||
const extHostDocumentsAndEditors = rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(rpcProtocol));
|
||||
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
|
||||
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors));
|
||||
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(logService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadEditors)));
|
||||
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadEditors)));
|
||||
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
|
||||
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, logService));
|
||||
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, extHostLogService));
|
||||
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands));
|
||||
rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace);
|
||||
const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace));
|
||||
@@ -111,7 +112,7 @@ export function createApiFactory(
|
||||
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService());
|
||||
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
|
||||
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol));
|
||||
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, logService));
|
||||
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
|
||||
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace));
|
||||
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
|
||||
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { UriComponents } from 'vs/base/common/uri';
|
||||
import URI, { UriComponents } from 'vs/base/common/uri';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
@@ -52,8 +52,9 @@ import { IStat, FileChangeType } from 'vs/platform/files/common/files';
|
||||
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/model';
|
||||
import { ILineMatch, IPatternInfo } from 'vs/platform/search/common/search';
|
||||
import { LogLevel } from 'vs/platform/log/common/log';
|
||||
|
||||
export interface IEnvironment {
|
||||
isExtensionDevelopmentDebug: boolean;
|
||||
@@ -71,6 +72,7 @@ export interface IWorkspaceData {
|
||||
id: string;
|
||||
name: string;
|
||||
folders: { uri: UriComponents, name: string, index: number }[];
|
||||
configuration?: UriComponents;
|
||||
}
|
||||
|
||||
export interface IInitData {
|
||||
@@ -83,6 +85,7 @@ export interface IInitData {
|
||||
windowId: number;
|
||||
args: ParsedArgs;
|
||||
execPath: string;
|
||||
logLevel: LogLevel;
|
||||
}
|
||||
|
||||
export interface IConfigurationInitData extends IConfigurationData {
|
||||
@@ -192,8 +195,6 @@ export interface IApplyEditsOptions extends IUndoStopOptions {
|
||||
setEndOfLine: EndOfLine;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface ITextDocumentShowOptions {
|
||||
position?: EditorPosition;
|
||||
preserveFocus?: boolean;
|
||||
@@ -201,16 +202,6 @@ export interface ITextDocumentShowOptions {
|
||||
selection?: IRange;
|
||||
}
|
||||
|
||||
export interface IWorkspaceResourceEdit {
|
||||
resource: UriComponents;
|
||||
modelVersionId?: number;
|
||||
edits: {
|
||||
range?: IRange;
|
||||
newText: string;
|
||||
newEol?: EndOfLineSequence;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface MainThreadEditorsShape extends IDisposable {
|
||||
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): TPromise<string>;
|
||||
$registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void;
|
||||
@@ -223,7 +214,7 @@ export interface MainThreadEditorsShape extends IDisposable {
|
||||
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<void>;
|
||||
$trySetSelections(id: string, selections: ISelection[]): TPromise<void>;
|
||||
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): TPromise<boolean>;
|
||||
$tryApplyWorkspaceEdit(workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean>;
|
||||
$tryApplyWorkspaceEdit(workspaceEditDto: WorkspaceEditDto): TPromise<boolean>;
|
||||
$tryInsertSnippet(id: string, template: string, selections: IRange[], opts: IUndoStopOptions): TPromise<boolean>;
|
||||
$getDiffInformation(id: string): TPromise<editorCommon.ILineChange[]>;
|
||||
}
|
||||
@@ -624,23 +615,44 @@ export interface WorkspaceSymbolsDto extends IdObject {
|
||||
symbols: SymbolInformationDto[];
|
||||
}
|
||||
|
||||
export interface ResourceEditDto {
|
||||
export interface ResourceFileEditDto {
|
||||
oldUri: UriComponents;
|
||||
newUri: UriComponents;
|
||||
}
|
||||
|
||||
export interface ResourceTextEditDto {
|
||||
resource: UriComponents;
|
||||
range: IRange;
|
||||
newText: string;
|
||||
modelVersionId?: number;
|
||||
edits: modes.TextEdit[];
|
||||
}
|
||||
|
||||
export interface WorkspaceEditDto {
|
||||
edits: ResourceEditDto[];
|
||||
edits: (ResourceFileEditDto | ResourceTextEditDto)[];
|
||||
|
||||
// todo@joh reject should go into rename
|
||||
rejectReason?: string;
|
||||
}
|
||||
|
||||
export function reviveWorkspaceEditDto(data: WorkspaceEditDto): modes.WorkspaceEdit {
|
||||
if (data && data.edits) {
|
||||
for (const edit of data.edits) {
|
||||
if (typeof (<ResourceTextEditDto>edit).resource === 'object') {
|
||||
(<ResourceTextEditDto>edit).resource = URI.revive((<ResourceTextEditDto>edit).resource);
|
||||
} else {
|
||||
(<ResourceFileEditDto>edit).newUri = URI.revive((<ResourceFileEditDto>edit).newUri);
|
||||
(<ResourceFileEditDto>edit).oldUri = URI.revive((<ResourceFileEditDto>edit).oldUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
return <modes.WorkspaceEdit>data;
|
||||
}
|
||||
|
||||
export interface CodeActionDto {
|
||||
title: string;
|
||||
edit?: WorkspaceEditDto;
|
||||
diagnostics?: IMarkerData[];
|
||||
command?: modes.Command;
|
||||
scope?: string;
|
||||
kind?: string;
|
||||
}
|
||||
|
||||
export interface ExtHostLanguageFeaturesShape {
|
||||
@@ -745,6 +757,10 @@ export interface ExtHostWindowShape {
|
||||
$onDidChangeWindowFocus(value: boolean): void;
|
||||
}
|
||||
|
||||
export interface ExtHostLogServiceShape {
|
||||
$setLevel(level: LogLevel);
|
||||
}
|
||||
|
||||
// --- proxy identifiers
|
||||
|
||||
export const MainContext = {
|
||||
@@ -795,7 +811,7 @@ export const ExtHostContext = {
|
||||
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures'),
|
||||
ExtHostQuickOpen: createExtId<ExtHostQuickOpenShape>('ExtHostQuickOpen'),
|
||||
ExtHostExtensionService: createExtId<ExtHostExtensionServiceShape>('ExtHostExtensionService'),
|
||||
// ExtHostLogService: createExtId<ExtHostLogServiceShape>('ExtHostLogService'),
|
||||
ExtHostLogService: createExtId<ExtHostLogServiceShape>('ExtHostLogService'),
|
||||
ExtHostTerminalService: createExtId<ExtHostTerminalServiceShape>('ExtHostTerminalService'),
|
||||
ExtHostSCM: createExtId<ExtHostSCMShape>('ExtHostSCM'),
|
||||
ExtHostTask: createExtId<ExtHostTaskShape>('ExtHostTask', ProxyType.CustomMarshaller),
|
||||
|
||||
@@ -353,11 +353,7 @@ export class ExtHostApiCommands {
|
||||
if (value.rejectReason) {
|
||||
return TPromise.wrapError<types.WorkspaceEdit>(new Error(value.rejectReason));
|
||||
}
|
||||
let workspaceEdit = new types.WorkspaceEdit();
|
||||
for (let edit of value.edits) {
|
||||
workspaceEdit.replace(edit.resource, typeConverters.toRange(edit.range), edit.newText);
|
||||
}
|
||||
return workspaceEdit;
|
||||
return typeConverters.WorkspaceEdit.to(value);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import Event from 'vs/base/common/event';
|
||||
import URI, { UriComponents } from 'vs/base/common/uri';
|
||||
import { sequence, always } from 'vs/base/common/async';
|
||||
import { illegalState } from 'vs/base/common/errors';
|
||||
import { ExtHostDocumentSaveParticipantShape, MainThreadEditorsShape, IWorkspaceResourceEdit } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { ExtHostDocumentSaveParticipantShape, MainThreadEditorsShape, ResourceTextEditDto } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { TextEdit } from 'vs/workbench/api/node/extHostTypes';
|
||||
import { fromRange, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/node/extHostTypeConverters';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
|
||||
@@ -142,7 +142,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
|
||||
|
||||
}).then(values => {
|
||||
|
||||
let workspaceResourceEdit: IWorkspaceResourceEdit = {
|
||||
const resourceEdit: ResourceTextEditDto = {
|
||||
resource: document.uri,
|
||||
edits: []
|
||||
};
|
||||
@@ -150,10 +150,10 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
|
||||
for (const value of values) {
|
||||
if (Array.isArray(value) && (<vscode.TextEdit[]>value).every(e => e instanceof TextEdit)) {
|
||||
for (const { newText, newEol, range } of value) {
|
||||
workspaceResourceEdit.edits.push({
|
||||
resourceEdit.edits.push({
|
||||
range: range && fromRange(range),
|
||||
newText,
|
||||
newEol: EndOfLine.from(newEol)
|
||||
text: newText,
|
||||
eol: EndOfLine.from(newEol)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -161,12 +161,12 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
|
||||
|
||||
// apply edits if any and if document
|
||||
// didn't change somehow in the meantime
|
||||
if (workspaceResourceEdit.edits.length === 0) {
|
||||
if (resourceEdit.edits.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (version === document.version) {
|
||||
return this._mainThreadEditors.$tryApplyWorkspaceEdit([workspaceResourceEdit]);
|
||||
return this._mainThreadEditors.$tryApplyWorkspaceEdit({ edits: [resourceEdit] });
|
||||
}
|
||||
|
||||
// TODO@joh bubble this to listener?
|
||||
|
||||
@@ -22,6 +22,7 @@ import { Barrier } from 'vs/base/common/async';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
|
||||
import URI from 'vs/base/common/uri';
|
||||
|
||||
class ExtensionMemento implements IExtensionMemento {
|
||||
|
||||
@@ -108,6 +109,7 @@ class ExtensionStoragePath {
|
||||
join(storagePath, 'meta.json'),
|
||||
JSON.stringify({
|
||||
id: this._workspace.id,
|
||||
configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(),
|
||||
name: this._workspace.name
|
||||
}, undefined, 2)
|
||||
);
|
||||
@@ -128,7 +130,6 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
|
||||
private readonly _storage: ExtHostStorage;
|
||||
private readonly _storagePath: ExtensionStoragePath;
|
||||
private readonly _proxy: MainThreadExtensionServiceShape;
|
||||
private readonly _logService: ILogService;
|
||||
private readonly _extHostLogService: ExtHostLogService;
|
||||
private _activator: ExtensionsActivator;
|
||||
private _extensionPathIndex: TPromise<TernarySearchTree<IExtensionDescription>>;
|
||||
@@ -139,21 +140,20 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
|
||||
extHostContext: IExtHostContext,
|
||||
extHostWorkspace: ExtHostWorkspace,
|
||||
extHostConfiguration: ExtHostConfiguration,
|
||||
logService: ILogService,
|
||||
extHostLogService: ExtHostLogService,
|
||||
environmentService: IEnvironmentService
|
||||
) {
|
||||
this._barrier = new Barrier();
|
||||
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
|
||||
this._logService = logService;
|
||||
this._extHostLogService = extHostLogService;
|
||||
this._mainThreadTelemetry = extHostContext.getProxy(MainContext.MainThreadTelemetry);
|
||||
this._storage = new ExtHostStorage(extHostContext);
|
||||
this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment);
|
||||
this._proxy = extHostContext.getProxy(MainContext.MainThreadExtensionService);
|
||||
this._activator = null;
|
||||
this._extHostLogService = new ExtHostLogService(environmentService);
|
||||
|
||||
// initialize API first (i.e. do not release barrier until the API is initialized)
|
||||
const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, logService);
|
||||
const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, this._extHostLogService);
|
||||
|
||||
initializeExtensionApi(this, apiFactory).then(() => {
|
||||
|
||||
@@ -314,14 +314,14 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
|
||||
return TPromise.as(new EmptyExtension(ExtensionActivationTimes.NONE));
|
||||
}
|
||||
|
||||
this._logService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`);
|
||||
this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`);
|
||||
|
||||
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
|
||||
return TPromise.join<any>([
|
||||
loadCommonJSModule(this._logService, extensionDescription.main, activationTimesBuilder),
|
||||
loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder),
|
||||
this._loadExtensionContext(extensionDescription)
|
||||
]).then(values => {
|
||||
return ExtHostExtensionService._callActivate(this._logService, extensionDescription.id, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
|
||||
return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.id, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
|
||||
}, (errors: any[]) => {
|
||||
// Avoid failing with an array of errors, fail with a single error
|
||||
if (errors[0]) {
|
||||
@@ -339,7 +339,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
|
||||
let globalState = new ExtensionMemento(extensionDescription.id, true, this._storage);
|
||||
let workspaceState = new ExtensionMemento(extensionDescription.id, false, this._storage);
|
||||
|
||||
this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`);
|
||||
this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`);
|
||||
return TPromise.join([
|
||||
globalState.whenReady,
|
||||
workspaceState.whenReady,
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
|
||||
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
|
||||
import { ExtHostDiagnostics, DiagnosticCollection } from 'vs/workbench/api/node/extHostDiagnostics';
|
||||
import { asWinJsPromise } from 'vs/base/common/async';
|
||||
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, SymbolInformationDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto } from './extHost.protocol';
|
||||
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, SymbolInformationDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto } from './extHost.protocol';
|
||||
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
@@ -255,7 +255,7 @@ class ReferenceAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
export interface CustomCodeAction extends modes.CodeAction {
|
||||
export interface CustomCodeAction extends CodeActionDto {
|
||||
_isSynthetic?: boolean;
|
||||
}
|
||||
|
||||
@@ -273,7 +273,8 @@ class CodeActionAdapter {
|
||||
this._provider = provider;
|
||||
}
|
||||
|
||||
provideCodeActions(resource: URI, range: IRange, context: modes.CodeActionContext): TPromise<modes.CodeAction[]> {
|
||||
|
||||
provideCodeActions(resource: URI, range: IRange, context: modes.CodeActionContext): TPromise<CodeActionDto[]> {
|
||||
|
||||
const doc = this._documents.getDocumentData(resource).document;
|
||||
const ran = <vscode.Range>TypeConverters.toRange(range);
|
||||
@@ -948,7 +949,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
||||
$provideCodeActions(handle: number, resource: UriComponents, range: IRange, context: modes.CodeActionContext): TPromise<modes.CodeAction[]> {
|
||||
|
||||
$provideCodeActions(handle: number, resource: UriComponents, range: IRange, context: modes.CodeActionContext): TPromise<CodeActionDto[]> {
|
||||
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), range, context));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,47 +8,61 @@ import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { mkdirp, dirExists } from 'vs/base/node/pfs';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import Event from 'vs/base/common/event';
|
||||
import { LogLevel } from 'vs/workbench/api/node/extHostTypes';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ILogService, DelegatedLogService } from 'vs/platform/log/common/log';
|
||||
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { ExtHostLogServiceShape } from 'vs/workbench/api/node/extHost.protocol';
|
||||
|
||||
|
||||
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
|
||||
|
||||
export class ExtHostLogService {
|
||||
private _loggers: Map<string, ExtHostLogger> = new Map();
|
||||
|
||||
constructor(private _environmentService: IEnvironmentService) {
|
||||
constructor(
|
||||
windowId: number,
|
||||
logLevel: LogLevel,
|
||||
private _environmentService: IEnvironmentService
|
||||
) {
|
||||
super(createSpdLogService(`exthost${windowId}`, logLevel, _environmentService.logsPath));
|
||||
}
|
||||
|
||||
$setLevel(level: LogLevel): void {
|
||||
this.setLevel(level);
|
||||
}
|
||||
|
||||
getExtLogger(extensionID: string): ExtHostLogger {
|
||||
if (!this._loggers.has(extensionID)) {
|
||||
const logService = createSpdLogService(extensionID, this._environmentService, extensionID);
|
||||
const logsDirPath = path.join(this._environmentService.logsPath, extensionID);
|
||||
this._loggers.set(extensionID, new ExtHostLogger(logService, logsDirPath));
|
||||
let logger = this._loggers.get(extensionID);
|
||||
if (!logger) {
|
||||
logger = this.createLogger(extensionID);
|
||||
this._loggers.set(extensionID, logger);
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
|
||||
return this._loggers.get(extensionID);
|
||||
private createLogger(extensionID: string): ExtHostLogger {
|
||||
const logService = createSpdLogService(extensionID, this.getLevel(), this._environmentService.logsPath, extensionID);
|
||||
const logsDirPath = path.join(this._environmentService.logsPath, extensionID);
|
||||
this._register(this.onDidChangeLogLevel(level => logService.setLevel(level)));
|
||||
return new ExtHostLogger(logService, logsDirPath);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostLogger implements vscode.Logger {
|
||||
private _currentLevel: LogLevel;
|
||||
private _onDidChangeLogLevel: Emitter<LogLevel>;
|
||||
|
||||
constructor(
|
||||
private readonly _logService: ILogService,
|
||||
private readonly _logDirectory: string
|
||||
) {
|
||||
this._currentLevel = this._logService.getLevel();
|
||||
this._onDidChangeLogLevel = new Emitter<LogLevel>();
|
||||
this.onDidChangeLogLevel = this._onDidChangeLogLevel.event;
|
||||
}
|
||||
|
||||
// TODO
|
||||
readonly onDidChangeLogLevel: Event<LogLevel>;
|
||||
get onDidChangeLogLevel(): Event<LogLevel> {
|
||||
return this._logService.onDidChangeLogLevel;
|
||||
}
|
||||
|
||||
get currentLevel(): LogLevel { return this._currentLevel; }
|
||||
get currentLevel(): LogLevel { return this._logService.getLevel(); }
|
||||
|
||||
@memoize
|
||||
get logDirectory(): TPromise<string> {
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as TypeConverters from './extHostTypeConverters';
|
||||
import { TextEditorDecorationType, ExtHostTextEditor } from './extHostTextEditor';
|
||||
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
|
||||
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
|
||||
import { MainContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IResolvedTextEditorConfiguration, ISelectionChangeEvent, IMainContext, IWorkspaceResourceEdit } from './extHost.protocol';
|
||||
import { MainContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IResolvedTextEditorConfiguration, ISelectionChangeEvent, IMainContext, WorkspaceEditDto } from './extHost.protocol';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export class ExtHostEditors implements ExtHostEditorsShape {
|
||||
@@ -92,36 +92,23 @@ export class ExtHostEditors implements ExtHostEditorsShape {
|
||||
|
||||
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
|
||||
|
||||
let workspaceResourceEdits: IWorkspaceResourceEdit[] = [];
|
||||
const dto: WorkspaceEditDto = { edits: [] };
|
||||
|
||||
let entries = edit.entries();
|
||||
for (let entry of entries) {
|
||||
let [uri, edits] = entry;
|
||||
|
||||
let doc = this._extHostDocumentsAndEditors.getDocument(uri.toString());
|
||||
let docVersion: number = undefined;
|
||||
if (doc) {
|
||||
docVersion = doc.version;
|
||||
}
|
||||
|
||||
let workspaceResourceEdit: IWorkspaceResourceEdit = {
|
||||
resource: uri,
|
||||
modelVersionId: docVersion,
|
||||
edits: []
|
||||
};
|
||||
|
||||
for (let edit of edits) {
|
||||
workspaceResourceEdit.edits.push({
|
||||
newText: edit.newText,
|
||||
newEol: TypeConverters.EndOfLine.from(edit.newEol),
|
||||
range: edit.range && TypeConverters.fromRange(edit.range)
|
||||
for (let entry of edit.allEntries()) {
|
||||
let [uri, uriOrEdits] = entry;
|
||||
if (Array.isArray(uriOrEdits)) {
|
||||
let doc = this._extHostDocumentsAndEditors.getDocument(uri.toString());
|
||||
dto.edits.push({
|
||||
resource: uri,
|
||||
modelVersionId: doc && doc.version,
|
||||
edits: uriOrEdits.map(TypeConverters.TextEdit.from)
|
||||
});
|
||||
} else {
|
||||
dto.edits.push({ oldUri: uri, newUri: uriOrEdits });
|
||||
}
|
||||
|
||||
workspaceResourceEdits.push(workspaceResourceEdit);
|
||||
}
|
||||
|
||||
return this._proxy.$tryApplyWorkspaceEdit(workspaceResourceEdits);
|
||||
return this._proxy.$tryApplyWorkspaceEdit(dto);
|
||||
}
|
||||
|
||||
// --- called from main thread
|
||||
|
||||
@@ -20,6 +20,7 @@ import { ISelection } from 'vs/editor/common/core/selection';
|
||||
import * as htmlContent from 'vs/base/common/htmlContent';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { LanguageSelector, LanguageFilter } from 'vs/editor/common/modes/languageSelector';
|
||||
import { WorkspaceEditDto, ResourceTextEditDto, ResourceFileEditDto } from 'vs/workbench/api/node/extHost.protocol';
|
||||
|
||||
export interface PositionLike {
|
||||
line: number;
|
||||
@@ -228,24 +229,36 @@ export const TextEdit = {
|
||||
|
||||
export namespace WorkspaceEdit {
|
||||
export function from(value: vscode.WorkspaceEdit): modes.WorkspaceEdit {
|
||||
const result: modes.WorkspaceEdit = { edits: [] };
|
||||
for (let entry of value.entries()) {
|
||||
let [uri, textEdits] = entry;
|
||||
for (let textEdit of textEdits) {
|
||||
result.edits.push({
|
||||
resource: uri,
|
||||
newText: textEdit.newText,
|
||||
range: fromRange(textEdit.range)
|
||||
});
|
||||
const result: modes.WorkspaceEdit = {
|
||||
edits: []
|
||||
};
|
||||
for (const entry of value.allEntries()) {
|
||||
const [uri, uriOrEdits] = entry;
|
||||
if (Array.isArray(uriOrEdits)) {
|
||||
// text edits
|
||||
result.edits.push({ resource: uri, edits: uriOrEdits.map(TextEdit.from) });
|
||||
} else {
|
||||
// resource edits
|
||||
result.edits.push({ oldUri: uri, newUri: uriOrEdits });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function to(value: modes.WorkspaceEdit) {
|
||||
export function to(value: WorkspaceEditDto) {
|
||||
const result = new types.WorkspaceEdit();
|
||||
for (const edit of value.edits) {
|
||||
result.replace(edit.resource, toRange(edit.range), edit.newText);
|
||||
if (Array.isArray((<ResourceTextEditDto>edit).edits)) {
|
||||
result.set(
|
||||
URI.revive((<ResourceTextEditDto>edit).resource),
|
||||
<types.TextEdit[]>(<ResourceTextEditDto>edit).edits.map(TextEdit.to)
|
||||
);
|
||||
} else {
|
||||
result.renameResource(
|
||||
URI.revive((<ResourceFileEditDto>edit).oldUri),
|
||||
URI.revive((<ResourceFileEditDto>edit).newUri)
|
||||
);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -492,10 +492,28 @@ export class TextEdit {
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkspaceEdit {
|
||||
export class WorkspaceEdit implements vscode.WorkspaceEdit {
|
||||
|
||||
private _values: [URI, TextEdit[]][] = [];
|
||||
private _index = new Map<string, number>();
|
||||
private _seqPool: number = 0;
|
||||
|
||||
private _resourceEdits: { seq: number, from: URI, to: URI }[] = [];
|
||||
private _textEdits = new Map<string, { seq: number, uri: URI, edits: TextEdit[] }>();
|
||||
|
||||
createResource(uri: vscode.Uri): void {
|
||||
this.renameResource(undefined, uri);
|
||||
}
|
||||
|
||||
deleteResource(uri: vscode.Uri): void {
|
||||
this.renameResource(uri, undefined);
|
||||
}
|
||||
|
||||
renameResource(from: vscode.Uri, to: vscode.Uri): void {
|
||||
this._resourceEdits.push({ seq: this._seqPool++, from, to });
|
||||
}
|
||||
|
||||
resourceEdits(): [vscode.Uri, vscode.Uri][] {
|
||||
return this._resourceEdits.map(({ from, to }) => (<[vscode.Uri, vscode.Uri]>[from, to]));
|
||||
}
|
||||
|
||||
replace(uri: URI, range: Range, newText: string): void {
|
||||
let edit = new TextEdit(range, newText);
|
||||
@@ -503,8 +521,9 @@ export class WorkspaceEdit {
|
||||
if (array) {
|
||||
array.push(edit);
|
||||
} else {
|
||||
this.set(uri, [edit]);
|
||||
array = [edit];
|
||||
}
|
||||
this.set(uri, array);
|
||||
}
|
||||
|
||||
insert(resource: URI, position: Position, newText: string): void {
|
||||
@@ -516,34 +535,58 @@ export class WorkspaceEdit {
|
||||
}
|
||||
|
||||
has(uri: URI): boolean {
|
||||
return this._index.has(uri.toString());
|
||||
return this._textEdits.has(uri.toString());
|
||||
}
|
||||
|
||||
set(uri: URI, edits: TextEdit[]): void {
|
||||
const idx = this._index.get(uri.toString());
|
||||
if (typeof idx === 'undefined') {
|
||||
let newLen = this._values.push([uri, edits]);
|
||||
this._index.set(uri.toString(), newLen - 1);
|
||||
let data = this._textEdits.get(uri.toString());
|
||||
if (!data) {
|
||||
data = { seq: this._seqPool++, uri, edits: [] };
|
||||
this._textEdits.set(uri.toString(), data);
|
||||
}
|
||||
if (!edits) {
|
||||
data.edits = undefined;
|
||||
} else {
|
||||
this._values[idx][1] = edits;
|
||||
data.edits = edits.slice(0);
|
||||
}
|
||||
}
|
||||
|
||||
get(uri: URI): TextEdit[] {
|
||||
let idx = this._index.get(uri.toString());
|
||||
return typeof idx !== 'undefined' && this._values[idx][1];
|
||||
if (!this._textEdits.has(uri.toString())) {
|
||||
return undefined;
|
||||
}
|
||||
const { edits } = this._textEdits.get(uri.toString());
|
||||
return edits ? edits.slice() : undefined;
|
||||
}
|
||||
|
||||
entries(): [URI, TextEdit[]][] {
|
||||
return this._values;
|
||||
const res: [URI, TextEdit[]][] = [];
|
||||
this._textEdits.forEach(value => res.push([value.uri, value.edits]));
|
||||
return res.slice();
|
||||
}
|
||||
|
||||
allEntries(): ([URI, TextEdit[]] | [URI, URI])[] {
|
||||
// use the 'seq' the we have assigned when inserting
|
||||
// the operation and use that order in the resulting
|
||||
// array
|
||||
const res: ([URI, TextEdit[]] | [URI, URI])[] = [];
|
||||
this._textEdits.forEach(value => {
|
||||
const { seq, uri, edits } = value;
|
||||
res[seq] = [uri, edits];
|
||||
});
|
||||
this._resourceEdits.forEach(value => {
|
||||
const { seq, from, to } = value;
|
||||
res[seq] = [from, to];
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return this._values.length;
|
||||
return this._textEdits.size + this._resourceEdits.length;
|
||||
}
|
||||
|
||||
toJSON(): any {
|
||||
return this._values;
|
||||
return this.entries();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { BACKUP_FILE_RESOLVE_OPTIONS, IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
@@ -20,6 +20,8 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Position } from 'vs/platform/editor/common/editor';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { DefaultEndOfLine } from 'vs/editor/common/model';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
/**
|
||||
* Shared function across some editor components to handle drag & drop of external resources. E.g. of folders and workspace files
|
||||
@@ -37,6 +39,7 @@ export class EditorAreaDropHandler {
|
||||
@IEditorGroupService private groupService: IEditorGroupService,
|
||||
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -107,13 +110,22 @@ export class EditorAreaDropHandler {
|
||||
}
|
||||
|
||||
// Resolve the contents of the dropped dirty resource from source
|
||||
return this.textFileService.resolveTextContent(droppedDirtyEditor.backupResource, BACKUP_FILE_RESOLVE_OPTIONS).then(content => {
|
||||
return this.backupFileService.resolveBackupContent(droppedDirtyEditor.backupResource).then(content => {
|
||||
|
||||
// Set the contents of to the resource to the target
|
||||
return this.backupFileService.backupResource(droppedDirtyEditor.resource, this.backupFileService.parseBackupContent(content.value));
|
||||
return this.backupFileService.backupResource(droppedDirtyEditor.resource, content.create(this.getDefaultEOL()).createSnapshot(true));
|
||||
}).then(() => false, () => false /* ignore any error */);
|
||||
}
|
||||
|
||||
private getDefaultEOL(): DefaultEndOfLine {
|
||||
const eol = this.configurationService.getValue('files.eol');
|
||||
if (eol === '\r\n') {
|
||||
return DefaultEndOfLine.CRLF;
|
||||
}
|
||||
|
||||
return DefaultEndOfLine.LF;
|
||||
}
|
||||
|
||||
private handleWorkspaceFileDrop(resources: (IDraggedResource | IDraggedEditor)[]): TPromise<boolean> {
|
||||
const externalResources = resources.filter(d => d.isExternal).map(d => d.resource);
|
||||
|
||||
|
||||
@@ -67,13 +67,13 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
|
||||
/**
|
||||
* Creates the text editor model with the provided value, modeId (can be comma separated for multiple values) and optional resource URL.
|
||||
*/
|
||||
protected createTextEditorModel(value: string | ITextBufferFactory, resource?: URI, modeId?: string): TPromise<EditorModel> {
|
||||
protected createTextEditorModel(value: ITextBufferFactory, resource?: URI, modeId?: string): TPromise<EditorModel> {
|
||||
const firstLineText = this.getFirstLineText(value);
|
||||
const mode = this.getOrCreateMode(this.modeService, modeId, firstLineText);
|
||||
return TPromise.as(this.doCreateTextEditorModel(value, mode, resource));
|
||||
}
|
||||
|
||||
private doCreateTextEditorModel(value: string | ITextBufferFactory, mode: TPromise<IMode>, resource: URI): EditorModel {
|
||||
private doCreateTextEditorModel(value: ITextBufferFactory, mode: TPromise<IMode>, resource: URI): EditorModel {
|
||||
let model = resource && this.modelService.getModel(resource);
|
||||
if (!model) {
|
||||
model = this.modelService.createModel(value, mode, resource);
|
||||
@@ -91,24 +91,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
|
||||
return this;
|
||||
}
|
||||
|
||||
protected getFirstLineText(value: string | ITextBufferFactory | ITextSnapshot): string {
|
||||
|
||||
// string
|
||||
if (typeof value === 'string') {
|
||||
const firstLineText = value.substr(0, 100);
|
||||
|
||||
let crIndex = firstLineText.indexOf('\r');
|
||||
if (crIndex < 0) {
|
||||
crIndex = firstLineText.length;
|
||||
}
|
||||
|
||||
let lfIndex = firstLineText.indexOf('\n');
|
||||
if (lfIndex < 0) {
|
||||
lfIndex = firstLineText.length;
|
||||
}
|
||||
|
||||
return firstLineText.substr(0, Math.min(crIndex, lfIndex));
|
||||
}
|
||||
protected getFirstLineText(value: ITextBufferFactory | ITextSnapshot): string {
|
||||
|
||||
// text buffer factory
|
||||
const textBufferFactory = value as ITextBufferFactory;
|
||||
@@ -118,7 +101,19 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
|
||||
|
||||
// text snapshot
|
||||
const textSnapshot = value as ITextSnapshot;
|
||||
return this.getFirstLineText(textSnapshot.read() || '');
|
||||
const firstLineText = textSnapshot.read().substr(0, 100);
|
||||
|
||||
let crIndex = firstLineText.indexOf('\r');
|
||||
if (crIndex < 0) {
|
||||
crIndex = firstLineText.length;
|
||||
}
|
||||
|
||||
let lfIndex = firstLineText.indexOf('\n');
|
||||
if (lfIndex < 0) {
|
||||
lfIndex = firstLineText.length;
|
||||
}
|
||||
|
||||
return firstLineText.substr(0, Math.min(crIndex, lfIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +128,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
|
||||
/**
|
||||
* Updates the text editor model with the provided value. If the value is the same as the model has, this is a no-op.
|
||||
*/
|
||||
protected updateTextEditorModel(newValue: string | ITextBufferFactory): void {
|
||||
protected updateTextEditorModel(newValue: ITextBufferFactory): void {
|
||||
if (!this.textEditorModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@ import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IMode } from 'vs/editor/common/modes';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { IBackupFileService, BACKUP_FILE_RESOLVE_OPTIONS } from 'vs/workbench/services/backup/common/backup';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { ITextBufferFactory } from 'vs/editor/common/model';
|
||||
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
|
||||
|
||||
export class UntitledEditorModel extends BaseTextEditorModel implements IEncodingSupport {
|
||||
|
||||
@@ -46,7 +47,6 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
|
||||
@IModeService modeService: IModeService,
|
||||
@IModelService modelService: IModelService,
|
||||
@IBackupFileService private backupFileService: IBackupFileService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@ITextResourceConfigurationService private configurationService: ITextResourceConfigurationService
|
||||
) {
|
||||
super(modelService, modeService);
|
||||
@@ -163,18 +163,24 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
|
||||
// Check for backups first
|
||||
return this.backupFileService.loadBackupResource(this.resource).then(backupResource => {
|
||||
if (backupResource) {
|
||||
return this.textFileService.resolveTextContent(backupResource, BACKUP_FILE_RESOLVE_OPTIONS).then(rawTextContent => {
|
||||
return this.backupFileService.parseBackupContent(rawTextContent.value);
|
||||
});
|
||||
return this.backupFileService.resolveBackupContent(backupResource);
|
||||
}
|
||||
|
||||
return null;
|
||||
}).then(backupContent => {
|
||||
}).then(backupTextBufferFactory => {
|
||||
const hasBackup = !!backupTextBufferFactory;
|
||||
|
||||
// untitled associated to file path are dirty right away as well as untitled with content
|
||||
this.setDirty(this.hasAssociatedFilePath || !!backupContent);
|
||||
this.setDirty(this.hasAssociatedFilePath || hasBackup);
|
||||
|
||||
return this.doLoad(backupContent || this.initialValue || '').then(model => {
|
||||
let untitledContents: ITextBufferFactory;
|
||||
if (backupTextBufferFactory) {
|
||||
untitledContents = backupTextBufferFactory;
|
||||
} else {
|
||||
untitledContents = createTextBufferFactory(this.initialValue || '');
|
||||
}
|
||||
|
||||
return this.doLoad(untitledContents).then(model => {
|
||||
// Encoding
|
||||
this.configuredEncoding = this.configurationService.getValue<string>(this.resource, 'files.encoding');
|
||||
|
||||
@@ -189,7 +195,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
|
||||
});
|
||||
}
|
||||
|
||||
private doLoad(content: string): TPromise<UntitledEditorModel> {
|
||||
private doLoad(content: ITextBufferFactory): TPromise<UntitledEditorModel> {
|
||||
|
||||
// Create text editor model if not yet done
|
||||
if (!this.textEditorModel) {
|
||||
|
||||
@@ -43,9 +43,10 @@ import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
|
||||
import fs = require('fs');
|
||||
import { ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log';
|
||||
import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log';
|
||||
import { IssueChannelClient } from 'vs/platform/issue/common/issueIpc';
|
||||
import { IIssueService } from 'vs/platform/issue/common/issue';
|
||||
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
|
||||
gracefulFs.gracefulify(fs); // enable gracefulFs
|
||||
|
||||
export function startup(configuration: IWindowConfiguration): TPromise<void> {
|
||||
@@ -75,10 +76,7 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise<void> {
|
||||
const mainServices = createMainProcessServices(mainProcessClient, configuration);
|
||||
|
||||
const environmentService = new EnvironmentService(configuration, configuration.execPath);
|
||||
const spdlogService = createSpdLogService(`renderer${configuration.windowId}`, environmentService);
|
||||
const consoleLogService = new ConsoleLogService(environmentService);
|
||||
const logService = new MultiplexLogService([consoleLogService, spdlogService]);
|
||||
|
||||
const logService = createLogService(mainProcessClient, configuration, environmentService);
|
||||
logService.trace('openWorkbench configuration', JSON.stringify(configuration));
|
||||
|
||||
// Since the configuration service is one of the core services that is used in so many places, we initialize it
|
||||
@@ -200,6 +198,14 @@ function createStorageService(workspaceService: IWorkspaceContextService, enviro
|
||||
return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId);
|
||||
}
|
||||
|
||||
function createLogService(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration, environmentService: IEnvironmentService): ILogService {
|
||||
const spdlogService = createSpdLogService(`renderer${configuration.windowId}`, configuration.logLevel, environmentService.logsPath);
|
||||
const consoleLogService = new ConsoleLogService(configuration.logLevel);
|
||||
const logService = new MultiplexLogService([consoleLogService, spdlogService]);
|
||||
const logLevelClient = new LogLevelSetterChannelClient(mainProcessClient.getChannel('loglevel'));
|
||||
return new FollowerLogService(logLevelClient, logService);
|
||||
}
|
||||
|
||||
function createMainProcessServices(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration): ServiceCollection {
|
||||
const serviceCollection = new ServiceCollection();
|
||||
|
||||
|
||||
@@ -21,12 +21,11 @@ import * as watchdog from 'native-watchdog';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { ExtensionActivatedByEvent } from 'vs/workbench/api/node/extHostExtensionActivator';
|
||||
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
|
||||
|
||||
// const nativeExit = process.exit.bind(process);
|
||||
function patchProcess(allowExit: boolean) {
|
||||
@@ -78,7 +77,7 @@ export class ExtensionHostMain {
|
||||
private _environment: IEnvironment;
|
||||
private _extensionService: ExtHostExtensionService;
|
||||
private _extHostConfiguration: ExtHostConfiguration;
|
||||
private _logService: ILogService;
|
||||
private _extHostLogService: ExtHostLogService;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(protocol: IMessagePassingProtocol, initData: IInitData) {
|
||||
@@ -92,14 +91,14 @@ export class ExtensionHostMain {
|
||||
const rpcProtocol = new RPCProtocol(protocol);
|
||||
const extHostWorkspace = new ExtHostWorkspace(rpcProtocol, initData.workspace);
|
||||
const environmentService = new EnvironmentService(initData.args, initData.execPath);
|
||||
this._logService = createSpdLogService(`exthost${initData.windowId}`, environmentService);
|
||||
this.disposables.push(this._logService);
|
||||
this._extHostLogService = new ExtHostLogService(initData.windowId, initData.logLevel, environmentService);
|
||||
this.disposables.push(this._extHostLogService);
|
||||
|
||||
this._logService.info('extension host started');
|
||||
this._logService.trace('initData', initData);
|
||||
this._extHostLogService.info('extension host started');
|
||||
this._extHostLogService.trace('initData', initData);
|
||||
|
||||
this._extHostConfiguration = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration);
|
||||
this._extensionService = new ExtHostExtensionService(initData, rpcProtocol, extHostWorkspace, this._extHostConfiguration, this._logService, environmentService);
|
||||
this._extensionService = new ExtHostExtensionService(initData, rpcProtocol, extHostWorkspace, this._extHostConfiguration, this._extHostLogService, environmentService);
|
||||
|
||||
// error forwarding and stack trace scanning
|
||||
const extensionErrors = new WeakMap<Error, IExtensionDescription>();
|
||||
@@ -143,7 +142,7 @@ export class ExtensionHostMain {
|
||||
.then(() => this.handleEagerExtensions())
|
||||
.then(() => this.handleExtensionTests())
|
||||
.then(() => {
|
||||
this._logService.info(`eager extensions activated`);
|
||||
this._extHostLogService.info(`eager extensions activated`);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -460,7 +460,7 @@ export interface ILaunch {
|
||||
* Returns the names of all configurations and compounds.
|
||||
* Ignores configurations which are invalid.
|
||||
*/
|
||||
getConfigurationNames(): string[];
|
||||
getConfigurationNames(includeCompounds?: boolean): string[];
|
||||
|
||||
/**
|
||||
* Returns the resolved configuration.
|
||||
|
||||
@@ -187,7 +187,12 @@ const schema: IJSONSchema = {
|
||||
type: 'array',
|
||||
default: [],
|
||||
items: {
|
||||
type: 'string'
|
||||
oneOf: [{
|
||||
enum: [],
|
||||
description: nls.localize('useUniqueNames', "Please use unique configuration names.")
|
||||
}, {
|
||||
type: 'object'
|
||||
}]
|
||||
},
|
||||
description: nls.localize('app.launch.json.compounds.configurations', "Names of configurations that will be started as part of this compound.")
|
||||
}
|
||||
@@ -311,6 +316,8 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
items.defaultSnippets.push(...configurationSnippets);
|
||||
}
|
||||
});
|
||||
|
||||
this.setCompoundSchemaValues();
|
||||
});
|
||||
|
||||
breakpointsExtPoint.setHandler(extensions => {
|
||||
@@ -328,6 +335,7 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('launch')) {
|
||||
this.selectConfiguration();
|
||||
this.setCompoundSchemaValues();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -345,6 +353,13 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
}
|
||||
}
|
||||
|
||||
private setCompoundSchemaValues(): void {
|
||||
const compoundConfigurationsSchema = (<IJSONSchema>schema.properties['compounds'].items).properties['configurations'];
|
||||
(<IJSONSchema>compoundConfigurationsSchema.items).oneOf[0].enum = this.launches.map(l =>
|
||||
l.getConfigurationNames(false)).reduce((first, second) => first.concat(second), []);
|
||||
jsonRegistry.registerSchema(launchSchemaId, schema);
|
||||
}
|
||||
|
||||
public getLaunches(): ILaunch[] {
|
||||
return this.launches;
|
||||
}
|
||||
@@ -495,13 +510,13 @@ class Launch implements ILaunch {
|
||||
return config.compounds.filter(compound => compound.name === name).pop();
|
||||
}
|
||||
|
||||
public getConfigurationNames(): string[] {
|
||||
public getConfigurationNames(includeCompounds = true): string[] {
|
||||
const config = this.getConfig();
|
||||
if (!config || !config.configurations || !Array.isArray(config.configurations)) {
|
||||
return [];
|
||||
} else {
|
||||
const names = config.configurations.filter(cfg => cfg && typeof cfg.name === 'string').map(cfg => cfg.name);
|
||||
if (names.length > 0 && config.compounds) {
|
||||
if (includeCompounds && names.length > 0 && config.compounds) {
|
||||
if (config.compounds) {
|
||||
names.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length)
|
||||
.map(compound => compound.name));
|
||||
|
||||
@@ -699,12 +699,9 @@ export class DebugService implements debug.IDebugService {
|
||||
}
|
||||
|
||||
let rootForName = root;
|
||||
if (launch === this.configurationManager.getWorkspaceLaunch()) {
|
||||
// For workspace launches allow comound referencing configurations across folder
|
||||
const launchContainingName = this.configurationManager.getLaunches().filter(l => !!l.getConfiguration(name)).pop();
|
||||
if (launchContainingName) {
|
||||
rootForName = launchContainingName.workspace;
|
||||
}
|
||||
const launchesContainingName = this.configurationManager.getLaunches().filter(l => !!l.getConfiguration(name));
|
||||
if (launchesContainingName && launchesContainingName.length === 1) {
|
||||
rootForName = launchesContainingName[0].workspace;
|
||||
}
|
||||
|
||||
return this.startDebugging(rootForName, name, noDebug, topCompoundName || compound.name);
|
||||
|
||||
@@ -30,7 +30,7 @@ import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
|
||||
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { Renderer, DataSource, Controller } from 'vs/workbench/parts/extensions/browser/dependenciesViewer';
|
||||
import { RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
|
||||
import { RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
|
||||
import { EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, BuiltinStatusLabelAction, ReloadAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
@@ -337,7 +337,7 @@ export class ExtensionEditor extends BaseEditor {
|
||||
this.repository.style.display = 'none';
|
||||
}
|
||||
|
||||
const install = this.instantiationService.createInstance(InstallWidget, this.installCount, { extension });
|
||||
const install = this.instantiationService.createInstance(InstallCountWidget, this.installCount, { extension });
|
||||
this.transientDisposables.push(install);
|
||||
|
||||
const ratings = this.instantiationService.createInstance(RatingsWidget, this.rating, { extension });
|
||||
|
||||
@@ -18,7 +18,7 @@ import { domEvent } from 'vs/base/browser/event';
|
||||
import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { InstallAction, UpdateAction, BuiltinStatusLabelAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, extensionButtonProminentForeground } from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { Label, RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
|
||||
import { Label, RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
@@ -93,7 +93,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
||||
actionbar.onDidRun(({ error }) => error && this.messageService.show(Severity.Error, error));
|
||||
|
||||
const versionWidget = this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version);
|
||||
const installCountWidget = this.instantiationService.createInstance(InstallWidget, installCount, { small: true });
|
||||
const installCountWidget = this.instantiationService.createInstance(InstallCountWidget, installCount, { small: true });
|
||||
const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, ratings, { small: true });
|
||||
|
||||
const builtinStatusAction = this.instantiationService.createInstance(BuiltinStatusLabelAction);
|
||||
|
||||
@@ -42,7 +42,7 @@ export class Label implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
export class InstallWidget implements IDisposable {
|
||||
export class InstallCountWidget implements IDisposable {
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
private _extension: IExtension;
|
||||
|
||||
@@ -58,6 +58,7 @@ import { ConfigurationTarget } from 'vs/platform/configuration/common/configurat
|
||||
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
|
||||
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
|
||||
export class PreferencesEditorInput extends SideBySideEditorInput {
|
||||
public static ID: string = 'workbench.editorinputs.preferencesEditorInput';
|
||||
@@ -123,7 +124,8 @@ export class PreferencesEditor extends BaseEditor {
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IProgressService private progressService: IProgressService
|
||||
) {
|
||||
super(PreferencesEditor.ID, telemetryService, themeService);
|
||||
this.defaultSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(this.contextKeyService);
|
||||
@@ -240,10 +242,10 @@ export class PreferencesEditor extends BaseEditor {
|
||||
private onInputChanged(): void {
|
||||
const query = this.searchWidget.getValue().trim();
|
||||
this.delayedFilterLogging.cancel();
|
||||
TPromise.join([
|
||||
this.progressService.showWhile(TPromise.join([
|
||||
this.preferencesRenderers.localFilterPreferences(query),
|
||||
this.triggerThrottledSearch(query)
|
||||
]).then(() => {
|
||||
]), 250).then(() => {
|
||||
const result = this.preferencesRenderers.lastFilterResult;
|
||||
if (result) {
|
||||
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(
|
||||
@@ -285,29 +287,33 @@ export class PreferencesEditor extends BaseEditor {
|
||||
if (count === 0) {
|
||||
this.searchWidget.showMessage(nls.localize('noSettingsFound', "No Results"), count);
|
||||
} else if (count === 1) {
|
||||
this.searchWidget.showMessage(nls.localize('oneSettingFound', "1 Setting matched"), count);
|
||||
this.searchWidget.showMessage(nls.localize('oneSettingFound', "1 Setting Found"), count);
|
||||
} else {
|
||||
this.searchWidget.showMessage(nls.localize('settingsFound', "{0} Settings matched", count), count);
|
||||
this.searchWidget.showMessage(nls.localize('settingsFound', "{0} Settings Found", count), count);
|
||||
}
|
||||
} else {
|
||||
this.searchWidget.showMessage(nls.localize('totalSettingsMessage', "Total {0} Settings", count), count);
|
||||
}
|
||||
}
|
||||
|
||||
private reportFilteringUsed(filter: string, counts: IStringDictionary<number>, metadata?: IFilterMetadata): void {
|
||||
private reportFilteringUsed(filter: string, counts: IStringDictionary<number>, metadata?: IStringDictionary<IFilterMetadata>): void {
|
||||
if (filter && filter !== this._lastReportedFilter) {
|
||||
let durations: any;
|
||||
if (metadata) {
|
||||
durations = Object.create(null);
|
||||
Object.keys(metadata).forEach(key => durations[key] = metadata[key].duration);
|
||||
}
|
||||
|
||||
let data = {
|
||||
filter,
|
||||
duration: metadata ? metadata.duration : undefined,
|
||||
context: metadata ? metadata.context : undefined,
|
||||
durations,
|
||||
counts
|
||||
};
|
||||
|
||||
/* __GDPR__
|
||||
"defaultSettings.filter" : {
|
||||
"filter": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"context" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"durations" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"counts" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
@@ -352,7 +358,7 @@ class SettingsNavigator implements INavigator<ISetting> {
|
||||
|
||||
interface IFilterOrSearchResult {
|
||||
defaultSettingsGroupCounts: IStringDictionary<number>;
|
||||
metadata: IFilterMetadata;
|
||||
metadata: IStringDictionary<IFilterMetadata>;
|
||||
}
|
||||
|
||||
class PreferencesRenderersController extends Disposable {
|
||||
@@ -434,7 +440,7 @@ class PreferencesRenderersController extends Disposable {
|
||||
this._currentNewExtensionsSearchProvider = (updateCurrentResults && this._currentNewExtensionsSearchProvider) || this.preferencesSearchService.getRemoteSearchProvider(query, true);
|
||||
|
||||
this._remoteFilterInProgress = this.filterOrSearchPreferences(query, this._currentRemoteSearchProvider, 'nlpResult', nls.localize('nlpResult', "Natural Language Results"), 1)
|
||||
.then(result => this.filterOrSearchPreferences(query, this._currentNewExtensionsSearchProvider, 'newExtensionsResult', nls.localize('newExtensionsResult', "Other Extension Results"), 2));
|
||||
.then(result => this.filterOrSearchPreferences(query, this._currentNewExtensionsSearchProvider, 'newExtensionsResult', nls.localize('newExtensionsResult', "Marketplace Extension Results"), 2));
|
||||
|
||||
return this._remoteFilterInProgress.then(() => {
|
||||
this._remoteFilterInProgress = null;
|
||||
|
||||
@@ -113,7 +113,6 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
|
||||
|
||||
if (this.filterResult) {
|
||||
data['query'] = this.filterResult.query;
|
||||
data['duration'] = this.filterResult.metadata && this.filterResult.metadata.duration;
|
||||
data['index'] = source.index;
|
||||
data['groupId'] = source.groupId;
|
||||
data['editableSide'] = !!fromEditableSettings;
|
||||
@@ -123,9 +122,8 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
|
||||
"defaultSettingsActions.copySetting" : {
|
||||
"userConfigurationKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"query" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"groupId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"groupId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"editableSide" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
@@ -622,31 +620,35 @@ export class FeedbackWidgetRenderer extends Disposable {
|
||||
}
|
||||
|
||||
const result = this._currentResult;
|
||||
const actualResults = result.metadata.scoredResults;
|
||||
const metadata = result.metadata['nlpResult']; // Feedback only on nlpResult set for now
|
||||
const marketplaceExtensionsResults = result.metadata['newExtensionsResult'] && result.metadata['newExtensionsResult'].scoredResults;
|
||||
const actualResults = metadata ? metadata.scoredResults : {};
|
||||
const actualResultIds = Object.keys(actualResults);
|
||||
|
||||
const feedbackQuery: any = {};
|
||||
feedbackQuery['comment'] = FeedbackWidgetRenderer.DEFAULT_COMMENT_TEXT;
|
||||
feedbackQuery['queryString'] = result.query;
|
||||
feedbackQuery['resultScores'] = {};
|
||||
feedbackQuery['resultScores'] = [];
|
||||
actualResultIds.forEach(settingId => {
|
||||
const outputKey = actualResults[settingId].key;
|
||||
feedbackQuery['resultScores'][outputKey] = 10;
|
||||
feedbackQuery['resultScores'].push({
|
||||
packageID: actualResults[settingId].packageId,
|
||||
key: actualResults[settingId].key,
|
||||
score: 10
|
||||
});
|
||||
});
|
||||
feedbackQuery['alts'] = [];
|
||||
|
||||
const contents = FeedbackWidgetRenderer.INSTRUCTION_TEXT + '\n' +
|
||||
JSON.stringify(feedbackQuery, undefined, ' ') + '\n\n' +
|
||||
actualResultIds.map(name => {
|
||||
return `// ${actualResults[name].key}: ${actualResults[name].score}`;
|
||||
}).join('\n');
|
||||
this.getScoreText(actualResults) + '\n\n' +
|
||||
this.getScoreText(marketplaceExtensionsResults) + '\n';
|
||||
|
||||
this.editorService.openEditor({ contents, language: 'jsonc' }, /*sideBySide=*/true).then(feedbackEditor => {
|
||||
const sendFeedbackWidget = this._register(this.instantiationService.createInstance(FloatingClickWidget, feedbackEditor.getControl(), 'Send feedback', null));
|
||||
sendFeedbackWidget.render();
|
||||
|
||||
this._register(sendFeedbackWidget.onClick(() => {
|
||||
this.sendFeedback(feedbackEditor.getControl() as ICodeEditor, result, result.metadata.scoredResults).then(() => {
|
||||
this.sendFeedback(feedbackEditor.getControl() as ICodeEditor, result, metadata.scoredResults).then(() => {
|
||||
sendFeedbackWidget.dispose();
|
||||
this.messageService.show(Severity.Info, 'Feedback sent successfully');
|
||||
}, err => {
|
||||
@@ -656,6 +658,17 @@ export class FeedbackWidgetRenderer extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private getScoreText(results?: IScoredResults): string {
|
||||
if (!results) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return Object.keys(results)
|
||||
.map(name => {
|
||||
return `// ${results[name].key}: ${results[name].score}`;
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
private sendFeedback(feedbackEditor: ICodeEditor, result: IFilterResult, actualResults: IScoredResults): TPromise<void> {
|
||||
const model = feedbackEditor.getModel();
|
||||
const expectedQueryLines = model.getLinesContent()
|
||||
@@ -685,11 +698,9 @@ export class FeedbackWidgetRenderer extends Disposable {
|
||||
"settingsSearchResultFeedback" : {
|
||||
"query" : { "classification": "CustomContent", "purpose": "FeatureInsight" },
|
||||
"userComment" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
|
||||
"actualResults" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"expectedResults" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"url" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
|
||||
"actualResults" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"timestamp" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
return this.telemetryService.publicLog('settingsSearchResultFeedback', {
|
||||
@@ -697,9 +708,7 @@ export class FeedbackWidgetRenderer extends Disposable {
|
||||
userComment,
|
||||
actualResults,
|
||||
expectedResults: expectedQuery.resultScores,
|
||||
url: result.metadata.remoteUrl,
|
||||
duration: result.metadata.duration,
|
||||
timestamp: result.metadata.timestamp,
|
||||
duration: result.metadata['nlpResult'].duration,
|
||||
buildNumber: this.environmentService.settingsSearchBuildId,
|
||||
alts,
|
||||
autoIngest
|
||||
@@ -877,7 +886,7 @@ export class ExtensionCodelensRenderer extends Disposable implements CodeLensPro
|
||||
const extId = s.extensionPublisher + '.' + s.extensionName;
|
||||
return <ICodeLensSymbol>{
|
||||
command: {
|
||||
title: nls.localize('newExtensionLabel', "View \"{0}\"", extId),
|
||||
title: nls.localize('newExtensionLabel', "Show Extension \"{0}\"", extId),
|
||||
id: 'workbench.extensions.action.showExtensionsWithId',
|
||||
arguments: [extId.toLowerCase()]
|
||||
},
|
||||
|
||||
@@ -16,6 +16,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
|
||||
import { join } from 'vs/base/common/paths';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import Event from 'vs/base/common/event';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
|
||||
export interface IWorkbenchSettingsConfiguration {
|
||||
workbench: {
|
||||
@@ -79,7 +80,7 @@ export interface IFilterResult {
|
||||
filteredGroups: ISettingsGroup[];
|
||||
allGroups: ISettingsGroup[];
|
||||
matches: IRange[];
|
||||
metadata?: IFilterMetadata;
|
||||
metadata?: IStringDictionary<IFilterMetadata>;
|
||||
}
|
||||
|
||||
export interface ISettingMatch {
|
||||
@@ -104,7 +105,8 @@ export interface IRemoteSetting {
|
||||
}
|
||||
|
||||
export interface IFilterMetadata {
|
||||
remoteUrl: string;
|
||||
requestUrl: string;
|
||||
requestBody: string;
|
||||
timestamp: number;
|
||||
duration: number;
|
||||
scoredResults: IScoredResults;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import * as map from 'vs/base/common/map';
|
||||
import { tail, flatten, first } from 'vs/base/common/arrays';
|
||||
import { tail, flatten } from 'vs/base/common/arrays';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IReference, Disposable } from 'vs/base/common/lifecycle';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
@@ -15,12 +15,13 @@ import { visit, JSONVisitor } from 'vs/base/common/json';
|
||||
import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
|
||||
import { EditorModel } from 'vs/workbench/common/editor';
|
||||
import { IConfigurationNode, IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN, IConfigurationPropertySchema, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ISettingsEditorModel, IKeybindingsEditorModel, ISettingsGroup, ISetting, IFilterResult, IGroupFilter, ISettingMatcher, ISettingMatch, ISearchResultGroup } from 'vs/workbench/parts/preferences/common/preferences';
|
||||
import { ISettingsEditorModel, IKeybindingsEditorModel, ISettingsGroup, ISetting, IFilterResult, IGroupFilter, ISettingMatcher, ISettingMatch, ISearchResultGroup, IFilterMetadata } from 'vs/workbench/parts/preferences/common/preferences';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ITextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
|
||||
export abstract class AbstractSettingsModel extends EditorModel {
|
||||
|
||||
@@ -114,6 +115,19 @@ export abstract class AbstractSettingsModel extends EditorModel {
|
||||
};
|
||||
}
|
||||
|
||||
protected collectMetadata(groups: ISearchResultGroup[]): IStringDictionary<IFilterMetadata> {
|
||||
const metadata = Object.create(null);
|
||||
let hasMetadata = false;
|
||||
groups.forEach(g => {
|
||||
if (g.result.metadata) {
|
||||
metadata[g.id] = g.result.metadata;
|
||||
hasMetadata = true;
|
||||
}
|
||||
});
|
||||
|
||||
return hasMetadata ? metadata : null;
|
||||
}
|
||||
|
||||
|
||||
protected get filterGroups(): ISettingsGroup[] {
|
||||
return this.settingsGroups;
|
||||
@@ -205,12 +219,12 @@ export class SettingsEditorModel extends AbstractSettingsModel implements ISetti
|
||||
};
|
||||
}
|
||||
|
||||
const groupWithMetadata = first(resultGroups, group => !!group.result.metadata);
|
||||
const metadata = this.collectMetadata(resultGroups);
|
||||
return <IFilterResult>{
|
||||
allGroups: this.settingsGroups,
|
||||
filteredGroups: filteredGroup ? [filteredGroup] : [],
|
||||
matches,
|
||||
metadata: groupWithMetadata && groupWithMetadata.result.metadata
|
||||
metadata
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -648,13 +662,13 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
|
||||
const startLine = tail(this.settingsGroups).range.endLineNumber + 2;
|
||||
const { settingsGroups: filteredGroups, matches } = this.writeResultGroups(nonEmptyResultGroups, startLine);
|
||||
|
||||
const groupWithMetadata = first(resultGroups, group => !!group.result.metadata);
|
||||
const metadata = this.collectMetadata(resultGroups);
|
||||
return resultGroups.length ?
|
||||
<IFilterResult>{
|
||||
allGroups: this.settingsGroups,
|
||||
filteredGroups,
|
||||
matches,
|
||||
metadata: groupWithMetadata && groupWithMetadata.result.metadata
|
||||
metadata
|
||||
} :
|
||||
null;
|
||||
}
|
||||
|
||||
@@ -215,6 +215,9 @@ class RemoteSearchProvider implements ISearchProvider {
|
||||
const packageId = r['packageid'];
|
||||
const id = getSettingKey(key, packageId);
|
||||
|
||||
const value = r['value'];
|
||||
const defaultValue = value ? JSON.parse(value) : value;
|
||||
|
||||
const packageName = r['packagename'];
|
||||
let extensionName: string;
|
||||
let extensionPublisher: string;
|
||||
@@ -225,7 +228,7 @@ class RemoteSearchProvider implements ISearchProvider {
|
||||
return <IRemoteSetting>{
|
||||
key,
|
||||
id,
|
||||
defaultValue: r['value'],
|
||||
defaultValue,
|
||||
score: r['@search.score'],
|
||||
description: JSON.parse(r['details']),
|
||||
packageId,
|
||||
@@ -240,7 +243,8 @@ class RemoteSearchProvider implements ISearchProvider {
|
||||
});
|
||||
|
||||
return <IFilterMetadata>{
|
||||
remoteUrl: details.url, // telemetry for filter text?
|
||||
requestUrl: details.url,
|
||||
requestBody: details.body,
|
||||
duration,
|
||||
timestamp,
|
||||
scoredResults,
|
||||
@@ -334,7 +338,7 @@ class RemoteSearchProvider implements ISearchProvider {
|
||||
|
||||
function getSettingKey(name: string, packageId?: string): string {
|
||||
return packageId ?
|
||||
packageId + '_' + name :
|
||||
packageId + '##' + name :
|
||||
name;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import { Mode, IEntryRunContext, IAutoFocus, IQuickNavigateConfiguration, IModel
|
||||
import { QuickOpenModel, QuickOpenEntryGroup, QuickOpenEntry } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { QuickOpenHandler, QuickOpenAction } from 'vs/workbench/browser/quickopen';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IOutputService, OUTPUT_PANEL_ID } from 'vs/workbench/parts/output/common/output';
|
||||
import { ITerminalService, TERMINAL_PANEL_ID } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { IOutputService } from 'vs/workbench/parts/output/common/output';
|
||||
import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
@@ -138,40 +138,27 @@ export class ViewPickerHandler extends QuickOpenHandler {
|
||||
|
||||
// Viewlets
|
||||
const viewlets = this.viewletService.getViewlets();
|
||||
viewlets.forEach((viewlet, index) => viewEntries.push(new ViewEntry(viewlet.name, nls.localize('views', "Views"), () => this.viewletService.openViewlet(viewlet.id, true).done(null, errors.onUnexpectedError))));
|
||||
|
||||
// Panels
|
||||
const panels = this.panelService.getPanels();
|
||||
panels.forEach((panel, index) => viewEntries.push(new ViewEntry(panel.name, nls.localize('panels', "Panels"), () => this.panelService.openPanel(panel.id, true).done(null, errors.onUnexpectedError))));
|
||||
|
||||
// Views
|
||||
viewlets.forEach((viewlet, index) => {
|
||||
const viewLocation: ViewLocation = viewlet.id === EXPLORER_VIEWLET_ID ? ViewLocation.Explorer
|
||||
: viewlet.id === DEBUG_VIEWLET_ID ? ViewLocation.Debug
|
||||
: viewlet.id === EXTENSIONS_VIEWLET_ID ? ViewLocation.Extensions
|
||||
: null;
|
||||
|
||||
const viewEntriesForViewlet: ViewEntry[] = viewLocation ? getViewEntriesForViewlet(viewlet, viewLocation)
|
||||
: [new ViewEntry(viewlet.name, nls.localize('views', "Views"), () => this.viewletService.openViewlet(viewlet.id, true).done(null, errors.onUnexpectedError))];
|
||||
|
||||
viewEntries.push(...viewEntriesForViewlet);
|
||||
});
|
||||
|
||||
const terminals = this.terminalService.terminalInstances;
|
||||
|
||||
// Panels
|
||||
const panels = this.panelService.getPanels().filter(p => {
|
||||
if (p.id === OUTPUT_PANEL_ID) {
|
||||
return false; // since we already show output channels below
|
||||
if (viewLocation) {
|
||||
const viewEntriesForViewlet: ViewEntry[] = getViewEntriesForViewlet(viewlet, viewLocation);
|
||||
viewEntries.push(...viewEntriesForViewlet);
|
||||
}
|
||||
|
||||
if (p.id === TERMINAL_PANEL_ID && terminals.length > 0) {
|
||||
return false; // since we already show terminal instances below
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
panels.forEach((panel, index) => {
|
||||
const panelsCategory = nls.localize('panels', "Panels");
|
||||
const entry = new ViewEntry(panel.name, panelsCategory, () => this.panelService.openPanel(panel.id, true).done(null, errors.onUnexpectedError));
|
||||
|
||||
viewEntries.push(entry);
|
||||
});
|
||||
|
||||
// Terminals
|
||||
const terminals = this.terminalService.terminalInstances;
|
||||
terminals.forEach((terminal, index) => {
|
||||
const terminalsCategory = nls.localize('terminals', "Terminal");
|
||||
const entry = new ViewEntry(nls.localize('terminalTitle', "{0}: {1}", index + 1, terminal.title), terminalsCategory, () => {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { Match, FileMatch, FileMatchOrMatch, ISearchWorkbenchService } from 'vs/workbench/parts/search/common/searchModel';
|
||||
import { BulkEdit, IResourceEdit, createBulkEdit } from 'vs/editor/browser/services/bulkEdit';
|
||||
import { BulkEdit } from 'vs/editor/browser/services/bulkEdit';
|
||||
import { IProgressRunner } from 'vs/platform/progress/common/progress';
|
||||
import { IDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService';
|
||||
@@ -24,6 +24,7 @@ import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ResourceTextEdit } from 'vs/editor/common/modes';
|
||||
import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
|
||||
|
||||
const REPLACE_PREVIEW = 'replacePreview';
|
||||
@@ -104,8 +105,7 @@ export class ReplaceService implements IReplaceService {
|
||||
public replace(match: FileMatchOrMatch, progress?: IProgressRunner, resource?: URI): TPromise<any>;
|
||||
public replace(arg: any, progress: IProgressRunner = null, resource: URI = null): TPromise<any> {
|
||||
|
||||
let bulkEdit: BulkEdit = createBulkEdit(this.textModelResolverService, null, this.fileService);
|
||||
bulkEdit.progress(progress);
|
||||
let bulkEdit = new BulkEdit(null, progress, this.textModelResolverService, this.fileService);
|
||||
|
||||
if (arg instanceof Match) {
|
||||
let match = <Match>arg;
|
||||
@@ -127,7 +127,7 @@ export class ReplaceService implements IReplaceService {
|
||||
});
|
||||
}
|
||||
|
||||
return bulkEdit.finish();
|
||||
return bulkEdit.perform();
|
||||
}
|
||||
|
||||
public openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise<any> {
|
||||
@@ -175,12 +175,14 @@ export class ReplaceService implements IReplaceService {
|
||||
});
|
||||
}
|
||||
|
||||
private createEdit(match: Match, text: string, resource: URI = null): IResourceEdit {
|
||||
private createEdit(match: Match, text: string, resource: URI = null): ResourceTextEdit {
|
||||
let fileMatch: FileMatch = match.parent();
|
||||
let resourceEdit: IResourceEdit = {
|
||||
let resourceEdit: ResourceTextEdit = {
|
||||
resource: resource !== null ? resource : fileMatch.resource(),
|
||||
range: match.range(),
|
||||
newText: text
|
||||
edits: [{
|
||||
range: match.range(),
|
||||
text: text
|
||||
}]
|
||||
};
|
||||
return resourceEdit;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ export const TerminalCursorStyle = {
|
||||
|
||||
export const TERMINAL_CONFIG_SECTION = 'terminal.integrated';
|
||||
|
||||
export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
|
||||
|
||||
export interface ITerminalConfiguration {
|
||||
shell: {
|
||||
linux: string;
|
||||
@@ -60,12 +62,13 @@ export interface ITerminalConfiguration {
|
||||
osx: string[];
|
||||
windows: string[];
|
||||
};
|
||||
enableBold: boolean;
|
||||
macOptionIsMeta: boolean;
|
||||
rightClickCopyPaste: boolean;
|
||||
cursorBlinking: boolean;
|
||||
cursorStyle: string;
|
||||
fontFamily: string;
|
||||
fontWeight: FontWeight;
|
||||
fontWeightBold: FontWeight;
|
||||
// fontLigatures: boolean;
|
||||
fontSize: number;
|
||||
lineHeight: number;
|
||||
|
||||
@@ -150,10 +150,17 @@ configurationRegistry.registerConfiguration({
|
||||
'type': 'number',
|
||||
'default': 1
|
||||
},
|
||||
'terminal.integrated.enableBold': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('terminal.integrated.enableBold', "Whether to enable bold text within the terminal, note that this requires support from the terminal shell."),
|
||||
'default': true
|
||||
'terminal.integrated.fontWeight': {
|
||||
'type': 'string',
|
||||
'enum': ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
||||
'description': nls.localize('terminal.integrated.fontWeight', "The font weight to use within the termianl for non-bold text."),
|
||||
'default': 'normal'
|
||||
},
|
||||
'terminal.integrated.fontWeightBold': {
|
||||
'type': 'string',
|
||||
'enum': ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
||||
'description': nls.localize('terminal.integrated.fontWeightBold', "The font weight to use within the termianl for bold text."),
|
||||
'default': 'bold'
|
||||
},
|
||||
'terminal.integrated.cursorBlinking': {
|
||||
'description': nls.localize('terminal.integrated.cursorBlinking', "Controls whether the terminal cursor blinks."),
|
||||
|
||||
@@ -278,9 +278,10 @@ export class TerminalInstance implements ITerminalInstance {
|
||||
scrollback: this._configHelper.config.scrollback,
|
||||
theme: this._getXtermTheme(),
|
||||
fontFamily: font.fontFamily,
|
||||
fontWeight: this._configHelper.config.fontWeight,
|
||||
fontWeightBold: this._configHelper.config.fontWeightBold,
|
||||
fontSize: font.fontSize,
|
||||
lineHeight: font.lineHeight,
|
||||
enableBold: this._configHelper.config.enableBold,
|
||||
bellStyle: this._configHelper.config.enableBell ? 'sound' : 'none',
|
||||
screenReaderMode: accessibilitySupport === 'on',
|
||||
macOptionIsMeta: this._configHelper.config.macOptionIsMeta
|
||||
@@ -1056,8 +1057,11 @@ export class TerminalInstance implements ITerminalInstance {
|
||||
if (this._xterm.getOption('fontFamily') !== font.fontFamily) {
|
||||
this._xterm.setOption('fontFamily', font.fontFamily);
|
||||
}
|
||||
if (this._xterm.getOption('enableBold') !== this._configHelper.config.enableBold) {
|
||||
this._xterm.setOption('enableBold', this._configHelper.config.enableBold);
|
||||
if (this._xterm.getOption('fontWeight') !== this._configHelper.config.fontWeight) {
|
||||
this._xterm.setOption('fontWeight', this._configHelper.config.fontWeight);
|
||||
}
|
||||
if (this._xterm.getOption('fontWeightBold') !== this._configHelper.config.fontWeightBold) {
|
||||
this._xterm.setOption('fontWeightBold', this._configHelper.config.fontWeightBold);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,10 +52,10 @@ export interface IBackupFileService {
|
||||
* Backs up a resource.
|
||||
*
|
||||
* @param resource The resource to back up.
|
||||
* @param content The content of the resource as value or snapshot.
|
||||
* @param content The content of the resource as snapshot.
|
||||
* @param versionId The version id of the resource to backup.
|
||||
*/
|
||||
backupResource(resource: Uri, content: string | ITextSnapshot, versionId?: number): TPromise<void>;
|
||||
backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): TPromise<void>;
|
||||
|
||||
/**
|
||||
* Gets a list of file backups for the current workspace.
|
||||
@@ -65,13 +65,12 @@ export interface IBackupFileService {
|
||||
getWorkspaceFileBackups(): TPromise<Uri[]>;
|
||||
|
||||
/**
|
||||
* Parses backup raw text content into the content, removing the metadata that is also stored
|
||||
* in the file.
|
||||
* Resolves the backup for the given resource.
|
||||
*
|
||||
* @param textBufferFactory The ITextBufferFactory from a backup resource.
|
||||
* @return The backup file's backed up content.
|
||||
* @param value The contents from a backup resource as stream.
|
||||
* @return The backup file's backed up content as text buffer factory.
|
||||
*/
|
||||
parseBackupContent(textBufferFactory: ITextBufferFactory): string;
|
||||
resolveBackupContent(backup: Uri): TPromise<ITextBufferFactory>;
|
||||
|
||||
/**
|
||||
* Discards the backup associated with a resource if it exists..
|
||||
|
||||
@@ -10,12 +10,12 @@ import * as crypto from 'crypto';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import Uri from 'vs/base/common/uri';
|
||||
import { ResourceQueue } from 'vs/base/common/async';
|
||||
import { IBackupFileService, BACKUP_FILE_UPDATE_OPTIONS } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IFileService, ITextSnapshot, IFileStat } from 'vs/platform/files/common/files';
|
||||
import { IBackupFileService, BACKUP_FILE_UPDATE_OPTIONS, BACKUP_FILE_RESOLVE_OPTIONS } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IFileService, ITextSnapshot } from 'vs/platform/files/common/files';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { readToMatchingString } from 'vs/base/node/stream';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { DefaultEndOfLine, ITextBufferFactory, EndOfLinePreference } from 'vs/editor/common/model';
|
||||
import { ITextBufferFactory } from 'vs/editor/common/model';
|
||||
import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel';
|
||||
|
||||
export interface IBackupFilesModel {
|
||||
resolve(backupRoot: string): TPromise<IBackupFilesModel>;
|
||||
@@ -171,7 +171,7 @@ export class BackupFileService implements IBackupFileService {
|
||||
});
|
||||
}
|
||||
|
||||
public backupResource(resource: Uri, content: string | ITextSnapshot, versionId?: number): TPromise<void> {
|
||||
public backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): TPromise<void> {
|
||||
if (this.isShuttingDown) {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
@@ -190,17 +190,7 @@ export class BackupFileService implements IBackupFileService {
|
||||
const preamble = `${resource.toString()}${BackupFileService.META_MARKER}`;
|
||||
|
||||
// Update content with value
|
||||
let updateContentPromise: TPromise<IFileStat>;
|
||||
if (typeof content === 'string') {
|
||||
updateContentPromise = this.fileService.updateContent(backupResource, `${preamble}${content}`, BACKUP_FILE_UPDATE_OPTIONS);
|
||||
}
|
||||
|
||||
// Update content with snapshot
|
||||
else {
|
||||
updateContentPromise = this.fileService.updateContent(backupResource, new BackupSnapshot(content, preamble), BACKUP_FILE_UPDATE_OPTIONS);
|
||||
}
|
||||
|
||||
return updateContentPromise.then(() => model.add(backupResource, versionId));
|
||||
return this.fileService.updateContent(backupResource, new BackupSnapshot(content, preamble), BACKUP_FILE_UPDATE_OPTIONS).then(() => model.add(backupResource, versionId));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -245,12 +235,27 @@ export class BackupFileService implements IBackupFileService {
|
||||
});
|
||||
}
|
||||
|
||||
public parseBackupContent(textBufferFactory: ITextBufferFactory): string {
|
||||
// The first line of a backup text file is the file name
|
||||
const textBuffer = textBufferFactory.create(DefaultEndOfLine.LF);
|
||||
const lineCount = textBuffer.getLineCount();
|
||||
const range = new Range(2, 1, lineCount, textBuffer.getLineLength(lineCount) + 1);
|
||||
return textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined);
|
||||
public resolveBackupContent(backup: Uri): TPromise<ITextBufferFactory> {
|
||||
return this.fileService.resolveStreamContent(backup, BACKUP_FILE_RESOLVE_OPTIONS).then(content => {
|
||||
|
||||
// Add a filter method to filter out everything until the meta marker
|
||||
let metaFound = false;
|
||||
const metaPreambleFilter = (chunk: string) => {
|
||||
if (!metaFound && chunk) {
|
||||
const metaIndex = chunk.indexOf(BackupFileService.META_MARKER);
|
||||
if (metaIndex === -1) {
|
||||
return ''; // meta not yet found, return empty string
|
||||
}
|
||||
|
||||
metaFound = true;
|
||||
return chunk.substr(metaIndex + 1); // meta found, return everything after
|
||||
}
|
||||
|
||||
return chunk;
|
||||
};
|
||||
|
||||
return createTextBufferFactoryFromStream(content.value, metaPreambleFilter);
|
||||
});
|
||||
}
|
||||
|
||||
public toBackupResource(resource: Uri): Uri {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user