mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-01 13:15:22 +01:00
Merge branch 'main' into aiday/positioningInlineChat
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
2023-03-31T12:39:03.753Z
|
||||
2023-06-12T12:55:48.130Z
|
||||
|
||||
@@ -37,6 +37,8 @@ fsevents/test/**
|
||||
@vscode/windows-process-tree/binding.gyp
|
||||
@vscode/windows-process-tree/build/**
|
||||
@vscode/windows-process-tree/src/**
|
||||
@vscode/windows-process-tree/tsconfig.json
|
||||
@vscode/windows-process-tree/tslint.json
|
||||
!@vscode/windows-process-tree/**/*.node
|
||||
|
||||
@vscode/windows-registry/binding.gyp
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
@vscode/windows-mutex/*.md
|
||||
@vscode/windows-mutex/package.json
|
||||
|
||||
@vscode/windows-process-tree/lib/**
|
||||
@vscode/windows-process-tree/**/*.node
|
||||
@vscode/windows-process-tree/LICENSE
|
||||
@vscode/windows-process-tree/package.json
|
||||
@vscode/windows-process-tree/*.md
|
||||
|
||||
@vscode/windows-registry/dist/**
|
||||
@vscode/windows-registry/**/*.node
|
||||
@vscode/windows-registry/*.md
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
@vscode/windows-mutex/*.md
|
||||
@vscode/windows-mutex/package.json
|
||||
|
||||
@vscode/windows-process-tree/lib/**
|
||||
@vscode/windows-process-tree/**/*.node
|
||||
@vscode/windows-process-tree/LICENSE
|
||||
@vscode/windows-process-tree/package.json
|
||||
@vscode/windows-process-tree/*.md
|
||||
|
||||
@vscode/windows-registry/dist/**
|
||||
@vscode/windows-registry/**/*.node
|
||||
@vscode/windows-registry/*.md
|
||||
|
||||
@@ -86,16 +86,10 @@ steps:
|
||||
# TODO@joaomoreno TODO@deepak1556 this should be part of the base image
|
||||
- ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}:
|
||||
- script: |
|
||||
if [ "$VSCODE_ARCH" = "x64" ]; then
|
||||
OS=ubuntu
|
||||
else
|
||||
OS=debian
|
||||
fi
|
||||
|
||||
sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg
|
||||
sudo mkdir -m 0755 -p /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/$OS/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$OS "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
sudo apt update && sudo apt install -y docker-ce-cli
|
||||
displayName: Install Docker client
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
|
||||
@@ -144,11 +144,11 @@ resources:
|
||||
endpoint: VSCodeHub
|
||||
options: --user 0:0 --cap-add SYS_ADMIN
|
||||
- container: vscode-arm64
|
||||
image: vscodehub.azurecr.io/vscode-linux-build-agent:buster-arm64
|
||||
image: vscodehub.azurecr.io/vscode-linux-build-agent:bionic-arm64
|
||||
endpoint: VSCodeHub
|
||||
options: --user 0:0 --cap-add SYS_ADMIN
|
||||
- container: vscode-armhf
|
||||
image: vscodehub.azurecr.io/vscode-linux-build-agent:buster-armhf
|
||||
image: vscodehub.azurecr.io/vscode-linux-build-agent:bionic-armhf
|
||||
endpoint: VSCodeHub
|
||||
options: --user 0:0 --cap-add SYS_ADMIN
|
||||
- container: snapcraft
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -17,7 +17,7 @@ import * as os from 'os';
|
||||
import ts = require('typescript');
|
||||
import * as File from 'vinyl';
|
||||
import * as task from './task';
|
||||
import { Mangler } from './mangleTypeScript';
|
||||
import { Mangler } from './mangle/index';
|
||||
import { RawSourceMap } from 'source-map';
|
||||
const watch = require('./watch');
|
||||
|
||||
@@ -124,21 +124,22 @@ export function compileTask(src: string, out: string, build: boolean, options: {
|
||||
// mangle: TypeScript to TypeScript
|
||||
let mangleStream = es.through();
|
||||
if (build && !options.disableMangle) {
|
||||
let ts2tsMangler = new Mangler(compile.projectPath, (...data) => fancyLog(ansiColors.blue('[mangler]'), ...data));
|
||||
let ts2tsMangler = new Mangler(compile.projectPath, (...data) => fancyLog(ansiColors.blue('[mangler]'), ...data), { mangleExports: true, manglePrivateFields: true });
|
||||
const newContentsByFileName = ts2tsMangler.computeNewFileContents(new Set(['saveState']));
|
||||
mangleStream = es.through(function write(data: File & { sourceMap?: RawSourceMap }) {
|
||||
mangleStream = es.through(async function write(data: File & { sourceMap?: RawSourceMap }) {
|
||||
type TypeScriptExt = typeof ts & { normalizePath(path: string): string };
|
||||
const tsNormalPath = (<TypeScriptExt>ts).normalizePath(data.path);
|
||||
const newContents = newContentsByFileName.get(tsNormalPath);
|
||||
const newContents = (await newContentsByFileName).get(tsNormalPath);
|
||||
if (newContents !== undefined) {
|
||||
data.contents = Buffer.from(newContents.out);
|
||||
data.sourceMap = newContents.sourceMap && JSON.parse(newContents.sourceMap);
|
||||
}
|
||||
this.push(data);
|
||||
}, function end() {
|
||||
this.push(null);
|
||||
}, async function end() {
|
||||
// free resources
|
||||
newContentsByFileName.clear();
|
||||
(await newContentsByFileName).clear();
|
||||
|
||||
this.push(null);
|
||||
(<any>ts2tsMangler) = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -521,6 +521,10 @@
|
||||
{
|
||||
"name": "vs/workbench/contrib/accessibility",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/issue",
|
||||
"project": "vscode-workbench"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,12 +3,15 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { argv } from 'process';
|
||||
import { Mapping, SourceMapGenerator } from 'source-map';
|
||||
import * as ts from 'typescript';
|
||||
import { pathToFileURL } from 'url';
|
||||
import * as workerpool from 'workerpool';
|
||||
import { StaticLanguageServiceHost } from './staticLanguageServiceHost';
|
||||
const buildfile = require('../../../src/buildfile');
|
||||
|
||||
class ShortIdent {
|
||||
|
||||
@@ -17,21 +20,20 @@ class ShortIdent {
|
||||
'import', 'in', 'instanceof', 'let', 'new', 'null', 'return', 'static', 'super', 'switch', 'this', 'throw',
|
||||
'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield']);
|
||||
|
||||
private static _alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
|
||||
private static _alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890$_'.split('');
|
||||
|
||||
private _value = 0;
|
||||
private readonly _isNameTaken: (name: string) => boolean;
|
||||
|
||||
constructor(isNameTaken: (name: string) => boolean) {
|
||||
this._isNameTaken = name => ShortIdent._keywords.has(name) || isNameTaken(name);
|
||||
}
|
||||
constructor(
|
||||
private readonly prefix: string
|
||||
) { }
|
||||
|
||||
next(): string {
|
||||
const candidate = ShortIdent.convert(this._value);
|
||||
next(isNameTaken?: (name: string) => boolean): string {
|
||||
const candidate = this.prefix + ShortIdent.convert(this._value);
|
||||
this._value++;
|
||||
if (this._isNameTaken(candidate)) {
|
||||
if (ShortIdent._keywords.has(candidate) || /^[_0-9]/.test(candidate) || isNameTaken?.(candidate)) {
|
||||
// try again
|
||||
return this.next();
|
||||
return this.next(isNameTaken);
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
@@ -181,8 +183,7 @@ class ClassData {
|
||||
|
||||
data.replacements = new Map();
|
||||
|
||||
const identPool = new ShortIdent(name => {
|
||||
|
||||
const isNameTaken = (name: string) => {
|
||||
// locally taken
|
||||
if (data._isNameTaken(name)) {
|
||||
return true;
|
||||
@@ -212,11 +213,12 @@ class ClassData {
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
const identPool = new ShortIdent('');
|
||||
|
||||
for (const [name, info] of data.fields) {
|
||||
if (ClassData._shouldMangle(info.type)) {
|
||||
const shortName = identPool.next();
|
||||
const shortName = identPool.next(isNameTaken);
|
||||
data.replacements.set(name, shortName);
|
||||
}
|
||||
}
|
||||
@@ -237,12 +239,11 @@ class ClassData {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((<any>this.node.getSourceFile()).identifiers instanceof Map) {
|
||||
// taken by any other usage
|
||||
if ((<any>this.node.getSourceFile()).identifiers.has(name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isNameTakenInFile(this.node, name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -267,59 +268,122 @@ class ClassData {
|
||||
}
|
||||
}
|
||||
|
||||
class StaticLanguageServiceHost implements ts.LanguageServiceHost {
|
||||
|
||||
private readonly _cmdLine: ts.ParsedCommandLine;
|
||||
private readonly _scriptSnapshots: Map<string, ts.IScriptSnapshot> = new Map();
|
||||
|
||||
constructor(readonly projectPath: string) {
|
||||
const existingOptions: Partial<ts.CompilerOptions> = {};
|
||||
const parsed = ts.readConfigFile(projectPath, ts.sys.readFile);
|
||||
if (parsed.error) {
|
||||
throw parsed.error;
|
||||
}
|
||||
this._cmdLine = ts.parseJsonConfigFileContent(parsed.config, ts.sys, path.dirname(projectPath), existingOptions);
|
||||
if (this._cmdLine.errors.length > 0) {
|
||||
throw parsed.error;
|
||||
function isNameTakenInFile(node: ts.Node, name: string): boolean {
|
||||
const identifiers = (<any>node.getSourceFile()).identifiers;
|
||||
if (identifiers instanceof Map) {
|
||||
if (identifiers.has(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
getCompilationSettings(): ts.CompilerOptions {
|
||||
return this._cmdLine.options;
|
||||
return false;
|
||||
}
|
||||
|
||||
const fileIdents = new class {
|
||||
private readonly idents = new ShortIdent('$');
|
||||
|
||||
next() {
|
||||
return this.idents.next();
|
||||
}
|
||||
getScriptFileNames(): string[] {
|
||||
return this._cmdLine.fileNames;
|
||||
};
|
||||
|
||||
const skippedExportMangledFiles = [
|
||||
// Build
|
||||
'css.build',
|
||||
'nls.build',
|
||||
|
||||
// Monaco
|
||||
'editorCommon',
|
||||
'editorOptions',
|
||||
'editorZoom',
|
||||
'standaloneEditor',
|
||||
'standaloneEnums',
|
||||
'standaloneLanguages',
|
||||
|
||||
// Generated
|
||||
'extensionsApiProposals',
|
||||
|
||||
// Module passed around as type
|
||||
'pfs',
|
||||
|
||||
// entry points
|
||||
...[
|
||||
buildfile.entrypoint('vs/server/node/server.main', []),
|
||||
buildfile.entrypoint('vs/workbench/workbench.desktop.main', []),
|
||||
buildfile.base,
|
||||
buildfile.workerExtensionHost,
|
||||
buildfile.workerNotebook,
|
||||
buildfile.workerLanguageDetection,
|
||||
buildfile.workerLocalFileSearch,
|
||||
buildfile.workerProfileAnalysis,
|
||||
buildfile.workbenchDesktop,
|
||||
buildfile.workbenchWeb,
|
||||
buildfile.code
|
||||
].flat().map(x => x.name),
|
||||
];
|
||||
|
||||
const skippedExportMangledProjects = [
|
||||
// Test projects
|
||||
'vscode-api-tests',
|
||||
|
||||
// These projects use webpack to dynamically rewrite imports, which messes up our mangling
|
||||
'configuration-editing',
|
||||
'microsoft-authentication',
|
||||
'github-authentication',
|
||||
'html-language-features/server',
|
||||
];
|
||||
|
||||
const skippedExportMangledSymbols = [
|
||||
// Don't mangle extension entry points
|
||||
'activate',
|
||||
'deactivate',
|
||||
];
|
||||
|
||||
class DeclarationData {
|
||||
|
||||
readonly replacementName: string;
|
||||
|
||||
constructor(
|
||||
readonly fileName: string,
|
||||
readonly node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.EnumDeclaration | ts.VariableDeclaration,
|
||||
private readonly service: ts.LanguageService,
|
||||
) {
|
||||
// Todo: generate replacement names based on usage count, with more used names getting shorter identifiers
|
||||
this.replacementName = fileIdents.next();
|
||||
}
|
||||
getScriptVersion(_fileName: string): string {
|
||||
return '1';
|
||||
}
|
||||
getProjectVersion(): string {
|
||||
return '1';
|
||||
}
|
||||
getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined {
|
||||
let result: ts.IScriptSnapshot | undefined = this._scriptSnapshots.get(fileName);
|
||||
if (result === undefined) {
|
||||
const content = ts.sys.readFile(fileName);
|
||||
if (content === undefined) {
|
||||
return undefined;
|
||||
|
||||
get locations(): Iterable<{ fileName: string; offset: number }> {
|
||||
if (ts.isVariableDeclaration(this.node)) {
|
||||
// If the const aliases any types, we need to rename those too
|
||||
const definitionResult = this.service.getDefinitionAndBoundSpan(this.fileName, this.node.name.getStart());
|
||||
if (definitionResult?.definitions && definitionResult.definitions.length > 1) {
|
||||
return definitionResult.definitions.map(x => ({ fileName: x.fileName, offset: x.textSpan.start }));
|
||||
}
|
||||
result = ts.ScriptSnapshot.fromString(content);
|
||||
this._scriptSnapshots.set(fileName, result);
|
||||
}
|
||||
return result;
|
||||
|
||||
return [{
|
||||
fileName: this.fileName,
|
||||
offset: this.node.name!.getStart()
|
||||
}];
|
||||
}
|
||||
getCurrentDirectory(): string {
|
||||
return path.dirname(this.projectPath);
|
||||
|
||||
shouldMangle(newName: string): boolean {
|
||||
const currentName = this.node.name!.getText();
|
||||
if (currentName.startsWith('$') || skippedExportMangledSymbols.includes(currentName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// New name is longer the existing one :'(
|
||||
if (newName.length >= currentName.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't mangle functions we've explicitly opted out
|
||||
if (this.node.getFullText().includes('@skipMangle')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
getDefaultLibFileName(options: ts.CompilerOptions): string {
|
||||
return ts.getDefaultLibFilePath(options);
|
||||
}
|
||||
directoryExists = ts.sys.directoryExists;
|
||||
getDirectories = ts.sys.getDirectories;
|
||||
fileExists = ts.sys.fileExists;
|
||||
readFile = ts.sys.readFile;
|
||||
readDirectory = ts.sys.readDirectory;
|
||||
// this is necessary to make source references work.
|
||||
realpath = ts.sys.realpath;
|
||||
}
|
||||
|
||||
export interface MangleOutput {
|
||||
@@ -339,26 +403,82 @@ export interface MangleOutput {
|
||||
export class Mangler {
|
||||
|
||||
private readonly allClassDataByKey = new Map<string, ClassData>();
|
||||
private readonly allExportedSymbols = new Set<DeclarationData>();
|
||||
|
||||
private readonly service: ts.LanguageService;
|
||||
private readonly renameWorkerPool: workerpool.WorkerPool;
|
||||
|
||||
constructor(readonly projectPath: string, readonly log: typeof console.log = () => { }) {
|
||||
constructor(
|
||||
private readonly projectPath: string,
|
||||
private readonly log: typeof console.log = () => { },
|
||||
private readonly config: { readonly manglePrivateFields: boolean; readonly mangleExports: boolean },
|
||||
) {
|
||||
this.service = ts.createLanguageService(new StaticLanguageServiceHost(projectPath));
|
||||
|
||||
this.renameWorkerPool = workerpool.pool(path.join(__dirname, 'renameWorker.js'), {
|
||||
maxWorkers: 2,
|
||||
minWorkers: 'max'
|
||||
});
|
||||
}
|
||||
|
||||
computeNewFileContents(strictImplicitPublicHandling?: Set<string>): Map<string, MangleOutput> {
|
||||
async computeNewFileContents(strictImplicitPublicHandling?: Set<string>): Promise<Map<string, MangleOutput>> {
|
||||
|
||||
// STEP: find all classes and their field info
|
||||
// STEP:
|
||||
// - Find all classes and their field info.
|
||||
// - Find exported symbols.
|
||||
|
||||
const visit = (node: ts.Node): void => {
|
||||
if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) {
|
||||
const anchor = node.name ?? node;
|
||||
const key = `${node.getSourceFile().fileName}|${anchor.getStart()}`;
|
||||
if (this.allClassDataByKey.has(key)) {
|
||||
throw new Error('DUPE?');
|
||||
if (this.config.manglePrivateFields) {
|
||||
if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) {
|
||||
const anchor = node.name ?? node;
|
||||
const key = `${node.getSourceFile().fileName}|${anchor.getStart()}`;
|
||||
if (this.allClassDataByKey.has(key)) {
|
||||
throw new Error('DUPE?');
|
||||
}
|
||||
this.allClassDataByKey.set(key, new ClassData(node.getSourceFile().fileName, node));
|
||||
}
|
||||
this.allClassDataByKey.set(key, new ClassData(node.getSourceFile().fileName, node));
|
||||
}
|
||||
|
||||
if (this.config.mangleExports) {
|
||||
// Find exported classes, functions, and vars
|
||||
if (
|
||||
(
|
||||
// Exported class
|
||||
ts.isClassDeclaration(node)
|
||||
&& hasModifier(node, ts.SyntaxKind.ExportKeyword)
|
||||
&& node.name
|
||||
) || (
|
||||
// Exported function
|
||||
ts.isFunctionDeclaration(node)
|
||||
&& ts.isSourceFile(node.parent)
|
||||
&& hasModifier(node, ts.SyntaxKind.ExportKeyword)
|
||||
&& node.name && node.body // On named function and not on the overload
|
||||
) || (
|
||||
// Exported variable
|
||||
ts.isVariableDeclaration(node)
|
||||
&& hasModifier(node.parent.parent, ts.SyntaxKind.ExportKeyword) // Variable statement is exported
|
||||
&& ts.isSourceFile(node.parent.parent.parent)
|
||||
)
|
||||
|
||||
// Disabled for now because we need to figure out how to handle
|
||||
// enums that are used in monaco or extHost interfaces.
|
||||
/* || (
|
||||
// Exported enum
|
||||
ts.isEnumDeclaration(node)
|
||||
&& ts.isSourceFile(node.parent)
|
||||
&& hasModifier(node, ts.SyntaxKind.ExportKeyword)
|
||||
&& !hasModifier(node, ts.SyntaxKind.ConstKeyword) // Don't bother mangling const enums because these are inlined
|
||||
&& node.name
|
||||
*/
|
||||
) {
|
||||
if (isInAmbientContext(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.allExportedSymbols.add(new DeclarationData(node.getSourceFile().fileName, node, this.service));
|
||||
}
|
||||
}
|
||||
|
||||
ts.forEachChild(node, visit);
|
||||
};
|
||||
|
||||
@@ -367,7 +487,7 @@ export class Mangler {
|
||||
ts.forEachChild(file, visit);
|
||||
}
|
||||
}
|
||||
this.log(`Done collecting classes: ${this.allClassDataByKey.size}`);
|
||||
this.log(`Done collecting. Classes: ${this.allClassDataByKey.size}. Exported symbols: ${this.allExportedSymbols.size}`);
|
||||
|
||||
|
||||
// STEP: connect sub and super-types
|
||||
@@ -433,9 +553,11 @@ export class Mangler {
|
||||
for (const data of this.allClassDataByKey.values()) {
|
||||
ClassData.fillInReplacement(data);
|
||||
}
|
||||
this.log(`Done creating replacements`);
|
||||
this.log(`Done creating class replacements`);
|
||||
|
||||
// STEP: prepare rename edits
|
||||
this.log(`Starting prepare rename edits`);
|
||||
|
||||
type Edit = { newText: string; offset: number; length: number };
|
||||
const editsByFile = new Map<string, Edit[]>();
|
||||
|
||||
@@ -447,9 +569,24 @@ export class Mangler {
|
||||
edits.push(edit);
|
||||
}
|
||||
};
|
||||
const appendRename = (newText: string, loc: ts.RenameLocation) => {
|
||||
appendEdit(loc.fileName, {
|
||||
newText: (loc.prefixText || '') + newText + (loc.suffixText || ''),
|
||||
offset: loc.textSpan.start,
|
||||
length: loc.textSpan.length
|
||||
});
|
||||
};
|
||||
|
||||
type RenameFn = (projectName: string, fileName: string, pos: number) => ts.RenameLocation[];
|
||||
|
||||
const renameResults: Array<Promise<{ readonly newName: string; readonly locations: readonly ts.RenameLocation[] }>> = [];
|
||||
|
||||
const queueRename = (fileName: string, pos: number, newName: string) => {
|
||||
renameResults.push(Promise.resolve(this.renameWorkerPool.exec<RenameFn>('findRenameLocations', [this.projectPath, fileName, pos]))
|
||||
.then((locations) => ({ newName, locations })));
|
||||
};
|
||||
|
||||
for (const data of this.allClassDataByKey.values()) {
|
||||
|
||||
if (hasModifier(data.node, ts.SyntaxKind.DeclareKeyword)) {
|
||||
continue;
|
||||
}
|
||||
@@ -469,18 +606,39 @@ export class Mangler {
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
const newText = data.lookupShortName(name);
|
||||
const locations = this.service.findRenameLocations(data.fileName, info.pos, false, false, true) ?? [];
|
||||
for (const loc of locations) {
|
||||
appendEdit(loc.fileName, {
|
||||
newText: (loc.prefixText || '') + newText + (loc.suffixText || ''),
|
||||
offset: loc.textSpan.start,
|
||||
length: loc.textSpan.length
|
||||
});
|
||||
}
|
||||
const newName = data.lookupShortName(name);
|
||||
queueRename(data.fileName, info.pos, newName);
|
||||
}
|
||||
}
|
||||
|
||||
for (const data of this.allExportedSymbols.values()) {
|
||||
if (data.fileName.endsWith('.d.ts')
|
||||
|| skippedExportMangledProjects.some(proj => data.fileName.includes(proj))
|
||||
|| skippedExportMangledFiles.some(file => data.fileName.endsWith(file + '.ts'))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!data.shouldMangle(data.replacementName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newText = data.replacementName;
|
||||
for (const { fileName, offset } of data.locations) {
|
||||
queueRename(fileName, offset, newText);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(renameResults).then((result) => {
|
||||
for (const { newName, locations } of result) {
|
||||
for (const loc of locations) {
|
||||
appendRename(newName, loc);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await this.renameWorkerPool.terminate();
|
||||
|
||||
this.log(`Done preparing edits: ${editsByFile.size} files`);
|
||||
|
||||
// STEP: apply all rename edits (per file)
|
||||
@@ -579,17 +737,32 @@ function hasModifier(node: ts.Node, kind: ts.SyntaxKind) {
|
||||
return Boolean(modifiers?.find(mode => mode.kind === kind));
|
||||
}
|
||||
|
||||
function isInAmbientContext(node: ts.Node): boolean {
|
||||
for (let p = node.parent; p; p = p.parent) {
|
||||
if (ts.isModuleDeclaration(p)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function normalize(path: string): string {
|
||||
return path.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
async function _run() {
|
||||
|
||||
const projectPath = path.join(__dirname, '../../src/tsconfig.json');
|
||||
const projectBase = path.dirname(projectPath);
|
||||
const root = path.join(__dirname, '..', '..', '..');
|
||||
const projectBase = path.join(root, 'src');
|
||||
const projectPath = path.join(projectBase, 'tsconfig.json');
|
||||
const newProjectBase = path.join(path.dirname(projectBase), path.basename(projectBase) + '2');
|
||||
|
||||
for await (const [fileName, contents] of new Mangler(projectPath, console.log).computeNewFileContents(new Set(['saveState']))) {
|
||||
fs.cpSync(projectBase, newProjectBase, { recursive: true });
|
||||
|
||||
const mangler = new Mangler(projectPath, console.log, {
|
||||
mangleExports: true,
|
||||
manglePrivateFields: true,
|
||||
});
|
||||
for (const [fileName, contents] of await mangler.computeNewFileContents(new Set(['saveState']))) {
|
||||
const newFilePath = path.join(newProjectBase, path.relative(projectBase, fileName));
|
||||
await fs.promises.mkdir(path.dirname(newFilePath), { recursive: true });
|
||||
await fs.promises.writeFile(newFilePath, contents.out);
|
||||
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ts = require("typescript");
|
||||
const workerpool = require("workerpool");
|
||||
const staticLanguageServiceHost_1 = require("./staticLanguageServiceHost");
|
||||
let service; // = ts.createLanguageService(new StaticLanguageServiceHost(projectPath));
|
||||
function findRenameLocations(projectPath, fileName, position) {
|
||||
if (!service) {
|
||||
service = ts.createLanguageService(new staticLanguageServiceHost_1.StaticLanguageServiceHost(projectPath));
|
||||
}
|
||||
return service.findRenameLocations(fileName, position, false, false, true) ?? [];
|
||||
}
|
||||
workerpool.worker({
|
||||
findRenameLocations
|
||||
});
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVuYW1lV29ya2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsicmVuYW1lV29ya2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7QUFFaEcsaUNBQWlDO0FBQ2pDLHlDQUF5QztBQUN6QywyRUFBd0U7QUFFeEUsSUFBSSxPQUF1QyxDQUFDLENBQUEsMEVBQTBFO0FBRXRILFNBQVMsbUJBQW1CLENBQzNCLFdBQW1CLEVBQ25CLFFBQWdCLEVBQ2hCLFFBQWdCO0lBRWhCLElBQUksQ0FBQyxPQUFPLEVBQUU7UUFDYixPQUFPLEdBQUcsRUFBRSxDQUFDLHFCQUFxQixDQUFDLElBQUkscURBQXlCLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQztLQUMvRTtJQUVELE9BQU8sT0FBTyxDQUFDLG1CQUFtQixDQUFDLFFBQVEsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7QUFDbEYsQ0FBQztBQUVELFVBQVUsQ0FBQyxNQUFNLENBQUM7SUFDakIsbUJBQW1CO0NBQ25CLENBQUMsQ0FBQyJ9
|
||||
@@ -0,0 +1,26 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as workerpool from 'workerpool';
|
||||
import { StaticLanguageServiceHost } from './staticLanguageServiceHost';
|
||||
|
||||
let service: ts.LanguageService | undefined;// = ts.createLanguageService(new StaticLanguageServiceHost(projectPath));
|
||||
|
||||
function findRenameLocations(
|
||||
projectPath: string,
|
||||
fileName: string,
|
||||
position: number,
|
||||
): readonly ts.RenameLocation[] {
|
||||
if (!service) {
|
||||
service = ts.createLanguageService(new StaticLanguageServiceHost(projectPath));
|
||||
}
|
||||
|
||||
return service.findRenameLocations(fileName, position, false, false, true) ?? [];
|
||||
}
|
||||
|
||||
workerpool.worker({
|
||||
findRenameLocations
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.StaticLanguageServiceHost = void 0;
|
||||
const ts = require("typescript");
|
||||
const path = require("path");
|
||||
class StaticLanguageServiceHost {
|
||||
projectPath;
|
||||
_cmdLine;
|
||||
_scriptSnapshots = new Map();
|
||||
constructor(projectPath) {
|
||||
this.projectPath = projectPath;
|
||||
const existingOptions = {};
|
||||
const parsed = ts.readConfigFile(projectPath, ts.sys.readFile);
|
||||
if (parsed.error) {
|
||||
throw parsed.error;
|
||||
}
|
||||
this._cmdLine = ts.parseJsonConfigFileContent(parsed.config, ts.sys, path.dirname(projectPath), existingOptions);
|
||||
if (this._cmdLine.errors.length > 0) {
|
||||
throw parsed.error;
|
||||
}
|
||||
}
|
||||
getCompilationSettings() {
|
||||
return this._cmdLine.options;
|
||||
}
|
||||
getScriptFileNames() {
|
||||
return this._cmdLine.fileNames;
|
||||
}
|
||||
getScriptVersion(_fileName) {
|
||||
return '1';
|
||||
}
|
||||
getProjectVersion() {
|
||||
return '1';
|
||||
}
|
||||
getScriptSnapshot(fileName) {
|
||||
let result = this._scriptSnapshots.get(fileName);
|
||||
if (result === undefined) {
|
||||
const content = ts.sys.readFile(fileName);
|
||||
if (content === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
result = ts.ScriptSnapshot.fromString(content);
|
||||
this._scriptSnapshots.set(fileName, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
getCurrentDirectory() {
|
||||
return path.dirname(this.projectPath);
|
||||
}
|
||||
getDefaultLibFileName(options) {
|
||||
return ts.getDefaultLibFilePath(options);
|
||||
}
|
||||
directoryExists = ts.sys.directoryExists;
|
||||
getDirectories = ts.sys.getDirectories;
|
||||
fileExists = ts.sys.fileExists;
|
||||
readFile = ts.sys.readFile;
|
||||
readDirectory = ts.sys.readDirectory;
|
||||
// this is necessary to make source references work.
|
||||
realpath = ts.sys.realpath;
|
||||
}
|
||||
exports.StaticLanguageServiceHost = StaticLanguageServiceHost;
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdGljTGFuZ3VhZ2VTZXJ2aWNlSG9zdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInN0YXRpY0xhbmd1YWdlU2VydmljZUhvc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOzs7QUFFaEcsaUNBQWlDO0FBQ2pDLDZCQUE2QjtBQUU3QixNQUFhLHlCQUF5QjtJQUtoQjtJQUhKLFFBQVEsQ0FBdUI7SUFDL0IsZ0JBQWdCLEdBQW9DLElBQUksR0FBRyxFQUFFLENBQUM7SUFFL0UsWUFBcUIsV0FBbUI7UUFBbkIsZ0JBQVcsR0FBWCxXQUFXLENBQVE7UUFDdkMsTUFBTSxlQUFlLEdBQWdDLEVBQUUsQ0FBQztRQUN4RCxNQUFNLE1BQU0sR0FBRyxFQUFFLENBQUMsY0FBYyxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQy9ELElBQUksTUFBTSxDQUFDLEtBQUssRUFBRTtZQUNqQixNQUFNLE1BQU0sQ0FBQyxLQUFLLENBQUM7U0FDbkI7UUFDRCxJQUFJLENBQUMsUUFBUSxHQUFHLEVBQUUsQ0FBQywwQkFBMEIsQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsRUFBRSxlQUFlLENBQUMsQ0FBQztRQUNqSCxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7WUFDcEMsTUFBTSxNQUFNLENBQUMsS0FBSyxDQUFDO1NBQ25CO0lBQ0YsQ0FBQztJQUNELHNCQUFzQjtRQUNyQixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDO0lBQzlCLENBQUM7SUFDRCxrQkFBa0I7UUFDakIsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQztJQUNoQyxDQUFDO0lBQ0QsZ0JBQWdCLENBQUMsU0FBaUI7UUFDakMsT0FBTyxHQUFHLENBQUM7SUFDWixDQUFDO0lBQ0QsaUJBQWlCO1FBQ2hCLE9BQU8sR0FBRyxDQUFDO0lBQ1osQ0FBQztJQUNELGlCQUFpQixDQUFDLFFBQWdCO1FBQ2pDLElBQUksTUFBTSxHQUFtQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2pGLElBQUksTUFBTSxLQUFLLFNBQVMsRUFBRTtZQUN6QixNQUFNLE9BQU8sR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUMxQyxJQUFJLE9BQU8sS0FBSyxTQUFTLEVBQUU7Z0JBQzFCLE9BQU8sU0FBUyxDQUFDO2FBQ2pCO1lBQ0QsTUFBTSxHQUFHLEVBQUUsQ0FBQyxjQUFjLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQy9DLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1NBQzVDO1FBQ0QsT0FBTyxNQUFNLENBQUM7SUFDZixDQUFDO0lBQ0QsbUJBQW1CO1FBQ2xCLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDdkMsQ0FBQztJQUNELHFCQUFxQixDQUFDLE9BQTJCO1FBQ2hELE9BQU8sRUFBRSxDQUFDLHFCQUFxQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzFDLENBQUM7SUFDRCxlQUFlLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUM7SUFDekMsY0FBYyxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDO0lBQ3ZDLFVBQVUsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQztJQUMvQixRQUFRLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUM7SUFDM0IsYUFBYSxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDO0lBQ3JDLG9EQUFvRDtJQUNwRCxRQUFRLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUM7Q0FDM0I7QUFyREQsOERBcURDIn0=
|
||||
@@ -0,0 +1,62 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as path from 'path';
|
||||
|
||||
export class StaticLanguageServiceHost implements ts.LanguageServiceHost {
|
||||
|
||||
private readonly _cmdLine: ts.ParsedCommandLine;
|
||||
private readonly _scriptSnapshots: Map<string, ts.IScriptSnapshot> = new Map();
|
||||
|
||||
constructor(readonly projectPath: string) {
|
||||
const existingOptions: Partial<ts.CompilerOptions> = {};
|
||||
const parsed = ts.readConfigFile(projectPath, ts.sys.readFile);
|
||||
if (parsed.error) {
|
||||
throw parsed.error;
|
||||
}
|
||||
this._cmdLine = ts.parseJsonConfigFileContent(parsed.config, ts.sys, path.dirname(projectPath), existingOptions);
|
||||
if (this._cmdLine.errors.length > 0) {
|
||||
throw parsed.error;
|
||||
}
|
||||
}
|
||||
getCompilationSettings(): ts.CompilerOptions {
|
||||
return this._cmdLine.options;
|
||||
}
|
||||
getScriptFileNames(): string[] {
|
||||
return this._cmdLine.fileNames;
|
||||
}
|
||||
getScriptVersion(_fileName: string): string {
|
||||
return '1';
|
||||
}
|
||||
getProjectVersion(): string {
|
||||
return '1';
|
||||
}
|
||||
getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined {
|
||||
let result: ts.IScriptSnapshot | undefined = this._scriptSnapshots.get(fileName);
|
||||
if (result === undefined) {
|
||||
const content = ts.sys.readFile(fileName);
|
||||
if (content === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
result = ts.ScriptSnapshot.fromString(content);
|
||||
this._scriptSnapshots.set(fileName, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
getCurrentDirectory(): string {
|
||||
return path.dirname(this.projectPath);
|
||||
}
|
||||
getDefaultLibFileName(options: ts.CompilerOptions): string {
|
||||
return ts.getDefaultLibFilePath(options);
|
||||
}
|
||||
directoryExists = ts.sys.directoryExists;
|
||||
getDirectories = ts.sys.getDirectories;
|
||||
fileExists = ts.sys.fileExists;
|
||||
readFile = ts.sys.readFile;
|
||||
readDirectory = ts.sys.readDirectory;
|
||||
// this is necessary to make source references work.
|
||||
realpath = ts.sys.realpath;
|
||||
}
|
||||
+169
-22
File diff suppressed because one or more lines are too long
@@ -63,9 +63,10 @@ exports.referenceGeneratedDepsByArch = {
|
||||
'libatk-bridge2.0-0 (>= 2.5.3)',
|
||||
'libatk1.0-0 (>= 2.2.0)',
|
||||
'libatspi2.0-0 (>= 2.9.90)',
|
||||
'libc6 (>= 2.15)',
|
||||
'libc6 (>= 2.17)',
|
||||
'libc6 (>= 2.28)',
|
||||
'libc6 (>= 2.4)',
|
||||
'libc6 (>= 2.8)',
|
||||
'libc6 (>= 2.9)',
|
||||
'libcairo2 (>= 1.6.0)',
|
||||
'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3',
|
||||
@@ -73,7 +74,7 @@ exports.referenceGeneratedDepsByArch = {
|
||||
'libdrm2 (>= 2.4.60)',
|
||||
'libexpat1 (>= 2.0.1)',
|
||||
'libgbm1 (>= 17.1.0~rc2)',
|
||||
'libglib2.0-0 (>= 2.16.0)',
|
||||
'libglib2.0-0 (>= 2.12.0)',
|
||||
'libglib2.0-0 (>= 2.39.4)',
|
||||
'libgtk-3-0 (>= 3.9.10)',
|
||||
'libgtk-3-0 (>= 3.9.10) | libgtk-4-1',
|
||||
@@ -82,7 +83,6 @@ exports.referenceGeneratedDepsByArch = {
|
||||
'libnss3 (>= 3.26)',
|
||||
'libpango-1.0-0 (>= 1.14.0)',
|
||||
'libsecret-1-0 (>= 0.18)',
|
||||
'libstdc++6 (>= 4.1.1)',
|
||||
'libstdc++6 (>= 5)',
|
||||
'libstdc++6 (>= 5.2)',
|
||||
'libstdc++6 (>= 6)',
|
||||
@@ -105,14 +105,13 @@ exports.referenceGeneratedDepsByArch = {
|
||||
'libatk1.0-0 (>= 2.2.0)',
|
||||
'libatspi2.0-0 (>= 2.9.90)',
|
||||
'libc6 (>= 2.17)',
|
||||
'libc6 (>= 2.28)',
|
||||
'libcairo2 (>= 1.6.0)',
|
||||
'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3',
|
||||
'libdbus-1-3 (>= 1.0.2)',
|
||||
'libdrm2 (>= 2.4.60)',
|
||||
'libexpat1 (>= 2.0.1)',
|
||||
'libgbm1 (>= 17.1.0~rc2)',
|
||||
'libglib2.0-0 (>= 2.16.0)',
|
||||
'libglib2.0-0 (>= 2.12.0)',
|
||||
'libglib2.0-0 (>= 2.39.4)',
|
||||
'libgtk-3-0 (>= 3.9.10)',
|
||||
'libgtk-3-0 (>= 3.9.10) | libgtk-4-1',
|
||||
@@ -121,7 +120,6 @@ exports.referenceGeneratedDepsByArch = {
|
||||
'libnss3 (>= 3.26)',
|
||||
'libpango-1.0-0 (>= 1.14.0)',
|
||||
'libsecret-1-0 (>= 0.18)',
|
||||
'libstdc++6 (>= 4.1.1)',
|
||||
'libstdc++6 (>= 5)',
|
||||
'libstdc++6 (>= 5.2)',
|
||||
'libstdc++6 (>= 6)',
|
||||
@@ -138,4 +136,4 @@ exports.referenceGeneratedDepsByArch = {
|
||||
'xdg-utils (>= 1.0.2)'
|
||||
]
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLGtIQUFrSDtBQUNsSCw0REFBNEQ7QUFDL0MsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHFDQUFxQztJQUNyQyxtQkFBbUI7SUFDbkIsc0RBQXNEO0lBQ3RELHNCQUFzQixDQUFDLGlCQUFpQjtDQUN4QyxDQUFDO0FBRUYsb0hBQW9IO0FBQ3BILDBDQUEwQztBQUMxQyw4REFBOEQ7QUFDakQsUUFBQSxlQUFlLEdBQUc7SUFDOUIsWUFBWSxDQUFDLHlFQUF5RTtDQUN0RixDQUFDO0FBRVcsUUFBQSw0QkFBNEIsR0FBRztJQUMzQyxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsa0JBQWtCO1FBQ2xCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQiwwQkFBMEI7UUFDMUIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyx3QkFBd0I7UUFDeEIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQiw0QkFBNEI7UUFDNUIseUJBQXlCO1FBQ3pCLFVBQVU7UUFDViwwQkFBMEI7UUFDMUIsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsVUFBVTtRQUNWLFlBQVk7UUFDWiwwQkFBMEI7UUFDMUIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7S0FDdEI7SUFDRCxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsZ0JBQWdCO1FBQ2hCLGdCQUFnQjtRQUNoQixzQkFBc0I7UUFDdEIsc0RBQXNEO1FBQ3RELHlCQUF5QjtRQUN6QixxQkFBcUI7UUFDckIsc0JBQXNCO1FBQ3RCLHlCQUF5QjtRQUN6QiwwQkFBMEI7UUFDMUIsMEJBQTBCO1FBQzFCLHdCQUF3QjtRQUN4QixxQ0FBcUM7UUFDckMsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLHlCQUF5QjtRQUN6Qix1QkFBdUI7UUFDdkIsbUJBQW1CO1FBQ25CLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsVUFBVTtRQUNWLDBCQUEwQjtRQUMxQixvQkFBb0I7UUFDcEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QixVQUFVO1FBQ1YsWUFBWTtRQUNaLDBCQUEwQjtRQUMxQixhQUFhO1FBQ2IsWUFBWTtRQUNaLHNCQUFzQjtLQUN0QjtJQUNELE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLGlCQUFpQjtRQUNqQixzQkFBc0I7UUFDdEIsc0RBQXNEO1FBQ3RELHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsc0JBQXNCO1FBQ3RCLHlCQUF5QjtRQUN6QiwwQkFBMEI7UUFDMUIsMEJBQTBCO1FBQzFCLHdCQUF3QjtRQUN4QixxQ0FBcUM7UUFDckMsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLHlCQUF5QjtRQUN6Qix1QkFBdUI7UUFDdkIsbUJBQW1CO1FBQ25CLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsVUFBVTtRQUNWLDBCQUEwQjtRQUMxQixvQkFBb0I7UUFDcEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QixVQUFVO1FBQ1YsWUFBWTtRQUNaLDBCQUEwQjtRQUMxQixhQUFhO1FBQ2IsWUFBWTtRQUNaLHNCQUFzQjtLQUN0QjtDQUNELENBQUMifQ==
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLGtIQUFrSDtBQUNsSCw0REFBNEQ7QUFDL0MsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHFDQUFxQztJQUNyQyxtQkFBbUI7SUFDbkIsc0RBQXNEO0lBQ3RELHNCQUFzQixDQUFDLGlCQUFpQjtDQUN4QyxDQUFDO0FBRUYsb0hBQW9IO0FBQ3BILDBDQUEwQztBQUMxQyw4REFBOEQ7QUFDakQsUUFBQSxlQUFlLEdBQUc7SUFDOUIsWUFBWSxDQUFDLHlFQUF5RTtDQUN0RixDQUFDO0FBRVcsUUFBQSw0QkFBNEIsR0FBRztJQUMzQyxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsa0JBQWtCO1FBQ2xCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQiwwQkFBMEI7UUFDMUIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyx3QkFBd0I7UUFDeEIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQiw0QkFBNEI7UUFDNUIseUJBQXlCO1FBQ3pCLFVBQVU7UUFDViwwQkFBMEI7UUFDMUIsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsVUFBVTtRQUNWLFlBQVk7UUFDWiwwQkFBMEI7UUFDMUIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7S0FDdEI7SUFDRCxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsZ0JBQWdCO1FBQ2hCLGdCQUFnQjtRQUNoQixnQkFBZ0I7UUFDaEIsc0JBQXNCO1FBQ3RCLHNEQUFzRDtRQUN0RCx5QkFBeUI7UUFDekIscUJBQXFCO1FBQ3JCLHNCQUFzQjtRQUN0Qix5QkFBeUI7UUFDekIsMEJBQTBCO1FBQzFCLDBCQUEwQjtRQUMxQix3QkFBd0I7UUFDeEIscUNBQXFDO1FBQ3JDLHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLDRCQUE0QjtRQUM1Qix5QkFBeUI7UUFDekIsbUJBQW1CO1FBQ25CLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsVUFBVTtRQUNWLDBCQUEwQjtRQUMxQixvQkFBb0I7UUFDcEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QixVQUFVO1FBQ1YsWUFBWTtRQUNaLDBCQUEwQjtRQUMxQixhQUFhO1FBQ2IsWUFBWTtRQUNaLHNCQUFzQjtLQUN0QjtJQUNELE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQiwwQkFBMEI7UUFDMUIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyx3QkFBd0I7UUFDeEIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQiw0QkFBNEI7UUFDNUIseUJBQXlCO1FBQ3pCLG1CQUFtQjtRQUNuQixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLFVBQVU7UUFDViwwQkFBMEI7UUFDMUIsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsVUFBVTtRQUNWLFlBQVk7UUFDWiwwQkFBMEI7UUFDMUIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7S0FDdEI7Q0FDRCxDQUFDIn0=
|
||||
@@ -63,9 +63,10 @@ export const referenceGeneratedDepsByArch = {
|
||||
'libatk-bridge2.0-0 (>= 2.5.3)',
|
||||
'libatk1.0-0 (>= 2.2.0)',
|
||||
'libatspi2.0-0 (>= 2.9.90)',
|
||||
'libc6 (>= 2.15)',
|
||||
'libc6 (>= 2.17)',
|
||||
'libc6 (>= 2.28)',
|
||||
'libc6 (>= 2.4)',
|
||||
'libc6 (>= 2.8)',
|
||||
'libc6 (>= 2.9)',
|
||||
'libcairo2 (>= 1.6.0)',
|
||||
'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3',
|
||||
@@ -73,7 +74,7 @@ export const referenceGeneratedDepsByArch = {
|
||||
'libdrm2 (>= 2.4.60)',
|
||||
'libexpat1 (>= 2.0.1)',
|
||||
'libgbm1 (>= 17.1.0~rc2)',
|
||||
'libglib2.0-0 (>= 2.16.0)',
|
||||
'libglib2.0-0 (>= 2.12.0)',
|
||||
'libglib2.0-0 (>= 2.39.4)',
|
||||
'libgtk-3-0 (>= 3.9.10)',
|
||||
'libgtk-3-0 (>= 3.9.10) | libgtk-4-1',
|
||||
@@ -82,7 +83,6 @@ export const referenceGeneratedDepsByArch = {
|
||||
'libnss3 (>= 3.26)',
|
||||
'libpango-1.0-0 (>= 1.14.0)',
|
||||
'libsecret-1-0 (>= 0.18)',
|
||||
'libstdc++6 (>= 4.1.1)',
|
||||
'libstdc++6 (>= 5)',
|
||||
'libstdc++6 (>= 5.2)',
|
||||
'libstdc++6 (>= 6)',
|
||||
@@ -105,14 +105,13 @@ export const referenceGeneratedDepsByArch = {
|
||||
'libatk1.0-0 (>= 2.2.0)',
|
||||
'libatspi2.0-0 (>= 2.9.90)',
|
||||
'libc6 (>= 2.17)',
|
||||
'libc6 (>= 2.28)',
|
||||
'libcairo2 (>= 1.6.0)',
|
||||
'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3',
|
||||
'libdbus-1-3 (>= 1.0.2)',
|
||||
'libdrm2 (>= 2.4.60)',
|
||||
'libexpat1 (>= 2.0.1)',
|
||||
'libgbm1 (>= 17.1.0~rc2)',
|
||||
'libglib2.0-0 (>= 2.16.0)',
|
||||
'libglib2.0-0 (>= 2.12.0)',
|
||||
'libglib2.0-0 (>= 2.39.4)',
|
||||
'libgtk-3-0 (>= 3.9.10)',
|
||||
'libgtk-3-0 (>= 3.9.10) | libgtk-4-1',
|
||||
@@ -121,7 +120,6 @@ export const referenceGeneratedDepsByArch = {
|
||||
'libnss3 (>= 3.26)',
|
||||
'libpango-1.0-0 (>= 1.14.0)',
|
||||
'libsecret-1-0 (>= 0.18)',
|
||||
'libstdc++6 (>= 4.1.1)',
|
||||
'libstdc++6 (>= 5)',
|
||||
'libstdc++6 (>= 5.2)',
|
||||
'libstdc++6 (>= 6)',
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -126,7 +126,6 @@ export const referenceGeneratedDepsByArch = {
|
||||
'libc.so.6(GLIBC_2.16)',
|
||||
'libc.so.6(GLIBC_2.17)',
|
||||
'libc.so.6(GLIBC_2.25)',
|
||||
'libc.so.6(GLIBC_2.28)',
|
||||
'libc.so.6(GLIBC_2.4)',
|
||||
'libc.so.6(GLIBC_2.6)',
|
||||
'libc.so.6(GLIBC_2.7)',
|
||||
@@ -220,7 +219,6 @@ export const referenceGeneratedDepsByArch = {
|
||||
'libc.so.6()(64bit)',
|
||||
'libc.so.6(GLIBC_2.17)(64bit)',
|
||||
'libc.so.6(GLIBC_2.25)(64bit)',
|
||||
'libc.so.6(GLIBC_2.28)(64bit)',
|
||||
'libcairo.so.2()(64bit)',
|
||||
'libcurl.so.4()(64bit)',
|
||||
'libdbus-1.so.3()(64bit)',
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
"@types/through2": "^2.0.36",
|
||||
"@types/tmp": "^0.2.1",
|
||||
"@types/underscore": "^1.8.9",
|
||||
"@types/workerpool": "^6.4.0",
|
||||
"@types/xml2js": "0.0.33",
|
||||
"@vscode/iconv-lite-umd": "0.7.0",
|
||||
"@vscode/vsce": "^2.16.0",
|
||||
@@ -70,5 +71,8 @@
|
||||
"tree-sitter": "https://github.com/joaomoreno/node-tree-sitter/releases/download/v0.20.0/tree-sitter-0.20.0.tgz",
|
||||
"tree-sitter-typescript": "^0.20.1",
|
||||
"vscode-gulp-watch": "^5.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"workerpool": "^6.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,6 +646,13 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/workerpool@^6.4.0":
|
||||
version "6.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/workerpool/-/workerpool-6.4.0.tgz#c79292915dd08350d10e78e74687b6f401f270b8"
|
||||
integrity sha512-SIF2/169pDsLKeM8GQGHkOFifGalDbZgiBSaLUnnlVSRsAOenkAvQ6h4uhV2W+PZZczS+8LQxACwNkSykdT91A==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/xml2js@0.0.33":
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.0.33.tgz#20c5dd6460245284d64a55690015b95e409fb7de"
|
||||
@@ -3017,6 +3024,11 @@ wide-align@^1.1.0:
|
||||
dependencies:
|
||||
string-width "^1.0.2 || 2 || 3 || 4"
|
||||
|
||||
workerpool@^6.4.0:
|
||||
version "6.4.0"
|
||||
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.4.0.tgz#f8d5cfb45fde32fa3b7af72ad617c3369567a462"
|
||||
integrity sha512-i3KR1mQMNwY2wx20ozq2EjISGtQWDIfV56We+yGJ5yDs8jTwQiLLaqHlkBHITlCuJnYlVRmXegxFxZg7gqI++A==
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
|
||||
@@ -3503,10 +3503,10 @@ export class CommandCenter {
|
||||
|
||||
const allRepositoriesLabel = l10n.t('All Repositories');
|
||||
const allRepositoriesQuickPickItem: QuickPickItem = { label: allRepositoriesLabel };
|
||||
const repositoriesQuickPickItems: QuickPickItem[] = Array.from(this.model.unsafeRepositories.keys())
|
||||
const repositoriesQuickPickItems: QuickPickItem[] = this.model.unsafeRepositories
|
||||
.sort(compareRepositoryLabel).map(r => new RepositoryItem(r));
|
||||
|
||||
quickpick.items = this.model.unsafeRepositories.size === 1 ? [...repositoriesQuickPickItems] :
|
||||
quickpick.items = this.model.unsafeRepositories.length === 1 ? [...repositoriesQuickPickItems] :
|
||||
[...repositoriesQuickPickItems, { label: '', kind: QuickPickItemKind.Separator }, allRepositoriesQuickPickItem];
|
||||
|
||||
quickpick.show();
|
||||
@@ -3523,7 +3523,7 @@ export class CommandCenter {
|
||||
|
||||
if (repositoryItem.label === allRepositoriesLabel) {
|
||||
// All Repositories
|
||||
unsafeRepositories.push(...this.model.unsafeRepositories.keys());
|
||||
unsafeRepositories.push(...this.model.unsafeRepositories);
|
||||
} else {
|
||||
// One Repository
|
||||
unsafeRepositories.push((repositoryItem as RepositoryItem).path);
|
||||
@@ -3531,11 +3531,11 @@ export class CommandCenter {
|
||||
|
||||
for (const unsafeRepository of unsafeRepositories) {
|
||||
// Mark as Safe
|
||||
await this.git.addSafeDirectory(this.model.unsafeRepositories.get(unsafeRepository)!);
|
||||
await this.git.addSafeDirectory(this.model.getUnsafeRepositoryPath(unsafeRepository)!);
|
||||
|
||||
// Open Repository
|
||||
await this.model.openRepository(unsafeRepository);
|
||||
this.model.unsafeRepositories.delete(unsafeRepository);
|
||||
this.model.deleteUnsafeRepository(unsafeRepository);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2021,7 +2021,7 @@ export class Repository {
|
||||
}
|
||||
|
||||
// --find-renames option is only available starting with git 2.18.0
|
||||
if (opts?.similarityThreshold && this._git.compareGitVersionTo('2.18.0') !== -1) {
|
||||
if (opts?.similarityThreshold && opts.similarityThreshold !== 50 && this._git.compareGitVersionTo('2.18.0') !== -1) {
|
||||
args.push(`--find-renames=${opts.similarityThreshold}%`);
|
||||
}
|
||||
|
||||
|
||||
+55
-41
@@ -34,40 +34,6 @@ class RepositoryPick implements QuickPickItem {
|
||||
constructor(public readonly repository: Repository, public readonly index: number) { }
|
||||
}
|
||||
|
||||
abstract class RepositoryMap<T = void> extends Map<string, T> {
|
||||
constructor() {
|
||||
super();
|
||||
this.updateContextKey();
|
||||
}
|
||||
|
||||
override set(key: string, value: T): this {
|
||||
const result = super.set(key, value);
|
||||
this.updateContextKey();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
override delete(key: string): boolean {
|
||||
const result = super.delete(key);
|
||||
this.updateContextKey();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
abstract updateContextKey(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key - normalized path used in user interface
|
||||
* Value - path extracted from the output of the `git status` command
|
||||
* used when calling `git config --global --add safe.directory`
|
||||
*/
|
||||
class UnsafeRepositoryMap extends RepositoryMap<string> {
|
||||
updateContextKey(): void {
|
||||
commands.executeCommand('setContext', 'git.unsafeRepositoryCount', this.size);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ModelChangeEvent {
|
||||
repository: Repository;
|
||||
uri: Uri;
|
||||
@@ -159,6 +125,45 @@ class ParentRepositoriesManager {
|
||||
}
|
||||
}
|
||||
|
||||
class UnsafeRepositoriesManager {
|
||||
|
||||
/**
|
||||
* Key - normalized path used in user interface
|
||||
* Value - path extracted from the output of the `git status` command
|
||||
* used when calling `git config --global --add safe.directory`
|
||||
*/
|
||||
private _repositories = new Map<string, string>();
|
||||
get repositories(): string[] {
|
||||
return [...this._repositories.keys()];
|
||||
}
|
||||
|
||||
addRepository(repository: string, path: string): void {
|
||||
this._repositories.set(repository, path);
|
||||
this.onDidChangeRepositories();
|
||||
}
|
||||
|
||||
deleteRepository(repository: string): boolean {
|
||||
const result = this._repositories.delete(repository);
|
||||
if (result) {
|
||||
this.onDidChangeRepositories();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getRepositoryPath(repository: string): string | undefined {
|
||||
return this._repositories.get(repository);
|
||||
}
|
||||
|
||||
hasRepository(repository: string): boolean {
|
||||
return this._repositories.has(repository);
|
||||
}
|
||||
|
||||
private onDidChangeRepositories(): void {
|
||||
commands.executeCommand('setContext', 'git.unsafeRepositoryCount', this._repositories.size);
|
||||
}
|
||||
}
|
||||
|
||||
export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry {
|
||||
|
||||
private _onDidOpenRepository = new EventEmitter<Repository>();
|
||||
@@ -226,9 +231,9 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu
|
||||
|
||||
private pushErrorHandlers = new Set<PushErrorHandler>();
|
||||
|
||||
private _unsafeRepositories = new UnsafeRepositoryMap();
|
||||
get unsafeRepositories(): UnsafeRepositoryMap {
|
||||
return this._unsafeRepositories;
|
||||
private _unsafeRepositoriesManager: UnsafeRepositoriesManager;
|
||||
get unsafeRepositories(): string[] {
|
||||
return this._unsafeRepositoriesManager.repositories;
|
||||
}
|
||||
|
||||
private _parentRepositoriesManager: ParentRepositoriesManager;
|
||||
@@ -257,6 +262,7 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu
|
||||
// Repositories managers
|
||||
this._closedRepositoriesManager = new ClosedRepositoriesManager(workspaceState);
|
||||
this._parentRepositoriesManager = new ParentRepositoriesManager(globalState);
|
||||
this._unsafeRepositoriesManager = new UnsafeRepositoriesManager();
|
||||
|
||||
workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables);
|
||||
window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables);
|
||||
@@ -296,7 +302,7 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu
|
||||
parentRepositoryConfig === 'prompt') {
|
||||
// Parent repositories notification
|
||||
this.showParentRepositoryNotification();
|
||||
} else if (this._unsafeRepositories.size !== 0) {
|
||||
} else if (this.unsafeRepositories.length !== 0) {
|
||||
// Unsafe repositories notification
|
||||
this.showUnsafeRepositoryNotification();
|
||||
}
|
||||
@@ -547,11 +553,11 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu
|
||||
this.logger.trace(`Unsafe repository: ${repositoryRoot}`);
|
||||
|
||||
// Show a notification if the unsafe repository is opened after the initial scan
|
||||
if (this._state === 'initialized' && !this._unsafeRepositories.has(repositoryRoot)) {
|
||||
if (this._state === 'initialized' && !this._unsafeRepositoriesManager.hasRepository(repositoryRoot)) {
|
||||
this.showUnsafeRepositoryNotification();
|
||||
}
|
||||
|
||||
this._unsafeRepositories.set(repositoryRoot, unsafeRepositoryMatch[2]);
|
||||
this._unsafeRepositoriesManager.addRepository(repositoryRoot, unsafeRepositoryMatch[2]);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -903,6 +909,14 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu
|
||||
return [...this.pushErrorHandlers];
|
||||
}
|
||||
|
||||
getUnsafeRepositoryPath(repository: string): string | undefined {
|
||||
return this._unsafeRepositoriesManager.getRepositoryPath(repository);
|
||||
}
|
||||
|
||||
deleteUnsafeRepository(repository: string): boolean {
|
||||
return this._unsafeRepositoriesManager.deleteRepository(repository);
|
||||
}
|
||||
|
||||
private async isRepositoryOutsideWorkspace(repositoryPath: string): Promise<boolean> {
|
||||
const workspaceFolders = (workspace.workspaceFolders || [])
|
||||
.filter(folder => folder.uri.scheme === 'file');
|
||||
@@ -969,7 +983,7 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu
|
||||
return;
|
||||
}
|
||||
|
||||
const message = this._unsafeRepositories.size === 1 ?
|
||||
const message = this.unsafeRepositories.length === 1 ?
|
||||
l10n.t('The git repository in the current folder is potentially unsafe as the folder is owned by someone other than the current user.') :
|
||||
l10n.t('The git repositories in the current folder are potentially unsafe as the folders are owned by someone other than the current user.');
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ const fs = require('fs');
|
||||
const webpack = require('webpack');
|
||||
const fancyLog = require('fancy-log');
|
||||
const ansiColors = require('ansi-colors');
|
||||
const { Mangler } = require('../build/lib/mangleTypeScript');
|
||||
const { Mangler } = require('../build/lib/mangle/index');
|
||||
|
||||
/**
|
||||
* Map of project paths to mangled file contents
|
||||
*
|
||||
* @type {Map<string, Map<string, { out: string; sourceMap?: string }>>}
|
||||
* @type {Map<string, Promise<Map<string, { out: string; sourceMap?: string }>>>}
|
||||
*/
|
||||
const mangleMap = new Map();
|
||||
|
||||
@@ -25,7 +25,7 @@ function getMangledFileContents(projectPath) {
|
||||
if (!entry) {
|
||||
const log = (...data) => fancyLog(ansiColors.blue('[mangler]'), ...data);
|
||||
log(`Mangling ${projectPath}`);
|
||||
const ts2tsMangler = new Mangler(projectPath, log);
|
||||
const ts2tsMangler = new Mangler(projectPath, log, { mangleExports: true, manglePrivateFields: true });
|
||||
entry = ts2tsMangler.computeNewFileContents();
|
||||
mangleMap.set(projectPath, entry);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ module.exports = async function (source, sourceMap, meta) {
|
||||
|
||||
const callback = this.async();
|
||||
|
||||
const fileContentsMap = getMangledFileContents(options.configFile);
|
||||
const fileContentsMap = await getMangledFileContents(options.configFile);
|
||||
|
||||
const newContents = fileContentsMap.get(this.resourcePath);
|
||||
callback(null, newContents?.out ?? source, sourceMap, meta);
|
||||
|
||||
@@ -58,7 +58,8 @@ class MyCompletionItem extends vscode.CompletionItem {
|
||||
public readonly metadata: any | undefined,
|
||||
client: ITypeScriptServiceClient,
|
||||
) {
|
||||
super(tsEntry.name, MyCompletionItem.convertKind(tsEntry.kind));
|
||||
const label = tsEntry.name || (tsEntry.insertText ?? '');
|
||||
super(label, MyCompletionItem.convertKind(tsEntry.kind));
|
||||
|
||||
if (tsEntry.source && tsEntry.hasAction && client.apiVersion.lt(API.v490)) {
|
||||
// De-prioritze auto-imports
|
||||
@@ -72,18 +73,18 @@ class MyCompletionItem extends vscode.CompletionItem {
|
||||
// Render "fancy" when source is a workspace path
|
||||
const qualifierCandidate = vscode.workspace.asRelativePath(tsEntry.source);
|
||||
if (qualifierCandidate !== tsEntry.source) {
|
||||
this.label = { label: tsEntry.name, description: qualifierCandidate };
|
||||
this.label = { label, description: qualifierCandidate };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const { sourceDisplay, isSnippet } = tsEntry;
|
||||
if (sourceDisplay) {
|
||||
this.label = { label: tsEntry.name, description: Previewer.asPlainTextWithLinks(sourceDisplay, client) };
|
||||
this.label = { label, description: Previewer.asPlainTextWithLinks(sourceDisplay, client) };
|
||||
}
|
||||
|
||||
if (tsEntry.labelDetails) {
|
||||
this.label = { label: tsEntry.name, ...tsEntry.labelDetails };
|
||||
this.label = { label, ...tsEntry.labelDetails };
|
||||
}
|
||||
|
||||
this.preselect = tsEntry.isRecommended;
|
||||
|
||||
+2
-2
@@ -75,6 +75,7 @@
|
||||
"@vscode/sudo-prompt": "9.3.1",
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"@vscode/windows-mutex": "^0.4.4",
|
||||
"@vscode/windows-process-tree": "^0.5.0",
|
||||
"@vscode/windows-registry": "^1.1.0",
|
||||
"graceful-fs": "4.2.8",
|
||||
"http-proxy-agent": "^2.1.0",
|
||||
@@ -85,7 +86,7 @@
|
||||
"native-is-elevated": "0.6.0",
|
||||
"native-keymap": "^3.3.2",
|
||||
"native-watchdog": "^1.4.1",
|
||||
"node-pty": "0.11.0-beta33",
|
||||
"node-pty": "1.0",
|
||||
"tas-client-umd": "0.1.8",
|
||||
"v8-inspect-profiler": "^0.1.0",
|
||||
"vscode-oniguruma": "1.7.0",
|
||||
@@ -227,7 +228,6 @@
|
||||
"url": "https://github.com/microsoft/vscode/issues"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@vscode/windows-process-tree": "0.4.2",
|
||||
"windows-foreground-love": "0.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-4
@@ -11,6 +11,7 @@
|
||||
"@vscode/ripgrep": "^1.15.4",
|
||||
"@vscode/spdlog": "^0.13.10",
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"@vscode/windows-process-tree": "^0.5.0",
|
||||
"@vscode/windows-registry": "^1.1.0",
|
||||
"cookie": "^0.4.0",
|
||||
"graceful-fs": "4.2.8",
|
||||
@@ -20,7 +21,7 @@
|
||||
"keytar": "7.9.0",
|
||||
"minimist": "^1.2.6",
|
||||
"native-watchdog": "^1.4.1",
|
||||
"node-pty": "0.11.0-beta33",
|
||||
"node-pty": "1.0",
|
||||
"tas-client-umd": "0.1.8",
|
||||
"vscode-oniguruma": "1.7.0",
|
||||
"vscode-regexpp": "^3.1.0",
|
||||
@@ -35,8 +36,5 @@
|
||||
"xterm-headless": "5.3.0-beta.1",
|
||||
"yauzl": "^2.9.2",
|
||||
"yazl": "^2.4.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@vscode/windows-process-tree": "0.4.2"
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -101,10 +101,10 @@
|
||||
dependencies:
|
||||
node-addon-api "^3.0.2"
|
||||
|
||||
"@vscode/windows-process-tree@0.4.2":
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/windows-process-tree/-/windows-process-tree-0.4.2.tgz#54d010fdeb06dfe3a9c6d58fcb3ed9acfc962f33"
|
||||
integrity sha512-b20865s1HG1VtGt887KrB1blwFS6p4L1Fl1o/WplO9j7sGBle8sLqkNnGXbCaRNgdIgfXtitmzG366FVynJZdQ==
|
||||
"@vscode/windows-process-tree@^0.5.0":
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/windows-process-tree/-/windows-process-tree-0.5.0.tgz#b8205b862c75a1e0ad8b7bf4350dc85036ee3a2c"
|
||||
integrity sha512-y8Oliel/rBSYh9f1T4F0zQjJNPeJRgYRhEKZsjas7JXKLf46FpE3Ux8e9+7HelUD8dXFH7C7N6895nU0WhrMlg==
|
||||
dependencies:
|
||||
nan "^2.17.0"
|
||||
|
||||
@@ -537,10 +537,10 @@ node-gyp-build@^4.3.0:
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
|
||||
integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==
|
||||
|
||||
node-pty@0.11.0-beta33:
|
||||
version "0.11.0-beta33"
|
||||
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta33.tgz#722a729fb9449f591279bee1f8b431b71a9af4a1"
|
||||
integrity sha512-SoP5BbSfvc8Um51rIriUEOPvMltc43iTaKXGJaJKLR3+NfQbjcCcNQGyOd9P9pvBccWYg+Rncv18qMtJKIAi1Q==
|
||||
node-pty@1.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.0.0.tgz#7daafc0aca1c4ca3de15c61330373af4af5861fd"
|
||||
integrity sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==
|
||||
dependencies:
|
||||
nan "^2.17.0"
|
||||
|
||||
|
||||
+13
-5
@@ -27,17 +27,19 @@ const { getUNCHost, addUNCHostToAllowlist } = require('./vs/base/node/unc');
|
||||
const product = require('../product.json');
|
||||
const { app, protocol, crashReporter, Menu } = require('electron');
|
||||
|
||||
// Enable sandbox globally
|
||||
app.enableSandbox();
|
||||
|
||||
// Enable portable support
|
||||
const portable = bootstrapNode.configurePortable(product);
|
||||
|
||||
// Enable ASAR support
|
||||
bootstrap.enableASARSupport();
|
||||
|
||||
// Set userData path before app 'ready' event
|
||||
// Enable sandbox globally unless disabled via `--no-sandbox` argument
|
||||
const args = parseCLIArgs();
|
||||
if (args['sandbox']) {
|
||||
app.enableSandbox();
|
||||
}
|
||||
|
||||
// Set userData path before app 'ready' event
|
||||
const userDataPath = getUserDataPath(args, product.nameShort ?? 'code-oss-dev');
|
||||
if (process.platform === 'win32') {
|
||||
const userDataUNCHost = getUNCHost(userDataPath);
|
||||
@@ -464,7 +466,13 @@ function parseCLIArgs() {
|
||||
'locale',
|
||||
'js-flags',
|
||||
'crash-reporter-directory'
|
||||
]
|
||||
],
|
||||
default: {
|
||||
'sandbox': true
|
||||
},
|
||||
alias: {
|
||||
'no-sandbox': 'sandbox'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// Copied from the `@vscode/windows-process-tree` package.
|
||||
// The dependency is an optional dependency that is only used on Windows,
|
||||
// but we need the typings to compile on all platforms.
|
||||
// The `@types/windows-process-tree` package has also been deprecated.
|
||||
declare module '@vscode/windows-process-tree' {
|
||||
export enum ProcessDataFlag { }
|
||||
|
||||
export interface IProcessInfo {
|
||||
pid: number;
|
||||
ppid: number;
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The working set size of the process, in bytes.
|
||||
*/
|
||||
memory?: number;
|
||||
|
||||
/**
|
||||
* The string returned is at most 512 chars, strings exceeding this length are truncated.
|
||||
*/
|
||||
commandLine?: string;
|
||||
}
|
||||
|
||||
export interface IProcessCpuInfo extends IProcessInfo {
|
||||
cpu?: number;
|
||||
}
|
||||
|
||||
export interface IProcessTreeNode {
|
||||
pid: number;
|
||||
name: string;
|
||||
memory?: number;
|
||||
commandLine?: string;
|
||||
children: IProcessTreeNode[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a tree of processes with the rootPid process as the root.
|
||||
* @param rootPid - The pid of the process that will be the root of the tree.
|
||||
* @param callback - The callback to use with the returned list of processes.
|
||||
* @param flags - The flags for what process data should be included.
|
||||
*/
|
||||
export function getProcessTree(rootPid: number, callback: (tree: IProcessTreeNode | undefined) => void, flags?: ProcessDataFlag): void;
|
||||
|
||||
namespace getProcessTree {
|
||||
function __promisify__(rootPid: number, flags?: ProcessDataFlag): Promise<IProcessTreeNode>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of processes containing the rootPid process and all of its descendants.
|
||||
* @param rootPid - The pid of the process of interest.
|
||||
* @param callback - The callback to use with the returned set of processes.
|
||||
* @param flags - The flags for what process data should be included.
|
||||
*/
|
||||
export function getProcessList(rootPid: number, callback: (processList: IProcessInfo[] | undefined) => void, flags?: ProcessDataFlag): void;
|
||||
|
||||
namespace getProcessList {
|
||||
function __promisify__(rootPid: number, flags?: ProcessDataFlag): Promise<IProcessInfo[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of processes annotated with cpu usage information.
|
||||
* @param processList - The list of processes.
|
||||
* @param callback - The callback to use with the returned list of processes.
|
||||
*/
|
||||
export function getProcessCpuUsage(processList: IProcessInfo[], callback: (processListWithCpu: IProcessCpuInfo[]) => void): void;
|
||||
|
||||
namespace getProcessCpuUsage {
|
||||
function __promisify__(processList: IProcessInfo[]): Promise<IProcessCpuInfo[]>;
|
||||
}
|
||||
}
|
||||
@@ -129,17 +129,30 @@ class Trait<T> implements ISpliceable<boolean>, IDisposable {
|
||||
|
||||
const diff = elements.length - deleteCount;
|
||||
const end = start + deleteCount;
|
||||
const sortedIndexes = [
|
||||
...this.sortedIndexes.filter(i => i < start),
|
||||
...elements.map((hasTrait, i) => hasTrait ? i + start : -1).filter(i => i !== -1),
|
||||
...this.sortedIndexes.filter(i => i >= end).map(i => i + diff)
|
||||
];
|
||||
const sortedIndexes: number[] = [];
|
||||
let firstSortedIndex: number | undefined = undefined;
|
||||
let i = 0;
|
||||
|
||||
while (i < this.sortedIndexes.length && this.sortedIndexes[i] < start) {
|
||||
sortedIndexes.push(this.sortedIndexes[i++]);
|
||||
}
|
||||
|
||||
for (let j = 0; j < elements.length; j++) {
|
||||
if (elements[j]) {
|
||||
sortedIndexes.push(j + start);
|
||||
firstSortedIndex = firstSortedIndex ?? sortedIndexes[sortedIndexes.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
while (i < this.sortedIndexes.length && this.sortedIndexes[i] >= end) {
|
||||
sortedIndexes.push(this.sortedIndexes[i++] + diff);
|
||||
firstSortedIndex = firstSortedIndex ?? sortedIndexes[sortedIndexes.length - 1];
|
||||
}
|
||||
|
||||
const length = this.length + diff;
|
||||
|
||||
if (this.sortedIndexes.length > 0 && sortedIndexes.length === 0 && length > 0) {
|
||||
const first = this.sortedIndexes.find(index => index >= start) ?? length - 1;
|
||||
sortedIndexes.push(Math.min(first, length - 1));
|
||||
sortedIndexes.push(Math.min(firstSortedIndex ?? length - 1, length - 1));
|
||||
}
|
||||
|
||||
this.renderer.splice(start, deleteCount, elements.length);
|
||||
@@ -226,12 +239,16 @@ class TraitSpliceable<T> implements ISpliceable<T> {
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[]): void {
|
||||
if (!this.identityProvider) {
|
||||
return this.trait.splice(start, deleteCount, elements.map(() => false));
|
||||
return this.trait.splice(start, deleteCount, new Array(elements.length).fill(false));
|
||||
}
|
||||
|
||||
const pastElementsWithTrait = this.trait.get().map(i => this.identityProvider!.getId(this.view.element(i)).toString());
|
||||
const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(this.identityProvider!.getId(e).toString()) > -1);
|
||||
if (pastElementsWithTrait.length === 0) {
|
||||
return this.trait.splice(start, deleteCount, new Array(elements.length).fill(false));
|
||||
}
|
||||
|
||||
const pastElementsWithTraitSet = new Set(pastElementsWithTrait);
|
||||
const elementsWithTrait = elements.map(e => pastElementsWithTraitSet.has(this.identityProvider!.getId(e).toString()));
|
||||
this.trait.splice(start, deleteCount, elementsWithTrait);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ export class ErrorHandler {
|
||||
|
||||
export const errorHandler = new ErrorHandler();
|
||||
|
||||
/** @skipMangle */
|
||||
export function setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void {
|
||||
errorHandler.setUnexpectedErrorHandler(newUnexpectedErrorHandler);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ else {
|
||||
* environments.
|
||||
*
|
||||
* Note: in web, this property is hardcoded to be `/`.
|
||||
*
|
||||
* @skipMangle
|
||||
*/
|
||||
export const cwd = safeProcess.cwd;
|
||||
|
||||
|
||||
+1
-1
@@ -10,5 +10,5 @@
|
||||
* supported in JSON.
|
||||
* @param content the content to strip comments from
|
||||
* @returns the content without comments
|
||||
*/
|
||||
*/
|
||||
export function stripComments(content: string): string;
|
||||
|
||||
@@ -558,6 +558,7 @@ export class SimpleWorkerServer<H extends object> {
|
||||
|
||||
/**
|
||||
* Called on the worker side
|
||||
* @skipMangle
|
||||
*/
|
||||
export function create(postMessage: (msg: Message, transfer?: ArrayBuffer[]) => void): SimpleWorkerServer<any> {
|
||||
return new SimpleWorkerServer(postMessage, null);
|
||||
|
||||
@@ -115,13 +115,13 @@ export function listProcesses(rootPid: number): Promise<ProcessItem> {
|
||||
|
||||
const cleanUNCPrefix = (value: string): string => {
|
||||
if (value.indexOf('\\\\?\\') === 0) {
|
||||
return value.substr(4);
|
||||
return value.substring(4);
|
||||
} else if (value.indexOf('\\??\\') === 0) {
|
||||
return value.substr(4);
|
||||
return value.substring(4);
|
||||
} else if (value.indexOf('"\\\\?\\') === 0) {
|
||||
return '"' + value.substr(5);
|
||||
return '"' + value.substring(5);
|
||||
} else if (value.indexOf('"\\??\\') === 0) {
|
||||
return '"' + value.substr(5);
|
||||
return '"' + value.substring(5);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
@@ -169,10 +169,7 @@ export function listProcesses(rootPid: number): Promise<ProcessItem> {
|
||||
reject(new Error(`Root process ${rootPid} not found`));
|
||||
}
|
||||
});
|
||||
},
|
||||
// Workaround duplicate enum identifiers issue in @vscode/windows-process-tree
|
||||
// Ref https://github.com/microsoft/vscode/pull/179508
|
||||
(windowsProcessTree.ProcessDataFlag as any).CommandLine | (windowsProcessTree.ProcessDataFlag as any).Memory);
|
||||
}, windowsProcessTree.ProcessDataFlag.CommandLine | windowsProcessTree.ProcessDataFlag.Memory);
|
||||
});
|
||||
} else { // OS X & Linux
|
||||
function calculateLinuxCpuUsage() {
|
||||
|
||||
@@ -9,6 +9,8 @@ interface ICSSPluginConfig {
|
||||
|
||||
/**
|
||||
* Invoked by the loader at run-time
|
||||
*
|
||||
* @skipMangle
|
||||
*/
|
||||
export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void {
|
||||
config = config || {};
|
||||
|
||||
@@ -297,7 +297,12 @@ export class TextAreaHandler extends ViewPart {
|
||||
};
|
||||
|
||||
const textAreaWrapper = this._register(new TextAreaWrapper(this.textArea.domNode));
|
||||
this._textAreaInput = this._register(new TextAreaInput(textAreaInputHost, textAreaWrapper, platform.OS, browser));
|
||||
this._textAreaInput = this._register(new TextAreaInput(textAreaInputHost, textAreaWrapper, platform.OS, {
|
||||
isAndroid: browser.isAndroid,
|
||||
isChrome: browser.isChrome,
|
||||
isFirefox: browser.isFirefox,
|
||||
isSafari: browser.isSafari,
|
||||
}));
|
||||
|
||||
this._register(this._textAreaInput.onKeyDown((e: IKeyboardEvent) => {
|
||||
this._viewController.emitKeyDown(e);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -37,3 +38,10 @@ export const diffDeleteDecoration = ModelDecorationOptions.register({
|
||||
className: 'char-delete',
|
||||
description: 'char-delete',
|
||||
});
|
||||
|
||||
export const arrowRevertChange = ModelDecorationOptions.register({
|
||||
description: 'diff-editor-arrow-revert-change',
|
||||
glyphMarginHoverMessage: new MarkdownString(undefined, { isTrusted: true, supportThemeIcons: true }).appendMarkdown(localize('revertChangeHoverMessage', 'Click to revert change')),
|
||||
glyphMarginClassName: 'arrow-revert-change ' + ThemeIcon.asClassName(Codicon.arrowRight),
|
||||
zIndex: 10001,
|
||||
});
|
||||
|
||||
@@ -14,12 +14,12 @@ import { isDefined } from 'vs/base/common/types';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import 'vs/css!./style';
|
||||
import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration';
|
||||
import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions, IMouseTargetViewZone } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditorWidget';
|
||||
import { diffAddDecoration, diffDeleteDecoration, diffFullLineAddDecoration, diffFullLineDeleteDecoration } from 'vs/editor/browser/widget/diffEditorWidget2/decorations';
|
||||
import { arrowRevertChange, diffAddDecoration, diffDeleteDecoration, diffFullLineAddDecoration, diffFullLineDeleteDecoration } from 'vs/editor/browser/widget/diffEditorWidget2/decorations';
|
||||
import { DiffEditorSash } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorSash';
|
||||
import { ViewZoneManager } from 'vs/editor/browser/widget/diffEditorWidget2/lineAlignment';
|
||||
import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines';
|
||||
@@ -40,6 +40,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { DelegatingEditor } from './delegatingEditorImpl';
|
||||
import { DiffMapping, DiffModel } from './diffModel';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
|
||||
|
||||
const diffEditorDefaultOptions: ValidDiffEditorBaseOptions = {
|
||||
enableSplitViewResizing: true,
|
||||
@@ -142,7 +144,16 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
|
||||
this._register(keepAlive(this._sash, true));
|
||||
|
||||
this._register(new UnchangedRangesFeature(this._originalEditor, this._modifiedEditor, this._diffModel));
|
||||
this._register(this._instantiationService.createInstance(ViewZoneManager, this._originalEditor, this._modifiedEditor, this._diffModel, this._options.map(o => o.renderSideBySide)));
|
||||
this._register(
|
||||
this._instantiationService.createInstance(
|
||||
ViewZoneManager,
|
||||
this._originalEditor,
|
||||
this._modifiedEditor,
|
||||
this._diffModel,
|
||||
this._options.map((o) => o.renderSideBySide),
|
||||
this
|
||||
)
|
||||
);
|
||||
|
||||
this._register(this._instantiationService.createInstance(OverviewRulerPart,
|
||||
this._originalEditor,
|
||||
@@ -229,6 +240,10 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
|
||||
originalDecorations.push({ range: i.originalRange, options: diffDeleteDecoration });
|
||||
modifiedDecorations.push({ range: i.modifiedRange, options: diffAddDecoration });
|
||||
}
|
||||
|
||||
if (!m.lineRangeMapping.modifiedRange.isEmpty) {
|
||||
modifiedDecorations.push({ range: Range.fromPositions(new Position(m.lineRangeMapping.modifiedRange.startLineNumber, 1)), options: arrowRevertChange });
|
||||
}
|
||||
}
|
||||
|
||||
if (currentMove) {
|
||||
@@ -311,24 +326,32 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
|
||||
m.syncedMovedTexts.set(movedText, undefined);
|
||||
}));
|
||||
// Revert change when an arrow is clicked.
|
||||
/*TODO
|
||||
this._register(editor.onMouseDown(event => {
|
||||
if (!event.event.rightButton && event.target.position && event.target.element?.className.includes('arrow-revert-change')) {
|
||||
const lineNumber = event.target.position.lineNumber;
|
||||
const viewZone = event.target as editorBrowser.IMouseTargetViewZone | undefined;
|
||||
const change = this._diffComputationResult?.changes.find(c =>
|
||||
// delete change
|
||||
viewZone?.detail.afterLineNumber === c.modifiedStartLineNumber ||
|
||||
// other changes
|
||||
(c.modifiedEndLineNumber > 0 && c.modifiedStartLineNumber === lineNumber));
|
||||
if (change) {
|
||||
this.revertChange(change);
|
||||
const viewZone = event.target as IMouseTargetViewZone | undefined;
|
||||
|
||||
const model = this._diffModel.get();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
const diffs = model.diff.get()?.mappings;
|
||||
if (!diffs) {
|
||||
return;
|
||||
}
|
||||
const diff = diffs.find(d =>
|
||||
viewZone?.detail.afterLineNumber === d.lineRangeMapping.modifiedRange.startLineNumber - 1 ||
|
||||
d.lineRangeMapping.modifiedRange.startLineNumber === lineNumber
|
||||
);
|
||||
if (!diff) {
|
||||
return;
|
||||
}
|
||||
this.revert(diff.lineRangeMapping);
|
||||
|
||||
event.event.stopPropagation();
|
||||
this._updateDecorations();
|
||||
return;
|
||||
}
|
||||
}));*/
|
||||
}));
|
||||
|
||||
return editor;
|
||||
}
|
||||
@@ -579,6 +602,17 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
|
||||
};
|
||||
}
|
||||
|
||||
public revert(diff: LineRangeMapping): void {
|
||||
const model = this._model.get();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
const originalText = model.original.getValueInRange(diff.originalRange.toExclusiveRange());
|
||||
this._modifiedEditor.executeEdits('diffEditor', [
|
||||
{ range: diff.modifiedRange.toExclusiveRange(), text: originalText }
|
||||
]);
|
||||
}
|
||||
|
||||
private _goTo(diff: DiffMapping): void {
|
||||
this._modifiedEditor.setPosition(new Position(diff.lineRangeMapping.modifiedRange.startLineNumber, 1));
|
||||
this._modifiedEditor.revealRangeInCenter(diff.lineRangeMapping.modifiedRange.toExclusiveRange());
|
||||
|
||||
@@ -11,6 +11,7 @@ import { isIOS } from 'vs/base/common/platform';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
|
||||
import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model';
|
||||
@@ -42,9 +43,10 @@ export class InlineDiffDeletedCodeMargin extends Disposable {
|
||||
constructor(
|
||||
private readonly _getViewZoneId: () => string,
|
||||
private readonly _marginDomNode: HTMLElement,
|
||||
private readonly editor: CodeEditorWidget,
|
||||
private readonly diff: LineRangeMapping,
|
||||
private readonly viewLineCounts: number[],
|
||||
private readonly _modifiedEditor: CodeEditorWidget,
|
||||
private readonly _diff: LineRangeMapping,
|
||||
private readonly _editor: DiffEditorWidget2,
|
||||
private readonly _viewLineCounts: number[],
|
||||
private readonly _originalTextModel: ITextModel,
|
||||
private readonly _contextMenuService: IContextMenuService,
|
||||
private readonly _clipboardService: IClipboardService,
|
||||
@@ -57,7 +59,7 @@ export class InlineDiffDeletedCodeMargin extends Disposable {
|
||||
this._diffActions = document.createElement('div');
|
||||
this._diffActions.className = ThemeIcon.asClassName(Codicon.lightBulb) + ' lightbulb-glyph';
|
||||
this._diffActions.style.position = 'absolute';
|
||||
const lineHeight = this.editor.getOption(EditorOption.lineHeight);
|
||||
const lineHeight = this._modifiedEditor.getOption(EditorOption.lineHeight);
|
||||
this._diffActions.style.right = '0px';
|
||||
this._diffActions.style.visibility = 'hidden';
|
||||
this._diffActions.style.height = `${lineHeight}px`;
|
||||
@@ -65,38 +67,38 @@ export class InlineDiffDeletedCodeMargin extends Disposable {
|
||||
this._marginDomNode.appendChild(this._diffActions);
|
||||
|
||||
const actions: Action[] = [];
|
||||
const isDeletion = diff.modifiedRange.isEmpty;
|
||||
const isDeletion = _diff.modifiedRange.isEmpty;
|
||||
|
||||
// default action
|
||||
actions.push(new Action(
|
||||
'diff.clipboard.copyDeletedContent',
|
||||
isDeletion
|
||||
? (diff.originalRange.length > 1
|
||||
? (_diff.originalRange.length > 1
|
||||
? localize('diff.clipboard.copyDeletedLinesContent.label', "Copy deleted lines")
|
||||
: localize('diff.clipboard.copyDeletedLinesContent.single.label', "Copy deleted line"))
|
||||
: (diff.originalRange.length > 1
|
||||
: (_diff.originalRange.length > 1
|
||||
? localize('diff.clipboard.copyChangedLinesContent.label', "Copy changed lines")
|
||||
: localize('diff.clipboard.copyChangedLinesContent.single.label', "Copy changed line")),
|
||||
undefined,
|
||||
true,
|
||||
async () => {
|
||||
const originalText = this._originalTextModel.getValueInRange(diff.originalRange.toExclusiveRange());
|
||||
const originalText = this._originalTextModel.getValueInRange(_diff.originalRange.toExclusiveRange());
|
||||
await this._clipboardService.writeText(originalText);
|
||||
}
|
||||
));
|
||||
|
||||
let currentLineNumberOffset = 0;
|
||||
let copyLineAction: Action | undefined = undefined;
|
||||
if (diff.originalRange.length > 1) {
|
||||
if (_diff.originalRange.length > 1) {
|
||||
copyLineAction = new Action(
|
||||
'diff.clipboard.copyDeletedLineContent',
|
||||
isDeletion
|
||||
? localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line ({0})", diff.originalRange.startLineNumber)
|
||||
: localize('diff.clipboard.copyChangedLineContent.label', "Copy changed line ({0})", diff.originalRange.startLineNumber),
|
||||
? localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line ({0})", _diff.originalRange.startLineNumber)
|
||||
: localize('diff.clipboard.copyChangedLineContent.label', "Copy changed line ({0})", _diff.originalRange.startLineNumber),
|
||||
undefined,
|
||||
true,
|
||||
async () => {
|
||||
let lineContent = this._originalTextModel.getLineContent(diff.originalRange.startLineNumber + currentLineNumberOffset);
|
||||
let lineContent = this._originalTextModel.getLineContent(_diff.originalRange.startLineNumber + currentLineNumberOffset);
|
||||
if (lineContent === '') {
|
||||
// empty line -> new line
|
||||
const eof = this._originalTextModel.getEndOfLineSequence();
|
||||
@@ -109,21 +111,18 @@ export class InlineDiffDeletedCodeMargin extends Disposable {
|
||||
actions.push(copyLineAction);
|
||||
}
|
||||
|
||||
const readOnly = editor.getOption(EditorOption.readOnly);
|
||||
const readOnly = _modifiedEditor.getOption(EditorOption.readOnly);
|
||||
if (!readOnly) {
|
||||
actions.push(new Action('diff.inline.revertChange', localize('diff.inline.revertChange.label', "Revert this change"), undefined, true, async () => {
|
||||
const originalText = this._originalTextModel.getValueInRange(this.diff.originalRange.toExclusiveRange());
|
||||
editor.executeEdits('diffEditor', [
|
||||
{ range: this.diff.modifiedRange.toExclusiveRange(), text: originalText }
|
||||
]);
|
||||
this._editor.revert(this._diff);
|
||||
}));
|
||||
}
|
||||
|
||||
const useShadowDOM = editor.getOption(EditorOption.useShadowDOM) && !isIOS; // Do not use shadow dom on IOS #122035
|
||||
const useShadowDOM = _modifiedEditor.getOption(EditorOption.useShadowDOM) && !isIOS; // Do not use shadow dom on IOS #122035
|
||||
|
||||
const showContextMenu = (x: number, y: number) => {
|
||||
this._contextMenuService.showContextMenu({
|
||||
domForShadowRoot: useShadowDOM ? editor.getDomNode() ?? undefined : undefined,
|
||||
domForShadowRoot: useShadowDOM ? _modifiedEditor.getDomNode() ?? undefined : undefined,
|
||||
getAnchor: () => {
|
||||
return {
|
||||
x,
|
||||
@@ -134,8 +133,8 @@ export class InlineDiffDeletedCodeMargin extends Disposable {
|
||||
if (copyLineAction) {
|
||||
copyLineAction.label =
|
||||
isDeletion
|
||||
? localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line ({0})", diff.originalRange.startLineNumber + currentLineNumberOffset)
|
||||
: localize('diff.clipboard.copyChangedLineContent.label', "Copy changed line ({0})", diff.originalRange.startLineNumber + currentLineNumberOffset);
|
||||
? localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line ({0})", _diff.originalRange.startLineNumber + currentLineNumberOffset)
|
||||
: localize('diff.clipboard.copyChangedLineContent.label', "Copy changed line ({0})", _diff.originalRange.startLineNumber + currentLineNumberOffset);
|
||||
}
|
||||
return actions;
|
||||
},
|
||||
@@ -150,7 +149,7 @@ export class InlineDiffDeletedCodeMargin extends Disposable {
|
||||
showContextMenu(e.posx, top + height + pad);
|
||||
}));
|
||||
|
||||
this._register(editor.onMouseMove((e: IEditorMouseEvent) => {
|
||||
this._register(_modifiedEditor.onMouseMove((e: IEditorMouseEvent) => {
|
||||
if ((e.target.type === MouseTargetType.CONTENT_VIEW_ZONE || e.target.type === MouseTargetType.GUTTER_VIEW_ZONE) && e.target.detail.viewZoneId === this._getViewZoneId()) {
|
||||
currentLineNumberOffset = this._updateLightBulbPosition(this._marginDomNode, e.event.browserEvent.y, lineHeight);
|
||||
this.visibility = true;
|
||||
@@ -159,7 +158,7 @@ export class InlineDiffDeletedCodeMargin extends Disposable {
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(editor.onMouseDown((e: IEditorMouseEvent) => {
|
||||
this._register(_modifiedEditor.onMouseDown((e: IEditorMouseEvent) => {
|
||||
if (!e.event.rightButton) {
|
||||
return;
|
||||
}
|
||||
@@ -182,10 +181,10 @@ export class InlineDiffDeletedCodeMargin extends Disposable {
|
||||
const lineNumberOffset = Math.floor(offset / lineHeight);
|
||||
const newTop = lineNumberOffset * lineHeight;
|
||||
this._diffActions.style.top = `${newTop}px`;
|
||||
if (this.viewLineCounts) {
|
||||
if (this._viewLineCounts) {
|
||||
let acc = 0;
|
||||
for (let i = 0; i < this.viewLineCounts.length; i++) {
|
||||
acc += this.viewLineCounts[i];
|
||||
for (let i = 0; i < this._viewLineCounts.length; i++) {
|
||||
acc += this._viewLineCounts[i];
|
||||
if (lineNumberOffset < acc) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { $ } from 'vs/base/browser/dom';
|
||||
import { ArrayQueue } from 'vs/base/common/arrays';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IObservable, derived, observableFromEvent, observableSignalFromEvent, observableValue } from 'vs/base/common/observable';
|
||||
import { autorun, autorunWithStore2 } from 'vs/base/common/observableImpl/autorun';
|
||||
@@ -14,6 +16,7 @@ import { IViewZone } from 'vs/editor/browser/editorBrowser';
|
||||
import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { diffDeleteDecoration, diffRemoveIcon } from 'vs/editor/browser/widget/diffEditorWidget2/decorations';
|
||||
import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2';
|
||||
import { DiffMapping, DiffModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffModel';
|
||||
import { InlineDiffDeletedCodeMargin } from 'vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin';
|
||||
import { LineSource, RenderOptions, renderLines } from 'vs/editor/browser/widget/diffEditorWidget2/renderLines';
|
||||
@@ -49,6 +52,7 @@ export class ViewZoneManager extends Disposable {
|
||||
private readonly _modifiedEditor: CodeEditorWidget,
|
||||
private readonly _diffModel: IObservable<DiffModel | undefined>,
|
||||
private readonly _renderSideBySide: IObservable<boolean>,
|
||||
private readonly _diffEditorWidget: DiffEditorWidget2,
|
||||
@IClipboardService private readonly _clipboardService: IClipboardService,
|
||||
@IContextMenuService private readonly _contextMenuService: IContextMenuService,
|
||||
) {
|
||||
@@ -190,10 +194,11 @@ export class ViewZoneManager extends Disposable {
|
||||
marginDomNode,
|
||||
this._modifiedEditor,
|
||||
a.diff,
|
||||
this._diffEditorWidget,
|
||||
result.viewLineCounts,
|
||||
this._originalEditor.getModel()!,
|
||||
this._contextMenuService,
|
||||
this._clipboardService
|
||||
this._clipboardService,
|
||||
)
|
||||
);
|
||||
|
||||
@@ -245,10 +250,22 @@ export class ViewZoneManager extends Disposable {
|
||||
continue;
|
||||
}
|
||||
|
||||
function createViewZoneMarginArrow(): HTMLElement {
|
||||
const arrow = document.createElement('div');
|
||||
arrow.className = 'arrow-revert-change ' + ThemeIcon.asClassName(Codicon.arrowRight);
|
||||
return $('div', {}, arrow);
|
||||
}
|
||||
|
||||
let marginDomNode: HTMLElement | undefined = undefined;
|
||||
if (a.diff && a.diff.modifiedRange.isEmpty) {
|
||||
marginDomNode = createViewZoneMarginArrow();
|
||||
}
|
||||
|
||||
modViewZones.push({
|
||||
afterLineNumber: a.modifiedRange.endLineNumberExclusive - 1,
|
||||
domNode: createFakeLinesDiv(),
|
||||
heightInPx: -delta,
|
||||
marginDomNode,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,10 @@ export class CodeActionController extends Disposable implements IEditorContribut
|
||||
return this.showCodeActionList(actions, at, { includeDisabledActions: false, fromLightbulb: false });
|
||||
}
|
||||
|
||||
public hideCodeActions(): void {
|
||||
this._actionWidgetService.hide();
|
||||
}
|
||||
|
||||
public manualTriggerAtCurrentPosition(
|
||||
notAvailableMessage: string,
|
||||
triggerAction: CodeActionTriggerSource,
|
||||
@@ -126,6 +130,10 @@ export class CodeActionController extends Disposable implements IEditorContribut
|
||||
}
|
||||
}
|
||||
|
||||
public hideLightBulbWidget(): void {
|
||||
this._lightBulbWidget.rawValue?.hide();
|
||||
}
|
||||
|
||||
private async update(newState: CodeActionsState.State): Promise<void> {
|
||||
if (newState.type !== CodeActionsState.Type.Triggered) {
|
||||
this._lightBulbWidget.rawValue?.hide();
|
||||
|
||||
@@ -332,6 +332,7 @@ export class SuggestController implements IEditorContribution {
|
||||
// keep item in memory
|
||||
this._memoryService.memorize(model, this.editor.getPosition(), item);
|
||||
|
||||
const isResolved = item.isResolved;
|
||||
|
||||
if (Array.isArray(item.completion.additionalTextEdits)) {
|
||||
|
||||
@@ -346,7 +347,7 @@ export class SuggestController implements IEditorContribution {
|
||||
);
|
||||
scrollState.restoreRelativeVerticalPositionOfCursor(this.editor);
|
||||
|
||||
} else if (!item.isResolved) {
|
||||
} else if (!isResolved) {
|
||||
// async additional edits
|
||||
const sw = new StopWatch(true);
|
||||
let position: IPosition | undefined;
|
||||
@@ -378,7 +379,7 @@ export class SuggestController implements IEditorContribution {
|
||||
|
||||
tasks.push(item.resolve(cts.token).then(() => {
|
||||
if (!item.completion.additionalTextEdits || cts.token.isCancellationRequested) {
|
||||
return false;
|
||||
return undefined;
|
||||
}
|
||||
if (position && item.completion.additionalTextEdits.some(edit => Position.isBefore(position!, Range.getStartPosition(edit.range)))) {
|
||||
return false;
|
||||
@@ -398,6 +399,20 @@ export class SuggestController implements IEditorContribution {
|
||||
return true;
|
||||
}).then(applied => {
|
||||
this._logService.trace('[suggest] async resolving of edits DONE (ms, applied?)', sw.elapsed(), applied);
|
||||
type AsyncSuggestEdits = { providerId: string; applied: boolean };
|
||||
type AsyncSuggestEditsClassification = {
|
||||
owner: 'jrieken';
|
||||
comment: 'Information about async additional text edits';
|
||||
providerId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'Provider of the completions item' };
|
||||
applied: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'If async additional text edits could be applied' };
|
||||
};
|
||||
if (typeof applied === 'boolean') {
|
||||
this._telemetryService.publicLog2<AsyncSuggestEdits, AsyncSuggestEditsClassification>('suggest.asyncAdditionalEdits', {
|
||||
providerId: item.extensionId?.value ?? 'unknown',
|
||||
applied
|
||||
});
|
||||
}
|
||||
}).finally(() => {
|
||||
docListener.dispose();
|
||||
typeListener.dispose();
|
||||
}));
|
||||
@@ -467,7 +482,7 @@ export class SuggestController implements IEditorContribution {
|
||||
|
||||
// clear only now - after all tasks are done
|
||||
Promise.all(tasks).finally(() => {
|
||||
this._reportSuggestionAcceptedTelemetry(item, model, event);
|
||||
this._reportSuggestionAcceptedTelemetry(item, model, event, isResolved);
|
||||
|
||||
this.model.clear();
|
||||
cts.dispose();
|
||||
@@ -475,12 +490,12 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
|
||||
private _telemetryGate: number = 0;
|
||||
private _reportSuggestionAcceptedTelemetry(item: CompletionItem, model: ITextModel, acceptedSuggestion: ISelectedSuggestion) {
|
||||
private _reportSuggestionAcceptedTelemetry(item: CompletionItem, model: ITextModel, acceptedSuggestion: ISelectedSuggestion, itemResolved: boolean) {
|
||||
if (this._telemetryGate++ % 100 !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
type AcceptedSuggestion = { providerId: string; fileExtension: string; languageId: string; basenameHash: string; kind: number };
|
||||
type AcceptedSuggestion = { providerId: string; fileExtension: string; languageId: string; basenameHash: string; kind: number; itemResolved: boolean };
|
||||
type AcceptedSuggestionClassification = {
|
||||
owner: 'jrieken';
|
||||
comment: 'Information accepting completion items';
|
||||
@@ -488,7 +503,8 @@ export class SuggestController implements IEditorContribution {
|
||||
basenameHash: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'Hash of the basename of the file into which the completion was inserted' };
|
||||
fileExtension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File extension of the file into which the completion was inserted' };
|
||||
languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Language type of the file into which the completion was inserted' };
|
||||
kind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The completion item kind' };
|
||||
kind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The completion item kind' };
|
||||
itemResolved: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'If the item was inserted before resolving was done' };
|
||||
};
|
||||
// _debugDisplayName looks like `vscode.css-language-features(/-:)`, where the last bit is the trigger chars
|
||||
// normalize it to just the extension ID and lowercase
|
||||
@@ -499,6 +515,7 @@ export class SuggestController implements IEditorContribution {
|
||||
basenameHash: hash(basename(model.uri)).toString(16),
|
||||
languageId: model.getLanguageId(),
|
||||
fileExtension: extname(model.uri),
|
||||
itemResolved
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,12 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string
|
||||
}
|
||||
};
|
||||
|
||||
const handler = new TextAreaInput(textAreaInputHost, new TextAreaWrapper(input), platform.OS, browser);
|
||||
const handler = new TextAreaInput(textAreaInputHost, new TextAreaWrapper(input), platform.OS, {
|
||||
isAndroid: browser.isAndroid,
|
||||
isFirefox: browser.isFirefox,
|
||||
isChrome: browser.isChrome,
|
||||
isSafari: browser.isSafari,
|
||||
});
|
||||
|
||||
const output = document.createElement('pre');
|
||||
output.className = 'output';
|
||||
|
||||
@@ -123,6 +123,9 @@ export function localize(info: ILocalizeInfo, message: string, ...args: (string
|
||||
*/
|
||||
export function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
|
||||
|
||||
/**
|
||||
* @skipMangle
|
||||
*/
|
||||
export function localize(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): string {
|
||||
return _format(message, args);
|
||||
}
|
||||
@@ -133,18 +136,25 @@ export function localize(data: ILocalizeInfo | string, message: string, ...args:
|
||||
* in order to ensure the loader plugin has been initialized before this function is called.
|
||||
*/
|
||||
export function getConfiguredDefaultLocale(stringFromLocalizeCall: string): string | undefined;
|
||||
/**
|
||||
* @skipMangle
|
||||
*/
|
||||
export function getConfiguredDefaultLocale(_: string): string | undefined {
|
||||
// This returns undefined because this implementation isn't used and is overwritten by the loader
|
||||
// when loaded.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @skipMangle
|
||||
*/
|
||||
export function setPseudoTranslation(value: boolean) {
|
||||
isPseudo = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked in a built product at run-time
|
||||
* @skipMangle
|
||||
*/
|
||||
export function create(key: string, data: IBundledStrings & IConsumerAPI): IConsumerAPI {
|
||||
return {
|
||||
@@ -155,10 +165,12 @@ export function create(key: string, data: IBundledStrings & IConsumerAPI): ICons
|
||||
|
||||
/**
|
||||
* Invoked by the loader at run-time
|
||||
* @skipMangle
|
||||
*/
|
||||
export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void {
|
||||
const pluginConfig: INLSPluginConfig = config['vs/nls'] ?? {};
|
||||
if (!name || name.length === 0) {
|
||||
// TODO: We need to give back the mangled names here
|
||||
return load({
|
||||
localize: localize,
|
||||
getConfiguredDefaultLocale: () => pluginConfig.availableLanguages?.['*']
|
||||
|
||||
@@ -11,7 +11,6 @@ import { IV8Profile, Utils } from 'vs/platform/profiling/common/profiling';
|
||||
import { IProfileModel, BottomUpSample, buildModel, BottomUpNode, processNode, CdpCallFrame } from 'vs/platform/profiling/common/profilingModel';
|
||||
import { BottomUpAnalysis, IProfileAnalysisWorker, ProfilingOutput } from 'vs/platform/profiling/electron-sandbox/profileAnalysisWorkerService';
|
||||
|
||||
|
||||
export function create(): IRequestHandler {
|
||||
return new ProfileAnalysisWorker();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location, InlineCompletionTriggerKind, InternalDataTransferItem } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location, InlineCompletionTriggerKind, InternalDataTransferItem, CodeActionTriggerKind } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import * as languages from 'vs/editor/common/languages';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
@@ -376,6 +376,7 @@ class CodeActionAdapter {
|
||||
|
||||
private readonly _cache = new Cache<vscode.CodeAction | vscode.Command>('CodeAction');
|
||||
private readonly _disposables = new Map<number, DisposableStore>();
|
||||
private readonly nbKind = new CodeActionKind('notebook');
|
||||
|
||||
constructor(
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
@@ -414,6 +415,7 @@ class CodeActionAdapter {
|
||||
if (!isNonEmptyArray(commandsOrActions) || token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cacheId = this._cache.add(commandsOrActions);
|
||||
const disposables = new DisposableStore();
|
||||
this._disposables.set(cacheId, disposables);
|
||||
@@ -434,6 +436,10 @@ class CodeActionAdapter {
|
||||
command: this._commands.toInternal(candidate, disposables),
|
||||
});
|
||||
} else {
|
||||
if (codeActionContext.triggerKind !== CodeActionTriggerKind.Invoke && candidate.kind && this.nbKind.contains(candidate.kind)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (codeActionContext.only) {
|
||||
if (!candidate.kind) {
|
||||
this._logService.warn(`${this._extension.identifier.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`);
|
||||
@@ -1040,7 +1046,7 @@ class CompletionsAdapter {
|
||||
additionalTextEdits: resolvedItem.additionalTextEdits
|
||||
};
|
||||
|
||||
if (item.insertText !== resolvedItem.insertText) {
|
||||
if (CompletionsAdapter._insertTextIdent(item.insertText) !== CompletionsAdapter._insertTextIdent(resolvedItem.insertText)) {
|
||||
this._apiDeprecation.report('CompletionItem.insertText', this._extension, 'extension MAY NOT change \'insertText\' of a CompletionItem during resolve');
|
||||
enforcedResolvedItem.insertText = resolvedItem.insertText;
|
||||
}
|
||||
@@ -1048,6 +1054,14 @@ class CompletionsAdapter {
|
||||
return this._convertCompletionItem(enforcedResolvedItem, id);
|
||||
}
|
||||
|
||||
private static _insertTextIdent(insertText: string | vscode.SnippetString | undefined) {
|
||||
switch (typeof insertText) {
|
||||
case 'string': return insertText;
|
||||
case 'undefined': return undefined;
|
||||
case 'object': return insertText.value;
|
||||
}
|
||||
}
|
||||
|
||||
releaseCompletionItems(id: number): any {
|
||||
this._disposables.get(id)?.dispose();
|
||||
this._disposables.delete(id);
|
||||
|
||||
@@ -22,7 +22,14 @@ import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/
|
||||
import { IDisposable } from 'xterm';
|
||||
|
||||
|
||||
export interface IAccessibleContentProvider { id: string; provideContent(): string; onClose(): void; onKeyDown?(e: IKeyboardEvent): void; options: IAccessibleViewOptions }
|
||||
export interface IAccessibleContentProvider {
|
||||
id: string;
|
||||
provideContent(): string;
|
||||
onClose(): void;
|
||||
onKeyDown?(e: IKeyboardEvent): void;
|
||||
options: IAccessibleViewOptions;
|
||||
}
|
||||
|
||||
export const IAccessibleViewService = createDecorator<IAccessibleViewService>('accessibleViewService');
|
||||
|
||||
export interface IAccessibleViewService {
|
||||
@@ -96,12 +103,13 @@ class AccessibleView extends Disposable {
|
||||
if (!domNode) {
|
||||
return;
|
||||
}
|
||||
container.appendChild(domNode);
|
||||
container.appendChild(this._editorContainer);
|
||||
this._layout();
|
||||
this._register(this._editorWidget.onKeyDown((e) => {
|
||||
this._register(this._editorWidget.onKeyUp((e) => {
|
||||
if (e.keyCode === KeyCode.Escape) {
|
||||
this._contextViewService.hideContextView();
|
||||
}
|
||||
e.stopPropagation();
|
||||
provider.onKeyDown?.(e);
|
||||
}));
|
||||
this._register(this._editorWidget.onDidBlurEditorText(() => this._contextViewService.hideContextView()));
|
||||
@@ -113,11 +121,6 @@ class AccessibleView extends Disposable {
|
||||
}
|
||||
|
||||
private _layout(): void {
|
||||
const domNode = this._editorWidget.getDomNode();
|
||||
if (!domNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const windowWidth = window.innerWidth;
|
||||
const windowHeight = window.innerHeight;
|
||||
|
||||
@@ -125,9 +128,9 @@ class AccessibleView extends Disposable {
|
||||
const height = Math.min(.4 * windowHeight, this._editorWidget.getContentHeight());
|
||||
this._editorWidget.layout({ width, height });
|
||||
const top = Math.round((windowHeight - height) / 2);
|
||||
domNode.style.top = `${top}px`;
|
||||
this._editorContainer.style.top = `${top}px`;
|
||||
const left = Math.round((windowWidth - width) / 2);
|
||||
domNode.style.left = `${left}px`;
|
||||
this._editorContainer.style.left = `${left}px`;
|
||||
}
|
||||
|
||||
private async _getTextModel(resource: URI): Promise<ITextModel | null> {
|
||||
|
||||
@@ -10,40 +10,30 @@ import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { EditMode } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView';
|
||||
import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
|
||||
|
||||
export function getAccessibilityHelpText(accessor: ServicesAccessor, type: 'chat' | 'inline', currentInput?: string): string {
|
||||
export function getAccessibilityHelpText(accessor: ServicesAccessor, type: 'chat' | 'inline'): string {
|
||||
const keybindingService = accessor.get(IKeybindingService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const content = [];
|
||||
if (type === 'chat') {
|
||||
content.push(localize('chat.overview', 'Chat responses will be announced as they come in. A response will indicate the number of code blocks, if any, and then the rest of the response.'));
|
||||
content.push(localize('chat.requestHistory', 'In the input box, use UpArrow/DownArrow to navigate your request history. Edit input and use enter to run a new request.'));
|
||||
content.push(localize('chat.requestHistory', 'In the input box, use up and down arrows to navigate your request history. Edit input and use enter to run a new request.'));
|
||||
content.push(descriptionForCommand('chat.action.focus', localize('workbench.action.chat.focus', 'The Focus Chat command ({0}) focuses the chat request/response list, which can be navigated with UpArrow/DownArrow.',), localize('workbench.action.chat.focusNoKb', 'The Focus Chat List command focuses the chat request/response list, which can be navigated with UpArrow/DownArrow and is currently not triggerable by a keybinding.'), keybindingService));
|
||||
content.push(descriptionForCommand('workbench.action.chat.focusInput', localize('workbench.action.chat.focusInput', 'The Focus Chat Input command ({0}) focuses the input box for chat requests.'), localize('workbench.action.interactiveSession.focusInputNoKb', 'Focus Chat Input command focuses the input box for chat requests and is currently not triggerable by a keybinding.'), keybindingService));
|
||||
content.push(descriptionForCommand('workbench.action.chat.nextCodeBlock', localize('workbench.action.chat.nextCodeBlock', 'The Chat: Next Code Block command ({0}) focuses the next code block within a response.'), localize('workbench.action.chat.nextCodeBlockNoKb', 'The Chat: Next Code Block command focuses the next code block within a response and is currently not triggerable by a keybinding.'), keybindingService));
|
||||
content.push(descriptionForCommand('workbench.action.chat.clear', localize('workbench.action.chat.clear', 'The Chat Clear command ({0}) clears the request/response list.'), localize('workbench.action.chat.clearNoKb', 'The Chat Clear command clears the request/response list and is currently not triggerable by a keybinding.'), keybindingService));
|
||||
} else {
|
||||
content.push(localize('inlineChat.makeRequest', "Tab once to reach the make request button, which will re-run the request."));
|
||||
const regex = /^(\/fix|\/explain)/;
|
||||
const match = currentInput?.match(regex);
|
||||
const command = match && match.length ? match[0].substring(1) : undefined;
|
||||
if (command === 'fix') {
|
||||
const editMode = configurationService.getValue('inlineChat.mode');
|
||||
if (editMode === EditMode.Preview) {
|
||||
const keybinding = keybindingService.lookupKeybinding('editor.action.diffReview.next')?.getAriaLabel();
|
||||
content.push(keybinding ? localize('inlineChat.diff', "Tab again to enter the Diff editor with the changes and enter review mode with ({0}). Use Up/DownArrow to navigate lines with the proposed changes.", keybinding) : localize('inlineChat.diffNoKb', "Tab again to enter the Diff editor with the changes and enter review mode with the Go to Next Difference Command. Use Up/DownArrow to navigate lines with the proposed changes."));
|
||||
content.push(localize('inlineChat.acceptReject', "Tab again to reach the action bar, which can be navigated with Left/RightArrow."));
|
||||
}
|
||||
} else if (command === 'explain') {
|
||||
content.push(localize('inlineChat.explain', "/explain commands will be run in the chat view."));
|
||||
content.push(localize('inlineChat.chatViewFocus', "To focus the chat view, run the GitHub Copilot: Focus on GitHub Copilot View command, which will focus the input box."));
|
||||
} else {
|
||||
content.push(localize('inlineChat.toolbar', "Use tab to reach conditional parts like commands, status message, message responses and more."));
|
||||
}
|
||||
const startChatKeybinding = keybindingService.lookupKeybinding('inlineChat.start')?.getAriaLabel();
|
||||
content.push(localize('inlineChat.overview', "Inline chat occurs within a code editor and takes into account current selection. It is useful for refactoring, fixing, and more. Keep in mind that Copilot generated code may be incorrect."));
|
||||
content.push(localize('inlineChat.access', "It can be activated via the Fix and Explain with Copilot context menu actions or directly using the command: Inline Chat: Start Code Chat ({0}).", startChatKeybinding));
|
||||
content.push(localize('chat.requestHistoryInline', 'In the input box, use up and down arrows to navigate your request history. Edit input and use enter or the make request button to run a new request.'));
|
||||
content.push(localize('inlineChat.contextActions', "Explain and Fix with Copilot actions run a request prefixed with /fix or /explain. These prefixes can be used directly in the input box to apply those specific actions."));
|
||||
content.push(localize('inlineChat.fix', "When a request is prefixed with /fix, a response will indicate the problem with the current code. A diff editor will be rendered and can be reached by tabbing."));
|
||||
const diffReviewKeybinding = keybindingService.lookupKeybinding('editor.action.diffReview.next')?.getAriaLabel();
|
||||
content.push(diffReviewKeybinding ? localize('inlineChat.diff', "Once in the diff editor, enter review mode with ({0}). Use up and down arrows to navigate lines with the proposed changes.", diffReviewKeybinding) : localize('inlineChat.diffNoKb', "Tab again to enter the Diff editor with the changes and enter review mode with the Go to Next Difference Command. Use Up/DownArrow to navigate lines with the proposed changes."));
|
||||
content.push(localize('inlineChat.explain', "When a request is prefixed with /explain, a response will explain the code in the current selection and the chat view will be focused."));
|
||||
content.push(localize('inlineChat.toolbar', "Use tab to reach conditional parts like commands, status, message responses and more."));
|
||||
}
|
||||
return content.join('\n');
|
||||
}
|
||||
@@ -70,10 +60,9 @@ export async function runAccessibilityHelpAction(accessor: ServicesAccessor, edi
|
||||
return;
|
||||
}
|
||||
|
||||
const cachedInput = inputEditor.getValue();
|
||||
const cachedPosition = inputEditor.getPosition();
|
||||
inputEditor.getSupportedActions();
|
||||
const helpText = getAccessibilityHelpText(accessor, type, type === 'inline' ? cachedInput : undefined);
|
||||
const helpText = getAccessibilityHelpText(accessor, type);
|
||||
const provider = accessibleViewService.registerProvider({
|
||||
id: type,
|
||||
provideContent: () => helpText,
|
||||
|
||||
@@ -88,7 +88,6 @@ export interface IChatRendererDelegate {
|
||||
}
|
||||
|
||||
export class ChatListItemRenderer extends Disposable implements ITreeRenderer<ChatTreeItem, FuzzyScore, IChatListItemTemplate> {
|
||||
static readonly cursorCharacter = '\u258c';
|
||||
static readonly ID = 'item';
|
||||
|
||||
private readonly codeBlocksByResponseId = new Map<string, IChatCodeBlockInfo[]>();
|
||||
@@ -368,12 +367,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
||||
isFullyRendered: renderValue.isFullString
|
||||
};
|
||||
|
||||
// Don't add the cursor if it will go after a codeblock, since this will always cause layout shifting
|
||||
// when the codeblock is the last thing in the response, and that happens often.
|
||||
const plusCursor = renderValue.value.match(/```\s*$/) ?
|
||||
renderValue.value :
|
||||
renderValue.value + ` ${ChatListItemRenderer.cursorCharacter}`;
|
||||
const result = this.renderMarkdown(new MarkdownString(plusCursor), element, disposables, templateData, true);
|
||||
const result = this.renderMarkdown(new MarkdownString(renderValue.value), element, disposables, templateData, true);
|
||||
// Doing the progressive render
|
||||
dom.clearNode(templateData.value);
|
||||
templateData.value.appendChild(result.element);
|
||||
@@ -754,28 +748,16 @@ class CodeBlockPart extends Disposable implements IChatResultCodeBlockPart {
|
||||
}
|
||||
|
||||
private setText(newText: string): void {
|
||||
let currentText = this.textModel.getLinesContent().join('\n');
|
||||
const currentText = this.textModel.getLinesContent().join('\n');
|
||||
if (newText === currentText) {
|
||||
return;
|
||||
}
|
||||
|
||||
let removedChars = 0;
|
||||
if (currentText.endsWith(` ${ChatListItemRenderer.cursorCharacter}`)) {
|
||||
removedChars = 2;
|
||||
} else if (currentText.endsWith(ChatListItemRenderer.cursorCharacter)) {
|
||||
removedChars = 1;
|
||||
}
|
||||
|
||||
if (removedChars > 0) {
|
||||
currentText = currentText.slice(0, currentText.length - removedChars);
|
||||
}
|
||||
|
||||
if (newText.startsWith(currentText)) {
|
||||
const text = newText.slice(currentText.length);
|
||||
const lastLine = this.textModel.getLineCount();
|
||||
const lastCol = this.textModel.getLineMaxColumn(lastLine);
|
||||
const insertAtCol = lastCol - removedChars;
|
||||
this.textModel.applyEdits([{ range: new Range(lastLine, insertAtCol, lastLine, lastCol), text }]);
|
||||
this.textModel.applyEdits([{ range: new Range(lastLine, lastCol, lastLine, lastCol), text }]);
|
||||
} else {
|
||||
// console.log(`Failed to optimize setText`);
|
||||
this.textModel.setValue(newText);
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-editor .accessibilityHelpWidget {
|
||||
padding: 10px;
|
||||
vertical-align: middle;
|
||||
overflow: auto;
|
||||
.accessible-view {
|
||||
position: relative;
|
||||
background-color: var(--vscode-editorWidget-background);
|
||||
color: var(--vscode-editorWidget-foreground);
|
||||
box-shadow: 0 2px 8px var(--vscode-widget-shadow);
|
||||
border: 2px solid var(--vscode-contrastBorder);
|
||||
border: 2px solid var(--vscode-contrastActiveBorder);
|
||||
}
|
||||
|
||||
@@ -753,7 +753,7 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget {
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
this.replInput.dispose();
|
||||
this.replInput?.dispose(); // Disposed before rendered? #174558
|
||||
this.replElementsChangeListener?.dispose();
|
||||
this.refreshScheduler.dispose();
|
||||
this.modelChangeListener.dispose();
|
||||
|
||||
@@ -528,7 +528,7 @@ export class Thread implements IThread {
|
||||
const firstAvailableStackFrame = callStack.find(sf => !!(sf &&
|
||||
((this.stoppedDetails?.reason === 'instruction breakpoint' || (this.stoppedDetails?.reason === 'step' && this.lastSteppingGranularity === 'instruction')) && sf.instructionPointerReference) ||
|
||||
(sf.source && sf.source.available && sf.source.presentationHint !== 'deemphasize')));
|
||||
return firstAvailableStackFrame || (callStack.length > 0 ? callStack[0] : undefined);
|
||||
return firstAvailableStackFrame;
|
||||
}
|
||||
|
||||
get stateLabel(): string {
|
||||
|
||||
+1
-1
@@ -280,7 +280,7 @@ class CodeActionOnSaveParticipant implements IStoredFileWorkingCopySaveParticipa
|
||||
|
||||
private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], progress: IProgress<CodeActionProvider>, token: CancellationToken) {
|
||||
return getCodeActions(this.languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), {
|
||||
type: CodeActionTriggerType.Auto,
|
||||
type: CodeActionTriggerType.Invoke,
|
||||
triggerAction: CodeActionTriggerSource.OnSave,
|
||||
filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true },
|
||||
}, progress, token);
|
||||
|
||||
@@ -30,6 +30,7 @@ import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/vi
|
||||
import { CodeCellViewModel, outputDisplayLimit } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { WordHighlighterContribution } from 'vs/editor/contrib/wordHighlighter/browser/wordHighlighter';
|
||||
import { CodeActionController } from 'vs/editor/contrib/codeAction/browser/codeActionController';
|
||||
|
||||
export class CodeCell extends Disposable {
|
||||
private _outputContainerRenderer: CellOutputContainer;
|
||||
@@ -267,6 +268,8 @@ export class CodeCell extends Disposable {
|
||||
|
||||
this._register(this.templateData.editor.onDidBlurEditorWidget(() => {
|
||||
WordHighlighterContribution.get(this.templateData.editor)?.stopHighlighting();
|
||||
CodeActionController.get(this.templateData.editor)?.hideCodeActions();
|
||||
CodeActionController.get(this.templateData.editor)?.hideLightBulbWidget();
|
||||
}));
|
||||
this._register(this.templateData.editor.onDidFocusEditorWidget(() => {
|
||||
WordHighlighterContribution.get(this.templateData.editor)?.restoreViewState(true);
|
||||
|
||||
@@ -31,6 +31,7 @@ import { IExternalUriOpenerService } from 'vs/workbench/contrib/externalUriOpene
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
|
||||
export const VIEWLET_ID = 'workbench.view.remote';
|
||||
|
||||
@@ -181,7 +182,7 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon
|
||||
@IRemoteExplorerService remoteExplorerService: IRemoteExplorerService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IWorkbenchConfigurationService configurationService: IWorkbenchConfigurationService,
|
||||
@IDebugService debugService: IDebugService,
|
||||
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
|
||||
@ITunnelService tunnelService: ITunnelService,
|
||||
@@ -193,7 +194,7 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon
|
||||
return;
|
||||
}
|
||||
|
||||
remoteAgentService.getEnvironment().then(environment => {
|
||||
configurationService.whenRemoteConfigurationLoaded().then(() => remoteAgentService.getEnvironment()).then(environment => {
|
||||
if (environment?.os !== OperatingSystem.Linux) {
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
.registerDefaultConfigurations([{ overrides: { 'remote.autoForwardPortsSource': PORT_AUTO_SOURCE_SETTING_OUTPUT } }]);
|
||||
|
||||
@@ -404,18 +404,20 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
};
|
||||
}
|
||||
|
||||
private async _getSearchAddon(): Promise<SearchAddonType> {
|
||||
if (this._searchAddon) {
|
||||
return this._searchAddon;
|
||||
private _searchAddonPromise: Promise<SearchAddonType> | undefined;
|
||||
private _getSearchAddon(): Promise<SearchAddonType> {
|
||||
if (!this._searchAddonPromise) {
|
||||
this._searchAddonPromise = this._getSearchAddonConstructor().then((AddonCtor) => {
|
||||
this._searchAddon = new AddonCtor({ highlightLimit: XtermTerminalConstants.SearchHighlightLimit });
|
||||
this.raw.loadAddon(this._searchAddon);
|
||||
this._searchAddon.onDidChangeResults((results: { resultIndex: number; resultCount: number }) => {
|
||||
this._lastFindResult = results;
|
||||
this._onDidChangeFindResults.fire(results);
|
||||
});
|
||||
return this._searchAddon;
|
||||
});
|
||||
}
|
||||
const AddonCtor = await this._getSearchAddonConstructor();
|
||||
this._searchAddon = new AddonCtor({ highlightLimit: XtermTerminalConstants.SearchHighlightLimit });
|
||||
this.raw.loadAddon(this._searchAddon);
|
||||
this._searchAddon.onDidChangeResults((results: { resultIndex: number; resultCount: number }) => {
|
||||
this._lastFindResult = results;
|
||||
this._onDidChangeFindResults.fire(results);
|
||||
});
|
||||
return this._searchAddon;
|
||||
return this._searchAddonPromise;
|
||||
}
|
||||
|
||||
clearSearchDecorations(): void {
|
||||
|
||||
@@ -6,14 +6,15 @@
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.welcome-widget {
|
||||
height: min-content;
|
||||
border-radius: 6px;
|
||||
.dialog-message-detail-title > div > p > .codicon[class*='codicon-']::before{
|
||||
position: relative;
|
||||
color: var(--vscode-textLink-foreground);
|
||||
padding-right: 10px;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.dialog-message-detail-title {
|
||||
height: 22px;
|
||||
padding-bottom: 4px;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { LanguageService } from 'vs/editor/common/services/languageService';
|
||||
import { ILanguageService } from 'vs/editor/common/languages/language';
|
||||
import { GettingStartedDetailsRenderer } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedDetailsRenderer';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
import { applicationConfigurationNodeBase } from 'vs/workbench/common/configuration';
|
||||
@@ -77,22 +76,18 @@ class WelcomeDialogContribution extends Disposable implements IWorkbenchContribu
|
||||
const scheduler = new RunOnceScheduler(() => {
|
||||
if (codeEditor === codeEditorService.getActiveCodeEditor()) {
|
||||
this.isRendered = true;
|
||||
const detailsRenderer = new GettingStartedDetailsRenderer(fileService, notificationService, extensionService, languageService);
|
||||
|
||||
const welcomeWidget = new WelcomeWidget(
|
||||
codeEditor,
|
||||
instantiationService,
|
||||
commandService,
|
||||
telemetryService,
|
||||
openerService,
|
||||
webviewService,
|
||||
detailsRenderer);
|
||||
openerService);
|
||||
|
||||
welcomeWidget.render(welcomeDialog.title,
|
||||
welcomeDialog.message,
|
||||
welcomeDialog.buttonText,
|
||||
welcomeDialog.buttonCommand,
|
||||
welcomeDialog.media);
|
||||
welcomeDialog.buttonCommand);
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
import 'vs/css!./media/welcomeWidget';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
|
||||
import { $, append, hide } from 'vs/base/browser/dom'; import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { $, append, hide } from 'vs/base/browser/dom';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ButtonBar } from 'vs/base/browser/ui/button/button';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { defaultButtonStyles, defaultDialogStyles } from 'vs/platform/theme/browser/defaultStyles';
|
||||
import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Action, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
@@ -25,16 +25,12 @@ import { Link } from 'vs/platform/opener/browser/link';
|
||||
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { GettingStartedDetailsRenderer } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedDetailsRenderer';
|
||||
import { FileAccess } from 'vs/base/common/network';
|
||||
import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { contrastBorder, editorWidgetBackground, editorWidgetForeground, widgetBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
export class WelcomeWidget extends Disposable implements IOverlayWidget {
|
||||
|
||||
private static readonly WIDGET_TIMEOUT: number = 15000;
|
||||
private static readonly WELCOME_MEDIA_PATH = 'vs/workbench/contrib/welcomeGettingStarted/common/media/';
|
||||
|
||||
private readonly _rootDomNode: HTMLElement;
|
||||
private readonly element: HTMLElement;
|
||||
private readonly messageContainer: HTMLElement;
|
||||
@@ -45,9 +41,7 @@ export class WelcomeWidget extends Disposable implements IOverlayWidget {
|
||||
private readonly instantiationService: IInstantiationService,
|
||||
private readonly commandService: ICommandService,
|
||||
private readonly telemetryService: ITelemetryService,
|
||||
private readonly openerService: IOpenerService,
|
||||
private readonly webviewService: IWebviewService,
|
||||
private readonly detailsRenderer: GettingStartedDetailsRenderer
|
||||
private readonly openerService: IOpenerService
|
||||
) {
|
||||
super();
|
||||
this._rootDomNode = document.createElement('div');
|
||||
@@ -64,7 +58,6 @@ export class WelcomeWidget extends Disposable implements IOverlayWidget {
|
||||
async executeCommand(commandId: string, ...args: string[]) {
|
||||
try {
|
||||
await this.commandService.executeCommand(commandId, ...args);
|
||||
this._hide(false);
|
||||
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', {
|
||||
id: commandId,
|
||||
from: 'welcomeWidget'
|
||||
@@ -74,41 +67,38 @@ export class WelcomeWidget extends Disposable implements IOverlayWidget {
|
||||
}
|
||||
}
|
||||
|
||||
public async render(title: string, message: string, buttonText: string, buttonAction: string, media: { altText: string; path: string }) {
|
||||
public async render(title: string, message: string, buttonText: string, buttonAction: string) {
|
||||
if (!this._editor._getViewModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.buildWidgetContent(title, message, buttonText, buttonAction, media);
|
||||
await this.buildWidgetContent(title, message, buttonText, buttonAction);
|
||||
this._editor.addOverlayWidget(this);
|
||||
this._revealTemporarily();
|
||||
this._show();
|
||||
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', {
|
||||
id: 'welcomeWidgetRendered',
|
||||
from: 'welcomeWidget'
|
||||
});
|
||||
}
|
||||
|
||||
private async buildWidgetContent(title: string, message: string, buttonText: string, buttonAction: string, media: { altText: string; path: string }) {
|
||||
private async buildWidgetContent(title: string, message: string, buttonText: string, buttonAction: string) {
|
||||
|
||||
const actionBar = this._register(new ActionBar(this.element, {}));
|
||||
|
||||
const action = this._register(new Action('dialog.close', localize('dialogClose', "Close Dialog"), ThemeIcon.asClassName(Codicon.dialogClose), true, async () => {
|
||||
this._hide(true);
|
||||
this._hide();
|
||||
}));
|
||||
actionBar.push(action, { icon: true, label: false });
|
||||
|
||||
if (media) {
|
||||
await this.buildSVGMediaComponent(media.path);
|
||||
}
|
||||
|
||||
const renderBody = (message: string): MarkdownString => {
|
||||
const mds = new MarkdownString(undefined, { supportHtml: true });
|
||||
const renderBody = (message: string, icon: string): MarkdownString => {
|
||||
const mds = new MarkdownString(undefined, { supportThemeIcons: true, supportHtml: true });
|
||||
mds.appendMarkdown(`<a class="copilot">$(${icon})</a>`);
|
||||
mds.appendMarkdown(message);
|
||||
return mds;
|
||||
};
|
||||
|
||||
const titleElement = this.messageContainer.appendChild($('#monaco-dialog-message-detail.dialog-message-detail-title'));
|
||||
const titleElementMdt = this.markdownRenderer.render(renderBody(title));
|
||||
const titleElementMdt = this.markdownRenderer.render(renderBody(title, 'zap'));
|
||||
titleElement.appendChild(titleElementMdt.element);
|
||||
|
||||
this.buildStepMarkdownDescription(this.messageContainer, message.split('\n').filter(x => x).map(text => parseLinkedText(text)));
|
||||
@@ -125,7 +115,6 @@ export class WelcomeWidget extends Disposable implements IOverlayWidget {
|
||||
}));
|
||||
|
||||
buttonBar.buttons[0].focus();
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
private buildStepMarkdownDescription(container: HTMLElement, text: LinkedText[]) {
|
||||
@@ -158,18 +147,6 @@ export class WelcomeWidget extends Disposable implements IOverlayWidget {
|
||||
return container;
|
||||
}
|
||||
|
||||
private async buildSVGMediaComponent(path: string) {
|
||||
|
||||
const mediaContainer = this.messageContainer.appendChild($('.dialog-image-container'));
|
||||
mediaContainer.id = generateUuid();
|
||||
|
||||
const webview = this._register(this.webviewService.createWebviewElement({ title: undefined, options: {}, contentOptions: {}, extension: undefined }));
|
||||
webview.mountTo(mediaContainer);
|
||||
|
||||
const body = await this.detailsRenderer.renderSVG(FileAccess.asFileUri(`${WelcomeWidget.WELCOME_MEDIA_PATH}${path}`));
|
||||
webview.setHtml(body);
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return 'editor.contrib.welcomeWidget';
|
||||
}
|
||||
@@ -184,14 +161,8 @@ export class WelcomeWidget extends Disposable implements IOverlayWidget {
|
||||
};
|
||||
}
|
||||
|
||||
private _hideSoon = this._register(new RunOnceScheduler(() => this._hide(false), WelcomeWidget.WIDGET_TIMEOUT));
|
||||
private _isVisible: boolean = false;
|
||||
|
||||
private _revealTemporarily(): void {
|
||||
this._show();
|
||||
this._hideSoon.schedule();
|
||||
}
|
||||
|
||||
private _show(): void {
|
||||
if (this._isVisible) {
|
||||
return;
|
||||
@@ -200,32 +171,48 @@ export class WelcomeWidget extends Disposable implements IOverlayWidget {
|
||||
this._rootDomNode.style.display = 'block';
|
||||
}
|
||||
|
||||
private _hide(isUserDismissed: boolean): void {
|
||||
private _hide(): void {
|
||||
if (!this._isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isVisible = false;
|
||||
this._isVisible = true;
|
||||
this._rootDomNode.style.display = 'none';
|
||||
this._editor.removeOverlayWidget(this);
|
||||
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', {
|
||||
id: isUserDismissed ? 'welcomeWidgetDismissed' : 'welcomeWidgetHidden',
|
||||
id: 'welcomeWidgetDismissed',
|
||||
from: 'welcomeWidget'
|
||||
});
|
||||
}
|
||||
|
||||
private applyStyles(): void {
|
||||
const style = defaultDialogStyles;
|
||||
|
||||
const fgColor = style.dialogForeground;
|
||||
const bgColor = style.dialogBackground;
|
||||
const shadowColor = style.dialogShadow ? `0 0px 8px ${style.dialogShadow}` : '';
|
||||
const border = style.dialogBorder ? `1px solid ${style.dialogBorder}` : '';
|
||||
|
||||
this._rootDomNode.style.boxShadow = shadowColor;
|
||||
|
||||
this._rootDomNode.style.color = fgColor ?? '';
|
||||
this._rootDomNode.style.backgroundColor = bgColor ?? '';
|
||||
this._rootDomNode.style.border = border;
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const addBackgroundColorRule = (selector: string, color: Color | undefined): void => {
|
||||
if (color) {
|
||||
collector.addRule(`.monaco-editor ${selector} { background-color: ${color}; }`);
|
||||
}
|
||||
};
|
||||
|
||||
const widgetBackground = theme.getColor(editorWidgetBackground);
|
||||
addBackgroundColorRule('.welcome-widget', widgetBackground);
|
||||
|
||||
const widgetShadowColor = theme.getColor(widgetShadow);
|
||||
if (widgetShadowColor) {
|
||||
collector.addRule(`.welcome-widget { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`);
|
||||
}
|
||||
|
||||
const widgetBorderColor = theme.getColor(widgetBorder);
|
||||
if (widgetBorderColor) {
|
||||
collector.addRule(`.welcome-widget { border-left: 1px solid ${widgetBorderColor}; border-right: 1px solid ${widgetBorderColor}; border-bottom: 1px solid ${widgetBorderColor}; }`);
|
||||
}
|
||||
|
||||
const hcBorder = theme.getColor(contrastBorder);
|
||||
if (hcBorder) {
|
||||
collector.addRule(`.welcome-widget { border: 1px solid ${hcBorder}; }`);
|
||||
}
|
||||
|
||||
const foreground = theme.getColor(editorWidgetForeground);
|
||||
if (foreground) {
|
||||
collector.addRule(`.welcome-widget { color: ${foreground}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
+4
-2
@@ -251,10 +251,12 @@ registerAction2(class extends Action2 {
|
||||
quickPick.hide();
|
||||
});
|
||||
quickPick.onDidHide(() => quickPick.dispose());
|
||||
quickPick.show();
|
||||
await extensionService.whenInstalledExtensionsRegistered();
|
||||
gettingStartedService.onDidAddWalkthrough(async () => {
|
||||
quickPick.items = await this.getQuickPickItems(contextService, gettingStartedService);
|
||||
});
|
||||
quickPick.show();
|
||||
quickPick.busy = false;
|
||||
quickPick.items = await this.getQuickPickItems(contextService, gettingStartedService);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -55,8 +55,6 @@ export class StartupPageContribution implements IWorkbenchContribution {
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IEditorResolverService editorResolverService: IEditorResolverService
|
||||
) {
|
||||
this.run().then(undefined, onUnexpectedError);
|
||||
|
||||
editorResolverService.registerEditor(
|
||||
`${GettingStartedInput.RESOURCE.scheme}:/**`,
|
||||
{
|
||||
@@ -80,6 +78,8 @@ export class StartupPageContribution implements IWorkbenchContribution {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.run().then(undefined, onUnexpectedError);
|
||||
}
|
||||
|
||||
private async run() {
|
||||
|
||||
@@ -185,11 +185,11 @@ class ExtensionBisectUi {
|
||||
private _showBisectPrompt(): void {
|
||||
|
||||
const goodPrompt: IPromptChoice = {
|
||||
label: 'Good Now',
|
||||
label: localize('I cannot reproduce', "I can't reproduce"),
|
||||
run: () => this._commandService.executeCommand('extension.bisect.next', false)
|
||||
};
|
||||
const badPrompt: IPromptChoice = {
|
||||
label: 'This is Bad',
|
||||
label: localize('This is Bad', "I can reproduce"),
|
||||
run: () => this._commandService.executeCommand('extension.bisect.next', true)
|
||||
};
|
||||
const stop: IPromptChoice = {
|
||||
@@ -329,11 +329,11 @@ registerAction2(class extends Action2 {
|
||||
detail: localize('bisect', "Extension Bisect is active and has disabled {0} extensions. Check if you can still reproduce the problem and proceed by selecting from these options.", bisectService.disabledCount),
|
||||
buttons: [
|
||||
{
|
||||
label: localize({ key: 'next.good', comment: ['&& denotes a mnemonic'] }, "&&Good now"),
|
||||
label: localize({ key: 'next.good', comment: ['&& denotes a mnemonic'] }, "I ca&&n't reproduce"),
|
||||
run: () => false // good now
|
||||
},
|
||||
{
|
||||
label: localize({ key: 'next.bad', comment: ['&& denotes a mnemonic'] }, "This is &&bad"),
|
||||
label: localize({ key: 'next.bad', comment: ['&& denotes a mnemonic'] }, "I can &&reproduce"),
|
||||
run: () => true // bad
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { IUserDataProfileImportExportService, IUserDataProfileManagementService, IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect';
|
||||
import { INotificationHandle, INotificationService, IPromptChoice, NotificationPriority, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
|
||||
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
const ITroubleshootIssueService = createDecorator<ITroubleshootIssueService>('ITroubleshootIssueService');
|
||||
|
||||
interface ITroubleshootIssueService {
|
||||
_serviceBrand: undefined;
|
||||
isActive(): boolean;
|
||||
start(): Promise<void>;
|
||||
resume(): Promise<void>;
|
||||
stop(): Promise<void>;
|
||||
}
|
||||
|
||||
enum TroubleshootStage {
|
||||
EXTENSIONS = 1,
|
||||
WORKBENCH,
|
||||
}
|
||||
|
||||
type TroubleShootResult = 'good' | 'bad' | 'stop';
|
||||
|
||||
class TroubleShootState {
|
||||
|
||||
static fromJSON(raw: string | undefined): TroubleShootState | undefined {
|
||||
if (!raw) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
interface Raw extends TroubleShootState { }
|
||||
const data: Raw = JSON.parse(raw);
|
||||
if (
|
||||
(data.stage === TroubleshootStage.EXTENSIONS || data.stage === TroubleshootStage.WORKBENCH)
|
||||
&& typeof data.profile === 'string'
|
||||
) {
|
||||
return new TroubleShootState(data.stage, data.profile);
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
return undefined;
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly stage: TroubleshootStage,
|
||||
readonly profile: string,
|
||||
) { }
|
||||
}
|
||||
|
||||
class TroubleshootIssueService extends Disposable implements ITroubleshootIssueService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
static readonly storageKey = 'issueTroubleshootState';
|
||||
|
||||
private notificationHandle: INotificationHandle | undefined;
|
||||
|
||||
constructor(
|
||||
@IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService,
|
||||
@IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,
|
||||
@IUserDataProfileManagementService private readonly userDataProfileManagementService: IUserDataProfileManagementService,
|
||||
@IUserDataProfileImportExportService private readonly userDataProfileImportExportService: IUserDataProfileImportExportService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IExtensionBisectService private readonly extensionBisectService: IExtensionBisectService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
|
||||
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
|
||||
@IWorkbenchIssueService private readonly issueService: IWorkbenchIssueService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.state !== undefined;
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
if (this.isActive()) {
|
||||
throw new Error('invalid state');
|
||||
}
|
||||
|
||||
const res = await this.dialogService.confirm({
|
||||
message: localize('troubleshoot issue', "Troubleshoot Issue"),
|
||||
detail: localize('detail.start', "Issue troubleshooting is a process to help you identify if the issue is with {0} or caused by an extension.\n\nDuring the process the window reloads repeatedly. Each time you must confirm if you are still seeing problems.", this.productService.nameLong),
|
||||
primaryButton: localize({ key: 'msg', comment: ['&& denotes a mnemonic'] }, "&&Troubleshoot Issue"),
|
||||
custom: true
|
||||
});
|
||||
|
||||
if (!res.confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalProfile = this.userDataProfileService.currentProfile;
|
||||
await this.userDataProfileImportExportService.createTemporaryProfile(this.userDataProfileService.currentProfile, localize('troubleshoot issue', "Troubleshoot Issue"), true);
|
||||
this.state = new TroubleShootState(TroubleshootStage.EXTENSIONS, originalProfile.id);
|
||||
await this.resume();
|
||||
}
|
||||
|
||||
async resume(): Promise<void> {
|
||||
if (!this.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state?.stage === TroubleshootStage.EXTENSIONS && !this.extensionBisectService.isActive) {
|
||||
await this.reproduceIssueWithExtensionsDisabled();
|
||||
}
|
||||
|
||||
if (this.state?.stage === TroubleshootStage.WORKBENCH) {
|
||||
await this.reproduceIssueWithEmptyProfile();
|
||||
}
|
||||
|
||||
await this.stop();
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
if (!this.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.notificationHandle) {
|
||||
this.notificationHandle.close();
|
||||
this.notificationHandle = undefined;
|
||||
}
|
||||
|
||||
if (this.extensionBisectService.isActive) {
|
||||
await this.extensionBisectService.reset();
|
||||
}
|
||||
|
||||
const profile = this.userDataProfilesService.profiles.find(p => p.id === this.state?.profile) ?? this.userDataProfilesService.defaultProfile;
|
||||
this.state = undefined;
|
||||
await this.userDataProfileManagementService.switchProfile(profile);
|
||||
}
|
||||
|
||||
private async reproduceIssueWithExtensionsDisabled(): Promise<void> {
|
||||
const result = await this.askToReproduceIssue(localize('profile.extensions.disabled', "Issue troubleshooting is active and has temprarily disabled all installed extensions. Check if you can still reproduce the problem and proceed by selecting from these options."));
|
||||
if (result === 'good') {
|
||||
const profile = this.userDataProfilesService.profiles.find(p => p.id === this.state!.profile) ?? this.userDataProfilesService.defaultProfile;
|
||||
await this.reproduceIssueWithExtensionsBisect(profile);
|
||||
}
|
||||
if (result === 'bad') {
|
||||
this.state = new TroubleShootState(TroubleshootStage.WORKBENCH, this.state!.profile);
|
||||
}
|
||||
if (result === 'stop') {
|
||||
await this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private async reproduceIssueWithEmptyProfile(): Promise<void> {
|
||||
await this.userDataProfileManagementService.createAndEnterTransientProfile();
|
||||
this.updateState(this.state);
|
||||
const result = await this.askToReproduceIssue(localize('empty.profile', "Issue troubleshooting is active and has temporarily reset your settings to defaults. Check if you can still reproduce the problem and proceed by selecting from these options."));
|
||||
if (result === 'stop') {
|
||||
await this.stop();
|
||||
}
|
||||
if (result === 'good') {
|
||||
await this.askToReportIssue(localize('issue is with configuration', "Issue troubleshooting has identified that the issue is caused by your settings. Please report the issue by sharing your settings."));
|
||||
}
|
||||
if (result === 'bad') {
|
||||
await this.askToReportIssue(localize('issue is in core', "Issue troubleshooting has identified that the issue is with {0}.", this.productService.nameLong));
|
||||
}
|
||||
}
|
||||
|
||||
private async reproduceIssueWithExtensionsBisect(profile: IUserDataProfile): Promise<void> {
|
||||
await this.userDataProfileManagementService.switchProfile(profile);
|
||||
const extensions = (await this.extensionManagementService.getInstalled(ExtensionType.User)).filter(ext => this.extensionEnablementService.isEnabled(ext));
|
||||
await this.extensionBisectService.start(extensions);
|
||||
await this.hostService.reload();
|
||||
}
|
||||
|
||||
private askToReproduceIssue(message: string): Promise<TroubleShootResult> {
|
||||
return new Promise((c, e) => {
|
||||
const goodPrompt: IPromptChoice = {
|
||||
label: localize('I cannot reproduce', "I can't reproduce"),
|
||||
run: () => c('good')
|
||||
};
|
||||
const badPrompt: IPromptChoice = {
|
||||
label: localize('This is Bad', "I can reproduce"),
|
||||
run: () => c('bad')
|
||||
};
|
||||
const stop: IPromptChoice = {
|
||||
label: localize('Stop', "Stop"),
|
||||
run: () => c('stop')
|
||||
};
|
||||
this.notificationHandle = this.notificationService.prompt(
|
||||
Severity.Info,
|
||||
message,
|
||||
[goodPrompt, badPrompt, stop],
|
||||
{ sticky: true, priority: NotificationPriority.URGENT }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private async askToReportIssue(message: string): Promise<void> {
|
||||
let isCheckedInInsiders = false;
|
||||
if (this.productService.quality === 'stable') {
|
||||
const res = await this.askToReproduceIssueWithInsiders();
|
||||
if (res === 'good') {
|
||||
await this.dialogService.prompt({
|
||||
type: Severity.Info,
|
||||
message: localize('troubleshoot issue', "Troubleshoot Issue"),
|
||||
detail: localize('use insiders', "This likely means that the issue has been addressed already and will be available in an upcoming release. You can safely use {0} insiders until the new stable version is available.", this.productService.nameLong),
|
||||
custom: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (res === 'stop') {
|
||||
await this.stop();
|
||||
return;
|
||||
}
|
||||
if (res === 'bad') {
|
||||
isCheckedInInsiders = true;
|
||||
}
|
||||
}
|
||||
|
||||
await this.issueService.openReporter({
|
||||
issueBody: `> ${message} ${isCheckedInInsiders ? `It is confirmed that the issue exists in ${this.productService.nameLong} Insiders` : ''}`,
|
||||
});
|
||||
}
|
||||
|
||||
private async askToReproduceIssueWithInsiders(): Promise<TroubleShootResult | undefined> {
|
||||
const confirmRes = await this.dialogService.confirm({
|
||||
type: 'info',
|
||||
message: localize('troubleshoot issue', "Troubleshoot Issue"),
|
||||
primaryButton: localize('download insiders', "Download {0} Insiders", this.productService.nameLong),
|
||||
cancelButton: localize('report anyway', "Report Issue Anyway"),
|
||||
detail: localize('ask to download insiders', "Please try to download and reproduce the issue in {0} insiders.", this.productService.nameLong),
|
||||
custom: {
|
||||
disableCloseAction: true,
|
||||
}
|
||||
});
|
||||
|
||||
if (!confirmRes.confirmed) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const opened = await this.openerService.open(URI.parse('https://aka.ms/vscode-insiders'));
|
||||
if (!opened) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const res = await this.dialogService.prompt<TroubleShootResult>({
|
||||
type: 'info',
|
||||
message: localize('troubleshoot issue', "Troubleshoot Issue"),
|
||||
buttons: [{
|
||||
label: localize('good', "I can't reproduce"),
|
||||
run: () => 'good'
|
||||
}, {
|
||||
label: localize('bad', "I can reproduce"),
|
||||
run: () => 'bad'
|
||||
}],
|
||||
cancelButton: {
|
||||
label: localize('stop', "Stop"),
|
||||
run: () => 'stop'
|
||||
},
|
||||
detail: localize('ask to reproduce issue', "Please try to reproduce the issue in {0} insiders and confirm if the issue exists there.", this.productService.nameLong),
|
||||
custom: {
|
||||
disableCloseAction: true,
|
||||
}
|
||||
});
|
||||
|
||||
return res.result;
|
||||
}
|
||||
|
||||
private _state: TroubleShootState | undefined | null;
|
||||
get state(): TroubleShootState | undefined {
|
||||
if (this._state === undefined) {
|
||||
const raw = this.storageService.get(TroubleshootIssueService.storageKey, StorageScope.PROFILE);
|
||||
this._state = TroubleShootState.fromJSON(raw);
|
||||
}
|
||||
return this._state || undefined;
|
||||
}
|
||||
|
||||
set state(state: TroubleShootState | undefined) {
|
||||
this._state = state ?? null;
|
||||
this.updateState(state);
|
||||
}
|
||||
|
||||
private updateState(state: TroubleShootState | undefined) {
|
||||
if (state) {
|
||||
this.storageService.store(TroubleshootIssueService.storageKey, JSON.stringify(state), StorageScope.PROFILE, StorageTarget.MACHINE);
|
||||
} else {
|
||||
this.storageService.remove(TroubleshootIssueService.storageKey, StorageScope.PROFILE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IssueTroubleshootUi extends Disposable {
|
||||
|
||||
static ctxIsTroubleshootActive = new RawContextKey<boolean>('isIssueTroubleshootActive', false);
|
||||
|
||||
constructor(
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@ITroubleshootIssueService private readonly troubleshootIssueService: ITroubleshootIssueService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
) {
|
||||
super();
|
||||
this.updateContext();
|
||||
if (troubleshootIssueService.isActive()) {
|
||||
troubleshootIssueService.resume();
|
||||
}
|
||||
this._register(storageService.onDidChangeValue(e => {
|
||||
if (e.key === TroubleshootIssueService.storageKey) {
|
||||
this.updateContext();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private updateContext(): void {
|
||||
IssueTroubleshootUi.ctxIsTroubleshootActive.bindTo(this.contextKeyService).set(this.troubleshootIssueService.isActive());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(IssueTroubleshootUi, LifecyclePhase.Restored);
|
||||
|
||||
registerAction2(class TroubleshootIssueAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.troubleshootIssue.start',
|
||||
title: { value: localize('troubleshootIssue', "Troubleshoot Issue..."), original: 'Troubleshoot Issue...' },
|
||||
category: Categories.Help,
|
||||
f1: true,
|
||||
precondition: IssueTroubleshootUi.ctxIsTroubleshootActive.negate(),
|
||||
});
|
||||
}
|
||||
run(accessor: ServicesAccessor): Promise<void> {
|
||||
return accessor.get(ITroubleshootIssueService).start();
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.troubleshootIssue.stop',
|
||||
title: { value: localize('title.stop', "Stop Troubleshoot Issue"), original: 'Stop Troubleshoot Issue' },
|
||||
category: Categories.Help,
|
||||
f1: true,
|
||||
precondition: IssueTroubleshootUi.ctxIsTroubleshootActive
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
return accessor.get(ITroubleshootIssueService).stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
registerSingleton(ITroubleshootIssueService, TroubleshootIssueService, InstantiationType.Delayed);
|
||||
@@ -96,6 +96,7 @@ export class ExtensionsResourceInitializer implements IProfileResourceInitialize
|
||||
export class ExtensionsResource implements IProfileResource {
|
||||
|
||||
constructor(
|
||||
private readonly extensionsDisabled: boolean,
|
||||
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
|
||||
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
|
||||
@IUserDataProfileStorageService private readonly userDataProfileStorageService: IUserDataProfileStorageService,
|
||||
@@ -189,7 +190,7 @@ export class ExtensionsResource implements IProfileResource {
|
||||
}
|
||||
}
|
||||
const profileExtension: IProfileExtension = { identifier, displayName: extension.manifest.displayName };
|
||||
if (disabled) {
|
||||
if (this.extensionsDisabled || disabled) {
|
||||
profileExtension.disabled = true;
|
||||
}
|
||||
if (!extension.isBuiltin && extension.pinned) {
|
||||
@@ -276,17 +277,18 @@ export class ExtensionsResourceExportTreeItem extends ExtensionsResourceTreeItem
|
||||
|
||||
constructor(
|
||||
private readonly profile: IUserDataProfile,
|
||||
private readonly extensionsDisabled: boolean,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected getExtensions(): Promise<IProfileExtension[]> {
|
||||
return this.instantiationService.createInstance(ExtensionsResource).getLocalExtensions(this.profile);
|
||||
return this.instantiationService.createInstance(ExtensionsResource, this.extensionsDisabled).getLocalExtensions(this.profile);
|
||||
}
|
||||
|
||||
async getContent(): Promise<string> {
|
||||
return this.instantiationService.createInstance(ExtensionsResource).getContent(this.profile, [...this.excludedExtensions.values()]);
|
||||
return this.instantiationService.createInstance(ExtensionsResource, this.extensionsDisabled).getContent(this.profile, [...this.excludedExtensions.values()]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -301,11 +303,11 @@ export class ExtensionsResourceImportTreeItem extends ExtensionsResourceTreeItem
|
||||
}
|
||||
|
||||
protected getExtensions(): Promise<IProfileExtension[]> {
|
||||
return this.instantiationService.createInstance(ExtensionsResource).getProfileExtensions(this.content);
|
||||
return this.instantiationService.createInstance(ExtensionsResource, false).getProfileExtensions(this.content);
|
||||
}
|
||||
|
||||
async getContent(): Promise<string> {
|
||||
const extensionsResource = this.instantiationService.createInstance(ExtensionsResource);
|
||||
const extensionsResource = this.instantiationService.createInstance(ExtensionsResource, false);
|
||||
const extensions = await extensionsResource.getProfileExtensions(this.content);
|
||||
return extensionsResource.toContent(extensions, [...this.excludedExtensions.values()]);
|
||||
}
|
||||
|
||||
+23
-12
@@ -225,7 +225,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
|
||||
}
|
||||
const disposables = new DisposableStore();
|
||||
try {
|
||||
const userDataProfilesExportState = disposables.add(this.instantiationService.createInstance(UserDataProfileExportState, this.userDataProfileService.currentProfile));
|
||||
const userDataProfilesExportState = disposables.add(this.instantiationService.createInstance(UserDataProfileExportState, this.userDataProfileService.currentProfile, false));
|
||||
const barrier = new Barrier();
|
||||
const exportAction = new BarrierAction(barrier, new Action('export', localize('export', "Export"), undefined, true, async () => {
|
||||
exportAction.enabled = false;
|
||||
@@ -247,7 +247,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
|
||||
}
|
||||
|
||||
async createFromCurrentProfile(name: string): Promise<void> {
|
||||
const userDataProfilesExportState = this.instantiationService.createInstance(UserDataProfileExportState, this.userDataProfileService.currentProfile);
|
||||
const userDataProfilesExportState = this.instantiationService.createInstance(UserDataProfileExportState, this.userDataProfileService.currentProfile, false);
|
||||
try {
|
||||
const profileTemplate = await userDataProfilesExportState.getProfileTemplate(name, undefined);
|
||||
await this.doImportProfile(profileTemplate);
|
||||
@@ -256,6 +256,16 @@ export class UserDataProfileImportExportService extends Disposable implements IU
|
||||
}
|
||||
}
|
||||
|
||||
async createTemporaryProfile(profile: IUserDataProfile, name: string, extensionsDisabled: boolean): Promise<void> {
|
||||
const userDataProfilesExportState = this.instantiationService.createInstance(UserDataProfileExportState, profile, extensionsDisabled);
|
||||
try {
|
||||
const profileTemplate = await userDataProfilesExportState.getProfileTemplate(name, undefined);
|
||||
await this.importAndSwitch(profileTemplate, true, true, extensionsDisabled, localize('import', "Create Profile"));
|
||||
} finally {
|
||||
userDataProfilesExportState.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private async doExportProfile(userDataProfilesExportState: UserDataProfileExportState): Promise<void> {
|
||||
const profile = await userDataProfilesExportState.getProfileToExport();
|
||||
if (!profile) {
|
||||
@@ -342,7 +352,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
|
||||
const userDataProfileImportState = disposables.add(this.instantiationService.createInstance(UserDataProfileImportState, profileTemplate));
|
||||
profileTemplate = await userDataProfileImportState.getProfileTemplateToImport();
|
||||
|
||||
const importedProfile = await this.importAndSwitch(profileTemplate, true, false, localize('preview profile', "Preview Profile"));
|
||||
const importedProfile = await this.importAndSwitch(profileTemplate, true, false, false, localize('preview profile', "Preview Profile"));
|
||||
|
||||
if (!importedProfile) {
|
||||
return;
|
||||
@@ -385,7 +395,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
|
||||
view.setMessage(undefined);
|
||||
const profileTemplate = await userDataProfileImportState.getProfileTemplateToImport();
|
||||
if (profileTemplate.extensions) {
|
||||
await that.instantiationService.createInstance(ExtensionsResource).apply(profileTemplate.extensions, importedProfile);
|
||||
await that.instantiationService.createInstance(ExtensionsResource, false).apply(profileTemplate.extensions, importedProfile);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -393,7 +403,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
|
||||
disposables.add(Event.debounce(this.extensionManagementService.onDidInstallExtensions, () => undefined, 100)(async () => {
|
||||
const profileTemplate = await userDataProfileImportState.getProfileTemplateToImport();
|
||||
if (profileTemplate.extensions) {
|
||||
const profileExtensions = await that.instantiationService.createInstance(ExtensionsResource).getProfileExtensions(profileTemplate.extensions!);
|
||||
const profileExtensions = await that.instantiationService.createInstance(ExtensionsResource, false).getProfileExtensions(profileTemplate.extensions!);
|
||||
const installed = await this.extensionManagementService.getInstalled(ExtensionType.User);
|
||||
if (profileExtensions.every(e => installed.some(i => areSameExtensions(e.identifier, i.identifier)))) {
|
||||
disposable.dispose();
|
||||
@@ -432,7 +442,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
|
||||
const importProfileFn = async () => {
|
||||
importAction.enabled = false;
|
||||
const profileTemplate = await userDataProfileImportState.getProfileTemplateToImport();
|
||||
const importedProfile = await this.importAndSwitch(profileTemplate, false, true, title);
|
||||
const importedProfile = await this.importAndSwitch(profileTemplate, false, true, false, title);
|
||||
if (!importedProfile) {
|
||||
return;
|
||||
}
|
||||
@@ -448,7 +458,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
|
||||
return importAction;
|
||||
}
|
||||
|
||||
private async importAndSwitch(profileTemplate: IUserDataProfileTemplate, temporaryProfile: boolean, extensions: boolean, title: string): Promise<IUserDataProfile | undefined> {
|
||||
private async importAndSwitch(profileTemplate: IUserDataProfileTemplate, temporaryProfile: boolean, extensions: boolean, extensionsDisabled: boolean, title: string): Promise<IUserDataProfile | undefined> {
|
||||
return this.progressService.withProgress({
|
||||
location: ProgressLocation.Window,
|
||||
command: showWindowLogActionId,
|
||||
@@ -481,7 +491,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
|
||||
}
|
||||
if (profileTemplate.extensions && extensions) {
|
||||
progress.report({ message: localize('progress extensions', "{0} ({1}): Applying Extensions...", title, profileTemplate.name) });
|
||||
await this.instantiationService.createInstance(ExtensionsResource).apply(profileTemplate.extensions, profile);
|
||||
await this.instantiationService.createInstance(ExtensionsResource, extensionsDisabled).apply(profileTemplate.extensions, profile);
|
||||
}
|
||||
|
||||
progress.report({ message: localize('switching profile', "{0} ({1}): Applying...", title, profileTemplate.name) });
|
||||
@@ -545,8 +555,8 @@ export class UserDataProfileImportExportService extends Disposable implements IU
|
||||
return result?.id;
|
||||
}
|
||||
|
||||
private async getProfileToImport(profileTemplate: IUserDataProfileTemplate, temp?: boolean): Promise<IUserDataProfile | undefined> {
|
||||
const profileName = temp ? `${profileTemplate.name} (${localize('preview', "Preview")})` : profileTemplate.name;
|
||||
private async getProfileToImport(profileTemplate: IUserDataProfileTemplate, temp: boolean): Promise<IUserDataProfile | undefined> {
|
||||
const profileName = profileTemplate.name;
|
||||
const profile = this.userDataProfilesService.profiles.find(p => p.name === profileName);
|
||||
if (profile) {
|
||||
if (temp) {
|
||||
@@ -666,7 +676,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
|
||||
await this.instantiationService.createInstance(GlobalStateResource).apply(profile.globalState, this.userDataProfileService.currentProfile);
|
||||
}
|
||||
if (profile.extensions) {
|
||||
await this.instantiationService.createInstance(ExtensionsResource).apply(profile.extensions, this.userDataProfileService.currentProfile);
|
||||
await this.instantiationService.createInstance(ExtensionsResource, false).apply(profile.extensions, this.userDataProfileService.currentProfile);
|
||||
}
|
||||
});
|
||||
this.notificationService.info(localize('applied profile', "{0}: Applied successfully.", PROFILES_CATEGORY.value));
|
||||
@@ -997,6 +1007,7 @@ class UserDataProfileExportState extends UserDataProfileImportExportState {
|
||||
|
||||
constructor(
|
||||
readonly profile: IUserDataProfile,
|
||||
private readonly disableExtensions: boolean,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
@@ -1052,7 +1063,7 @@ class UserDataProfileExportState extends UserDataProfileImportExportState {
|
||||
roots.push(globalStateResourceTreeItem);
|
||||
}
|
||||
|
||||
const extensionsResourceTreeItem = this.instantiationService.createInstance(ExtensionsResourceExportTreeItem, exportPreviewProfle);
|
||||
const extensionsResourceTreeItem = this.instantiationService.createInstance(ExtensionsResourceExportTreeItem, exportPreviewProfle, this.disableExtensions);
|
||||
if (await extensionsResourceTreeItem.hasContent()) {
|
||||
roots.push(extensionsResourceTreeItem);
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ export interface IUserDataProfileImportExportService {
|
||||
importProfile(uri: URI, options?: IProfileImportOptions): Promise<void>;
|
||||
showProfileContents(): Promise<void>;
|
||||
createFromCurrentProfile(name: string): Promise<void>;
|
||||
createTemporaryProfile(from: IUserDataProfile, name: string, extensionsDisabled: boolean): Promise<void>;
|
||||
setProfile(profile: IUserDataProfileTemplate): Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry';
|
||||
* and can be used to add assertions. e.g. that registries are empty, etc.
|
||||
*
|
||||
* !! This is called directly by the testing framework.
|
||||
*
|
||||
* @skipMangle
|
||||
*/
|
||||
export function assertCleanState(): void {
|
||||
// If this test fails, it is a clear indication that
|
||||
|
||||
@@ -109,6 +109,7 @@ import 'vs/editor/common/services/treeViewsDndService';
|
||||
import 'vs/workbench/services/textMate/browser/textMateTokenizationFeature.contribution';
|
||||
import 'vs/workbench/services/userActivity/common/userActivityService';
|
||||
import 'vs/workbench/services/userActivity/browser/userActivityBrowser';
|
||||
import 'vs/workbench/services/issue/browser/issueTroubleshoot';
|
||||
|
||||
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
|
||||
|
||||
@@ -1400,10 +1400,10 @@
|
||||
bindings "^1.2.1"
|
||||
nan "^2.17.0"
|
||||
|
||||
"@vscode/windows-process-tree@0.4.2":
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/windows-process-tree/-/windows-process-tree-0.4.2.tgz#54d010fdeb06dfe3a9c6d58fcb3ed9acfc962f33"
|
||||
integrity sha512-b20865s1HG1VtGt887KrB1blwFS6p4L1Fl1o/WplO9j7sGBle8sLqkNnGXbCaRNgdIgfXtitmzG366FVynJZdQ==
|
||||
"@vscode/windows-process-tree@^0.5.0":
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/windows-process-tree/-/windows-process-tree-0.5.0.tgz#b8205b862c75a1e0ad8b7bf4350dc85036ee3a2c"
|
||||
integrity sha512-y8Oliel/rBSYh9f1T4F0zQjJNPeJRgYRhEKZsjas7JXKLf46FpE3Ux8e9+7HelUD8dXFH7C7N6895nU0WhrMlg==
|
||||
dependencies:
|
||||
nan "^2.17.0"
|
||||
|
||||
@@ -6973,10 +6973,10 @@ node-gyp-build@^4.3.0:
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
|
||||
integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==
|
||||
|
||||
node-pty@0.11.0-beta33:
|
||||
version "0.11.0-beta33"
|
||||
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta33.tgz#722a729fb9449f591279bee1f8b431b71a9af4a1"
|
||||
integrity sha512-SoP5BbSfvc8Um51rIriUEOPvMltc43iTaKXGJaJKLR3+NfQbjcCcNQGyOd9P9pvBccWYg+Rncv18qMtJKIAi1Q==
|
||||
node-pty@1.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.0.0.tgz#7daafc0aca1c4ca3de15c61330373af4af5861fd"
|
||||
integrity sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==
|
||||
dependencies:
|
||||
nan "^2.17.0"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user