mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-24 20:26:08 +00:00
Merge branch 'main' into joh/voluminous-lobster
This commit is contained in:
@@ -110,7 +110,7 @@ const tasks = compilations.map(function (tsconfigFile) {
|
||||
overrideOptions.inlineSources = Boolean(build);
|
||||
overrideOptions.base = path.dirname(absolutePath);
|
||||
|
||||
const compilation = tsb.create(absolutePath, overrideOptions, false, err => reporter(err.toString()));
|
||||
const compilation = tsb.create(absolutePath, overrideOptions, { verbose: false }, err => reporter(err.toString()));
|
||||
|
||||
const pipeline = function () {
|
||||
const input = es.through();
|
||||
|
||||
@@ -42,7 +42,7 @@ function createCompile(src, build, emitError) {
|
||||
if (!build) {
|
||||
overrideOptions.inlineSourceMap = true;
|
||||
}
|
||||
const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
|
||||
const compilation = tsb.create(projectPath, overrideOptions, { verbose: false }, err => reporter(err));
|
||||
function pipeline(token) {
|
||||
const bom = require('gulp-bom');
|
||||
const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path));
|
||||
|
||||
@@ -50,7 +50,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean) {
|
||||
overrideOptions.inlineSourceMap = true;
|
||||
}
|
||||
|
||||
const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
|
||||
const compilation = tsb.create(projectPath, overrideOptions, { verbose: false }, err => reporter(err));
|
||||
|
||||
function pipeline(token?: util.ICancellationToken) {
|
||||
const bom = require('gulp-bom') as typeof import('gulp-bom');
|
||||
|
||||
@@ -9,7 +9,6 @@ const fs_1 = require("fs");
|
||||
const path = require("path");
|
||||
const crypto = require("crypto");
|
||||
const utils = require("./utils");
|
||||
const log = require("fancy-log");
|
||||
const colors = require("ansi-colors");
|
||||
const ts = require("typescript");
|
||||
const Vinyl = require("vinyl");
|
||||
@@ -23,11 +22,7 @@ function normalize(path) {
|
||||
return path.replace(/\\/g, '/');
|
||||
}
|
||||
function createTypeScriptBuilder(config, projectFile, cmd) {
|
||||
function _log(topic, message) {
|
||||
if (config.verbose) {
|
||||
log(colors.cyan(topic), message);
|
||||
}
|
||||
}
|
||||
const _log = config.logFn;
|
||||
const host = new LanguageServiceHost(cmd, projectFile, _log);
|
||||
const service = ts.createLanguageService(host, ts.createDocumentRegistry());
|
||||
const lastBuildVersion = Object.create(null);
|
||||
@@ -290,12 +285,10 @@ function createTypeScriptBuilder(config, projectFile, cmd) {
|
||||
});
|
||||
oldErrors = newErrors;
|
||||
// print stats
|
||||
if (config.verbose) {
|
||||
const headNow = process.memoryUsage().heapUsed;
|
||||
const MB = 1024 * 1024;
|
||||
log('[tsb]', 'time:', colors.yellow((Date.now() - t1) + 'ms'), 'mem:', colors.cyan(Math.ceil(headNow / MB) + 'MB'), colors.bgCyan('delta: ' + Math.ceil((headNow - headUsed) / MB)));
|
||||
headUsed = headNow;
|
||||
}
|
||||
const headNow = process.memoryUsage().heapUsed;
|
||||
const MB = 1024 * 1024;
|
||||
_log('[tsb]', `time: ${colors.yellow((Date.now() - t1) + 'ms')} + \nmem: ${colors.cyan(Math.ceil(headNow / MB) + 'MB')} ${colors.bgCyan('delta: ' + Math.ceil((headNow - headUsed) / MB))}`);
|
||||
headUsed = headNow;
|
||||
});
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -7,13 +7,12 @@ import { statSync, readFileSync } from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as crypto from 'crypto';
|
||||
import * as utils from './utils';
|
||||
import * as log from 'fancy-log';
|
||||
import * as colors from 'ansi-colors';
|
||||
import * as ts from 'typescript';
|
||||
import * as Vinyl from 'vinyl';
|
||||
|
||||
export interface IConfiguration {
|
||||
verbose: boolean;
|
||||
logFn: (topic: string, message: string) => void;
|
||||
_emitWithoutBasePath?: boolean;
|
||||
}
|
||||
|
||||
@@ -39,11 +38,7 @@ function normalize(path: string): string {
|
||||
|
||||
export function createTypeScriptBuilder(config: IConfiguration, projectFile: string, cmd: ts.ParsedCommandLine): ITypeScriptBuilder {
|
||||
|
||||
function _log(topic: string, message: string): void {
|
||||
if (config.verbose) {
|
||||
log(colors.cyan(topic), message);
|
||||
}
|
||||
}
|
||||
const _log = config.logFn;
|
||||
|
||||
const host = new LanguageServiceHost(cmd, projectFile, _log);
|
||||
const service = ts.createLanguageService(host, ts.createDocumentRegistry());
|
||||
@@ -57,7 +52,6 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str
|
||||
// always emit declaraction files
|
||||
host.getCompilationSettings().declaration = true;
|
||||
|
||||
|
||||
function file(file: Vinyl): void {
|
||||
// support gulp-sourcemaps
|
||||
if ((<any>file).sourceMap) {
|
||||
@@ -355,15 +349,13 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str
|
||||
oldErrors = newErrors;
|
||||
|
||||
// print stats
|
||||
if (config.verbose) {
|
||||
const headNow = process.memoryUsage().heapUsed;
|
||||
const MB = 1024 * 1024;
|
||||
log('[tsb]',
|
||||
'time:', colors.yellow((Date.now() - t1) + 'ms'),
|
||||
'mem:', colors.cyan(Math.ceil(headNow / MB) + 'MB'), colors.bgCyan('delta: ' + Math.ceil((headNow - headUsed) / MB))
|
||||
);
|
||||
headUsed = headNow;
|
||||
}
|
||||
const headNow = process.memoryUsage().heapUsed;
|
||||
const MB = 1024 * 1024;
|
||||
_log(
|
||||
'[tsb]',
|
||||
`time: ${colors.yellow((Date.now() - t1) + 'ms')} + \nmem: ${colors.cyan(Math.ceil(headNow / MB) + 'MB')} ${colors.bgCyan('delta: ' + Math.ceil((headNow - headUsed) / MB))}`
|
||||
);
|
||||
headUsed = headNow;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ const stream_1 = require("stream");
|
||||
const path_1 = require("path");
|
||||
const utils_1 = require("./utils");
|
||||
const fs_1 = require("fs");
|
||||
const log = require("fancy-log");
|
||||
const colors = require("ansi-colors");
|
||||
class EmptyDuplex extends stream_1.Duplex {
|
||||
_write(_chunk, _encoding, callback) { callback(); }
|
||||
_read() { this.push(null); }
|
||||
@@ -23,7 +25,7 @@ function createNullCompiler() {
|
||||
return result;
|
||||
}
|
||||
const _defaultOnError = (err) => console.log(JSON.stringify(err, null, 4));
|
||||
function create(projectPath, existingOptions, verbose = false, onError = _defaultOnError) {
|
||||
function create(projectPath, existingOptions, config, onError = _defaultOnError) {
|
||||
function printDiagnostic(diag) {
|
||||
if (!diag.file || !diag.start) {
|
||||
onError(ts.flattenDiagnosticMessageText(diag.messageText, '\n'));
|
||||
@@ -43,8 +45,14 @@ function create(projectPath, existingOptions, verbose = false, onError = _defaul
|
||||
cmdLine.errors.forEach(printDiagnostic);
|
||||
return createNullCompiler();
|
||||
}
|
||||
const _builder = builder.createTypeScriptBuilder({ verbose }, projectPath, cmdLine);
|
||||
function createStream(token) {
|
||||
function logFn(topic, message) {
|
||||
if (config.verbose) {
|
||||
log(colors.cyan(topic), message);
|
||||
}
|
||||
}
|
||||
// FULL COMPILE stream doing transpile, syntax and semantic diagnostics
|
||||
function createCompileStream(token) {
|
||||
const _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine);
|
||||
return through(function (file) {
|
||||
// give the file to the compiler
|
||||
if (file.isStream()) {
|
||||
@@ -57,7 +65,38 @@ function create(projectPath, existingOptions, verbose = false, onError = _defaul
|
||||
_builder.build(file => this.queue(file), printDiagnostic, token).catch(e => console.error(e)).then(() => this.queue(null));
|
||||
});
|
||||
}
|
||||
const result = (token) => createStream(token);
|
||||
// TRANSPILE ONLY stream doing just TS to JS conversion
|
||||
function createTranspileStream() {
|
||||
return through(function (file) {
|
||||
// give the file to the compiler
|
||||
if (file.isStream()) {
|
||||
this.emit('error', 'no support for streams');
|
||||
return;
|
||||
}
|
||||
if (!file.contents) {
|
||||
return;
|
||||
}
|
||||
const out = ts.transpileModule(String(file.contents), {
|
||||
compilerOptions: { ...cmdLine.options, declaration: false, sourceMap: false }
|
||||
});
|
||||
if (out.diagnostics) {
|
||||
out.diagnostics.forEach(printDiagnostic);
|
||||
}
|
||||
const outFile = new Vinyl({
|
||||
path: file.path.replace(/\.ts$/, '.js'),
|
||||
cwd: file.cwd,
|
||||
base: file.base,
|
||||
contents: Buffer.from(out.outputText),
|
||||
});
|
||||
this.push(outFile);
|
||||
logFn('Transpiled', file.path);
|
||||
});
|
||||
}
|
||||
const result = (token) => {
|
||||
return config.transplileOnly
|
||||
? createTranspileStream()
|
||||
: createCompileStream(token);
|
||||
};
|
||||
result.src = (opts) => {
|
||||
let _pos = 0;
|
||||
const _fileNames = cmdLine.fileNames.slice(0);
|
||||
|
||||
@@ -11,6 +11,8 @@ import { Readable, Writable, Duplex } from 'stream';
|
||||
import { dirname } from 'path';
|
||||
import { strings } from './utils';
|
||||
import { readFileSync, statSync } from 'fs';
|
||||
import * as log from 'fancy-log';
|
||||
import colors = require('ansi-colors');
|
||||
|
||||
export interface IncrementalCompiler {
|
||||
(token?: any): Readable & Writable;
|
||||
@@ -33,7 +35,7 @@ const _defaultOnError = (err: string) => console.log(JSON.stringify(err, null, 4
|
||||
export function create(
|
||||
projectPath: string,
|
||||
existingOptions: Partial<ts.CompilerOptions>,
|
||||
verbose: boolean = false,
|
||||
config: { verbose?: boolean; transplileOnly?: boolean },
|
||||
onError: (message: string) => void = _defaultOnError
|
||||
): IncrementalCompiler {
|
||||
|
||||
@@ -64,9 +66,16 @@ export function create(
|
||||
return createNullCompiler();
|
||||
}
|
||||
|
||||
const _builder = builder.createTypeScriptBuilder({ verbose }, projectPath, cmdLine);
|
||||
function logFn(topic: string, message: string): void {
|
||||
if (config.verbose) {
|
||||
log(colors.cyan(topic), message);
|
||||
}
|
||||
}
|
||||
|
||||
function createStream(token?: builder.CancellationToken): Readable & Writable {
|
||||
// FULL COMPILE stream doing transpile, syntax and semantic diagnostics
|
||||
function createCompileStream(token?: builder.CancellationToken): Readable & Writable {
|
||||
|
||||
const _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine);
|
||||
|
||||
return through(function (this: through.ThroughStream, file: Vinyl) {
|
||||
// give the file to the compiler
|
||||
@@ -86,7 +95,48 @@ export function create(
|
||||
});
|
||||
}
|
||||
|
||||
const result = (token: builder.CancellationToken) => createStream(token);
|
||||
// TRANSPILE ONLY stream doing just TS to JS conversion
|
||||
function createTranspileStream(): Readable & Writable {
|
||||
|
||||
return through(function (this: through.ThroughStream, file: Vinyl) {
|
||||
// give the file to the compiler
|
||||
if (file.isStream()) {
|
||||
this.emit('error', 'no support for streams');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.contents) {
|
||||
return;
|
||||
}
|
||||
|
||||
const out = ts.transpileModule(String(file.contents), {
|
||||
compilerOptions: { ...cmdLine.options, declaration: false, sourceMap: false }
|
||||
});
|
||||
|
||||
if (out.diagnostics) {
|
||||
out.diagnostics.forEach(printDiagnostic);
|
||||
}
|
||||
|
||||
const outFile = new Vinyl({
|
||||
path: file.path.replace(/\.ts$/, '.js'),
|
||||
cwd: file.cwd,
|
||||
base: file.base,
|
||||
contents: Buffer.from(out.outputText),
|
||||
});
|
||||
|
||||
this.push(outFile);
|
||||
|
||||
logFn('Transpiled', file.path);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const result = (token: builder.CancellationToken) => {
|
||||
return config.transplileOnly
|
||||
? createTranspileStream()
|
||||
: createCompileStream(token);
|
||||
};
|
||||
|
||||
result.src = (opts?: { cwd?: string; base?: string }) => {
|
||||
let _pos = 0;
|
||||
const _fileNames = cmdLine.fileNames.slice(0);
|
||||
|
||||
@@ -62,9 +62,15 @@ class Settings {
|
||||
const minimapOpts = options.get(EditorOption.minimap);
|
||||
const minimapEnabled = minimapOpts.enabled;
|
||||
const minimapSide = minimapOpts.side;
|
||||
const backgroundColor = minimapEnabled
|
||||
? theme.getColor(editorOverviewRulerBackground) || TokenizationRegistry.getDefaultBackground()
|
||||
: null;
|
||||
const themeColor = theme.getColor(editorOverviewRulerBackground);
|
||||
const defaultBackground = TokenizationRegistry.getDefaultBackground();
|
||||
let backgroundColor: Color | null = null;
|
||||
|
||||
if (themeColor !== undefined) {
|
||||
backgroundColor = themeColor;
|
||||
} else if (minimapEnabled) {
|
||||
backgroundColor = defaultBackground;
|
||||
}
|
||||
|
||||
if (backgroundColor === null || minimapSide === 'left') {
|
||||
this.backgroundColor = null;
|
||||
|
||||
@@ -4996,7 +4996,7 @@ export const EditorOptions = {
|
||||
scrollbar: register(new EditorScrollbar()),
|
||||
scrollBeyondLastColumn: register(new EditorIntOption(
|
||||
EditorOption.scrollBeyondLastColumn, 'scrollBeyondLastColumn',
|
||||
5, 0, Constants.MAX_SAFE_SMALL_INTEGER,
|
||||
4, 0, Constants.MAX_SAFE_SMALL_INTEGER,
|
||||
{ description: nls.localize('scrollBeyondLastColumn', "Controls the number of extra characters beyond which the editor will scroll horizontally.") }
|
||||
)),
|
||||
scrollBeyondLastLine: register(new EditorBooleanOption(
|
||||
|
||||
@@ -308,8 +308,8 @@ export class ViewLayout extends Disposable implements IViewLayout {
|
||||
const options = this._configuration.options;
|
||||
const wrappingInfo = options.get(EditorOption.wrappingInfo);
|
||||
const fontInfo = options.get(EditorOption.fontInfo);
|
||||
const layoutInfo = options.get(EditorOption.layoutInfo);
|
||||
if (wrappingInfo.isViewportWrapping) {
|
||||
const layoutInfo = options.get(EditorOption.layoutInfo);
|
||||
const minimap = options.get(EditorOption.minimap);
|
||||
if (maxLineWidth > layoutInfo.contentWidth + fontInfo.typicalHalfwidthCharacterWidth) {
|
||||
// This is a case where viewport wrapping is on, but the line extends above the viewport
|
||||
@@ -322,7 +322,7 @@ export class ViewLayout extends Disposable implements IViewLayout {
|
||||
} else {
|
||||
const extraHorizontalSpace = options.get(EditorOption.scrollBeyondLastColumn) * fontInfo.typicalHalfwidthCharacterWidth;
|
||||
const whitespaceMinWidth = this._linesLayout.getWhitespaceMinWidth();
|
||||
return Math.max(maxLineWidth + extraHorizontalSpace, whitespaceMinWidth);
|
||||
return Math.max(maxLineWidth + extraHorizontalSpace + layoutInfo.verticalScrollbarWidth, whitespaceMinWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -140,13 +140,16 @@ flakySuite('BackupMainService', () => {
|
||||
return pfs.Promises.rm(testDir);
|
||||
});
|
||||
|
||||
test('service validates backup workspaces on startup and cleans up (folder workspaces)', async function () {
|
||||
test('service validates backup workspaces on startup and cleans up (folder workspaces) (1)', async function () {
|
||||
|
||||
// 1) backup workspace path does not exist
|
||||
service.registerFolderBackupSync(toFolderBackupInfo(fooFile));
|
||||
service.registerFolderBackupSync(toFolderBackupInfo(barFile));
|
||||
await service.initialize();
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('service validates backup workspaces on startup and cleans up (folder workspaces) (2)', async function () {
|
||||
|
||||
// 2) backup workspace path exists with empty contents within
|
||||
fs.mkdirSync(service.toBackupPath(fooFile));
|
||||
@@ -157,6 +160,9 @@ flakySuite('BackupMainService', () => {
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));
|
||||
assert.ok(!fs.existsSync(service.toBackupPath(barFile)));
|
||||
});
|
||||
|
||||
test('service validates backup workspaces on startup and cleans up (folder workspaces) (3)', async function () {
|
||||
|
||||
// 3) backup workspace path exists with empty folders within
|
||||
fs.mkdirSync(service.toBackupPath(fooFile));
|
||||
@@ -169,6 +175,9 @@ flakySuite('BackupMainService', () => {
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));
|
||||
assert.ok(!fs.existsSync(service.toBackupPath(barFile)));
|
||||
});
|
||||
|
||||
test('service validates backup workspaces on startup and cleans up (folder workspaces) (4)', async function () {
|
||||
|
||||
// 4) backup workspace path points to a workspace that no longer exists
|
||||
// so it should convert the backup worspace to an empty workspace backup
|
||||
@@ -185,13 +194,16 @@ flakySuite('BackupMainService', () => {
|
||||
assert.strictEqual(service.getEmptyWindowBackupPaths().length, 1);
|
||||
});
|
||||
|
||||
test('service validates backup workspaces on startup and cleans up (root workspaces)', async function () {
|
||||
test('service validates backup workspaces on startup and cleans up (root workspaces) (1)', async function () {
|
||||
|
||||
// 1) backup workspace path does not exist
|
||||
service.registerWorkspaceBackupSync(toWorkspaceBackupInfo(fooFile.fsPath));
|
||||
service.registerWorkspaceBackupSync(toWorkspaceBackupInfo(barFile.fsPath));
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('service validates backup workspaces on startup and cleans up (root workspaces) (2)', async function () {
|
||||
|
||||
// 2) backup workspace path exists with empty contents within
|
||||
fs.mkdirSync(service.toBackupPath(fooFile));
|
||||
@@ -202,6 +214,9 @@ flakySuite('BackupMainService', () => {
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));
|
||||
assert.ok(!fs.existsSync(service.toBackupPath(barFile)));
|
||||
});
|
||||
|
||||
test('service validates backup workspaces on startup and cleans up (root workspaces) (3)', async function () {
|
||||
|
||||
// 3) backup workspace path exists with empty folders within
|
||||
fs.mkdirSync(service.toBackupPath(fooFile));
|
||||
@@ -214,6 +229,9 @@ flakySuite('BackupMainService', () => {
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));
|
||||
assert.ok(!fs.existsSync(service.toBackupPath(barFile)));
|
||||
});
|
||||
|
||||
test('service validates backup workspaces on startup and cleans up (root workspaces) (4)', async function () {
|
||||
|
||||
// 4) backup workspace path points to a workspace that no longer exists
|
||||
// so it should convert the backup worspace to an empty workspace backup
|
||||
@@ -273,13 +291,19 @@ flakySuite('BackupMainService', () => {
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getFolderBackupPaths() should return [] when workspaces.json is not properly formed JSON', async () => {
|
||||
test('getFolderBackupPaths() should return [] when workspaces.json is not properly formed JSON (1)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '');
|
||||
await service.initialize();
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getFolderBackupPaths() should return [] when workspaces.json is not properly formed JSON (2)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{]');
|
||||
await service.initialize();
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getFolderBackupPaths() should return [] when workspaces.json is not properly formed JSON (3)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, 'foo');
|
||||
await service.initialize();
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
@@ -291,22 +315,37 @@ flakySuite('BackupMainService', () => {
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getFolderBackupPaths() should return [] when folderWorkspaceInfos in workspaces.json is not a string array', async () => {
|
||||
test('getFolderBackupPaths() should return [] when folderWorkspaceInfos in workspaces.json is not a string array (1)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaceInfos":{}}');
|
||||
await service.initialize();
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getFolderBackupPaths() should return [] when folderWorkspaceInfos in workspaces.json is not a string array (2)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaceInfos":{"foo": ["bar"]}}');
|
||||
await service.initialize();
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getFolderBackupPaths() should return [] when folderWorkspaceInfos in workspaces.json is not a string array (3)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaceInfos":{"foo": []}}');
|
||||
await service.initialize();
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getFolderBackupPaths() should return [] when folderWorkspaceInfos in workspaces.json is not a string array (4)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaceInfos":{"foo": "bar"}}');
|
||||
await service.initialize();
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getFolderBackupPaths() should return [] when folderWorkspaceInfos in workspaces.json is not a string array (5)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaceInfos":"foo"}');
|
||||
await service.initialize();
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getFolderBackupPaths() should return [] when folderWorkspaceInfos in workspaces.json is not a string array (6)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaceInfos":1}');
|
||||
await service.initialize();
|
||||
assertEqualFolderInfos(service.getFolderBackupPaths(), []);
|
||||
@@ -333,13 +372,19 @@ flakySuite('BackupMainService', () => {
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when workspaces.json is not properly formed JSON', async () => {
|
||||
test('getWorkspaceBackups() should return [] when workspaces.json is not properly formed JSON (1)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when workspaces.json is not properly formed JSON (2)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{]');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when workspaces.json is not properly formed JSON (3)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, 'foo');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
@@ -351,43 +396,73 @@ flakySuite('BackupMainService', () => {
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootWorkspaces in workspaces.json is not a object array', async () => {
|
||||
test('getWorkspaceBackups() should return [] when rootWorkspaces in workspaces.json is not a object array (1)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootWorkspaces in workspaces.json is not a object array (2)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{"foo": ["bar"]}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootWorkspaces in workspaces.json is not a object array (3)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{"foo": []}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootWorkspaces in workspaces.json is not a object array (4)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{"foo": "bar"}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootWorkspaces in workspaces.json is not a object array (5)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":"foo"}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootWorkspaces in workspaces.json is not a object array (6)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":1}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootURIWorkspaces in workspaces.json is not a object array', async () => {
|
||||
test('getWorkspaceBackups() should return [] when rootURIWorkspaces in workspaces.json is not a object array (1)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":{}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootURIWorkspaces in workspaces.json is not a object array (2)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":{"foo": ["bar"]}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootURIWorkspaces in workspaces.json is not a object array (3)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":{"foo": []}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootURIWorkspaces in workspaces.json is not a object array (4)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":{"foo": "bar"}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootURIWorkspaces in workspaces.json is not a object array (5)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":"foo"}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
});
|
||||
|
||||
test('getWorkspaceBackups() should return [] when rootURIWorkspaces in workspaces.json is not a object array (6)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":1}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getWorkspaceBackups(), []);
|
||||
@@ -407,13 +482,19 @@ flakySuite('BackupMainService', () => {
|
||||
assert.deepStrictEqual(service.getEmptyWindowBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json is not properly formed JSON', async () => {
|
||||
test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json is not properly formed JSON (1)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getEmptyWindowBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json is not properly formed JSON (2)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{]');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getEmptyWindowBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json is not properly formed JSON (3)', async () => {
|
||||
fs.writeFileSync(backupWorkspacesPath, 'foo');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getEmptyWindowBackupPaths(), []);
|
||||
@@ -425,22 +506,37 @@ flakySuite('BackupMainService', () => {
|
||||
assert.deepStrictEqual(service.getEmptyWindowBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array', async function () {
|
||||
test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array (1)', async function () {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getEmptyWindowBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array (2)', async function () {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": ["bar"]}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getEmptyWindowBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array (3)', async function () {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": []}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getEmptyWindowBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array (4)', async function () {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": "bar"}}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getEmptyWindowBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array (5)', async function () {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":"foo"}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getEmptyWindowBackupPaths(), []);
|
||||
});
|
||||
|
||||
test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array (6)', async function () {
|
||||
fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":1}');
|
||||
await service.initialize();
|
||||
assert.deepStrictEqual(service.getEmptyWindowBackupPaths(), []);
|
||||
|
||||
@@ -99,6 +99,9 @@ export class CommandCenterControl {
|
||||
// label: just workspace name and optional decorations
|
||||
const { prefix, suffix } = windowTitle.getTitleDecorations();
|
||||
let label = windowTitle.workspaceName;
|
||||
if (!label) {
|
||||
label = localize('label.dfl', "Search");
|
||||
}
|
||||
if (prefix) {
|
||||
label = localize('label1', "{0} {1}", prefix, label);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor';
|
||||
import { DEFAULT_EDITOR_MAX_DIMENSIONS, DEFAULT_EDITOR_MIN_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
|
||||
import { IEditorControl, IEditorOpenContext } from 'vs/workbench/common/editor';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
@@ -383,12 +384,11 @@ abstract class CodeEditorView extends Disposable {
|
||||
|
||||
public readonly view: IView = {
|
||||
element: this.htmlElements.root,
|
||||
minimumWidth: 10,
|
||||
maximumWidth: Number.MAX_SAFE_INTEGER,
|
||||
minimumHeight: 10,
|
||||
maximumHeight: Number.MAX_SAFE_INTEGER,
|
||||
minimumWidth: DEFAULT_EDITOR_MIN_DIMENSIONS.width,
|
||||
maximumWidth: DEFAULT_EDITOR_MAX_DIMENSIONS.width,
|
||||
minimumHeight: DEFAULT_EDITOR_MIN_DIMENSIONS.height,
|
||||
maximumHeight: DEFAULT_EDITOR_MAX_DIMENSIONS.height,
|
||||
onDidChange: this._onDidViewChange.event,
|
||||
|
||||
layout: (width: number, height: number, top: number, left: number) => {
|
||||
setStyle(this.htmlElements.root, { width, height, top, left });
|
||||
this.editor.layout({
|
||||
|
||||
@@ -539,6 +539,12 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP
|
||||
});
|
||||
}
|
||||
|
||||
public clearKeyboardShortcutSearchHistory(): void {
|
||||
this.searchWidget.inputBox.clearHistory();
|
||||
this.getMemento(StorageScope.GLOBAL, StorageTarget.USER)['searchHistory'] = this.searchWidget.inputBox.getHistory();
|
||||
this.saveState();
|
||||
}
|
||||
|
||||
private renderKeybindingsEntries(reset: boolean, preserveFocus?: boolean): void {
|
||||
if (this.keybindingsEditorModel) {
|
||||
const filter = this.searchWidget.getValue();
|
||||
|
||||
@@ -34,7 +34,7 @@ import { ConfigureLanguageBasedSettingsAction } from 'vs/workbench/contrib/prefe
|
||||
import { SettingsEditorContribution } from 'vs/workbench/contrib/preferences/browser/preferencesEditor';
|
||||
import { preferencesOpenSettingsIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons';
|
||||
import { SettingsEditor2, SettingsFocusContext } from 'vs/workbench/contrib/preferences/browser/settingsEditor2';
|
||||
import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, CONTEXT_WHEN_FOCUS, KEYBINDINGS_EDITOR_COMMAND_ADD, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND_TITLE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_EXTENSION_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences';
|
||||
import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, CONTEXT_WHEN_FOCUS, KEYBINDINGS_EDITOR_COMMAND_ADD, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_HISTORY, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND_TITLE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_EXTENSION_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences';
|
||||
import { PreferencesContribution } from 'vs/workbench/contrib/preferences/common/preferencesContribution';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
@@ -887,6 +887,28 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_HISTORY,
|
||||
title: nls.localize('clearHistory', "Clear Keyboard Shortcuts Search History"),
|
||||
category,
|
||||
menu: [
|
||||
{
|
||||
id: MenuId.CommandPalette,
|
||||
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR),
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
run(accessor: ServicesAccessor) {
|
||||
const editorPane = accessor.get(IEditorService).activeEditorPane;
|
||||
if (editorPane instanceof KeybindingsEditor) {
|
||||
editorPane.clearKeyboardShortcutSearchHistory();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.registerKeybindingEditorActions();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ export const CONTEXT_WHEN_FOCUS = new RawContextKey<boolean>('whenFocus', false)
|
||||
|
||||
export const KEYBINDINGS_EDITOR_COMMAND_SEARCH = 'keybindings.editor.searchKeybindings';
|
||||
export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'keybindings.editor.clearSearchResults';
|
||||
export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_HISTORY = 'keybindings.editor.clearSearchHistory';
|
||||
export const KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS = 'keybindings.editor.recordSearchKeys';
|
||||
export const KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE = 'keybindings.editor.toggleSortByPrecedence';
|
||||
export const KEYBINDINGS_EDITOR_COMMAND_DEFINE = 'keybindings.editor.defineKeybinding';
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.test-explorer .test-item .label,
|
||||
.test-output-peek-tree .test-peek-item .name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.test-explorer .test-item,
|
||||
.test-output-peek-tree .test-peek-item {
|
||||
display: flex;
|
||||
@@ -210,3 +216,10 @@
|
||||
.test-message-inline-content-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.test-label-description {
|
||||
opacity: .7;
|
||||
margin-left: 0.5em;
|
||||
font-size: .9em;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testPro
|
||||
import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult';
|
||||
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { getContextForTestItem, ITestService, testsInFile } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { stripIcons } from 'vs/base/common/iconLabels';
|
||||
|
||||
const MAX_INLINE_MESSAGE_LENGTH = 128;
|
||||
|
||||
@@ -818,7 +819,7 @@ class MultiRunTestDecoration extends RunTestDecoration implements ITestDecoratio
|
||||
const testSubmenus = this.tests.map(({ test, resultItem }) => {
|
||||
const actions = this.getTestContextMenuActions(test, resultItem);
|
||||
disposable.add(actions);
|
||||
return new SubmenuAction(test.item.extId, test.item.label, actions.object);
|
||||
return new SubmenuAction(test.item.extId, stripIcons(test.item.label), actions.object);
|
||||
});
|
||||
|
||||
return { object: Separator.join(allActions, testSubmenus), dispose: () => disposable.dispose() };
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as dom from 'vs/base/browser/dom';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { ActionBar, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { DefaultKeyboardNavigationDelegate, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { AbstractTreeViewState, IAbstractTreeViewState } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
@@ -32,7 +33,6 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
|
||||
@@ -44,7 +44,6 @@ import { foreground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
|
||||
import { IResourceLabel, IResourceLabelOptions, IResourceLabelProps, ResourceLabels } from 'vs/workbench/browser/labels';
|
||||
import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane';
|
||||
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
@@ -58,7 +57,6 @@ import { ITestingProgressUiService } from 'vs/workbench/contrib/testing/browser/
|
||||
import { getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration';
|
||||
import { labelForTestInState, TestCommandId, TestExplorerViewMode, TestExplorerViewSorting, Testing } from 'vs/workbench/contrib/testing/common/constants';
|
||||
import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue';
|
||||
import { InternalTestItem, ITestRunProfile, TestItemExpandState, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
import { ITestExplorerFilterState, TestExplorerFilterState, TestFilterTerm } from 'vs/workbench/contrib/testing/common/testExplorerFilterState';
|
||||
import { TestId } from 'vs/workbench/contrib/testing/common/testId';
|
||||
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
|
||||
@@ -68,6 +66,7 @@ import { canUseProfileWithTest, ITestProfileService } from 'vs/workbench/contrib
|
||||
import { TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult';
|
||||
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { IMainThreadTestCollection, ITestService, testCollectionIsEmpty } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { InternalTestItem, ITestRunProfile, TestItemExpandState, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
export class TestingExplorerView extends ViewPane {
|
||||
@@ -474,8 +473,6 @@ export class TestingExplorerViewModel extends Disposable {
|
||||
this._viewMode.set(this.storageService.get('testing.viewMode', StorageScope.WORKSPACE, TestExplorerViewMode.Tree) as TestExplorerViewMode);
|
||||
this._viewSorting.set(this.storageService.get('testing.viewSorting', StorageScope.WORKSPACE, TestExplorerViewSorting.ByLocation) as TestExplorerViewSorting);
|
||||
|
||||
const labels = this._register(instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: onDidChangeVisibility }));
|
||||
|
||||
this.reevaluateWelcomeState();
|
||||
this.filter = this.instantiationService.createInstance(TestsFilter, testService.collection);
|
||||
this.tree = instantiationService.createInstance(
|
||||
@@ -484,7 +481,7 @@ export class TestingExplorerViewModel extends Disposable {
|
||||
listContainer,
|
||||
new ListDelegate(),
|
||||
[
|
||||
instantiationService.createInstance(TestItemRenderer, labels, this.actionRunner),
|
||||
instantiationService.createInstance(TestItemRenderer, this.actionRunner),
|
||||
instantiationService.createInstance(ErrorRenderer),
|
||||
],
|
||||
{
|
||||
@@ -1095,7 +1092,7 @@ class ErrorRenderer implements ITreeRenderer<TestTreeErrorMessage, FuzzyScore, I
|
||||
}
|
||||
|
||||
interface IActionableElementTemplateData {
|
||||
label: IResourceLabel;
|
||||
label: HTMLElement;
|
||||
icon: HTMLElement;
|
||||
wrapper: HTMLElement;
|
||||
actionBar: ActionBar;
|
||||
@@ -1106,7 +1103,6 @@ interface IActionableElementTemplateData {
|
||||
abstract class ActionableItemTemplateData<T extends TestItemTreeElement> extends Disposable
|
||||
implements ITreeRenderer<T, FuzzyScore, IActionableElementTemplateData> {
|
||||
constructor(
|
||||
protected readonly labels: ResourceLabels,
|
||||
private readonly actionRunner: TestExplorerActionRunner,
|
||||
@IMenuService private readonly menuService: IMenuService,
|
||||
@ITestService protected readonly testService: ITestService,
|
||||
@@ -1129,8 +1125,7 @@ abstract class ActionableItemTemplateData<T extends TestItemTreeElement> extends
|
||||
const wrapper = dom.append(container, dom.$('.test-item'));
|
||||
|
||||
const icon = dom.append(wrapper, dom.$('.computed-state'));
|
||||
const name = dom.append(wrapper, dom.$('.name'));
|
||||
const label = this.labels.create(name, { supportHighlights: true });
|
||||
const label = dom.append(wrapper, dom.$('.label'));
|
||||
|
||||
dom.append(wrapper, dom.$(ThemeIcon.asCSSSelector(icons.testingHiddenIcon)));
|
||||
const actionBar = new ActionBar(wrapper, {
|
||||
@@ -1141,7 +1136,7 @@ abstract class ActionableItemTemplateData<T extends TestItemTreeElement> extends
|
||||
: undefined
|
||||
});
|
||||
|
||||
return { wrapper, label, actionBar, icon, elementDisposable: [], templateDisposable: [label, actionBar] };
|
||||
return { wrapper, label, actionBar, icon, elementDisposable: [], templateDisposable: [actionBar] };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1192,10 +1187,6 @@ class TestItemRenderer extends ActionableItemTemplateData<TestItemTreeElement> {
|
||||
public override renderElement(node: ITreeNode<TestItemTreeElement, FuzzyScore>, depth: number, data: IActionableElementTemplateData): void {
|
||||
super.renderElement(node, depth, data);
|
||||
|
||||
const label: IResourceLabelProps = { name: node.element.label };
|
||||
const options: IResourceLabelOptions = {};
|
||||
data.label.setResource(label, options);
|
||||
|
||||
const testHidden = this.testService.excluded.contains(node.element.test);
|
||||
data.wrapper.classList.toggle('test-is-hidden', testHidden);
|
||||
|
||||
@@ -1205,18 +1196,20 @@ class TestItemRenderer extends ActionableItemTemplateData<TestItemTreeElement> {
|
||||
: node.element.state);
|
||||
|
||||
data.icon.className = 'computed-state ' + (icon ? ThemeIcon.asClassName(icon) : '');
|
||||
label.resource = node.element.test.item.uri;
|
||||
options.title = getLabelForTestTreeElement(node.element);
|
||||
options.fileKind = FileKind.FILE;
|
||||
label.description = node.element.description || undefined;
|
||||
|
||||
data.label.title = getLabelForTestTreeElement(node.element);
|
||||
dom.reset(data.label, ...renderLabelWithIcons(node.element.label));
|
||||
|
||||
let description = node.element.description;
|
||||
if (node.element.duration !== undefined) {
|
||||
label.description = label.description
|
||||
? `${label.description}: ${formatDuration(node.element.duration)}`
|
||||
description = description
|
||||
? `${description}: ${formatDuration(node.element.duration)}`
|
||||
: formatDuration(node.element.duration);
|
||||
}
|
||||
|
||||
data.label.setResource(label, options);
|
||||
if (description) {
|
||||
dom.append(data.label, dom.$('span.test-label-description', {}, description));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as dom from 'vs/base/browser/dom';
|
||||
import { renderStringAsPlaintext } from 'vs/base/browser/markdownRenderer';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
import { IIdentityProvider } from 'vs/base/browser/ui/list/list';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview';
|
||||
@@ -20,6 +21,7 @@ import { Color } from 'vs/base/common/color';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { stripIcons } from 'vs/base/common/iconLabels';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Lazy } from 'vs/base/common/lazy';
|
||||
@@ -53,7 +55,6 @@ import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listSe
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels';
|
||||
import { CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { EditorModel } from 'vs/workbench/common/editor/editorModel';
|
||||
import { flatTestItemDelimiter } from 'vs/workbench/contrib/testing/browser/explorerProjections/display';
|
||||
@@ -65,7 +66,6 @@ import { AutoOpenPeekViewWhen, getTestingConfiguration, TestingConfigKeys } from
|
||||
import { Testing } from 'vs/workbench/contrib/testing/common/constants';
|
||||
import { IObservableValue, MutableObservableValue } from 'vs/workbench/contrib/testing/common/observableValue';
|
||||
import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue';
|
||||
import { IRichLocation, ITestErrorMessage, ITestItem, ITestMessage, ITestRunTask, ITestTaskState, TestMessageType, TestResultItem, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
import { ITestExplorerFilterState } from 'vs/workbench/contrib/testing/common/testExplorerFilterState';
|
||||
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
|
||||
import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener';
|
||||
@@ -75,6 +75,7 @@ import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testPro
|
||||
import { ITestResult, maxCountPriority, resultItemParents, TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult';
|
||||
import { ITestResultService, ResultChangeEvent } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { IRichLocation, ITestErrorMessage, ITestItem, ITestMessage, ITestRunTask, ITestTaskState, TestMessageType, TestResultItem, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
class TestDto {
|
||||
@@ -769,7 +770,7 @@ class TestingOutputPeek extends PeekViewWidget {
|
||||
*/
|
||||
public async showInPlace(dto: TestDto) {
|
||||
const message = dto.messages[dto.messageIndex];
|
||||
this.setTitle(firstLine(renderStringAsPlaintext(message.message)), dto.test.label);
|
||||
this.setTitle(firstLine(renderStringAsPlaintext(message.message)), stripIcons(dto.test.label));
|
||||
this.didReveal.fire(dto);
|
||||
this.visibilityChange.fire(true);
|
||||
await Promise.all(this.contentProviders.map(p => p.update(dto, message)));
|
||||
@@ -1084,6 +1085,7 @@ interface ITreeElement {
|
||||
context: unknown;
|
||||
id: string;
|
||||
label: string;
|
||||
labelWithIcons?: readonly (HTMLSpanElement | string)[];
|
||||
icon?: ThemeIcon;
|
||||
description?: string;
|
||||
ariaLabel?: string;
|
||||
@@ -1111,6 +1113,7 @@ export class TestCaseElement implements ITreeElement {
|
||||
public readonly context = this.test.item.extId;
|
||||
public readonly id = `${this.results.id}/${this.test.item.extId}`;
|
||||
public readonly label = this.test.item.label;
|
||||
public readonly labelWithIcons = renderLabelWithIcons(this.label);
|
||||
public readonly description?: string;
|
||||
|
||||
public get icon() {
|
||||
@@ -1209,7 +1212,6 @@ class OutputPeekTree extends Disposable {
|
||||
super();
|
||||
|
||||
this.treeActions = instantiationService.createInstance(TreeActionsProvider);
|
||||
const labels = instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility });
|
||||
const diffIdentityProvider: IIdentityProvider<TreeElement> = {
|
||||
getId(e: TreeElement) {
|
||||
return e.id;
|
||||
@@ -1224,7 +1226,7 @@ class OutputPeekTree extends Disposable {
|
||||
getHeight: () => 22,
|
||||
getTemplateId: () => TestRunElementRenderer.ID,
|
||||
},
|
||||
[instantiationService.createInstance(TestRunElementRenderer, labels, this.treeActions)],
|
||||
[instantiationService.createInstance(TestRunElementRenderer, this.treeActions)],
|
||||
{
|
||||
compressionEnabled: true,
|
||||
hideTwistiesOfChildlessElements: true,
|
||||
@@ -1418,7 +1420,7 @@ class OutputPeekTree extends Disposable {
|
||||
}
|
||||
|
||||
interface TemplateData {
|
||||
label: IResourceLabel;
|
||||
label: HTMLElement;
|
||||
icon: HTMLElement;
|
||||
actionBar: ActionBar;
|
||||
elementDisposable: DisposableStore;
|
||||
@@ -1430,7 +1432,6 @@ class TestRunElementRenderer implements ICompressibleTreeRenderer<ITreeElement,
|
||||
public readonly templateId = TestRunElementRenderer.ID;
|
||||
|
||||
constructor(
|
||||
private readonly labels: ResourceLabels,
|
||||
private readonly treeActions: TreeActionsProvider,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
) { }
|
||||
@@ -1451,10 +1452,7 @@ class TestRunElementRenderer implements ICompressibleTreeRenderer<ITreeElement,
|
||||
const templateDisposable = new DisposableStore();
|
||||
const wrapper = dom.append(container, dom.$('.test-peek-item'));
|
||||
const icon = dom.append(wrapper, dom.$('.state'));
|
||||
const name = dom.append(wrapper, dom.$('.name'));
|
||||
|
||||
const label = this.labels.create(name, { supportHighlights: true });
|
||||
templateDisposable.add(label);
|
||||
const label = dom.append(wrapper, dom.$('.name'));
|
||||
|
||||
const actionBar = new ActionBar(wrapper, {
|
||||
actionViewItemProvider: action =>
|
||||
@@ -1486,7 +1484,13 @@ class TestRunElementRenderer implements ICompressibleTreeRenderer<ITreeElement,
|
||||
|
||||
private doRender(element: ITreeElement, templateData: TemplateData) {
|
||||
templateData.elementDisposable.clear();
|
||||
templateData.label.setLabel(element.label, element.description);
|
||||
if (element.labelWithIcons) {
|
||||
dom.reset(templateData.label, ...element.labelWithIcons);
|
||||
} else if (element.description) {
|
||||
dom.reset(templateData.label, element.label, dom.$('span.test-label-description', {}, element.description));
|
||||
} else {
|
||||
dom.reset(templateData.label, element.label);
|
||||
}
|
||||
|
||||
const icon = element.icon;
|
||||
templateData.icon.className = `computed-state ${icon ? ThemeIcon.asClassName(icon) : ''}`;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { stripIcons } from 'vs/base/common/iconLabels';
|
||||
import { localize } from 'vs/nls';
|
||||
import { TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
|
||||
@@ -44,7 +45,7 @@ export const testStateNames: { [K in TestResultState]: string } = {
|
||||
export const labelForTestInState = (label: string, state: TestResultState) => localize({
|
||||
key: 'testing.treeElementLabel',
|
||||
comment: ['label then the unit tests state, for example "Addition Tests (Running)"'],
|
||||
}, '{0} ({1})', label, testStateNames[state]);
|
||||
}, '{0} ({1})', stripIcons(label), testStateNames[state]);
|
||||
|
||||
export const testConfigurationGroupNames: { [K in TestRunProfileBitset]: string } = {
|
||||
[TestRunProfileBitset.Debug]: localize('testGroup.debug', 'Debug'),
|
||||
|
||||
@@ -26,6 +26,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { Memento } from 'vs/workbench/common/memento';
|
||||
import { firstOrDefault } from 'vs/base/common/arrays';
|
||||
|
||||
const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoint<ResourceLabelFormatter[]>({
|
||||
extensionPoint: 'resourceLabelFormatters',
|
||||
@@ -223,8 +224,23 @@ export class LabelService extends Disposable implements ILabelService {
|
||||
}
|
||||
|
||||
// Relative label
|
||||
if (options.relative) {
|
||||
const folder = this.contextService?.getWorkspaceFolder(resource);
|
||||
if (options.relative && this.contextService) {
|
||||
let folder = this.contextService.getWorkspaceFolder(resource);
|
||||
if (!folder) {
|
||||
|
||||
// It is possible that the resource we want to resolve the
|
||||
// workspace folder for is not using the same scheme as
|
||||
// the folders in the workspace, so we help by trying again
|
||||
// to resolve a workspace folder by trying again with a
|
||||
// scheme that is workspace contained.
|
||||
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
const firstFolder = firstOrDefault(workspace.folders);
|
||||
if (firstFolder && resource.scheme !== firstFolder.uri.scheme && resource.path.startsWith(posix.sep)) {
|
||||
folder = this.contextService.getWorkspaceFolder(firstFolder.uri.with({ path: resource.path }));
|
||||
}
|
||||
}
|
||||
|
||||
if (folder) {
|
||||
const folderLabel = this.formatUri(folder.uri, formatting, options.noPrefix);
|
||||
|
||||
|
||||
@@ -423,7 +423,7 @@ suite('StoredFileWorkingCopy', function () {
|
||||
assert.strictEqual(backupContents, 'hello backup');
|
||||
});
|
||||
|
||||
test('save (no errors)', async () => {
|
||||
test('save (no errors) - simple', async () => {
|
||||
let savedCounter = 0;
|
||||
let lastSaveEvent: IStoredFileWorkingCopySaveEvent | undefined = undefined;
|
||||
workingCopy.onDidSave(e => {
|
||||
@@ -453,21 +453,49 @@ suite('StoredFileWorkingCopy', function () {
|
||||
assert.ok(lastSaveEvent!.stat);
|
||||
assert.ok(isStoredFileWorkingCopySaveEvent(lastSaveEvent!));
|
||||
assert.strictEqual(workingCopy.model?.pushedStackElement, true);
|
||||
});
|
||||
|
||||
test('save (no errors) - save reason', async () => {
|
||||
let savedCounter = 0;
|
||||
let lastSaveEvent: IStoredFileWorkingCopySaveEvent | undefined = undefined;
|
||||
workingCopy.onDidSave(e => {
|
||||
savedCounter++;
|
||||
lastSaveEvent = e;
|
||||
});
|
||||
|
||||
let saveErrorCounter = 0;
|
||||
workingCopy.onDidSaveError(() => {
|
||||
saveErrorCounter++;
|
||||
});
|
||||
|
||||
// save reason
|
||||
await workingCopy.resolve();
|
||||
workingCopy.model?.updateContents('hello save');
|
||||
|
||||
const source = SaveSourceRegistry.registerSource('testSource', 'Hello Save');
|
||||
await workingCopy.save({ reason: SaveReason.AUTO, source });
|
||||
|
||||
assert.strictEqual(savedCounter, 2);
|
||||
assert.strictEqual(savedCounter, 1);
|
||||
assert.strictEqual(saveErrorCounter, 0);
|
||||
assert.strictEqual(workingCopy.isDirty(), false);
|
||||
assert.strictEqual((lastSaveEvent as IStoredFileWorkingCopySaveEvent).reason, SaveReason.AUTO);
|
||||
assert.strictEqual((lastSaveEvent as IStoredFileWorkingCopySaveEvent).source, source);
|
||||
assert.strictEqual((lastSaveEvent! as IStoredFileWorkingCopySaveEvent).reason, SaveReason.AUTO);
|
||||
assert.strictEqual((lastSaveEvent! as IStoredFileWorkingCopySaveEvent).source, source);
|
||||
});
|
||||
|
||||
test('save (no errors) - multiple', async () => {
|
||||
let savedCounter = 0;
|
||||
workingCopy.onDidSave(e => {
|
||||
savedCounter++;
|
||||
});
|
||||
|
||||
let saveErrorCounter = 0;
|
||||
workingCopy.onDidSaveError(() => {
|
||||
saveErrorCounter++;
|
||||
});
|
||||
|
||||
// multiple saves in parallel are fine and result
|
||||
// in a single save when content does not change
|
||||
await workingCopy.resolve();
|
||||
workingCopy.model?.updateContents('hello save');
|
||||
await Promises.settled([
|
||||
workingCopy.save({ reason: SaveReason.AUTO }),
|
||||
@@ -475,34 +503,87 @@ suite('StoredFileWorkingCopy', function () {
|
||||
workingCopy.save({ reason: SaveReason.WINDOW_CHANGE })
|
||||
]);
|
||||
|
||||
assert.strictEqual(savedCounter, 3);
|
||||
assert.strictEqual(savedCounter, 1);
|
||||
assert.strictEqual(saveErrorCounter, 0);
|
||||
assert.strictEqual(workingCopy.isDirty(), false);
|
||||
});
|
||||
|
||||
test('save (no errors) - multiple, cancellation', async () => {
|
||||
let savedCounter = 0;
|
||||
workingCopy.onDidSave(e => {
|
||||
savedCounter++;
|
||||
});
|
||||
|
||||
let saveErrorCounter = 0;
|
||||
workingCopy.onDidSaveError(() => {
|
||||
saveErrorCounter++;
|
||||
});
|
||||
|
||||
// multiple saves in parallel are fine and result
|
||||
// in just one save operation (the second one
|
||||
// cancels the first)
|
||||
await workingCopy.resolve();
|
||||
workingCopy.model?.updateContents('hello save');
|
||||
const firstSave = workingCopy.save();
|
||||
workingCopy.model?.updateContents('hello save more');
|
||||
const secondSave = workingCopy.save();
|
||||
|
||||
await Promises.settled([firstSave, secondSave]);
|
||||
assert.strictEqual(savedCounter, 4);
|
||||
assert.strictEqual(savedCounter, 1);
|
||||
assert.strictEqual(saveErrorCounter, 0);
|
||||
assert.strictEqual(workingCopy.isDirty(), false);
|
||||
});
|
||||
|
||||
test('save (no errors) - not forced but not dirty', async () => {
|
||||
let savedCounter = 0;
|
||||
workingCopy.onDidSave(e => {
|
||||
savedCounter++;
|
||||
});
|
||||
|
||||
let saveErrorCounter = 0;
|
||||
workingCopy.onDidSaveError(() => {
|
||||
saveErrorCounter++;
|
||||
});
|
||||
|
||||
// no save when not forced and not dirty
|
||||
await workingCopy.resolve();
|
||||
await workingCopy.save();
|
||||
assert.strictEqual(savedCounter, 4);
|
||||
assert.strictEqual(savedCounter, 0);
|
||||
assert.strictEqual(saveErrorCounter, 0);
|
||||
assert.strictEqual(workingCopy.isDirty(), false);
|
||||
});
|
||||
|
||||
test('save (no errors) - forced but not dirty', async () => {
|
||||
let savedCounter = 0;
|
||||
workingCopy.onDidSave(e => {
|
||||
savedCounter++;
|
||||
});
|
||||
|
||||
let saveErrorCounter = 0;
|
||||
workingCopy.onDidSaveError(() => {
|
||||
saveErrorCounter++;
|
||||
});
|
||||
|
||||
// save when forced even when not dirty
|
||||
await workingCopy.resolve();
|
||||
await workingCopy.save({ force: true });
|
||||
assert.strictEqual(savedCounter, 5);
|
||||
assert.strictEqual(savedCounter, 1);
|
||||
assert.strictEqual(saveErrorCounter, 0);
|
||||
assert.strictEqual(workingCopy.isDirty(), false);
|
||||
});
|
||||
|
||||
test('save (no errors) - save clears orphaned', async () => {
|
||||
let savedCounter = 0;
|
||||
workingCopy.onDidSave(e => {
|
||||
savedCounter++;
|
||||
});
|
||||
|
||||
let saveErrorCounter = 0;
|
||||
workingCopy.onDidSaveError(() => {
|
||||
saveErrorCounter++;
|
||||
});
|
||||
|
||||
await workingCopy.resolve();
|
||||
|
||||
// save clears orphaned
|
||||
const orphanedPromise = Event.toPromise(workingCopy.onDidChangeOrphaned);
|
||||
@@ -514,7 +595,7 @@ suite('StoredFileWorkingCopy', function () {
|
||||
assert.strictEqual(workingCopy.hasState(StoredFileWorkingCopyState.ORPHAN), true);
|
||||
|
||||
await workingCopy.save({ force: true });
|
||||
assert.strictEqual(savedCounter, 6);
|
||||
assert.strictEqual(savedCounter, 1);
|
||||
assert.strictEqual(saveErrorCounter, 0);
|
||||
assert.strictEqual(workingCopy.isDirty(), false);
|
||||
assert.strictEqual(workingCopy.hasState(StoredFileWorkingCopyState.ORPHAN), false);
|
||||
|
||||
@@ -540,9 +540,11 @@ flakySuite('WorkingCopyBackupService', () => {
|
||||
const backupId2 = toTypedWorkingCopyId(fooFile, 'type1');
|
||||
const backupId3 = toTypedWorkingCopyId(fooFile, 'type2');
|
||||
|
||||
await service.backup(backupId1);
|
||||
await service.backup(backupId2);
|
||||
await service.backup(backupId3);
|
||||
await Promise.all([
|
||||
service.backup(backupId1),
|
||||
service.backup(backupId2),
|
||||
service.backup(backupId3)
|
||||
]);
|
||||
|
||||
assert.strictEqual(readdirSync(join(workspaceBackupPath, 'file')).length, 3);
|
||||
|
||||
@@ -609,9 +611,11 @@ flakySuite('WorkingCopyBackupService', () => {
|
||||
const backupId2 = toTypedWorkingCopyId(fooFile, 'type1');
|
||||
const backupId3 = toTypedWorkingCopyId(fooFile, 'type2');
|
||||
|
||||
await service.backup(backupId1);
|
||||
await service.backup(backupId2);
|
||||
await service.backup(backupId3);
|
||||
await Promise.all([
|
||||
service.backup(backupId1),
|
||||
service.backup(backupId2),
|
||||
service.backup(backupId3)
|
||||
]);
|
||||
|
||||
assert.strictEqual(readdirSync(join(workspaceBackupPath, 'file')).length, 3);
|
||||
|
||||
@@ -716,9 +720,11 @@ flakySuite('WorkingCopyBackupService', () => {
|
||||
|
||||
suite('getBackups', () => {
|
||||
test('text file', async () => {
|
||||
await service.backup(toUntypedWorkingCopyId(fooFile), bufferToReadable(VSBuffer.fromString('test')));
|
||||
await service.backup(toTypedWorkingCopyId(fooFile, 'type1'), bufferToReadable(VSBuffer.fromString('test')));
|
||||
await service.backup(toTypedWorkingCopyId(fooFile, 'type2'), bufferToReadable(VSBuffer.fromString('test')));
|
||||
await Promise.all([
|
||||
service.backup(toUntypedWorkingCopyId(fooFile), bufferToReadable(VSBuffer.fromString('test'))),
|
||||
service.backup(toTypedWorkingCopyId(fooFile, 'type1'), bufferToReadable(VSBuffer.fromString('test'))),
|
||||
service.backup(toTypedWorkingCopyId(fooFile, 'type2'), bufferToReadable(VSBuffer.fromString('test')))
|
||||
]);
|
||||
|
||||
let backups = await service.getBackups();
|
||||
assert.strictEqual(backups.length, 3);
|
||||
@@ -742,9 +748,11 @@ flakySuite('WorkingCopyBackupService', () => {
|
||||
});
|
||||
|
||||
test('untitled file', async () => {
|
||||
await service.backup(toUntypedWorkingCopyId(untitledFile), bufferToReadable(VSBuffer.fromString('test')));
|
||||
await service.backup(toTypedWorkingCopyId(untitledFile, 'type1'), bufferToReadable(VSBuffer.fromString('test')));
|
||||
await service.backup(toTypedWorkingCopyId(untitledFile, 'type2'), bufferToReadable(VSBuffer.fromString('test')));
|
||||
await Promise.all([
|
||||
service.backup(toUntypedWorkingCopyId(untitledFile), bufferToReadable(VSBuffer.fromString('test'))),
|
||||
service.backup(toTypedWorkingCopyId(untitledFile, 'type1'), bufferToReadable(VSBuffer.fromString('test'))),
|
||||
service.backup(toTypedWorkingCopyId(untitledFile, 'type2'), bufferToReadable(VSBuffer.fromString('test')))
|
||||
]);
|
||||
|
||||
const backups = await service.getBackups();
|
||||
assert.strictEqual(backups.length, 3);
|
||||
|
||||
Reference in New Issue
Block a user