Merge branch 'main' into npm-extension-bun-package-manager

This commit is contained in:
Marvin A. Ruder 2023-11-13 18:20:49 +01:00 committed by GitHub
commit dde91250f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 398 additions and 124 deletions

View file

@ -44,6 +44,12 @@ export interface Branch extends Ref {
readonly behind?: number;
}
export interface CommitShortStat {
readonly files: number;
readonly insertions: number;
readonly deletions: number;
}
export interface Commit {
readonly hash: string;
readonly message: string;
@ -52,6 +58,7 @@ export interface Commit {
readonly authorName?: string;
readonly authorEmail?: string;
readonly commitDate?: Date;
readonly shortStat?: CommitShortStat;
}
export interface Submodule {
@ -134,6 +141,7 @@ export interface LogOptions {
readonly range?: string;
readonly reverse?: boolean;
readonly sortByAuthorDate?: boolean;
readonly shortStats?: boolean;
}
export interface CommitOptions {

View file

@ -706,6 +706,12 @@ export class Git {
}
}
export interface CommitShortStat {
readonly files: number;
readonly insertions: number;
readonly deletions: number;
}
export interface Commit {
hash: string;
message: string;
@ -715,6 +721,7 @@ export interface Commit {
authorEmail?: string;
commitDate?: Date;
refNames: string[];
shortStat?: CommitShortStat;
}
interface GitConfigSection {
@ -866,7 +873,7 @@ export function parseGitRemotes(raw: string): MutableRemote[] {
return remotes;
}
const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm;
const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)(?:\n((?:.*)files? changed(?:.*))$)?/gm;
export function parseGitCommits(data: string): Commit[] {
const commits: Commit[] = [];
@ -879,6 +886,7 @@ export function parseGitCommits(data: string): Commit[] {
let parents;
let refNames;
let message;
let shortStat;
let match;
do {
@ -887,7 +895,7 @@ export function parseGitCommits(data: string): Commit[] {
break;
}
[, ref, authorName, authorEmail, authorDate, commitDate, parents, refNames, message] = match;
[, ref, authorName, authorEmail, authorDate, commitDate, parents, refNames, message, shortStat] = match;
if (message[message.length - 1] === '\n') {
message = message.substr(0, message.length - 1);
@ -902,13 +910,27 @@ export function parseGitCommits(data: string): Commit[] {
authorName: ` ${authorName}`.substr(1),
authorEmail: ` ${authorEmail}`.substr(1),
commitDate: new Date(Number(commitDate) * 1000),
refNames: refNames.split(',').map(s => s.trim())
refNames: refNames.split(',').map(s => s.trim()),
shortStat: shortStat ? parseGitDiffShortStat(shortStat) : undefined
});
} while (true);
return commits;
}
const diffShortStatRegex = /(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?/;
function parseGitDiffShortStat(data: string): CommitShortStat {
const matches = data.trim().match(diffShortStatRegex);
if (!matches) {
return { files: 0, insertions: 0, deletions: 0 };
}
const [, files, insertions = undefined, deletions = undefined] = matches;
return { files: parseInt(files), insertions: parseInt(insertions ?? '0'), deletions: parseInt(deletions ?? '0') };
}
interface LsTreeElement {
mode: string;
type: string;
@ -1018,6 +1040,10 @@ export class Repository {
async log(options?: LogOptions): Promise<Commit[]> {
const args = ['log', `--format=${COMMIT_FORMAT}`, '-z'];
if (options?.shortStats) {
args.push('--shortstat', '--diff-merges=first-parent');
}
if (options?.reverse) {
args.push('--reverse', '--ancestry-path');
}
@ -1324,15 +1350,7 @@ export class Repository {
return { files: 0, insertions: 0, deletions: 0 };
}
const regex = /(\d+) files? changed(?:, (\d+) insertions\(\+\))?(?:, (\d+) deletions\(-\))?/;
const matches = result.stdout.trim().match(regex);
if (!matches) {
return { files: 0, insertions: 0, deletions: 0 };
}
const [, files, insertions = undefined, deletions = undefined] = matches;
return { files: parseInt(files), insertions: parseInt(insertions ?? '0'), deletions: parseInt(deletions ?? '0') };
return parseGitDiffShortStat(result.stdout.trim());
}
private async diffFiles(cached: boolean, ref?: string): Promise<Change[]> {

View file

@ -80,7 +80,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
const historyItemGroupIdRef = await this.repository.revParse(historyItemGroupId) ?? '';
const [commits, summary] = await Promise.all([
this.repository.log({ range: `${optionsRef}..${historyItemGroupIdRef}`, sortByAuthorDate: true }),
this.repository.log({ range: `${optionsRef}..${historyItemGroupIdRef}`, shortStats: true, sortByAuthorDate: true }),
this.getSummaryHistoryItem(optionsRef, historyItemGroupIdRef)
]);
@ -97,7 +97,8 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
label: emojify(subject),
description: commit.authorName,
icon: new ThemeIcon('git-commit'),
timestamp: commit.authorDate?.getTime()
timestamp: commit.authorDate?.getTime(),
statistics: commit.shortStat
};
}));

View file

@ -270,14 +270,15 @@ suite('git', () => {
suite('parseGitCommit', () => {
test('single parent commit', function () {
const GIT_OUTPUT_SINGLE_PARENT = `52c293a05038d865604c2284aa8698bd087915a1
John Doe
john.doe@mail.com
1580811030
1580811031
8e5a374372b8393906c7e380dbb09349c5385554
main,branch
This is a commit message.\x00`;
const GIT_OUTPUT_SINGLE_PARENT =
'52c293a05038d865604c2284aa8698bd087915a1\n' +
'John Doe\n' +
'john.doe@mail.com\n' +
'1580811030\n' +
'1580811031\n' +
'8e5a374372b8393906c7e380dbb09349c5385554\n' +
'main,branch\n' +
'This is a commit message.\x00';
assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_SINGLE_PARENT), [{
hash: '52c293a05038d865604c2284aa8698bd087915a1',
@ -288,18 +289,20 @@ This is a commit message.\x00`;
authorEmail: 'john.doe@mail.com',
commitDate: new Date(1580811031000),
refNames: ['main', 'branch'],
shortStat: undefined
}]);
});
test('multiple parent commits', function () {
const GIT_OUTPUT_MULTIPLE_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1
John Doe
john.doe@mail.com
1580811030
1580811031
8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217
main
This is a commit message.\x00`;
const GIT_OUTPUT_MULTIPLE_PARENTS =
'52c293a05038d865604c2284aa8698bd087915a1\n' +
'John Doe\n' +
'john.doe@mail.com\n' +
'1580811030\n' +
'1580811031\n' +
'8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217\n' +
'main\n' +
'This is a commit message.\x00';
assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_MULTIPLE_PARENTS), [{
hash: '52c293a05038d865604c2284aa8698bd087915a1',
@ -310,18 +313,20 @@ This is a commit message.\x00`;
authorEmail: 'john.doe@mail.com',
commitDate: new Date(1580811031000),
refNames: ['main'],
shortStat: undefined
}]);
});
test('no parent commits', function () {
const GIT_OUTPUT_NO_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1
John Doe
john.doe@mail.com
1580811030
1580811031
main
This is a commit message.\x00`;
const GIT_OUTPUT_NO_PARENTS =
'52c293a05038d865604c2284aa8698bd087915a1\n' +
'John Doe\n' +
'john.doe@mail.com\n' +
'1580811030\n' +
'1580811031\n' +
'\n' +
'main\n' +
'This is a commit message.\x00';
assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_NO_PARENTS), [{
hash: '52c293a05038d865604c2284aa8698bd087915a1',
@ -332,6 +337,191 @@ This is a commit message.\x00`;
authorEmail: 'john.doe@mail.com',
commitDate: new Date(1580811031000),
refNames: ['main'],
shortStat: undefined
}]);
});
test('commit with shortstat', function () {
const GIT_OUTPUT_SINGLE_PARENT =
'52c293a05038d865604c2284aa8698bd087915a1\n' +
'John Doe\n' +
'john.doe@mail.com\n' +
'1580811030\n' +
'1580811031\n' +
'8e5a374372b8393906c7e380dbb09349c5385554\n' +
'main,branch\n' +
'This is a commit message.\x00\n' +
' 1 file changed, 2 insertions(+), 3 deletion(-)';
assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_SINGLE_PARENT), [{
hash: '52c293a05038d865604c2284aa8698bd087915a1',
message: 'This is a commit message.',
parents: ['8e5a374372b8393906c7e380dbb09349c5385554'],
authorDate: new Date(1580811030000),
authorName: 'John Doe',
authorEmail: 'john.doe@mail.com',
commitDate: new Date(1580811031000),
refNames: ['main', 'branch'],
shortStat: {
deletions: 3,
files: 1,
insertions: 2
}
}]);
});
test('commit with shortstat (no insertions)', function () {
const GIT_OUTPUT_SINGLE_PARENT =
'52c293a05038d865604c2284aa8698bd087915a1\n' +
'John Doe\n' +
'john.doe@mail.com\n' +
'1580811030\n' +
'1580811031\n' +
'8e5a374372b8393906c7e380dbb09349c5385554\n' +
'main,branch\n' +
'This is a commit message.\x00\n' +
' 1 file changed, 3 deletion(-)';
assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_SINGLE_PARENT), [{
hash: '52c293a05038d865604c2284aa8698bd087915a1',
message: 'This is a commit message.',
parents: ['8e5a374372b8393906c7e380dbb09349c5385554'],
authorDate: new Date(1580811030000),
authorName: 'John Doe',
authorEmail: 'john.doe@mail.com',
commitDate: new Date(1580811031000),
refNames: ['main', 'branch'],
shortStat: {
deletions: 3,
files: 1,
insertions: 0
}
}]);
});
test('commit with shortstat (no deletions)', function () {
const GIT_OUTPUT_SINGLE_PARENT =
'52c293a05038d865604c2284aa8698bd087915a1\n' +
'John Doe\n' +
'john.doe@mail.com\n' +
'1580811030\n' +
'1580811031\n' +
'8e5a374372b8393906c7e380dbb09349c5385554\n' +
'main,branch\n' +
'This is a commit message.\x00\n' +
' 1 file changed, 2 insertions(+)';
assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_SINGLE_PARENT), [{
hash: '52c293a05038d865604c2284aa8698bd087915a1',
message: 'This is a commit message.',
parents: ['8e5a374372b8393906c7e380dbb09349c5385554'],
authorDate: new Date(1580811030000),
authorName: 'John Doe',
authorEmail: 'john.doe@mail.com',
commitDate: new Date(1580811031000),
refNames: ['main', 'branch'],
shortStat: {
deletions: 0,
files: 1,
insertions: 2
}
}]);
});
test('commit list', function () {
const GIT_OUTPUT_SINGLE_PARENT =
'52c293a05038d865604c2284aa8698bd087915a1\n' +
'John Doe\n' +
'john.doe@mail.com\n' +
'1580811030\n' +
'1580811031\n' +
'8e5a374372b8393906c7e380dbb09349c5385554\n' +
'main,branch\n' +
'This is a commit message.\x00\n' +
'52c293a05038d865604c2284aa8698bd087915a2\n' +
'Jane Doe\n' +
'jane.doe@mail.com\n' +
'1580811032\n' +
'1580811033\n' +
'8e5a374372b8393906c7e380dbb09349c5385555\n' +
'main,branch\n' +
'This is another commit message.\x00';
assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_SINGLE_PARENT), [
{
hash: '52c293a05038d865604c2284aa8698bd087915a1',
message: 'This is a commit message.',
parents: ['8e5a374372b8393906c7e380dbb09349c5385554'],
authorDate: new Date(1580811030000),
authorName: 'John Doe',
authorEmail: 'john.doe@mail.com',
commitDate: new Date(1580811031000),
refNames: ['main', 'branch'],
shortStat: undefined,
},
{
hash: '52c293a05038d865604c2284aa8698bd087915a2',
message: 'This is another commit message.',
parents: ['8e5a374372b8393906c7e380dbb09349c5385555'],
authorDate: new Date(1580811032000),
authorName: 'Jane Doe',
authorEmail: 'jane.doe@mail.com',
commitDate: new Date(1580811033000),
refNames: ['main', 'branch'],
shortStat: undefined,
},
]);
});
test('commit list with shortstat', function () {
const GIT_OUTPUT_SINGLE_PARENT = '52c293a05038d865604c2284aa8698bd087915a1\n' +
'John Doe\n' +
'john.doe@mail.com\n' +
'1580811030\n' +
'1580811031\n' +
'8e5a374372b8393906c7e380dbb09349c5385554\n' +
'main,branch\n' +
'This is a commit message.\x00\n' +
' 5 file changed, 12 insertions(+), 13 deletion(-)\n' +
'52c293a05038d865604c2284aa8698bd087915a2\n' +
'Jane Doe\n' +
'jane.doe@mail.com\n' +
'1580811032\n' +
'1580811033\n' +
'8e5a374372b8393906c7e380dbb09349c5385555\n' +
'main,branch\n' +
'This is another commit message.\x00\n' +
' 6 file changed, 22 insertions(+), 23 deletion(-)';
assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_SINGLE_PARENT), [{
hash: '52c293a05038d865604c2284aa8698bd087915a1',
message: 'This is a commit message.',
parents: ['8e5a374372b8393906c7e380dbb09349c5385554'],
authorDate: new Date(1580811030000),
authorName: 'John Doe',
authorEmail: 'john.doe@mail.com',
commitDate: new Date(1580811031000),
refNames: ['main', 'branch'],
shortStat: {
deletions: 13,
files: 5,
insertions: 12
}
},
{
hash: '52c293a05038d865604c2284aa8698bd087915a2',
message: 'This is another commit message.',
parents: ['8e5a374372b8393906c7e380dbb09349c5385555'],
authorDate: new Date(1580811032000),
authorName: 'Jane Doe',
authorEmail: 'jane.doe@mail.com',
commitDate: new Date(1580811033000),
refNames: ['main', 'branch'],
shortStat: {
deletions: 23,
files: 6,
insertions: 22
}
}]);
});
});

View file

@ -71,7 +71,7 @@
"@parcel/watcher": "2.1.0",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/policy-watcher": "^1.1.4",
"@vscode/proxy-agent": "^0.17.5",
"@vscode/proxy-agent": "^0.18.0",
"@vscode/ripgrep": "^1.15.6",
"@vscode/spdlog": "^0.13.12",
"@vscode/sqlite3": "5.1.6-vscode",

View file

@ -7,7 +7,7 @@
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.1.0",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/proxy-agent": "^0.17.5",
"@vscode/proxy-agent": "^0.18.0",
"@vscode/ripgrep": "^1.15.6",
"@vscode/spdlog": "^0.13.12",
"@vscode/vscode-languagedetection": "1.0.21",

View file

@ -58,10 +58,10 @@
resolved "https://registry.yarnpkg.com/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz#d2f1e0664ee6036408f9743fee264ea0699b0e48"
integrity sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==
"@vscode/proxy-agent@^0.17.5":
version "0.17.5"
resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.17.5.tgz#a59f6087a39795425b2601c9ee95bcb0338154e6"
integrity sha512-plKfR1i9ce09aro1/yvK3Ckiu84Cj5ViuLqJ/7VRT6E9w5xP2YUPcgrCy+u7FGorKZmJb+wQ1L6f/cdJ7axulw==
"@vscode/proxy-agent@^0.18.0":
version "0.18.0"
resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.18.0.tgz#08c1adc4707844788738e87814a425c1f553d695"
integrity sha512-lOBA4Ns6PqJtX6LCUpKiwxkx3uhPoOdChtSSvO0hujON1sPBdSjyAwECoEWlUAk8FTJ3040ClPoLsDn9gUw2lw==
dependencies:
"@tootallnate/once" "^3.0.0"
agent-base "^7.0.1"

View file

@ -26,3 +26,13 @@ export const mainWindow = window as CodeWindow;
* or `DOM.getActiveWindow()` to obtain the correct window for the context you are in.
*/
export const $window = mainWindow;
export function isAuxiliaryWindow(obj: Window): obj is CodeWindow {
if (obj === mainWindow) {
return false;
}
const candidate = obj as CodeWindow | undefined;
return typeof candidate?.vscodeWindowId === 'number';
}

View file

@ -86,10 +86,6 @@ export class ModesHoverController extends Disposable implements IEditorContribut
}
private _hookEvents(): void {
const hideWidgetsCancelSchedulerEventHandler = () => {
this._cancelScheduler();
this._hideWidgets();
};
const hoverOpts = this._editor.getOption(EditorOption.hover);
this._isHoverEnabled = hoverOpts.enabled;
@ -106,8 +102,11 @@ export class ModesHoverController extends Disposable implements IEditorContribut
}
this._toUnhook.add(this._editor.onMouseLeave((e) => this._onEditorMouseLeave(e)));
this._toUnhook.add(this._editor.onDidChangeModel(hideWidgetsCancelSchedulerEventHandler));
this._toUnhook.add(this._editor.onDidChangeModelContent(hideWidgetsCancelSchedulerEventHandler));
this._toUnhook.add(this._editor.onDidChangeModel(() => {
this._cancelScheduler();
this._hideWidgets();
}));
this._toUnhook.add(this._editor.onDidChangeModelContent(() => this._cancelScheduler()));
this._toUnhook.add(this._editor.onDidScrollChange((e: IScrollEvent) => this._onEditorScrollChanged(e)));
}

View file

@ -180,6 +180,7 @@ export interface ICommonNativeHostService {
// Connectivity
resolveProxy(url: string): Promise<string | undefined>;
loadCertificates(): Promise<string[]>;
findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride?: number): Promise<number>;
// Registry (windows only)

View file

@ -43,6 +43,7 @@ import { WindowProfiler } from 'vs/platform/profiling/electron-main/windowProfil
import { IV8Profile } from 'vs/platform/profiling/common/profiling';
import { IAuxiliaryWindowsMainService, isAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows';
import { IAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow';
import { loadSystemCertificates } from '@vscode/proxy-agent';
export interface INativeHostMainService extends AddFirstParameterToFunctions<ICommonNativeHostService, Promise<unknown> /* only methods, not events */, number | undefined /* window ID */> { }
@ -756,6 +757,10 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
return session?.resolveProxy(url);
}
async loadCertificates(_windowId: number | undefined): Promise<string[]> {
return loadSystemCertificates({ log: this.logService });
}
findFreePort(windowId: number | undefined, startPort: number, giveUpAfter: number, timeout: number, stride = 1): Promise<number> {
return findFreePort(startPort, giveUpAfter, timeout, stride);
}

View file

@ -35,4 +35,8 @@ export class RequestService extends AbstractRequestService implements IRequestSe
async resolveProxy(url: string): Promise<string | undefined> {
return undefined; // not implemented in the web
}
async loadCertificates(): Promise<string[]> {
return []; // not implemented in the web
}
}

View file

@ -22,6 +22,7 @@ export interface IRequestService {
request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext>;
resolveProxy(url: string): Promise<string | undefined>;
loadCertificates(): Promise<string[]>;
}
class LoggableHeaders {
@ -79,6 +80,7 @@ export abstract class AbstractRequestService extends Disposable implements IRequ
abstract request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext>;
abstract resolveProxy(url: string): Promise<string | undefined>;
abstract loadCertificates(): Promise<string[]>;
}
export function isSuccess(context: IRequestContext): boolean {

View file

@ -34,6 +34,7 @@ export class RequestChannel implements IServerChannel {
return <RequestResponse>[{ statusCode: res.statusCode, headers: res.headers }, buffer];
});
case 'resolveProxy': return this.service.resolveProxy(args[0]);
case 'loadCertificates': return this.service.loadCertificates();
}
throw new Error('Invalid call');
}
@ -54,4 +55,7 @@ export class RequestChannelClient implements IRequestService {
return this.channel.call<string | undefined>('resolveProxy', [url]);
}
async loadCertificates(): Promise<string[]> {
return this.channel.call<string[]>('loadCertificates');
}
}

View file

@ -20,6 +20,7 @@ import { ILogService, ILoggerService } from 'vs/platform/log/common/log';
import { AbstractRequestService, IRequestService } from 'vs/platform/request/common/request';
import { Agent, getProxyAgent } from 'vs/platform/request/node/proxy';
import { createGunzip } from 'zlib';
import { loadSystemCertificates } from '@vscode/proxy-agent';
interface IHTTPConfiguration {
proxy?: string;
@ -109,6 +110,10 @@ export class RequestService extends AbstractRequestService implements IRequestSe
async resolveProxy(url: string): Promise<string | undefined> {
return undefined; // currently not implemented in node
}
async loadCertificates(): Promise<string[]> {
return loadSystemCertificates({ log: this.logService });
}
}
async function getNodeRequest(options: IRequestOptions): Promise<IRawRequestFunction> {

View file

@ -188,6 +188,7 @@ export class UserDataSyncTestServer implements IRequestService {
constructor(private readonly rateLimit = Number.MAX_SAFE_INTEGER, private readonly retryAfter?: number) { }
async resolveProxy(url: string): Promise<string | undefined> { return url; }
async loadCertificates(): Promise<string[]> { return []; }
async request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> {
if (this._requests.length === this.rateLimit) {

View file

@ -413,7 +413,8 @@ suite('UserDataSyncRequestsSession', () => {
const requestService: IRequestService = {
_serviceBrand: undefined,
async request() { return { res: { headers: {} }, stream: newWriteableBufferStream() }; },
async resolveProxy() { return undefined; }
async resolveProxy() { return undefined; },
async loadCertificates() { return []; }
};
test('too many requests are thrown when limit exceeded', async () => {

View file

@ -230,6 +230,10 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
return this._requestService.resolveProxy(url);
}
$loadCertificates(): Promise<string[]> {
return this._requestService.loadCertificates();
}
// --- trust ---
$requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean | undefined> {

View file

@ -1311,6 +1311,7 @@ export interface MainThreadWorkspaceShape extends IDisposable {
$saveAll(includeUntitled?: boolean): Promise<boolean>;
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents; name?: string }[]): Promise<void>;
$resolveProxy(url: string): Promise<string | undefined>;
$loadCertificates(): Promise<string[]>;
$requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean | undefined>;
$registerEditSessionIdentityProvider(handle: number, scheme: string): void;
$unregisterEditSessionIdentityProvider(handle: number): void;

View file

@ -39,6 +39,7 @@ export interface IExtHostWorkspaceProvider {
resolveWorkspaceFolder(uri: vscode.Uri): Promise<vscode.WorkspaceFolder | undefined>;
getWorkspaceFolders2(): Promise<vscode.WorkspaceFolder[] | undefined>;
resolveProxy(url: string): Promise<string | undefined>;
loadCertificates(): Promise<string[]>;
}
function isFolderEqual(folderA: URI, folderB: URI, extHostFileSystemInfo: IExtHostFileSystemInfo): boolean {
@ -578,6 +579,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
return this._proxy.$resolveProxy(url);
}
loadCertificates(): Promise<string[]> {
return this._proxy.$loadCertificates();
}
// --- trust ---
get trusted(): boolean {

View file

@ -16,7 +16,7 @@ import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionS
import { URI } from 'vs/base/common/uri';
import { ILogService, LogLevel as LogServiceLevel } from 'vs/platform/log/common/log';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { LogLevel, createHttpPatch, createProxyResolver, createTlsPatch, ProxySupportSetting, ProxyAgentParams, createNetPatch } from '@vscode/proxy-agent';
import { LogLevel, createHttpPatch, createProxyResolver, createTlsPatch, ProxySupportSetting, ProxyAgentParams, createNetPatch, loadSystemCertificates } from '@vscode/proxy-agent';
const systemCertificatesV2Default = false;
@ -35,24 +35,9 @@ export function connectProxyResolver(
lookupProxyAuthorization: lookupProxyAuthorization.bind(undefined, extHostLogService, mainThreadTelemetry, configProvider, {}, initData.remote.isRemote),
getProxyURL: () => configProvider.getConfiguration('http').get('proxy'),
getProxySupport: () => configProvider.getConfiguration('http').get<ProxySupportSetting>('proxySupport') || 'off',
getSystemCertificatesV1: () => certSettingV1(configProvider),
getSystemCertificatesV2: () => certSettingV2(configProvider),
log: (level, message, ...args) => {
switch (level) {
case LogLevel.Trace: extHostLogService.trace(message, ...args); break;
case LogLevel.Debug: extHostLogService.debug(message, ...args); break;
case LogLevel.Info: extHostLogService.info(message, ...args); break;
case LogLevel.Warning: extHostLogService.warn(message, ...args); break;
case LogLevel.Error: extHostLogService.error(message, ...args); break;
case LogLevel.Critical: extHostLogService.error(message, ...args); break;
case LogLevel.Off: break;
default: never(level, message, args); break;
}
function never(level: never, message: string, ...args: any[]) {
extHostLogService.error('Unknown log level', level);
extHostLogService.error(message, ...args);
}
},
addCertificatesV1: () => certSettingV1(configProvider),
addCertificatesV2: () => certSettingV2(configProvider),
log: extHostLogService,
getLogLevel: () => {
const level = extHostLogService.getLevel();
switch (level) {
@ -71,7 +56,19 @@ export function connectProxyResolver(
},
proxyResolveTelemetry: () => { },
useHostProxy: doUseHostProxy,
addCertificates: [],
loadAdditionalCertificates: async () => {
const promises: Promise<string[]>[] = [];
if (initData.remote.isRemote) {
promises.push(loadSystemCertificates({ log: extHostLogService }));
}
if (doUseHostProxy) {
extHostLogService.trace('ProxyResolver#loadAdditionalCertificates: Loading certificates from main process');
const certs = extHostWorkspace.loadCertificates(); // Loading from main process to share cache.
certs.then(certs => extHostLogService.trace('ProxyResolver#loadAdditionalCertificates: Loaded certificates from main process', certs.length));
promises.push(certs);
}
return (await Promise.all(promises)).flat();
},
env: process.env,
};
const resolveProxy = createProxyResolver(params);

View file

@ -27,7 +27,7 @@ import { BrowserLifecycleService } from 'vs/workbench/services/lifecycle/browser
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { registerWindowDriver } from 'vs/workbench/services/driver/browser/driver';
import { CodeWindow, mainWindow } from 'vs/base/browser/window';
import { CodeWindow, isAuxiliaryWindow, mainWindow } from 'vs/base/browser/window';
import { createSingleCallFunction } from 'vs/base/common/functional';
export abstract class BaseWindow extends Disposable {
@ -71,6 +71,10 @@ export abstract class BaseWindow extends Disposable {
});
for (const { window, disposables } of dom.getWindows()) {
if (isAuxiliaryWindow(window) && window.document.visibilityState === 'hidden') {
continue; // skip over hidden windows (but never over main window)
}
const handle = (window as any).vscodeOriginalSetTimeout.apply(this, [handlerFn, timeout, ...args]);
const timeoutDisposable = toDisposable(() => {

View file

@ -32,7 +32,7 @@ import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/cont
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { Lazy } from 'vs/base/common/lazy';
import { AsyncProgress } from 'vs/platform/progress/common/progress';
import { Progress } from 'vs/platform/progress/common/progress';
import { generateUuid } from 'vs/base/common/uuid';
import { TextEdit } from 'vs/editor/common/languages';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
@ -602,7 +602,7 @@ export class InlineChatController implements IEditorContribution {
const progressiveEditsClock = StopWatch.create();
const progressiveEditsQueue = new Queue();
const progress = new AsyncProgress<IInlineChatProgressItem>(async data => {
const progress = new Progress<IInlineChatProgressItem>(data => {
this._log('received chunk', data, request);
if (requestCts.token.isCancellationRequested) {
@ -671,7 +671,6 @@ export class InlineChatController implements IEditorContribution {
// we must wait for all edits that came in via progress to complete
await Event.toPromise(progressiveEditsQueue.onDrained);
}
await progress.drain();
if (!reply) {
response = new EmptyResponse();

View file

@ -221,6 +221,11 @@
font-size: 10px;
}
.scm-view .monaco-list-row.focused .separator-container .label-name,
.scm-view .monaco-list-row.selected .separator-container .label-name {
color: var(--vscode-foreground);
}
.scm-view .monaco-list-row .separator-container .separator {
display: flex;
flex-grow: 1;

View file

@ -812,12 +812,14 @@ class HistoryItemRenderer implements ICompressibleTreeRenderer<SCMHistoryItemTre
}
if (historyItem.statistics?.insertions) {
const insertionsDescription = localize('insertions', "insertions{0}", '(+)');
const insertionsDescription = historyItem.statistics.insertions === 1 ?
localize('insertion', "insertion{0}", '(+)') : localize('insertions', "insertions{0}", '(+)');
statsLabelTitle.push(`${historyItem.statistics.insertions} ${insertionsDescription}`);
}
if (historyItem.statistics?.deletions) {
const deletionsDescription = localize('deletions', "deletions{0}", '(-)');
const deletionsDescription = historyItem.statistics.deletions === 1 ?
localize('deletion', "deletion{0}", '(-)') : localize('deletions', "deletions{0}", '(-)');
statsLabelTitle.push(`${historyItem.statistics.deletions} ${deletionsDescription}`);
}
@ -2421,11 +2423,12 @@ export class SCMViewPane extends ViewPane {
Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.providerCountBadge'), this.disposables)(updateProviderCountVisibility, this, this.disposables);
updateProviderCountVisibility();
this.createTree(this.treeContainer);
const viewState = this.loadTreeViewState();
this.createTree(this.treeContainer, viewState);
this.onDidChangeBodyVisibility(async visible => {
if (visible) {
await this.tree.setInput(this.scmViewService, this.loadTreeViewState());
await this.tree.setInput(this.scmViewService, viewState);
const onDidChangeConfiguration = (e?: IConfigurationChangeEvent) => {
if (!e || e.affectsConfiguration('scm.showActionButton') || e.affectsConfiguration('scm.alwaysShowRepositories') || e.affectsConfiguration('scm.experimental.showSyncInformation')) {
@ -2467,7 +2470,7 @@ export class SCMViewPane extends ViewPane {
this.updateIndentStyles(this.themeService.getFileIconTheme());
}
private createTree(container: HTMLElement): void {
private createTree(container: HTMLElement, viewState?: IAsyncDataTreeViewState): void {
const overflowWidgetsDomNode = $('.scm-overflow-widgets-container.monaco-editor');
this.inputRenderer = this.instantiationService.createInstance(InputRenderer, this.layoutCache, overflowWidgetsDomNode, (input, height) => { this.tree.updateElementHeight(input, height); });
@ -2510,7 +2513,15 @@ export class SCMViewPane extends ViewPane {
overrideStyles: {
listBackground: this.viewDescriptorService.getViewLocationById(this.id) === ViewContainerLocation.Panel ? PANEL_BACKGROUND : SIDE_BAR_BACKGROUND
},
collapseByDefault: (e: unknown) => isSCMHistoryItemGroupTreeElement(e) || isSCMHistoryItemTreeElement(e) || isSCMHistoryItemChangeTreeElement(e),
collapseByDefault: (e: unknown) => {
// Repository, Resource Group, Resource Folder (Tree), History Item Change Folder (Tree)
if (isSCMRepository(e) || isSCMResourceGroup(e) || isSCMResourceNode(e) || isSCMHistoryItemChangeNode(e)) {
return false;
}
// History Item Group, History Item, or History Item Change
return (viewState?.expanded ?? []).indexOf(getSCMResourceId(e as TreeElement)) === -1;
},
accessibilityProvider: this.instantiationService.createInstance(SCMAccessibilityProvider)
}) as WorkbenchCompressibleAsyncDataTree<ISCMViewService, TreeElement, FuzzyScore>;

View file

@ -339,8 +339,8 @@ export class NativeWindow extends BaseWindow {
}
}));
// Listen to visible editor changes (debounced)
this._register(Event.debounce(this.editorService.onDidVisibleEditorsChange, () => undefined, 50, undefined, undefined, undefined, this._store)(() => this.onDidChangeVisibleEditors()));
// Listen to visible editor changes (debounced in case a new editor opens immediately after)
this._register(Event.debounce(this.editorService.onDidVisibleEditorsChange, () => undefined, 0, undefined, undefined, undefined, this._store)(() => this.maybeCloseWindow()));
// Listen to editor closing (if we run with --wait)
const filesToWait = this.environmentService.filesToWait;
@ -598,7 +598,11 @@ export class NativeWindow extends BaseWindow {
this.nativeHostService.setMinimumSize(minWidth, undefined);
}
private onDidChangeVisibleEditors(): void {
private maybeCloseWindow(): void {
const closeWhenEmpty = this.configurationService.getValue('window.closeWhenEmpty') || this.environmentService.args.wait;
if (!closeWhenEmpty) {
return; // return early if configured to not close when empty
}
// Close empty editor groups based on setting and environment
for (const editorPart of this.editorGroupService.parts) {
@ -606,12 +610,12 @@ export class NativeWindow extends BaseWindow {
continue; // not empty
}
let closeWhenEmpty = this.configurationService.getValue('window.closeWhenEmpty') || this.environmentService.args.wait;
if (editorPart === this.editorGroupService.mainPart && (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY || this.environmentService.isExtensionDevelopment)) {
closeWhenEmpty = false; // disabled for main part when window is not empty or extension development
}
if (!closeWhenEmpty) {
continue; // not enabled to close when empty
if (editorPart === this.editorGroupService.mainPart && (
this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY || // only for empty windows
this.environmentService.isExtensionDevelopment || // not when developing an extension
this.editorService.visibleEditors.length > 0 // not when there are still editors open in other windows
)) {
continue;
}
if (editorPart === this.editorGroupService.mainPart) {

View file

@ -110,16 +110,6 @@ class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow {
}
}
export function isAuxiliaryWindow(obj: Window): obj is CodeWindow {
if (obj === mainWindow) {
return false;
}
const candidate = obj as CodeWindow | undefined;
return typeof candidate?.vscodeWindowId === 'number';
}
export class BrowserAuxiliaryWindowService extends Disposable implements IAuxiliaryWindowService {
declare readonly _serviceBrand: undefined;
@ -208,7 +198,11 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
return auxiliaryWindow?.window;
}
protected createContainer(auxiliaryWindow: Window, disposables: DisposableStore): HTMLElement {
protected async resolveWindowId(auxiliaryWindow: Window): Promise<number> {
return BrowserAuxiliaryWindowService.WINDOW_IDS++;
}
protected createContainer(auxiliaryWindow: CodeWindow, disposables: DisposableStore): HTMLElement {
this.patchMethods(auxiliaryWindow);
this.applyMeta(auxiliaryWindow);
@ -217,7 +211,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
return this.applyHTML(auxiliaryWindow, disposables);
}
protected patchMethods(auxiliaryWindow: Window): void {
protected patchMethods(auxiliaryWindow: CodeWindow): void {
// Disallow `createElement` because it would create
// HTML Elements in the "wrong" context and break
@ -227,11 +221,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
};
}
protected async resolveWindowId(auxiliaryWindow: Window): Promise<number> {
return BrowserAuxiliaryWindowService.WINDOW_IDS++;
}
private applyMeta(auxiliaryWindow: Window): void {
private applyMeta(auxiliaryWindow: CodeWindow): void {
const metaCharset = createMetaElement(auxiliaryWindow.document.head);
metaCharset.setAttribute('charset', 'utf-8');
@ -247,7 +237,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
}
}
protected applyCSS(auxiliaryWindow: Window, disposables: DisposableStore): void {
protected applyCSS(auxiliaryWindow: CodeWindow, disposables: DisposableStore): void {
mark('code/auxiliaryWindow/willApplyCSS');
const mapOriginalToClone = new Map<Node /* original */, Node /* clone */>();
@ -313,7 +303,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
mark('code/auxiliaryWindow/didApplyCSS');
}
private applyHTML(auxiliaryWindow: Window, disposables: DisposableStore): HTMLElement {
private applyHTML(auxiliaryWindow: CodeWindow, disposables: DisposableStore): HTMLElement {
mark('code/auxiliaryWindow/willApplyHTML');
// Create workbench container and apply classes

View file

@ -31,6 +31,14 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService
super(layoutService, dialogService);
}
protected override async resolveWindowId(auxiliaryWindow: NativeAuxiliaryWindow): Promise<number> {
mark('code/auxiliaryWindow/willResolveWindowId');
const windowId = await auxiliaryWindow.vscode.ipcRenderer.invoke('vscode:registerAuxiliaryWindow', this.nativeHostService.windowId);
mark('code/auxiliaryWindow/didResolveWindowId');
return windowId;
}
protected override createContainer(auxiliaryWindow: NativeAuxiliaryWindow, disposables: DisposableStore): HTMLElement {
// Zoom level
@ -41,14 +49,6 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService
return super.createContainer(auxiliaryWindow, disposables);
}
protected override async resolveWindowId(auxiliaryWindow: NativeAuxiliaryWindow): Promise<number> {
mark('code/auxiliaryWindow/willResolveWindowId');
const windowId = await auxiliaryWindow.vscode.ipcRenderer.invoke('vscode:registerAuxiliaryWindow', this.nativeHostService.windowId);
mark('code/auxiliaryWindow/didResolveWindowId');
return windowId;
}
protected override patchMethods(auxiliaryWindow: NativeAuxiliaryWindow): void {
super.patchMethods(auxiliaryWindow);

View file

@ -37,8 +37,7 @@ import { Schemas } from 'vs/base/common/network';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { coalesce } from 'vs/base/common/arrays';
import { isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
import { mainWindow } from 'vs/base/browser/window';
import { mainWindow, isAuxiliaryWindow } from 'vs/base/browser/window';
/**
* A workspace to open in the workbench can either be:

View file

@ -14,9 +14,10 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { NativeHostService } from 'vs/platform/native/common/nativeHostService';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { IMainProcessService } from 'vs/platform/ipc/common/mainProcessService';
import { IAuxiliaryWindowService, isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
import { IAuxiliaryWindowService } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
import { disposableWindowInterval, getActiveDocument, getWindowsCount, onDidRegisterWindow } from 'vs/base/browser/dom';
import { memoize } from 'vs/base/common/decorators';
import { isAuxiliaryWindow } from 'vs/base/browser/window';
class WorkbenchNativeHostService extends NativeHostService {

View file

@ -23,6 +23,10 @@ export class NativeRequestService extends RequestService {
override async resolveProxy(url: string): Promise<string | undefined> {
return this.nativeHostService.resolveProxy(url);
}
override async loadCertificates(): Promise<string[]> {
return this.nativeHostService.loadCertificates();
}
}
registerSingleton(IRequestService, NativeRequestService, InstantiationType.Delayed);

View file

@ -141,6 +141,7 @@ export class TestNativeHostService implements INativeHostService {
async openDevTools(options?: Electron.OpenDevToolsOptions | undefined): Promise<void> { }
async toggleDevTools(): Promise<void> { }
async resolveProxy(url: string): Promise<string | undefined> { return undefined; }
async loadCertificates(): Promise<string[]> { return []; }
async findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride?: number): Promise<number> { return -1; }
async readClipboardText(type?: 'selection' | 'clipboard' | undefined): Promise<string> { return ''; }
async writeClipboardText(text: string, type?: 'selection' | 'clipboard' | undefined): Promise<void> { }

View file

@ -1392,10 +1392,10 @@
bindings "^1.5.0"
node-addon-api "^6.0.0"
"@vscode/proxy-agent@^0.17.5":
version "0.17.5"
resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.17.5.tgz#a59f6087a39795425b2601c9ee95bcb0338154e6"
integrity sha512-plKfR1i9ce09aro1/yvK3Ckiu84Cj5ViuLqJ/7VRT6E9w5xP2YUPcgrCy+u7FGorKZmJb+wQ1L6f/cdJ7axulw==
"@vscode/proxy-agent@^0.18.0":
version "0.18.0"
resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.18.0.tgz#08c1adc4707844788738e87814a425c1f553d695"
integrity sha512-lOBA4Ns6PqJtX6LCUpKiwxkx3uhPoOdChtSSvO0hujON1sPBdSjyAwECoEWlUAk8FTJ3040ClPoLsDn9gUw2lw==
dependencies:
"@tootallnate/once" "^3.0.0"
agent-base "^7.0.1"