Skip to content

Commit 5accc60

Browse files
committed
Merge remote-tracking branch 'origin/main' into tyriar/256552
2 parents dfaf67b + ff6f70b commit 5accc60

File tree

62 files changed

+1365
-270
lines changed

Some content is hidden

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

62 files changed

+1365
-270
lines changed

cli/src/commands/serve_web.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result<i3
127127
};
128128
let builder = Server::try_bind(&addr).map_err(CodeError::CouldNotListenOnInterface)?;
129129

130-
let mut listening = format!("Web UI available at http://{addr}");
130+
// Get the actual bound address (important when port 0 is used for random port assignment)
131+
let bound_addr = builder.local_addr();
132+
let mut listening = format!("Web UI available at http://{bound_addr}");
131133
if let Some(base) = args.server_base_path {
132134
if !base.starts_with('/') {
133135
listening.push('/');

extensions/git/src/commands.ts

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2917,10 +2917,15 @@ export class CommandCenter {
29172917
try {
29182918
await item.run(repository, opts);
29192919
} catch (err) {
2920-
if (err.gitErrorCode !== GitErrorCodes.DirtyWorkTree) {
2920+
if (err.gitErrorCode !== GitErrorCodes.DirtyWorkTree && err.gitErrorCode !== GitErrorCodes.WorktreeAlreadyExists) {
29212921
throw err;
29222922
}
29232923

2924+
if (err.gitErrorCode === GitErrorCodes.WorktreeAlreadyExists) {
2925+
this.handleWorktreeError(err);
2926+
return false;
2927+
}
2928+
29242929
const stash = l10n.t('Stash & Checkout');
29252930
const migrate = l10n.t('Migrate Changes');
29262931
const force = l10n.t('Force Checkout');
@@ -3458,37 +3463,45 @@ export class CommandCenter {
34583463
try {
34593464
await repository.worktree({ name: name, path: worktreePath });
34603465
} catch (err) {
3461-
if (err.gitErrorCode === GitErrorCodes.WorktreeAlreadyExists) {
3462-
const errorMessage = err.stderr;
3463-
const match = errorMessage.match(/worktree at '([^']+)'/) || errorMessage.match(/'([^']+)'/);
3464-
const path = match ? match[1] : undefined;
3466+
if (err.gitErrorCode !== GitErrorCodes.WorktreeAlreadyExists) {
3467+
throw err;
3468+
}
34653469

3466-
if (!path) {
3467-
return;
3468-
}
3470+
this.handleWorktreeError(err);
3471+
return;
34693472

3470-
const openWorktree = l10n.t('Open in current window');
3471-
const openWorktreeInNewWindow = l10n.t('Open in new window');
3472-
const message = l10n.t(errorMessage || 'A worktree for branch \'{0}\' already exists at \'{1}\'.', name, path);
3473-
const choice = await window.showWarningMessage(message, { modal: true }, openWorktree, openWorktreeInNewWindow);
3473+
}
3474+
}
34743475

3475-
const worktreeRepository = this.model.getRepository(path) || this.model.getRepository(Uri.file(path));
3476+
private async handleWorktreeError(err: any): Promise<void> {
3477+
const errorMessage = err.stderr;
3478+
const match = errorMessage.match(/worktree at '([^']+)'/) || errorMessage.match(/'([^']+)'/);
3479+
const path = match ? match[1] : undefined;
34763480

3477-
if (!worktreeRepository) {
3478-
return;
3479-
}
3481+
if (!path) {
3482+
return;
3483+
}
34803484

3481-
if (choice === openWorktree) {
3482-
await this.openWorktreeInCurrentWindow(worktreeRepository);
3483-
} else if (choice === openWorktreeInNewWindow) {
3484-
await this.openWorktreeInNewWindow(worktreeRepository);
3485-
}
3485+
const worktreeRepository = this.model.getRepository(path) || this.model.getRepository(Uri.file(path));
34863486

3487-
return;
3488-
}
3487+
if (!worktreeRepository) {
3488+
return;
3489+
}
34893490

3490-
throw err;
3491+
const openWorktree = l10n.t('Open in current window');
3492+
const openWorktreeInNewWindow = l10n.t('Open in new window');
3493+
const message = l10n.t(errorMessage);
3494+
const choice = await window.showWarningMessage(message, { modal: true }, openWorktree, openWorktreeInNewWindow);
3495+
3496+
3497+
3498+
if (choice === openWorktree) {
3499+
await this.openWorktreeInCurrentWindow(worktreeRepository);
3500+
} else if (choice === openWorktreeInNewWindow) {
3501+
await this.openWorktreeInNewWindow(worktreeRepository);
34913502
}
3503+
3504+
return;
34923505
}
34933506

34943507
@command('git.deleteWorktree', { repository: true })

src/vs/base/browser/ui/toggle/toggle.css

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,3 @@
6767
.monaco-action-bar .checkbox-action-item > .checkbox-label {
6868
font-size: 12px;
6969
}
70-
71-
/* hide check when unchecked */
72-
.monaco-custom-toggle.monaco-checkbox:not(.checked)::before {
73-
visibility: hidden;
74-
}

src/vs/base/browser/ui/toggle/toggle.ts

Lines changed: 92 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -258,27 +258,20 @@ export class Toggle extends Widget {
258258
}
259259
}
260260

261-
export class Checkbox extends Widget {
262261

262+
abstract class BaseCheckbox extends Widget {
263263
static readonly CLASS_NAME = 'monaco-checkbox';
264264

265-
private readonly _onChange = this._register(new Emitter<boolean>());
265+
protected readonly _onChange = this._register(new Emitter<boolean>());
266266
readonly onChange: Event<boolean /* via keyboard */> = this._onChange.event;
267267

268-
private checkbox: Toggle;
269-
private styles: ICheckboxStyles;
270-
271-
readonly domNode: HTMLElement;
272-
273-
constructor(private title: string, private isChecked: boolean, styles: ICheckboxStyles) {
268+
constructor(
269+
protected readonly checkbox: Toggle,
270+
readonly domNode: HTMLElement,
271+
protected readonly styles: ICheckboxStyles
272+
) {
274273
super();
275274

276-
this.checkbox = this._register(new Toggle({ title: this.title, isChecked: this.isChecked, icon: Codicon.check, actionClassName: Checkbox.CLASS_NAME, hoverDelegate: styles.hoverDelegate, ...unthemedToggleStyles }));
277-
278-
this.domNode = this.checkbox.domNode;
279-
280-
this.styles = styles;
281-
282275
this.applyStyles();
283276

284277
this._register(this.checkbox.onChange(keyboard => {
@@ -287,20 +280,10 @@ export class Checkbox extends Widget {
287280
}));
288281
}
289282

290-
get checked(): boolean {
291-
return this.checkbox.checked;
292-
}
293-
294283
get enabled(): boolean {
295284
return this.checkbox.enabled;
296285
}
297286

298-
set checked(newIsChecked: boolean) {
299-
this.checkbox.checked = newIsChecked;
300-
301-
this.applyStyles();
302-
}
303-
304287
focus(): void {
305288
this.domNode.focus();
306289
}
@@ -336,6 +319,91 @@ export class Checkbox extends Widget {
336319
}
337320
}
338321

322+
export class Checkbox extends BaseCheckbox {
323+
constructor(title: string, isChecked: boolean, styles: ICheckboxStyles) {
324+
const toggle = new Toggle({ title, isChecked, icon: Codicon.check, actionClassName: BaseCheckbox.CLASS_NAME, hoverDelegate: styles.hoverDelegate, ...unthemedToggleStyles });
325+
326+
super(toggle, toggle.domNode, styles);
327+
this._register(toggle);
328+
329+
this.applyStyles();
330+
331+
this._register(this.checkbox.onChange(keyboard => {
332+
this.applyStyles();
333+
this._onChange.fire(keyboard);
334+
}));
335+
}
336+
337+
get checked(): boolean {
338+
return this.checkbox.checked;
339+
}
340+
341+
set checked(newIsChecked: boolean) {
342+
this.checkbox.checked = newIsChecked;
343+
if (newIsChecked) {
344+
this.checkbox.setIcon(Codicon.check);
345+
} else {
346+
this.checkbox.setIcon(undefined);
347+
}
348+
this.applyStyles();
349+
}
350+
}
351+
352+
export class TriStateCheckbox extends BaseCheckbox {
353+
constructor(title: string, initialState: boolean | 'partial', styles: ICheckboxStyles) {
354+
let icon: ThemeIcon | undefined;
355+
switch (initialState) {
356+
case true:
357+
icon = Codicon.check;
358+
break;
359+
case 'partial':
360+
icon = Codicon.dash;
361+
break;
362+
case false:
363+
icon = undefined;
364+
break;
365+
}
366+
const checkbox = new Toggle({
367+
title,
368+
isChecked: initialState === true,
369+
icon,
370+
actionClassName: Checkbox.CLASS_NAME,
371+
hoverDelegate: styles.hoverDelegate,
372+
...unthemedToggleStyles
373+
});
374+
375+
super(
376+
checkbox,
377+
checkbox.domNode,
378+
styles
379+
);
380+
this._register(checkbox);
381+
}
382+
383+
get checked(): boolean | 'partial' {
384+
return this.checkbox.checked;
385+
}
386+
387+
set checked(newState: boolean | 'partial') {
388+
const checked = newState === true;
389+
this.checkbox.checked = checked;
390+
391+
switch (newState) {
392+
case true:
393+
this.checkbox.setIcon(Codicon.check);
394+
break;
395+
case 'partial':
396+
this.checkbox.setIcon(Codicon.dash);
397+
break;
398+
case false:
399+
this.checkbox.setIcon(undefined);
400+
break;
401+
}
402+
403+
this.applyStyles();
404+
}
405+
}
406+
339407
export interface ICheckboxActionViewItemOptions extends IActionViewItemOptions {
340408
checkboxStyles: ICheckboxStyles;
341409
}

src/vs/editor/common/config/editorOptions.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5305,7 +5305,12 @@ class WordSegmenterLocales extends BaseEditorOption<EditorOption.wordSegmenterLo
53055305
}
53065306
}
53075307
],
5308-
description: nls.localize('wordSegmenterLocales', "Locales to be used for word segmentation when doing word related navigations or operations. Specify the BCP 47 language tag of the word you wish to recognize (e.g., ja, zh-CN, zh-Hant-TW, etc.). The locale specification can be a string or an array of strings."),
5308+
description: nls.localize('wordSegmenterLocales', "Locales to be used for word segmentation when doing word related navigations or operations. Specify the BCP 47 language tag of the word you wish to recognize (e.g., ja, zh-CN, zh-Hant-TW, etc.)."),
5309+
type: 'array',
5310+
items: {
5311+
type: 'string',
5312+
},
5313+
default: defaults,
53095314
},
53105315
);
53115316
}

src/vs/platform/extensions/common/extensionsApiProposals.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ const _allApiProposals = {
3737
chatEditing: {
3838
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatEditing.d.ts',
3939
},
40+
chatOutputRenderer: {
41+
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatOutputRenderer.d.ts',
42+
},
4043
chatParticipantAdditions: {
4144
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts',
4245
},

src/vs/platform/mcp/common/mcpManagementService.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,11 @@ export abstract class AbstractMcpResourceManagementService extends Disposable {
8484
private initialize(): Promise<void> {
8585
if (!this.initializePromise) {
8686
this.initializePromise = (async () => {
87-
this.local = await this.populateLocalServers();
88-
this.startWatching();
87+
try {
88+
this.local = await this.populateLocalServers();
89+
} finally {
90+
this.startWatching();
91+
}
8992
})();
9093
}
9194
return this.initializePromise;

src/vs/platform/mcp/common/mcpResourceScannerService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export class McpResourceScannerService extends Disposable implements IMcpResourc
100100
try {
101101
const content = await this.fileService.readFile(mcpResource);
102102
const errors: ParseError[] = [];
103-
const result = parse(content.value.toString(), errors, { allowTrailingComma: true, allowEmptyContent: true });
103+
const result = parse(content.value.toString(), errors, { allowTrailingComma: true, allowEmptyContent: true }) || {};
104104
if (errors.length > 0) {
105105
throw new Error('Failed to parse scanned MCP servers: ' + errors.join(', '));
106106
}

src/vs/platform/quickinput/browser/quickInput.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { IInputBoxStyles } from '../../../base/browser/ui/inputbox/inputBox.js';
1313
import { IKeybindingLabelStyles } from '../../../base/browser/ui/keybindingLabel/keybindingLabel.js';
1414
import { IListStyles } from '../../../base/browser/ui/list/listWidget.js';
1515
import { IProgressBarStyles, ProgressBar } from '../../../base/browser/ui/progressbar/progressbar.js';
16-
import { Checkbox, IToggleStyles, Toggle } from '../../../base/browser/ui/toggle/toggle.js';
16+
import { IToggleStyles, Toggle, TriStateCheckbox } from '../../../base/browser/ui/toggle/toggle.js';
1717
import { equals } from '../../../base/common/arrays.js';
1818
import { TimeoutTimer } from '../../../base/common/async.js';
1919
import { Codicon } from '../../../base/common/codicons.js';
@@ -30,7 +30,7 @@ import { QuickInputBox } from './quickInputBox.js';
3030
import { quickInputButtonToAction, renderQuickInputDescription } from './quickInputUtils.js';
3131
import { IConfigurationService } from '../../configuration/common/configuration.js';
3232
import { IHoverService, WorkbenchHoverDelegate } from '../../hover/browser/hover.js';
33-
import { QuickInputTree } from './quickInputTree.js';
33+
import { QuickInputList } from './quickInputList.js';
3434
import type { IHoverOptions } from '../../../base/browser/ui/hover/hover.js';
3535
import { ContextKeyExpr, RawContextKey } from '../../contextkey/common/contextkey.js';
3636

@@ -103,7 +103,7 @@ export interface QuickInputUI {
103103
widget: HTMLElement;
104104
rightActionBar: ActionBar;
105105
inlineActionBar: ActionBar;
106-
checkAll: Checkbox;
106+
checkAll: TriStateCheckbox;
107107
inputContainer: HTMLElement;
108108
filterContainer: HTMLElement;
109109
inputBox: QuickInputBox;
@@ -117,7 +117,7 @@ export interface QuickInputUI {
117117
customButtonContainer: HTMLElement;
118118
customButton: Button;
119119
progressBar: ProgressBar;
120-
list: QuickInputTree;
120+
list: QuickInputList;
121121
onDidAccept: Event<void>;
122122
onDidCustom: Event<void>;
123123
onDidTriggerButton: Event<IQuickInputButton>;

src/vs/platform/quickinput/browser/quickInputController.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { QuickInputUI, Writeable, IQuickInputStyles, IQuickInputOptions, QuickPi
2323
import { ILayoutService } from '../../layout/browser/layoutService.js';
2424
import { mainWindow } from '../../../base/browser/window.js';
2525
import { IInstantiationService } from '../../instantiation/common/instantiation.js';
26-
import { QuickInputTree } from './quickInputTree.js';
26+
import { QuickInputList } from './quickInputList.js';
2727
import { IContextKey, IContextKeyService } from '../../contextkey/common/contextkey.js';
2828
import './quickInputActions.js';
2929
import { autorun, observableValue } from '../../../base/common/observable.js';
@@ -33,7 +33,7 @@ import { IConfigurationService } from '../../configuration/common/configuration.
3333
import { Platform, platform } from '../../../base/common/platform.js';
3434
import { getWindowControlsStyle, WindowControlsStyle } from '../../window/common/window.js';
3535
import { getZoomFactor } from '../../../base/browser/browser.js';
36-
import { Checkbox } from '../../../base/browser/ui/toggle/toggle.js';
36+
import { TriStateCheckbox } from '../../../base/browser/ui/toggle/toggle.js';
3737
import { defaultCheckboxStyles } from '../../theme/browser/defaultStyles.js';
3838

3939
const $ = dom.$;
@@ -154,11 +154,11 @@ export class QuickInputController extends Disposable {
154154

155155
const headerContainer = dom.append(container, $('.quick-input-header'));
156156

157-
const checkAll = this._register(new Checkbox(localize('quickInput.checkAll', "Toggle all checkboxes"), false, { ...defaultCheckboxStyles, size: 15 }));
157+
const checkAll = this._register(new TriStateCheckbox(localize('quickInput.checkAll', "Toggle all checkboxes"), false, { ...defaultCheckboxStyles, size: 15 }));
158158
dom.append(headerContainer, checkAll.domNode);
159159
this._register(checkAll.onChange(() => {
160160
const checked = checkAll.checked;
161-
list.setAllVisibleChecked(checked);
161+
list.setAllVisibleChecked(checked === true);
162162
}));
163163
this._register(dom.addDisposableListener(checkAll.domNode, dom.EventType.CLICK, e => {
164164
if (e.x || e.y) { // Avoid 'click' triggered by 'space'...
@@ -210,14 +210,15 @@ export class QuickInputController extends Disposable {
210210
const description1 = dom.append(container, $('.quick-input-description'));
211211

212212
const listId = this.idPrefix + 'list';
213-
const list = this._register(this.instantiationService.createInstance(QuickInputTree, container, this.options.hoverDelegate, this.options.linkOpenerDelegate, listId));
213+
const list = this._register(this.instantiationService.createInstance(QuickInputList, container, this.options.hoverDelegate, this.options.linkOpenerDelegate, listId));
214214
inputBox.setAttribute('aria-controls', listId);
215215
this._register(list.onDidChangeFocus(() => {
216216
if (inputBox.hasFocus()) {
217217
inputBox.setAttribute('aria-activedescendant', list.getActiveDescendant() ?? '');
218218
}
219219
}));
220220
this._register(list.onChangedAllVisibleChecked(checked => {
221+
// TODO: Support tri-state checkbox when we remove the .indent property that is faking tree structure.
221222
checkAll.checked = checked;
222223
}));
223224
this._register(list.onChangedVisibleCount(c => {

0 commit comments

Comments
 (0)