Skip to content

Commit 16074f9

Browse files
committed
Merge remote-tracking branch 'origin/main' into alexd/popular-krill
2 parents 9cb7a27 + eb66332 commit 16074f9

File tree

68 files changed

+963
-283
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+963
-283
lines changed

extensions/git/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3372,22 +3372,22 @@
33723372
{
33733373
"view": "scm",
33743374
"contents": "%view.workbench.scm.missing%",
3375-
"when": "config.git.enabled && git.missing"
3375+
"when": "config.git.enabled && git.missing && remoteName != ''"
33763376
},
33773377
{
33783378
"view": "scm",
33793379
"contents": "%view.workbench.scm.missing.mac%",
3380-
"when": "config.git.enabled && git.missing && isMac"
3380+
"when": "config.git.enabled && git.missing && remoteName == '' && isMac"
33813381
},
33823382
{
33833383
"view": "scm",
33843384
"contents": "%view.workbench.scm.missing.windows%",
3385-
"when": "config.git.enabled && git.missing && isWindows"
3385+
"when": "config.git.enabled && git.missing && remoteName == '' && isWindows"
33863386
},
33873387
{
33883388
"view": "scm",
33893389
"contents": "%view.workbench.scm.missing.linux%",
3390-
"when": "config.git.enabled && git.missing && isLinux"
3390+
"when": "config.git.enabled && git.missing && remoteName == '' && isLinux"
33913391
},
33923392
{
33933393
"view": "scm",

extensions/git/src/api/api1.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ export class ApiRepositoryUIState implements RepositoryUIState {
7272
}
7373

7474
export class ApiRepository implements Repository {
75-
7675
#repository: BaseRepository;
7776

7877
readonly rootUri: Uri;
@@ -311,9 +310,19 @@ export class ApiRepository implements Repository {
311310
export class ApiGit implements Git {
312311
#model: Model;
313312

313+
private _env: { [key: string]: string } | undefined;
314+
314315
constructor(model: Model) { this.#model = model; }
315316

316317
get path(): string { return this.#model.git.path; }
318+
319+
get env(): { [key: string]: string } {
320+
if (this._env === undefined) {
321+
this._env = Object.freeze(this.#model.git.env);
322+
}
323+
324+
return this._env;
325+
}
317326
}
318327

319328
export class ApiImpl implements API {

extensions/git/src/commands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{
309309
const isBothAddedOrModified = (s: Resource) => s.type === Status.BOTH_MODIFIED || s.type === Status.BOTH_ADDED;
310310
const isAnyDeleted = (s: Resource) => s.type === Status.DELETED_BY_THEM || s.type === Status.DELETED_BY_US;
311311
const possibleUnresolved = merge.filter(isBothAddedOrModified);
312-
const promises = possibleUnresolved.map(s => grep(s.resourceUri.fsPath, /^<{7}|^={7}|^>{7}/));
312+
const promises = possibleUnresolved.map(s => grep(s.resourceUri.fsPath, /^<{7}\s|^={7}$|^>{7}\s/));
313313
const unresolvedBothModified = await Promise.all<boolean>(promises);
314314
const resolved = possibleUnresolved.filter((_s, i) => !unresolvedBothModified[i]);
315315
const deletionConflicts = merge.filter(s => isAnyDeleted(s));

extensions/git/src/fileSystemProvider.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ export class GitFileSystemProvider implements FileSystemProvider {
192192
try {
193193
return await repository.buffer(sanitizeRef(ref, path, repository), path);
194194
} catch (err) {
195-
return new Uint8Array(0);
195+
// File does not exist in git (ex: git ignored)
196+
throw FileSystemError.FileNotFound();
196197
}
197198
}
198199

extensions/git/src/git.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ export interface IGitOptions {
315315
gitPath: string;
316316
userAgent: string;
317317
version: string;
318-
env?: any;
318+
env?: { [key: string]: string };
319319
}
320320

321321
function getGitErrorCode(stderr: string): string | undefined {
@@ -369,7 +369,8 @@ export class Git {
369369
readonly path: string;
370370
readonly userAgent: string;
371371
readonly version: string;
372-
private env: any;
372+
readonly env: { [key: string]: string };
373+
373374
private commandsToLog: string[] = [];
374375

375376
private _onOutput = new EventEmitter();
@@ -1657,9 +1658,9 @@ export class Repository {
16571658
await this.exec(args);
16581659
}
16591660

1660-
async stage(path: string, data: string): Promise<void> {
1661+
async stage(path: string, data: string, encoding: string): Promise<void> {
16611662
const child = this.stream(['hash-object', '--stdin', '-w', '--path', sanitizePath(path)], { stdio: [null, null, null] });
1662-
child.stdin!.end(data, 'utf8');
1663+
child.stdin!.end(iconv.encode(data, encoding));
16631664

16641665
const { exitCode, stdout } = await exec(child);
16651666
const hash = stdout.toString('utf8');

extensions/git/src/repository.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import TelemetryReporter from '@vscode/extension-telemetry';
77
import * as fs from 'fs';
88
import * as path from 'path';
99
import picomatch from 'picomatch';
10+
import * as iconv from '@vscode/iconv-lite-umd';
1011
import { CancellationError, CancellationToken, CancellationTokenSource, Command, commands, Disposable, Event, EventEmitter, FileDecoration, l10n, LogLevel, LogOutputChannel, Memento, ProgressLocation, ProgressOptions, QuickDiffProvider, RelativePattern, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, TabInputNotebookDiff, TabInputTextDiff, TabInputTextMultiDiff, ThemeColor, Uri, window, workspace, WorkspaceEdit } from 'vscode';
1112
import { ActionButton } from './actionButton';
1213
import { ApiRepository } from './api/api1';
@@ -24,6 +25,7 @@ import { StatusBarCommands } from './statusbar';
2425
import { toGitUri } from './uri';
2526
import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util';
2627
import { IFileWatcher, watch } from './watch';
28+
import { detectEncoding } from './encoding';
2729

2830
const timeout = (millis: number) => new Promise(c => setTimeout(c, millis));
2931

@@ -1215,7 +1217,17 @@ export class Repository implements Disposable {
12151217
async stage(resource: Uri, contents: string): Promise<void> {
12161218
const path = relativePath(this.repository.root, resource.fsPath).replace(/\\/g, '/');
12171219
await this.run(Operation.Stage, async () => {
1218-
await this.repository.stage(path, contents);
1220+
const configFiles = workspace.getConfiguration('files', Uri.file(resource.fsPath));
1221+
let encoding = configFiles.get<string>('encoding') ?? 'utf8';
1222+
const autoGuessEncoding = configFiles.get<boolean>('autoGuessEncoding') === true;
1223+
const candidateGuessEncodings = configFiles.get<string[]>('candidateGuessEncodings') ?? [];
1224+
1225+
if (autoGuessEncoding) {
1226+
encoding = detectEncoding(Buffer.from(contents), candidateGuessEncodings) ?? encoding;
1227+
}
1228+
1229+
encoding = iconv.encodingExists(encoding) ? encoding : 'utf8';
1230+
await this.repository.stage(path, contents, encoding);
12191231

12201232
this._onDidChangeOriginalResource.fire(resource);
12211233
this.closeDiffEditors([], [...resource.fsPath]);

extensions/microsoft-authentication/src/node/authProvider.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
5-
import { AccountInfo, AuthenticationResult, ServerError } from '@azure/msal-node';
5+
import { AccountInfo, AuthenticationResult, ClientAuthError, ClientAuthErrorCodes, ServerError } from '@azure/msal-node';
66
import { AuthenticationGetSessionOptions, AuthenticationProvider, AuthenticationProviderAuthenticationSessionsChangeEvent, AuthenticationProviderSessionOptions, AuthenticationSession, AuthenticationSessionAccountInformation, CancellationError, env, EventEmitter, ExtensionContext, l10n, LogOutputChannel, Uri, window } from 'vscode';
77
import { Environment } from '@azure/ms-rest-azure-env';
88
import { CachedPublicClientApplicationManager } from './publicClientCache';
@@ -229,6 +229,12 @@ export class MsalAuthProvider implements AuthenticationProvider {
229229
throw e;
230230
}
231231

232+
// The user closed the modal window
233+
if ((e as ClientAuthError).errorCode === ClientAuthErrorCodes.userCanceled) {
234+
this._telemetryReporter.sendLoginFailedEvent();
235+
throw e;
236+
}
237+
232238
// The user wants to try the loopback client or we got an error likely due to spinning up the server
233239
const loopbackClient = new UriHandlerLoopbackClient(this._uriHandler, redirectUri, this._logger);
234240
try {

extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,19 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
102102
);
103103
if (fiveMinutesBefore < new Date()) {
104104
this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is expired or about to expire. Forcing refresh...`);
105-
result = await this._sequencer.queue(() => this._pca.acquireTokenSilent({ ...request, forceRefresh: true }));
105+
const newRequest = this._isBrokerAvailable
106+
// HACK: Broker doesn't support forceRefresh so we need to pass in claims which will force a refresh
107+
? { ...request, claims: '{ "id_token": {}}' }
108+
: { ...request, forceRefresh: true };
109+
result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(newRequest));
106110
this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] got refreshed result`);
107111
}
112+
const newIdTokenExpirationInSecs = (result.idTokenClaims as { exp?: number }).exp;
113+
if (newIdTokenExpirationInSecs) {
114+
if (new Date(newIdTokenExpirationInSecs * 1000) < new Date()) {
115+
this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is still expired.`);
116+
}
117+
}
108118
}
109119

110120
// this._setupRefresh(result);

extensions/npm/src/preferred-pm.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ async function isBunPreferred(pkgPath: string): Promise<PreferredProperties> {
2828
return { isPreferred: true, hasLockfile: true };
2929
}
3030

31+
if (await pathExists(path.join(pkgPath, 'bun.lock'))) {
32+
return { isPreferred: true, hasLockfile: true };
33+
}
34+
3135
return { isPreferred: false, hasLockfile: false };
3236
}
3337

extensions/terminal-suggest/src/terminalSuggestMain.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ import codeCompletionSpec from './completions/code';
1212
import cdSpec from './completions/cd';
1313

1414
let cachedAvailableCommands: Set<string> | undefined;
15-
let cachedBuiltinCommands: Map<string, string[]> | undefined;
15+
const cachedBuiltinCommands: Map<string, string[] | undefined> = new Map();
1616

1717
export const availableSpecs = [codeCompletionSpec, codeInsidersCompletionSpec, cdSpec];
1818

1919
function getBuiltinCommands(shell: string): string[] | undefined {
2020
try {
2121
const shellType = path.basename(shell, path.extname(shell));
22-
const cachedCommands = cachedBuiltinCommands?.get(shellType);
22+
const cachedCommands = cachedBuiltinCommands.get(shellType);
2323
if (cachedCommands) {
2424
return cachedCommands;
2525
}
@@ -38,32 +38,27 @@ function getBuiltinCommands(shell: string): string[] | undefined {
3838
break;
3939
}
4040
case 'fish': {
41-
// TODO: ghost text in the command line prevents
42-
// completions from working ATM for fish
41+
// TODO: Ghost text in the command line prevents completions from working ATM for fish
4342
const fishOutput = execSync('functions -n', options);
4443
commands = fishOutput.split(', ').filter(filter);
4544
break;
4645
}
4746
case 'pwsh': {
48-
const output = execSync('Get-Command | Select-Object Name, CommandType, DisplayName | ConvertTo-Json', options);
47+
// TODO: Select `CommandType, DisplayName` and map to a rich type with kind and detail
48+
const output = execSync('Get-Command -All | Select-Object Name | ConvertTo-Json', options);
4949
let json: any;
5050
try {
5151
json = JSON.parse(output);
5252
} catch (e) {
5353
console.error('Error parsing pwsh output:', e);
5454
return [];
5555
}
56-
// TODO: Return a rich type with kind and detail
5756
commands = (json as any[]).map(e => e.Name);
5857
break;
5958
}
6059
}
61-
// TODO: Cache failure results too
62-
if (commands?.length) {
63-
cachedBuiltinCommands?.set(shellType, commands);
64-
return commands;
65-
}
66-
return;
60+
cachedBuiltinCommands.set(shellType, commands);
61+
return commands;
6762

6863
} catch (error) {
6964
console.error('Error fetching builtin commands:', error);

0 commit comments

Comments
 (0)