mirror of
https://github.com/Microsoft/vscode
synced 2024-09-19 18:48:00 +00:00
Merge remote-tracking branch 'origin/master' into alex/wrapping
This commit is contained in:
commit
4f52d506ea
2
.github/copycat.yml
vendored
2
.github/copycat.yml
vendored
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
perform: false,
|
||||
perform: true,
|
||||
target_owner: 'chrmarti',
|
||||
target_repo: 'testissues'
|
||||
}
|
||||
|
|
20
.vscode/launch.json
vendored
20
.vscode/launch.json
vendored
|
@ -19,10 +19,7 @@
|
|||
"restart": true,
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"presentation": {
|
||||
"hidden": true,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
|
@ -227,6 +224,21 @@
|
|||
"order": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "HTML Unit Tests",
|
||||
"program": "${workspaceFolder}/extensions/html-language-features/server/node_modules/mocha/bin/_mocha",
|
||||
"stopOnEntry": false,
|
||||
"cwd": "${workspaceFolder}/extensions/html-language-features/server",
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/extensions/html-language-features/server/out/**/*.js"
|
||||
],
|
||||
"presentation": {
|
||||
"group": "5_tests",
|
||||
"order": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -23,6 +23,7 @@ import { formatError, runSafe, runSafeAsync } from './utils/runner';
|
|||
import { getFoldingRanges } from './modes/htmlFolding';
|
||||
import { getDataProviders } from './customData';
|
||||
import { getSelectionRanges } from './modes/selectionRanges';
|
||||
import { SemanticTokenProvider, newSemanticTokenProvider } from './modes/semanticTokens';
|
||||
|
||||
namespace TagCloseRequest {
|
||||
export const type: RequestType<TextDocumentPositionParams, string | null, any, any> = new RequestType('html/tag');
|
||||
|
@ -530,14 +531,19 @@ connection.onRequest(MatchingTagPositionRequest.type, (params, token) => {
|
|||
}, null, `Error while computing matching tag position for ${params.textDocument.uri}`, token);
|
||||
});
|
||||
|
||||
let semanticTokensProvider: SemanticTokenProvider | undefined;
|
||||
function getSemanticTokenProvider() {
|
||||
if (!semanticTokensProvider) {
|
||||
semanticTokensProvider = newSemanticTokenProvider(languageModes);
|
||||
}
|
||||
return semanticTokensProvider;
|
||||
}
|
||||
|
||||
connection.onRequest(SemanticTokenRequest.type, (params, token) => {
|
||||
return runSafe(() => {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
const jsMode = languageModes.getMode('javascript');
|
||||
if (jsMode && jsMode.getSemanticTokens) {
|
||||
return jsMode.getSemanticTokens(document, params.ranges);
|
||||
}
|
||||
return getSemanticTokenProvider().getSemanticTokens(document, params.ranges);
|
||||
}
|
||||
return null;
|
||||
}, null, `Error while computing semantic tokens for ${params.textDocument.uri}`, token);
|
||||
|
@ -545,11 +551,7 @@ connection.onRequest(SemanticTokenRequest.type, (params, token) => {
|
|||
|
||||
connection.onRequest(SemanticTokenLegendRequest.type, (_params, token) => {
|
||||
return runSafe(() => {
|
||||
const jsMode = languageModes.getMode('javascript');
|
||||
if (jsMode && jsMode.getSemanticTokenLegend) {
|
||||
return jsMode.getSemanticTokenLegend();
|
||||
}
|
||||
return null;
|
||||
return getSemanticTokenProvider().legend;
|
||||
}, null, `Error while computing semantic tokens legend`, token);
|
||||
});
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ export function getDocumentRegions(languageService: LanguageService, document: T
|
|||
} else if (lastAttributeName === 'type' && lastTagName.toLowerCase() === 'script') {
|
||||
if (/["'](module|(text|application)\/(java|ecma)script|text\/babel)["']/.test(scanner.getTokenText())) {
|
||||
languageIdFromType = 'javascript';
|
||||
} else if (/["']text\/typescript["']/.test(scanner.getTokenText())) {
|
||||
languageIdFromType = 'typescript';
|
||||
} else {
|
||||
languageIdFromType = undefined;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
SymbolInformation, SymbolKind, CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation,
|
||||
Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, MarkedString,
|
||||
DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions, FoldingRange, FoldingRangeKind, SelectionRange,
|
||||
LanguageMode, Settings
|
||||
LanguageMode, Settings, SemanticTokenData
|
||||
} from './languageModes';
|
||||
import { getWordAtText, startsWith, isWhitespaceOnly, repeat } from '../utils/strings';
|
||||
import { HTMLDocumentRegions } from './embeddedSupport';
|
||||
|
@ -17,8 +17,6 @@ import * as ts from 'typescript';
|
|||
import { join } from 'path';
|
||||
import { getSemanticTokens, getSemanticTokenLegend } from './javascriptSemanticTokens';
|
||||
|
||||
const FILE_NAME = 'vscode://javascript/1'; // the same 'file' is used for all contents
|
||||
const TS_FILE_NAME = 'vscode://javascript/2.ts';
|
||||
const JS_WORD_REGEX = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g;
|
||||
|
||||
let jquery_d_ts = join(__dirname, '../lib/jquery.d.ts'); // when packaged
|
||||
|
@ -26,8 +24,10 @@ if (!ts.sys.fileExists(jquery_d_ts)) {
|
|||
jquery_d_ts = join(__dirname, '../../lib/jquery.d.ts'); // from source
|
||||
}
|
||||
|
||||
export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocumentRegions>): LanguageMode {
|
||||
let jsDocuments = getLanguageModelCache<TextDocument>(10, 60, document => documentRegions.get(document).getEmbeddedDocument('javascript'));
|
||||
export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocumentRegions>, languageId: 'javascript' | 'typescript'): LanguageMode {
|
||||
let jsDocuments = getLanguageModelCache<TextDocument>(10, 60, document => documentRegions.get(document).getEmbeddedDocument(languageId));
|
||||
|
||||
const workingFile = languageId === 'javascript' ? 'vscode://javascript/1.js' : 'vscode://javascript/2.ts'; // the same 'file' is used for all contents
|
||||
|
||||
let compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es6.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic };
|
||||
let currentTextDocument: TextDocument;
|
||||
|
@ -40,10 +40,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
}
|
||||
const host: ts.LanguageServiceHost = {
|
||||
getCompilationSettings: () => compilerOptions,
|
||||
getScriptFileNames: () => [FILE_NAME, TS_FILE_NAME, jquery_d_ts],
|
||||
getScriptKind: (fileName) => fileName === TS_FILE_NAME ? ts.ScriptKind.TS : ts.ScriptKind.JS,
|
||||
getScriptFileNames: () => [workingFile, jquery_d_ts],
|
||||
getScriptKind: (fileName) => fileName.substr(fileName.length - 2) === 'ts' ? ts.ScriptKind.TS : ts.ScriptKind.JS,
|
||||
getScriptVersion: (fileName: string) => {
|
||||
if (fileName === FILE_NAME || fileName === TS_FILE_NAME) {
|
||||
if (fileName === workingFile) {
|
||||
return String(scriptFileVersion);
|
||||
}
|
||||
return '1'; // default lib an jquery.d.ts are static
|
||||
|
@ -51,7 +51,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
getScriptSnapshot: (fileName: string) => {
|
||||
let text = '';
|
||||
if (startsWith(fileName, 'vscode:')) {
|
||||
if (fileName === FILE_NAME || fileName === TS_FILE_NAME) {
|
||||
if (fileName === workingFile) {
|
||||
text = currentTextDocument.getText();
|
||||
}
|
||||
} else {
|
||||
|
@ -72,17 +72,17 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
|
||||
return {
|
||||
getId() {
|
||||
return 'javascript';
|
||||
return languageId;
|
||||
},
|
||||
doValidation(document: TextDocument): Diagnostic[] {
|
||||
updateCurrentTextDocument(document);
|
||||
const syntaxDiagnostics: ts.Diagnostic[] = jsLanguageService.getSyntacticDiagnostics(FILE_NAME);
|
||||
const semanticDiagnostics = jsLanguageService.getSemanticDiagnostics(FILE_NAME);
|
||||
const syntaxDiagnostics: ts.Diagnostic[] = jsLanguageService.getSyntacticDiagnostics(workingFile);
|
||||
const semanticDiagnostics = jsLanguageService.getSemanticDiagnostics(workingFile);
|
||||
return syntaxDiagnostics.concat(semanticDiagnostics).map((diag: ts.Diagnostic): Diagnostic => {
|
||||
return {
|
||||
range: convertRange(currentTextDocument, diag),
|
||||
severity: DiagnosticSeverity.Error,
|
||||
source: 'js',
|
||||
source: languageId,
|
||||
message: ts.flattenDiagnosticMessageText(diag.messageText, '\n')
|
||||
};
|
||||
});
|
||||
|
@ -90,7 +90,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
doComplete(document: TextDocument, position: Position): CompletionList {
|
||||
updateCurrentTextDocument(document);
|
||||
let offset = currentTextDocument.offsetAt(position);
|
||||
let completions = jsLanguageService.getCompletionsAtPosition(FILE_NAME, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
|
||||
let completions = jsLanguageService.getCompletionsAtPosition(workingFile, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
|
||||
if (!completions) {
|
||||
return { isIncomplete: false, items: [] };
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
kind: convertKind(entry.kind),
|
||||
textEdit: TextEdit.replace(replaceRange, entry.name),
|
||||
data: { // data used for resolving item details (see 'doResolve')
|
||||
languageId: 'javascript',
|
||||
languageId,
|
||||
uri: document.uri,
|
||||
offset: offset
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
},
|
||||
doResolve(document: TextDocument, item: CompletionItem): CompletionItem {
|
||||
updateCurrentTextDocument(document);
|
||||
let details = jsLanguageService.getCompletionEntryDetails(FILE_NAME, item.data.offset, item.label, undefined, undefined, undefined);
|
||||
let details = jsLanguageService.getCompletionEntryDetails(workingFile, item.data.offset, item.label, undefined, undefined, undefined);
|
||||
if (details) {
|
||||
item.detail = ts.displayPartsToString(details.displayParts);
|
||||
item.documentation = ts.displayPartsToString(details.documentation);
|
||||
|
@ -126,7 +126,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
},
|
||||
doHover(document: TextDocument, position: Position): Hover | null {
|
||||
updateCurrentTextDocument(document);
|
||||
let info = jsLanguageService.getQuickInfoAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
|
||||
let info = jsLanguageService.getQuickInfoAtPosition(workingFile, currentTextDocument.offsetAt(position));
|
||||
if (info) {
|
||||
let contents = ts.displayPartsToString(info.displayParts);
|
||||
return {
|
||||
|
@ -138,7 +138,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
},
|
||||
doSignatureHelp(document: TextDocument, position: Position): SignatureHelp | null {
|
||||
updateCurrentTextDocument(document);
|
||||
let signHelp = jsLanguageService.getSignatureHelpItems(FILE_NAME, currentTextDocument.offsetAt(position), undefined);
|
||||
let signHelp = jsLanguageService.getSignatureHelpItems(workingFile, currentTextDocument.offsetAt(position), undefined);
|
||||
if (signHelp) {
|
||||
let ret: SignatureHelp = {
|
||||
activeSignature: signHelp.selectedItemIndex,
|
||||
|
@ -175,7 +175,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
},
|
||||
findDocumentHighlight(document: TextDocument, position: Position): DocumentHighlight[] {
|
||||
updateCurrentTextDocument(document);
|
||||
const highlights = jsLanguageService.getDocumentHighlights(FILE_NAME, currentTextDocument.offsetAt(position), [FILE_NAME]);
|
||||
const highlights = jsLanguageService.getDocumentHighlights(workingFile, currentTextDocument.offsetAt(position), [workingFile]);
|
||||
const out: DocumentHighlight[] = [];
|
||||
for (const entry of highlights || []) {
|
||||
for (const highlight of entry.highlightSpans) {
|
||||
|
@ -189,7 +189,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
},
|
||||
findDocumentSymbols(document: TextDocument): SymbolInformation[] {
|
||||
updateCurrentTextDocument(document);
|
||||
let items = jsLanguageService.getNavigationBarItems(FILE_NAME);
|
||||
let items = jsLanguageService.getNavigationBarItems(workingFile);
|
||||
if (items) {
|
||||
let result: SymbolInformation[] = [];
|
||||
let existing = Object.create(null);
|
||||
|
@ -225,9 +225,9 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
},
|
||||
findDefinition(document: TextDocument, position: Position): Definition | null {
|
||||
updateCurrentTextDocument(document);
|
||||
let definition = jsLanguageService.getDefinitionAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
|
||||
let definition = jsLanguageService.getDefinitionAtPosition(workingFile, currentTextDocument.offsetAt(position));
|
||||
if (definition) {
|
||||
return definition.filter(d => d.fileName === FILE_NAME).map(d => {
|
||||
return definition.filter(d => d.fileName === workingFile).map(d => {
|
||||
return {
|
||||
uri: document.uri,
|
||||
range: convertRange(currentTextDocument, d.textSpan)
|
||||
|
@ -238,9 +238,9 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
},
|
||||
findReferences(document: TextDocument, position: Position): Location[] {
|
||||
updateCurrentTextDocument(document);
|
||||
let references = jsLanguageService.getReferencesAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
|
||||
let references = jsLanguageService.getReferencesAtPosition(workingFile, currentTextDocument.offsetAt(position));
|
||||
if (references) {
|
||||
return references.filter(d => d.fileName === FILE_NAME).map(d => {
|
||||
return references.filter(d => d.fileName === workingFile).map(d => {
|
||||
return {
|
||||
uri: document.uri,
|
||||
range: convertRange(currentTextDocument, d.textSpan)
|
||||
|
@ -255,7 +255,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
const parent = selectionRange.parent ? convertSelectionRange(selectionRange.parent) : undefined;
|
||||
return SelectionRange.create(convertRange(currentTextDocument, selectionRange.textSpan), parent);
|
||||
}
|
||||
const range = jsLanguageService.getSmartSelectionRange(FILE_NAME, currentTextDocument.offsetAt(position));
|
||||
const range = jsLanguageService.getSmartSelectionRange(workingFile, currentTextDocument.offsetAt(position));
|
||||
return convertSelectionRange(range);
|
||||
},
|
||||
format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings: Settings = globalSettings): TextEdit[] {
|
||||
|
@ -273,7 +273,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
end -= range.end.character;
|
||||
lastLineRange = Range.create(Position.create(range.end.line, 0), range.end);
|
||||
}
|
||||
let edits = jsLanguageService.getFormattingEditsForRange(FILE_NAME, start, end, formatSettings);
|
||||
let edits = jsLanguageService.getFormattingEditsForRange(workingFile, start, end, formatSettings);
|
||||
if (edits) {
|
||||
let result = [];
|
||||
for (let edit of edits) {
|
||||
|
@ -296,7 +296,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
},
|
||||
getFoldingRanges(document: TextDocument): FoldingRange[] {
|
||||
updateCurrentTextDocument(document);
|
||||
let spans = jsLanguageService.getOutliningSpans(FILE_NAME);
|
||||
let spans = jsLanguageService.getOutliningSpans(workingFile);
|
||||
let ranges: FoldingRange[] = [];
|
||||
for (let span of spans) {
|
||||
let curr = convertRange(currentTextDocument, span.textSpan);
|
||||
|
@ -316,12 +316,9 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
|||
onDocumentRemoved(document: TextDocument) {
|
||||
jsDocuments.onDocumentRemoved(document);
|
||||
},
|
||||
getSemanticTokens(document: TextDocument, ranges: Range[] | undefined): number[] {
|
||||
getSemanticTokens(document: TextDocument): SemanticTokenData[] {
|
||||
updateCurrentTextDocument(document);
|
||||
if (!ranges) {
|
||||
ranges = [Range.create(Position.create(0, 0), document.positionAt(document.getText().length))];
|
||||
}
|
||||
return getSemanticTokens(jsLanguageService, currentTextDocument, TS_FILE_NAME, ranges);
|
||||
return getSemanticTokens(jsLanguageService, currentTextDocument, workingFile);
|
||||
},
|
||||
getSemanticTokenLegend(): { types: string[], modifiers: string[] } {
|
||||
return getSemanticTokenLegend();
|
||||
|
|
|
@ -3,22 +3,14 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TextDocument, Range } from './languageModes';
|
||||
import { TextDocument, SemanticTokenData } from './languageModes';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
type SemanticTokenData = { offset: number, length: number, typeIdx: number, modifierSet: number };
|
||||
|
||||
export function getSemanticTokens(jsLanguageService: ts.LanguageService, currentTextDocument: TextDocument, fileName: string, ranges: Range[]) {
|
||||
export function getSemanticTokens(jsLanguageService: ts.LanguageService, currentTextDocument: TextDocument, fileName: string): SemanticTokenData[] {
|
||||
//https://ts-ast-viewer.com/#code/AQ0g2CmAuwGbALzAJwG4BQZQGNwEMBnQ4AQQEYBmYAb2C22zgEtJwATJVTRxgcwD27AQAp8AGmAAjAJS0A9POB8+7NQ168oscAJz5wANXwAnLug2bsJmAFcTAO2XAA1MHyvgu-UdOeWbOw8ViAAvpagocBAA
|
||||
|
||||
let resultTokens: SemanticTokenData[] = [];
|
||||
// const tokens = jsLanguageService.getSemanticClassifications(fileName, { start: 0, length: currentTextDocument.getText().length });
|
||||
// for (let token of tokens) {
|
||||
// const typeIdx = tokenFromClassificationMapping[token.classificationType];
|
||||
// if (typeIdx !== undefined) {
|
||||
// resultTokens.push({ offset: token.textSpan.start, length: token.textSpan.length, typeIdx, modifierSet: 0 });
|
||||
// }
|
||||
// }
|
||||
|
||||
const program = jsLanguageService.getProgram();
|
||||
if (program) {
|
||||
|
@ -46,7 +38,7 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current
|
|||
modifierSet |= TokenModifier.async;
|
||||
}
|
||||
if (typeIdx !== undefined) {
|
||||
resultTokens.push({ offset: node.getStart(), length: node.getWidth(), typeIdx, modifierSet });
|
||||
resultTokens.push({ start: currentTextDocument.positionAt(node.getStart()), length: node.getWidth(), typeIdx, modifierSet });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,41 +52,7 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
resultTokens = resultTokens.sort((d1, d2) => d1.offset - d2.offset);
|
||||
const offsetRanges = ranges.map(r => ({ startOffset: currentTextDocument.offsetAt(r.start), endOffset: currentTextDocument.offsetAt(r.end) })).sort((d1, d2) => d1.startOffset - d2.startOffset);
|
||||
|
||||
let rangeIndex = 0;
|
||||
let currRange = offsetRanges[rangeIndex++];
|
||||
|
||||
let prefLine = 0;
|
||||
let prevChar = 0;
|
||||
|
||||
let encodedResult: number[] = [];
|
||||
|
||||
for (let k = 0; k < resultTokens.length && currRange; k++) {
|
||||
const curr = resultTokens[k];
|
||||
if (currRange.startOffset <= curr.offset && curr.offset + curr.length <= currRange.endOffset) {
|
||||
// token inside a range
|
||||
|
||||
const startPos = currentTextDocument.positionAt(curr.offset);
|
||||
if (prefLine !== startPos.line) {
|
||||
prevChar = 0;
|
||||
}
|
||||
encodedResult.push(startPos.line - prefLine); // line delta
|
||||
encodedResult.push(startPos.character - prevChar); // line delta
|
||||
encodedResult.push(curr.length); // length
|
||||
encodedResult.push(curr.typeIdx); // tokenType
|
||||
encodedResult.push(curr.modifierSet); // tokenModifier
|
||||
|
||||
prefLine = startPos.line;
|
||||
prevChar = startPos.character;
|
||||
|
||||
} else if (currRange.endOffset >= curr.offset) {
|
||||
currRange = offsetRanges[rangeIndex++];
|
||||
}
|
||||
}
|
||||
return encodedResult;
|
||||
return resultTokens;
|
||||
}
|
||||
|
||||
|
||||
|
@ -103,15 +61,15 @@ export function getSemanticTokenLegend() {
|
|||
}
|
||||
|
||||
|
||||
const tokenTypes: string[] = ['class', 'enum', 'interface', 'namespace', 'parameterType', 'type', 'parameter', 'variable', 'property', 'constant', 'function', 'member'];
|
||||
const tokenTypes: string[] = ['class', 'enum', 'interface', 'namespace', 'typeParameter', 'type', 'parameter', 'variable', 'property', 'constant', 'function', 'member'];
|
||||
const tokenModifiers: string[] = ['declaration', 'static', 'async'];
|
||||
|
||||
enum TokenType {
|
||||
const enum TokenType {
|
||||
'class' = 0,
|
||||
'enum' = 1,
|
||||
'interface' = 2,
|
||||
'namespace' = 3,
|
||||
'parameterType' = 4,
|
||||
'typeParameter' = 4,
|
||||
'type' = 5,
|
||||
'parameter' = 6,
|
||||
'variable' = 7,
|
||||
|
@ -121,22 +79,13 @@ enum TokenType {
|
|||
'member' = 11
|
||||
}
|
||||
|
||||
enum TokenModifier {
|
||||
|
||||
const enum TokenModifier {
|
||||
'declaration' = 0x01,
|
||||
'static' = 0x02,
|
||||
'async' = 0x04,
|
||||
}
|
||||
|
||||
// const tokenFromClassificationMapping: { [name: string]: TokenType } = {
|
||||
// [ts.ClassificationTypeNames.className]: TokenType.class,
|
||||
// [ts.ClassificationTypeNames.enumName]: TokenType.enum,
|
||||
// [ts.ClassificationTypeNames.interfaceName]: TokenType.interface,
|
||||
// [ts.ClassificationTypeNames.moduleName]: TokenType.namespace,
|
||||
// [ts.ClassificationTypeNames.typeParameterName]: TokenType.parameterType,
|
||||
// [ts.ClassificationTypeNames.typeAliasName]: TokenType.type,
|
||||
// [ts.ClassificationTypeNames.parameterName]: TokenType.parameter
|
||||
// };
|
||||
|
||||
const tokenFromDeclarationMapping: { [name: string]: TokenType } = {
|
||||
[ts.SyntaxKind.VariableDeclaration]: TokenType.variable,
|
||||
[ts.SyntaxKind.Parameter]: TokenType.parameter,
|
||||
|
@ -150,4 +99,7 @@ const tokenFromDeclarationMapping: { [name: string]: TokenType } = {
|
|||
[ts.SyntaxKind.MethodSignature]: TokenType.member,
|
||||
[ts.SyntaxKind.GetAccessor]: TokenType.property,
|
||||
[ts.SyntaxKind.PropertySignature]: TokenType.property,
|
||||
[ts.SyntaxKind.InterfaceDeclaration]: TokenType.interface,
|
||||
[ts.SyntaxKind.TypeAliasDeclaration]: TokenType.type,
|
||||
[ts.SyntaxKind.TypeParameter]: TokenType.typeParameter
|
||||
};
|
||||
|
|
|
@ -31,6 +31,13 @@ export interface Workspace {
|
|||
readonly folders: WorkspaceFolder[];
|
||||
}
|
||||
|
||||
export interface SemanticTokenData {
|
||||
start: Position;
|
||||
length: number;
|
||||
typeIdx: number;
|
||||
modifierSet: number;
|
||||
}
|
||||
|
||||
export interface LanguageMode {
|
||||
getId(): string;
|
||||
getSelectionRange?: (document: TextDocument, position: Position) => SelectionRange;
|
||||
|
@ -52,7 +59,7 @@ export interface LanguageMode {
|
|||
findMatchingTagPosition?: (document: TextDocument, position: Position) => Position | null;
|
||||
getFoldingRanges?: (document: TextDocument) => FoldingRange[];
|
||||
onDocumentRemoved(document: TextDocument): void;
|
||||
getSemanticTokens?(document: TextDocument, ranges: Range[] | undefined): number[];
|
||||
getSemanticTokens?(document: TextDocument): SemanticTokenData[];
|
||||
getSemanticTokenLegend?(): { types: string[], modifiers: string[] };
|
||||
dispose(): void;
|
||||
}
|
||||
|
@ -87,7 +94,8 @@ export function getLanguageModes(supportedLanguages: { [languageId: string]: boo
|
|||
modes['css'] = getCSSMode(cssLanguageService, documentRegions, workspace);
|
||||
}
|
||||
if (supportedLanguages['javascript']) {
|
||||
modes['javascript'] = getJavaScriptMode(documentRegions);
|
||||
modes['javascript'] = getJavaScriptMode(documentRegions, 'javascript');
|
||||
modes['typescript'] = getJavaScriptMode(documentRegions, 'typescript');
|
||||
}
|
||||
return {
|
||||
getModeAtPosition(document: TextDocument, position: Position): LanguageMode | undefined {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { LanguageModes, TextDocument, Position, Range, SelectionRange } from './languageModes';
|
||||
import { insideRangeButNotSame } from '../utils/positions';
|
||||
|
||||
export function getSelectionRanges(languageModes: LanguageModes, document: TextDocument, positions: Position[]) {
|
||||
const htmlMode = languageModes.getMode('html');
|
||||
|
@ -23,13 +24,3 @@ export function getSelectionRanges(languageModes: LanguageModes, document: TextD
|
|||
});
|
||||
}
|
||||
|
||||
function beforeOrSame(p1: Position, p2: Position) {
|
||||
return p1.line < p2.line || p1.line === p2.line && p1.character <= p2.character;
|
||||
}
|
||||
function insideRangeButNotSame(r1: Range, r2: Range) {
|
||||
return beforeOrSame(r1.start, r2.start) && beforeOrSame(r2.end, r1.end) && !equalRange(r1, r2);
|
||||
}
|
||||
function equalRange(r1: Range, r2: Range) {
|
||||
return r1.start.line === r2.start.line && r1.start.character === r2.start.character && r1.end.line === r2.end.line && r1.end.character === r2.end.character;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SemanticTokenData, Range, TextDocument, LanguageModes, Position } from './languageModes';
|
||||
import { beforeOrSame } from '../utils/positions';
|
||||
|
||||
interface LegendMapping {
|
||||
types: number[] | undefined;
|
||||
modifiers: number[] | undefined;
|
||||
}
|
||||
|
||||
export interface SemanticTokenProvider {
|
||||
readonly legend: { types: string[]; modifiers: string[] };
|
||||
getSemanticTokens(document: TextDocument, ranges?: Range[]): number[];
|
||||
}
|
||||
|
||||
|
||||
export function newSemanticTokenProvider(languageModes: LanguageModes): SemanticTokenProvider {
|
||||
|
||||
// combined legend across modes
|
||||
const legend = { types: [], modifiers: [] };
|
||||
const legendMappings: { [modeId: string]: LegendMapping } = {};
|
||||
|
||||
for (let mode of languageModes.getAllModes()) {
|
||||
if (mode.getSemanticTokenLegend && mode.getSemanticTokens) {
|
||||
const modeLegend = mode.getSemanticTokenLegend();
|
||||
legendMappings[mode.getId()] = { types: createMapping(modeLegend.types, legend.types), modifiers: createMapping(modeLegend.modifiers, legend.modifiers) };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
legend,
|
||||
getSemanticTokens(document: TextDocument, ranges?: Range[]): number[] {
|
||||
const allTokens: SemanticTokenData[] = [];
|
||||
for (let mode of languageModes.getAllModesInDocument(document)) {
|
||||
if (mode.getSemanticTokens) {
|
||||
const mapping = legendMappings[mode.getId()];
|
||||
const tokens = mode.getSemanticTokens(document);
|
||||
applyTypesMapping(tokens, mapping.types);
|
||||
applyModifiersMapping(tokens, mapping.modifiers);
|
||||
for (let token of tokens) {
|
||||
allTokens.push(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
return encodeTokens(allTokens, ranges);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createMapping(origLegend: string[], newLegend: string[]): number[] | undefined {
|
||||
const mapping: number[] = [];
|
||||
let needsMapping = false;
|
||||
for (let origIndex = 0; origIndex < origLegend.length; origIndex++) {
|
||||
const entry = origLegend[origIndex];
|
||||
let newIndex = newLegend.indexOf(entry);
|
||||
if (newIndex === -1) {
|
||||
newIndex = newLegend.length;
|
||||
newLegend.push(entry);
|
||||
}
|
||||
mapping.push(newIndex);
|
||||
needsMapping = needsMapping || (newIndex !== origIndex);
|
||||
}
|
||||
return needsMapping ? mapping : undefined;
|
||||
}
|
||||
|
||||
function applyTypesMapping(tokens: SemanticTokenData[], typesMapping: number[] | undefined): void {
|
||||
if (typesMapping) {
|
||||
for (let token of tokens) {
|
||||
token.typeIdx = typesMapping[token.typeIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applyModifiersMapping(tokens: SemanticTokenData[], modifiersMapping: number[] | undefined): void {
|
||||
if (modifiersMapping) {
|
||||
for (let token of tokens) {
|
||||
let modifierSet = token.modifierSet;
|
||||
if (modifierSet) {
|
||||
let index = 0;
|
||||
let result = 0;
|
||||
while (modifierSet > 0) {
|
||||
if ((modifierSet & 1) !== 0) {
|
||||
result = result + (1 << modifiersMapping[index]);
|
||||
}
|
||||
index++;
|
||||
modifierSet = modifierSet >> 1;
|
||||
}
|
||||
token.modifierSet = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fullRange = [Range.create(Position.create(0, 0), Position.create(Number.MAX_VALUE, 0))];
|
||||
|
||||
function encodeTokens(tokens: SemanticTokenData[], ranges?: Range[]): number[] {
|
||||
|
||||
const resultTokens = tokens.sort((d1, d2) => d1.start.line - d2.start.line || d1.start.character - d2.start.character);
|
||||
if (ranges) {
|
||||
ranges = ranges.sort((d1, d2) => d1.start.line - d2.start.line || d1.start.character - d2.start.character);
|
||||
} else {
|
||||
ranges = fullRange;
|
||||
}
|
||||
|
||||
let rangeIndex = 0;
|
||||
let currRange = ranges[rangeIndex++];
|
||||
|
||||
let prefLine = 0;
|
||||
let prevChar = 0;
|
||||
|
||||
let encodedResult: number[] = [];
|
||||
|
||||
for (let k = 0; k < resultTokens.length && currRange; k++) {
|
||||
const curr = resultTokens[k];
|
||||
const start = curr.start;
|
||||
while (currRange && beforeOrSame(currRange.end, start)) {
|
||||
currRange = ranges[rangeIndex++];
|
||||
}
|
||||
if (currRange && beforeOrSame(currRange.start, start) && beforeOrSame({ line: start.line, character: start.character + curr.length }, currRange.end)) {
|
||||
// token inside a range
|
||||
|
||||
if (prefLine !== start.line) {
|
||||
prevChar = 0;
|
||||
}
|
||||
encodedResult.push(start.line - prefLine); // line delta
|
||||
encodedResult.push(start.character - prevChar); // line delta
|
||||
encodedResult.push(curr.length); // length
|
||||
encodedResult.push(curr.typeIdx); // tokenType
|
||||
encodedResult.push(curr.modifierSet); // tokenModifier
|
||||
|
||||
prefLine = start.line;
|
||||
prevChar = start.character;
|
||||
}
|
||||
}
|
||||
return encodedResult;
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { TextDocument, getLanguageModes, ClientCapabilities, Range, Position } from '../modes/languageModes';
|
||||
import { newSemanticTokenProvider } from '../modes/semanticTokens';
|
||||
|
||||
interface ExpectedToken {
|
||||
startLine: number;
|
||||
|
@ -14,22 +15,17 @@ interface ExpectedToken {
|
|||
tokenClassifiction: string;
|
||||
}
|
||||
|
||||
function assertTokens(lines: string[], expected: ExpectedToken[], range?: Range, message?: string): void {
|
||||
function assertTokens(lines: string[], expected: ExpectedToken[], ranges?: Range[], message?: string): void {
|
||||
const document = TextDocument.create('test://foo/bar.html', 'html', 1, lines.join('\n'));
|
||||
const workspace = {
|
||||
settings: {},
|
||||
folders: [{ name: 'foo', uri: 'test://foo' }]
|
||||
};
|
||||
const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST);
|
||||
const semanticTokensProvider = newSemanticTokenProvider(languageModes);
|
||||
|
||||
if (!range) {
|
||||
range = Range.create(Position.create(0, 0), document.positionAt(document.getText().length));
|
||||
}
|
||||
|
||||
const jsMode = languageModes.getMode('javascript')!;
|
||||
|
||||
const legend = jsMode.getSemanticTokenLegend!();
|
||||
const actual = jsMode.getSemanticTokens!(document, [range]);
|
||||
const legend = semanticTokensProvider.legend;
|
||||
const actual = semanticTokensProvider.getSemanticTokens(document, ranges);
|
||||
|
||||
let actualRanges = [];
|
||||
let lastLine = 0;
|
||||
|
@ -50,9 +46,9 @@ function t(startLine: number, character: number, length: number, tokenClassifict
|
|||
return { startLine, character, length, tokenClassifiction };
|
||||
}
|
||||
|
||||
suite('JavaScript Semantic Tokens', () => {
|
||||
suite('HTML Semantic Tokens', () => {
|
||||
|
||||
test('variables', () => {
|
||||
test('Variables', () => {
|
||||
const input = [
|
||||
/*0*/'<html>',
|
||||
/*1*/'<head>',
|
||||
|
@ -75,7 +71,7 @@ suite('JavaScript Semantic Tokens', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
test('function', () => {
|
||||
test('Functions', () => {
|
||||
const input = [
|
||||
/*0*/'<html>',
|
||||
/*1*/'<head>',
|
||||
|
@ -95,7 +91,7 @@ suite('JavaScript Semantic Tokens', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
test('members', () => {
|
||||
test('Members', () => {
|
||||
const input = [
|
||||
/*0*/'<html>',
|
||||
/*1*/'<head>',
|
||||
|
@ -124,6 +120,87 @@ suite('JavaScript Semantic Tokens', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
test('Interfaces', () => {
|
||||
const input = [
|
||||
/*0*/'<html>',
|
||||
/*1*/'<head>',
|
||||
/*2*/'<script type="text/typescript">',
|
||||
/*3*/' interface Position { x: number, y: number };',
|
||||
/*4*/' const p = { x: 1, y: 2 } as Position;',
|
||||
/*5*/' const foo = (o: Position) => o.x + o.y;',
|
||||
/*6*/'</script>',
|
||||
/*7*/'</head>',
|
||||
/*8*/'</html>',
|
||||
];
|
||||
assertTokens(input, [
|
||||
t(3, 12, 8, 'interface.declaration'), t(3, 23, 1, 'property.declaration'), t(3, 34, 1, 'property.declaration'),
|
||||
t(4, 8, 1, 'variable.declaration'), t(4, 30, 8, 'interface'),
|
||||
t(5, 8, 3, 'variable.declaration'), t(5, 15, 1, 'parameter.declaration'), t(5, 18, 8, 'interface'), t(5, 31, 1, 'parameter'), t(5, 33, 1, 'property'), t(5, 37, 1, 'parameter'), t(5, 39, 1, 'property')
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
test('Type aliases and type parameters', () => {
|
||||
const input = [
|
||||
/*0*/'<html>',
|
||||
/*1*/'<head>',
|
||||
/*2*/'<script type="text/typescript">',
|
||||
/*3*/' type MyMap = Map<string, number>;',
|
||||
/*4*/' function f<T extends MyMap>(t: T | number) : T { ',
|
||||
/*5*/' return <T> <unknown> new Map<string, MyMap>();',
|
||||
/*6*/' }',
|
||||
/*7*/'</script>',
|
||||
/*8*/'</head>',
|
||||
/*9*/'</html>',
|
||||
];
|
||||
assertTokens(input, [
|
||||
t(3, 7, 5, 'type.declaration'), t(3, 15, 3, 'variable') /* to investiagte */,
|
||||
t(4, 11, 1, 'function.declaration'), t(4, 13, 1, 'typeParameter.declaration'), t(4, 23, 5, 'type'), t(4, 30, 1, 'parameter.declaration'), t(4, 33, 1, 'typeParameter'), t(4, 47, 1, 'typeParameter'),
|
||||
t(5, 12, 1, 'typeParameter'), t(5, 29, 3, 'variable'), t(5, 41, 5, 'type'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('TS and JS', () => {
|
||||
const input = [
|
||||
/*0*/'<html>',
|
||||
/*1*/'<head>',
|
||||
/*2*/'<script type="text/typescript">',
|
||||
/*3*/' function f<T>(p1: T): T[] { return [ p1 ]; }',
|
||||
/*4*/'</script>',
|
||||
/*5*/'<script>',
|
||||
/*6*/' window.alert("Hello");',
|
||||
/*7*/'</script>',
|
||||
/*8*/'</head>',
|
||||
/*9*/'</html>',
|
||||
];
|
||||
assertTokens(input, [
|
||||
t(3, 11, 1, 'function.declaration'), t(3, 13, 1, 'typeParameter.declaration'), t(3, 16, 2, 'parameter.declaration'), t(3, 20, 1, 'typeParameter'), t(3, 24, 1, 'typeParameter'), t(3, 39, 2, 'parameter'),
|
||||
t(6, 2, 6, 'variable'), t(6, 9, 5, 'member')
|
||||
]);
|
||||
});
|
||||
|
||||
test('Ranges', () => {
|
||||
const input = [
|
||||
/*0*/'<html>',
|
||||
/*1*/'<head>',
|
||||
/*2*/'<script>',
|
||||
/*3*/' window.alert("Hello");',
|
||||
/*4*/'</script>',
|
||||
/*5*/'<script>',
|
||||
/*6*/' window.alert("World");',
|
||||
/*7*/'</script>',
|
||||
/*8*/'</head>',
|
||||
/*9*/'</html>',
|
||||
];
|
||||
assertTokens(input, [
|
||||
t(3, 2, 6, 'variable'), t(3, 9, 5, 'member')
|
||||
], [Range.create(Position.create(2, 0), Position.create(4, 0))]);
|
||||
|
||||
assertTokens(input, [
|
||||
t(6, 2, 6, 'variable'),
|
||||
], [Range.create(Position.create(6, 2), Position.create(6, 8))]);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Position, Range } from '../modes/languageModes';
|
||||
|
||||
export function beforeOrSame(p1: Position, p2: Position) {
|
||||
return p1.line < p2.line || p1.line === p2.line && p1.character <= p2.character;
|
||||
}
|
||||
export function insideRangeButNotSame(r1: Range, r2: Range) {
|
||||
return beforeOrSame(r1.start, r2.start) && beforeOrSame(r2.end, r1.end) && !equalRange(r1, r2);
|
||||
}
|
||||
export function equalRange(r1: Range, r2: Range) {
|
||||
return r1.start.line === r2.start.line && r1.start.character === r2.start.character && r1.end.line === r2.end.line && r1.end.character === r2.end.character;
|
||||
}
|
|
@ -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';
|
||||
|
||||
|
||||
|
@ -78,6 +78,7 @@ export default class LanguageProvider extends Disposable {
|
|||
import('./features/signatureHelp').then(provider => this._register(provider.register(selector, this.client))),
|
||||
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/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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.42.0",
|
||||
"distro": "aafa05cffde354e7f58dc9fd2dcfc9aa3e81ea77",
|
||||
"distro": "683b7a48a0cab9871ae34d129cc31e1036746b37",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
@ -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);
|
||||
|
|
|
@ -329,24 +329,31 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
|||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
// commands that aren't needed anymore because there is now ContextKeyExpr.OR
|
||||
CommandsRegistry.registerCommandAlias('goToNextReferenceFromEmbeddedEditor', 'goToNextReference');
|
||||
CommandsRegistry.registerCommandAlias('goToPreviousReferenceFromEmbeddedEditor', 'goToPreviousReference');
|
||||
|
||||
// close
|
||||
CommandsRegistry.registerCommandAlias('closeReferenceSearchEditor', 'closeReferenceSearch');
|
||||
CommandsRegistry.registerCommand(
|
||||
'closeReferenceSearch',
|
||||
accessor => withController(accessor, controller => controller.closeWidget())
|
||||
);
|
||||
KeybindingsRegistry.registerKeybindingRule({
|
||||
id: 'closeReferenceSearch',
|
||||
weight: KeybindingWeight.EditorContrib - 101,
|
||||
primary: KeyCode.Escape,
|
||||
secondary: [KeyMod.Shift | KeyCode.Escape],
|
||||
when: ContextKeyExpr.or(
|
||||
ContextKeyExpr.and(ctxReferenceSearchVisible, ContextKeyExpr.not('config.editor.stablePeek')),
|
||||
ContextKeyExpr.and(PeekContext.inPeekEditor, ContextKeyExpr.not('config.editor.stablePeek'))
|
||||
),
|
||||
handler(accessor: ServicesAccessor) {
|
||||
withController(accessor, controller => controller.closeWidget());
|
||||
}
|
||||
when: ContextKeyExpr.and(PeekContext.inPeekEditor, ContextKeyExpr.not('config.editor.stablePeek'))
|
||||
});
|
||||
KeybindingsRegistry.registerKeybindingRule({
|
||||
id: 'closeReferenceSearch',
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
primary: KeyCode.Escape,
|
||||
secondary: [KeyMod.Shift | KeyCode.Escape],
|
||||
when: ContextKeyExpr.and(ctxReferenceSearchVisible, ContextKeyExpr.not('config.editor.stablePeek'))
|
||||
});
|
||||
|
||||
// commands that aren't needed anymore because there is now ContextKeyExpr.OR
|
||||
CommandsRegistry.registerCommandAlias('goToNextReferenceFromEmbeddedEditor', 'goToNextReference');
|
||||
CommandsRegistry.registerCommandAlias('goToPreviousReferenceFromEmbeddedEditor', 'goToPreviousReference');
|
||||
CommandsRegistry.registerCommandAlias('closeReferenceSearchEditor', 'closeReferenceSearch');
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'openReferenceToSide',
|
||||
|
|
|
@ -133,6 +133,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
|
|||
const options = this.editor.getOptions();
|
||||
const fontInfo = options.get(EditorOption.fontInfo);
|
||||
const fontFamily = fontInfo.fontFamily;
|
||||
const fontFeatureSettings = fontInfo.fontFeatureSettings;
|
||||
const fontSize = options.get(EditorOption.suggestFontSize) || fontInfo.fontSize;
|
||||
const lineHeight = options.get(EditorOption.suggestLineHeight) || fontInfo.lineHeight;
|
||||
const fontWeight = fontInfo.fontWeight;
|
||||
|
@ -142,6 +143,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
|
|||
data.root.style.fontSize = fontSizePx;
|
||||
data.root.style.fontWeight = fontWeight;
|
||||
main.style.fontFamily = fontFamily;
|
||||
main.style.fontFeatureSettings = fontFeatureSettings;
|
||||
main.style.lineHeight = lineHeightPx;
|
||||
data.icon.style.height = lineHeightPx;
|
||||
data.icon.style.width = lineHeightPx;
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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.');
|
||||
}
|
||||
}
|
|
@ -378,7 +378,7 @@ function registerDefaultClassifications(): void {
|
|||
registerTokenType('class', nls.localize('class', "Style for classes."), [['entity.name.class']], 'type');
|
||||
registerTokenType('interface', nls.localize('interface', "Style for interfaces."), undefined, 'type');
|
||||
registerTokenType('enum', nls.localize('enum', "Style for enums."), undefined, 'type');
|
||||
registerTokenType('parameterType', nls.localize('parameterType', "Style for parameter types."), undefined, 'type');
|
||||
registerTokenType('typeParameter', nls.localize('typeParameter', "Style for type parameters."), undefined, 'type');
|
||||
|
||||
registerTokenType('function', nls.localize('function', "Style for functions"), [['entity.name.function'], ['support.function']]);
|
||||
registerTokenType('member', nls.localize('member', "Style for member"), [['entity.name.function'], ['support.function']]);
|
||||
|
@ -395,7 +395,6 @@ function registerDefaultClassifications(): void {
|
|||
|
||||
tokenClassificationRegistry.registerTokenModifier('declaration', nls.localize('declaration', "Style for all symbol declarations."), undefined);
|
||||
tokenClassificationRegistry.registerTokenModifier('documentation', nls.localize('documentation', "Style to use for references in documentation."), undefined);
|
||||
//tokenClassificationRegistry.registerTokenModifier('member', nls.localize('member', "Style to use for member functions, variables (fields) and types."), undefined);
|
||||
tokenClassificationRegistry.registerTokenModifier('static', nls.localize('static', "Style to use for symbols that are static."), undefined);
|
||||
tokenClassificationRegistry.registerTokenModifier('abstract', nls.localize('abstract', "Style to use for symbols that are abstract."), undefined);
|
||||
tokenClassificationRegistry.registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined);
|
||||
|
|
|
@ -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';
|
||||
|
@ -25,10 +25,12 @@ interface ISyncPreviewResult {
|
|||
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';
|
||||
|
||||
|
@ -81,12 +83,44 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
|||
return false;
|
||||
}
|
||||
|
||||
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 getConflicts(): Promise<IConflictSetting[]> {
|
||||
if (!this.syncPreviewResultPromise) {
|
||||
return [];
|
||||
}
|
||||
if (this.status !== SyncStatus.HasConflicts) {
|
||||
return [];
|
||||
}
|
||||
const preview = await this.syncPreviewResultPromise;
|
||||
return preview.conflicts;
|
||||
}
|
||||
|
||||
async resolveConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<void> {
|
||||
if (this.status === SyncStatus.HasConflicts) {
|
||||
this.stop();
|
||||
await this.doSync(resolvedConflicts);
|
||||
}
|
||||
}
|
||||
|
||||
private async doSync(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<boolean> {
|
||||
this.logService.trace('Settings: Started synchronizing settings...');
|
||||
this.setStatus(SyncStatus.Syncing);
|
||||
|
||||
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 +144,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 +202,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,14 +217,14 @@ 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() : '{}';
|
||||
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 };
|
||||
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, conflicts };
|
||||
}
|
||||
|
||||
if (!lastSyncData // First time sync
|
||||
|
@ -209,12 +233,12 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
|||
) {
|
||||
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 +255,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
|||
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(previewContent));
|
||||
}
|
||||
|
||||
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
|
||||
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, conflicts };
|
||||
}
|
||||
|
||||
private _formattingOptions: Promise<FormattingOptions> | undefined = undefined;
|
||||
|
|
|
@ -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,41 @@ 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;
|
||||
getConflicts(): Promise<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,30 @@ 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;
|
||||
}
|
||||
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 'stop': this.service.stop(); return Promise.resolve();
|
||||
case 'getConflicts': return this.service.getConflicts();
|
||||
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,
|
||||
|
|
15
src/vs/vscode.d.ts
vendored
15
src/vs/vscode.d.ts
vendored
|
@ -9518,6 +9518,21 @@ declare module 'vscode' {
|
|||
* @return The resolved debug configuration or undefined or null.
|
||||
*/
|
||||
resolveDebugConfiguration?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult<DebugConfiguration>;
|
||||
|
||||
/**
|
||||
* This hook is directly called after 'resolveDebugConfiguration' but with all variables substituted.
|
||||
* It can be used to resolve or verify a [debug configuration](#DebugConfiguration) by filling in missing values or by adding/changing/removing attributes.
|
||||
* If more than one debug configuration provider is registered for the same type, the 'resolveDebugConfigurationWithSubstitutedVariables' calls are chained
|
||||
* in arbitrary order and the initial debug configuration is piped through the chain.
|
||||
* Returning the value 'undefined' prevents the debug session from starting.
|
||||
* Returning the value 'null' prevents the debug session from starting and opens the underlying debug configuration instead.
|
||||
*
|
||||
* @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup.
|
||||
* @param debugConfiguration The [debug configuration](#DebugConfiguration) to resolve.
|
||||
* @param token A cancellation token.
|
||||
* @return The resolved debug configuration or undefined or null.
|
||||
*/
|
||||
resolveDebugConfigurationWithSubstitutedVariables?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult<DebugConfiguration>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
29
src/vs/vscode.proposed.d.ts
vendored
29
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;
|
||||
}
|
||||
|
@ -792,24 +795,6 @@ declare module 'vscode' {
|
|||
|
||||
//#region Debug:
|
||||
|
||||
export interface DebugConfigurationProvider {
|
||||
|
||||
/**
|
||||
* This hook is directly called after 'resolveDebugConfiguration' but with all variables substituted.
|
||||
* It can be used to resolve or verify a [debug configuration](#DebugConfiguration) by filling in missing values or by adding/changing/removing attributes.
|
||||
* If more than one debug configuration provider is registered for the same type, the 'resolveDebugConfigurationWithSubstitutedVariables' calls are chained
|
||||
* in arbitrary order and the initial debug configuration is piped through the chain.
|
||||
* Returning the value 'undefined' prevents the debug session from starting.
|
||||
* Returning the value 'null' prevents the debug session from starting and opens the underlying debug configuration instead.
|
||||
*
|
||||
* @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup.
|
||||
* @param debugConfiguration The [debug configuration](#DebugConfiguration) to resolve.
|
||||
* @param token A cancellation token.
|
||||
* @return The resolved debug configuration or undefined or null.
|
||||
*/
|
||||
resolveDebugConfigurationWithSubstitutedVariables?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult<DebugConfiguration>;
|
||||
}
|
||||
|
||||
// deprecated
|
||||
|
||||
export interface DebugConfigurationProvider {
|
||||
|
@ -1320,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
|
||||
|
@ -1446,6 +1431,8 @@ declare module 'vscode' {
|
|||
workspaceLanguageValue?: T;
|
||||
workspaceFolderLanguageValue?: T;
|
||||
|
||||
languages?: string[];
|
||||
|
||||
} | undefined;
|
||||
|
||||
/**
|
||||
|
|
|
@ -245,14 +245,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
return extHostTerminalService.getDefaultShell(false, configProvider);
|
||||
},
|
||||
openExternal(uri: URI) {
|
||||
return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.isRemote });
|
||||
return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.authority });
|
||||
},
|
||||
asExternalUri(uri: URI) {
|
||||
if (uri.scheme === initData.environment.appUriScheme) {
|
||||
return extHostUrls.createAppUri(uri);
|
||||
}
|
||||
|
||||
return extHostWindow.asExternalUri(uri, { allowTunneling: !!initData.remote.isRemote });
|
||||
return extHostWindow.asExternalUri(uri, { allowTunneling: !!initData.remote.authority });
|
||||
},
|
||||
get remoteName() {
|
||||
return getRemoteName(initData.remote.authority);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -87,22 +87,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
|||
}
|
||||
|
||||
const configProvider = await this._configurationService.getConfigProvider();
|
||||
const terminalConfig = configProvider.getConfiguration('terminal');
|
||||
|
||||
let shell;
|
||||
const automationShellConfig = terminalConfig.integrated.automationShell;
|
||||
if (automationShellConfig) {
|
||||
if (env.isWindows) {
|
||||
shell = automationShellConfig.windows;
|
||||
} else if (env.isLinux) {
|
||||
shell = automationShellConfig.linux;
|
||||
} else if (env.isMacintosh) {
|
||||
shell = automationShellConfig.osx;
|
||||
}
|
||||
}
|
||||
if (!shell) {
|
||||
shell = this._terminalService.getDefaultShell(true, configProvider);
|
||||
}
|
||||
const shell = this._terminalService.getDefaultShell(true, configProvider);
|
||||
|
||||
if (needNewTerminal || !this._integratedTerminalInstance) {
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
|||
}
|
||||
|
||||
public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string {
|
||||
const fetchSetting = (key: string) => {
|
||||
const fetchSetting = (key: string): { userValue: string | string[] | undefined, value: string | string[] | undefined, defaultValue: string | string[] | undefined } => {
|
||||
const setting = configProvider
|
||||
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
|
||||
.inspect<string | string[]>(key.substr(key.lastIndexOf('.') + 1));
|
||||
|
@ -79,7 +79,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
|||
}
|
||||
|
||||
private _getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string {
|
||||
const fetchSetting = (key: string) => {
|
||||
const fetchSetting = (key: string): { userValue: string | string[] | undefined, value: string | string[] | undefined, defaultValue: string | string[] | undefined } => {
|
||||
const setting = configProvider
|
||||
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
|
||||
.inspect<string | string[]>(key.substr(key.lastIndexOf('.') + 1));
|
||||
|
@ -91,11 +91,11 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
|||
|
||||
private _apiInspectConfigToPlain<T>(
|
||||
config: { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T, workspaceFolderValue?: T } | undefined
|
||||
): { user: T | undefined, value: T | undefined, default: T | undefined } {
|
||||
): { userValue: T | undefined, value: T | undefined, defaultValue: T | undefined } {
|
||||
return {
|
||||
user: config ? config.globalValue : undefined,
|
||||
userValue: config ? config.globalValue : undefined,
|
||||
value: config ? config.workspaceValue : undefined,
|
||||
default: config ? config.defaultValue : undefined,
|
||||
defaultValue: config ? config.defaultValue : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
];
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
|
|||
private registerListeners(): void {
|
||||
this._register(this.hostService.onDidChangeFocus(focused => this.onWindowFocusChange(focused)));
|
||||
this._register(this.editorService.onDidActiveEditorChange(() => this.onDidActiveEditorChange()));
|
||||
this._register(this.filesConfigurationService.onAutoSaveConfigurationChange(() => this.onAutoSaveConfigurationChange()));
|
||||
}
|
||||
|
||||
private onWindowFocusChange(focused: boolean): void {
|
||||
|
@ -83,4 +84,25 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onAutoSaveConfigurationChange(): void {
|
||||
let reason: SaveReason | undefined = undefined;
|
||||
switch (this.filesConfigurationService.getAutoSaveMode()) {
|
||||
case AutoSaveMode.ON_FOCUS_CHANGE:
|
||||
reason = SaveReason.FOCUS_CHANGE;
|
||||
break;
|
||||
case AutoSaveMode.ON_WINDOW_CHANGE:
|
||||
reason = SaveReason.WINDOW_CHANGE;
|
||||
break;
|
||||
case AutoSaveMode.AFTER_SHORT_DELAY:
|
||||
case AutoSaveMode.AFTER_LONG_DELAY:
|
||||
reason = SaveReason.AUTO;
|
||||
break;
|
||||
}
|
||||
|
||||
// Trigger a save-all when auto save is enabled
|
||||
if (reason) {
|
||||
this.editorService.saveAll({ reason });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,8 +59,9 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
|
|||
// Update editors from disk changes
|
||||
this._register(this.fileService.onFileChanges(e => this.onFileChanges(e)));
|
||||
|
||||
// Open editors from dirty text file models
|
||||
this._register(this.textFileService.models.onModelsDirty(e => this.onTextFilesDirty(e)));
|
||||
// Ensure dirty text file 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)));
|
||||
|
||||
// Out of workspace file watchers
|
||||
this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange()));
|
||||
|
@ -263,29 +264,19 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Text File Dirty: Ensure every dirty text file is opened in an editor
|
||||
//#region Text File: Ensure every dirty text file is opened in an editor
|
||||
|
||||
private onTextFilesDirty(events: ReadonlyArray<TextFileModelChangeEvent>): void {
|
||||
|
||||
// If files become dirty but are not opened, we open it in the background unless there are pending to be saved
|
||||
this.doOpenDirtyResourcesInBackground(distinct(events.filter(({ resource }) => {
|
||||
|
||||
// Only dirty models that are not PENDING_SAVE
|
||||
private ensureDirtyTextFilesAreOpened(events: ReadonlyArray<TextFileModelChangeEvent>): void {
|
||||
this.editorService.openEditors(distinct(events.filter(({ resource }) => {
|
||||
const model = this.textFileService.models.get(resource);
|
||||
const shouldOpen = model?.isDirty() && !model.hasState(ModelState.PENDING_SAVE);
|
||||
|
||||
// Only if not open already
|
||||
return shouldOpen && !this.editorService.isOpen({ resource });
|
||||
}).map(event => event.resource), resource => resource.toString()));
|
||||
}
|
||||
|
||||
private doOpenDirtyResourcesInBackground(resources: URI[]): void {
|
||||
this.editorService.openEditors(resources.map(resource => {
|
||||
return {
|
||||
resource,
|
||||
options: { inactive: true, pinned: true, preserveFocus: true }
|
||||
};
|
||||
}));
|
||||
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 => ({
|
||||
resource,
|
||||
options: { inactive: true, pinned: true, preserveFocus: true }
|
||||
})));
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -226,7 +226,8 @@ class TunnelTreeRenderer extends Disposable implements ITreeRenderer<ITunnelGrou
|
|||
}
|
||||
|
||||
private renderTunnel(node: ITunnelItem, templateData: ITunnelTemplateData) {
|
||||
templateData.iconLabel.setLabel(node.label, node.description, { title: node.label + ' - ' + node.description, extraClasses: ['tunnel-view-label'] });
|
||||
const label = node.label + (node.description ? (' - ' + node.description) : '');
|
||||
templateData.iconLabel.setLabel(node.label, node.description, { title: label, extraClasses: ['tunnel-view-label'] });
|
||||
templateData.actionBar.context = node;
|
||||
const contextKeyService = this._register(this.contextKeyService.createScoped());
|
||||
contextKeyService.createKey('view', this.viewId);
|
||||
|
@ -355,10 +356,14 @@ class TunnelItem implements ITunnelItem {
|
|||
get label(): string {
|
||||
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);
|
||||
} else if (this.localAddress) {
|
||||
return nls.localize('remote.tunnelsView.forwardedPortLabel2', "{0} to {1}", this.remotePort, this.localAddress);
|
||||
return nls.localize('remote.tunnelsView.forwardedPortLabel3', "{0} to {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 {
|
||||
return nls.localize('remote.tunnelsView.forwardedPortLabel3', "{0} not forwarded", this.remotePort);
|
||||
return nls.localize('remote.tunnelsView.forwardedPortLabel5', "{0} not forwarded", this.remotePort);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,7 +41,6 @@ import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessi
|
|||
import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenubarControl } from '../browser/parts/titlebar/menubarControl';
|
||||
|
@ -98,7 +97,6 @@ export class ElectronWindow extends Disposable {
|
|||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IElectronService private readonly electronService: IElectronService,
|
||||
|
@ -626,7 +624,7 @@ export class ElectronWindow extends Disposable {
|
|||
// to close the editor while the save still continues in the background. As such
|
||||
// we have to also check if the files to wait for are dirty and if so wait
|
||||
// for them to get saved before deleting the wait marker file.
|
||||
const dirtyFilesToWait = this.textFileService.getDirty(resourcesToWaitFor);
|
||||
const dirtyFilesToWait = resourcesToWaitFor.filter(resourceToWaitFor => this.workingCopyService.isDirty(resourceToWaitFor));
|
||||
if (dirtyFilesToWait.length > 0) {
|
||||
await Promise.all(dirtyFilesToWait.map(async dirtyFileToWait => await this.joinResourceSaved(dirtyFileToWait)));
|
||||
}
|
||||
|
@ -641,13 +639,13 @@ export class ElectronWindow extends Disposable {
|
|||
|
||||
private joinResourceSaved(resource: URI): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
if (!this.textFileService.isDirty(resource)) {
|
||||
if (!this.workingCopyService.isDirty(resource)) {
|
||||
return resolve(); // return early if resource is not dirty
|
||||
}
|
||||
|
||||
// Otherwise resolve promise when resource is saved
|
||||
const listener = this.textFileService.models.onModelSaved(e => {
|
||||
if (isEqual(resource, e.resource)) {
|
||||
const listener = this.workingCopyService.onDidChangeDirty(e => {
|
||||
if (!e.isDirty() && isEqual(resource, e.resource)) {
|
||||
listener.dispose();
|
||||
|
||||
resolve();
|
||||
|
|
|
@ -133,6 +133,9 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
|
|||
@memoize
|
||||
get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); }
|
||||
|
||||
@memoize
|
||||
get argvResource(): URI { return joinPath(this.userRoamingDataHome, 'argv.json'); }
|
||||
|
||||
@memoize
|
||||
get settingsSyncPreviewResource(): URI { return joinPath(this.userRoamingDataHome, '.settings.json'); }
|
||||
|
||||
|
@ -235,8 +238,6 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
|
|||
|
||||
nodeCachedDataDir?: string;
|
||||
|
||||
argvResource!: URI;
|
||||
|
||||
disableCrashReporter!: boolean;
|
||||
|
||||
driverHandle?: string;
|
||||
|
|
|
@ -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,
|
||||
|
|
151
src/vs/workbench/services/remote/common/tunnelService.ts
Normal file
151
src/vs/workbench/services/remote/common/tunnelService.ts
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export abstract class AbstractTunnelService implements ITunnelService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
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;
|
||||
protected readonly _tunnels = new Map</*host*/ string, Map</* port */ number, { refcount: number, readonly value: Promise<RemoteTunnel> }>>();
|
||||
protected _tunnelProvider: ITunnelProvider | undefined;
|
||||
|
||||
public constructor(
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@ILogService protected readonly logService: ILogService
|
||||
) { }
|
||||
|
||||
setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable {
|
||||
if (!provider) {
|
||||
return {
|
||||
dispose: () => { }
|
||||
};
|
||||
}
|
||||
this._tunnelProvider = provider;
|
||||
return {
|
||||
dispose: () => {
|
||||
this._tunnelProvider = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public get tunnels(): Promise<readonly RemoteTunnel[]> {
|
||||
const promises: Promise<RemoteTunnel>[] = [];
|
||||
Array.from(this._tunnels.values()).forEach(portMap => Array.from(portMap.values()).forEach(x => promises.push(x.value)));
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
for (const portMap of this._tunnels.values()) {
|
||||
for (const { value } of portMap.values()) {
|
||||
value.then(tunnel => tunnel.dispose());
|
||||
}
|
||||
portMap.clear();
|
||||
}
|
||||
this._tunnels.clear();
|
||||
}
|
||||
|
||||
openTunnel(remoteHost: string | undefined, remotePort: number, localPort: number): Promise<RemoteTunnel> | undefined {
|
||||
const remoteAuthority = this.environmentService.configuration.remoteAuthority;
|
||||
if (!remoteAuthority) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!remoteHost || (remoteHost === '127.0.0.1')) {
|
||||
remoteHost = 'localhost';
|
||||
}
|
||||
|
||||
const resolvedTunnel = this.retainOrCreateTunnel(remoteAuthority, remoteHost, remotePort, localPort);
|
||||
if (!resolvedTunnel) {
|
||||
return resolvedTunnel;
|
||||
}
|
||||
|
||||
return resolvedTunnel.then(tunnel => {
|
||||
const newTunnel = this.makeTunnel(tunnel);
|
||||
if (tunnel.tunnelRemoteHost !== remoteHost || tunnel.tunnelRemotePort !== remotePort) {
|
||||
this.logService.warn('Created tunnel does not match requirements of requested tunnel. Host or port mismatch.');
|
||||
}
|
||||
this._onTunnelOpened.fire(newTunnel);
|
||||
return newTunnel;
|
||||
});
|
||||
}
|
||||
|
||||
private makeTunnel(tunnel: RemoteTunnel): RemoteTunnel {
|
||||
return {
|
||||
tunnelRemotePort: tunnel.tunnelRemotePort,
|
||||
tunnelRemoteHost: tunnel.tunnelRemoteHost,
|
||||
tunnelLocalPort: tunnel.tunnelLocalPort,
|
||||
localAddress: tunnel.localAddress,
|
||||
dispose: () => {
|
||||
const existingHost = this._tunnels.get(tunnel.tunnelRemoteHost);
|
||||
if (existingHost) {
|
||||
const existing = existingHost.get(tunnel.tunnelRemotePort);
|
||||
if (existing) {
|
||||
existing.refcount--;
|
||||
this.tryDisposeTunnel(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort, existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private async tryDisposeTunnel(remoteHost: string, remotePort: number, tunnel: { refcount: number, readonly value: Promise<RemoteTunnel> }): Promise<void> {
|
||||
if (tunnel.refcount <= 0) {
|
||||
const disposePromise: Promise<void> = tunnel.value.then(tunnel => {
|
||||
tunnel.dispose();
|
||||
this._onTunnelClosed.fire({ host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort });
|
||||
});
|
||||
if (this._tunnels.has(remoteHost)) {
|
||||
this._tunnels.get(remoteHost)!.delete(remotePort);
|
||||
}
|
||||
return disposePromise;
|
||||
}
|
||||
}
|
||||
|
||||
async closeTunnel(remoteHost: string, remotePort: number): Promise<void> {
|
||||
const portMap = this._tunnels.get(remoteHost);
|
||||
if (portMap && portMap.has(remotePort)) {
|
||||
const value = portMap.get(remotePort)!;
|
||||
value.refcount = 0;
|
||||
await this.tryDisposeTunnel(remoteHost, remotePort, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected addTunnelToMap(remoteHost: string, remotePort: number, tunnel: Promise<RemoteTunnel>) {
|
||||
if (!this._tunnels.has(remoteHost)) {
|
||||
this._tunnels.set(remoteHost, new Map());
|
||||
}
|
||||
this._tunnels.get(remoteHost)!.set(remotePort, { refcount: 1, value: tunnel });
|
||||
}
|
||||
|
||||
protected abstract retainOrCreateTunnel(remoteAuthority: string, remoteHost: string, remotePort: number, localPort?: number): Promise<RemoteTunnel> | undefined;
|
||||
}
|
||||
|
||||
export class TunnelService extends AbstractTunnelService {
|
||||
protected retainOrCreateTunnel(remoteAuthority: string, remoteHost: string, remotePort: number, localPort?: number | undefined): Promise<RemoteTunnel> | undefined {
|
||||
const portMap = this._tunnels.get(remoteHost);
|
||||
const existing = portMap ? portMap.get(remotePort) : undefined;
|
||||
if (existing) {
|
||||
++existing.refcount;
|
||||
return existing.value;
|
||||
}
|
||||
|
||||
if (this._tunnelProvider) {
|
||||
const tunnel = this._tunnelProvider.forwardPort({ remoteAddress: { host: remoteHost, port: remotePort } });
|
||||
if (tunnel) {
|
||||
this.addTunnelToMap(remoteHost, remotePort, tunnel);
|
||||
}
|
||||
return tunnel;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
|
@ -5,22 +5,22 @@
|
|||
|
||||
import * as net from 'net';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { connectRemoteAgentTunnel, IConnectionOptions } from 'vs/platform/remote/common/remoteAgentConnection';
|
||||
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { ITunnelService, RemoteTunnel, ITunnelProvider } from 'vs/platform/remote/common/tunnel';
|
||||
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
|
||||
import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory';
|
||||
import { ISignService } from 'vs/platform/sign/common/sign';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { findFreePort } from 'vs/base/node/ports';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { AbstractTunnelService } from 'vs/workbench/services/remote/common/tunnelService';
|
||||
|
||||
export async function createRemoteTunnel(options: IConnectionOptions, tunnelRemotePort: number, tunnelLocalPort?: number): Promise<RemoteTunnel> {
|
||||
const tunnel = new NodeRemoteTunnel(options, tunnelRemotePort, tunnelLocalPort);
|
||||
export async function createRemoteTunnel(options: IConnectionOptions, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort?: number): Promise<RemoteTunnel> {
|
||||
const tunnel = new NodeRemoteTunnel(options, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort);
|
||||
return tunnel.waitForReady();
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel {
|
|||
|
||||
public readonly tunnelRemotePort: number;
|
||||
public tunnelLocalPort!: number;
|
||||
public tunnelRemoteHost: string = 'localhost';
|
||||
public tunnelRemoteHost: string;
|
||||
public localAddress!: string;
|
||||
|
||||
private readonly _options: IConnectionOptions;
|
||||
|
@ -38,7 +38,7 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel {
|
|||
private readonly _listeningListener: () => void;
|
||||
private readonly _connectionListener: (socket: net.Socket) => void;
|
||||
|
||||
constructor(options: IConnectionOptions, tunnelRemotePort: number, private readonly suggestedLocalPort?: number) {
|
||||
constructor(options: IConnectionOptions, tunnelRemoteHost: string, tunnelRemotePort: number, private readonly suggestedLocalPort?: number) {
|
||||
super();
|
||||
this._options = options;
|
||||
this._server = net.createServer();
|
||||
|
@ -51,7 +51,7 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel {
|
|||
this._server.on('connection', this._connectionListener);
|
||||
|
||||
this.tunnelRemotePort = tunnelRemotePort;
|
||||
|
||||
this.tunnelRemoteHost = tunnelRemoteHost;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
@ -98,124 +98,17 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel {
|
|||
}
|
||||
}
|
||||
|
||||
export class TunnelService implements ITunnelService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
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;
|
||||
private readonly _tunnels = new Map</*host*/ string, Map</* port */ number, { refcount: number, readonly value: Promise<RemoteTunnel> }>>();
|
||||
private _tunnelProvider: ITunnelProvider | undefined;
|
||||
|
||||
export class TunnelService extends AbstractTunnelService {
|
||||
public constructor(
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@ILogService logService: ILogService,
|
||||
@IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService,
|
||||
@ISignService private readonly signService: ISignService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
) { }
|
||||
|
||||
setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable {
|
||||
if (!provider) {
|
||||
return {
|
||||
dispose: () => { }
|
||||
};
|
||||
}
|
||||
this._tunnelProvider = provider;
|
||||
return {
|
||||
dispose: () => {
|
||||
this._tunnelProvider = undefined;
|
||||
}
|
||||
};
|
||||
) {
|
||||
super(environmentService, logService);
|
||||
}
|
||||
|
||||
public get tunnels(): Promise<readonly RemoteTunnel[]> {
|
||||
const promises: Promise<RemoteTunnel>[] = [];
|
||||
Array.from(this._tunnels.values()).forEach(portMap => Array.from(portMap.values()).forEach(x => promises.push(x.value)));
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
for (const portMap of this._tunnels.values()) {
|
||||
for (const { value } of portMap.values()) {
|
||||
value.then(tunnel => tunnel.dispose());
|
||||
}
|
||||
portMap.clear();
|
||||
}
|
||||
this._tunnels.clear();
|
||||
}
|
||||
|
||||
openTunnel(remoteHost: string | undefined, remotePort: number, localPort: number): Promise<RemoteTunnel> | undefined {
|
||||
const remoteAuthority = this.environmentService.configuration.remoteAuthority;
|
||||
if (!remoteAuthority) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!remoteHost || (remoteHost === '127.0.0.1')) {
|
||||
remoteHost = 'localhost';
|
||||
}
|
||||
|
||||
const resolvedTunnel = this.retainOrCreateTunnel(remoteAuthority, remoteHost, remotePort, localPort);
|
||||
if (!resolvedTunnel) {
|
||||
return resolvedTunnel;
|
||||
}
|
||||
|
||||
return resolvedTunnel.then(tunnel => {
|
||||
const newTunnel = this.makeTunnel(tunnel);
|
||||
this._onTunnelOpened.fire(newTunnel);
|
||||
return newTunnel;
|
||||
});
|
||||
}
|
||||
|
||||
private makeTunnel(tunnel: RemoteTunnel): RemoteTunnel {
|
||||
return {
|
||||
tunnelRemotePort: tunnel.tunnelRemotePort,
|
||||
tunnelRemoteHost: tunnel.tunnelRemoteHost,
|
||||
tunnelLocalPort: tunnel.tunnelLocalPort,
|
||||
localAddress: tunnel.localAddress,
|
||||
dispose: () => {
|
||||
const existingHost = this._tunnels.get(tunnel.tunnelRemoteHost);
|
||||
if (existingHost) {
|
||||
const existing = existingHost.get(tunnel.tunnelRemotePort);
|
||||
if (existing) {
|
||||
existing.refcount--;
|
||||
this.tryDisposeTunnel(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort, existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private async tryDisposeTunnel(remoteHost: string, remotePort: number, tunnel: { refcount: number, readonly value: Promise<RemoteTunnel> }): Promise<void> {
|
||||
if (tunnel.refcount <= 0) {
|
||||
const disposePromise: Promise<void> = tunnel.value.then(tunnel => {
|
||||
tunnel.dispose();
|
||||
this._onTunnelClosed.fire({ host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort });
|
||||
});
|
||||
if (this._tunnels.has(remoteHost)) {
|
||||
this._tunnels.get(remoteHost)!.delete(remotePort);
|
||||
}
|
||||
return disposePromise;
|
||||
}
|
||||
}
|
||||
|
||||
async closeTunnel(remoteHost: string, remotePort: number): Promise<void> {
|
||||
const portMap = this._tunnels.get(remoteHost);
|
||||
if (portMap && portMap.has(remotePort)) {
|
||||
const value = portMap.get(remotePort)!;
|
||||
value.refcount = 0;
|
||||
await this.tryDisposeTunnel(remoteHost, remotePort, value);
|
||||
}
|
||||
}
|
||||
|
||||
private addTunnelToMap(remoteHost: string, remotePort: number, tunnel: Promise<RemoteTunnel>) {
|
||||
if (!this._tunnels.has(remoteHost)) {
|
||||
this._tunnels.set(remoteHost, new Map());
|
||||
}
|
||||
this._tunnels.get(remoteHost)!.set(remotePort, { refcount: 1, value: tunnel });
|
||||
}
|
||||
|
||||
private retainOrCreateTunnel(remoteAuthority: string, remoteHost: string, remotePort: number, localPort?: number): Promise<RemoteTunnel> | undefined {
|
||||
protected retainOrCreateTunnel(remoteAuthority: string, remoteHost: string, remotePort: number, localPort?: number): Promise<RemoteTunnel> | undefined {
|
||||
const portMap = this._tunnels.get(remoteHost);
|
||||
const existing = portMap ? portMap.get(remotePort) : undefined;
|
||||
if (existing) {
|
||||
|
@ -229,7 +122,7 @@ export class TunnelService implements ITunnelService {
|
|||
this.addTunnelToMap(remoteHost, remotePort, tunnel);
|
||||
}
|
||||
return tunnel;
|
||||
} else if (remoteHost === 'localhost') {
|
||||
} else {
|
||||
const options: IConnectionOptions = {
|
||||
commit: product.commit,
|
||||
socketFactory: nodeSocketFactory,
|
||||
|
@ -243,11 +136,10 @@ export class TunnelService implements ITunnelService {
|
|||
logService: this.logService
|
||||
};
|
||||
|
||||
const tunnel = createRemoteTunnel(options, remotePort, localPort);
|
||||
const tunnel = createRemoteTunnel(options, remoteHost, remotePort, localPort);
|
||||
this.addTunnelToMap(remoteHost, remotePort, tunnel);
|
||||
return tunnel;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,17 +92,6 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
|
|||
// Lifecycle
|
||||
this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(event.reason)));
|
||||
this.lifecycleService.onShutdown(this.dispose, this);
|
||||
|
||||
// Auto save changes
|
||||
this._register(this.filesConfigurationService.onAutoSaveConfigurationChange(() => this.onAutoSaveConfigurationChange()));
|
||||
}
|
||||
|
||||
private onAutoSaveConfigurationChange(): void {
|
||||
|
||||
// save all dirty when enabling auto save
|
||||
if (this.filesConfigurationService.getAutoSaveMode() !== AutoSaveMode.OFF) {
|
||||
this.saveAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected onBeforeShutdown(reason: ShutdownReason): boolean | Promise<boolean> {
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SyncStatus, ISettingsSyncService, IConflictSetting } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
export class SettingsSyncService extends Disposable implements ISettingsSyncService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly channel: IChannel;
|
||||
|
||||
private _status: SyncStatus = SyncStatus.Uninitialized;
|
||||
get status(): SyncStatus { return this._status; }
|
||||
private _onDidChangeStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
|
||||
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangeStatus.event;
|
||||
|
||||
get onDidChangeLocal(): Event<void> { return this.channel.listen('onDidChangeLocal'); }
|
||||
|
||||
constructor(
|
||||
@ISharedProcessService sharedProcessService: ISharedProcessService
|
||||
) {
|
||||
super();
|
||||
this.channel = sharedProcessService.getChannel('settingsSync');
|
||||
this.channel.call<SyncStatus>('_getInitialStatus').then(status => {
|
||||
this.updateStatus(status);
|
||||
this._register(this.channel.listen<SyncStatus>('onDidChangeStatus')(status => this.updateStatus(status)));
|
||||
});
|
||||
}
|
||||
|
||||
sync(_continue?: boolean): Promise<boolean> {
|
||||
return this.channel.call('sync', [_continue]);
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
this.channel.call('stop');
|
||||
}
|
||||
|
||||
getConflicts(): Promise<IConflictSetting[]> {
|
||||
return this.channel.call<IConflictSetting[]>('getConflicts');
|
||||
}
|
||||
|
||||
resolveConflicts(conflicts: { key: string, value: any | undefined }[]): Promise<void> {
|
||||
return this.channel.call('resolveConflicts', [conflicts]);
|
||||
}
|
||||
|
||||
private async updateStatus(status: SyncStatus): Promise<void> {
|
||||
this._status = status;
|
||||
this._onDidChangeStatus.fire(status);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registerSingleton(ISettingsSyncService, SettingsSyncService);
|
|
@ -95,8 +95,6 @@ import { find } from 'vs/base/common/arrays';
|
|||
import { WorkingCopyService, IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { IFilesConfigurationService, FilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IDebugService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { MockDebugService } from 'vs/workbench/contrib/debug/test/common/mockDebug';
|
||||
|
||||
export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
|
||||
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined);
|
||||
|
@ -326,7 +324,6 @@ export function workbenchInstantiationService(): ITestInstantiationService {
|
|||
instantiationService.stub(ICodeEditorService, new TestCodeEditorService());
|
||||
instantiationService.stub(IViewletService, new TestViewletService());
|
||||
instantiationService.stub(IWorkingCopyService, new TestWorkingCopyService());
|
||||
instantiationService.stub(IDebugService, new MockDebugService());
|
||||
|
||||
return instantiationService;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ import 'vs/workbench/services/url/electron-browser/urlService';
|
|||
import 'vs/workbench/services/workspaces/electron-browser/workspacesService';
|
||||
import 'vs/workbench/services/workspaces/electron-browser/workspaceEditingService';
|
||||
import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncService';
|
||||
import 'vs/workbench/services/userDataSync/electron-browser/settingsSyncService';
|
||||
import 'vs/workbench/services/authToken/electron-browser/authTokenService';
|
||||
import 'vs/workbench/services/host/electron-browser/desktopHostService';
|
||||
import 'vs/workbench/services/request/electron-browser/requestService';
|
||||
|
|
|
@ -34,6 +34,26 @@ interface IExternalUriResolver {
|
|||
(uri: URI): Promise<URI>;
|
||||
}
|
||||
|
||||
interface TunnelOptions {
|
||||
remoteAddress: { port: number, host: string };
|
||||
// The desired local port. If this port can't be used, then another will be chosen.
|
||||
localAddressPort?: number;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
interface Tunnel {
|
||||
remoteAddress: { port: number, host: string };
|
||||
//The complete local address(ex. localhost:1234)
|
||||
localAddress: string;
|
||||
// Implementers of Tunnel should fire onDidDispose when dispose is called.
|
||||
onDidDispose: Event<void>;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
interface ITunnelFactory {
|
||||
(tunnelOptions: TunnelOptions): Thenable<Tunnel> | undefined;
|
||||
}
|
||||
|
||||
interface IWorkbenchConstructionOptions {
|
||||
|
||||
/**
|
||||
|
@ -104,6 +124,11 @@ interface IWorkbenchConstructionOptions {
|
|||
*/
|
||||
readonly resolveExternalUri?: IExternalUriResolver;
|
||||
|
||||
/**
|
||||
* Support for creating tunnels.
|
||||
*/
|
||||
readonly tunnelFactory?: ITunnelFactory;
|
||||
|
||||
/**
|
||||
* Current logging level. Default is `LogLevel.Info`.
|
||||
*/
|
||||
|
|
|
@ -60,25 +60,27 @@ import { BackupFileService } from 'vs/workbench/services/backup/common/backupFil
|
|||
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService';
|
||||
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
|
||||
import { NoOpTunnelService } from 'vs/platform/remote/common/tunnelService';
|
||||
import { TunnelService } from 'vs/workbench/services/remote/common/tunnelService';
|
||||
import { ILoggerService } from 'vs/platform/log/common/log';
|
||||
import { FileLoggerService } from 'vs/platform/log/common/fileLogService';
|
||||
import { IAuthTokenService } from 'vs/platform/auth/common/auth';
|
||||
import { AuthTokenService } from 'vs/workbench/services/authToken/browser/authTokenService';
|
||||
import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService, ISettingsSyncService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
|
||||
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
|
||||
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
|
||||
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
|
||||
|
||||
registerSingleton(IExtensionManagementService, ExtensionManagementService);
|
||||
registerSingleton(IBackupFileService, BackupFileService);
|
||||
registerSingleton(IAccessibilityService, BrowserAccessibilityService, true);
|
||||
registerSingleton(IContextMenuService, ContextMenuService);
|
||||
registerSingleton(ITunnelService, NoOpTunnelService, true);
|
||||
registerSingleton(ITunnelService, TunnelService, true);
|
||||
registerSingleton(ILoggerService, FileLoggerService);
|
||||
registerSingleton(IAuthTokenService, AuthTokenService);
|
||||
registerSingleton(IUserDataSyncLogService, UserDataSyncLogService);
|
||||
registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService);
|
||||
registerSingleton(ISettingsSyncService, SettingsSynchroniser);
|
||||
registerSingleton(IUserDataSyncService, UserDataSyncService);
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -9185,10 +9185,10 @@ typescript@^2.6.2:
|
|||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"
|
||||
integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=
|
||||
|
||||
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==
|
||||
|
||||
uc.micro@^1.0.1, uc.micro@^1.0.3:
|
||||
version "1.0.3"
|
||||
|
|
Loading…
Reference in a new issue