Skip to content

Commit 47aaaac

Browse files
authored
Merge branch 'microsoft:main' into main
2 parents 85e2fde + f195c00 commit 47aaaac

File tree

8 files changed

+261
-59
lines changed

8 files changed

+261
-59
lines changed

extensions/git/src/git.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1995,6 +1995,26 @@ export class Repository {
19951995
});
19961996
}
19971997

1998+
async getHEADBranch(): Promise<Branch | undefined> {
1999+
let HEAD: Branch | undefined;
2000+
2001+
try {
2002+
HEAD = await this.getHEAD();
2003+
2004+
if (HEAD.name) {
2005+
try {
2006+
HEAD = await this.getBranch(HEAD.name);
2007+
} catch (err) {
2008+
// noop
2009+
}
2010+
}
2011+
} catch (err) {
2012+
// noop
2013+
}
2014+
2015+
return HEAD;
2016+
}
2017+
19982018
async getHEAD(): Promise<Ref> {
19992019
try {
20002020
// Attempt to parse the HEAD file

extensions/git/src/repository.ts

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,27 +2017,19 @@ export class Repository implements Disposable {
20172017
this.telemetryReporter.sendTelemetryEvent('statusSlow', { ignoreSubmodules: String(ignoreSubmodules), didHitLimit: String(didHitLimit), didWarnAboutLimit: String(this.didWarnAboutLimit) }, { statusLength, totalTime });
20182018
}
20192019

2020-
let HEAD: Branch | undefined;
2021-
2022-
try {
2023-
HEAD = await this.repository.getHEAD();
2024-
2025-
if (HEAD.name) {
2026-
try {
2027-
HEAD = await this.repository.getBranch(HEAD.name);
2028-
} catch (err) {
2029-
// noop
2030-
}
2031-
}
2032-
} catch (err) {
2033-
// noop
2034-
}
2035-
20362020
let sort = config.get<'alphabetically' | 'committerdate'>('branchSortOrder') || 'alphabetically';
20372021
if (sort !== 'alphabetically' && sort !== 'committerdate') {
20382022
sort = 'alphabetically';
20392023
}
2040-
const [refs, remotes, submodules, rebaseCommit, mergeInProgress, commitTemplate] = await Promise.all([this.repository.getRefs({ sort }), this.repository.getRemotes(), this.repository.getSubmodules(), this.getRebaseCommit(), this.isMergeInProgress(), this.getInputTemplate()]);
2024+
const [HEAD, refs, remotes, submodules, rebaseCommit, mergeInProgress, commitTemplate] =
2025+
await Promise.all([
2026+
this.repository.getHEADBranch(),
2027+
this.repository.getRefs({ sort }),
2028+
this.repository.getRemotes(),
2029+
this.repository.getSubmodules(),
2030+
this.getRebaseCommit(),
2031+
this.isMergeInProgress(),
2032+
this.getInputTemplate()]);
20412033

20422034
this._HEAD = HEAD;
20432035
this._refs = refs!;

src/vs/editor/browser/editorExtensions.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,12 @@ export abstract class EditorCommand extends Command {
248248
};
249249
}
250250

251-
public runCommand(accessor: ServicesAccessor, args: any): void | Promise<void> {
251+
public static runEditorCommand(
252+
accessor: ServicesAccessor,
253+
args: any,
254+
precondition: ContextKeyExpression | undefined,
255+
runner: (accessor: ServicesAccessor | null, editor: ICodeEditor, args: any) => void | Promise<void>
256+
): void | Promise<void> {
252257
const codeEditorService = accessor.get(ICodeEditorService);
253258

254259
// Find the editor with text focus or active
@@ -260,15 +265,19 @@ export abstract class EditorCommand extends Command {
260265

261266
return editor.invokeWithinContext((editorAccessor) => {
262267
const kbService = editorAccessor.get(IContextKeyService);
263-
if (!kbService.contextMatchesRules(withNullAsUndefined(this.precondition))) {
268+
if (!kbService.contextMatchesRules(withNullAsUndefined(precondition))) {
264269
// precondition does not hold
265270
return;
266271
}
267272

268-
return this.runEditorCommand(editorAccessor, editor!, args);
273+
return runner(editorAccessor, editor, args);
269274
});
270275
}
271276

277+
public runCommand(accessor: ServicesAccessor, args: any): void | Promise<void> {
278+
return EditorCommand.runEditorCommand(accessor, args, this.precondition, (accessor, editor, args) => this.runEditorCommand(accessor, editor, args));
279+
}
280+
272281
public abstract runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void | Promise<void>;
273282
}
274283

src/vs/editor/standalone/browser/standaloneEditor.ts

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import 'vs/css!./standalone-tokens';
7-
import { IDisposable } from 'vs/base/common/lifecycle';
7+
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
88
import { splitLines } from 'vs/base/common/strings';
99
import { URI } from 'vs/base/common/uri';
1010
import { FontMeasurements } from 'vs/editor/browser/config/fontMeasurements';
@@ -23,12 +23,16 @@ import { IModelService } from 'vs/editor/common/services/model';
2323
import { createWebWorker as actualCreateWebWorker, IWebWorkerOptions, MonacoWebWorker } from 'vs/editor/browser/services/webWorker';
2424
import * as standaloneEnums from 'vs/editor/common/standalone/standaloneEnums';
2525
import { Colorizer, IColorizerElementOptions, IColorizerOptions } from 'vs/editor/standalone/browser/colorizer';
26-
import { createTextModel, IStandaloneCodeEditor, IStandaloneDiffEditor, IStandaloneDiffEditorConstructionOptions, IStandaloneEditorConstructionOptions, StandaloneDiffEditor, StandaloneEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor';
27-
import { IEditorOverrideServices, StandaloneServices } from 'vs/editor/standalone/browser/standaloneServices';
26+
import { createTextModel, IActionDescriptor, IStandaloneCodeEditor, IStandaloneDiffEditor, IStandaloneDiffEditorConstructionOptions, IStandaloneEditorConstructionOptions, StandaloneDiffEditor, StandaloneEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor';
27+
import { IEditorOverrideServices, StandaloneKeybindingService, StandaloneServices } from 'vs/editor/standalone/browser/standaloneServices';
2828
import { StandaloneThemeService } from 'vs/editor/standalone/browser/standaloneThemeService';
2929
import { IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneTheme';
30-
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
30+
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
3131
import { IMarker, IMarkerData, IMarkerService } from 'vs/platform/markers/common/markers';
32+
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
33+
import { EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
34+
import { IMenuItem, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
35+
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
3236

3337
/**
3438
* Create a new editor under `domElement`.
@@ -99,6 +103,119 @@ export function createDiffNavigator(diffEditor: IStandaloneDiffEditor, opts?: ID
99103
return new DiffNavigator(diffEditor, opts);
100104
}
101105

106+
/**
107+
* Description of a command contribution
108+
*/
109+
export interface ICommandDescriptor {
110+
/**
111+
* An unique identifier of the contributed command.
112+
*/
113+
id: string;
114+
/**
115+
* Callback that will be executed when the command is triggered.
116+
*/
117+
run: ICommandHandler;
118+
}
119+
120+
/**
121+
* Add a command.
122+
*/
123+
export function addCommand(descriptor: ICommandDescriptor): IDisposable {
124+
if ((typeof descriptor.id !== 'string') || (typeof descriptor.run !== 'function')) {
125+
throw new Error('Invalid command descriptor, `id` and `run` are required properties!');
126+
}
127+
return CommandsRegistry.registerCommand(descriptor.id, descriptor.run);
128+
}
129+
130+
/**
131+
* Add an action to all editors.
132+
*/
133+
export function addEditorAction(descriptor: IActionDescriptor): IDisposable {
134+
if ((typeof descriptor.id !== 'string') || (typeof descriptor.label !== 'string') || (typeof descriptor.run !== 'function')) {
135+
throw new Error('Invalid action descriptor, `id`, `label` and `run` are required properties!');
136+
}
137+
138+
const precondition = ContextKeyExpr.deserialize(descriptor.precondition);
139+
const run = (accessor: ServicesAccessor, ...args: any[]): void | Promise<void> => {
140+
return EditorCommand.runEditorCommand(accessor, args, precondition, (accessor, editor, args) => Promise.resolve(descriptor.run(editor, ...args)));
141+
};
142+
143+
const toDispose = new DisposableStore();
144+
145+
// Register the command
146+
toDispose.add(CommandsRegistry.registerCommand(descriptor.id, run));
147+
148+
// Register the context menu item
149+
if (descriptor.contextMenuGroupId) {
150+
const menuItem: IMenuItem = {
151+
command: {
152+
id: descriptor.id,
153+
title: descriptor.label
154+
},
155+
when: precondition,
156+
group: descriptor.contextMenuGroupId,
157+
order: descriptor.contextMenuOrder || 0
158+
};
159+
toDispose.add(MenuRegistry.appendMenuItem(MenuId.EditorContext, menuItem));
160+
}
161+
162+
// Register the keybindings
163+
if (Array.isArray(descriptor.keybindings)) {
164+
const keybindingService = StandaloneServices.get(IKeybindingService);
165+
if (!(keybindingService instanceof StandaloneKeybindingService)) {
166+
console.warn('Cannot add keybinding because the editor is configured with an unrecognized KeybindingService');
167+
} else {
168+
const keybindingsWhen = ContextKeyExpr.and(precondition, ContextKeyExpr.deserialize(descriptor.keybindingContext));
169+
toDispose.add(keybindingService.addDynamicKeybindings(descriptor.keybindings.map((keybinding) => {
170+
return {
171+
keybinding,
172+
command: descriptor.id,
173+
when: keybindingsWhen
174+
};
175+
})));
176+
}
177+
}
178+
179+
return toDispose;
180+
}
181+
182+
/**
183+
* A keybinding rule.
184+
*/
185+
export interface IKeybindingRule {
186+
keybinding: number;
187+
command?: string | null;
188+
commandArgs?: any;
189+
when?: string | null;
190+
}
191+
192+
/**
193+
* Add a keybinding rule.
194+
*/
195+
export function addKeybindingRule(rule: IKeybindingRule): IDisposable {
196+
return addKeybindingRules([rule]);
197+
}
198+
199+
/**
200+
* Add keybinding rules.
201+
*/
202+
export function addKeybindingRules(rules: IKeybindingRule[]): IDisposable {
203+
const keybindingService = StandaloneServices.get(IKeybindingService);
204+
if (!(keybindingService instanceof StandaloneKeybindingService)) {
205+
console.warn('Cannot add keybinding because the editor is configured with an unrecognized KeybindingService');
206+
return Disposable.None;
207+
}
208+
209+
return keybindingService.addDynamicKeybindings(rules.map((rule) => {
210+
return {
211+
keybinding: rule.keybinding,
212+
command: rule.command,
213+
commandArgs: rule.commandArgs,
214+
when: ContextKeyExpr.deserialize(rule.when),
215+
};
216+
}));
217+
}
218+
102219
/**
103220
* Create a new editor model.
104221
* You can specify the language that should be set for this model or let the language be inferred from the `uri`.
@@ -325,6 +442,11 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
325442
createDiffEditor: <any>createDiffEditor,
326443
createDiffNavigator: <any>createDiffNavigator,
327444

445+
addCommand: <any>addCommand,
446+
addEditorAction: <any>addEditorAction,
447+
addKeybindingRule: <any>addKeybindingRule,
448+
addKeybindingRules: <any>addKeybindingRules,
449+
328450
createModel: <any>createModel,
329451
setModelLanguage: <any>setModelLanguage,
330452
setModelMarkers: <any>setModelMarkers,

src/vs/editor/standalone/browser/standaloneServices.ts

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import * as dom from 'vs/base/browser/dom';
1414
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
1515
import { Emitter, Event } from 'vs/base/common/event';
1616
import { Keybinding, ResolvedKeybinding, SimpleKeybinding, createKeybinding } from 'vs/base/common/keybindings';
17-
import { IDisposable, IReference, ImmortalReference, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
17+
import { IDisposable, IReference, ImmortalReference, toDisposable, DisposableStore, Disposable, combinedDisposable } from 'vs/base/common/lifecycle';
1818
import { OS, isLinux, isMacintosh } from 'vs/base/common/platform';
1919
import Severity from 'vs/base/common/severity';
2020
import { URI } from 'vs/base/common/uri';
@@ -323,9 +323,16 @@ export class StandaloneCommandService implements ICommandService {
323323
}
324324
}
325325

326+
export interface IKeybindingRule {
327+
keybinding: number;
328+
command?: string | null;
329+
commandArgs?: any;
330+
when?: ContextKeyExpression | null;
331+
}
332+
326333
export class StandaloneKeybindingService extends AbstractKeybindingService {
327334
private _cachedResolver: KeybindingResolver | null;
328-
private readonly _dynamicKeybindings: IKeybindingItem[];
335+
private _dynamicKeybindings: IKeybindingItem[];
329336
private readonly _domNodeListeners: DomNodeListeners[];
330337

331338
constructor(
@@ -403,39 +410,45 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
403410
codeEditorService.listDiffEditors().forEach(addDiffEditor);
404411
}
405412

406-
public addDynamicKeybinding(commandId: string, _keybinding: number, handler: ICommandHandler, when: ContextKeyExpression | undefined): IDisposable {
407-
const keybinding = createKeybinding(_keybinding, OS);
408-
409-
const toDispose = new DisposableStore();
413+
public addDynamicKeybinding(command: string, keybinding: number, handler: ICommandHandler, when: ContextKeyExpression | undefined): IDisposable {
414+
return combinedDisposable(
415+
CommandsRegistry.registerCommand(command, handler),
416+
this.addDynamicKeybindings([{
417+
keybinding,
418+
command,
419+
when
420+
}])
421+
);
422+
}
410423

411-
if (keybinding) {
412-
this._dynamicKeybindings.push({
413-
keybinding: keybinding.parts,
414-
command: commandId,
415-
when: when,
424+
public addDynamicKeybindings(rules: IKeybindingRule[]): IDisposable {
425+
const entries: IKeybindingItem[] = rules.map((rule) => {
426+
const keybinding = createKeybinding(rule.keybinding, OS);
427+
return {
428+
keybinding: keybinding?.parts ?? null,
429+
command: rule.command ?? null,
430+
commandArgs: rule.commandArgs,
431+
when: rule.when,
416432
weight1: 1000,
417433
weight2: 0,
418434
extensionId: null,
419435
isBuiltinExtension: false
420-
});
421-
422-
toDispose.add(toDisposable(() => {
423-
for (let i = 0; i < this._dynamicKeybindings.length; i++) {
424-
const kb = this._dynamicKeybindings[i];
425-
if (kb.command === commandId) {
426-
this._dynamicKeybindings.splice(i, 1);
427-
this.updateResolver();
428-
return;
429-
}
430-
}
431-
}));
432-
}
433-
434-
toDispose.add(CommandsRegistry.registerCommand(commandId, handler));
436+
};
437+
});
438+
this._dynamicKeybindings = this._dynamicKeybindings.concat(entries);
435439

436440
this.updateResolver();
437441

438-
return toDispose;
442+
return toDisposable(() => {
443+
// Search the first entry and remove them all since they will be contiguous
444+
for (let i = 0; i < this._dynamicKeybindings.length; i++) {
445+
if (this._dynamicKeybindings[i] === entries[0]) {
446+
this._dynamicKeybindings.splice(i, entries.length);
447+
this.updateResolver();
448+
return;
449+
}
450+
}
451+
});
439452
}
440453

441454
private updateResolver(): void {

0 commit comments

Comments
 (0)