Merge branch 'master' into aeschli/ts-sem
This commit is contained in:
commit
741a8b2b2f
2
.github/copycat.yml
vendored
2
.github/copycat.yml
vendored
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
perform: false,
|
||||
perform: true,
|
||||
target_owner: 'chrmarti',
|
||||
target_repo: 'testissues'
|
||||
}
|
||||
|
|
2
.yarnrc
2
.yarnrc
|
@ -1,3 +1,3 @@
|
|||
disturl "https://atom.io/download/electron"
|
||||
target "6.1.6"
|
||||
target "7.1.7"
|
||||
runtime "electron"
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
"minimist": "^1.2.0",
|
||||
"request": "^2.85.0",
|
||||
"terser": "4.3.8",
|
||||
"typescript": "^3.8.0-dev.20200104",
|
||||
"typescript": "^3.8.0-dev.20200108",
|
||||
"vsce": "1.48.0",
|
||||
"vscode-telemetry-extractor": "^1.5.4",
|
||||
"xml2js": "^0.4.17"
|
||||
|
|
|
@ -6,4 +6,4 @@ AddToPath=Add to PATH (requires shell restart)
|
|||
RunAfter=Run %1 after installation
|
||||
Other=Other:
|
||||
SourceFile=%1 Source File
|
||||
OpenWithCodeContextMenu=Open with %1
|
||||
OpenWithCodeContextMenu=Open w&ith %1
|
||||
|
|
|
@ -2458,10 +2458,10 @@ typescript@^3.0.1:
|
|||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
|
||||
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
|
||||
|
||||
typescript@^3.8.0-dev.20200104:
|
||||
version "3.8.0-dev.20200104"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.0-dev.20200104.tgz#521b2f0b5a288b6e3f8a095525f64712330cc649"
|
||||
integrity sha512-Zdb8X1uzvUPrRvRBqega83NxqCuN/kyxuXG1u8BV10mGOqfwQb0SreSDoDDM1zUgrqFZ93neVh3DVyWTvx6XlA==
|
||||
typescript@^3.8.0-dev.20200108:
|
||||
version "3.8.0-dev.20200108"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.0-dev.20200108.tgz#ca3a4d950cd19112d80758be779fb07d577e49bc"
|
||||
integrity sha512-SD3VEYUUrDGc0djorpi0zVdmVwmvuaSHta18WP3sS9X0HC7eA4izdjj07pVUc99IBpBw55ljUATm5vkNdvxX6w==
|
||||
|
||||
typical@^4.0.0:
|
||||
version "4.0.0"
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"git": {
|
||||
"name": "chromium",
|
||||
"repositoryUrl": "https://chromium.googlesource.com/chromium/src",
|
||||
"commitHash": "91f08db83c2ce8c722ddf0911ead8f7c473bedfa"
|
||||
"commitHash": "e4745133a1d3745f066e068b8033c6a269b59caf"
|
||||
}
|
||||
},
|
||||
"licenseDetail": [
|
||||
|
@ -40,7 +40,7 @@
|
|||
"SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
],
|
||||
"isOnlyProductionDependency": true,
|
||||
"version": "76.0.3809.146"
|
||||
"version": "78.0.3904.130"
|
||||
},
|
||||
{
|
||||
"component": {
|
||||
|
@ -48,11 +48,11 @@
|
|||
"git": {
|
||||
"name": "nodejs",
|
||||
"repositoryUrl": "https://github.com/nodejs/node",
|
||||
"commitHash": "64219741218aa87e259cf8257596073b8e747f0a"
|
||||
"commitHash": "787378879acfb212ed4ff824bf9f767a24a5cb43a"
|
||||
}
|
||||
},
|
||||
"isOnlyProductionDependency": true,
|
||||
"version": "12.4.0"
|
||||
"version": "12.8.1"
|
||||
},
|
||||
{
|
||||
"component": {
|
||||
|
@ -60,12 +60,12 @@
|
|||
"git": {
|
||||
"name": "electron",
|
||||
"repositoryUrl": "https://github.com/electron/electron",
|
||||
"commitHash": "19c705ab80cd6fdccca3d65803ec2c4addb9540a"
|
||||
"commitHash": "bef0dd868b7d6d32716c319664ed480f2ae17396"
|
||||
}
|
||||
},
|
||||
"isOnlyProductionDependency": true,
|
||||
"license": "MIT",
|
||||
"version": "6.1.6"
|
||||
"version": "7.1.7"
|
||||
},
|
||||
{
|
||||
"component": {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -22,4 +22,4 @@
|
|||
"folding": {
|
||||
"offSide": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
],
|
||||
"default": null,
|
||||
"description": "%emmetExtensionsPath%"
|
||||
},
|
||||
|
@ -453,7 +453,7 @@
|
|||
"@emmetio/html-matcher": "^0.3.3",
|
||||
"@emmetio/math-expression": "^0.1.1",
|
||||
"image-size": "^0.5.2",
|
||||
"vscode-emmet-helper": "^1.2.16",
|
||||
"vscode-emmet-helper": "^1.2.17",
|
||||
"vscode-html-languageservice": "^3.0.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2469,10 +2469,10 @@ vinyl@~2.0.1:
|
|||
remove-trailing-separator "^1.0.1"
|
||||
replace-ext "^1.0.0"
|
||||
|
||||
vscode-emmet-helper@^1.2.16:
|
||||
version "1.2.16"
|
||||
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.16.tgz#cfefb8b54c68178b4696d4abae806bb3a2b043e8"
|
||||
integrity sha512-BuQK6fTV2w65Yd0/CJGj1EOvcJ9NHWfrMJ9nA8pjnu9jzAAnXLhnbviuGT9medMiPU0mp0tJqc/8Z0qlXcqdGw==
|
||||
vscode-emmet-helper@^1.2.17:
|
||||
version "1.2.17"
|
||||
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.17.tgz#f0c6bfcebc4285d081fb2618e6e5b9a08c567afa"
|
||||
integrity sha512-X4pzcrJ8dE7M3ArFuySF5fgipKDd/EauXkiJwtjBIVRWpVNq0tF9+lNCyuC7iDUwP3Oq7ow/TGssD3GdG96Jow==
|
||||
dependencies:
|
||||
"@emmetio/extract-abbreviation" "0.1.6"
|
||||
jsonc-parser "^1.0.0"
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -22,4 +22,4 @@
|
|||
["\"", "\""],
|
||||
["'", "'"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current
|
|||
if (node.kind === ts.SyntaxKind.Identifier) {
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
if (symbol) {
|
||||
const decl = symbol.valueDeclaration || symbol.declarations[0];
|
||||
const decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0];
|
||||
if (decl) {
|
||||
let typeIdx = tokenFromDeclarationMapping[decl.kind];
|
||||
let modifierSet = 0;
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -22,4 +22,4 @@
|
|||
["\"", "\""],
|
||||
["'", "'"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -21,4 +21,4 @@
|
|||
["\"", "\""],
|
||||
["'", "'"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"],
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] },
|
||||
{ "open": "/**", "close": " */", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
|
|
@ -144,7 +144,7 @@ async function detectNpmScripts(): Promise<Task[]> {
|
|||
for (const folder of folders) {
|
||||
if (isAutoDetectionEnabled(folder)) {
|
||||
let relativePattern = new RelativePattern(folder, '**/package.json');
|
||||
let paths = await workspace.findFiles(relativePattern, '**/node_modules/**');
|
||||
let paths = await workspace.findFiles(relativePattern, '**/{node_modules,.vscode-test}/**');
|
||||
for (const path of paths) {
|
||||
if (!isExcluded(folder, path) && !visitedPackageJsonFiles.has(path.fsPath)) {
|
||||
let tasks = await provideNpmScriptsForFolder(path);
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -22,4 +22,4 @@
|
|||
["\"", "\""],
|
||||
["'", "'"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"],
|
||||
["`", "`"]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] },
|
||||
{ "open": "`", "close": "`", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -29,4 +29,4 @@
|
|||
"end": "^(?:(?:=cut\\s*$)|(?:\\s*#endregion\\b))"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"],
|
||||
["`", "`"]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] },
|
||||
{ "open": "`", "close": "`", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -23,4 +23,4 @@
|
|||
["'", "'"],
|
||||
["`", "`"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["'", "'"],
|
||||
["\"", "\""]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -24,4 +24,4 @@
|
|||
"folding": {
|
||||
"offSide": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,14 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""]
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,15 +12,17 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] },
|
||||
{ "open": "`", "close": "`", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
["'", "'"],
|
||||
["`", "`"]
|
||||
],
|
||||
"indentationRules": {
|
||||
"increaseIndentPattern": "^\\s*((begin|class|(private|protected)\\s+def|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while|case)|([^#]*\\sdo\\b)|([^#]*=\\s*(case|if|unless)))\\b([^#\\{;]|(\"|'|\/).*\\4)*(#.*)?$",
|
||||
|
|
|
@ -12,14 +12,13 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
["\"", "\""]
|
||||
],
|
||||
"indentationRules": {
|
||||
"increaseIndentPattern": "^.*\\{[^}\"']*$|^.*\\([^\\)\"']*$",
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -20,4 +20,4 @@
|
|||
["(", ")"],
|
||||
["\"", "\""]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"],
|
||||
["`", "`"]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] },
|
||||
{ "open": "`", "close": "`", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"],
|
||||
["`", "`"]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] },
|
||||
{ "open": "`", "close": "`", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -24,4 +24,4 @@
|
|||
["'", "'"],
|
||||
["`", "`"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
import API from '../utils/api';
|
||||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
import * as Proto from '../protocol';
|
||||
import * as path from 'path';
|
||||
import * as PConst from '../protocol.const';
|
||||
|
||||
class TypeScriptCallHierarchySupport implements vscode.CallHierarchyProvider {
|
||||
public static readonly minVersion = API.v380;
|
||||
public constructor(
|
||||
private readonly client: ITypeScriptServiceClient) { }
|
||||
|
||||
public async prepareCallHierarchy(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position,
|
||||
token: vscode.CancellationToken
|
||||
): Promise<vscode.CallHierarchyItem | vscode.CallHierarchyItem[] | undefined> {
|
||||
const filepath = this.client.toOpenedFilePath(document);
|
||||
if (!filepath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position);
|
||||
const response = await this.client.execute('prepareCallHierarchy', args, token);
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Array.isArray(response.body)
|
||||
? response.body.map(fromProtocolCallHierarchyItem)
|
||||
: fromProtocolCallHierarchyItem(response.body);
|
||||
}
|
||||
|
||||
public async provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise<vscode.CallHierarchyIncomingCall[] | undefined> {
|
||||
const filepath = this.client.toPath(item.uri);
|
||||
if (!filepath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const args = typeConverters.Position.toFileLocationRequestArgs(filepath, item.selectionRange.start);
|
||||
const response = await this.client.execute('provideCallHierarchyIncomingCalls', args, token);
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return response.body.map(fromProtocolCallHierchyIncomingCall);
|
||||
}
|
||||
|
||||
public async provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise<vscode.CallHierarchyOutgoingCall[] | undefined> {
|
||||
const filepath = this.client.toPath(item.uri);
|
||||
if (!filepath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const args = typeConverters.Position.toFileLocationRequestArgs(filepath, item.selectionRange.start);
|
||||
const response = await this.client.execute('provideCallHierarchyOutgoingCalls', args, token);
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return response.body.map(fromProtocolCallHierchyOutgoingCall);
|
||||
}
|
||||
}
|
||||
|
||||
function isSourceFileItem(item: Proto.CallHierarchyItem) {
|
||||
return item.kind === PConst.Kind.script || item.kind === PConst.Kind.module && item.selectionSpan.start.line === 0 && item.selectionSpan.start.offset === 0;
|
||||
}
|
||||
|
||||
function fromProtocolCallHierarchyItem(item: Proto.CallHierarchyItem): vscode.CallHierarchyItem {
|
||||
const useFileName = isSourceFileItem(item);
|
||||
const name = useFileName ? path.basename(item.file) : item.name;
|
||||
const detail = useFileName ? vscode.workspace.asRelativePath(path.dirname(item.file)) : '';
|
||||
return new vscode.CallHierarchyItem(
|
||||
typeConverters.SymbolKind.fromProtocolScriptElementKind(item.kind),
|
||||
name,
|
||||
detail,
|
||||
vscode.Uri.file(item.file),
|
||||
typeConverters.Range.fromTextSpan(item.span),
|
||||
typeConverters.Range.fromTextSpan(item.selectionSpan)
|
||||
);
|
||||
}
|
||||
|
||||
function fromProtocolCallHierchyIncomingCall(item: Proto.CallHierarchyIncomingCall): vscode.CallHierarchyIncomingCall {
|
||||
return new vscode.CallHierarchyIncomingCall(
|
||||
fromProtocolCallHierarchyItem(item.from),
|
||||
item.fromSpans.map(typeConverters.Range.fromTextSpan)
|
||||
);
|
||||
}
|
||||
|
||||
function fromProtocolCallHierchyOutgoingCall(item: Proto.CallHierarchyOutgoingCall): vscode.CallHierarchyOutgoingCall {
|
||||
return new vscode.CallHierarchyOutgoingCall(
|
||||
fromProtocolCallHierarchyItem(item.to),
|
||||
item.fromSpans.map(typeConverters.Range.fromTextSpan)
|
||||
);
|
||||
}
|
||||
|
||||
export function register(
|
||||
selector: vscode.DocumentSelector,
|
||||
client: ITypeScriptServiceClient
|
||||
) {
|
||||
return new VersionDependentRegistration(client, TypeScriptCallHierarchySupport.minVersion,
|
||||
() => vscode.languages.registerCallHierarchyProvider(selector,
|
||||
new TypeScriptCallHierarchySupport(client)));
|
||||
}
|
|
@ -16,7 +16,7 @@ import { ConfigurationDependentRegistration } from '../utils/dependentRegistrati
|
|||
import { memoize } from '../utils/memoize';
|
||||
import * as Previewer from '../utils/previewer';
|
||||
import { snippetForFunctionCall } from '../utils/snippetForFunctionCall';
|
||||
import TelemetryReporter from '../utils/telemetry';
|
||||
import { TelemetryReporter } from '../utils/telemetry';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
import TypingsStatus from '../utils/typingsStatus';
|
||||
import FileConfigurationManager from './fileConfigurationManager';
|
||||
|
@ -405,15 +405,17 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider
|
|||
"duration" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
|
||||
"type" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
|
||||
"count" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
|
||||
"updateGraphDurationMs" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
"${TypeScriptCommonProperties}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryReporter.logTelemetry('completions.execute', {
|
||||
duration: duration + '',
|
||||
type: response ? response.type : 'unknown',
|
||||
count: (response && response.type === 'response' && response.body ? response.body.entries.length : 0) + ''
|
||||
duration: duration,
|
||||
type: response?.type ?? 'unknown',
|
||||
count: response?.type === 'response' && response.body ? response.body.entries.length : 0,
|
||||
updateGraphDurationMs: response?.type === 'response' ? response.performanceData?.updateGraphDurationMs : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Command, CommandManager } from '../utils/commandManager';
|
|||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
import * as typeconverts from '../utils/typeConverters';
|
||||
import FileConfigurationManager from './fileConfigurationManager';
|
||||
import TelemetryReporter from '../utils/telemetry';
|
||||
import { TelemetryReporter } from '../utils/telemetry';
|
||||
import { nulToken } from '../utils/cancellation';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
|
|
@ -12,7 +12,7 @@ import { nulToken } from '../utils/cancellation';
|
|||
import { applyCodeActionCommands, getEditForCodeAction } from '../utils/codeAction';
|
||||
import { Command, CommandManager } from '../utils/commandManager';
|
||||
import { memoize } from '../utils/memoize';
|
||||
import TelemetryReporter from '../utils/telemetry';
|
||||
import { TelemetryReporter } from '../utils/telemetry';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
import { DiagnosticsManager } from './diagnostics';
|
||||
import FileConfigurationManager from './fileConfigurationManager';
|
||||
|
|
|
@ -11,7 +11,7 @@ import API from '../utils/api';
|
|||
import { nulToken } from '../utils/cancellation';
|
||||
import { Command, CommandManager } from '../utils/commandManager';
|
||||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
import TelemetryReporter from '../utils/telemetry';
|
||||
import { TelemetryReporter } from '../utils/telemetry';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
import FormattingOptionsManager from './fileConfigurationManager';
|
||||
import * as fileSchemes from '../utils/fileSchemes';
|
||||
|
|
|
@ -14,7 +14,7 @@ import { Disposable } from './utils/dispose';
|
|||
import * as fileSchemes from './utils/fileSchemes';
|
||||
import { LanguageDescription } from './utils/languageDescription';
|
||||
import { memoize } from './utils/memoize';
|
||||
import TelemetryReporter from './utils/telemetry';
|
||||
import { TelemetryReporter } from './utils/telemetry';
|
||||
import TypingsStatus from './utils/typingsStatus';
|
||||
|
||||
|
||||
|
@ -79,6 +79,7 @@ export default class LanguageProvider extends Disposable {
|
|||
import('./features/tagClosing').then(provider => this._register(provider.register(selector, this.description.id, this.client))),
|
||||
import('./features/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))),
|
||||
import('./features/semanticColoring').then(provider => this._register(provider.register(selector, this.client))),
|
||||
import('./features/callHierarchy').then(provider => this._register(provider.register(selector, this.client))),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ export class Kind {
|
|||
public static readonly warning = 'warning';
|
||||
public static readonly string = 'string';
|
||||
public static readonly parameter = 'parameter';
|
||||
public static readonly typeParameter = 'type parameter';
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,61 @@
|
|||
import * as Proto from 'typescript/lib/protocol';
|
||||
export = Proto;
|
||||
|
||||
declare module "typescript/lib/protocol" {
|
||||
// TODO: Remove this hardcoded type once we update to TS 3.8+ that brings in the proper types
|
||||
interface Response {
|
||||
performanceData?: {
|
||||
updateGraphDurationMs?: number;
|
||||
}
|
||||
}
|
||||
|
||||
const enum CommandTypes {
|
||||
PrepareCallHierarchy = "prepareCallHierarchy",
|
||||
ProvideCallHierarchyIncomingCalls = "provideCallHierarchyIncomingCalls",
|
||||
ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls",
|
||||
}
|
||||
|
||||
interface CallHierarchyItem {
|
||||
name: string;
|
||||
kind: ScriptElementKind;
|
||||
file: string;
|
||||
span: TextSpan;
|
||||
selectionSpan: TextSpan;
|
||||
}
|
||||
|
||||
interface CallHierarchyIncomingCall {
|
||||
from: CallHierarchyItem;
|
||||
fromSpans: TextSpan[];
|
||||
}
|
||||
|
||||
interface CallHierarchyOutgoingCall {
|
||||
to: CallHierarchyItem;
|
||||
fromSpans: TextSpan[];
|
||||
}
|
||||
|
||||
interface PrepareCallHierarchyRequest extends FileLocationRequest {
|
||||
command: CommandTypes.PrepareCallHierarchy;
|
||||
}
|
||||
|
||||
interface PrepareCallHierarchyResponse extends Response {
|
||||
readonly body: CallHierarchyItem | CallHierarchyItem[];
|
||||
}
|
||||
|
||||
interface ProvideCallHierarchyIncomingCallsRequest extends FileLocationRequest {
|
||||
command: CommandTypes.ProvideCallHierarchyIncomingCalls;
|
||||
kind: ScriptElementKind;
|
||||
}
|
||||
|
||||
interface ProvideCallHierarchyIncomingCallsResponse extends Response {
|
||||
readonly body: CallHierarchyIncomingCall[];
|
||||
}
|
||||
|
||||
interface ProvideCallHierarchyOutgoingCallsRequest extends FileLocationRequest {
|
||||
command: CommandTypes.ProvideCallHierarchyOutgoingCalls;
|
||||
kind: ScriptElementKind;
|
||||
}
|
||||
|
||||
interface ProvideCallHierarchyOutgoingCallsResponse extends Response {
|
||||
readonly body: CallHierarchyOutgoingCall[];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import * as stream from 'stream';
|
|||
import { PipeRequestCanceller, TsServerProcess, ProcessBasedTsServer } from '../tsServer/server';
|
||||
import { nulToken } from '../utils/cancellation';
|
||||
import Logger from '../utils/logger';
|
||||
import TelemetryReporter from '../utils/telemetry';
|
||||
import { TelemetryReporter } from '../utils/telemetry';
|
||||
import Tracer from '../utils/tracer';
|
||||
import * as Proto from '../protocol';
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import * as vscode from 'vscode';
|
|||
import * as Proto from '../protocol';
|
||||
import { ServerResponse, TypeScriptRequests } from '../typescriptService';
|
||||
import { Disposable } from '../utils/dispose';
|
||||
import TelemetryReporter from '../utils/telemetry';
|
||||
import { TelemetryReporter } from '../utils/telemetry';
|
||||
import Tracer from '../utils/tracer';
|
||||
import { TypeScriptVersion } from '../utils/versionProvider';
|
||||
import { Reader } from '../utils/wireProtocol';
|
||||
|
|
|
@ -15,7 +15,7 @@ import LogDirectoryProvider from '../utils/logDirectoryProvider';
|
|||
import Logger from '../utils/logger';
|
||||
import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider';
|
||||
import { PluginManager } from '../utils/plugins';
|
||||
import TelemetryReporter from '../utils/telemetry';
|
||||
import { TelemetryReporter } from '../utils/telemetry';
|
||||
import Tracer from '../utils/tracer';
|
||||
import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider';
|
||||
import { ITypeScriptServer, PipeRequestCanceller, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerProcess, TsServerDelegate } from './server';
|
||||
|
|
|
@ -58,6 +58,9 @@ interface StandardTsServerRequests {
|
|||
'signatureHelp': [Proto.SignatureHelpRequestArgs, Proto.SignatureHelpResponse];
|
||||
'typeDefinition': [Proto.FileLocationRequestArgs, Proto.TypeDefinitionResponse];
|
||||
'updateOpen': [Proto.UpdateOpenRequestArgs, Proto.Response];
|
||||
'prepareCallHierarchy': [Proto.FileLocationRequestArgs, Proto.PrepareCallHierarchyResponse];
|
||||
'provideCallHierarchyIncomingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyIncomingCallsResponse];
|
||||
'provideCallHierarchyOutgoingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyOutgoingCallsResponse];
|
||||
}
|
||||
|
||||
interface NoResponseTsServerRequests {
|
||||
|
|
|
@ -22,7 +22,7 @@ import LogDirectoryProvider from './utils/logDirectoryProvider';
|
|||
import Logger from './utils/logger';
|
||||
import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider';
|
||||
import { PluginManager } from './utils/plugins';
|
||||
import TelemetryReporter, { VSCodeTelemetryReporter } from './utils/telemetry';
|
||||
import { TelemetryReporter, VSCodeTelemetryReporter, TelemetryProperties } from './utils/telemetry';
|
||||
import Tracer from './utils/tracer';
|
||||
import { inferredProjectCompilerOptions } from './utils/tsconfig';
|
||||
import { TypeScriptVersionPicker } from './utils/versionPicker';
|
||||
|
@ -271,7 +271,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
|||
this.logger.error(message, data);
|
||||
}
|
||||
|
||||
private logTelemetry(eventName: string, properties?: { readonly [prop: string]: string }) {
|
||||
private logTelemetry(eventName: string, properties?: TelemetryProperties) {
|
||||
this.telemetryReporter.logTelemetry(eventName, properties);
|
||||
}
|
||||
|
||||
|
@ -711,7 +711,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
|||
"${include}": [
|
||||
"${TypeScriptCommonProperties}",
|
||||
"${TypeScriptRequestErrorProperties}"
|
||||
]
|
||||
],
|
||||
"command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
}
|
||||
*/
|
||||
this.logTelemetry('fatalError', { command, ...(error instanceof TypeScriptServerError ? error.telemetry : {}) });
|
||||
|
|
|
@ -31,6 +31,7 @@ export default class API {
|
|||
public static readonly v340 = API.fromSimpleString('3.4.0');
|
||||
public static readonly v345 = API.fromSimpleString('3.4.5');
|
||||
public static readonly v350 = API.fromSimpleString('3.5.0');
|
||||
public static readonly v380 = API.fromSimpleString('3.8.0');
|
||||
|
||||
public static fromVersionString(versionString: string): API {
|
||||
let version = semver.valid(versionString);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { loadMessageBundle } from 'vscode-nls';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import TelemetryReporter from './telemetry';
|
||||
import { TelemetryReporter } from './telemetry';
|
||||
import { isImplicitProjectConfigFile, openOrCreateConfigFile } from './tsconfig';
|
||||
|
||||
const localize = loadMessageBundle();
|
||||
|
|
|
@ -13,8 +13,12 @@ interface PackageInfo {
|
|||
readonly aiKey: string;
|
||||
}
|
||||
|
||||
export default interface TelemetryReporter {
|
||||
logTelemetry(eventName: string, properties?: { readonly [prop: string]: string }): void;
|
||||
export interface TelemetryProperties {
|
||||
readonly [prop: string]: string | number | undefined;
|
||||
}
|
||||
|
||||
export interface TelemetryReporter {
|
||||
logTelemetry(eventName: string, properties?: TelemetryProperties): void;
|
||||
|
||||
dispose(): void;
|
||||
}
|
||||
|
|
|
@ -9,12 +9,18 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
import * as Proto from '../protocol';
|
||||
import * as PConst from '../protocol.const';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
|
||||
export namespace Range {
|
||||
export const fromTextSpan = (span: Proto.TextSpan): vscode.Range =>
|
||||
fromLocations(span.start, span.end);
|
||||
|
||||
export const toTextSpan = (range: vscode.Range): Proto.TextSpan => ({
|
||||
start: Position.toLocation(range.start),
|
||||
end: Position.toLocation(range.end)
|
||||
});
|
||||
|
||||
export const fromLocations = (start: Proto.Location, end: Proto.Location): vscode.Range =>
|
||||
new vscode.Range(
|
||||
Math.max(0, start.line - 1), Math.max(start.offset - 1, 0),
|
||||
|
@ -90,3 +96,33 @@ export namespace WorkspaceEdit {
|
|||
return workspaceEdit;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace SymbolKind {
|
||||
export function fromProtocolScriptElementKind(kind: Proto.ScriptElementKind) {
|
||||
switch (kind) {
|
||||
case PConst.Kind.module: return vscode.SymbolKind.Module;
|
||||
case PConst.Kind.class: return vscode.SymbolKind.Class;
|
||||
case PConst.Kind.enum: return vscode.SymbolKind.Enum;
|
||||
case PConst.Kind.enumMember: return vscode.SymbolKind.EnumMember;
|
||||
case PConst.Kind.interface: return vscode.SymbolKind.Interface;
|
||||
case PConst.Kind.indexSignature: return vscode.SymbolKind.Method;
|
||||
case PConst.Kind.callSignature: return vscode.SymbolKind.Method;
|
||||
case PConst.Kind.memberFunction: return vscode.SymbolKind.Method;
|
||||
case PConst.Kind.memberVariable: return vscode.SymbolKind.Property;
|
||||
case PConst.Kind.memberGetAccessor: return vscode.SymbolKind.Property;
|
||||
case PConst.Kind.memberSetAccessor: return vscode.SymbolKind.Property;
|
||||
case PConst.Kind.variable: return vscode.SymbolKind.Variable;
|
||||
case PConst.Kind.let: return vscode.SymbolKind.Variable;
|
||||
case PConst.Kind.const: return vscode.SymbolKind.Variable;
|
||||
case PConst.Kind.localVariable: return vscode.SymbolKind.Variable;
|
||||
case PConst.Kind.alias: return vscode.SymbolKind.Variable;
|
||||
case PConst.Kind.function: return vscode.SymbolKind.Function;
|
||||
case PConst.Kind.localFunction: return vscode.SymbolKind.Function;
|
||||
case PConst.Kind.constructSignature: return vscode.SymbolKind.Constructor;
|
||||
case PConst.Kind.constructorImplementation: return vscode.SymbolKind.Constructor;
|
||||
case PConst.Kind.typeParameter: return vscode.SymbolKind.TypeParameter;
|
||||
case PConst.Kind.string: return vscode.SymbolKind.String;
|
||||
default: return vscode.SymbolKind.Variable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""]
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -27,4 +27,4 @@
|
|||
"end": "^\\s*#End Region\\b"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ const webviewId = 'myWebview';
|
|||
|
||||
const testDocument = join(vscode.workspace.rootPath || '', './bower.json');
|
||||
|
||||
suite('Webview tests', () => {
|
||||
suite.skip('Webview tests', () => {
|
||||
const disposables: vscode.Disposable[] = [];
|
||||
|
||||
function _register<T extends vscode.Disposable>(disposable: T) {
|
||||
|
|
|
@ -145,17 +145,24 @@ suite('window namespace tests', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('active editor not always correct... #49125', async function () {
|
||||
test.skip('active editor not always correct... #49125', async function () {
|
||||
const randomFile1 = await createRandomFile();
|
||||
const randomFile2 = await createRandomFile();
|
||||
|
||||
console.log('Created random files: ' + randomFile1.toString() + ' and ' + randomFile2.toString());
|
||||
|
||||
const [docA, docB] = await Promise.all([
|
||||
workspace.openTextDocument(await createRandomFile()),
|
||||
workspace.openTextDocument(await createRandomFile()),
|
||||
workspace.openTextDocument(randomFile1),
|
||||
workspace.openTextDocument(randomFile2)
|
||||
]);
|
||||
for (let c = 0; c < 4; c++) {
|
||||
let editorA = await window.showTextDocument(docA, ViewColumn.One);
|
||||
assert(window.activeTextEditor === editorA);
|
||||
console.log('Showing: ' + editorA.document.fileName + ' and active editor is: ' + window.activeTextEditor?.document.fileName);
|
||||
assert.equal(window.activeTextEditor, editorA);
|
||||
|
||||
let editorB = await window.showTextDocument(docB, ViewColumn.Two);
|
||||
assert(window.activeTextEditor === editorB);
|
||||
console.log('Showing: ' + editorB.document.fileName + ' and active editor is: ' + window.activeTextEditor?.document.fileName);
|
||||
assert.equal(window.activeTextEditor, editorB);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
{ "open": "{", "close": "}"},
|
||||
{ "open": "[", "close": "]"},
|
||||
{ "open": "(", "close": ")" },
|
||||
{ "open": "'", "close": "'" },
|
||||
{ "open": "\"", "close": "\"" },
|
||||
{ "open": "\"", "close": "\"", "notIn": ["string"] },
|
||||
{ "open": "'", "close": "'", "notIn": ["string"] },
|
||||
{ "open": "<!--", "close": "-->", "notIn": [ "comment", "string" ]},
|
||||
{ "open": "<![CDATA[", "close": "]]>", "notIn": [ "comment", "string" ]}
|
||||
],
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.42.0",
|
||||
"distro": "aafa05cffde354e7f58dc9fd2dcfc9aa3e81ea77",
|
||||
"distro": "683b7a48a0cab9871ae34d129cc31e1036746b37",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
@ -91,7 +91,7 @@
|
|||
"coveralls": "^2.11.11",
|
||||
"cson-parser": "^1.3.3",
|
||||
"debounce": "^1.0.0",
|
||||
"electron": "6.1.6",
|
||||
"electron": "7.1.7",
|
||||
"eslint": "6.8.0",
|
||||
"eslint-plugin-jsdoc": "^19.1.0",
|
||||
"event-stream": "3.3.4",
|
||||
|
@ -144,7 +144,7 @@
|
|||
"sinon": "^1.17.2",
|
||||
"source-map": "^0.4.4",
|
||||
"ts-loader": "^4.4.2",
|
||||
"typescript": "^3.8.0-dev.20200104",
|
||||
"typescript": "^3.8.0-dev.20200108",
|
||||
"typescript-formatter": "7.1.0",
|
||||
"underscore": "^1.8.2",
|
||||
"vinyl": "^2.0.0",
|
||||
|
|
|
@ -50,10 +50,10 @@ import { IFileService } from 'vs/platform/files/common/files';
|
|||
import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
|
||||
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
|
||||
import { UserDataSyncChannel, UserDataSyncUtilServiceClient } from 'vs/platform/userDataSync/common/userDataSyncIpc';
|
||||
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
import { LoggerService } from 'vs/platform/log/node/loggerService';
|
||||
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
|
||||
|
@ -63,6 +63,7 @@ import { AuthTokenChannel } from 'vs/platform/auth/common/authTokenIpc';
|
|||
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
|
||||
import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService';
|
||||
import { UserDataAutoSync } from 'vs/platform/userDataSync/electron-browser/userDataAutoSync';
|
||||
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
|
||||
|
||||
export interface ISharedProcessConfiguration {
|
||||
readonly machineId: string;
|
||||
|
@ -186,6 +187,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
|
|||
services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService));
|
||||
services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', activeWindowRouter)));
|
||||
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
|
||||
services.set(ISettingsSyncService, new SyncDescriptor(SettingsSynchroniser));
|
||||
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
|
||||
registerConfiguration();
|
||||
|
||||
|
@ -209,6 +211,10 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
|
|||
const authTokenChannel = new AuthTokenChannel(authTokenService);
|
||||
server.registerChannel('authToken', authTokenChannel);
|
||||
|
||||
const settingsSyncService = accessor.get(ISettingsSyncService);
|
||||
const settingsSyncChannel = new SettingsSyncChannel(settingsSyncService);
|
||||
server.registerChannel('settingsSync', settingsSyncChannel);
|
||||
|
||||
const userDataSyncService = accessor.get(IUserDataSyncService);
|
||||
const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService);
|
||||
server.registerChannel('userDataSync', userDataSyncChannel);
|
||||
|
|
|
@ -171,7 +171,7 @@ export class CodeApplication extends Disposable {
|
|||
app.on('web-contents-created', (_event: Event, contents) => {
|
||||
contents.on('will-attach-webview', (event: Event, webPreferences, params) => {
|
||||
|
||||
const isValidWebviewSource = (source: string): boolean => {
|
||||
const isValidWebviewSource = (source: string | undefined): boolean => {
|
||||
if (!source) {
|
||||
return false;
|
||||
}
|
||||
|
@ -191,11 +191,11 @@ export class CodeApplication extends Disposable {
|
|||
webPreferences.nodeIntegration = false;
|
||||
|
||||
// Verify URLs being loaded
|
||||
if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) {
|
||||
if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preload)) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete webPreferences.preloadUrl;
|
||||
delete (webPreferences as { preloadURL: string }).preloadURL; // https://github.com/electron/electron/issues/21553
|
||||
|
||||
// Otherwise prevent loading
|
||||
this.logService.error('webContents#web-contents-created: Prevented webview attach');
|
||||
|
@ -497,27 +497,27 @@ export class CodeApplication extends Disposable {
|
|||
this.logService.info(`Tracing: waiting for windows to get ready...`);
|
||||
|
||||
let recordingStopped = false;
|
||||
const stopRecording = (timeout: boolean) => {
|
||||
const stopRecording = async (timeout: boolean) => {
|
||||
if (recordingStopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
recordingStopped = true; // only once
|
||||
|
||||
contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), path => {
|
||||
if (!timeout) {
|
||||
if (this.dialogMainService) {
|
||||
this.dialogMainService.showMessageBox({
|
||||
type: 'info',
|
||||
message: localize('trace.message', "Successfully created trace."),
|
||||
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
|
||||
buttons: [localize('trace.ok', "Ok")]
|
||||
}, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
|
||||
}
|
||||
} else {
|
||||
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
|
||||
const path = await contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`));
|
||||
|
||||
if (!timeout) {
|
||||
if (this.dialogMainService) {
|
||||
this.dialogMainService.showMessageBox({
|
||||
type: 'info',
|
||||
message: localize('trace.message', "Successfully created trace."),
|
||||
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
|
||||
buttons: [localize('trace.ok', "Ok")]
|
||||
}, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Wait up to 30s before creating the trace anyways
|
||||
|
|
|
@ -347,9 +347,9 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||
});
|
||||
|
||||
this._win.webContents.session.webRequest.onHeadersReceived(null!, (details, callback) => {
|
||||
const responseHeaders = details.responseHeaders as { [key: string]: string[] };
|
||||
const responseHeaders = details.responseHeaders as Record<string, (string) | (string[])>;
|
||||
|
||||
const contentType: string[] = (responseHeaders['content-type'] || responseHeaders['Content-Type']);
|
||||
const contentType = (responseHeaders['content-type'] || responseHeaders['Content-Type']);
|
||||
if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) {
|
||||
return callback({ cancel: true });
|
||||
}
|
||||
|
@ -441,7 +441,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||
// Inject headers when requests are incoming
|
||||
const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*'];
|
||||
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) =>
|
||||
this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined } })));
|
||||
this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as Record<string, string> })));
|
||||
}
|
||||
|
||||
private onWindowError(error: WindowError): void {
|
||||
|
|
|
@ -83,6 +83,8 @@ export interface IConfigurationValue<T> {
|
|||
readonly workspace?: { value?: T, override?: T };
|
||||
readonly workspaceFolder?: { value?: T, override?: T };
|
||||
readonly memory?: { value?: T, override?: T };
|
||||
|
||||
readonly overrideIdentifiers?: string[];
|
||||
}
|
||||
|
||||
export interface IConfigurationService {
|
||||
|
|
|
@ -391,6 +391,7 @@ export class Configuration {
|
|||
const workspaceFolderValue = folderConfigurationModel ? overrides.overrideIdentifier ? folderConfigurationModel.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : folderConfigurationModel.freeze().getValue<C>(key) : undefined;
|
||||
const memoryValue = overrides.overrideIdentifier ? memoryConfigurationModel.override(overrides.overrideIdentifier).getValue<C>(key) : memoryConfigurationModel.getValue<C>(key);
|
||||
const value = consolidateConfigurationModel.getValue<C>(key);
|
||||
const overrideIdentifiers: string[] = arrays.distinct(arrays.flatten(consolidateConfigurationModel.overrides.map(override => override.identifiers))).filter(overrideIdentifier => consolidateConfigurationModel.getOverrideValue(key, overrideIdentifier) !== undefined);
|
||||
|
||||
return {
|
||||
defaultValue: defaultValue,
|
||||
|
@ -409,6 +410,8 @@ export class Configuration {
|
|||
workspace: workspaceValue !== undefined ? { value: this._workspaceConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this._workspaceConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
workspaceFolder: workspaceFolderValue !== undefined ? { value: folderConfigurationModel?.freeze().getValue(key), override: overrides.overrideIdentifier ? folderConfigurationModel?.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
memory: memoryValue !== undefined ? { value: memoryConfigurationModel.getValue(key), override: overrides.overrideIdentifier ? memoryConfigurationModel.getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
|
||||
overrideIdentifiers: overrideIdentifiers.length ? overrideIdentifiers : undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -361,6 +361,17 @@ suite('CustomConfigurationModel', () => {
|
|||
|
||||
suite('Configuration', () => {
|
||||
|
||||
test('Test inspect for overrideIdentifiers', () => {
|
||||
const defaultConfigurationModel = parseConfigurationModel({ '[l1]': { 'a': 1 }, '[l2]': { 'b': 1 } });
|
||||
const userConfigurationModel = parseConfigurationModel({ '[l3]': { 'a': 2 } });
|
||||
const workspaceConfigurationModel = parseConfigurationModel({ '[l1]': { 'a': 3 }, '[l4]': { 'a': 3 } });
|
||||
const testObject: Configuration = new Configuration(defaultConfigurationModel, userConfigurationModel, new ConfigurationModel(), workspaceConfigurationModel);
|
||||
|
||||
const { overrideIdentifiers } = testObject.inspect('a', {}, undefined);
|
||||
|
||||
assert.deepEqual(overrideIdentifiers, ['l1', 'l3', 'l4']);
|
||||
});
|
||||
|
||||
test('Test update value', () => {
|
||||
const parser = new ConfigurationModelParser('test');
|
||||
parser.parseContent(JSON.stringify({ 'a': 1 }));
|
||||
|
@ -468,7 +479,7 @@ suite('Configuration', () => {
|
|||
|
||||
});
|
||||
|
||||
test('Test compare and deletre workspace folder configuration', () => {
|
||||
test('Test compare and delete workspace folder configuration', () => {
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
testObject.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
|
@ -484,6 +495,12 @@ suite('Configuration', () => {
|
|||
|
||||
});
|
||||
|
||||
function parseConfigurationModel(content: any): ConfigurationModel {
|
||||
const parser = new ConfigurationModelParser('test');
|
||||
parser.parseContent(JSON.stringify(content));
|
||||
return parser.configurationModel;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
suite('ConfigurationChangeEvent', () => {
|
||||
|
|
|
@ -173,7 +173,7 @@ export class DialogMainService implements IDialogMainService {
|
|||
|
||||
showOpenDialog(options: OpenDialogOptions, window?: BrowserWindow): Promise<OpenDialogReturnValue> {
|
||||
|
||||
function normalizePaths(paths: string[] | undefined): string[] | undefined {
|
||||
function normalizePaths(paths: string[]): string[] {
|
||||
if (paths && paths.length > 0 && isMacintosh) {
|
||||
paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import { ScanCodeBinding } from 'vs/base/common/scanCode';
|
|||
import { KeybindingParser } from 'vs/base/common/keybindingParser';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { IDriver, IElement, IWindowDriver } from 'vs/platform/driver/common/driver';
|
||||
import { NativeImage } from 'electron';
|
||||
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
import { IElectronMainService } from 'vs/platform/electron/electron-main/electronMainService';
|
||||
|
||||
|
@ -67,7 +66,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
|
|||
throw new Error('Invalid window');
|
||||
}
|
||||
const webContents = window.win.webContents;
|
||||
const image = await new Promise<NativeImage>(c => webContents.capturePage(c));
|
||||
const image = await webContents.capturePage();
|
||||
return image.toPNG().toString('base64');
|
||||
}
|
||||
|
||||
|
|
|
@ -367,15 +367,13 @@ export class ElectronMainService implements IElectronMainService {
|
|||
//#region Connectivity
|
||||
|
||||
async resolveProxy(windowId: number | undefined, url: string): Promise<string | undefined> {
|
||||
return new Promise(resolve => {
|
||||
const window = this.windowById(windowId);
|
||||
const session = window?.win?.webContents?.session;
|
||||
if (session) {
|
||||
session.resolveProxy(url, proxy => resolve(proxy));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
const window = this.windowById(windowId);
|
||||
const session = window?.win?.webContents?.session;
|
||||
if (session) {
|
||||
return session.resolveProxy(url);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -57,6 +57,7 @@ export interface IProgressNotificationOptions extends IProgressOptions {
|
|||
readonly location: ProgressLocation.Notification;
|
||||
readonly primaryActions?: ReadonlyArray<IAction>;
|
||||
readonly secondaryActions?: ReadonlyArray<IAction>;
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
export interface IProgressWindowOptions extends IProgressOptions {
|
||||
|
|
|
@ -17,8 +17,12 @@ export interface ResolvedOptions {
|
|||
readonly extensionHostEnv?: { [key: string]: string | null };
|
||||
}
|
||||
|
||||
export interface TunnelDescription {
|
||||
remoteAddress: { port: number, host: string };
|
||||
localAddress: string;
|
||||
}
|
||||
export interface TunnelInformation {
|
||||
environmentTunnels?: { remoteAddress: { port: number, host: string }, localAddress: string }[];
|
||||
environmentTunnels?: TunnelDescription[];
|
||||
hideCandidatePorts?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITunnelService, RemoteTunnel, ITunnelProvider } from 'vs/platform/remote/common/tunnel';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class NoOpTunnelService implements ITunnelService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
public readonly tunnels: Promise<readonly RemoteTunnel[]> = Promise.resolve([]);
|
||||
private _onTunnelOpened: Emitter<RemoteTunnel> = new Emitter();
|
||||
public onTunnelOpened: Event<RemoteTunnel> = this._onTunnelOpened.event;
|
||||
private _onTunnelClosed: Emitter<{ host: string, port: number }> = new Emitter();
|
||||
public onTunnelClosed: Event<{ host: string, port: number }> = this._onTunnelClosed.event;
|
||||
openTunnel(_remoteHost: string, _remotePort: number): Promise<RemoteTunnel> | undefined {
|
||||
return undefined;
|
||||
}
|
||||
async closeTunnel(_remoteHost: string, _remotePort: number): Promise<void> {
|
||||
}
|
||||
setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import { values } from 'vs/base/common/map';
|
|||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||
import * as contentUtil from 'vs/platform/userDataSync/common/content';
|
||||
import { IConflictSetting } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
|
||||
export function computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: string[], formattingOptions: FormattingOptions): string {
|
||||
if (ignoredSettings.length) {
|
||||
|
@ -24,7 +25,7 @@ export function computeRemoteContent(localContent: string, remoteContent: string
|
|||
return localContent;
|
||||
}
|
||||
|
||||
export function merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: string[], formattingOptions: FormattingOptions): { mergeContent: string, hasChanges: boolean, hasConflicts: boolean } {
|
||||
export function merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: string[], resolvedConflicts: { key: string, value: any | undefined }[], formattingOptions: FormattingOptions): { mergeContent: string, hasChanges: boolean, conflicts: IConflictSetting[] } {
|
||||
const local = parse(localContent);
|
||||
const remote = parse(remoteContent);
|
||||
const base = baseContent ? parse(baseContent) : null;
|
||||
|
@ -33,30 +34,41 @@ export function merge(localContent: string, remoteContent: string, baseContent:
|
|||
const localToRemote = compare(local, remote, ignored);
|
||||
if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) {
|
||||
// No changes found between local and remote.
|
||||
return { mergeContent: localContent, hasChanges: false, hasConflicts: false };
|
||||
return { mergeContent: localContent, hasChanges: false, conflicts: [] };
|
||||
}
|
||||
|
||||
const conflicts: Set<string> = new Set<string>();
|
||||
const conflicts: Map<string, IConflictSetting> = new Map<string, IConflictSetting>();
|
||||
const handledConflicts: Set<string> = new Set<string>();
|
||||
const baseToLocal = base ? compare(base, local, ignored) : { added: Object.keys(local).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
||||
const baseToRemote = base ? compare(base, remote, ignored) : { added: Object.keys(remote).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
||||
let mergeContent = localContent;
|
||||
|
||||
const handleConflict = (conflictKey: string): void => {
|
||||
handledConflicts.add(conflictKey);
|
||||
const resolvedConflict = resolvedConflicts.filter(({ key }) => key === conflictKey)[0];
|
||||
if (resolvedConflict) {
|
||||
mergeContent = contentUtil.edit(mergeContent, [conflictKey], resolvedConflict.value, formattingOptions);
|
||||
} else {
|
||||
conflicts.set(conflictKey, { key: conflictKey, localValue: local[conflictKey], remoteValue: remote[conflictKey] });
|
||||
}
|
||||
};
|
||||
|
||||
// Removed settings in Local
|
||||
for (const key of values(baseToLocal.removed)) {
|
||||
// Got updated in remote
|
||||
if (baseToRemote.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
handleConflict(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Removed settings in Remote
|
||||
for (const key of values(baseToRemote.removed)) {
|
||||
if (conflicts.has(key)) {
|
||||
if (handledConflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
// Got updated in local
|
||||
if (baseToLocal.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
handleConflict(key);
|
||||
} else {
|
||||
mergeContent = contentUtil.edit(mergeContent, [key], undefined, formattingOptions);
|
||||
}
|
||||
|
@ -64,28 +76,28 @@ export function merge(localContent: string, remoteContent: string, baseContent:
|
|||
|
||||
// Added settings in Local
|
||||
for (const key of values(baseToLocal.added)) {
|
||||
if (conflicts.has(key)) {
|
||||
if (handledConflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
// Got added in remote
|
||||
if (baseToRemote.added.has(key)) {
|
||||
// Has different value
|
||||
if (localToRemote.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
handleConflict(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Added settings in remote
|
||||
for (const key of values(baseToRemote.added)) {
|
||||
if (conflicts.has(key)) {
|
||||
if (handledConflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
// Got added in local
|
||||
if (baseToLocal.added.has(key)) {
|
||||
// Has different value
|
||||
if (localToRemote.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
handleConflict(key);
|
||||
}
|
||||
} else {
|
||||
mergeContent = contentUtil.edit(mergeContent, [key], remote[key], formattingOptions);
|
||||
|
@ -94,28 +106,28 @@ export function merge(localContent: string, remoteContent: string, baseContent:
|
|||
|
||||
// Updated settings in Local
|
||||
for (const key of values(baseToLocal.updated)) {
|
||||
if (conflicts.has(key)) {
|
||||
if (handledConflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
// Got updated in remote
|
||||
if (baseToRemote.updated.has(key)) {
|
||||
// Has different value
|
||||
if (localToRemote.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
handleConflict(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Updated settings in Remote
|
||||
for (const key of values(baseToRemote.updated)) {
|
||||
if (conflicts.has(key)) {
|
||||
if (handledConflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
// Got updated in local
|
||||
if (baseToLocal.updated.has(key)) {
|
||||
// Has different value
|
||||
if (localToRemote.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
handleConflict(key);
|
||||
}
|
||||
} else {
|
||||
mergeContent = contentUtil.edit(mergeContent, [key], remote[key], formattingOptions);
|
||||
|
@ -126,7 +138,7 @@ export function merge(localContent: string, remoteContent: string, baseContent:
|
|||
const conflictNodes: { key: string, node: Node | undefined }[] = [];
|
||||
const tree = parseTree(mergeContent);
|
||||
const eol = formattingOptions.eol!;
|
||||
for (const key of values(conflicts)) {
|
||||
for (const { key } of values(conflicts)) {
|
||||
const node = findNodeAtLocation(tree, [key]);
|
||||
conflictNodes.push({ key, node });
|
||||
}
|
||||
|
@ -166,7 +178,7 @@ export function merge(localContent: string, remoteContent: string, baseContent:
|
|||
}
|
||||
}
|
||||
|
||||
return { mergeContent, hasChanges: true, hasConflicts: conflicts.size > 0 };
|
||||
return { mergeContent, hasChanges: true, conflicts: values(conflicts) };
|
||||
}
|
||||
|
||||
function compare(from: IStringDictionary<any>, to: IStringDictionary<any>, ignored: Set<string>): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IFileService, FileSystemProviderErrorCode, FileSystemProviderError, IFileContent } from 'vs/platform/files/common/files';
|
||||
import { IUserData, UserDataSyncStoreError, UserDataSyncStoreErrorCode, ISynchroniser, SyncStatus, IUserDataSyncStoreService, DEFAULT_IGNORED_SETTINGS, IUserDataSyncLogService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IUserData, UserDataSyncStoreError, UserDataSyncStoreErrorCode, SyncStatus, IUserDataSyncStoreService, DEFAULT_IGNORED_SETTINGS, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, ISettingsSyncService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { parse, ParseError } from 'vs/base/common/json';
|
||||
import { localize } from 'vs/nls';
|
||||
|
@ -19,16 +19,20 @@ import { startsWith } from 'vs/base/common/strings';
|
|||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { computeRemoteContent, merge } from 'vs/platform/userDataSync/common/settingsMerge';
|
||||
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
|
||||
interface ISyncPreviewResult {
|
||||
readonly fileContent: IFileContent | null;
|
||||
readonly remoteUserData: IUserData;
|
||||
readonly hasLocalChanged: boolean;
|
||||
readonly hasRemoteChanged: boolean;
|
||||
readonly hasConflicts: boolean;
|
||||
readonly conflicts: IConflictSetting[];
|
||||
}
|
||||
|
||||
export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
||||
export class SettingsSynchroniser extends Disposable implements ISettingsSyncService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private static EXTERNAL_USER_DATA_SETTINGS_KEY: string = 'settings';
|
||||
|
||||
|
@ -39,6 +43,11 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
|||
private _onDidChangStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
|
||||
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangStatus.event;
|
||||
|
||||
private _conflicts: IConflictSetting[] = [];
|
||||
get conflicts(): IConflictSetting[] { return this._conflicts; }
|
||||
private _onDidChangeConflicts: Emitter<IConflictSetting[]> = this._register(new Emitter<IConflictSetting[]>());
|
||||
readonly onDidChangeConflicts: Event<IConflictSetting[]> = this._onDidChangeConflicts.event;
|
||||
|
||||
private _onDidChangeLocal: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChangeLocal: Event<void> = this._onDidChangeLocal.event;
|
||||
|
||||
|
@ -63,6 +72,18 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
|||
this._status = status;
|
||||
this._onDidChangStatus.fire(status);
|
||||
}
|
||||
if (this._status !== SyncStatus.HasConflicts) {
|
||||
this.setConflicts([]);
|
||||
}
|
||||
}
|
||||
|
||||
private setConflicts(conflicts: IConflictSetting[]): void {
|
||||
if (!arrays.equals(this.conflicts, conflicts,
|
||||
(a, b) => a.key === b.key && objects.equals(a.localValue, b.localValue) && objects.equals(a.remoteValue, b.remoteValue))
|
||||
) {
|
||||
this._conflicts = conflicts;
|
||||
this._onDidChangeConflicts.fire(conflicts);
|
||||
}
|
||||
}
|
||||
|
||||
async sync(_continue?: boolean): Promise<boolean> {
|
||||
|
@ -83,10 +104,31 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
|||
|
||||
this.logService.trace('Settings: Started synchronizing settings...');
|
||||
this.setStatus(SyncStatus.Syncing);
|
||||
return this.doSync([]);
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (this.syncPreviewResultPromise) {
|
||||
this.syncPreviewResultPromise.cancel();
|
||||
this.syncPreviewResultPromise = null;
|
||||
this.logService.info('Settings: Stopped synchronizing settings.');
|
||||
}
|
||||
this.fileService.del(this.environmentService.settingsSyncPreviewResource);
|
||||
this.setStatus(SyncStatus.Idle);
|
||||
}
|
||||
|
||||
async resolveConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<void> {
|
||||
if (this.status === SyncStatus.HasConflicts) {
|
||||
this.syncPreviewResultPromise!.cancel();
|
||||
this.syncPreviewResultPromise = null;
|
||||
await this.doSync(resolvedConflicts);
|
||||
}
|
||||
}
|
||||
|
||||
private async doSync(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<boolean> {
|
||||
try {
|
||||
const result = await this.getPreview();
|
||||
if (result.hasConflicts) {
|
||||
const result = await this.getPreview(resolvedConflicts);
|
||||
if (result.conflicts.length) {
|
||||
this.logService.info('Settings: Detected conflicts while synchronizing settings.');
|
||||
this.setStatus(SyncStatus.HasConflicts);
|
||||
return false;
|
||||
|
@ -110,16 +152,6 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
|||
}
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (this.syncPreviewResultPromise) {
|
||||
this.syncPreviewResultPromise.cancel();
|
||||
this.syncPreviewResultPromise = null;
|
||||
this.logService.info('Settings: Stopped synchronizing settings.');
|
||||
}
|
||||
this.fileService.del(this.environmentService.settingsSyncPreviewResource);
|
||||
this.setStatus(SyncStatus.Idle);
|
||||
}
|
||||
|
||||
private async continueSync(): Promise<boolean> {
|
||||
if (this.status === SyncStatus.HasConflicts) {
|
||||
await this.apply();
|
||||
|
@ -178,14 +210,14 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
|||
return parseErrors.length > 0;
|
||||
}
|
||||
|
||||
private getPreview(): Promise<ISyncPreviewResult> {
|
||||
private getPreview(resolvedConflicts: { key: string, value: any }[]): Promise<ISyncPreviewResult> {
|
||||
if (!this.syncPreviewResultPromise) {
|
||||
this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview(token));
|
||||
this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview(resolvedConflicts, token));
|
||||
}
|
||||
return this.syncPreviewResultPromise;
|
||||
}
|
||||
|
||||
private async generatePreview(token: CancellationToken): Promise<ISyncPreviewResult> {
|
||||
private async generatePreview(resolvedConflicts: { key: string, value: any }[], token: CancellationToken): Promise<ISyncPreviewResult> {
|
||||
const lastSyncData = await this.getLastSyncUserData();
|
||||
const remoteUserData = await this.userDataSyncStoreService.read(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, lastSyncData);
|
||||
const remoteContent: string | null = remoteUserData.content;
|
||||
|
@ -193,28 +225,29 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
|||
const fileContent = await this.getLocalFileContent();
|
||||
let hasLocalChanged: boolean = false;
|
||||
let hasRemoteChanged: boolean = false;
|
||||
let hasConflicts: boolean = false;
|
||||
let conflicts: IConflictSetting[] = [];
|
||||
let previewContent = null;
|
||||
|
||||
if (remoteContent) {
|
||||
const localContent: string = fileContent ? fileContent.value.toString() : '{}';
|
||||
|
||||
// No action when there are errors
|
||||
if (this.hasErrors(localContent)) {
|
||||
this.logService.error('Settings: Unable to sync settings as there are errors/warning in settings file.');
|
||||
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
|
||||
}
|
||||
|
||||
if (!lastSyncData // First time sync
|
||||
else if (!lastSyncData // First time sync
|
||||
|| lastSyncData.content !== localContent // Local has forwarded
|
||||
|| lastSyncData.content !== remoteContent // Remote has forwarded
|
||||
) {
|
||||
this.logService.trace('Settings: Merging remote settings with local settings...');
|
||||
const formatUtils = await this.getFormattingOptions();
|
||||
const result = merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null, this.getIgnoredSettings(), formatUtils);
|
||||
const result = merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null, this.getIgnoredSettings(), resolvedConflicts, formatUtils);
|
||||
// Sync only if there are changes
|
||||
if (result.hasChanges) {
|
||||
hasLocalChanged = result.mergeContent !== localContent;
|
||||
hasRemoteChanged = result.mergeContent !== remoteContent;
|
||||
hasConflicts = result.hasConflicts;
|
||||
conflicts = result.conflicts;
|
||||
previewContent = result.mergeContent;
|
||||
}
|
||||
}
|
||||
|
@ -231,7 +264,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
|||
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(previewContent));
|
||||
}
|
||||
|
||||
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
|
||||
this.setConflicts(conflicts);
|
||||
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, conflicts };
|
||||
}
|
||||
|
||||
private _formattingOptions: Promise<FormattingOptions> | undefined = undefined;
|
||||
|
|
|
@ -57,7 +57,7 @@ export function registerConfiguration(): IDisposable {
|
|||
},
|
||||
'sync.enableUIState': {
|
||||
type: 'boolean',
|
||||
description: localize('sync.enableUIState', "Enable synchronizing UI state."),
|
||||
description: localize('sync.enableUIState', "Enable synchronizing UI state (Only Display Language)."),
|
||||
default: true,
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
},
|
||||
|
@ -135,12 +135,9 @@ export function getUserDataSyncStore(configurationService: IConfigurationService
|
|||
}
|
||||
|
||||
export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService');
|
||||
|
||||
export interface IUserDataSyncStoreService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
readonly userDataSyncStore: IUserDataSyncStore | undefined;
|
||||
|
||||
read(key: string, oldValue: IUserData | null): Promise<IUserData>;
|
||||
write(key: string, content: string, ref: string | null): Promise<string>;
|
||||
}
|
||||
|
@ -170,40 +167,42 @@ export const enum SyncStatus {
|
|||
}
|
||||
|
||||
export interface ISynchroniser {
|
||||
|
||||
readonly status: SyncStatus;
|
||||
readonly onDidChangeStatus: Event<SyncStatus>;
|
||||
readonly onDidChangeLocal: Event<void>;
|
||||
|
||||
sync(_continue?: boolean): Promise<boolean>;
|
||||
stop(): void;
|
||||
}
|
||||
|
||||
export const IUserDataSyncService = createDecorator<IUserDataSyncService>('IUserDataSyncService');
|
||||
|
||||
export interface IUserDataSyncService extends ISynchroniser {
|
||||
_serviceBrand: any;
|
||||
readonly conflictsSource: SyncSource | null;
|
||||
|
||||
removeExtension(identifier: IExtensionIdentifier): Promise<void>;
|
||||
}
|
||||
|
||||
export const IUserDataSyncUtilService = createDecorator<IUserDataSyncUtilService>('IUserDataSyncUtilService');
|
||||
|
||||
export interface IUserDataSyncUtilService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
resolveUserBindings(userbindings: string[]): Promise<IStringDictionary<string>>;
|
||||
|
||||
resolveFormattingOptions(resource: URI): Promise<FormattingOptions>;
|
||||
|
||||
}
|
||||
|
||||
export const IUserDataSyncLogService = createDecorator<IUserDataSyncLogService>('IUserDataSyncLogService');
|
||||
export interface IUserDataSyncLogService extends ILogService { }
|
||||
|
||||
export interface IUserDataSyncLogService extends ILogService {
|
||||
export interface IConflictSetting {
|
||||
key: string;
|
||||
localValue: any | undefined;
|
||||
remoteValue: any | undefined;
|
||||
}
|
||||
|
||||
export const ISettingsSyncService = createDecorator<ISettingsSyncService>('ISettingsSyncService');
|
||||
export interface ISettingsSyncService extends ISynchroniser {
|
||||
_serviceBrand: any;
|
||||
readonly onDidChangeConflicts: Event<IConflictSetting[]>;
|
||||
readonly conflicts: IConflictSetting[];
|
||||
resolveConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<void>;
|
||||
}
|
||||
|
||||
export const CONTEXT_SYNC_STATE = new RawContextKey<string>('syncStatus', SyncStatus.Uninitialized);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IUserDataSyncService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||
|
@ -34,6 +34,31 @@ export class UserDataSyncChannel implements IServerChannel {
|
|||
}
|
||||
}
|
||||
|
||||
export class SettingsSyncChannel implements IServerChannel {
|
||||
|
||||
constructor(private readonly service: ISettingsSyncService) { }
|
||||
|
||||
listen(_: unknown, event: string): Event<any> {
|
||||
switch (event) {
|
||||
case 'onDidChangeStatus': return this.service.onDidChangeStatus;
|
||||
case 'onDidChangeLocal': return this.service.onDidChangeLocal;
|
||||
case 'onDidChangeConflicts': return this.service.onDidChangeConflicts;
|
||||
}
|
||||
throw new Error(`Event not found: ${event}`);
|
||||
}
|
||||
|
||||
call(context: any, command: string, args?: any): Promise<any> {
|
||||
switch (command) {
|
||||
case 'sync': return this.service.sync(args[0]);
|
||||
case '_getInitialStatus': return Promise.resolve(this.service.status);
|
||||
case '_getInitialConflicts': return Promise.resolve(this.service.conflicts);
|
||||
case 'stop': this.service.stop(); return Promise.resolve();
|
||||
case 'resolveConflicts': return this.service.resolveConflicts(args[0]);
|
||||
}
|
||||
throw new Error('Invalid call');
|
||||
}
|
||||
}
|
||||
|
||||
export class UserDataSycnUtilServiceChannel implements IServerChannel {
|
||||
|
||||
constructor(private readonly service: IUserDataSyncUtilService) { }
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, ISettingsSyncService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
|
||||
|
@ -30,7 +30,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
|||
private _conflictsSource: SyncSource | null = null;
|
||||
get conflictsSource(): SyncSource | null { return this._conflictsSource; }
|
||||
|
||||
private readonly settingsSynchroniser: SettingsSynchroniser;
|
||||
private readonly keybindingsSynchroniser: KeybindingsSynchroniser;
|
||||
private readonly extensionsSynchroniser: ExtensionsSynchroniser;
|
||||
private readonly globalStateSynchroniser: GlobalStateSynchroniser;
|
||||
|
@ -39,9 +38,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
|||
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IAuthTokenService private readonly authTokenService: IAuthTokenService,
|
||||
@ISettingsSyncService private readonly settingsSynchroniser: ISettingsSyncService,
|
||||
) {
|
||||
super();
|
||||
this.settingsSynchroniser = this._register(this.instantiationService.createInstance(SettingsSynchroniser));
|
||||
this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser));
|
||||
this.globalStateSynchroniser = this._register(this.instantiationService.createInstance(GlobalStateSynchroniser));
|
||||
this.extensionsSynchroniser = this._register(this.instantiationService.createInstance(ExtensionsSynchroniser));
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { merge, computeRemoteContent } from 'vs/platform/userDataSync/common/settingsMerge';
|
||||
import { IConflictSetting } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
|
||||
const formattingOptions = { eol: '\n', insertSpaces: false, tabSize: 4 };
|
||||
|
||||
|
@ -13,9 +14,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
test('merge when local and remote are same with one entry', async () => {
|
||||
const localContent = stringify({ 'a': 1 });
|
||||
const remoteContent = stringify({ 'a': 1 });
|
||||
const actual = merge(localContent, remoteContent, null, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, null, [], [], formattingOptions);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
|
@ -28,9 +29,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
'a': 1,
|
||||
'b': 2
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, null, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, null, [], [], formattingOptions);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
|
@ -43,9 +44,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
'a': 1,
|
||||
'b': 2
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, null, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, null, [], [], formattingOptions);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
|
@ -62,9 +63,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
'a': 1,
|
||||
'b': 2
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, baseContent, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, baseContent, [], [], formattingOptions);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
|
@ -76,9 +77,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
'a': 1,
|
||||
'b': 2
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, null, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, null, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
|
@ -96,9 +97,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
'b': 2,
|
||||
'c': 3,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, null, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, null, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, expected);
|
||||
});
|
||||
|
||||
|
@ -116,9 +117,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
'b': 2,
|
||||
'c': 3,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, localContent, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, localContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, expected);
|
||||
});
|
||||
|
||||
|
@ -130,9 +131,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
const remoteContent = stringify({
|
||||
'a': 1,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, localContent, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, localContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
|
@ -141,9 +142,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
'a': 1,
|
||||
});
|
||||
const remoteContent = stringify({});
|
||||
const actual = merge(localContent, remoteContent, localContent, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, localContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.deepEqual(JSON.parse(actual.mergeContent), {});
|
||||
});
|
||||
|
||||
|
@ -154,9 +155,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
const remoteContent = stringify({
|
||||
'a': 2
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, localContent, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, localContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
|
@ -170,9 +171,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
'c': 3,
|
||||
'd': 4,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, localContent, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, localContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
|
@ -186,9 +187,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
const remoteContent = stringify({
|
||||
'a': 1,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, null, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, null, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
|
@ -202,9 +203,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
const remoteContent = stringify({
|
||||
'a': 1,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, remoteContent, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, remoteContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
|
@ -219,9 +220,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
'c': 3,
|
||||
'd': 4,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, remoteContent, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, remoteContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
|
@ -234,9 +235,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
'a': 2,
|
||||
'c': 2,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, remoteContent, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, remoteContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
|
@ -250,9 +251,9 @@ suite('SettingsMerge - No Conflicts', () => {
|
|||
const remoteContent = stringify({
|
||||
'a': 1,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, remoteContent, [], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, remoteContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
|
@ -267,9 +268,10 @@ suite('SettingsMerge - Conflicts', () => {
|
|||
const remoteContent = stringify({
|
||||
'a': 2
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, null, [], formattingOptions);
|
||||
const expectedConflicts: IConflictSetting[] = [{ key: 'a', localValue: 1, remoteValue: 2 }];
|
||||
const actual = merge(localContent, remoteContent, null, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.deepEqual(actual.conflicts, expectedConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`{
|
||||
<<<<<<< local
|
||||
|
@ -290,9 +292,10 @@ suite('SettingsMerge - Conflicts', () => {
|
|||
const remoteContent = stringify({
|
||||
'b': 2
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, baseContent, [], formattingOptions);
|
||||
const expectedConflicts: IConflictSetting[] = [{ key: 'a', localValue: 2, remoteValue: undefined }];
|
||||
const actual = merge(localContent, remoteContent, baseContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.deepEqual(actual.conflicts, expectedConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`{
|
||||
<<<<<<< local
|
||||
|
@ -311,9 +314,10 @@ suite('SettingsMerge - Conflicts', () => {
|
|||
const remoteContent = stringify({
|
||||
'a': 2
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, baseContent, [], formattingOptions);
|
||||
const expectedConflicts: IConflictSetting[] = [{ key: 'a', localValue: undefined, remoteValue: 2 }];
|
||||
const actual = merge(localContent, remoteContent, baseContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.deepEqual(actual.conflicts, expectedConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`{
|
||||
<<<<<<< local
|
||||
|
@ -343,9 +347,15 @@ suite('SettingsMerge - Conflicts', () => {
|
|||
'd': 6,
|
||||
'e': 5,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, baseContent, [], formattingOptions);
|
||||
const expectedConflicts: IConflictSetting[] = [
|
||||
{ key: 'b', localValue: undefined, remoteValue: 3 },
|
||||
{ key: 'a', localValue: 2, remoteValue: undefined },
|
||||
{ key: 'e', localValue: 4, remoteValue: 5 },
|
||||
{ key: 'd', localValue: 5, remoteValue: 6 },
|
||||
];
|
||||
const actual = merge(localContent, remoteContent, baseContent, [], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.deepEqual(actual.conflicts, expectedConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`{
|
||||
<<<<<<< local
|
||||
|
@ -371,6 +381,46 @@ suite('SettingsMerge - Conflicts', () => {
|
|||
}`);
|
||||
});
|
||||
|
||||
test('resolve when local and remote has moved forwareded with conflicts', async () => {
|
||||
const baseContent = stringify({
|
||||
'a': 1,
|
||||
'b': 2,
|
||||
'c': 3,
|
||||
'd': 4,
|
||||
});
|
||||
const localContent = stringify({
|
||||
'a': 2,
|
||||
'c': 3,
|
||||
'd': 5,
|
||||
'e': 4,
|
||||
'f': 1,
|
||||
});
|
||||
const remoteContent = stringify({
|
||||
'b': 3,
|
||||
'c': 3,
|
||||
'd': 6,
|
||||
'e': 5,
|
||||
});
|
||||
const expectedConflicts: IConflictSetting[] = [
|
||||
{ key: 'd', localValue: 5, remoteValue: 6 },
|
||||
];
|
||||
const actual = merge(localContent, remoteContent, baseContent, [], [{ key: 'a', value: 2 }, { key: 'b', value: undefined }, { key: 'e', value: 5 }], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.deepEqual(actual.conflicts, expectedConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`{
|
||||
"a": 2,
|
||||
"c": 3,
|
||||
<<<<<<< local
|
||||
"d": 5,
|
||||
=======
|
||||
"d": 6,
|
||||
>>>>>>> remote
|
||||
"e": 5,
|
||||
"f": 1
|
||||
}`);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('SettingsMerge - Ignored Settings', () => {
|
||||
|
@ -378,9 +428,9 @@ suite('SettingsMerge - Ignored Settings', () => {
|
|||
test('ignored setting is not merged when changed in local and remote', async () => {
|
||||
const localContent = stringify({ 'a': 1 });
|
||||
const remoteContent = stringify({ 'a': 2 });
|
||||
const actual = merge(localContent, remoteContent, null, ['a'], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, null, ['a'], [], formattingOptions);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
|
@ -388,45 +438,45 @@ suite('SettingsMerge - Ignored Settings', () => {
|
|||
const baseContent = stringify({ 'a': 0 });
|
||||
const localContent = stringify({ 'a': 1 });
|
||||
const remoteContent = stringify({ 'a': 2 });
|
||||
const actual = merge(localContent, remoteContent, baseContent, ['a'], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, baseContent, ['a'], [], formattingOptions);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('ignored setting is not merged when added in remote', async () => {
|
||||
const localContent = stringify({});
|
||||
const remoteContent = stringify({ 'a': 1 });
|
||||
const actual = merge(localContent, remoteContent, null, ['a'], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, null, ['a'], [], formattingOptions);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('ignored setting is not merged when added in remote from base', async () => {
|
||||
const localContent = stringify({ 'b': 2 });
|
||||
const remoteContent = stringify({ 'a': 1, 'b': 2 });
|
||||
const actual = merge(localContent, remoteContent, localContent, ['a'], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, localContent, ['a'], [], formattingOptions);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('ignored setting is not merged when removed in remote', async () => {
|
||||
const localContent = stringify({ 'a': 1 });
|
||||
const remoteContent = stringify({});
|
||||
const actual = merge(localContent, remoteContent, null, ['a'], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, null, ['a'], [], formattingOptions);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('ignored setting is not merged when removed in remote from base', async () => {
|
||||
const localContent = stringify({ 'a': 2 });
|
||||
const remoteContent = stringify({});
|
||||
const actual = merge(localContent, remoteContent, localContent, ['a'], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, localContent, ['a'], [], formattingOptions);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
|
@ -453,9 +503,9 @@ suite('SettingsMerge - Ignored Settings', () => {
|
|||
'a': 1,
|
||||
'b': 3,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, baseContent, ['a', 'e'], formattingOptions);
|
||||
const actual = merge(localContent, remoteContent, baseContent, ['a', 'e'], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.conflicts.length, 0);
|
||||
assert.equal(actual.mergeContent, expectedContent);
|
||||
});
|
||||
|
||||
|
@ -478,12 +528,14 @@ suite('SettingsMerge - Ignored Settings', () => {
|
|||
'b': 3,
|
||||
'e': 6,
|
||||
});
|
||||
const actual = merge(localContent, remoteContent, baseContent, ['a', 'e'], formattingOptions);
|
||||
//'{\n\t"a": 1,\n\n<<<<<<< local\t"b": 4,\n=======\n\t"b": 3,\n>>>>>>> remote'
|
||||
//'{\n\t"a": 1,\n<<<<<<< local\n\t"b": 4,\n=======\n\t"b": 3,\n>>>>>>> remote\n<<<<<<< local\n\t"d": 5\n=======\n>>>>>>> remote\n}'
|
||||
const expectedConflicts: IConflictSetting[] = [
|
||||
{ key: 'd', localValue: 5, remoteValue: undefined },
|
||||
{ key: 'b', localValue: 4, remoteValue: 3 },
|
||||
];
|
||||
const actual = merge(localContent, remoteContent, baseContent, ['a', 'e'], [], formattingOptions);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.deepEqual(actual.conflicts, expectedConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`{
|
||||
"a": 1,
|
||||
|
|
11
src/vs/vscode.proposed.d.ts
vendored
11
src/vs/vscode.proposed.d.ts
vendored
|
@ -40,10 +40,13 @@ declare module 'vscode' {
|
|||
label?: string;
|
||||
}
|
||||
|
||||
export interface Tunnel {
|
||||
export interface TunnelDescription {
|
||||
remoteAddress: { port: number, host: string };
|
||||
//The complete local address(ex. localhost:1234)
|
||||
localAddress: string;
|
||||
}
|
||||
|
||||
export interface Tunnel extends TunnelDescription {
|
||||
// Implementers of Tunnel should fire onDidDispose when dispose is called.
|
||||
onDidDispose: Event<void>;
|
||||
dispose(): void;
|
||||
|
@ -58,7 +61,7 @@ declare module 'vscode' {
|
|||
* The localAddress should be the complete local address (ex. localhost:1234) for connecting to the port. Tunnels provided through
|
||||
* detected are read-only from the forwarded ports UI.
|
||||
*/
|
||||
environmentTunnels?: { remoteAddress: { port: number, host: string }, localAddress: string }[];
|
||||
environmentTunnels?: TunnelDescription[];
|
||||
|
||||
hideCandidatePorts?: boolean;
|
||||
}
|
||||
|
@ -1302,7 +1305,7 @@ declare module 'vscode' {
|
|||
|
||||
//#region Language specific settings: https://github.com/microsoft/vscode/issues/26707
|
||||
|
||||
export type ConfigurationScope = Uri | TextDocument | WorkspaceFolder | { uri: Uri, languageId: string };
|
||||
export type ConfigurationScope = Uri | TextDocument | WorkspaceFolder | { uri?: Uri, languageId: string };
|
||||
|
||||
/**
|
||||
* An event describing the change in Configuration
|
||||
|
@ -1428,6 +1431,8 @@ declare module 'vscode' {
|
|||
workspaceLanguageValue?: T;
|
||||
workspaceFolderLanguageValue?: T;
|
||||
|
||||
languages?: string[];
|
||||
|
||||
} | undefined;
|
||||
|
||||
/**
|
||||
|
|
|
@ -227,12 +227,12 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
|
|||
}
|
||||
|
||||
private _doCreateUntitled(resource?: URI, mode?: string, initialValue?: string): Promise<URI> {
|
||||
return this._untitledTextEditorService.loadOrCreate({
|
||||
return this._untitledTextEditorService.createOrGet({
|
||||
resource,
|
||||
mode,
|
||||
initialValue,
|
||||
useResourcePath: Boolean(resource && resource.path)
|
||||
}).then(model => {
|
||||
}).resolve().then(model => {
|
||||
const resource = model.resource;
|
||||
|
||||
if (!this._modelIsSynced.has(resource.toString())) {
|
||||
|
|
|
@ -27,7 +27,7 @@ import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/c
|
|||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { IProgressService, ProgressLocation, IProgressStep, IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { ISaveParticipant, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
@ -37,6 +37,8 @@ import { IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/sta
|
|||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { SettingsEditor2 } from 'vs/workbench/contrib/preferences/browser/settingsEditor2';
|
||||
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { canceled, isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
|
||||
export interface ICodeActionsOnSaveOptions {
|
||||
[kind: string]: boolean;
|
||||
|
@ -48,8 +50,8 @@ class SaveParticipantError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
export interface ISaveParticipantParticipant extends ISaveParticipant {
|
||||
// progressMessage: string;
|
||||
export interface ISaveParticipantParticipant {
|
||||
participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void>;
|
||||
}
|
||||
|
||||
class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
|
||||
|
@ -92,7 +94,7 @@ class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
|
|||
return; // Nothing to do
|
||||
}
|
||||
|
||||
model.pushEditOperations(prevSelection, ops, (edits) => prevSelection);
|
||||
model.pushEditOperations(prevSelection, ops, (_edits) => prevSelection);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,7 +125,7 @@ export class FinalNewLineParticipant implements ISaveParticipantParticipant {
|
|||
// Nothing
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
|
||||
async participate(model: IResolvedTextFileEditorModel, _env: { reason: SaveReason; }): Promise<void> {
|
||||
if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
|
||||
this.doInsertFinalNewLine(model.textEditorModel);
|
||||
}
|
||||
|
@ -209,7 +211,7 @@ export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant
|
|||
return;
|
||||
}
|
||||
|
||||
model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], edits => prevSelection);
|
||||
model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], _edits => prevSelection);
|
||||
|
||||
if (editor) {
|
||||
editor.setSelections(prevSelection);
|
||||
|
@ -227,7 +229,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
|
|||
// Nothing
|
||||
}
|
||||
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
|
||||
|
||||
const model = editorModel.textEditorModel;
|
||||
const overrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri };
|
||||
|
@ -236,26 +238,13 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
const source = new CancellationTokenSource();
|
||||
const editorOrModel = findEditor(model, this._codeEditorService) || model;
|
||||
const timeout = this._configurationService.getValue<number>('editor.formatOnSaveTimeout', overrides);
|
||||
const request = this._instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, source.token);
|
||||
|
||||
setTimeout(() => {
|
||||
reject(new SaveParticipantError(
|
||||
localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout),
|
||||
'editor.formatOnSaveTimeout'
|
||||
));
|
||||
source.cancel();
|
||||
}, timeout);
|
||||
|
||||
request.then(resolve, reject);
|
||||
});
|
||||
progress.report({ message: localize('formatting', "Formatting") });
|
||||
const editorOrModel = findEditor(model, this._codeEditorService) || model;
|
||||
await this._instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, token);
|
||||
}
|
||||
}
|
||||
|
||||
class CodeActionOnSaveParticipant implements ISaveParticipant {
|
||||
class CodeActionOnSaveParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
constructor(
|
||||
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
|
||||
|
@ -264,7 +253,7 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
|
|||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) { }
|
||||
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
|
||||
if (env.reason === SaveReason.AUTO) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -300,23 +289,8 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
|
|||
.filter(x => setting[x] === false)
|
||||
.map(x => new CodeActionKind(x));
|
||||
|
||||
const tokenSource = new CancellationTokenSource();
|
||||
|
||||
const timeout = this._configurationService.getValue<number>('editor.codeActionsOnSaveTimeout', settingsOverrides);
|
||||
|
||||
return Promise.race([
|
||||
new Promise<void>((_resolve, reject) =>
|
||||
setTimeout(() => {
|
||||
tokenSource.cancel();
|
||||
reject(new SaveParticipantError(
|
||||
localize('codeActionsOnSave.didTimeout', "Aborted codeActionsOnSave after {0}ms", timeout),
|
||||
'editor.codeActionsOnSaveTimeout'
|
||||
));
|
||||
}, timeout)),
|
||||
this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, tokenSource.token)
|
||||
]).finally(() => {
|
||||
tokenSource.cancel();
|
||||
});
|
||||
progress.report({ message: localize('codeaction', "Quick Fixes") });
|
||||
await this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, token);
|
||||
}
|
||||
|
||||
private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], token: CancellationToken): Promise<void> {
|
||||
|
@ -354,7 +328,7 @@ class ExtHostSaveParticipant implements ISaveParticipantParticipant {
|
|||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentSaveParticipant);
|
||||
}
|
||||
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, _progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
|
||||
|
||||
if (!shouldSynchronizeModel(editorModel.textEditorModel)) {
|
||||
// the model never made it to the extension
|
||||
|
@ -363,6 +337,9 @@ class ExtHostSaveParticipant implements ISaveParticipantParticipant {
|
|||
}
|
||||
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
|
||||
token.onCancellationRequested(() => reject(canceled()));
|
||||
|
||||
setTimeout(
|
||||
() => reject(new SaveParticipantError(localize('timeout.onWillSave', "Aborted onWillSaveTextDocument-event after 1750ms"))),
|
||||
1750
|
||||
|
@ -388,7 +365,8 @@ export class SaveParticipant implements ISaveParticipant {
|
|||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IProgressService private readonly _progressService: IProgressService,
|
||||
@IStatusbarService private readonly _statusbarService: IStatusbarService,
|
||||
@ILogService private readonly _logService: ILogService
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
) {
|
||||
this._saveParticipants = new IdleValue(() => [
|
||||
instantiationService.createInstance(TrimWhitespaceParticipant),
|
||||
|
@ -408,23 +386,41 @@ export class SaveParticipant implements ISaveParticipant {
|
|||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
|
||||
return this._progressService.withProgress({ location: ProgressLocation.Window }, async progress => {
|
||||
progress.report({ message: localize('saveParticipants', "Running Save Participants...") });
|
||||
|
||||
const cts = new CancellationTokenSource();
|
||||
|
||||
return this._progressService.withProgress({
|
||||
title: localize('saveParticipants', "Running Save Participants for '{0}'", this._labelService.getUriLabel(model.resource, { relative: true })),
|
||||
location: ProgressLocation.Notification,
|
||||
cancellable: true,
|
||||
delay: model.isDirty() ? 3000 : 5000
|
||||
}, async progress => {
|
||||
|
||||
let firstError: SaveParticipantError | undefined;
|
||||
|
||||
for (let p of this._saveParticipants.getValue()) {
|
||||
|
||||
if (cts.token.isCancellationRequested) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
await p.participate(model, env);
|
||||
await p.participate(model, env, progress, cts.token);
|
||||
|
||||
} catch (err) {
|
||||
this._logService.warn(err);
|
||||
firstError = !firstError && err instanceof SaveParticipantError ? err : firstError;
|
||||
if (!isPromiseCanceledError(err)) {
|
||||
this._logService.warn(err);
|
||||
firstError = !firstError && err instanceof SaveParticipantError ? err : firstError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstError) {
|
||||
this._showParticipantError(firstError);
|
||||
}
|
||||
|
||||
}, () => {
|
||||
// user cancel
|
||||
cts.dispose(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -45,14 +45,9 @@ type ConfigurationInspect<T> = {
|
|||
userLanguageValue?: T;
|
||||
workspaceLanguageValue?: T;
|
||||
workspaceFolderLanguageValue?: T;
|
||||
};
|
||||
|
||||
function isWorkspaceFolder(thing: any): thing is vscode.WorkspaceFolder {
|
||||
return thing
|
||||
&& thing.uri instanceof URI
|
||||
&& (!thing.name || typeof thing.name === 'string')
|
||||
&& (!thing.index || typeof thing.index === 'number');
|
||||
}
|
||||
languages?: string[];
|
||||
};
|
||||
|
||||
function isUri(thing: any): thing is vscode.Uri {
|
||||
return thing instanceof URI;
|
||||
|
@ -61,19 +56,35 @@ function isUri(thing: any): thing is vscode.Uri {
|
|||
function isResourceLanguage(thing: any): thing is { uri: URI, languageId: string } {
|
||||
return thing
|
||||
&& thing.uri instanceof URI
|
||||
&& (!thing.languageId || typeof thing.languageId === 'string');
|
||||
&& (thing.languageId && typeof thing.languageId === 'string');
|
||||
}
|
||||
|
||||
function isLanguage(thing: any): thing is { languageId: string } {
|
||||
return thing
|
||||
&& !thing.uri
|
||||
&& (thing.languageId && typeof thing.languageId === 'string');
|
||||
}
|
||||
|
||||
function isWorkspaceFolder(thing: any): thing is vscode.WorkspaceFolder {
|
||||
return thing
|
||||
&& thing.uri instanceof URI
|
||||
&& (!thing.name || typeof thing.name === 'string')
|
||||
&& (!thing.index || typeof thing.index === 'number');
|
||||
}
|
||||
|
||||
function scopeToOverrides(scope: vscode.ConfigurationScope | undefined | null): IConfigurationOverrides | undefined {
|
||||
if (isUri(scope)) {
|
||||
return { resource: scope };
|
||||
}
|
||||
if (isWorkspaceFolder(scope)) {
|
||||
return { resource: scope.uri };
|
||||
}
|
||||
if (isResourceLanguage(scope)) {
|
||||
return { resource: scope.uri, overrideIdentifier: scope.languageId };
|
||||
}
|
||||
if (isLanguage(scope)) {
|
||||
return { overrideIdentifier: scope.languageId };
|
||||
}
|
||||
if (isWorkspaceFolder(scope)) {
|
||||
return { resource: scope.uri };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -255,6 +266,8 @@ export class ExtHostConfigProvider {
|
|||
userLanguageValue: config.user?.override,
|
||||
workspaceLanguageValue: config.workspace?.override,
|
||||
workspaceFolderLanguageValue: config.workspaceFolder?.override,
|
||||
|
||||
languages: config.overrideIdentifiers
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
|
|
|
@ -337,7 +337,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
|||
}
|
||||
|
||||
// make sure that only one factory for this type is registered
|
||||
if (this.getAdapterFactoryByType(type)) {
|
||||
if (this.getAdapterDescriptorFactoryByType(type)) {
|
||||
throw new Error(`a DebugAdapterDescriptorFactory can only be registered once per a type.`);
|
||||
}
|
||||
|
||||
|
@ -412,89 +412,92 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
|||
|
||||
const session = await this.getSession(sessionDto);
|
||||
|
||||
return this.getAdapterDescriptor(this.getAdapterFactoryByType(session.type), session).then(daDescriptor => {
|
||||
return this.getAdapterDescriptor(this.getAdapterDescriptorFactoryByType(session.type), session).then(daDescriptor => {
|
||||
|
||||
const adapter = this.convertToDto(daDescriptor);
|
||||
if (!daDescriptor) {
|
||||
throw new Error(`Couldn't find a debug adapter descriptor for debug type '${session.type}' (extension might have failed to activate)`);
|
||||
}
|
||||
|
||||
const da: AbstractDebugAdapter | undefined = this.createDebugAdapter(adapter, session);
|
||||
const adapterDescriptor = this.convertToDto(daDescriptor);
|
||||
|
||||
const da = this.createDebugAdapter(adapterDescriptor, session);
|
||||
if (!da) {
|
||||
throw new Error(`Couldn't create a debug adapter for type '${session.type}'.`);
|
||||
}
|
||||
|
||||
const debugAdapter = da;
|
||||
|
||||
if (debugAdapter) {
|
||||
this._debugAdapters.set(debugAdapterHandle, debugAdapter);
|
||||
this._debugAdapters.set(debugAdapterHandle, debugAdapter);
|
||||
|
||||
return this.getDebugAdapterTrackers(session).then(tracker => {
|
||||
return this.getDebugAdapterTrackers(session).then(tracker => {
|
||||
|
||||
if (tracker) {
|
||||
this._debugAdaptersTrackers.set(debugAdapterHandle, tracker);
|
||||
}
|
||||
if (tracker) {
|
||||
this._debugAdaptersTrackers.set(debugAdapterHandle, tracker);
|
||||
}
|
||||
|
||||
debugAdapter.onMessage(async message => {
|
||||
debugAdapter.onMessage(async message => {
|
||||
|
||||
if (message.type === 'request' && (<DebugProtocol.Request>message).command === 'handshake') {
|
||||
if (message.type === 'request' && (<DebugProtocol.Request>message).command === 'handshake') {
|
||||
|
||||
const request = <DebugProtocol.Request>message;
|
||||
const request = <DebugProtocol.Request>message;
|
||||
|
||||
const response: DebugProtocol.Response = {
|
||||
type: 'response',
|
||||
seq: 0,
|
||||
command: request.command,
|
||||
request_seq: request.seq,
|
||||
success: true
|
||||
};
|
||||
const response: DebugProtocol.Response = {
|
||||
type: 'response',
|
||||
seq: 0,
|
||||
command: request.command,
|
||||
request_seq: request.seq,
|
||||
success: true
|
||||
};
|
||||
|
||||
if (!this._signService) {
|
||||
this._signService = this.createSignService();
|
||||
}
|
||||
if (!this._signService) {
|
||||
this._signService = this.createSignService();
|
||||
}
|
||||
|
||||
try {
|
||||
if (this._signService) {
|
||||
const signature = await this._signService.sign(request.arguments.value);
|
||||
response.body = {
|
||||
signature: signature
|
||||
};
|
||||
debugAdapter.sendResponse(response);
|
||||
} else {
|
||||
throw new Error('no signer');
|
||||
}
|
||||
} catch (e) {
|
||||
response.success = false;
|
||||
response.message = e.message;
|
||||
try {
|
||||
if (this._signService) {
|
||||
const signature = await this._signService.sign(request.arguments.value);
|
||||
response.body = {
|
||||
signature: signature
|
||||
};
|
||||
debugAdapter.sendResponse(response);
|
||||
} else {
|
||||
throw new Error('no signer');
|
||||
}
|
||||
} else {
|
||||
if (tracker && tracker.onDidSendMessage) {
|
||||
tracker.onDidSendMessage(message);
|
||||
}
|
||||
|
||||
// DA -> VS Code
|
||||
message = convertToVSCPaths(message, true);
|
||||
|
||||
mythis._debugServiceProxy.$acceptDAMessage(debugAdapterHandle, message);
|
||||
} catch (e) {
|
||||
response.success = false;
|
||||
response.message = e.message;
|
||||
debugAdapter.sendResponse(response);
|
||||
}
|
||||
});
|
||||
debugAdapter.onError(err => {
|
||||
if (tracker && tracker.onError) {
|
||||
tracker.onError(err);
|
||||
} else {
|
||||
if (tracker && tracker.onDidSendMessage) {
|
||||
tracker.onDidSendMessage(message);
|
||||
}
|
||||
this._debugServiceProxy.$acceptDAError(debugAdapterHandle, err.name, err.message, err.stack);
|
||||
});
|
||||
debugAdapter.onExit((code: number | null) => {
|
||||
if (tracker && tracker.onExit) {
|
||||
tracker.onExit(withNullAsUndefined(code), undefined);
|
||||
}
|
||||
this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, withNullAsUndefined(code), undefined);
|
||||
});
|
||||
|
||||
if (tracker && tracker.onWillStartSession) {
|
||||
tracker.onWillStartSession();
|
||||
// DA -> VS Code
|
||||
message = convertToVSCPaths(message, true);
|
||||
|
||||
mythis._debugServiceProxy.$acceptDAMessage(debugAdapterHandle, message);
|
||||
}
|
||||
|
||||
return debugAdapter.startSession();
|
||||
});
|
||||
debugAdapter.onError(err => {
|
||||
if (tracker && tracker.onError) {
|
||||
tracker.onError(err);
|
||||
}
|
||||
this._debugServiceProxy.$acceptDAError(debugAdapterHandle, err.name, err.message, err.stack);
|
||||
});
|
||||
debugAdapter.onExit((code: number | null) => {
|
||||
if (tracker && tracker.onExit) {
|
||||
tracker.onExit(withNullAsUndefined(code), undefined);
|
||||
}
|
||||
this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, withNullAsUndefined(code), undefined);
|
||||
});
|
||||
|
||||
}
|
||||
return undefined;
|
||||
if (tracker && tracker.onWillStartSession) {
|
||||
tracker.onWillStartSession();
|
||||
}
|
||||
|
||||
return debugAdapter.startSession();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -663,13 +666,18 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
|||
});
|
||||
}
|
||||
|
||||
public async $provideDebugAdapter(adapterProviderHandle: number, sessionDto: IDebugSessionDto): Promise<IAdapterDescriptor> {
|
||||
const adapterProvider = this.getAdapterProviderByHandle(adapterProviderHandle);
|
||||
if (!adapterProvider) {
|
||||
return Promise.reject(new Error('no handler found'));
|
||||
public async $provideDebugAdapter(adapterFactoryHandle: number, sessionDto: IDebugSessionDto): Promise<IAdapterDescriptor> {
|
||||
const adapterDescriptorFactory = this.getAdapterDescriptorFactoryByHandle(adapterFactoryHandle);
|
||||
if (!adapterDescriptorFactory) {
|
||||
return Promise.reject(new Error('no adapter descriptor factory found for handle'));
|
||||
}
|
||||
const session = await this.getSession(sessionDto);
|
||||
return this.getAdapterDescriptor(adapterProvider, session).then(x => this.convertToDto(x));
|
||||
return this.getAdapterDescriptor(adapterDescriptorFactory, session).then(adapterDescriptor => {
|
||||
if (!adapterDescriptor) {
|
||||
throw new Error(`Couldn't find a debug adapter descriptor for debug type '${session.type}'`);
|
||||
}
|
||||
return this.convertToDto(adapterDescriptor);
|
||||
});
|
||||
}
|
||||
|
||||
public async $acceptDebugSessionStarted(sessionDto: IDebugSessionDto): Promise<void> {
|
||||
|
@ -709,7 +717,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
|||
|
||||
// private & dto helpers
|
||||
|
||||
private convertToDto(x: vscode.DebugAdapterDescriptor | undefined): IAdapterDescriptor {
|
||||
private convertToDto(x: vscode.DebugAdapterDescriptor): IAdapterDescriptor {
|
||||
|
||||
if (x instanceof DebugAdapterExecutable) {
|
||||
return <IDebugAdapterExecutable>{
|
||||
|
@ -734,7 +742,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
|||
}
|
||||
}
|
||||
|
||||
private getAdapterFactoryByType(type: string): vscode.DebugAdapterDescriptorFactory | undefined {
|
||||
private getAdapterDescriptorFactoryByType(type: string): vscode.DebugAdapterDescriptorFactory | undefined {
|
||||
const results = this._adapterFactories.filter(p => p.type === type);
|
||||
if (results.length > 0) {
|
||||
return results[0].factory;
|
||||
|
@ -742,7 +750,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
|||
return undefined;
|
||||
}
|
||||
|
||||
private getAdapterProviderByHandle(handle: number): vscode.DebugAdapterDescriptorFactory | undefined {
|
||||
private getAdapterDescriptorFactoryByHandle(handle: number): vscode.DebugAdapterDescriptorFactory | undefined {
|
||||
const results = this._adapterFactories.filter(p => p.handle === handle);
|
||||
if (results.length > 0) {
|
||||
return results[0].factory;
|
||||
|
@ -804,7 +812,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
|||
});
|
||||
}
|
||||
|
||||
private async getAdapterDescriptor(adapterProvider: vscode.DebugAdapterDescriptorFactory | undefined, session: ExtHostDebugSession): Promise<vscode.DebugAdapterDescriptor | undefined> {
|
||||
private async getAdapterDescriptor(adapterDescriptorFactory: vscode.DebugAdapterDescriptorFactory | undefined, session: ExtHostDebugSession): Promise<vscode.DebugAdapterDescriptor | undefined> {
|
||||
|
||||
// a "debugServer" attribute in the launch config takes precedence
|
||||
const serverPort = session.configuration.debugServer;
|
||||
|
@ -824,9 +832,9 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
|||
});
|
||||
}
|
||||
|
||||
if (adapterProvider) {
|
||||
if (adapterDescriptorFactory) {
|
||||
const extensionRegistry = await this._extensionService.getExtensionRegistry();
|
||||
return asPromise(() => adapterProvider.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry))).then(daDescriptor => {
|
||||
return asPromise(() => adapterDescriptorFactory.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry))).then(daDescriptor => {
|
||||
if (daDescriptor) {
|
||||
return daDescriptor;
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
|||
const cwd = fs.readlinkSync(resources.joinPath(childUri, 'cwd').fsPath);
|
||||
const rawCmd = fs.readFileSync(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8');
|
||||
const nullIndex = rawCmd.indexOf('\0');
|
||||
const cmd = rawCmd.substr(0, nullIndex > 0 ? nullIndex : rawCmd.length);
|
||||
const cmd = rawCmd.substr(0, nullIndex > 0 ? nullIndex : rawCmd.length).trim();
|
||||
processes.push({ pid, cwd, cmd });
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -183,7 +183,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
|||
ip: this.parseIpAddress(address[0]),
|
||||
port: parseInt(address[1], 16)
|
||||
};
|
||||
}).map(port => [port.port, port])
|
||||
}).map(port => [port.ip + ':' + port.port, port])
|
||||
).values()
|
||||
];
|
||||
}
|
||||
|
|
|
@ -357,6 +357,8 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
|||
|
||||
// Pass Focus to Viewer
|
||||
this.tree.domFocus();
|
||||
} else if (this.tree) {
|
||||
this.tree.domFocus();
|
||||
} else {
|
||||
this.domNode.focus();
|
||||
}
|
||||
|
|
|
@ -316,7 +316,6 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
|||
}
|
||||
|
||||
create(parent: HTMLElement): void {
|
||||
// super.create(parent);
|
||||
this.paneview = this._register(new PaneView(parent, this.options));
|
||||
this._register(this.paneview.onDidDrop(({ from, to }) => this.movePane(from as ViewPane, to as ViewPane)));
|
||||
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
|
||||
|
|
|
@ -67,13 +67,13 @@ export class BackupModelTracker extends Disposable implements IWorkbenchContribu
|
|||
|
||||
private onUntitledModelCreated(resource: Uri): void {
|
||||
if (this.untitledTextEditorService.isDirty(resource)) {
|
||||
this.untitledTextEditorService.loadOrCreate({ resource }).then(model => model.backup());
|
||||
this.untitledTextEditorService.createOrGet({ resource }).resolve().then(model => model.backup());
|
||||
}
|
||||
}
|
||||
|
||||
private onUntitledModelChanged(resource: Uri): void {
|
||||
if (this.untitledTextEditorService.isDirty(resource)) {
|
||||
this.untitledTextEditorService.loadOrCreate({ resource }).then(model => model.backup());
|
||||
this.untitledTextEditorService.createOrGet({ resource }).resolve().then(model => model.backup());
|
||||
} else {
|
||||
this.discardBackup(resource);
|
||||
}
|
||||
|
|
|
@ -40,13 +40,7 @@ const codeActionsOnSaveSchema: IConfigurationPropertySchema = {
|
|||
export const editorConfiguration = Object.freeze<IConfigurationNode>({
|
||||
...editorConfigurationBaseNode,
|
||||
properties: {
|
||||
'editor.codeActionsOnSave': codeActionsOnSaveSchema,
|
||||
'editor.codeActionsOnSaveTimeout': {
|
||||
type: 'number',
|
||||
default: 750,
|
||||
description: nls.localize('codeActionsOnSaveTimeout', "Timeout in milliseconds after which the code actions that are run on save are cancelled."),
|
||||
scope: ConfigurationScope.RESOURCE_LANGUAGE,
|
||||
},
|
||||
'editor.codeActionsOnSave': codeActionsOnSaveSchema
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -11,13 +11,12 @@ import severity from 'vs/base/common/severity';
|
|||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IContentWidget, IActiveCodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, ITextModel, OverviewRulerLane, IModelDecorationOverviewRulerOptions } from 'vs/editor/common/model';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { RemoveBreakpointAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, BreakpointWidgetContext, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, IBreakpointUpdateData, IDebugConfiguration, State, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, BreakpointWidgetContext, IBreakpointEditorContribution, IBreakpointUpdateData, IDebugConfiguration, State, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
|
@ -145,7 +144,7 @@ async function createCandidateDecorations(model: ITextModel, breakpointDecoratio
|
|||
return result;
|
||||
}
|
||||
|
||||
class BreakpointEditorContribution implements IBreakpointEditorContribution {
|
||||
export class BreakpointEditorContribution implements IBreakpointEditorContribution {
|
||||
|
||||
private breakpointHintDecoration: string[] = [];
|
||||
private breakpointWidget: BreakpointWidget | undefined;
|
||||
|
@ -692,5 +691,3 @@ const debugIconBreakpointDisabledForeground = registerColor('debugIcon.breakpoin
|
|||
const debugIconBreakpointUnverifiedForeground = registerColor('debugIcon.breakpointUnverifiedForeground', { dark: '#848484', light: '#848484', hc: '#848484' }, nls.localize('debugIcon.breakpointUnverifiedForeground', 'Icon color for unverified breakpoints.'));
|
||||
const debugIconBreakpointCurrentStackframeForeground = registerColor('debugIcon.breakpointCurrentStackframeForeground', { dark: '#FFCC00', light: '#FFCC00', hc: '#FFCC00' }, nls.localize('debugIcon.breakpointCurrentStackframeForeground', 'Icon color for the current breakpoint stack frame.'));
|
||||
const debugIconBreakpointStackframeForeground = registerColor('debugIcon.breakpointStackframeForeground', { dark: '#89D185', light: '#89D185', hc: '#89D185' }, nls.localize('debugIcon.breakpointStackframeForeground', 'Icon color for all breakpoint stack frames.'));
|
||||
|
||||
registerEditorContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID, BreakpointEditorContribution);
|
||||
|
|
|
@ -13,7 +13,6 @@ import { localize } from 'vs/nls';
|
|||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
|
||||
const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
|
||||
|
||||
|
@ -82,7 +81,7 @@ export function createDecorationsForStackFrame(stackFrame: IStackFrame, topStack
|
|||
return result;
|
||||
}
|
||||
|
||||
class CallStackEditorContribution implements IEditorContribution {
|
||||
export class CallStackEditorContribution implements IEditorContribution {
|
||||
private toDispose: IDisposable[] = [];
|
||||
private decorationIds: string[] = [];
|
||||
private topStackFrameRange: Range | undefined;
|
||||
|
@ -147,5 +146,3 @@ registerThemingParticipant((theme, collector) => {
|
|||
|
||||
const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#fff600' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.'));
|
||||
const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#cee7ce' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.'));
|
||||
|
||||
registerEditorContribution('editor.contrib.callStack', CallStackEditorContribution);
|
||||
|
|
|
@ -308,7 +308,13 @@ export class CallStackView extends ViewPane {
|
|||
this.ignoreSelectionChangedEvent = true;
|
||||
try {
|
||||
this.tree.setSelection([element]);
|
||||
this.tree.reveal(element);
|
||||
// If the element is outside of the screen bounds,
|
||||
// position it in the middle
|
||||
if (this.tree.getRelativeTop(element) === null) {
|
||||
this.tree.reveal(element, 0.5);
|
||||
} else {
|
||||
this.tree.reveal(element);
|
||||
}
|
||||
} catch (e) { }
|
||||
finally {
|
||||
this.ignoreSelectionChangedEvent = false;
|
||||
|
|
|
@ -20,7 +20,7 @@ import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'
|
|||
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import {
|
||||
IDebugService, VIEWLET_ID, REPL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA,
|
||||
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX,
|
||||
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID,
|
||||
} from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
|
@ -49,6 +49,9 @@ import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugCon
|
|||
import { StartView } from 'vs/workbench/contrib/debug/browser/startView';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { DebugViewPaneContainer } from 'vs/workbench/contrib/debug/browser/debugViewlet';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { CallStackEditorContribution } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution';
|
||||
import { BreakpointEditorContribution } from 'vs/workbench/contrib/debug/browser/breakpointEditorContribution';
|
||||
|
||||
class OpenDebugViewletAction extends ShowViewletAction {
|
||||
public static readonly ID = VIEWLET_ID;
|
||||
|
@ -336,6 +339,11 @@ registerDebugCallstackItem(TERMINATE_THREAD_ID, nls.localize('terminateThread',
|
|||
registerDebugCallstackItem(RESTART_FRAME_ID, nls.localize('restartFrame', "Restart Frame"), 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'), CONTEXT_RESTART_FRAME_SUPPORTED));
|
||||
registerDebugCallstackItem(COPY_STACK_TRACE_ID, nls.localize('copyStackTrace', "Copy Call Stack"), 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'));
|
||||
|
||||
// Editor contributions
|
||||
|
||||
registerEditorContribution('editor.contrib.callStack', CallStackEditorContribution);
|
||||
registerEditorContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID, BreakpointEditorContribution);
|
||||
|
||||
// View menu
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
|
||||
|
|
|
@ -161,9 +161,6 @@ export class StartDebugActionViewItem implements IActionViewItem {
|
|||
let lastGroup: string | undefined;
|
||||
const disabledIdxs: number[] = [];
|
||||
manager.getAllConfigurations().forEach(({ launch, name, presentation }) => {
|
||||
if (name === manager.selectedConfiguration.name && launch === manager.selectedConfiguration.launch) {
|
||||
this.selected = this.options.length;
|
||||
}
|
||||
if (lastGroup !== presentation?.group) {
|
||||
lastGroup = presentation?.group;
|
||||
if (this.options.length) {
|
||||
|
@ -171,6 +168,9 @@ export class StartDebugActionViewItem implements IActionViewItem {
|
|||
disabledIdxs.push(this.options.length - 1);
|
||||
}
|
||||
}
|
||||
if (name === manager.selectedConfiguration.name && launch === manager.selectedConfiguration.launch) {
|
||||
this.selected = this.options.length;
|
||||
}
|
||||
|
||||
const label = inWorkspace ? `${name} (${launch.name})` : name;
|
||||
this.options.push({ label, handler: () => { manager.selectConfiguration(launch, name); return true; } });
|
||||
|
|
|
@ -36,7 +36,7 @@ import { getHover } from 'vs/editor/contrib/hover/getHover';
|
|||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
const HOVER_DELAY = 300;
|
||||
const LAUNCH_JSON_REGEX = /launch\.json$/;
|
||||
const LAUNCH_JSON_REGEX = /\.vscode\/launch\.json$/;
|
||||
const INLINE_VALUE_DECORATION_KEY = 'inlinevaluedecoration';
|
||||
const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We want to limit ourselves for perf reasons
|
||||
const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added
|
||||
|
|
|
@ -33,6 +33,8 @@ import { dispose } from 'vs/base/common/lifecycle';
|
|||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
|
||||
const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
|
||||
let ignoreVariableSetEmitter = false;
|
||||
let useCachedEvaluation = false;
|
||||
|
||||
export class WatchExpressionsView extends ViewPane {
|
||||
|
||||
|
@ -90,7 +92,12 @@ export class WatchExpressionsView extends ViewPane {
|
|||
if (!this.isBodyVisible()) {
|
||||
this.needsRefresh = true;
|
||||
} else {
|
||||
if (we && !we.name) {
|
||||
// We are adding a new input box, no need to re-evaluate watch expressions
|
||||
useCachedEvaluation = true;
|
||||
}
|
||||
await this.tree.updateChildren();
|
||||
useCachedEvaluation = false;
|
||||
if (we instanceof Expression) {
|
||||
this.tree.reveal(we);
|
||||
}
|
||||
|
@ -106,7 +113,11 @@ export class WatchExpressionsView extends ViewPane {
|
|||
this.onWatchExpressionsUpdatedScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
this._register(variableSetEmitter.event(() => this.tree.updateChildren()));
|
||||
this._register(variableSetEmitter.event(() => {
|
||||
if (!ignoreVariableSetEmitter) {
|
||||
this.tree.updateChildren();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this.onDidChangeBodyVisibility(visible => {
|
||||
if (visible && this.needsRefresh) {
|
||||
|
@ -221,7 +232,7 @@ class WatchExpressionsDataSource implements IAsyncDataSource<IDebugService, IExp
|
|||
const debugService = element as IDebugService;
|
||||
const watchExpressions = debugService.getModel().getWatchExpressions();
|
||||
const viewModel = debugService.getViewModel();
|
||||
return Promise.all(watchExpressions.map(we => !!we.name
|
||||
return Promise.all(watchExpressions.map(we => !!we.name && !useCachedEvaluation
|
||||
? we.evaluate(viewModel.focusedSession!, viewModel.focusedStackFrame!, 'watch').then(() => we)
|
||||
: Promise.resolve(we)));
|
||||
}
|
||||
|
@ -259,7 +270,9 @@ export class WatchExpressionsRenderer extends AbstractExpressionsRenderer {
|
|||
onFinish: (value: string, success: boolean) => {
|
||||
if (success && value) {
|
||||
this.debugService.renameWatchExpression(expression.getId(), value);
|
||||
ignoreVariableSetEmitter = true;
|
||||
variableSetEmitter.fire();
|
||||
ignoreVariableSetEmitter = false;
|
||||
} else if (!expression.name) {
|
||||
this.debugService.removeWatchExpressions(expression.getId());
|
||||
}
|
||||
|
|
|
@ -3,29 +3,19 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
import { AdapterEndEvent, IBreakpoint, IBreakpointData, IBreakpointsChangeEvent, IBreakpointUpdateData, IConfig, IConfigurationManager, IDataBreakpoint, IDebugger, IDebugModel, IDebugService, IDebugSession, IDebugSessionOptions, IEvaluate, IExceptionBreakpoint, IExceptionInfo, IExpression, IFunctionBreakpoint, ILaunch, IRawModelUpdate, IReplElement, IReplElementSource, IStackFrame, IThread, IViewModel, LoadedSourceEvent, State } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
|
||||
const noopEvent = new Emitter<any>().event;
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
|
||||
export class MockDebugService implements IDebugService {
|
||||
|
||||
public _serviceBrand: undefined;
|
||||
|
||||
private readonly _model: IDebugModel;
|
||||
private readonly _viewModel: IViewModel;
|
||||
|
||||
constructor() {
|
||||
this._model = new MockDebugModel();
|
||||
this._viewModel = new MockDebugViewModel();
|
||||
}
|
||||
|
||||
public get state(): State {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
@ -42,7 +32,9 @@ export class MockDebugService implements IDebugService {
|
|||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public onDidChangeState: Event<State> = noopEvent;
|
||||
public get onDidChangeState(): Event<State> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public getConfigurationManager(): IConfigurationManager {
|
||||
throw new Error('not implemented');
|
||||
|
@ -124,11 +116,11 @@ export class MockDebugService implements IDebugService {
|
|||
}
|
||||
|
||||
public getModel(): IDebugModel {
|
||||
return this._model;
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public getViewModel(): IViewModel {
|
||||
return this._viewModel;
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public logToRepl(session: IDebugSession, value: string): void { }
|
||||
|
@ -536,97 +528,3 @@ export class MockDebugAdapter extends AbstractDebugAdapter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MockDebugModel implements IDebugModel {
|
||||
onDidChangeBreakpoints: Event<IBreakpointsChangeEvent | undefined> = noopEvent;
|
||||
|
||||
get onDidChangeCallStack(): Event<void> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
get onDidChangeWatchExpressions(): Event<IExpression | undefined> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
getSession(sessionId: string | undefined, includeInactive?: boolean | undefined): IDebugSession | undefined {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
getSessions(includeInactive?: boolean | undefined): IDebugSession[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getBreakpoints(filter?: { uri?: uri | undefined; lineNumber?: number | undefined; column?: number | undefined; enabledOnly?: boolean | undefined; } | undefined): readonly IBreakpoint[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
areBreakpointsActivated(): boolean {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
getFunctionBreakpoints(): readonly IFunctionBreakpoint[] {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
getDataBreakpoints(): readonly IDataBreakpoint[] {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
getExceptionBreakpoints(): readonly IExceptionBreakpoint[] {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
getWatchExpressions(): readonly (IExpression & IEvaluate)[] {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
class MockDebugViewModel implements IViewModel {
|
||||
get focusedSession(): IDebugSession | undefined {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
get focusedThread(): IThread | undefined {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
focusedStackFrame: IStackFrame | undefined = undefined;
|
||||
|
||||
get onDidFocusSession(): Event<IDebugSession | undefined> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
onDidFocusStackFrame: Event<{ stackFrame: IStackFrame | undefined; explicit: boolean; }> = noopEvent;
|
||||
|
||||
get onDidSelectExpression(): Event<IExpression | undefined> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
getSelectedExpression(): IExpression | undefined {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getSelectedFunctionBreakpoint(): IFunctionBreakpoint | undefined {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
setSelectedExpression(expression: IExpression | undefined): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
isMultiSessionView(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
.extension-editor {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.extension-editor .clickable {
|
||||
|
@ -188,7 +190,7 @@
|
|||
}
|
||||
|
||||
.extension-editor > .body {
|
||||
height: calc(100% - 168px);
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,17 +21,16 @@ import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
|||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { timeout, RunOnceWorker } from 'vs/base/common/async';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { isEqualOrParent, joinPath } from 'vs/base/common/resources';
|
||||
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
|
||||
export class FileEditorTracker extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
private readonly activeOutOfWorkspaceWatchers = new ResourceMap<IDisposable>();
|
||||
|
||||
private closeOnFileDelete: boolean = false;
|
||||
|
||||
constructor(
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
|
@ -43,6 +42,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
|
|||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
|
||||
@IUntitledTextEditorService private readonly untitledTextEditorService: IUntitledTextEditorService
|
||||
) {
|
||||
super();
|
||||
|
||||
|
@ -59,9 +59,10 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
|
|||
// Update editors from disk changes
|
||||
this._register(this.fileService.onFileChanges(e => this.onFileChanges(e)));
|
||||
|
||||
// Ensure dirty text file models are always opened as editors
|
||||
// Ensure dirty text file and untitled models are always opened as editors
|
||||
this._register(this.textFileService.models.onModelsDirty(e => this.ensureDirtyTextFilesAreOpened(e)));
|
||||
this._register(this.textFileService.models.onModelsSaveError(e => this.ensureDirtyTextFilesAreOpened(e)));
|
||||
this._register(this.untitledTextEditorService.onDidChangeDirty(e => this.ensureDirtyUntitledTextFilesAreOpenedWorker.work(e)));
|
||||
|
||||
// Out of workspace file watchers
|
||||
this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange()));
|
||||
|
@ -175,7 +176,17 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region File Changes: Close editors of deleted files
|
||||
//#region File Changes: Close editors of deleted files unless configured otherwise
|
||||
|
||||
private closeOnFileDelete: boolean = false;
|
||||
|
||||
private onConfigurationUpdated(configuration: IWorkbenchEditorConfiguration): void {
|
||||
if (typeof configuration.workbench?.editor?.closeOnFileDelete === 'boolean') {
|
||||
this.closeOnFileDelete = configuration.workbench.editor.closeOnFileDelete;
|
||||
} else {
|
||||
this.closeOnFileDelete = false; // default
|
||||
}
|
||||
}
|
||||
|
||||
private onFileChanges(e: FileChangesEvent): void {
|
||||
if (e.gotDeleted()) {
|
||||
|
@ -264,16 +275,33 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Text File: Ensure every dirty text file is opened in an editor
|
||||
//#region Text File: Ensure every dirty text and untitled file is opened in an editor
|
||||
|
||||
private readonly ensureDirtyUntitledTextFilesAreOpenedWorker = this._register(new RunOnceWorker<URI>(units => this.ensureDirtyUntitledTextFilesAreOpened(units), 250));
|
||||
|
||||
private ensureDirtyTextFilesAreOpened(events: ReadonlyArray<TextFileModelChangeEvent>): void {
|
||||
this.editorService.openEditors(distinct(events.filter(({ resource }) => {
|
||||
this.doEnsureDirtyFilesAreOpened(distinct(events.filter(({ resource }) => {
|
||||
const model = this.textFileService.models.get(resource);
|
||||
|
||||
return model?.hasState(ModelState.DIRTY) && // model must be dirty
|
||||
!model.hasState(ModelState.PENDING_SAVE) && // model should not be saving currently
|
||||
!this.editorService.isOpen({ resource }); // model is not currently opened as editor
|
||||
}).map(event => event.resource), resource => resource.toString()).map(resource => ({
|
||||
}).map(event => event.resource), resource => resource.toString()));
|
||||
}
|
||||
|
||||
private ensureDirtyUntitledTextFilesAreOpened(resources: URI[]): void {
|
||||
this.doEnsureDirtyFilesAreOpened(distinct(resources.filter(resource => {
|
||||
return this.untitledTextEditorService.isDirty(resource) && // untitled must be dirty
|
||||
!this.editorService.isOpen({ resource }); // untitled is not currently opened as editor
|
||||
}), resource => resource.toString()));
|
||||
}
|
||||
|
||||
private doEnsureDirtyFilesAreOpened(resources: URI[]): void {
|
||||
if (!resources.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.editorService.openEditors(resources.map(resource => ({
|
||||
resource,
|
||||
options: { inactive: true, pinned: true, preserveFocus: true }
|
||||
})));
|
||||
|
@ -352,18 +380,6 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Configuration Change
|
||||
|
||||
private onConfigurationUpdated(configuration: IWorkbenchEditorConfiguration): void {
|
||||
if (typeof configuration.workbench?.editor?.closeOnFileDelete === 'boolean') {
|
||||
this.closeOnFileDelete = configuration.workbench.editor.closeOnFileDelete;
|
||||
} else {
|
||||
this.closeOnFileDelete = false; // default
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
|
|
|
@ -1099,6 +1099,7 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => {
|
|||
if (pasteShouldMove) {
|
||||
// Cut is done. Make sure to clear cut state.
|
||||
explorerService.setToCopy([], false);
|
||||
pasteShouldMove = false;
|
||||
}
|
||||
if (stats.length >= 1) {
|
||||
const stat = stats[0];
|
||||
|
|
|
@ -344,12 +344,6 @@ configurationRegistry.registerConfiguration({
|
|||
'default': false,
|
||||
'description': nls.localize('formatOnSave', "Format a file on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down."),
|
||||
scope: ConfigurationScope.RESOURCE_LANGUAGE,
|
||||
},
|
||||
'editor.formatOnSaveTimeout': {
|
||||
'type': 'number',
|
||||
'default': 750,
|
||||
'description': nls.localize('formatOnSaveTimeout', "Timeout in milliseconds after which the formatting that is run on file save is cancelled."),
|
||||
scope: ConfigurationScope.RESOURCE_LANGUAGE,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,37 +4,62 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { FileEditorTracker } from 'vs/workbench/contrib/files/browser/editors/fileEditorTracker';
|
||||
import { toResource } from 'vs/base/test/common/utils';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { workbenchInstantiationService, TestTextFileService, TestFileService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITextFileService, IResolvedTextFileEditorModel, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { TextFileEditor } from 'vs/workbench/contrib/files/browser/editors/textFileEditor';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { EditorInput } from 'vs/workbench/common/editor';
|
||||
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
|
||||
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
|
||||
import { EditorService } from 'vs/workbench/services/editor/browser/editorService';
|
||||
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
|
||||
class ServiceAccessor {
|
||||
constructor(
|
||||
@IEditorService public editorService: IEditorService,
|
||||
@IEditorGroupsService public editorGroupService: IEditorGroupsService,
|
||||
@ITextFileService public textFileService: TestTextFileService,
|
||||
@IFileService public fileService: TestFileService
|
||||
@IFileService public fileService: TestFileService,
|
||||
@IUntitledTextEditorService public untitledTextEditorService: IUntitledTextEditorService
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
suite('Files - FileEditorTracker', () => {
|
||||
|
||||
let instantiationService: IInstantiationService;
|
||||
let accessor: ServiceAccessor;
|
||||
let disposables: IDisposable[] = [];
|
||||
|
||||
setup(() => {
|
||||
instantiationService = workbenchInstantiationService();
|
||||
accessor = instantiationService.createInstance(ServiceAccessor);
|
||||
disposables.push(Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
EditorDescriptor.create(
|
||||
TextFileEditor,
|
||||
TextFileEditor.ID,
|
||||
'Text File Editor'
|
||||
),
|
||||
[new SyncDescriptor<EditorInput>(FileEditorInput)]
|
||||
));
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
dispose(disposables);
|
||||
disposables = [];
|
||||
});
|
||||
|
||||
test('file change event updates model', async function () {
|
||||
const instantiationService = workbenchInstantiationService();
|
||||
const accessor = instantiationService.createInstance(ServiceAccessor);
|
||||
|
||||
const tracker = instantiationService.createInstance(FileEditorTracker);
|
||||
|
||||
const resource = toResource.call(this, '/path/index.txt');
|
||||
|
@ -54,5 +79,77 @@ suite('Files - FileEditorTracker', () => {
|
|||
assert.equal(snapshotToString(model.createSnapshot()!), 'Hello Html');
|
||||
|
||||
tracker.dispose();
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).clear();
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).dispose();
|
||||
});
|
||||
|
||||
test('dirty text file model opens as editor', async function () {
|
||||
const instantiationService = workbenchInstantiationService();
|
||||
|
||||
const part = instantiationService.createInstance(EditorPart);
|
||||
part.create(document.createElement('div'));
|
||||
part.layout(400, 300);
|
||||
|
||||
instantiationService.stub(IEditorGroupsService, part);
|
||||
|
||||
const editorService: EditorService = instantiationService.createInstance(EditorService);
|
||||
instantiationService.stub(IEditorService, editorService);
|
||||
|
||||
const accessor = instantiationService.createInstance(ServiceAccessor);
|
||||
|
||||
const tracker = instantiationService.createInstance(FileEditorTracker);
|
||||
|
||||
const resource = toResource.call(this, '/path/index.txt');
|
||||
|
||||
assert.ok(!editorService.isOpen({ resource }));
|
||||
|
||||
const model = await accessor.textFileService.models.loadOrCreate(resource) as IResolvedTextFileEditorModel;
|
||||
|
||||
model.textEditorModel.setValue('Super Good');
|
||||
|
||||
await awaitEditorOpening(editorService);
|
||||
assert.ok(editorService.isOpen({ resource }));
|
||||
|
||||
part.dispose();
|
||||
tracker.dispose();
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).clear();
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).dispose();
|
||||
});
|
||||
|
||||
test('dirty untitled text file model opens as editor', async function () {
|
||||
const instantiationService = workbenchInstantiationService();
|
||||
|
||||
const part = instantiationService.createInstance(EditorPart);
|
||||
part.create(document.createElement('div'));
|
||||
part.layout(400, 300);
|
||||
|
||||
instantiationService.stub(IEditorGroupsService, part);
|
||||
|
||||
const editorService: EditorService = instantiationService.createInstance(EditorService);
|
||||
instantiationService.stub(IEditorService, editorService);
|
||||
|
||||
const accessor = instantiationService.createInstance(ServiceAccessor);
|
||||
|
||||
const tracker = instantiationService.createInstance(FileEditorTracker);
|
||||
|
||||
const untitledEditor = accessor.untitledTextEditorService.createOrGet();
|
||||
const model = await untitledEditor.resolve();
|
||||
|
||||
assert.ok(!editorService.isOpen(untitledEditor));
|
||||
|
||||
model.textEditorModel.setValue('Super Good');
|
||||
|
||||
await awaitEditorOpening(editorService);
|
||||
assert.ok(editorService.isOpen(untitledEditor));
|
||||
|
||||
part.dispose();
|
||||
tracker.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
function awaitEditorOpening(editorService: IEditorService): Promise<void> {
|
||||
return new Promise(c => {
|
||||
Event.once(editorService.onDidActiveEditorChange)(c);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -18,7 +18,7 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
|||
import { ICommandService, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
|
||||
import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent, ITreeMouseEvent } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { Disposable, IDisposable, toDisposable, MutableDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ActionBar, ActionViewItem, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
|
@ -56,7 +56,7 @@ export interface ITunnelViewModel {
|
|||
readonly forwarded: TunnelItem[];
|
||||
readonly detected: TunnelItem[];
|
||||
readonly candidates: Promise<TunnelItem[]>;
|
||||
readonly input: ITunnelItem | ITunnelGroup | undefined;
|
||||
readonly input: TunnelItem;
|
||||
groups(): Promise<ITunnelGroup[]>;
|
||||
}
|
||||
|
||||
|
@ -64,16 +64,23 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel {
|
|||
private _onForwardedPortsChanged: Emitter<void> = new Emitter();
|
||||
public onForwardedPortsChanged: Event<void> = this._onForwardedPortsChanged.event;
|
||||
private model: TunnelModel;
|
||||
private _input: ITunnelItem | ITunnelGroup | undefined;
|
||||
private _input: TunnelItem;
|
||||
|
||||
constructor(
|
||||
@IRemoteExplorerService remoteExplorerService: IRemoteExplorerService) {
|
||||
@IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService) {
|
||||
super();
|
||||
this.model = remoteExplorerService.tunnelModel;
|
||||
this._register(this.model.onForwardPort(() => this._onForwardedPortsChanged.fire()));
|
||||
this._register(this.model.onClosePort(() => this._onForwardedPortsChanged.fire()));
|
||||
this._register(this.model.onPortName(() => this._onForwardedPortsChanged.fire()));
|
||||
this._register(this.model.onCandidatesChanged(() => this._onForwardedPortsChanged.fire()));
|
||||
this._input = {
|
||||
label: nls.localize('remote.tunnelsView.add', "Forward a Port..."),
|
||||
tunnelType: TunnelType.Add,
|
||||
remoteHost: 'localhost',
|
||||
remotePort: 0,
|
||||
description: ''
|
||||
};
|
||||
}
|
||||
|
||||
async groups(): Promise<ITunnelGroup[]> {
|
||||
|
@ -100,20 +107,20 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel {
|
|||
items: candidates
|
||||
});
|
||||
}
|
||||
if (!this._input) {
|
||||
this._input = {
|
||||
label: nls.localize('remote.tunnelsView.add', "Forward a Port..."),
|
||||
tunnelType: TunnelType.Add,
|
||||
};
|
||||
if (groups.length === 0) {
|
||||
groups.push(this._input);
|
||||
}
|
||||
groups.push(this._input);
|
||||
return groups;
|
||||
}
|
||||
|
||||
get forwarded(): TunnelItem[] {
|
||||
return Array.from(this.model.forwarded.values()).map(tunnel => {
|
||||
const forwarded = Array.from(this.model.forwarded.values()).map(tunnel => {
|
||||
return new TunnelItem(TunnelType.Forwarded, tunnel.remoteHost, tunnel.remotePort, tunnel.localAddress, tunnel.closeable, tunnel.name, tunnel.description);
|
||||
});
|
||||
if (this.remoteExplorerService.getEditableData(undefined)) {
|
||||
forwarded.push(this._input);
|
||||
}
|
||||
return forwarded;
|
||||
}
|
||||
|
||||
get detected(): TunnelItem[] {
|
||||
|
@ -135,7 +142,7 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel {
|
|||
});
|
||||
}
|
||||
|
||||
get input(): ITunnelItem | ITunnelGroup | undefined {
|
||||
get input(): TunnelItem {
|
||||
return this._input;
|
||||
}
|
||||
|
||||
|
@ -357,9 +364,9 @@ class TunnelItem implements ITunnelItem {
|
|||
if (this.name) {
|
||||
return nls.localize('remote.tunnelsView.forwardedPortLabel0', "{0}", this.name);
|
||||
} else if (this.localAddress && (this.remoteHost !== 'localhost')) {
|
||||
return nls.localize('remote.tunnelsView.forwardedPortLabel2', "{0}:{1} to {2}", this.remoteHost, this.remotePort, this.localAddress);
|
||||
return nls.localize('remote.tunnelsView.forwardedPortLabel2', "{0}:{1} \u2192 {2}", this.remoteHost, this.remotePort, this.localAddress);
|
||||
} else if (this.localAddress) {
|
||||
return nls.localize('remote.tunnelsView.forwardedPortLabel3', "{0} to {1}", this.remotePort, this.localAddress);
|
||||
return nls.localize('remote.tunnelsView.forwardedPortLabel3', "{0} \u2192 {1}", this.remotePort, this.localAddress);
|
||||
} else if (this.remoteHost !== 'localhost') {
|
||||
return nls.localize('remote.tunnelsView.forwardedPortLabel4', "{0}:{1} not forwarded", this.remoteHost, this.remotePort);
|
||||
} else {
|
||||
|
@ -460,6 +467,7 @@ export class TunnelPanel extends ViewPane {
|
|||
renderer.actionRunner = actionRunner;
|
||||
|
||||
this._register(this.tree.onContextMenu(e => this.onContextMenu(e, actionRunner)));
|
||||
this._register(this.tree.onMouseDblClick(e => this.onMouseDblClick(e)));
|
||||
|
||||
this.tree.setInput(this.viewModel);
|
||||
this._register(this.viewModel.onForwardedPortsChanged(() => {
|
||||
|
@ -507,7 +515,7 @@ export class TunnelPanel extends ViewPane {
|
|||
}
|
||||
|
||||
private onContextMenu(treeEvent: ITreeContextMenuEvent<ITunnelItem | ITunnelGroup>, actionRunner: ActionRunner): void {
|
||||
if (!(treeEvent.element instanceof TunnelItem)) {
|
||||
if ((treeEvent.element !== null) && !(treeEvent.element instanceof TunnelItem)) {
|
||||
return;
|
||||
}
|
||||
const node: ITunnelItem | null = treeEvent.element;
|
||||
|
@ -516,9 +524,14 @@ export class TunnelPanel extends ViewPane {
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.tree!.setFocus([node]);
|
||||
this.tunnelTypeContext.set(node.tunnelType);
|
||||
this.tunnelCloseableContext.set(!!node.closeable);
|
||||
if (node) {
|
||||
this.tree!.setFocus([node]);
|
||||
this.tunnelTypeContext.set(node.tunnelType);
|
||||
this.tunnelCloseableContext.set(!!node.closeable);
|
||||
} else {
|
||||
this.tunnelTypeContext.set(TunnelType.Add);
|
||||
this.tunnelCloseableContext.set(false);
|
||||
}
|
||||
|
||||
const actions: IAction[] = [];
|
||||
this._register(createAndFillInContextMenuActions(this.contributedContextMenu, { shouldForwardArgs: true }, actions, this.contextMenuService));
|
||||
|
@ -543,6 +556,12 @@ export class TunnelPanel extends ViewPane {
|
|||
});
|
||||
}
|
||||
|
||||
private onMouseDblClick(e: ITreeMouseEvent<ITunnelGroup | ITunnelItem | null>): void {
|
||||
if (!e.element) {
|
||||
this.commandService.executeCommand(ForwardPortAction.INLINE_ID);
|
||||
}
|
||||
}
|
||||
|
||||
protected layoutBody(height: number, width: number): void {
|
||||
this.tree.layout(height, width);
|
||||
}
|
||||
|
@ -793,7 +812,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({
|
|||
id: ForwardPortAction.INLINE_ID,
|
||||
title: ForwardPortAction.LABEL,
|
||||
},
|
||||
when: TunnelTypeContextKey.isEqualTo(TunnelType.Candidate)
|
||||
when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Candidate), TunnelTypeContextKey.isEqualTo(TunnelType.Add))
|
||||
}));
|
||||
MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({
|
||||
group: '0_manage',
|
||||
|
|
|
@ -16,6 +16,7 @@ import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/s
|
|||
import { localize } from 'vs/nls';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { TunnelFactoryContribution } from 'vs/workbench/contrib/remote/common/tunnelFactory';
|
||||
|
||||
export const VIEWLET_ID = 'workbench.view.remote';
|
||||
|
||||
|
@ -83,3 +84,4 @@ const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegist
|
|||
workbenchContributionsRegistry.registerWorkbenchContribution(LabelContribution, LifecyclePhase.Starting);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteChannelsContribution, LifecyclePhase.Starting);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteLogOutputChannels, LifecyclePhase.Restored);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(TunnelFactoryContribution, LifecyclePhase.Ready);
|
||||
|
|
39
src/vs/workbench/contrib/remote/common/tunnelFactory.ts
Normal file
39
src/vs/workbench/contrib/remote/common/tunnelFactory.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITunnelService, TunnelOptions, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
export class TunnelFactoryContribution extends Disposable implements IWorkbenchContribution {
|
||||
constructor(
|
||||
@ITunnelService tunnelService: ITunnelService,
|
||||
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService,
|
||||
) {
|
||||
super();
|
||||
if (workbenchEnvironmentService.options && workbenchEnvironmentService.options.tunnelFactory) {
|
||||
this._register(tunnelService.setTunnelProvider({
|
||||
forwardPort: (tunnelOptions: TunnelOptions): Promise<RemoteTunnel> | undefined => {
|
||||
const tunnelPromise = workbenchEnvironmentService.options!.tunnelFactory!(tunnelOptions);
|
||||
if (!tunnelPromise) {
|
||||
return undefined;
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
tunnelPromise.then(tunnel => {
|
||||
const remoteTunnel: RemoteTunnel = {
|
||||
tunnelRemotePort: tunnel.remoteAddress.port,
|
||||
tunnelRemoteHost: tunnel.remoteAddress.host,
|
||||
localAddress: tunnel.localAddress,
|
||||
dispose: tunnel.dispose
|
||||
};
|
||||
resolve(remoteTunnel);
|
||||
});
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -214,7 +214,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
label: localize('user keybindings', "User Keybindings")
|
||||
}, {
|
||||
id: 'sync.enableUIState',
|
||||
label: localize('ui state', "UI State")
|
||||
label: localize('ui state', "UI State"),
|
||||
description: localize('ui state description', "Display Language (Only)")
|
||||
}, {
|
||||
id: 'sync.enableExtensions',
|
||||
label: localize('extensions', "Extensions")
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { FindInPageOptions, OnBeforeRequestDetails, OnHeadersReceivedDetails, Response, WebContents, WebviewTag } from 'electron';
|
||||
import { FindInPageOptions, OnBeforeRequestListenerDetails, OnHeadersReceivedListenerDetails, Response, WebContents, WebviewTag } from 'electron';
|
||||
import { addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
|
@ -65,8 +65,8 @@ class WebviewTagHandle extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
type OnBeforeRequestDelegate = (details: OnBeforeRequestDetails) => Promise<Response | undefined>;
|
||||
type OnHeadersReceivedDelegate = (details: OnHeadersReceivedDetails) => { cancel: boolean; } | undefined;
|
||||
type OnBeforeRequestDelegate = (details: OnBeforeRequestListenerDetails) => Promise<Response | undefined>;
|
||||
type OnHeadersReceivedDelegate = (details: OnHeadersReceivedListenerDetails) => { cancel: boolean; } | undefined;
|
||||
|
||||
class WebviewSession extends Disposable {
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import * as browser from 'vs/base/browser/browser';
|
|||
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService';
|
||||
import { ipcRenderer as ipc, webFrame, crashReporter, Event as IpcEvent } from 'electron';
|
||||
import { ipcRenderer as ipc, webFrame, crashReporter, CrashReporterStartOptions, Event as IpcEvent } from 'electron';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing';
|
||||
import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
@ -537,13 +537,13 @@ export class ElectronWindow extends Disposable {
|
|||
}
|
||||
|
||||
// base options with product info
|
||||
const options = {
|
||||
const options: CrashReporterStartOptions = {
|
||||
companyName,
|
||||
productName,
|
||||
submitURL: isWindows ? hockeyAppConfig[process.arch === 'ia32' ? 'win32-ia32' : 'win32-x64'] : isLinux ? hockeyAppConfig[`linux-x64`] : hockeyAppConfig.darwin,
|
||||
extra: {
|
||||
vscode_version: product.version,
|
||||
vscode_commit: product.commit
|
||||
vscode_commit: product.commit || ''
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1094,7 +1094,7 @@ suite('WorkspaceConfigurationService - Folder', () => {
|
|||
test('no change event when there are no global tasks', async () => {
|
||||
const target = sinon.spy();
|
||||
testObject.onDidChangeConfiguration(target);
|
||||
await timeout(500);
|
||||
await timeout(5);
|
||||
assert.ok(target.notCalled);
|
||||
});
|
||||
|
||||
|
|
|
@ -146,10 +146,7 @@ export class ProgressService extends Disposable implements IProgressService {
|
|||
private withNotificationProgress<P extends Promise<R>, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: (choice?: number) => void): P {
|
||||
const toDispose = new DisposableStore();
|
||||
|
||||
const createNotification = (message: string | undefined, increment?: number): INotificationHandle | undefined => {
|
||||
if (!message) {
|
||||
return undefined; // we need a message at least
|
||||
}
|
||||
const createNotification = (message: string, increment?: number): INotificationHandle => {
|
||||
|
||||
const primaryActions = options.primaryActions ? Array.from(options.primaryActions) : [];
|
||||
const secondaryActions = options.secondaryActions ? Array.from(options.secondaryActions) : [];
|
||||
|
@ -222,21 +219,34 @@ export class ProgressService extends Disposable implements IProgressService {
|
|||
};
|
||||
|
||||
let handle: INotificationHandle | undefined;
|
||||
let handleSoon: any | undefined;
|
||||
|
||||
let titleAndMessage: string | undefined; // hoisted to make sure a delayed notification shows the most recent message
|
||||
|
||||
const updateNotification = (message?: string, increment?: number): void => {
|
||||
if (!handle) {
|
||||
handle = createNotification(message, increment);
|
||||
|
||||
// full message (inital or update)
|
||||
if (message && options.title) {
|
||||
titleAndMessage = `${options.title}: ${message}`; // always prefix with overall title if we have it (https://github.com/Microsoft/vscode/issues/50932)
|
||||
} else {
|
||||
if (typeof message === 'string') {
|
||||
let newMessage: string;
|
||||
if (typeof options.title === 'string') {
|
||||
newMessage = `${options.title}: ${message}`; // always prefix with overall title if we have it (https://github.com/Microsoft/vscode/issues/50932)
|
||||
} else {
|
||||
newMessage = message;
|
||||
titleAndMessage = options.title || message;
|
||||
}
|
||||
|
||||
if (!handle && titleAndMessage) {
|
||||
// create notification now or after a delay
|
||||
if (typeof options.delay === 'number' && options.delay > 0) {
|
||||
if (typeof handleSoon !== 'number') {
|
||||
handleSoon = setTimeout(() => handle = createNotification(titleAndMessage!, increment), options.delay);
|
||||
}
|
||||
|
||||
handle.updateMessage(newMessage);
|
||||
} else {
|
||||
handle = createNotification(titleAndMessage, increment);
|
||||
}
|
||||
}
|
||||
|
||||
if (handle) {
|
||||
if (titleAndMessage) {
|
||||
handle.updateMessage(titleAndMessage);
|
||||
}
|
||||
if (typeof increment === 'number') {
|
||||
updateProgress(handle, increment);
|
||||
}
|
||||
|
@ -244,7 +254,7 @@ export class ProgressService extends Disposable implements IProgressService {
|
|||
};
|
||||
|
||||
// Show initially
|
||||
updateNotification(options.title);
|
||||
updateNotification();
|
||||
|
||||
// Update based on progress
|
||||
const promise = callback({
|
||||
|
@ -255,6 +265,7 @@ export class ProgressService extends Disposable implements IProgressService {
|
|||
|
||||
// Show progress for at least 800ms and then hide once done or canceled
|
||||
Promise.all([timeout(800), promise]).finally(() => {
|
||||
clearTimeout(handleSoon);
|
||||
if (handle) {
|
||||
handle.close();
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
|
|||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IEditableData } from 'vs/workbench/common/views';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TunnelInformation } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { TunnelInformation, TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
|
||||
export const IRemoteExplorerService = createDecorator<IRemoteExplorerService>('remoteExplorerService');
|
||||
export const REMOTE_EXPLORER_TYPE_KEY: string = 'remote.explorerType';
|
||||
|
@ -46,7 +46,7 @@ export interface Tunnel {
|
|||
}
|
||||
|
||||
export function MakeAddress(host: string, port: number): string {
|
||||
if (host = '127.0.0.1') {
|
||||
if (host === '127.0.0.1') {
|
||||
host = 'localhost';
|
||||
}
|
||||
return host + ':' + port;
|
||||
|
@ -172,7 +172,7 @@ export class TunnelModel extends Disposable {
|
|||
return (this.forwarded.get(key) || this.detected.get(key))?.localAddress;
|
||||
}
|
||||
|
||||
addEnvironmentTunnels(tunnels: { remoteAddress: { port: number, host: string }, localAddress: string }[]): void {
|
||||
addEnvironmentTunnels(tunnels: TunnelDescription[]): void {
|
||||
tunnels.forEach(tunnel => {
|
||||
this.detected.set(MakeAddress(tunnel.remoteAddress.host, tunnel.remoteAddress.port), {
|
||||
remoteHost: tunnel.remoteAddress.host,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue