mirror of
https://github.com/Microsoft/vscode
synced 2024-07-17 02:57:19 +00:00
Merge branch 'main' into joh/voluminous-lobster
This commit is contained in:
commit
ed9ed45794
|
@ -318,7 +318,7 @@ steps:
|
|||
targetPath: .build/logs
|
||||
displayName: "Publish Log Files"
|
||||
continueOnError: true
|
||||
condition: failed()
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish Tests Results
|
||||
|
|
|
@ -387,7 +387,7 @@ steps:
|
|||
targetPath: .build/logs
|
||||
displayName: "Publish Log Files"
|
||||
continueOnError: true
|
||||
condition: and(failed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish Tests Results
|
||||
|
|
|
@ -339,7 +339,7 @@ steps:
|
|||
targetPath: .build\logs
|
||||
displayName: "Publish Log Files"
|
||||
continueOnError: true
|
||||
condition: and(failed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish Tests Results
|
||||
|
|
|
@ -3129,9 +3129,7 @@ export class CommandCenter {
|
|||
if (result) {
|
||||
const resultFn = choices.get(result);
|
||||
|
||||
if (resultFn) {
|
||||
resultFn();
|
||||
}
|
||||
resultFn?.();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -556,9 +556,7 @@ export class Git {
|
|||
private async _exec(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult<string>> {
|
||||
const child = this.spawn(args, options);
|
||||
|
||||
if (options.onSpawn) {
|
||||
options.onSpawn(child);
|
||||
}
|
||||
options.onSpawn?.(child);
|
||||
|
||||
if (options.input) {
|
||||
child.stdin!.end(options.input, 'utf8');
|
||||
|
|
|
@ -251,9 +251,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
|||
// The settings have changed. Is send on server activation as well.
|
||||
connection.onDidChangeConfiguration((change) => {
|
||||
const settings = <Settings>change.settings;
|
||||
if (runtime.configureHttpRequests) {
|
||||
runtime.configureHttpRequests(settings?.http?.proxy, !!settings.http?.proxyStrictSSL);
|
||||
}
|
||||
runtime.configureHttpRequests?.(settings?.http?.proxy, !!settings.http?.proxyStrictSSL);
|
||||
jsonConfigurationSettings = settings.json?.schemas;
|
||||
validateEnabled = !!settings.json?.validate?.enable;
|
||||
updateConfiguration();
|
||||
|
|
|
@ -185,12 +185,12 @@ function stripAngleBrackets(link: string) {
|
|||
/**
|
||||
* Matches `[text](link)`
|
||||
*/
|
||||
const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g;
|
||||
const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]]|\][^(])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g;
|
||||
|
||||
/**
|
||||
* Matches `[text](<link>)`
|
||||
*/
|
||||
const linkPatternAngle = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*<)(([^<>]|\([^\s\(\)]*?\))+)>\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g;
|
||||
const linkPatternAngle = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]]|\][^(])*\])\(\s*<)(([^<>]|\([^\s\(\)]*?\))+)>\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -101,6 +101,21 @@ suite('markdown.DocumentLinkProvider', () => {
|
|||
}
|
||||
});
|
||||
|
||||
test('Should ignore texts in brackets inside link title (#150921)', async () => {
|
||||
{
|
||||
const links = await getLinksForFile('[some [inner bracket pairs] in title](<link>)');
|
||||
assertLinksEqual(links, [
|
||||
new vscode.Range(0, 39, 0, 43),
|
||||
]);
|
||||
}
|
||||
{
|
||||
const links = await getLinksForFile('[some [inner bracket pairs] in title](link)');
|
||||
assertLinksEqual(links, [
|
||||
new vscode.Range(0, 38, 0, 42)
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
test('Should handle two links without space', async () => {
|
||||
const links = await getLinksForFile('a ([test](test)[test2](test2)) c');
|
||||
assertLinksEqual(links, [
|
||||
|
|
|
@ -287,8 +287,9 @@ async function provideNpmScriptsForFolder(context: ExtensionContext, packageJson
|
|||
result.push({ task, location: new Location(packageJsonUri, nameRange) });
|
||||
}
|
||||
|
||||
// always add npm install (without a problem matcher)
|
||||
result.push({ task: await createTask(packageManager, INSTALL_SCRIPT, [INSTALL_SCRIPT], folder, packageJsonUri, 'install dependencies from package', []) });
|
||||
if (!workspace.getConfiguration('npm', folder).get<string[]>('scriptExplorerExclude', []).find(e => e.includes('install'))) {
|
||||
result.push({ task: await createTask(packageManager, INSTALL_SCRIPT, [INSTALL_SCRIPT], folder, packageJsonUri, 'install dependencies from package', []) });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -927,6 +927,80 @@ const apiTestContentProvider: vscode.NotebookContentProvider = {
|
|||
|
||||
assert.strictEqual(executionWasCancelled, true);
|
||||
});
|
||||
|
||||
test('appendOutput to different cell', async function () {
|
||||
const notebook = await openRandomNotebookDocument();
|
||||
const editor = await vscode.window.showNotebookDocument(notebook);
|
||||
const cell0 = editor.notebook.cellAt(0);
|
||||
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
|
||||
const cell1 = editor.notebook.cellAt(1);
|
||||
|
||||
const nextCellKernel = new class extends Kernel {
|
||||
constructor() {
|
||||
super('nextCellKernel', 'Append to cell kernel');
|
||||
}
|
||||
|
||||
override async _runCell(cell: vscode.NotebookCell) {
|
||||
const task = this.controller.createNotebookCellExecution(cell);
|
||||
task.start();
|
||||
await task.appendOutput([new vscode.NotebookCellOutput([
|
||||
vscode.NotebookCellOutputItem.text('my output')
|
||||
])], cell1);
|
||||
await task.appendOutput([new vscode.NotebookCellOutput([
|
||||
vscode.NotebookCellOutputItem.text('my output 2')
|
||||
])], cell1);
|
||||
task.end(true);
|
||||
}
|
||||
};
|
||||
testDisposables.push(nextCellKernel.controller);
|
||||
|
||||
await withEvent<vscode.NotebookDocumentChangeEvent>(vscode.workspace.onDidChangeNotebookDocument, async (event) => {
|
||||
await assertKernel(nextCellKernel, notebook);
|
||||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
await event;
|
||||
assert.strictEqual(cell0.outputs.length, 0, 'should not change cell 0');
|
||||
assert.strictEqual(cell1.outputs.length, 2, 'should update cell 1');
|
||||
assert.strictEqual(cell1.outputs[0].items.length, 1);
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell1.outputs[0].items[0].data), 'my output');
|
||||
});
|
||||
});
|
||||
|
||||
test('replaceOutput to different cell', async function () {
|
||||
const notebook = await openRandomNotebookDocument();
|
||||
const editor = await vscode.window.showNotebookDocument(notebook);
|
||||
const cell0 = editor.notebook.cellAt(0);
|
||||
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
|
||||
const cell1 = editor.notebook.cellAt(1);
|
||||
|
||||
const nextCellKernel = new class extends Kernel {
|
||||
constructor() {
|
||||
super('nextCellKernel', 'Replace to cell kernel');
|
||||
}
|
||||
|
||||
override async _runCell(cell: vscode.NotebookCell) {
|
||||
const task = this.controller.createNotebookCellExecution(cell);
|
||||
task.start();
|
||||
await task.replaceOutput([new vscode.NotebookCellOutput([
|
||||
vscode.NotebookCellOutputItem.text('my output')
|
||||
])], cell1);
|
||||
await task.replaceOutput([new vscode.NotebookCellOutput([
|
||||
vscode.NotebookCellOutputItem.text('my output 2')
|
||||
])], cell1);
|
||||
task.end(true);
|
||||
}
|
||||
};
|
||||
testDisposables.push(nextCellKernel.controller);
|
||||
|
||||
await withEvent<vscode.NotebookDocumentChangeEvent>(vscode.workspace.onDidChangeNotebookDocument, async (event) => {
|
||||
await assertKernel(nextCellKernel, notebook);
|
||||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
await event;
|
||||
assert.strictEqual(cell0.outputs.length, 0, 'should not change cell 0');
|
||||
assert.strictEqual(cell1.outputs.length, 1, 'should update cell 1');
|
||||
assert.strictEqual(cell1.outputs[0].items.length, 1);
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell1.outputs[0].items[0].data), 'my output 2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
(vscode.env.uiKind === vscode.UIKind.Web ? suite.skip : suite)('statusbar', () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.68.0",
|
||||
"version": "1.69.0",
|
||||
"distro": "2966cd72fc1a3a5fb89bf2d85a1a66e56206961a",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
|
|
|
@ -1733,9 +1733,7 @@ export class DragAndDropObserver extends Disposable {
|
|||
this._register(addDisposableListener(this.element, EventType.DRAG_OVER, (e: DragEvent) => {
|
||||
e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
|
||||
|
||||
if (this.callbacks.onDragOver) {
|
||||
this.callbacks.onDragOver(e, e.timeStamp - this.dragStartTime);
|
||||
}
|
||||
this.callbacks.onDragOver?.(e, e.timeStamp - this.dragStartTime);
|
||||
}));
|
||||
|
||||
this._register(addDisposableListener(this.element, EventType.DRAG_LEAVE, (e: DragEvent) => {
|
||||
|
|
|
@ -220,9 +220,7 @@ export class ContextView extends Disposable {
|
|||
this.doLayout();
|
||||
|
||||
// Focus
|
||||
if (this.delegate.focus) {
|
||||
this.delegate.focus();
|
||||
}
|
||||
this.delegate.focus?.();
|
||||
}
|
||||
|
||||
getViewElement(): HTMLElement {
|
||||
|
|
|
@ -857,9 +857,7 @@ class LeafNode implements ISplitView<ILayoutContext>, IDisposable {
|
|||
set boundarySashes(boundarySashes: IRelativeBoundarySashes) {
|
||||
this._boundarySashes = boundarySashes;
|
||||
|
||||
if (this.view.setBoundarySashes) {
|
||||
this.view.setBoundarySashes(toAbsoluteBoundarySashes(boundarySashes, this.orientation));
|
||||
}
|
||||
this.view.setBoundarySashes?.(toAbsoluteBoundarySashes(boundarySashes, this.orientation));
|
||||
}
|
||||
|
||||
layout(size: number, offset: number, ctx: ILayoutContext | undefined): void {
|
||||
|
@ -897,9 +895,7 @@ class LeafNode implements ISplitView<ILayoutContext>, IDisposable {
|
|||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
if (this.view.setVisible) {
|
||||
this.view.setVisible(visible);
|
||||
}
|
||||
this.view.setVisible?.(visible);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
|
|
@ -1032,9 +1032,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
|||
this.currentDragData = new ElementsDragAndDropData(elements);
|
||||
StaticDND.CurrentDragAndDropData = new ExternalElementsDragAndDropData(elements);
|
||||
|
||||
if (this.dnd.onDragStart) {
|
||||
this.dnd.onDragStart(this.currentDragData, event);
|
||||
}
|
||||
this.dnd.onDragStart?.(this.currentDragData, event);
|
||||
}
|
||||
|
||||
private onDragOver(event: IListDragEvent<T>): boolean {
|
||||
|
@ -1169,9 +1167,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
|||
this.currentDragData = undefined;
|
||||
StaticDND.CurrentDragAndDropData = undefined;
|
||||
|
||||
if (this.dnd.onDragEnd) {
|
||||
this.dnd.onDragEnd(event);
|
||||
}
|
||||
this.dnd.onDragEnd?.(event);
|
||||
}
|
||||
|
||||
private clearDragOverFeedback(): void {
|
||||
|
@ -1379,16 +1375,12 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
|||
if (renderer) {
|
||||
renderer.renderElement(item.element, index, row.templateData, undefined);
|
||||
|
||||
if (renderer.disposeElement) {
|
||||
renderer.disposeElement(item.element, index, row.templateData, undefined);
|
||||
}
|
||||
renderer.disposeElement?.(item.element, index, row.templateData, undefined);
|
||||
}
|
||||
|
||||
item.size = row.domNode.offsetHeight;
|
||||
|
||||
if (this.virtualDelegate.setDynamicHeight) {
|
||||
this.virtualDelegate.setDynamicHeight(item.element, item.size);
|
||||
}
|
||||
this.virtualDelegate.setDynamicHeight?.(item.element, item.size);
|
||||
|
||||
item.lastDynamicHeightWidth = this.renderWidth;
|
||||
this.rowsContainer.removeChild(row.domNode);
|
||||
|
@ -1429,9 +1421,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
|||
if (item.row) {
|
||||
const renderer = this.renderers.get(item.row.templateId);
|
||||
if (renderer) {
|
||||
if (renderer.disposeElement) {
|
||||
renderer.disposeElement(item.element, -1, item.row.templateData, undefined);
|
||||
}
|
||||
renderer.disposeElement?.(item.element, -1, item.row.templateData, undefined);
|
||||
renderer.disposeTemplate(item.row.templateData);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1109,9 +1109,7 @@ class PipelineRenderer<T> implements IListRenderer<T, any> {
|
|||
let i = 0;
|
||||
|
||||
for (const renderer of this.renderers) {
|
||||
if (renderer.disposeElement) {
|
||||
renderer.disposeElement(element, index, templateData[i], height);
|
||||
}
|
||||
renderer.disposeElement?.(element, index, templateData[i], height);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
@ -1182,9 +1180,7 @@ class ListViewDragAndDrop<T> implements IListViewDragAndDrop<T> {
|
|||
}
|
||||
|
||||
onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void {
|
||||
if (this.dnd.onDragStart) {
|
||||
this.dnd.onDragStart(data, originalEvent);
|
||||
}
|
||||
this.dnd.onDragStart?.(data, originalEvent);
|
||||
}
|
||||
|
||||
onDragOver(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): boolean | IListDragOverReaction {
|
||||
|
@ -1196,9 +1192,7 @@ class ListViewDragAndDrop<T> implements IListViewDragAndDrop<T> {
|
|||
}
|
||||
|
||||
onDragEnd(originalEvent: DragEvent): void {
|
||||
if (this.dnd.onDragEnd) {
|
||||
this.dnd.onDragEnd(originalEvent);
|
||||
}
|
||||
this.dnd.onDragEnd?.(originalEvent);
|
||||
}
|
||||
|
||||
drop(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): void {
|
||||
|
@ -1328,9 +1322,7 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
|
|||
if (this.accessibilityProvider) {
|
||||
baseRenderers.push(new AccessibiltyRenderer<T>(this.accessibilityProvider));
|
||||
|
||||
if (this.accessibilityProvider.onDidChangeActiveDescendant) {
|
||||
this.accessibilityProvider.onDidChangeActiveDescendant(this.onDidChangeActiveDescendant, this, this.disposables);
|
||||
}
|
||||
this.accessibilityProvider.onDidChangeActiveDescendant?.(this.onDidChangeActiveDescendant, this, this.disposables);
|
||||
}
|
||||
|
||||
renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r]));
|
||||
|
|
|
@ -231,9 +231,7 @@ abstract class ViewItem<TLayoutContext> {
|
|||
|
||||
this.container.classList.toggle('visible', visible);
|
||||
|
||||
if (this.view.setVisible) {
|
||||
this.view.setVisible(visible);
|
||||
}
|
||||
this.view.setVisible?.(visible);
|
||||
}
|
||||
|
||||
get minimumSize(): number { return this.visible ? this.view.minimumSize : 0; }
|
||||
|
|
|
@ -70,9 +70,7 @@ class TreeNodeListDragAndDrop<T, TFilterData, TRef> implements IListDragAndDrop<
|
|||
}
|
||||
|
||||
onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void {
|
||||
if (this.dnd.onDragStart) {
|
||||
this.dnd.onDragStart(asTreeDragAndDropData(data), originalEvent);
|
||||
}
|
||||
this.dnd.onDragStart?.(asTreeDragAndDropData(data), originalEvent);
|
||||
}
|
||||
|
||||
onDragOver(data: IDragAndDropData, targetNode: ITreeNode<T, TFilterData> | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction {
|
||||
|
@ -137,9 +135,7 @@ class TreeNodeListDragAndDrop<T, TFilterData, TRef> implements IListDragAndDrop<
|
|||
}
|
||||
|
||||
onDragEnd(originalEvent: DragEvent): void {
|
||||
if (this.dnd.onDragEnd) {
|
||||
this.dnd.onDragEnd(originalEvent);
|
||||
}
|
||||
this.dnd.onDragEnd?.(originalEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,9 +216,7 @@ export class ComposedTreeDelegate<T, N extends { element: T }> implements IListV
|
|||
}
|
||||
|
||||
setDynamicHeight(element: N, height: number): void {
|
||||
if (this.delegate.setDynamicHeight) {
|
||||
this.delegate.setDynamicHeight(element.element, height);
|
||||
}
|
||||
this.delegate.setDynamicHeight?.(element.element, height);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,9 +344,7 @@ class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListRenderer
|
|||
|
||||
Event.map(onDidChangeCollapseState, e => e.node)(this.onDidChangeNodeTwistieState, this, this.disposables);
|
||||
|
||||
if (renderer.onDidChangeTwistieState) {
|
||||
renderer.onDidChangeTwistieState(this.onDidChangeTwistieState, this, this.disposables);
|
||||
}
|
||||
renderer.onDidChangeTwistieState?.(this.onDidChangeTwistieState, this, this.disposables);
|
||||
}
|
||||
|
||||
updateOptions(options: ITreeRendererOptions = {}): void {
|
||||
|
@ -414,9 +406,7 @@ class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListRenderer
|
|||
disposeElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, height: number | undefined): void {
|
||||
templateData.indentGuidesDisposable.dispose();
|
||||
|
||||
if (this.renderer.disposeElement) {
|
||||
this.renderer.disposeElement(node, index, templateData.templateData, height);
|
||||
}
|
||||
this.renderer.disposeElement?.(node, index, templateData.templateData, height);
|
||||
|
||||
if (typeof height === 'number') {
|
||||
this.renderedNodes.delete(node);
|
||||
|
|
|
@ -119,9 +119,7 @@ class AsyncDataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements IT
|
|||
}
|
||||
|
||||
disposeElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
|
||||
if (this.renderer.disposeElement) {
|
||||
this.renderer.disposeElement(this.nodeMapper.map(node) as ITreeNode<T, TFilterData>, index, templateData.templateData, height);
|
||||
}
|
||||
this.renderer.disposeElement?.(this.nodeMapper.map(node) as ITreeNode<T, TFilterData>, index, templateData.templateData, height);
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IDataTreeListTemplateData<TTemplateData>): void {
|
||||
|
@ -196,9 +194,7 @@ class AsyncDataTreeNodeListDragAndDrop<TInput, T> implements IListDragAndDrop<IA
|
|||
}
|
||||
|
||||
onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void {
|
||||
if (this.dnd.onDragStart) {
|
||||
this.dnd.onDragStart(asAsyncDataTreeDragAndDropData(data), originalEvent);
|
||||
}
|
||||
this.dnd.onDragStart?.(asAsyncDataTreeDragAndDropData(data), originalEvent);
|
||||
}
|
||||
|
||||
onDragOver(data: IDragAndDropData, targetNode: IAsyncDataTreeNode<TInput, T> | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction {
|
||||
|
@ -210,9 +206,7 @@ class AsyncDataTreeNodeListDragAndDrop<TInput, T> implements IListDragAndDrop<IA
|
|||
}
|
||||
|
||||
onDragEnd(originalEvent: DragEvent): void {
|
||||
if (this.dnd.onDragEnd) {
|
||||
this.dnd.onDragEnd(originalEvent);
|
||||
}
|
||||
this.dnd.onDragEnd?.(originalEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1086,15 +1080,11 @@ class CompressibleAsyncDataTreeRenderer<TInput, T, TFilterData, TTemplateData> i
|
|||
}
|
||||
|
||||
disposeElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
|
||||
if (this.renderer.disposeElement) {
|
||||
this.renderer.disposeElement(this.nodeMapper.map(node) as ITreeNode<T, TFilterData>, index, templateData.templateData, height);
|
||||
}
|
||||
this.renderer.disposeElement?.(this.nodeMapper.map(node) as ITreeNode<T, TFilterData>, index, templateData.templateData, height);
|
||||
}
|
||||
|
||||
disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<IAsyncDataTreeNode<TInput, T>>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
|
||||
if (this.renderer.disposeCompressedElements) {
|
||||
this.renderer.disposeCompressedElements(this.compressibleNodeMapperProvider().map(node) as ITreeNode<ICompressedTreeNode<T>, TFilterData>, index, templateData.templateData, height);
|
||||
}
|
||||
this.renderer.disposeCompressedElements?.(this.compressibleNodeMapperProvider().map(node) as ITreeNode<ICompressedTreeNode<T>, TFilterData>, index, templateData.templateData, height);
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IDataTreeListTemplateData<TTemplateData>): void {
|
||||
|
|
|
@ -140,9 +140,7 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
|
|||
insertedElements.add(id);
|
||||
this.nodesByIdentity.set(id, node);
|
||||
|
||||
if (outerOnDidCreateNode) {
|
||||
outerOnDidCreateNode(node);
|
||||
}
|
||||
outerOnDidCreateNode?.(node);
|
||||
};
|
||||
|
||||
onDidDeleteNode = (node: ITreeNode<T, TFilterData>) => {
|
||||
|
|
|
@ -554,9 +554,7 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
|
|||
node.renderNodeCount = renderNodeCount;
|
||||
}
|
||||
|
||||
if (onDidCreateNode) {
|
||||
onDidCreateNode(node);
|
||||
}
|
||||
onDidCreateNode?.(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
|
|
@ -140,13 +140,9 @@ class CompressibleRenderer<T extends NonNullable<any>, TFilterData, TTemplateDat
|
|||
|
||||
disposeElement(node: ITreeNode<T, TFilterData>, index: number, templateData: CompressibleTemplateData<T, TFilterData, TTemplateData>, height: number | undefined): void {
|
||||
if (templateData.compressedTreeNode) {
|
||||
if (this.renderer.disposeCompressedElements) {
|
||||
this.renderer.disposeCompressedElements(templateData.compressedTreeNode, index, templateData.data, height);
|
||||
}
|
||||
this.renderer.disposeCompressedElements?.(templateData.compressedTreeNode, index, templateData.data, height);
|
||||
} else {
|
||||
if (this.renderer.disposeElement) {
|
||||
this.renderer.disposeElement(node, index, templateData.data, height);
|
||||
}
|
||||
this.renderer.disposeElement?.(node, index, templateData.data, height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -354,9 +354,7 @@ export class Delayer<T> implements IDisposable {
|
|||
this.cancelTimeout();
|
||||
|
||||
if (this.completionPromise) {
|
||||
if (this.doReject) {
|
||||
this.doReject(new CancellationError());
|
||||
}
|
||||
this.doReject?.(new CancellationError());
|
||||
this.completionPromise = null;
|
||||
}
|
||||
}
|
||||
|
@ -885,9 +883,7 @@ export class RunOnceScheduler {
|
|||
}
|
||||
|
||||
protected doRun(): void {
|
||||
if (this.runner) {
|
||||
this.runner();
|
||||
}
|
||||
this.runner?.();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -960,9 +956,7 @@ export class ProcessTimeRunOnceScheduler {
|
|||
// time elapsed
|
||||
clearInterval(this.intervalToken);
|
||||
this.intervalToken = -1;
|
||||
if (this.runner) {
|
||||
this.runner();
|
||||
}
|
||||
this.runner?.();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -985,9 +979,7 @@ export class RunOnceWorker<T> extends RunOnceScheduler {
|
|||
const units = this.units;
|
||||
this.units = [];
|
||||
|
||||
if (this.runner) {
|
||||
this.runner(units);
|
||||
}
|
||||
this.runner?.(units);
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
|
|
|
@ -95,6 +95,7 @@ export interface SerializedError {
|
|||
readonly name: string;
|
||||
readonly message: string;
|
||||
readonly stack: string;
|
||||
readonly noTelemetry: boolean;
|
||||
}
|
||||
|
||||
export function transformErrorForSerialization(error: Error): SerializedError;
|
||||
|
@ -107,7 +108,8 @@ export function transformErrorForSerialization(error: any): any {
|
|||
$isError: true,
|
||||
name,
|
||||
message,
|
||||
stack
|
||||
stack,
|
||||
noTelemetry: error instanceof ErrorNoTelemetry
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -690,9 +690,7 @@ export class Emitter<T> {
|
|||
}
|
||||
|
||||
const result = listener.subscription.set(() => {
|
||||
if (removeMonitor) {
|
||||
removeMonitor();
|
||||
}
|
||||
removeMonitor?.();
|
||||
if (!this._disposed) {
|
||||
removeListener();
|
||||
if (this._options && this._options.onLastListenerRemove) {
|
||||
|
|
|
@ -283,9 +283,7 @@ export class SimpleWorkerClient<W extends object, H extends object> extends Disp
|
|||
(err: any) => {
|
||||
// in Firefox, web workers fail lazily :(
|
||||
// we will reject the proxy
|
||||
if (lazyProxyReject) {
|
||||
lazyProxyReject(err);
|
||||
}
|
||||
lazyProxyReject?.(err);
|
||||
}
|
||||
));
|
||||
|
||||
|
|
|
@ -15,9 +15,7 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions, onHide
|
|||
const onClickChannel = `vscode:onContextMenu${contextMenuId}`;
|
||||
const onClickChannelHandler = (event: unknown, itemId: number, context: IContextMenuEvent) => {
|
||||
const item = processedItems[itemId];
|
||||
if (item.click) {
|
||||
item.click(context);
|
||||
}
|
||||
item.click?.(context);
|
||||
};
|
||||
|
||||
ipcRenderer.once(onClickChannel, onClickChannelHandler);
|
||||
|
@ -28,9 +26,7 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions, onHide
|
|||
|
||||
ipcRenderer.removeListener(onClickChannel, onClickChannelHandler);
|
||||
|
||||
if (onHide) {
|
||||
onHide();
|
||||
}
|
||||
onHide?.();
|
||||
});
|
||||
|
||||
ipcRenderer.send(CONTEXT_MENU_CHANNEL, contextMenuId, items.map(item => createItem(item, processedItems)), onClickChannel, options);
|
||||
|
|
|
@ -707,9 +707,7 @@ export class ChannelClient implements IChannelClient, IDisposable {
|
|||
|
||||
const handler = this.handlers.get(response.id);
|
||||
|
||||
if (handler) {
|
||||
handler(response);
|
||||
}
|
||||
handler?.(response);
|
||||
}
|
||||
|
||||
@memoize
|
||||
|
|
|
@ -26,9 +26,7 @@ export class Server<TContext extends string> extends IPCServer<TContext> {
|
|||
super({
|
||||
send: r => {
|
||||
try {
|
||||
if (process.send) {
|
||||
process.send((<Buffer>r.buffer).toString('base64'));
|
||||
}
|
||||
process.send?.((<Buffer>r.buffer).toString('base64'));
|
||||
} catch (e) { /* not much to do */ }
|
||||
},
|
||||
onMessage: Event.fromNodeEventEmitter(process, 'message', msg => VSBuffer.wrap(Buffer.from(msg, 'base64')))
|
||||
|
|
|
@ -1406,9 +1406,7 @@ export class QuickInputController extends Disposable {
|
|||
return new Promise<R>((doResolve, reject) => {
|
||||
let resolve = (result: R) => {
|
||||
resolve = doResolve;
|
||||
if (options.onKeyMods) {
|
||||
options.onKeyMods(input.keyMods);
|
||||
}
|
||||
options.onKeyMods?.(input.keyMods);
|
||||
doResolve(result);
|
||||
};
|
||||
if (token.isCancellationRequested) {
|
||||
|
|
|
@ -440,14 +440,10 @@ class SQLiteStorageDatabaseLogger {
|
|||
}
|
||||
|
||||
trace(msg: string): void {
|
||||
if (this.logTrace) {
|
||||
this.logTrace(msg);
|
||||
}
|
||||
this.logTrace?.(msg);
|
||||
}
|
||||
|
||||
error(error: string | Error): void {
|
||||
if (this.logError) {
|
||||
this.logError(error);
|
||||
}
|
||||
this.logError?.(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,13 +47,9 @@ export function endTrackingDisposables(): void {
|
|||
}
|
||||
|
||||
export function beginLoggingFS(withStacks: boolean = false): void {
|
||||
if ((<any>self).beginLoggingFS) {
|
||||
(<any>self).beginLoggingFS(withStacks);
|
||||
}
|
||||
(<any>self).beginLoggingFS?.(withStacks);
|
||||
}
|
||||
|
||||
export function endLoggingFS(): void {
|
||||
if ((<any>self).endLoggingFS) {
|
||||
(<any>self).endLoggingFS();
|
||||
}
|
||||
(<any>self).endLoggingFS?.();
|
||||
}
|
||||
|
|
|
@ -33,69 +33,47 @@ export class ViewUserInputEvents {
|
|||
}
|
||||
|
||||
public emitKeyDown(e: IKeyboardEvent): void {
|
||||
if (this.onKeyDown) {
|
||||
this.onKeyDown(e);
|
||||
}
|
||||
this.onKeyDown?.(e);
|
||||
}
|
||||
|
||||
public emitKeyUp(e: IKeyboardEvent): void {
|
||||
if (this.onKeyUp) {
|
||||
this.onKeyUp(e);
|
||||
}
|
||||
this.onKeyUp?.(e);
|
||||
}
|
||||
|
||||
public emitContextMenu(e: IEditorMouseEvent): void {
|
||||
if (this.onContextMenu) {
|
||||
this.onContextMenu(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
this.onContextMenu?.(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
|
||||
public emitMouseMove(e: IEditorMouseEvent): void {
|
||||
if (this.onMouseMove) {
|
||||
this.onMouseMove(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
this.onMouseMove?.(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
|
||||
public emitMouseLeave(e: IPartialEditorMouseEvent): void {
|
||||
if (this.onMouseLeave) {
|
||||
this.onMouseLeave(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
this.onMouseLeave?.(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
|
||||
public emitMouseDown(e: IEditorMouseEvent): void {
|
||||
if (this.onMouseDown) {
|
||||
this.onMouseDown(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
this.onMouseDown?.(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
|
||||
public emitMouseUp(e: IEditorMouseEvent): void {
|
||||
if (this.onMouseUp) {
|
||||
this.onMouseUp(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
this.onMouseUp?.(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
|
||||
public emitMouseDrag(e: IEditorMouseEvent): void {
|
||||
if (this.onMouseDrag) {
|
||||
this.onMouseDrag(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
this.onMouseDrag?.(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
|
||||
public emitMouseDrop(e: IPartialEditorMouseEvent): void {
|
||||
if (this.onMouseDrop) {
|
||||
this.onMouseDrop(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
this.onMouseDrop?.(this._convertViewToModelMouseEvent(e));
|
||||
}
|
||||
|
||||
public emitMouseDropCanceled(): void {
|
||||
if (this.onMouseDropCanceled) {
|
||||
this.onMouseDropCanceled();
|
||||
}
|
||||
this.onMouseDropCanceled?.();
|
||||
}
|
||||
|
||||
public emitMouseWheel(e: IMouseWheelEvent): void {
|
||||
if (this.onMouseWheel) {
|
||||
this.onMouseWheel(e);
|
||||
}
|
||||
this.onMouseWheel?.(e);
|
||||
}
|
||||
|
||||
private _convertViewToModelMouseEvent(e: IEditorMouseEvent): IEditorMouseEvent;
|
||||
|
|
|
@ -2136,9 +2136,7 @@ class CodeEditorWidgetFocusTracker extends Disposable {
|
|||
}
|
||||
|
||||
public refreshState(): void {
|
||||
if (this._domFocusTracker.refreshState) {
|
||||
this._domFocusTracker.refreshState();
|
||||
}
|
||||
this._domFocusTracker.refreshState?.();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat
|
|||
|
||||
export const codeActionCommandId = 'editor.action.codeAction';
|
||||
export const refactorCommandId = 'editor.action.refactor';
|
||||
export const refactorPreviewCommandId = 'editor.action.refactor.preview';
|
||||
export const sourceActionCommandId = 'editor.action.sourceAction';
|
||||
export const organizeImportsCommandId = 'editor.action.organizeImports';
|
||||
export const fixAllCommandId = 'editor.action.fixAll';
|
||||
|
|
|
@ -18,7 +18,7 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
|||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { CodeActionTriggerType } from 'vs/editor/common/languages';
|
||||
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
||||
import { codeActionCommandId, CodeActionItem, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction';
|
||||
import { codeActionCommandId, CodeActionItem, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, refactorPreviewCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction';
|
||||
import { CodeActionUi } from 'vs/editor/contrib/codeAction/browser/codeActionUi';
|
||||
import { MessageController } from 'vs/editor/contrib/message/browser/messageController';
|
||||
import * as nls from 'vs/nls';
|
||||
|
@ -27,8 +27,8 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo
|
|||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel';
|
||||
import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './types';
|
||||
|
@ -39,6 +39,26 @@ function contextKeyForSupportedActions(kind: CodeActionKind) {
|
|||
new RegExp('(\\s|^)' + escapeRegExpCharacters(kind.value) + '\\b'));
|
||||
}
|
||||
|
||||
function RefactorTrigger(editor: ICodeEditor, userArgs: any, preview: boolean) {
|
||||
const args = CodeActionCommandArgs.fromUser(userArgs, {
|
||||
kind: CodeActionKind.Refactor,
|
||||
apply: CodeActionAutoApply.Never
|
||||
});
|
||||
return triggerCodeActionsForEditorSelection(editor,
|
||||
typeof userArgs?.kind === 'string'
|
||||
? args.preferred
|
||||
? nls.localize('editor.action.refactor.noneMessage.preferred.kind', "No preferred refactorings for '{0}' available", userArgs.kind)
|
||||
: nls.localize('editor.action.refactor.noneMessage.kind', "No refactorings for '{0}' available", userArgs.kind)
|
||||
: args.preferred
|
||||
? nls.localize('editor.action.refactor.noneMessage.preferred', "No preferred refactorings available")
|
||||
: nls.localize('editor.action.refactor.noneMessage', "No refactorings available"),
|
||||
{
|
||||
include: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.None,
|
||||
onlyIncludePreferredActions: args.preferred
|
||||
},
|
||||
args.apply, preview);
|
||||
}
|
||||
|
||||
const argsSchema: IJSONSchema = {
|
||||
type: 'object',
|
||||
defaultSnippets: [{ body: { kind: '' } }],
|
||||
|
@ -92,11 +112,12 @@ export class QuickFixController extends Disposable implements IEditorContributio
|
|||
this._model = this._register(new CodeActionModel(this._editor, languageFeaturesService.codeActionProvider, markerService, contextKeyService, progressService));
|
||||
this._register(this._model.onDidChangeState(newState => this.update(newState)));
|
||||
|
||||
|
||||
this._ui = new Lazy(() =>
|
||||
this._register(new CodeActionUi(editor, QuickFixAction.Id, AutoFixAction.Id, {
|
||||
applyCodeAction: async (action, retrigger) => {
|
||||
applyCodeAction: async (action, retrigger, preview) => {
|
||||
try {
|
||||
await this._applyCodeAction(action);
|
||||
await this._applyCodeAction(action, preview);
|
||||
} finally {
|
||||
if (retrigger) {
|
||||
this._trigger({ type: CodeActionTriggerType.Auto, filter: {} });
|
||||
|
@ -118,7 +139,8 @@ export class QuickFixController extends Disposable implements IEditorContributio
|
|||
public manualTriggerAtCurrentPosition(
|
||||
notAvailableMessage: string,
|
||||
filter?: CodeActionFilter,
|
||||
autoApply?: CodeActionAutoApply
|
||||
autoApply?: CodeActionAutoApply,
|
||||
preview?: boolean
|
||||
): void {
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
|
@ -126,22 +148,22 @@ export class QuickFixController extends Disposable implements IEditorContributio
|
|||
|
||||
MessageController.get(this._editor)?.closeMessage();
|
||||
const triggerPosition = this._editor.getPosition();
|
||||
this._trigger({ type: CodeActionTriggerType.Invoke, filter, autoApply, context: { notAvailableMessage, position: triggerPosition } });
|
||||
this._trigger({ type: CodeActionTriggerType.Invoke, filter, autoApply, context: { notAvailableMessage, position: triggerPosition }, preview });
|
||||
}
|
||||
|
||||
private _trigger(trigger: CodeActionTrigger) {
|
||||
return this._model.trigger(trigger);
|
||||
}
|
||||
|
||||
private _applyCodeAction(action: CodeActionItem): Promise<void> {
|
||||
return this._instantiationService.invokeFunction(applyCodeAction, action, this._editor);
|
||||
private _applyCodeAction(action: CodeActionItem, preview: boolean): Promise<void> {
|
||||
return this._instantiationService.invokeFunction(applyCodeAction, action, { preview, editor: this._editor });
|
||||
}
|
||||
}
|
||||
|
||||
export async function applyCodeAction(
|
||||
accessor: ServicesAccessor,
|
||||
item: CodeActionItem,
|
||||
editor?: ICodeEditor,
|
||||
options?: { preview?: boolean; editor?: ICodeEditor }
|
||||
): Promise<void> {
|
||||
const bulkEditService = accessor.get(IBulkEditService);
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
@ -171,11 +193,12 @@ export async function applyCodeAction(
|
|||
|
||||
if (item.action.edit) {
|
||||
await bulkEditService.apply(ResourceEdit.convert(item.action.edit), {
|
||||
editor,
|
||||
editor: options?.editor,
|
||||
label: item.action.title,
|
||||
quotableLabel: item.action.title,
|
||||
code: 'undoredo.codeAction',
|
||||
respectAutoSaveConfig: true
|
||||
respectAutoSaveConfig: true,
|
||||
showPreview: options?.preview,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -206,12 +229,13 @@ function triggerCodeActionsForEditorSelection(
|
|||
editor: ICodeEditor,
|
||||
notAvailableMessage: string,
|
||||
filter: CodeActionFilter | undefined,
|
||||
autoApply: CodeActionAutoApply | undefined
|
||||
autoApply: CodeActionAutoApply | undefined,
|
||||
preview: boolean = false
|
||||
): void {
|
||||
if (editor.hasModel()) {
|
||||
const controller = QuickFixController.get(editor);
|
||||
if (controller) {
|
||||
controller.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply);
|
||||
controller.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply, preview);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -306,23 +330,27 @@ export class RefactorAction extends EditorAction {
|
|||
}
|
||||
|
||||
public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArgs: any): void {
|
||||
const args = CodeActionCommandArgs.fromUser(userArgs, {
|
||||
kind: CodeActionKind.Refactor,
|
||||
apply: CodeActionAutoApply.Never
|
||||
return RefactorTrigger(editor, userArgs, false);
|
||||
}
|
||||
}
|
||||
|
||||
export class RefactorPreview extends EditorAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: refactorPreviewCommandId,
|
||||
label: nls.localize('refactor.preview.label', "Refactor with Preview..."),
|
||||
alias: 'Refactor Preview...',
|
||||
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
|
||||
description: {
|
||||
description: 'Refactor Preview...',
|
||||
args: [{ name: 'args', schema: argsSchema }]
|
||||
}
|
||||
});
|
||||
return triggerCodeActionsForEditorSelection(editor,
|
||||
typeof userArgs?.kind === 'string'
|
||||
? args.preferred
|
||||
? nls.localize('editor.action.refactor.noneMessage.preferred.kind', "No preferred refactorings for '{0}' available", userArgs.kind)
|
||||
: nls.localize('editor.action.refactor.noneMessage.kind', "No refactorings for '{0}' available", userArgs.kind)
|
||||
: args.preferred
|
||||
? nls.localize('editor.action.refactor.noneMessage.preferred', "No preferred refactorings available")
|
||||
: nls.localize('editor.action.refactor.noneMessage', "No refactorings available"),
|
||||
{
|
||||
include: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.None,
|
||||
onlyIncludePreferredActions: args.preferred,
|
||||
},
|
||||
args.apply);
|
||||
}
|
||||
|
||||
public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArgs: any): void {
|
||||
return RefactorTrigger(editor, userArgs, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { AutoFixAction, CodeActionCommand, FixAllAction, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, SourceAction } from 'vs/editor/contrib/codeAction/browser/codeActionCommands';
|
||||
import { AutoFixAction, CodeActionCommand, FixAllAction, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, RefactorPreview, SourceAction } from 'vs/editor/contrib/codeAction/browser/codeActionCommands';
|
||||
|
||||
|
||||
registerEditorContribution(QuickFixController.ID, QuickFixController);
|
||||
registerEditorAction(QuickFixAction);
|
||||
registerEditorAction(RefactorAction);
|
||||
registerEditorAction(RefactorPreview);
|
||||
registerEditorAction(SourceAction);
|
||||
registerEditorAction(OrganizeImportsAction);
|
||||
registerEditorAction(AutoFixAction);
|
||||
|
|
|
@ -23,7 +23,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
|||
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
|
||||
interface CodeActionWidgetDelegate {
|
||||
onSelectCodeAction: (action: CodeActionItem) => Promise<any>;
|
||||
onSelectCodeAction: (action: CodeActionItem, trigger: CodeActionTrigger) => Promise<any>;
|
||||
}
|
||||
|
||||
interface ResolveCodeActionKeybinding {
|
||||
|
@ -115,7 +115,7 @@ export class CodeActionMenu extends Disposable {
|
|||
actionsToShow: readonly CodeActionItem[],
|
||||
documentation: readonly Command[]
|
||||
): IAction[] {
|
||||
const toCodeActionAction = (item: CodeActionItem): CodeActionAction => new CodeActionAction(item.action, () => this._delegate.onSelectCodeAction(item));
|
||||
const toCodeActionAction = (item: CodeActionItem): CodeActionAction => new CodeActionAction(item.action, () => this._delegate.onSelectCodeAction(item, trigger));
|
||||
|
||||
const result: IAction[] = actionsToShow
|
||||
.map(toCodeActionAction);
|
||||
|
|
|
@ -31,7 +31,7 @@ export class CodeActionUi extends Disposable {
|
|||
quickFixActionId: string,
|
||||
preferredFixActionId: string,
|
||||
private readonly delegate: {
|
||||
applyCodeAction: (action: CodeActionItem, regtriggerAfterApply: boolean) => Promise<void>;
|
||||
applyCodeAction: (action: CodeActionItem, regtriggerAfterApply: boolean, preview: boolean) => Promise<void>;
|
||||
},
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
) {
|
||||
|
@ -39,8 +39,8 @@ export class CodeActionUi extends Disposable {
|
|||
|
||||
this._codeActionWidget = new Lazy(() => {
|
||||
return this._register(instantiationService.createInstance(CodeActionMenu, this._editor, {
|
||||
onSelectCodeAction: async (action) => {
|
||||
this.delegate.applyCodeAction(action, /* retrigger */ true);
|
||||
onSelectCodeAction: async (action, trigger) => {
|
||||
this.delegate.applyCodeAction(action, /* retrigger */ true, Boolean(trigger.preview));
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
@ -85,7 +85,7 @@ export class CodeActionUi extends Disposable {
|
|||
if (validActionToApply) {
|
||||
try {
|
||||
this._lightBulbWidget.getValue().hide();
|
||||
await this.delegate.applyCodeAction(validActionToApply, false);
|
||||
await this.delegate.applyCodeAction(validActionToApply, false, false);
|
||||
} finally {
|
||||
actions.dispose();
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@ export interface CodeActionTrigger {
|
|||
readonly notAvailableMessage: string;
|
||||
readonly position: Position;
|
||||
};
|
||||
readonly preview?: boolean;
|
||||
}
|
||||
|
||||
export class CodeActionCommandArgs {
|
||||
|
|
|
@ -53,7 +53,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
|
|||
|
||||
private readonly _editor: ICodeEditor;
|
||||
|
||||
private _currentClipboardItem: undefined | {
|
||||
private _currentClipboardItem?: {
|
||||
readonly handle: string;
|
||||
readonly dataTransferPromise: CancelablePromise<VSDataTransfer>;
|
||||
};
|
||||
|
@ -70,131 +70,134 @@ export class CopyPasteController extends Disposable implements IEditorContributi
|
|||
this._editor = editor;
|
||||
|
||||
const container = editor.getContainerDomNode();
|
||||
|
||||
this._register(addDisposableListener(container, 'copy', (e: ClipboardEvent) => {
|
||||
if (!e.clipboardData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = editor.getModel();
|
||||
const selections = this._editor.getSelections();
|
||||
if (!model || !selections?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.arePasteActionsEnabled(model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model).filter(x => !!x.prepareDocumentPaste);
|
||||
if (!providers.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataTransfer = toVSDataTransfer(e.clipboardData);
|
||||
|
||||
// Save off a handle pointing to data that VS Code maintains.
|
||||
const handle = generateUuid();
|
||||
e.clipboardData.setData(vscodeClipboardMime, handle);
|
||||
|
||||
const promise = createCancelablePromise(async token => {
|
||||
const results = await Promise.all(providers.map(provider => {
|
||||
return provider.prepareDocumentPaste!(model, selections, dataTransfer, token);
|
||||
}));
|
||||
|
||||
for (const result of results) {
|
||||
result?.forEach((value, key) => {
|
||||
dataTransfer.replace(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
return dataTransfer;
|
||||
});
|
||||
|
||||
this._currentClipboardItem?.dataTransferPromise.cancel();
|
||||
this._currentClipboardItem = { handle: handle, dataTransferPromise: promise };
|
||||
}));
|
||||
|
||||
this._register(addDisposableListener(container, 'paste', async (e: ClipboardEvent) => {
|
||||
const selections = this._editor.getSelections();
|
||||
if (!e.clipboardData || !selections?.length || !editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = editor.getModel();
|
||||
if (!this.arePasteActionsEnabled(model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handle = e.clipboardData?.getData(vscodeClipboardMime);
|
||||
if (typeof handle !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model);
|
||||
if (!providers.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
const originalDocVersion = model.getVersionId();
|
||||
const tokenSource = new EditorStateCancellationTokenSource(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection);
|
||||
|
||||
try {
|
||||
const dataTransfer = toVSDataTransfer(e.clipboardData);
|
||||
|
||||
if (handle && this._currentClipboardItem?.handle === handle) {
|
||||
const toMergeDataTransfer = await this._currentClipboardItem.dataTransferPromise;
|
||||
toMergeDataTransfer.forEach((value, key) => {
|
||||
dataTransfer.replace(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
if (!dataTransfer.has(Mimes.uriList)) {
|
||||
const resources = await this._clipboardService.readResources();
|
||||
if (resources.length) {
|
||||
const value = resources.join('\n');
|
||||
dataTransfer.append(Mimes.uriList, createStringDataTransferItem(value));
|
||||
}
|
||||
}
|
||||
|
||||
dataTransfer.delete(vscodeClipboardMime);
|
||||
|
||||
for (const provider of [...providers, defaultPasteEditProvider]) {
|
||||
if (!provider.pasteMimeTypes.some(type => {
|
||||
if (type.toLowerCase() === DataTransfers.FILES.toLowerCase()) {
|
||||
return [...dataTransfer.values()].some(item => item.asFile());
|
||||
}
|
||||
return dataTransfer.has(type);
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const edit = await provider.provideDocumentPasteEdits(model, selections, dataTransfer, tokenSource.token);
|
||||
if (originalDocVersion !== model.getVersionId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (edit) {
|
||||
performSnippetEdit(editor, typeof edit.insertText === 'string' ? SnippetParser.escape(edit.insertText) : edit.insertText.snippet, selections);
|
||||
|
||||
if (edit.additionalEdit) {
|
||||
await this._bulkEditService.apply(ResourceEdit.convert(edit.additionalEdit), { editor });
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
tokenSource.dispose();
|
||||
}
|
||||
}, true));
|
||||
this._register(addDisposableListener(container, 'copy', e => this.handleCopy(e)));
|
||||
this._register(addDisposableListener(container, 'cut', e => this.handleCopy(e)));
|
||||
this._register(addDisposableListener(container, 'paste', e => this.handlePaste(e), true));
|
||||
}
|
||||
|
||||
public arePasteActionsEnabled(model: ITextModel): boolean {
|
||||
private arePasteActionsEnabled(model: ITextModel): boolean {
|
||||
return this._configurationService.getValue('editor.experimental.pasteActions.enabled', {
|
||||
resource: model.uri
|
||||
});
|
||||
}
|
||||
|
||||
private handleCopy(e: ClipboardEvent) {
|
||||
if (!e.clipboardData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this._editor.getModel();
|
||||
const selections = this._editor.getSelections();
|
||||
if (!model || !selections?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.arePasteActionsEnabled(model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model).filter(x => !!x.prepareDocumentPaste);
|
||||
if (!providers.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataTransfer = toVSDataTransfer(e.clipboardData);
|
||||
|
||||
// Save off a handle pointing to data that VS Code maintains.
|
||||
const handle = generateUuid();
|
||||
e.clipboardData.setData(vscodeClipboardMime, handle);
|
||||
|
||||
const promise = createCancelablePromise(async token => {
|
||||
const results = await Promise.all(providers.map(provider => {
|
||||
return provider.prepareDocumentPaste!(model, selections, dataTransfer, token);
|
||||
}));
|
||||
|
||||
for (const result of results) {
|
||||
result?.forEach((value, key) => {
|
||||
dataTransfer.replace(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
return dataTransfer;
|
||||
});
|
||||
|
||||
this._currentClipboardItem?.dataTransferPromise.cancel();
|
||||
this._currentClipboardItem = { handle: handle, dataTransferPromise: promise };
|
||||
}
|
||||
|
||||
private async handlePaste(e: ClipboardEvent) {
|
||||
const selections = this._editor.getSelections();
|
||||
if (!e.clipboardData || !selections?.length || !this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this._editor.getModel();
|
||||
if (!this.arePasteActionsEnabled(model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handle = e.clipboardData?.getData(vscodeClipboardMime);
|
||||
if (typeof handle !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model);
|
||||
if (!providers.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
const originalDocVersion = model.getVersionId();
|
||||
const tokenSource = new EditorStateCancellationTokenSource(this._editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection);
|
||||
|
||||
try {
|
||||
const dataTransfer = toVSDataTransfer(e.clipboardData);
|
||||
|
||||
if (handle && this._currentClipboardItem?.handle === handle) {
|
||||
const toMergeDataTransfer = await this._currentClipboardItem.dataTransferPromise;
|
||||
toMergeDataTransfer.forEach((value, key) => {
|
||||
dataTransfer.replace(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
if (!dataTransfer.has(Mimes.uriList)) {
|
||||
const resources = await this._clipboardService.readResources();
|
||||
if (resources.length) {
|
||||
const value = resources.join('\n');
|
||||
dataTransfer.append(Mimes.uriList, createStringDataTransferItem(value));
|
||||
}
|
||||
}
|
||||
|
||||
dataTransfer.delete(vscodeClipboardMime);
|
||||
|
||||
for (const provider of [...providers, defaultPasteEditProvider]) {
|
||||
if (!provider.pasteMimeTypes.some(type => {
|
||||
if (type.toLowerCase() === DataTransfers.FILES.toLowerCase()) {
|
||||
return [...dataTransfer.values()].some(item => item.asFile());
|
||||
}
|
||||
return dataTransfer.has(type);
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const edit = await provider.provideDocumentPasteEdits(model, selections, dataTransfer, tokenSource.token);
|
||||
if (originalDocVersion !== model.getVersionId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (edit) {
|
||||
performSnippetEdit(this._editor, typeof edit.insertText === 'string' ? SnippetParser.escape(edit.insertText) : edit.insertText.snippet, selections);
|
||||
|
||||
if (edit.additionalEdit) {
|
||||
await this._bulkEditService.apply(ResourceEdit.convert(edit.additionalEdit), { editor: this._editor });
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
tokenSource.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1348,9 +1348,7 @@ export class SimpleButton extends Widget {
|
|||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (this._opts.onKeyDown) {
|
||||
this._opts.onKeyDown(e);
|
||||
}
|
||||
this._opts.onKeyDown?.(e);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -74,9 +74,7 @@ export class RangesCollector {
|
|||
}
|
||||
return new FoldingRegions(startIndexes, endIndexes);
|
||||
} else {
|
||||
if (this._notifyTooManyRegions) {
|
||||
this._notifyTooManyRegions(this._foldingRangesLimit);
|
||||
}
|
||||
this._notifyTooManyRegions?.(this._foldingRangesLimit);
|
||||
let entries = 0;
|
||||
let maxIndent = this._indentOccurrences.length;
|
||||
for (let i = 0; i < this._indentOccurrences.length; i++) {
|
||||
|
|
|
@ -121,9 +121,7 @@ export class RangesCollector {
|
|||
}
|
||||
return new FoldingRegions(startIndexes, endIndexes, this._types);
|
||||
} else {
|
||||
if (this._notifyTooManyRegions) {
|
||||
this._notifyTooManyRegions(this._foldingRangesLimit);
|
||||
}
|
||||
this._notifyTooManyRegions?.(this._foldingRangesLimit);
|
||||
let entries = 0;
|
||||
let maxLevel = this._nestingLevelCounts.length;
|
||||
for (let i = 0; i < this._nestingLevelCounts.length; i++) {
|
||||
|
|
|
@ -243,9 +243,7 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel {
|
|||
lastCompletionItem = currentCompletion.sourceInlineCompletion;
|
||||
|
||||
const provider = currentCompletion.sourceProvider;
|
||||
if (provider.handleItemDidShow) {
|
||||
provider.handleItemDidShow(currentCompletion.sourceInlineCompletions, lastCompletionItem);
|
||||
}
|
||||
provider.handleItemDidShow?.(currentCompletion.sourceInlineCompletions, lastCompletionItem);
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
|
@ -146,15 +146,11 @@ export class RenameInputField implements IContentWidget {
|
|||
private _currentCancelInput?: (focusEditor: boolean) => void;
|
||||
|
||||
acceptInput(wantsPreview: boolean): void {
|
||||
if (this._currentAcceptInput) {
|
||||
this._currentAcceptInput(wantsPreview);
|
||||
}
|
||||
this._currentAcceptInput?.(wantsPreview);
|
||||
}
|
||||
|
||||
cancelInput(focusEditor: boolean): void {
|
||||
if (this._currentCancelInput) {
|
||||
this._currentCancelInput(focusEditor);
|
||||
}
|
||||
this._currentCancelInput?.(focusEditor);
|
||||
}
|
||||
|
||||
getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number, supportPreview: boolean, token: CancellationToken): Promise<RenameInputFieldResult | boolean> {
|
||||
|
|
|
@ -39,9 +39,7 @@ export class BannerController extends Disposable {
|
|||
...item,
|
||||
onClose: () => {
|
||||
this.hide();
|
||||
if (item.onClose) {
|
||||
item.onClose();
|
||||
}
|
||||
item.onClose?.();
|
||||
}
|
||||
});
|
||||
this._editor.setBanner(this.banner.element, BANNER_ELEMENT_HEIGHT);
|
||||
|
|
|
@ -129,9 +129,7 @@ export class ContextMenuHandler {
|
|||
},
|
||||
|
||||
onHide: (didCancel?: boolean) => {
|
||||
if (delegate.onHide) {
|
||||
delegate.onHide(!!didCancel);
|
||||
}
|
||||
delegate.onHide?.(!!didCancel);
|
||||
|
||||
if (this.block) {
|
||||
this.block.remove();
|
||||
|
|
|
@ -47,9 +47,7 @@ export class ContextMenuService extends Disposable implements IContextMenuServic
|
|||
this.contextMenuHandler.showContextMenu({
|
||||
...delegate,
|
||||
onHide: (didCancel) => {
|
||||
if (delegate.onHide) {
|
||||
delegate.onHide(didCancel);
|
||||
}
|
||||
delegate.onHide?.(didCancel);
|
||||
|
||||
this._onDidHideContextMenu.fire();
|
||||
}
|
||||
|
|
|
@ -816,9 +816,7 @@ export class Menubar {
|
|||
const originalClick = options.click;
|
||||
options.click = (item, window, event) => {
|
||||
this.reportMenuActionTelemetry(commandId);
|
||||
if (originalClick) {
|
||||
originalClick(item, window, event);
|
||||
}
|
||||
originalClick?.(item, window, event);
|
||||
};
|
||||
|
||||
return options;
|
||||
|
|
|
@ -71,9 +71,7 @@ export class RemoteAuthorityResolverService extends Disposable implements IRemot
|
|||
const key = uri.toString();
|
||||
if (!this._canonicalURIRequests.has(key)) {
|
||||
const request = new PendingPromise<URI, URI>(uri);
|
||||
if (this._canonicalURIProvider) {
|
||||
this._canonicalURIProvider(request.input).then((uri) => request.resolve(uri), (err) => request.reject(err));
|
||||
}
|
||||
this._canonicalURIProvider?.(request.input).then((uri) => request.resolve(uri), (err) => request.reject(err));
|
||||
this._canonicalURIRequests.set(key, request);
|
||||
}
|
||||
return this._canonicalURIRequests.get(key)!.promise;
|
||||
|
|
|
@ -183,9 +183,10 @@ async function transformToTerminalProfiles(
|
|||
validatedProfile.color = profile.color;
|
||||
resultProfiles.push(validatedProfile);
|
||||
} else {
|
||||
logService?.trace('profile not validated', profileName, originalPaths);
|
||||
logService?.debug('Terminal profile not validated', profileName, originalPaths);
|
||||
}
|
||||
}
|
||||
logService?.debug('Validated terminal profiles', resultProfiles);
|
||||
return resultProfiles;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,17 +74,18 @@ export class MainThreadCommands implements MainThreadCommandsShape {
|
|||
}
|
||||
}
|
||||
|
||||
async $executeCommand<T>(id: string, args: any[] | SerializableObjectWithBuffers<any[]>, retry: boolean): Promise<T | undefined> {
|
||||
async $activateByCommandEvent(id: string): Promise<void> {
|
||||
const activationEvent = `onCommand:${id}`;
|
||||
await this._extensionService.activateByEvent(activationEvent);
|
||||
}
|
||||
|
||||
async $executeCommand<T>(id: string, args: any[] | SerializableObjectWithBuffers<any[]>): Promise<T | undefined> {
|
||||
if (args instanceof SerializableObjectWithBuffers) {
|
||||
args = args.value;
|
||||
}
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
args[i] = revive(args[i]);
|
||||
}
|
||||
if (retry && args.length > 0 && !CommandsRegistry.getCommand(id)) {
|
||||
await this._extensionService.activateByEvent(`onCommand:${id}`);
|
||||
throw new Error('$executeCommand:retry');
|
||||
}
|
||||
return this._commandService.executeCommand<T>(id, ...args);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SerializedError, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { SerializedError, onUnexpectedError, ErrorNoTelemetry } from 'vs/base/common/errors';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers';
|
||||
import { MainContext, MainThreadErrorsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
|
@ -17,7 +17,7 @@ export class MainThreadErrors implements MainThreadErrorsShape {
|
|||
$onUnexpectedError(err: any | SerializedError): void {
|
||||
if (err && err.$isError) {
|
||||
const { name, message, stack } = err;
|
||||
err = new Error();
|
||||
err = err.noTelemetry ? new ErrorNoTelemetry() : new Error();
|
||||
err.message = message;
|
||||
err.name = name;
|
||||
err.stack = stack;
|
||||
|
|
|
@ -96,6 +96,7 @@ export namespace NotebookDto {
|
|||
if (data.editType === CellExecutionUpdateType.Output) {
|
||||
return {
|
||||
editType: data.editType,
|
||||
cellHandle: data.cellHandle,
|
||||
append: data.append,
|
||||
outputs: data.outputs.map(fromNotebookOutputDto)
|
||||
};
|
||||
|
|
|
@ -94,9 +94,7 @@ class SearchOperation {
|
|||
this.matches.set(match.resource.toString(), match);
|
||||
}
|
||||
|
||||
if (this.progress) {
|
||||
this.progress(match);
|
||||
}
|
||||
this.progress?.(match);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,8 @@ export interface MainThreadClipboardShape extends IDisposable {
|
|||
export interface MainThreadCommandsShape extends IDisposable {
|
||||
$registerCommand(id: string): void;
|
||||
$unregisterCommand(id: string): void;
|
||||
$executeCommand(id: string, args: any[] | SerializableObjectWithBuffers<any[]>, retry: boolean): Promise<unknown | undefined>;
|
||||
$activateByCommandEvent(id: string): Promise<void>;
|
||||
$executeCommand(id: string, args: any[] | SerializableObjectWithBuffers<any[]>): Promise<unknown | undefined>;
|
||||
$getCommands(): Promise<string[]>;
|
||||
}
|
||||
|
||||
|
@ -999,6 +1000,7 @@ export interface INotebookProxyKernelDto {
|
|||
|
||||
export interface ICellExecuteOutputEditDto {
|
||||
editType: CellExecutionUpdateType.Output;
|
||||
cellHandle: number;
|
||||
append?: boolean;
|
||||
outputs: NotebookOutputDto[];
|
||||
}
|
||||
|
|
|
@ -163,10 +163,15 @@ export class ExtHostCommands implements ExtHostCommandsShape {
|
|||
|
||||
executeCommand<T>(id: string, ...args: any[]): Promise<T> {
|
||||
this._logService.trace('ExtHostCommands#executeCommand', id);
|
||||
return this._doExecuteCommand(id, args, true);
|
||||
return this._doExecuteCommand(id, args);
|
||||
}
|
||||
|
||||
private async _doExecuteCommand<T>(id: string, args: any[], retry: boolean): Promise<T> {
|
||||
private async _doExecuteCommand<T>(id: string, args: any[]): Promise<T> {
|
||||
|
||||
// make sure to emit an onCommand-activation event in ALL cases
|
||||
// (1) locally known command -> activation notificed bystander
|
||||
// (2) unknown command -> can activate future local extension
|
||||
await this.#proxy.$activateByCommandEvent(id);
|
||||
|
||||
if (this._commands.has(id)) {
|
||||
// we stay inside the extension host and support
|
||||
|
@ -201,17 +206,10 @@ export class ExtHostCommands implements ExtHostCommandsShape {
|
|||
});
|
||||
|
||||
try {
|
||||
const result = await this.#proxy.$executeCommand(id, hasBuffers ? new SerializableObjectWithBuffers(toArgs) : toArgs, retry);
|
||||
const result = await this.#proxy.$executeCommand(id, hasBuffers ? new SerializableObjectWithBuffers(toArgs) : toArgs);
|
||||
return revive<any>(result);
|
||||
} catch (e) {
|
||||
// Rerun the command when it wasn't known, had arguments, and when retry
|
||||
// is enabled. We do this because the command might be registered inside
|
||||
// the extension host now and can therfore accept the arguments as-is.
|
||||
if (e instanceof Error && e.message === '$executeCommand:retry') {
|
||||
return this._doExecuteCommand(id, args, false);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -441,6 +441,17 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
private cellIndexToHandle(cellOrCellIndex: vscode.NotebookCell | undefined): number {
|
||||
let cell: ExtHostCell | undefined = this._cell;
|
||||
if (cellOrCellIndex) {
|
||||
cell = this._cell.notebook.getCellFromApiCell(cellOrCellIndex);
|
||||
}
|
||||
if (!cell) {
|
||||
throw new Error('INVALID cell');
|
||||
}
|
||||
return cell.handle;
|
||||
}
|
||||
|
||||
private validateAndConvertOutputs(items: vscode.NotebookCellOutput[]): NotebookOutputDto[] {
|
||||
return items.map(output => {
|
||||
const newOutput = NotebookCellOutput.ensureUniqueMimeTypes(output.items, true);
|
||||
|
@ -456,10 +467,12 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
}
|
||||
|
||||
private async updateOutputs(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell: vscode.NotebookCell | undefined, append: boolean): Promise<void> {
|
||||
const handle = this.cellIndexToHandle(cell);
|
||||
const outputDtos = this.validateAndConvertOutputs(asArray(outputs));
|
||||
return this.updateSoon(
|
||||
{
|
||||
editType: CellExecutionUpdateType.Output,
|
||||
cellHandle: handle,
|
||||
append,
|
||||
outputs: outputDtos
|
||||
});
|
||||
|
|
|
@ -136,9 +136,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
|||
}
|
||||
|
||||
$onItemSelected(handle: number): void {
|
||||
if (this._onDidSelectItem) {
|
||||
this._onDidSelectItem(handle);
|
||||
}
|
||||
this._onDidSelectItem?.(handle);
|
||||
}
|
||||
|
||||
// ---- input
|
||||
|
|
|
@ -2473,9 +2473,9 @@ export class DataTransfer {
|
|||
this.#items.set(mimeType, [value]);
|
||||
}
|
||||
|
||||
forEach(callbackfn: (value: DataTransferItem, key: string) => void): void {
|
||||
forEach(callbackfn: (value: DataTransferItem, key: string) => void, thisArg?: unknown): void {
|
||||
for (const [mime, items] of this.#items) {
|
||||
items.forEach(item => callbackfn(item, mime));
|
||||
items.forEach(item => callbackfn(item, mime), thisArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -546,9 +546,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
|||
}
|
||||
|
||||
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void {
|
||||
if (this._activeSearchCallbacks[requestId]) {
|
||||
this._activeSearchCallbacks[requestId](result);
|
||||
}
|
||||
this._activeSearchCallbacks[requestId]?.(result);
|
||||
}
|
||||
|
||||
saveAll(includeUntitled?: boolean): Promise<boolean> {
|
||||
|
|
|
@ -197,9 +197,7 @@ function _createExtHostProtocol(): Promise<IMessagePassingProtocol> {
|
|||
|
||||
// Now that we have managed to install a message listener, ask the other side to send us the socket
|
||||
const req: IExtHostReadyMessage = { type: 'VSCODE_EXTHOST_IPC_READY' };
|
||||
if (process.send) {
|
||||
process.send(req);
|
||||
}
|
||||
process.send?.(req);
|
||||
});
|
||||
|
||||
} else {
|
||||
|
|
|
@ -60,7 +60,7 @@ suite('ExtHostCommands', function () {
|
|||
assert.strictEqual(unregisterCounter, 1);
|
||||
});
|
||||
|
||||
test('execute with retry', async function () {
|
||||
test('execute triggers activate', async function () {
|
||||
|
||||
let count = 0;
|
||||
|
||||
|
@ -68,16 +68,13 @@ suite('ExtHostCommands', function () {
|
|||
override $registerCommand(id: string): void {
|
||||
//
|
||||
}
|
||||
override async $executeCommand<T>(id: string, args: any[], retry: boolean): Promise<T | undefined> {
|
||||
count++;
|
||||
assert.strictEqual(retry, count === 1);
|
||||
if (count === 1) {
|
||||
assert.strictEqual(retry, true);
|
||||
throw new Error('$executeCommand:retry');
|
||||
} else {
|
||||
assert.strictEqual(retry, false);
|
||||
return <any>17;
|
||||
}
|
||||
|
||||
override async $activateByCommandEvent(id: string): Promise<void> {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
override async $executeCommand<T>(id: string, args: any[]): Promise<T | undefined> {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -86,8 +83,7 @@ suite('ExtHostCommands', function () {
|
|||
new NullLogService()
|
||||
);
|
||||
|
||||
const result = await commands.executeCommand('fooo', [this, true]);
|
||||
assert.strictEqual(result, 17);
|
||||
assert.strictEqual(count, 2);
|
||||
await commands.executeCommand('fooo', [this, true]);
|
||||
assert.strictEqual(count, 1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { SingleProxyRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { mock } from 'vs/base/test/common/mock';
|
||||
|
@ -42,46 +42,4 @@ suite('MainThreadCommands', function () {
|
|||
assert.strictEqual(CommandsRegistry.getCommand('foo'), undefined);
|
||||
assert.strictEqual(CommandsRegistry.getCommand('bar'), undefined);
|
||||
});
|
||||
|
||||
test('activate and throw when needed', async function () {
|
||||
|
||||
const activations: string[] = [];
|
||||
const runs: string[] = [];
|
||||
|
||||
const commands = new MainThreadCommands(
|
||||
SingleProxyRPCProtocol(null),
|
||||
new class extends mock<ICommandService>() {
|
||||
override executeCommand<T>(id: string): Promise<T | undefined> {
|
||||
runs.push(id);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
},
|
||||
new class extends mock<IExtensionService>() {
|
||||
override activateByEvent(id: string) {
|
||||
activations.push(id);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// case 1: arguments and retry
|
||||
try {
|
||||
activations.length = 0;
|
||||
await commands.$executeCommand('bazz', [1, 2, { n: 3 }], true);
|
||||
assert.ok(false);
|
||||
} catch (e) {
|
||||
assert.deepStrictEqual(activations, ['onCommand:bazz']);
|
||||
assert.strictEqual((<Error>e).message, '$executeCommand:retry');
|
||||
}
|
||||
|
||||
// case 2: no arguments and retry
|
||||
runs.length = 0;
|
||||
await commands.$executeCommand('bazz', [], true);
|
||||
assert.deepStrictEqual(runs, ['bazz']);
|
||||
|
||||
// case 3: arguments and no retry
|
||||
runs.length = 0;
|
||||
await commands.$executeCommand('bazz', [1, 2, true], false);
|
||||
assert.deepStrictEqual(runs, ['bazz']);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -57,9 +57,7 @@ export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService {
|
|||
private set _callCount(value: number) {
|
||||
this._callCountValue = value;
|
||||
if (this._callCountValue === 0) {
|
||||
if (this._completeIdle) {
|
||||
this._completeIdle();
|
||||
}
|
||||
this._completeIdle?.();
|
||||
this._idle = undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -609,9 +609,7 @@ export class CompositeDragAndDropObserver extends Disposable {
|
|||
return;
|
||||
}
|
||||
|
||||
if (callbacks.onDragLeave) {
|
||||
callbacks.onDragLeave({ eventData: e, dragAndDropData: data! });
|
||||
}
|
||||
callbacks.onDragLeave?.({ eventData: e, dragAndDropData: data! });
|
||||
},
|
||||
onDrop: e => {
|
||||
if (callbacks.onDrop) {
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
import { Event } from 'vs/base/common/event';
|
||||
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface IObservable<T> {
|
||||
export interface IObservable<T, TChange = void> {
|
||||
_change: TChange;
|
||||
|
||||
/**
|
||||
* Reads the current value.
|
||||
*
|
||||
|
@ -39,7 +41,7 @@ export interface IReader {
|
|||
*
|
||||
* Is called by `Observable.read`.
|
||||
*/
|
||||
handleBeforeReadObservable<T>(observable: IObservable<T>): void;
|
||||
handleBeforeReadObservable<T>(observable: IObservable<T, any>): void;
|
||||
}
|
||||
|
||||
export interface IObserver {
|
||||
|
@ -61,7 +63,7 @@ export interface IObserver {
|
|||
* Implementations must not call into other observables!
|
||||
* The change should be processed when {@link IObserver.endUpdate} is called.
|
||||
*/
|
||||
handleChange<T>(observable: IObservable<T>): void;
|
||||
handleChange<T, TChange>(observable: IObservable<T, TChange>, change: TChange): void;
|
||||
|
||||
/**
|
||||
* Indicates that an update operation has completed.
|
||||
|
@ -69,8 +71,8 @@ export interface IObserver {
|
|||
endUpdate<T>(observable: IObservable<T>): void;
|
||||
}
|
||||
|
||||
export interface ISettable<T> {
|
||||
set(value: T, transaction: ITransaction | undefined): void;
|
||||
export interface ISettable<T, TChange = void> {
|
||||
set(value: T, transaction: ITransaction | undefined, change: TChange): void;
|
||||
}
|
||||
|
||||
export interface ITransaction {
|
||||
|
@ -80,12 +82,14 @@ export interface ITransaction {
|
|||
*/
|
||||
updateObserver(
|
||||
observer: IObserver,
|
||||
observable: IObservable<any>
|
||||
observable: IObservable<any, any>
|
||||
): void;
|
||||
}
|
||||
|
||||
// === Base ===
|
||||
export abstract class ConvenientObservable<T> implements IObservable<T> {
|
||||
export abstract class ConvenientObservable<T, TChange> implements IObservable<T, TChange> {
|
||||
get _change(): TChange { return null!; }
|
||||
|
||||
public abstract get(): T;
|
||||
public abstract subscribe(observer: IObserver): void;
|
||||
public abstract unsubscribe(observer: IObserver): void;
|
||||
|
@ -100,7 +104,7 @@ export abstract class ConvenientObservable<T> implements IObservable<T> {
|
|||
}
|
||||
}
|
||||
|
||||
export abstract class BaseObservable<T> extends ConvenientObservable<T> {
|
||||
export abstract class BaseObservable<T, TChange = void> extends ConvenientObservable<T, TChange> {
|
||||
protected readonly observers = new Set<IObserver>();
|
||||
|
||||
public subscribe(observer: IObserver): void {
|
||||
|
@ -151,9 +155,9 @@ class TransactionImpl implements ITransaction {
|
|||
}
|
||||
}
|
||||
|
||||
export class ObservableValue<T>
|
||||
extends BaseObservable<T>
|
||||
implements ISettable<T>
|
||||
export class ObservableValue<T, TChange = void>
|
||||
extends BaseObservable<T, TChange>
|
||||
implements ISettable<T, TChange>
|
||||
{
|
||||
private value: T;
|
||||
|
||||
|
@ -166,14 +170,14 @@ export class ObservableValue<T>
|
|||
return this.value;
|
||||
}
|
||||
|
||||
public set(value: T, tx: ITransaction | undefined): void {
|
||||
public set(value: T, tx: ITransaction | undefined, change: TChange): void {
|
||||
if (this.value === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tx) {
|
||||
transaction((tx) => {
|
||||
this.set(value, tx);
|
||||
this.set(value, tx, change);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -182,7 +186,7 @@ export class ObservableValue<T>
|
|||
|
||||
for (const observer of this.observers) {
|
||||
tx.updateObserver(observer, this);
|
||||
observer.handleChange(this);
|
||||
observer.handleChange(this, change);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,7 +195,7 @@ export function constObservable<T>(value: T): IObservable<T> {
|
|||
return new ConstObservable(value);
|
||||
}
|
||||
|
||||
class ConstObservable<T> extends ConvenientObservable<T> {
|
||||
class ConstObservable<T> extends ConvenientObservable<T, void> {
|
||||
constructor(private readonly value: T) {
|
||||
super();
|
||||
}
|
||||
|
@ -208,11 +212,28 @@ class ConstObservable<T> extends ConvenientObservable<T> {
|
|||
}
|
||||
|
||||
// == autorun ==
|
||||
export function autorun(
|
||||
fn: (reader: IReader) => void,
|
||||
name: string
|
||||
export function autorun(fn: (reader: IReader) => void, name: string): IDisposable {
|
||||
return new AutorunObserver(fn, name, undefined);
|
||||
}
|
||||
|
||||
interface IChangeContext {
|
||||
readonly changedObservable: IObservable<any, any>;
|
||||
readonly change: unknown;
|
||||
|
||||
didChange<T, TChange>(observable: IObservable<T, TChange>): this is { change: TChange };
|
||||
}
|
||||
|
||||
export function autorunHandleChanges(
|
||||
name: string,
|
||||
options: {
|
||||
/**
|
||||
* Returns if this change should cause a re-run of the autorun.
|
||||
*/
|
||||
handleChange: (context: IChangeContext) => boolean;
|
||||
},
|
||||
fn: (reader: IReader) => void
|
||||
): IDisposable {
|
||||
return new AutorunObserver(fn, name);
|
||||
return new AutorunObserver(fn, name, options.handleChange);
|
||||
}
|
||||
|
||||
export function autorunWithStore(
|
||||
|
@ -252,7 +273,8 @@ export class AutorunObserver implements IObserver, IReader, IDisposable {
|
|||
|
||||
constructor(
|
||||
private readonly runFn: (reader: IReader) => void,
|
||||
public readonly name: string
|
||||
public readonly name: string,
|
||||
private readonly _handleChange: ((context: IChangeContext) => boolean) | undefined
|
||||
) {
|
||||
this.runIfNeeded();
|
||||
}
|
||||
|
@ -264,8 +286,13 @@ export class AutorunObserver implements IObserver, IReader, IDisposable {
|
|||
}
|
||||
}
|
||||
|
||||
public handleChange() {
|
||||
this.needsToRun = true;
|
||||
public handleChange<T, TChange>(observable: IObservable<T, TChange>, change: TChange): void {
|
||||
const shouldReact = this._handleChange ? this._handleChange({
|
||||
changedObservable: observable,
|
||||
change,
|
||||
didChange: o => o === observable as any,
|
||||
}) : true;
|
||||
this.needsToRun = this.needsToRun || shouldReact;
|
||||
|
||||
if (this.updateCount === 0) {
|
||||
this.runIfNeeded();
|
||||
|
@ -337,7 +364,7 @@ export function autorunDelta<T>(
|
|||
export function derivedObservable<T>(name: string, computeFn: (reader: IReader) => T): IObservable<T> {
|
||||
return new LazyDerived(computeFn, name);
|
||||
}
|
||||
export class LazyDerived<T> extends ConvenientObservable<T> {
|
||||
export class LazyDerived<T> extends ConvenientObservable<T, void> {
|
||||
private readonly observer: LazyDerivedObserver<T>;
|
||||
|
||||
constructor(computeFn: (reader: IReader) => T, name: string) {
|
||||
|
@ -366,7 +393,7 @@ export class LazyDerived<T> extends ConvenientObservable<T> {
|
|||
* @internal
|
||||
*/
|
||||
class LazyDerivedObserver<T>
|
||||
extends BaseObservable<T>
|
||||
extends BaseObservable<T, void>
|
||||
implements IReader, IObserver {
|
||||
private hadValue = false;
|
||||
private hasValue = false;
|
||||
|
@ -486,9 +513,8 @@ class LazyDerivedObserver<T>
|
|||
|
||||
this.hasValue = true;
|
||||
if (this.hadValue && oldValue !== this.value) {
|
||||
//
|
||||
for (const r of this.observers) {
|
||||
r.handleChange(this);
|
||||
r.handleChange(this, undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -508,6 +534,20 @@ export function observableFromPromise<T>(promise: Promise<T>): IObservable<{ val
|
|||
return observable;
|
||||
}
|
||||
|
||||
export function waitForState<T, TState extends T>(observable: IObservable<T>, predicate: (state: T) => state is TState): Promise<TState>;
|
||||
export function waitForState<T>(observable: IObservable<T>, predicate: (state: T) => boolean): Promise<T>;
|
||||
export function waitForState<T>(observable: IObservable<T>, predicate: (state: T) => boolean): Promise<T> {
|
||||
return new Promise(resolve => {
|
||||
const d = autorun(reader => {
|
||||
const currentState = observable.read(reader);
|
||||
if (predicate(currentState)) {
|
||||
d.dispose();
|
||||
resolve(currentState);
|
||||
}
|
||||
}, 'waitForState');
|
||||
});
|
||||
}
|
||||
|
||||
export function observableFromEvent<T, TArgs = unknown>(
|
||||
event: Event<TArgs>,
|
||||
getValue: (args: TArgs | undefined) => T
|
||||
|
@ -540,7 +580,7 @@ class FromEventObservable<TArgs, T> extends BaseObservable<T> {
|
|||
transaction(tx => {
|
||||
for (const o of this.observers) {
|
||||
tx.updateObserver(o, this);
|
||||
o.handleChange(this);
|
||||
o.handleChange(this, undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -622,3 +662,12 @@ export function keepAlive(observable: IObservable<any>): IDisposable {
|
|||
observable.read(reader);
|
||||
}, 'keep-alive');
|
||||
}
|
||||
|
||||
export function derivedObservableWithCache<T>(name: string, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable<T> {
|
||||
let lastValue: T | undefined = undefined;
|
||||
const observable = derivedObservable(name, reader => {
|
||||
lastValue = computeFn(reader, lastValue);
|
||||
return lastValue;
|
||||
});
|
||||
return observable;
|
||||
}
|
||||
|
|
|
@ -272,9 +272,7 @@ export class BulkEditPane extends ViewPane {
|
|||
}
|
||||
|
||||
private _done(accept: boolean): void {
|
||||
if (this._currentResolve) {
|
||||
this._currentResolve(accept ? this._currentInput?.getWorkspaceEdit() : undefined);
|
||||
}
|
||||
this._currentResolve?.(accept ? this._currentInput?.getWorkspaceEdit() : undefined);
|
||||
this._currentInput = undefined;
|
||||
this._setState(State.Message);
|
||||
this._sessionDisposables.clear();
|
||||
|
|
|
@ -250,9 +250,7 @@ export class CommentReply<T extends IRange | ICellRange> extends Disposable {
|
|||
}));
|
||||
|
||||
this._commentFormActions = new CommentFormActions(container, async (action: IAction) => {
|
||||
if (this._actionRunDelegate) {
|
||||
this._actionRunDelegate();
|
||||
}
|
||||
this._actionRunDelegate?.();
|
||||
|
||||
action.run({
|
||||
thread: this._commentThread,
|
||||
|
|
|
@ -155,14 +155,10 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
|
|||
|
||||
switch (message.type) {
|
||||
case 'event':
|
||||
if (this.eventCallback) {
|
||||
this.eventCallback(<DebugProtocol.Event>message);
|
||||
}
|
||||
this.eventCallback?.(<DebugProtocol.Event>message);
|
||||
break;
|
||||
case 'request':
|
||||
if (this.requestCallback) {
|
||||
this.requestCallback(<DebugProtocol.Request>message);
|
||||
}
|
||||
this.requestCallback?.(<DebugProtocol.Request>message);
|
||||
break;
|
||||
case 'response': {
|
||||
const response = <DebugProtocol.Response>message;
|
||||
|
|
|
@ -1519,9 +1519,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
|||
this.progressService.withProgress({ location: ProgressLocation.Extensions }, () => new Promise(resolve => this._activityCallBack = resolve));
|
||||
}
|
||||
} else {
|
||||
if (this._activityCallBack) {
|
||||
this._activityCallBack();
|
||||
}
|
||||
this._activityCallBack?.();
|
||||
this._activityCallBack = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -215,6 +215,10 @@
|
|||
min-width: 0;
|
||||
}
|
||||
|
||||
.monaco-list-row.disabled .extension-list-item .details .description {
|
||||
color: var(--vscode-disabledForeground);
|
||||
}
|
||||
|
||||
.extension-list-item .monaco-action-bar .action-label.icon {
|
||||
padding: 1px 2px;
|
||||
}
|
||||
|
|
|
@ -45,12 +45,14 @@ interface IMarkerCodeColumnTemplateData {
|
|||
}
|
||||
|
||||
interface IMarkerFileColumnTemplateData {
|
||||
readonly columnElement: HTMLElement;
|
||||
readonly fileLabel: HighlightedLabel;
|
||||
readonly positionLabel: HighlightedLabel;
|
||||
}
|
||||
|
||||
|
||||
interface IMarkerHighlightedLabelColumnTemplateData {
|
||||
readonly columnElement: HTMLElement;
|
||||
readonly highlightedLabel: HighlightedLabel;
|
||||
}
|
||||
|
||||
|
@ -86,6 +88,7 @@ class MarkerSeverityColumnRenderer implements ITableRenderer<MarkerTableItem, IM
|
|||
}
|
||||
};
|
||||
|
||||
templateData.icon.title = MarkerSeverity.toString(element.marker.severity);
|
||||
templateData.icon.className = `marker-icon codicon ${SeverityIcon.className(MarkerSeverity.toSeverity(element.marker.severity))}`;
|
||||
|
||||
templateData.actionBar.clear();
|
||||
|
@ -137,9 +140,11 @@ class MarkerCodeColumnRenderer implements ITableRenderer<MarkerTableItem, IMarke
|
|||
DOM.show(templateData.codeLabel.element);
|
||||
|
||||
if (typeof element.marker.code === 'string') {
|
||||
templateData.codeColumn.title = `${element.marker.source} (${element.marker.code})`;
|
||||
templateData.sourceLabel.set(element.marker.source, element.sourceMatches);
|
||||
templateData.codeLabel.set(element.marker.code, element.codeMatches);
|
||||
} else {
|
||||
templateData.codeColumn.title = `${element.marker.source} (${element.marker.code.value})`;
|
||||
templateData.sourceLabel.set(element.marker.source, element.sourceMatches);
|
||||
|
||||
const codeLinkLabel = new HighlightedLabel($('.code-link-label'));
|
||||
|
@ -152,6 +157,7 @@ class MarkerCodeColumnRenderer implements ITableRenderer<MarkerTableItem, IMarke
|
|||
};
|
||||
}
|
||||
} else {
|
||||
templateData.codeColumn.title = '';
|
||||
templateData.sourceLabel.set('-');
|
||||
DOM.hide(templateData.codeLabel.element);
|
||||
}
|
||||
|
@ -167,13 +173,14 @@ class MarkerMessageColumnRenderer implements ITableRenderer<MarkerTableItem, IMa
|
|||
readonly templateId: string = MarkerMessageColumnRenderer.TEMPLATE_ID;
|
||||
|
||||
renderTemplate(container: HTMLElement): IMarkerHighlightedLabelColumnTemplateData {
|
||||
const fileColumn = DOM.append(container, $('.message'));
|
||||
const highlightedLabel = new HighlightedLabel(fileColumn);
|
||||
const columnElement = DOM.append(container, $('.message'));
|
||||
const highlightedLabel = new HighlightedLabel(columnElement);
|
||||
|
||||
return { highlightedLabel };
|
||||
return { columnElement, highlightedLabel };
|
||||
}
|
||||
|
||||
renderElement(element: MarkerTableItem, index: number, templateData: IMarkerHighlightedLabelColumnTemplateData, height: number | undefined): void {
|
||||
templateData.columnElement.title = element.marker.message;
|
||||
templateData.highlightedLabel.set(element.marker.message, element.messageMatches);
|
||||
}
|
||||
|
||||
|
@ -191,18 +198,21 @@ class MarkerFileColumnRenderer implements ITableRenderer<MarkerTableItem, IMarke
|
|||
) { }
|
||||
|
||||
renderTemplate(container: HTMLElement): IMarkerFileColumnTemplateData {
|
||||
const fileColumn = DOM.append(container, $('.file'));
|
||||
const fileLabel = new HighlightedLabel(fileColumn);
|
||||
const columnElement = DOM.append(container, $('.file'));
|
||||
const fileLabel = new HighlightedLabel(columnElement);
|
||||
fileLabel.element.classList.add('file-label');
|
||||
const positionLabel = new HighlightedLabel(fileColumn);
|
||||
const positionLabel = new HighlightedLabel(columnElement);
|
||||
positionLabel.element.classList.add('file-position');
|
||||
|
||||
return { fileLabel, positionLabel };
|
||||
return { columnElement, fileLabel, positionLabel };
|
||||
}
|
||||
|
||||
renderElement(element: MarkerTableItem, index: number, templateData: IMarkerFileColumnTemplateData, height: number | undefined): void {
|
||||
const positionLabel = Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(element.marker.startLineNumber, element.marker.startColumn);
|
||||
|
||||
templateData.columnElement.title = `${this.labelService.getUriLabel(element.marker.resource, { relative: false })} ${positionLabel}`;
|
||||
templateData.fileLabel.set(this.labelService.getUriLabel(element.marker.resource, { relative: true }), element.fileMatches);
|
||||
templateData.positionLabel.set(Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(element.marker.startLineNumber, element.marker.startColumn), undefined);
|
||||
templateData.positionLabel.set(positionLabel, undefined);
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IMarkerFileColumnTemplateData): void { }
|
||||
|
@ -215,12 +225,13 @@ class MarkerOwnerColumnRenderer implements ITableRenderer<MarkerTableItem, IMark
|
|||
readonly templateId: string = MarkerOwnerColumnRenderer.TEMPLATE_ID;
|
||||
|
||||
renderTemplate(container: HTMLElement): IMarkerHighlightedLabelColumnTemplateData {
|
||||
const fileColumn = DOM.append(container, $('.owner'));
|
||||
const highlightedLabel = new HighlightedLabel(fileColumn);
|
||||
return { highlightedLabel };
|
||||
const columnElement = DOM.append(container, $('.owner'));
|
||||
const highlightedLabel = new HighlightedLabel(columnElement);
|
||||
return { columnElement, highlightedLabel };
|
||||
}
|
||||
|
||||
renderElement(element: MarkerTableItem, index: number, templateData: IMarkerHighlightedLabelColumnTemplateData, height: number | undefined): void {
|
||||
templateData.columnElement.title = element.marker.owner;
|
||||
templateData.highlightedLabel.set(element.marker.owner, element.ownerMatches);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/tex
|
|||
import { localize } from 'vs/nls';
|
||||
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
@ -42,7 +43,7 @@ import { DEFAULT_EDITOR_MAX_DIMENSIONS, DEFAULT_EDITOR_MIN_DIMENSIONS } from 'vs
|
|||
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';
|
||||
import { autorun, derivedObservable, IObservable, ITransaction, keepAlive, ObservableValue } from 'vs/workbench/contrib/audioCues/browser/observable';
|
||||
import { autorun, autorunWithStore, derivedObservable, IObservable, ITransaction, keepAlive, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable';
|
||||
import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput';
|
||||
import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel';
|
||||
import { LineRange, ModifiedBaseRange } from 'vs/workbench/contrib/mergeEditor/browser/model';
|
||||
|
@ -61,8 +62,8 @@ export class MergeEditor extends EditorPane {
|
|||
|
||||
private _grid!: Grid<IView>;
|
||||
|
||||
private readonly input1View = this.instantiation.createInstance(InputCodeEditorView, 1, { readonly: true });
|
||||
private readonly input2View = this.instantiation.createInstance(InputCodeEditorView, 2, { readonly: true });
|
||||
private readonly input1View = this.instantiation.createInstance(InputCodeEditorView, 1, { readonly: !this.inputsWritable });
|
||||
private readonly input2View = this.instantiation.createInstance(InputCodeEditorView, 2, { readonly: !this.inputsWritable });
|
||||
private readonly inputResultView = this.instantiation.createInstance(ResultCodeEditorView, { readonly: false });
|
||||
|
||||
private readonly _ctxIsMergeEditor: IContextKey<boolean>;
|
||||
|
@ -71,6 +72,10 @@ export class MergeEditor extends EditorPane {
|
|||
private _model: MergeEditorModel | undefined;
|
||||
public get model(): MergeEditorModel | undefined { return this._model; }
|
||||
|
||||
private get inputsWritable(): boolean {
|
||||
return !!this._configurationService.getValue<boolean>('mergeEditor.writableInputs');
|
||||
}
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiation: IInstantiationService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
|
@ -80,6 +85,7 @@ export class MergeEditor extends EditorPane {
|
|||
@IStorageService storageService: IStorageService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@ITextResourceConfigurationService private readonly textResourceConfigurationService: ITextResourceConfigurationService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
) {
|
||||
super(MergeEditor.ID, telemetryService, themeService, storageService);
|
||||
|
||||
|
@ -94,7 +100,7 @@ export class MergeEditor extends EditorPane {
|
|||
return undefined;
|
||||
}
|
||||
const resultDiffs = model.resultDiffs.read(reader);
|
||||
const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input1, model.input1LinesDiffs, model.result, resultDiffs);
|
||||
const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input1, model.input1LinesDiffs.read(reader), model.result, resultDiffs);
|
||||
return modifiedBaseRanges;
|
||||
});
|
||||
const input2ResultMapping = derivedObservable('input2ResultMapping', reader => {
|
||||
|
@ -103,7 +109,7 @@ export class MergeEditor extends EditorPane {
|
|||
return undefined;
|
||||
}
|
||||
const resultDiffs = model.resultDiffs.read(reader);
|
||||
const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input2, model.input2LinesDiffs, model.result, resultDiffs);
|
||||
const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input2, model.input2LinesDiffs.read(reader), model.result, resultDiffs);
|
||||
return modifiedBaseRanges;
|
||||
});
|
||||
|
||||
|
@ -228,42 +234,44 @@ export class MergeEditor extends EditorPane {
|
|||
|
||||
// TODO: Update editor options!
|
||||
|
||||
const input1ViewZoneIds: string[] = [];
|
||||
const input2ViewZoneIds: string[] = [];
|
||||
for (const m of model.modifiedBaseRanges) {
|
||||
const max = Math.max(m.input1Range.lineCount, m.input2Range.lineCount, 1);
|
||||
this._sessionDisposables.add(autorunWithStore((reader, store) => {
|
||||
const input1ViewZoneIds: string[] = [];
|
||||
const input2ViewZoneIds: string[] = [];
|
||||
for (const m of model.modifiedBaseRanges.read(reader)) {
|
||||
const max = Math.max(m.input1Range.lineCount, m.input2Range.lineCount, 1);
|
||||
|
||||
this.input1View.editor.changeViewZones(a => {
|
||||
input1ViewZoneIds.push(a.addZone({
|
||||
afterLineNumber: m.input1Range.endLineNumberExclusive - 1,
|
||||
heightInLines: max - m.input1Range.lineCount,
|
||||
domNode: $('div.diagonal-fill'),
|
||||
}));
|
||||
});
|
||||
|
||||
this.input2View.editor.changeViewZones(a => {
|
||||
input2ViewZoneIds.push(a.addZone({
|
||||
afterLineNumber: m.input2Range.endLineNumberExclusive - 1,
|
||||
heightInLines: max - m.input2Range.lineCount,
|
||||
domNode: $('div.diagonal-fill'),
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
this._sessionDisposables.add({
|
||||
dispose: () => {
|
||||
this.input1View.editor.changeViewZones(a => {
|
||||
for (const zone of input1ViewZoneIds) {
|
||||
a.removeZone(zone);
|
||||
}
|
||||
input1ViewZoneIds.push(a.addZone({
|
||||
afterLineNumber: m.input1Range.endLineNumberExclusive - 1,
|
||||
heightInLines: max - m.input1Range.lineCount,
|
||||
domNode: $('div.diagonal-fill'),
|
||||
}));
|
||||
});
|
||||
|
||||
this.input2View.editor.changeViewZones(a => {
|
||||
for (const zone of input2ViewZoneIds) {
|
||||
a.removeZone(zone);
|
||||
}
|
||||
input2ViewZoneIds.push(a.addZone({
|
||||
afterLineNumber: m.input2Range.endLineNumberExclusive - 1,
|
||||
heightInLines: max - m.input2Range.lineCount,
|
||||
domNode: $('div.diagonal-fill'),
|
||||
}));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
store.add({
|
||||
dispose: () => {
|
||||
this.input1View.editor.changeViewZones(a => {
|
||||
for (const zone of input1ViewZoneIds) {
|
||||
a.removeZone(zone);
|
||||
}
|
||||
});
|
||||
this.input2View.editor.changeViewZones(a => {
|
||||
for (const zone of input2ViewZoneIds) {
|
||||
a.removeZone(zone);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 'update alignment view zones'));
|
||||
}
|
||||
|
||||
protected override setEditorVisible(visible: boolean): void {
|
||||
|
@ -448,7 +456,7 @@ class InputCodeEditorView extends CodeEditorView {
|
|||
return [];
|
||||
}
|
||||
const result = new Array<IModelDeltaDecoration>();
|
||||
for (const m of model.modifiedBaseRanges) {
|
||||
for (const m of model.modifiedBaseRanges.read(reader)) {
|
||||
const range = m.getInputRange(this.inputNumber);
|
||||
if (!range.isEmpty) {
|
||||
result.push({
|
||||
|
@ -478,13 +486,14 @@ class InputCodeEditorView extends CodeEditorView {
|
|||
getIntersectingGutterItems: (range, reader) => {
|
||||
const model = this.model.read(reader);
|
||||
if (!model) { return []; }
|
||||
return model.modifiedBaseRanges
|
||||
return model.modifiedBaseRanges.read(reader)
|
||||
.filter((r) => r.getInputDiffs(this.inputNumber).length > 0)
|
||||
.map<ModifiedBaseRangeGutterItemInfo>((baseRange, idx) => ({
|
||||
id: idx.toString(),
|
||||
additionalHeightInPx: 0,
|
||||
offsetInPx: 0,
|
||||
range: baseRange.getInputRange(this.inputNumber),
|
||||
enabled: model.isUpToDate,
|
||||
toggleState: derivedObservable('toggle', (reader) =>
|
||||
model
|
||||
.getState(baseRange)
|
||||
|
@ -510,14 +519,19 @@ class InputCodeEditorView extends CodeEditorView {
|
|||
}
|
||||
|
||||
interface ModifiedBaseRangeGutterItemInfo extends IGutterItemInfo {
|
||||
enabled: IObservable<boolean>;
|
||||
toggleState: IObservable<boolean | undefined>;
|
||||
setState(value: boolean, tx: ITransaction | undefined): void;
|
||||
setState(value: boolean, tx: ITransaction): void;
|
||||
}
|
||||
|
||||
class MergeConflictGutterItemView extends Disposable implements IGutterItemView<ModifiedBaseRangeGutterItemInfo> {
|
||||
constructor(private item: ModifiedBaseRangeGutterItemInfo, private readonly target: HTMLElement) {
|
||||
private readonly item = new ObservableValue<ModifiedBaseRangeGutterItemInfo | undefined>(undefined, 'item');
|
||||
|
||||
constructor(item: ModifiedBaseRangeGutterItemInfo, private readonly target: HTMLElement) {
|
||||
super();
|
||||
|
||||
this.item.set(item, undefined);
|
||||
|
||||
target.classList.add('merge-accept-gutter-marker');
|
||||
|
||||
// TODO: localized title
|
||||
|
@ -526,7 +540,8 @@ class MergeConflictGutterItemView extends Disposable implements IGutterItemView<
|
|||
|
||||
this._register(
|
||||
autorun((reader) => {
|
||||
const value = this.item.toggleState.read(reader);
|
||||
const item = this.item.read(reader)!;
|
||||
const value = item.toggleState.read(reader);
|
||||
checkBox.setIcon(
|
||||
value === true
|
||||
? Codicon.check
|
||||
|
@ -535,11 +550,19 @@ class MergeConflictGutterItemView extends Disposable implements IGutterItemView<
|
|||
: Codicon.circleFilled
|
||||
);
|
||||
checkBox.checked = value === true;
|
||||
|
||||
if (!item.enabled.read(reader)) {
|
||||
checkBox.disable();
|
||||
} else {
|
||||
checkBox.enable();
|
||||
}
|
||||
}, 'Update Toggle State')
|
||||
);
|
||||
|
||||
this._register(checkBox.onChange(() => {
|
||||
this.item.setState(checkBox.checked, undefined);
|
||||
transaction(tx => {
|
||||
this.item.get()!.setState(checkBox.checked, tx);
|
||||
});
|
||||
}));
|
||||
|
||||
target.appendChild(n('div.background', [noBreakWhitespace]).root);
|
||||
|
@ -555,7 +578,7 @@ class MergeConflictGutterItemView extends Disposable implements IGutterItemView<
|
|||
}
|
||||
|
||||
update(baseRange: ModifiedBaseRangeGutterItemInfo): void {
|
||||
this.item = baseRange;
|
||||
this.item.set(baseRange, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
|
|||
import { IUntypedEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput';
|
||||
import { MergeEditorModel, MergeEditorModelFactory } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel';
|
||||
import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
||||
|
@ -39,10 +39,9 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput {
|
|||
|
||||
private _model?: MergeEditorModel;
|
||||
private _outTextModel?: ITextFileEditorModel;
|
||||
private readonly mergeEditorModelFactory = this._instaService.createInstance(MergeEditorModelFactory);
|
||||
|
||||
constructor(
|
||||
private readonly _anchestor: URI,
|
||||
private readonly _base: URI,
|
||||
private readonly _input1: MergeEditorInputData,
|
||||
private readonly _input2: MergeEditorInputData,
|
||||
private readonly _result: URI,
|
||||
|
@ -101,13 +100,14 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput {
|
|||
|
||||
if (!this._model) {
|
||||
|
||||
const anchestor = await this._textModelService.createModelReference(this._anchestor);
|
||||
const base = await this._textModelService.createModelReference(this._base);
|
||||
const input1 = await this._textModelService.createModelReference(this._input1.uri);
|
||||
const input2 = await this._textModelService.createModelReference(this._input2.uri);
|
||||
const result = await this._textModelService.createModelReference(this._result);
|
||||
|
||||
this._model = await this.mergeEditorModelFactory.create(
|
||||
anchestor.object.textEditorModel,
|
||||
this._model = this._instaService.createInstance(
|
||||
MergeEditorModel,
|
||||
base.object.textEditorModel,
|
||||
input1.object.textEditorModel,
|
||||
this._input1.detail,
|
||||
this._input1.description,
|
||||
|
@ -117,13 +117,13 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput {
|
|||
result.object.textEditorModel
|
||||
);
|
||||
|
||||
await this._model.onInitialized;
|
||||
|
||||
this._store.add(this._model);
|
||||
this._store.add(anchestor);
|
||||
this._store.add(base);
|
||||
this._store.add(input1);
|
||||
this._store.add(input2);
|
||||
this._store.add(result);
|
||||
|
||||
// result.object.
|
||||
}
|
||||
return this._model;
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput {
|
|||
if (!(otherInput instanceof MergeEditorInput)) {
|
||||
return false;
|
||||
}
|
||||
return isEqual(this._anchestor, otherInput._anchestor)
|
||||
return isEqual(this._base, otherInput._base)
|
||||
&& isEqual(this._input1.uri, otherInput._input1.uri)
|
||||
&& isEqual(this._input2.uri, otherInput._input2.uri)
|
||||
&& isEqual(this._result, otherInput._result);
|
||||
|
@ -140,7 +140,7 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput {
|
|||
|
||||
toJSON(): MergeEditorInputJSON {
|
||||
return {
|
||||
anchestor: this._anchestor,
|
||||
anchestor: this._base,
|
||||
inputOne: this._input1,
|
||||
inputTwo: this._input2,
|
||||
result: this._result,
|
||||
|
|
|
@ -3,96 +3,72 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { compareBy, CompareResult, equals, numberComparator } from 'vs/base/common/arrays';
|
||||
import { compareBy, CompareResult, equals } from 'vs/base/common/arrays';
|
||||
import { BugIndicatingError } from 'vs/base/common/errors';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker';
|
||||
import { EditorModel } from 'vs/workbench/common/editor/editorModel';
|
||||
import { IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable';
|
||||
import { ModifiedBaseRange, LineEdit, LineDiff, ModifiedBaseRangeState, LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model';
|
||||
import { leftJoin, ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils';
|
||||
import { autorunHandleChanges, derivedObservable, derivedObservableWithCache, IObservable, ITransaction, keepAlive, ObservableValue, transaction, waitForState } from 'vs/workbench/contrib/audioCues/browser/observable';
|
||||
import { LineDiff, LineEdit, LineRange, ModifiedBaseRange, ModifiedBaseRangeState } from 'vs/workbench/contrib/mergeEditor/browser/model';
|
||||
import { EditorWorkerServiceDiffComputer, TextModelDiffChangeReason, TextModelDiffs, TextModelDiffState } from 'vs/workbench/contrib/mergeEditor/browser/textModelDiffs';
|
||||
import { leftJoin } from 'vs/workbench/contrib/mergeEditor/browser/utils';
|
||||
|
||||
export class MergeEditorModelFactory {
|
||||
constructor(
|
||||
@IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService
|
||||
) {
|
||||
}
|
||||
|
||||
public async create(
|
||||
base: ITextModel,
|
||||
input1: ITextModel,
|
||||
input1Detail: string | undefined,
|
||||
input1Description: string | undefined,
|
||||
input2: ITextModel,
|
||||
input2Detail: string | undefined,
|
||||
input2Description: string | undefined,
|
||||
result: ITextModel,
|
||||
): Promise<MergeEditorModel> {
|
||||
|
||||
const baseToInput1DiffPromise = this._editorWorkerService.computeDiff(
|
||||
base.uri,
|
||||
input1.uri,
|
||||
false,
|
||||
1000
|
||||
);
|
||||
const baseToInput2DiffPromise = this._editorWorkerService.computeDiff(
|
||||
base.uri,
|
||||
input2.uri,
|
||||
false,
|
||||
1000
|
||||
);
|
||||
const baseToResultDiffPromise = this._editorWorkerService.computeDiff(
|
||||
base.uri,
|
||||
result.uri,
|
||||
false,
|
||||
1000
|
||||
);
|
||||
|
||||
const [baseToInput1Diff, baseToInput2Diff, baseToResultDiff] = await Promise.all([
|
||||
baseToInput1DiffPromise,
|
||||
baseToInput2DiffPromise,
|
||||
baseToResultDiffPromise
|
||||
]);
|
||||
|
||||
const changesInput1 =
|
||||
baseToInput1Diff?.changes.map((c) =>
|
||||
LineDiff.fromLineChange(c, base, input1)
|
||||
) || [];
|
||||
const changesInput2 =
|
||||
baseToInput2Diff?.changes.map((c) =>
|
||||
LineDiff.fromLineChange(c, base, input2)
|
||||
) || [];
|
||||
const changesResult =
|
||||
baseToResultDiff?.changes.map((c) =>
|
||||
LineDiff.fromLineChange(c, base, result)
|
||||
) || [];
|
||||
|
||||
return new MergeEditorModel(
|
||||
InternalSymbol,
|
||||
base,
|
||||
input1,
|
||||
input1Detail,
|
||||
input1Description,
|
||||
input2,
|
||||
input2Detail,
|
||||
input2Description,
|
||||
result,
|
||||
changesInput1,
|
||||
changesInput2,
|
||||
changesResult,
|
||||
this._editorWorkerService,
|
||||
);
|
||||
}
|
||||
export const enum MergeEditorModelState {
|
||||
initializing = 1,
|
||||
upToDate = 2,
|
||||
updating = 3,
|
||||
}
|
||||
|
||||
const InternalSymbol: unique symbol = null!;
|
||||
|
||||
export class MergeEditorModel extends EditorModel {
|
||||
private resultEdits: ResultEdits;
|
||||
private readonly diffComputer = new EditorWorkerServiceDiffComputer(this.editorWorkerService);
|
||||
private readonly input1TextModelDiffs = new TextModelDiffs(this.base, this.input1, this.diffComputer);
|
||||
private readonly input2TextModelDiffs = new TextModelDiffs(this.base, this.input2, this.diffComputer);
|
||||
private readonly resultTextModelDiffs = new TextModelDiffs(this.base, this.result, this.diffComputer);
|
||||
|
||||
public readonly state = derivedObservable('state', reader => {
|
||||
const states = [
|
||||
this.input1TextModelDiffs,
|
||||
this.input2TextModelDiffs,
|
||||
this.resultTextModelDiffs,
|
||||
].map((s) => s.state.read(reader));
|
||||
|
||||
if (states.some((s) => s === TextModelDiffState.initializing)) {
|
||||
return MergeEditorModelState.initializing;
|
||||
}
|
||||
if (states.some((s) => s === TextModelDiffState.updating)) {
|
||||
return MergeEditorModelState.updating;
|
||||
}
|
||||
return MergeEditorModelState.upToDate;
|
||||
});
|
||||
|
||||
public readonly isUpToDate = derivedObservable('isUpdating', reader => this.state.read(reader) === MergeEditorModelState.upToDate);
|
||||
|
||||
public readonly onInitialized = waitForState(this.state, state => state === MergeEditorModelState.upToDate);
|
||||
|
||||
public readonly modifiedBaseRanges = derivedObservableWithCache<ModifiedBaseRange[]>('modifiedBaseRanges', (reader, lastValue) => {
|
||||
if (this.state.read(reader) !== MergeEditorModelState.upToDate) {
|
||||
return lastValue || [];
|
||||
}
|
||||
|
||||
const input1Diffs = this.input1TextModelDiffs.diffs.read(reader);
|
||||
const input2Diffs = this.input2TextModelDiffs.diffs.read(reader);
|
||||
|
||||
return ModifiedBaseRange.fromDiffs(this.base, this.input1, input1Diffs, this.input2, input2Diffs);
|
||||
});
|
||||
|
||||
public readonly input1LinesDiffs = this.input1TextModelDiffs.diffs;
|
||||
public readonly input2LinesDiffs = this.input2TextModelDiffs.diffs;
|
||||
public readonly resultDiffs = this.resultTextModelDiffs.diffs;
|
||||
|
||||
private readonly modifiedBaseRangeStateStores =
|
||||
derivedObservable('modifiedBaseRangeStateStores', reader => {
|
||||
const map = new Map(
|
||||
this.modifiedBaseRanges.read(reader).map(s => ([s, new ObservableValue(ModifiedBaseRangeState.default, 'State')]))
|
||||
);
|
||||
return map;
|
||||
});
|
||||
|
||||
constructor(
|
||||
_symbol: typeof InternalSymbol,
|
||||
readonly base: ITextModel,
|
||||
readonly input1: ITextModel,
|
||||
readonly input1Detail: string | undefined,
|
||||
|
@ -101,27 +77,43 @@ export class MergeEditorModel extends EditorModel {
|
|||
readonly input2Detail: string | undefined,
|
||||
readonly input2Description: string | undefined,
|
||||
readonly result: ITextModel,
|
||||
public readonly input1LinesDiffs: readonly LineDiff[],
|
||||
public readonly input2LinesDiffs: readonly LineDiff[],
|
||||
resultDiffs: LineDiff[],
|
||||
private readonly editorWorkerService: IEditorWorkerService
|
||||
@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.resultEdits = new ResultEdits(resultDiffs, this.base, this.result, this.editorWorkerService);
|
||||
this.resultEdits.onDidChange(() => {
|
||||
this.recomputeState();
|
||||
});
|
||||
this.recomputeState();
|
||||
this._register(keepAlive(this.modifiedBaseRangeStateStores));
|
||||
|
||||
this.resetUnknown();
|
||||
this._register(
|
||||
autorunHandleChanges(
|
||||
'Recompute State',
|
||||
{
|
||||
handleChange: (ctx) =>
|
||||
ctx.didChange(this.resultTextModelDiffs.diffs)
|
||||
// Ignore non-text changes as we update the state directly
|
||||
? ctx.change === TextModelDiffChangeReason.textChange
|
||||
: true,
|
||||
},
|
||||
(reader) => {
|
||||
if (!this.isUpToDate.read(reader)) {
|
||||
return;
|
||||
}
|
||||
const resultDiffs = this.resultTextModelDiffs.diffs.read(reader);
|
||||
const stores = this.modifiedBaseRangeStateStores.read(reader);
|
||||
this.recomputeState(resultDiffs, stores);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
this.onInitialized.then(() => {
|
||||
this.resetUnknown();
|
||||
});
|
||||
}
|
||||
|
||||
private recomputeState(): void {
|
||||
private recomputeState(resultDiffs: LineDiff[], stores: Map<ModifiedBaseRange, ObservableValue<ModifiedBaseRangeState>>): void {
|
||||
transaction(tx => {
|
||||
const baseRangeWithStoreAndTouchingDiffs = leftJoin(
|
||||
this.modifiedBaseRangeStateStores,
|
||||
this.resultEdits.diffs.get(),
|
||||
stores,
|
||||
resultDiffs,
|
||||
(baseRange, diff) =>
|
||||
baseRange[0].baseRange.touches(diff.originalRange)
|
||||
? CompareResult.neitherLessOrGreaterThan
|
||||
|
@ -132,14 +124,14 @@ export class MergeEditorModel extends EditorModel {
|
|||
);
|
||||
|
||||
for (const row of baseRangeWithStoreAndTouchingDiffs) {
|
||||
row.left[1].set(this.computeState(row.left[0], row.rights), tx);
|
||||
row.left[1].set(computeState(row.left[0], row.rights), tx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public resetUnknown(): void {
|
||||
transaction(tx => {
|
||||
for (const range of this.modifiedBaseRanges) {
|
||||
for (const range of this.modifiedBaseRanges.get()) {
|
||||
if (this.getState(range).get().conflicting) {
|
||||
this.setState(range, ModifiedBaseRangeState.default, tx);
|
||||
}
|
||||
|
@ -149,7 +141,7 @@ export class MergeEditorModel extends EditorModel {
|
|||
|
||||
public mergeNonConflictingDiffs(): void {
|
||||
transaction((tx) => {
|
||||
for (const m of this.modifiedBaseRanges) {
|
||||
for (const m of this.modifiedBaseRanges.get()) {
|
||||
if (m.isConflicting) {
|
||||
continue;
|
||||
}
|
||||
|
@ -164,54 +156,8 @@ export class MergeEditorModel extends EditorModel {
|
|||
});
|
||||
}
|
||||
|
||||
public get resultDiffs(): IObservable<readonly LineDiff[]> {
|
||||
return this.resultEdits.diffs;
|
||||
}
|
||||
|
||||
public readonly modifiedBaseRanges = ModifiedBaseRange.fromDiffs(
|
||||
this.base,
|
||||
this.input1,
|
||||
this.input1LinesDiffs,
|
||||
this.input2,
|
||||
this.input2LinesDiffs
|
||||
);
|
||||
|
||||
private readonly modifiedBaseRangeStateStores: ReadonlyMap<ModifiedBaseRange, ObservableValue<ModifiedBaseRangeState>> = new Map(
|
||||
this.modifiedBaseRanges.map(s => ([s, new ObservableValue(ModifiedBaseRangeState.default, 'State')]))
|
||||
);
|
||||
|
||||
private computeState(baseRange: ModifiedBaseRange, conflictingDiffs?: LineDiff[]): ModifiedBaseRangeState {
|
||||
if (!conflictingDiffs) {
|
||||
conflictingDiffs = this.resultEdits.findTouchingDiffs(
|
||||
baseRange.baseRange
|
||||
);
|
||||
}
|
||||
|
||||
if (conflictingDiffs.length === 0) {
|
||||
return ModifiedBaseRangeState.default;
|
||||
}
|
||||
const conflictingEdits = conflictingDiffs.map((d) => d.getLineEdit());
|
||||
|
||||
function editsAgreeWithDiffs(diffs: readonly LineDiff[]): boolean {
|
||||
return equals(
|
||||
conflictingEdits,
|
||||
diffs.map((d) => d.getLineEdit()),
|
||||
(a, b) => a.equals(b)
|
||||
);
|
||||
}
|
||||
|
||||
if (editsAgreeWithDiffs(baseRange.input1Diffs)) {
|
||||
return ModifiedBaseRangeState.default.withInput1(true);
|
||||
}
|
||||
if (editsAgreeWithDiffs(baseRange.input2Diffs)) {
|
||||
return ModifiedBaseRangeState.default.withInput2(true);
|
||||
}
|
||||
|
||||
return ModifiedBaseRangeState.conflicting;
|
||||
}
|
||||
|
||||
public getState(baseRange: ModifiedBaseRange): IObservable<ModifiedBaseRangeState> {
|
||||
const existingState = this.modifiedBaseRangeStateStores.get(baseRange);
|
||||
const existingState = this.modifiedBaseRangeStateStores.get().get(baseRange);
|
||||
if (!existingState) {
|
||||
throw new BugIndicatingError('object must be from this instance');
|
||||
}
|
||||
|
@ -221,240 +167,122 @@ export class MergeEditorModel extends EditorModel {
|
|||
public setState(
|
||||
baseRange: ModifiedBaseRange,
|
||||
state: ModifiedBaseRangeState,
|
||||
transaction: ITransaction | undefined
|
||||
transaction: ITransaction
|
||||
): void {
|
||||
const existingState = this.modifiedBaseRangeStateStores.get(baseRange);
|
||||
if (!this.isUpToDate.get()) {
|
||||
throw new BugIndicatingError('Cannot set state while updating');
|
||||
}
|
||||
|
||||
const existingState = this.modifiedBaseRangeStateStores.get().get(baseRange);
|
||||
if (!existingState) {
|
||||
throw new BugIndicatingError('object must be from this instance');
|
||||
}
|
||||
|
||||
|
||||
const conflictingDiffs = this.resultEdits.findTouchingDiffs(
|
||||
const conflictingDiffs = this.resultTextModelDiffs.findTouchingDiffs(
|
||||
baseRange.baseRange
|
||||
);
|
||||
if (conflictingDiffs) {
|
||||
this.resultEdits.removeDiffs(conflictingDiffs, transaction);
|
||||
this.resultTextModelDiffs.removeDiffs(conflictingDiffs, transaction);
|
||||
}
|
||||
|
||||
function getEdit(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState): { edit: LineEdit | undefined; effectiveState: ModifiedBaseRangeState } {
|
||||
interface LineDiffWithInputNumber {
|
||||
diff: LineDiff;
|
||||
inputNumber: 1 | 2;
|
||||
}
|
||||
|
||||
const diffs = new Array<LineDiffWithInputNumber>();
|
||||
if (state.input1) {
|
||||
if (baseRange.input1CombinedDiff) {
|
||||
diffs.push({ diff: baseRange.input1CombinedDiff, inputNumber: 1 });
|
||||
}
|
||||
}
|
||||
if (state.input2) {
|
||||
if (baseRange.input2CombinedDiff) {
|
||||
diffs.push({ diff: baseRange.input2CombinedDiff, inputNumber: 2 });
|
||||
}
|
||||
}
|
||||
if (state.input2First) {
|
||||
diffs.reverse();
|
||||
}
|
||||
const firstDiff: LineDiffWithInputNumber | undefined = diffs[0];
|
||||
const secondDiff: LineDiffWithInputNumber | undefined = diffs[1];
|
||||
diffs.sort(compareBy(d => d.diff.originalRange, LineRange.compareByStart));
|
||||
|
||||
if (!firstDiff) {
|
||||
return { edit: undefined, effectiveState: state };
|
||||
}
|
||||
|
||||
if (!secondDiff) {
|
||||
return { edit: firstDiff.diff.getLineEdit(), effectiveState: state };
|
||||
}
|
||||
|
||||
// Two inserts
|
||||
if (
|
||||
firstDiff.diff.originalRange.lineCount === 0 &&
|
||||
firstDiff.diff.originalRange.equals(secondDiff.diff.originalRange)
|
||||
) {
|
||||
return {
|
||||
edit: new LineEdit(
|
||||
firstDiff.diff.originalRange,
|
||||
firstDiff.diff
|
||||
.getLineEdit()
|
||||
.newLines.concat(secondDiff.diff.getLineEdit().newLines)
|
||||
),
|
||||
effectiveState: state,
|
||||
};
|
||||
}
|
||||
|
||||
// Technically non-conflicting diffs
|
||||
if (diffs.length === 2 && diffs[0].diff.originalRange.endLineNumberExclusive === diffs[1].diff.originalRange.startLineNumber) {
|
||||
return {
|
||||
edit: new LineEdit(
|
||||
LineRange.join(diffs.map(d => d.diff.originalRange))!,
|
||||
diffs.flatMap(d => d.diff.getLineEdit().newLines)
|
||||
),
|
||||
effectiveState: state,
|
||||
};
|
||||
}
|
||||
|
||||
return { edit: firstDiff.diff.getLineEdit(), effectiveState: state };
|
||||
}
|
||||
const { edit, effectiveState } = getEdit(baseRange, state);
|
||||
const { edit, effectiveState } = getEditForBase(baseRange, state);
|
||||
|
||||
existingState.set(effectiveState, transaction);
|
||||
|
||||
if (edit) {
|
||||
this.resultEdits.applyEditRelativeToOriginal(edit, transaction);
|
||||
this.resultTextModelDiffs.applyEditRelativeToOriginal(edit, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
public getResultRange(baseRange: LineRange): LineRange {
|
||||
return this.resultEdits.getResultRange(baseRange);
|
||||
}
|
||||
}
|
||||
|
||||
class ResultEdits {
|
||||
private readonly barrier = new ReentrancyBarrier();
|
||||
private readonly onDidChangeEmitter = new Emitter();
|
||||
public readonly onDidChange = this.onDidChangeEmitter.event;
|
||||
function getEditForBase(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState): { edit: LineEdit | undefined; effectiveState: ModifiedBaseRangeState } {
|
||||
interface LineDiffWithInputNumber {
|
||||
diff: LineDiff;
|
||||
inputNumber: 1 | 2;
|
||||
}
|
||||
|
||||
constructor(
|
||||
diffs: LineDiff[],
|
||||
private readonly baseTextModel: ITextModel,
|
||||
private readonly resultTextModel: ITextModel,
|
||||
private readonly _editorWorkerService: IEditorWorkerService
|
||||
const diffs = new Array<LineDiffWithInputNumber>();
|
||||
if (state.input1) {
|
||||
if (baseRange.input1CombinedDiff) {
|
||||
diffs.push({ diff: baseRange.input1CombinedDiff, inputNumber: 1 });
|
||||
}
|
||||
}
|
||||
if (state.input2) {
|
||||
if (baseRange.input2CombinedDiff) {
|
||||
diffs.push({ diff: baseRange.input2CombinedDiff, inputNumber: 2 });
|
||||
}
|
||||
}
|
||||
if (state.input2First) {
|
||||
diffs.reverse();
|
||||
}
|
||||
const firstDiff: LineDiffWithInputNumber | undefined = diffs[0];
|
||||
const secondDiff: LineDiffWithInputNumber | undefined = diffs[1];
|
||||
diffs.sort(compareBy(d => d.diff.originalRange, LineRange.compareByStart));
|
||||
|
||||
if (!firstDiff) {
|
||||
return { edit: undefined, effectiveState: ModifiedBaseRangeState.default };
|
||||
}
|
||||
|
||||
if (!secondDiff) {
|
||||
return { edit: firstDiff.diff.getLineEdit(), effectiveState: ModifiedBaseRangeState.default.withInputValue(firstDiff.inputNumber, true) };
|
||||
}
|
||||
|
||||
// Two inserts
|
||||
if (
|
||||
firstDiff.diff.originalRange.lineCount === 0 &&
|
||||
firstDiff.diff.originalRange.equals(secondDiff.diff.originalRange)
|
||||
) {
|
||||
diffs.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator));
|
||||
this._diffs.set(diffs, undefined);
|
||||
|
||||
resultTextModel.onDidChangeContent(e => {
|
||||
this.barrier.runExclusively(() => {
|
||||
this._editorWorkerService.computeDiff(
|
||||
baseTextModel.uri,
|
||||
resultTextModel.uri,
|
||||
false,
|
||||
1000
|
||||
).then(e => {
|
||||
const diffs =
|
||||
e?.changes.map((c) =>
|
||||
LineDiff.fromLineChange(c, baseTextModel, resultTextModel)
|
||||
) || [];
|
||||
this._diffs.set(diffs, undefined);
|
||||
|
||||
this.onDidChangeEmitter.fire(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
return {
|
||||
edit: new LineEdit(
|
||||
firstDiff.diff.originalRange,
|
||||
firstDiff.diff
|
||||
.getLineEdit()
|
||||
.newLines.concat(secondDiff.diff.getLineEdit().newLines)
|
||||
),
|
||||
effectiveState: state,
|
||||
};
|
||||
}
|
||||
|
||||
private readonly _diffs = new ObservableValue<LineDiff[]>([], 'diffs');
|
||||
|
||||
public readonly diffs: IObservable<readonly LineDiff[]> = this._diffs;
|
||||
|
||||
public removeDiffs(diffToRemoves: LineDiff[], transaction: ITransaction | undefined): void {
|
||||
diffToRemoves.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator));
|
||||
diffToRemoves.reverse();
|
||||
|
||||
let diffs = this._diffs.get();
|
||||
|
||||
for (const diffToRemove of diffToRemoves) {
|
||||
// TODO improve performance
|
||||
const len = diffs.length;
|
||||
diffs = diffs.filter((d) => d !== diffToRemove);
|
||||
if (len === diffs.length) {
|
||||
throw new BugIndicatingError();
|
||||
}
|
||||
|
||||
this.barrier.runExclusivelyOrThrow(() => {
|
||||
diffToRemove.getReverseLineEdit().apply(this.resultTextModel);
|
||||
});
|
||||
|
||||
diffs = diffs.map((d) =>
|
||||
d.modifiedRange.isAfter(diffToRemove.modifiedRange)
|
||||
? new LineDiff(
|
||||
d.originalTextModel,
|
||||
d.originalRange,
|
||||
d.modifiedTextModel,
|
||||
d.modifiedRange.delta(
|
||||
diffToRemove.originalRange.lineCount - diffToRemove.modifiedRange.lineCount
|
||||
)
|
||||
)
|
||||
: d
|
||||
);
|
||||
}
|
||||
|
||||
this._diffs.set(diffs, transaction);
|
||||
// Technically non-conflicting diffs
|
||||
if (diffs.length === 2 && diffs[0].diff.originalRange.endLineNumberExclusive === diffs[1].diff.originalRange.startLineNumber) {
|
||||
return {
|
||||
edit: new LineEdit(
|
||||
LineRange.join(diffs.map(d => d.diff.originalRange))!,
|
||||
diffs.flatMap(d => d.diff.getLineEdit().newLines)
|
||||
),
|
||||
effectiveState: state,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit must be conflict free.
|
||||
*/
|
||||
public applyEditRelativeToOriginal(edit: LineEdit, transaction: ITransaction | undefined): void {
|
||||
let firstAfter = false;
|
||||
let delta = 0;
|
||||
const newDiffs = new Array<LineDiff>();
|
||||
for (const diff of this._diffs.get()) {
|
||||
if (diff.originalRange.touches(edit.range)) {
|
||||
throw new BugIndicatingError('Edit must be conflict free.');
|
||||
} else if (diff.originalRange.isAfter(edit.range)) {
|
||||
if (!firstAfter) {
|
||||
firstAfter = true;
|
||||
|
||||
newDiffs.push(new LineDiff(
|
||||
this.baseTextModel,
|
||||
edit.range,
|
||||
this.resultTextModel,
|
||||
new LineRange(edit.range.startLineNumber + delta, edit.newLines.length)
|
||||
));
|
||||
}
|
||||
|
||||
newDiffs.push(new LineDiff(
|
||||
diff.originalTextModel,
|
||||
diff.originalRange,
|
||||
diff.modifiedTextModel,
|
||||
diff.modifiedRange.delta(edit.newLines.length - edit.range.lineCount)
|
||||
));
|
||||
} else {
|
||||
newDiffs.push(diff);
|
||||
}
|
||||
|
||||
if (!firstAfter) {
|
||||
delta += diff.modifiedRange.lineCount - diff.originalRange.lineCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (!firstAfter) {
|
||||
firstAfter = true;
|
||||
|
||||
newDiffs.push(new LineDiff(
|
||||
this.baseTextModel,
|
||||
edit.range,
|
||||
this.resultTextModel,
|
||||
new LineRange(edit.range.startLineNumber + delta, edit.newLines.length)
|
||||
));
|
||||
}
|
||||
|
||||
this.barrier.runExclusivelyOrThrow(() => {
|
||||
new LineEdit(edit.range.delta(delta), edit.newLines).apply(this.resultTextModel);
|
||||
});
|
||||
this._diffs.set(newDiffs, transaction);
|
||||
}
|
||||
|
||||
public findTouchingDiffs(baseRange: LineRange): LineDiff[] {
|
||||
return this.diffs.get().filter(d => d.originalRange.touches(baseRange));
|
||||
}
|
||||
|
||||
public getResultRange(baseRange: LineRange): LineRange {
|
||||
let startOffset = 0;
|
||||
let lengthOffset = 0;
|
||||
for (const diff of this.diffs.get()) {
|
||||
if (diff.originalRange.endLineNumberExclusive <= baseRange.startLineNumber) {
|
||||
startOffset += diff.resultingDeltaFromOriginalToModified;
|
||||
} else if (diff.originalRange.startLineNumber <= baseRange.endLineNumberExclusive) {
|
||||
lengthOffset += diff.resultingDeltaFromOriginalToModified;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new LineRange(baseRange.startLineNumber + startOffset, baseRange.lineCount + lengthOffset);
|
||||
}
|
||||
return {
|
||||
edit: secondDiff.diff.getLineEdit(),
|
||||
effectiveState: ModifiedBaseRangeState.default.withInputValue(
|
||||
secondDiff.inputNumber,
|
||||
true
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function computeState(baseRange: ModifiedBaseRange, conflictingDiffs: LineDiff[]): ModifiedBaseRangeState {
|
||||
if (conflictingDiffs.length === 0) {
|
||||
return ModifiedBaseRangeState.default;
|
||||
}
|
||||
const conflictingEdits = conflictingDiffs.map((d) => d.getLineEdit());
|
||||
|
||||
function editsAgreeWithDiffs(diffs: readonly LineDiff[]): boolean {
|
||||
return equals(
|
||||
conflictingEdits,
|
||||
diffs.map((d) => d.getLineEdit()),
|
||||
(a, b) => a.equals(b)
|
||||
);
|
||||
}
|
||||
|
||||
if (editsAgreeWithDiffs(baseRange.input1Diffs)) {
|
||||
return ModifiedBaseRangeState.default.withInput1(true);
|
||||
}
|
||||
if (editsAgreeWithDiffs(baseRange.input2Diffs)) {
|
||||
return ModifiedBaseRangeState.default.withInput2(true);
|
||||
}
|
||||
|
||||
return ModifiedBaseRangeState.conflicting;
|
||||
}
|
||||
|
|
229
src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts
Normal file
229
src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { compareBy, numberComparator } from 'vs/base/common/arrays';
|
||||
import { BugIndicatingError } from 'vs/base/common/errors';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker';
|
||||
import { IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable';
|
||||
import { LineDiff, LineEdit, LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model';
|
||||
import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils';
|
||||
|
||||
export class TextModelDiffs extends Disposable {
|
||||
private updateCount = 0;
|
||||
private readonly _state = new ObservableValue<TextModelDiffState, TextModelDiffChangeReason>(TextModelDiffState.initializing, 'LiveDiffState');
|
||||
private readonly _diffs = new ObservableValue<LineDiff[], TextModelDiffChangeReason>([], 'LiveDiffs');
|
||||
|
||||
private readonly barrier = new ReentrancyBarrier();
|
||||
|
||||
constructor(
|
||||
private readonly baseTextModel: ITextModel,
|
||||
private readonly textModel: ITextModel,
|
||||
private readonly diffComputer: IDiffComputer,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.update(true);
|
||||
this._register(baseTextModel.onDidChangeContent(this.barrier.makeExclusive(() => this.update())));
|
||||
this._register(textModel.onDidChangeContent(this.barrier.makeExclusive(() => this.update())));
|
||||
}
|
||||
|
||||
public get state(): IObservable<TextModelDiffState, TextModelDiffChangeReason> {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public get diffs(): IObservable<LineDiff[], TextModelDiffChangeReason> {
|
||||
return this._diffs;
|
||||
}
|
||||
|
||||
private async update(initializing: boolean = false): Promise<void> {
|
||||
this.updateCount++;
|
||||
const currentUpdateCount = this.updateCount;
|
||||
|
||||
if (this._state.get() === TextModelDiffState.initializing) {
|
||||
initializing = true;
|
||||
}
|
||||
|
||||
transaction(tx => {
|
||||
this._state.set(
|
||||
initializing ? TextModelDiffState.initializing : TextModelDiffState.updating,
|
||||
tx,
|
||||
TextModelDiffChangeReason.other
|
||||
);
|
||||
});
|
||||
|
||||
const result = await this.diffComputer.computeDiff(this.baseTextModel, this.textModel);
|
||||
|
||||
if (currentUpdateCount !== this.updateCount) {
|
||||
// There is a newer update call
|
||||
return;
|
||||
}
|
||||
|
||||
transaction(tx => {
|
||||
if (result) {
|
||||
this._state.set(TextModelDiffState.upToDate, tx, TextModelDiffChangeReason.textChange);
|
||||
this._diffs.set(result, tx, TextModelDiffChangeReason.textChange);
|
||||
} else {
|
||||
this._state.set(TextModelDiffState.error, tx, TextModelDiffChangeReason.textChange);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ensureUpToDate(): void {
|
||||
if (this.state.get() !== TextModelDiffState.upToDate) {
|
||||
throw new BugIndicatingError('Cannot remove diffs when the model is not up to date');
|
||||
}
|
||||
}
|
||||
|
||||
public removeDiffs(diffToRemoves: LineDiff[], transaction: ITransaction | undefined): void {
|
||||
this.ensureUpToDate();
|
||||
|
||||
diffToRemoves.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator));
|
||||
diffToRemoves.reverse();
|
||||
|
||||
let diffs = this._diffs.get();
|
||||
|
||||
for (const diffToRemove of diffToRemoves) {
|
||||
// TODO improve performance
|
||||
const len = diffs.length;
|
||||
diffs = diffs.filter((d) => d !== diffToRemove);
|
||||
if (len === diffs.length) {
|
||||
throw new BugIndicatingError();
|
||||
}
|
||||
|
||||
this.barrier.runExclusivelyOrThrow(() => {
|
||||
diffToRemove.getReverseLineEdit().apply(this.textModel);
|
||||
});
|
||||
|
||||
diffs = diffs.map((d) =>
|
||||
d.modifiedRange.isAfter(diffToRemove.modifiedRange)
|
||||
? new LineDiff(
|
||||
d.originalTextModel,
|
||||
d.originalRange,
|
||||
d.modifiedTextModel,
|
||||
d.modifiedRange.delta(
|
||||
diffToRemove.originalRange.lineCount - diffToRemove.modifiedRange.lineCount
|
||||
)
|
||||
)
|
||||
: d
|
||||
);
|
||||
}
|
||||
|
||||
this._diffs.set(diffs, transaction, TextModelDiffChangeReason.other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit must be conflict free.
|
||||
*/
|
||||
public applyEditRelativeToOriginal(edit: LineEdit, transaction: ITransaction | undefined): void {
|
||||
this.ensureUpToDate();
|
||||
|
||||
let firstAfter = false;
|
||||
let delta = 0;
|
||||
const newDiffs = new Array<LineDiff>();
|
||||
for (const diff of this.diffs.get()) {
|
||||
if (diff.originalRange.touches(edit.range)) {
|
||||
throw new BugIndicatingError('Edit must be conflict free.');
|
||||
} else if (diff.originalRange.isAfter(edit.range)) {
|
||||
if (!firstAfter) {
|
||||
firstAfter = true;
|
||||
|
||||
newDiffs.push(new LineDiff(
|
||||
this.baseTextModel,
|
||||
edit.range,
|
||||
this.textModel,
|
||||
new LineRange(edit.range.startLineNumber + delta, edit.newLines.length)
|
||||
));
|
||||
}
|
||||
|
||||
newDiffs.push(new LineDiff(
|
||||
diff.originalTextModel,
|
||||
diff.originalRange,
|
||||
diff.modifiedTextModel,
|
||||
diff.modifiedRange.delta(edit.newLines.length - edit.range.lineCount)
|
||||
));
|
||||
} else {
|
||||
newDiffs.push(diff);
|
||||
}
|
||||
|
||||
if (!firstAfter) {
|
||||
delta += diff.modifiedRange.lineCount - diff.originalRange.lineCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (!firstAfter) {
|
||||
firstAfter = true;
|
||||
|
||||
newDiffs.push(new LineDiff(
|
||||
this.baseTextModel,
|
||||
edit.range,
|
||||
this.textModel,
|
||||
new LineRange(edit.range.startLineNumber + delta, edit.newLines.length)
|
||||
));
|
||||
}
|
||||
|
||||
this.barrier.runExclusivelyOrThrow(() => {
|
||||
new LineEdit(edit.range.delta(delta), edit.newLines).apply(this.textModel);
|
||||
});
|
||||
this._diffs.set(newDiffs, transaction, TextModelDiffChangeReason.other);
|
||||
}
|
||||
|
||||
public findTouchingDiffs(baseRange: LineRange): LineDiff[] {
|
||||
return this.diffs.get().filter(d => d.originalRange.touches(baseRange));
|
||||
}
|
||||
|
||||
/*
|
||||
public getResultRange(baseRange: LineRange): LineRange {
|
||||
let startOffset = 0;
|
||||
let lengthOffset = 0;
|
||||
for (const diff of this.diffs.get()) {
|
||||
if (diff.originalRange.endLineNumberExclusive <= baseRange.startLineNumber) {
|
||||
startOffset += diff.resultingDeltaFromOriginalToModified;
|
||||
} else if (diff.originalRange.startLineNumber <= baseRange.endLineNumberExclusive) {
|
||||
lengthOffset += diff.resultingDeltaFromOriginalToModified;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new LineRange(baseRange.startLineNumber + startOffset, baseRange.lineCount + lengthOffset);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
export const enum TextModelDiffChangeReason {
|
||||
other = 0,
|
||||
textChange = 1,
|
||||
}
|
||||
|
||||
export const enum TextModelDiffState {
|
||||
initializing = 1,
|
||||
upToDate = 2,
|
||||
updating = 3,
|
||||
error = 4,
|
||||
}
|
||||
|
||||
export interface ITextModelDiffsState {
|
||||
state: TextModelDiffState;
|
||||
diffs: LineDiff[];
|
||||
}
|
||||
|
||||
export interface IDiffComputer {
|
||||
computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise<LineDiff[] | null>;
|
||||
}
|
||||
|
||||
export class EditorWorkerServiceDiffComputer implements IDiffComputer {
|
||||
constructor(@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService) { }
|
||||
|
||||
async computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise<LineDiff[] | null> {
|
||||
//await wait(1000);
|
||||
const diffs = await this.editorWorkerService.computeDiff(textModel1.uri, textModel2.uri, false, 1000);
|
||||
if (!diffs || diffs.quitEarly) {
|
||||
return null;
|
||||
}
|
||||
return diffs.changes.map((c) => LineDiff.fromLineChange(c, textModel1, textModel2));
|
||||
}
|
||||
}
|
|
@ -544,6 +544,8 @@ registerAction2(class RevealRunningCellAction extends NotebookAction {
|
|||
super({
|
||||
id: REVEAL_RUNNING_CELL,
|
||||
title: localize('revealRunningCell', "Go To Running Cell"),
|
||||
tooltip: localize('revealRunningCell', "Go To Running Cell"),
|
||||
shortTitle: localize('revealRunningCellShort', "Go To"),
|
||||
precondition: NOTEBOOK_HAS_RUNNING_CELL,
|
||||
menu: [
|
||||
{
|
||||
|
@ -587,7 +589,7 @@ registerAction2(class RevealRunningCellAction extends NotebookAction {
|
|||
if (executingCells[0]) {
|
||||
const cell = context.notebookEditor.getCellByHandle(executingCells[0].cellHandle);
|
||||
if (cell) {
|
||||
context.notebookEditor.revealInCenter(cell);
|
||||
context.notebookEditor.focusNotebookCell(cell, 'container');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1459,11 +1459,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
}
|
||||
hasPendingChangeContentHeight = true;
|
||||
|
||||
DOM.scheduleAtNextAnimationFrame(() => {
|
||||
this._localStore.add(DOM.scheduleAtNextAnimationFrame(() => {
|
||||
hasPendingChangeContentHeight = false;
|
||||
this._updateScrollHeight();
|
||||
this._onDidChangeContentHeight.fire(this._list.getScrollHeight());
|
||||
}, 100);
|
||||
}, 100));
|
||||
}));
|
||||
|
||||
this._localStore.add(this._list.onDidRemoveOutputs(outputs => {
|
||||
|
|
|
@ -217,7 +217,7 @@ function updateToEdit(update: ICellExecuteUpdate, cellHandle: number): ICellEdit
|
|||
if (update.editType === CellExecutionUpdateType.Output) {
|
||||
return {
|
||||
editType: CellEditType.Output,
|
||||
handle: cellHandle,
|
||||
handle: update.cellHandle,
|
||||
append: update.append,
|
||||
outputs: update.outputs,
|
||||
};
|
||||
|
|
|
@ -164,9 +164,7 @@ export class CellTitleToolbarPart extends CellPart {
|
|||
|
||||
if (deferredUpdate && !visible) {
|
||||
this._register(disposableTimeout(() => {
|
||||
if (deferredUpdate) {
|
||||
deferredUpdate();
|
||||
}
|
||||
deferredUpdate?.();
|
||||
}));
|
||||
|
||||
deferredUpdate = undefined;
|
||||
|
|
|
@ -405,9 +405,7 @@ export class NotebookEditorToolbar extends Disposable {
|
|||
|
||||
if (deferredUpdate && !visible) {
|
||||
setTimeout(() => {
|
||||
if (deferredUpdate) {
|
||||
deferredUpdate();
|
||||
}
|
||||
deferredUpdate?.();
|
||||
}, 0);
|
||||
deferredUpdate = undefined;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ export enum CellExecutionUpdateType {
|
|||
|
||||
export interface ICellExecuteOutputEdit {
|
||||
editType: CellExecutionUpdateType.Output;
|
||||
cellHandle: number;
|
||||
append?: boolean;
|
||||
outputs: IOutputDto[];
|
||||
}
|
||||
|
|
|
@ -1166,9 +1166,7 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr
|
|||
common.toDispose.add(
|
||||
listWidget.onDidChangeList(e => {
|
||||
const newList = this.computeNewList(template, e);
|
||||
if (template.onChange) {
|
||||
template.onChange(newList);
|
||||
}
|
||||
template.onChange?.(newList);
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -1349,9 +1347,7 @@ abstract class AbstractSettingObjectRenderer extends AbstractSettingRenderer imp
|
|||
template.objectDropdownWidget!.setValue(newItems);
|
||||
}
|
||||
|
||||
if (template.onChange) {
|
||||
template.onChange(newValue);
|
||||
}
|
||||
template.onChange?.(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1539,9 +1535,7 @@ abstract class AbstractSettingTextRenderer extends AbstractSettingRenderer imple
|
|||
}));
|
||||
common.toDispose.add(
|
||||
inputBox.onDidChange(e => {
|
||||
if (template.onChange) {
|
||||
template.onChange(e);
|
||||
}
|
||||
template.onChange?.(e);
|
||||
}));
|
||||
common.toDispose.add(inputBox);
|
||||
inputBox.inputElement.classList.add(AbstractSettingRenderer.CONTROL_CLASS);
|
||||
|
@ -1652,9 +1646,7 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre
|
|||
|
||||
common.toDispose.add(
|
||||
selectBox.onDidSelect(e => {
|
||||
if (template.onChange) {
|
||||
template.onChange(e.index);
|
||||
}
|
||||
template.onChange?.(e.index);
|
||||
}));
|
||||
|
||||
const enumDescriptionElement = common.containerElement.insertBefore($('.setting-item-enumDescription'), common.descriptionElement.nextSibling);
|
||||
|
@ -1758,9 +1750,7 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT
|
|||
}));
|
||||
common.toDispose.add(
|
||||
inputBox.onDidChange(e => {
|
||||
if (template.onChange) {
|
||||
template.onChange(e);
|
||||
}
|
||||
template.onChange?.(e);
|
||||
}));
|
||||
common.toDispose.add(inputBox);
|
||||
inputBox.inputElement.classList.add(AbstractSettingRenderer.CONTROL_CLASS);
|
||||
|
|
|
@ -1077,9 +1077,7 @@ export class SearchModel extends Disposable {
|
|||
progressEmitter.fire();
|
||||
this.onSearchProgress(p);
|
||||
|
||||
if (onProgress) {
|
||||
onProgress(p);
|
||||
}
|
||||
onProgress?.(p);
|
||||
});
|
||||
|
||||
const dispose = () => tokenSource.dispose();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -26,40 +26,40 @@ const ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE = 'tasks.run.allowAutomatic';
|
|||
|
||||
export class RunAutomaticTasks extends Disposable implements IWorkbenchContribution {
|
||||
constructor(
|
||||
@ITaskService private readonly taskService: ITaskService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService,
|
||||
@ILogService private readonly logService: ILogService) {
|
||||
@ITaskService private readonly _taskService: ITaskService,
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,
|
||||
@ILogService private readonly _logService: ILogService) {
|
||||
super();
|
||||
this.tryRunTasks();
|
||||
this._tryRunTasks();
|
||||
}
|
||||
|
||||
private async tryRunTasks() {
|
||||
this.logService.trace('RunAutomaticTasks: Trying to run tasks.');
|
||||
private async _tryRunTasks() {
|
||||
this._logService.trace('RunAutomaticTasks: Trying to run tasks.');
|
||||
// Wait until we have task system info (the extension host and workspace folders are available).
|
||||
if (!this.taskService.hasTaskSystemInfo) {
|
||||
this.logService.trace('RunAutomaticTasks: Awaiting task system info.');
|
||||
await Event.toPromise(Event.once(this.taskService.onDidChangeTaskSystemInfo));
|
||||
if (!this._taskService.hasTaskSystemInfo) {
|
||||
this._logService.trace('RunAutomaticTasks: Awaiting task system info.');
|
||||
await Event.toPromise(Event.once(this._taskService.onDidChangeTaskSystemInfo));
|
||||
}
|
||||
|
||||
this.logService.trace('RunAutomaticTasks: Checking if automatic tasks should run.');
|
||||
const isFolderAutomaticAllowed = this.storageService.getBoolean(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, StorageScope.WORKSPACE, undefined);
|
||||
await this.workspaceTrustManagementService.workspaceTrustInitialized;
|
||||
const isWorkspaceTrusted = this.workspaceTrustManagementService.isWorkspaceTrusted();
|
||||
this._logService.trace('RunAutomaticTasks: Checking if automatic tasks should run.');
|
||||
const isFolderAutomaticAllowed = this._storageService.getBoolean(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, StorageScope.WORKSPACE, undefined);
|
||||
await this._workspaceTrustManagementService.workspaceTrustInitialized;
|
||||
const isWorkspaceTrusted = this._workspaceTrustManagementService.isWorkspaceTrusted();
|
||||
// Only run if allowed. Prompting for permission occurs when a user first tries to run a task.
|
||||
if (isFolderAutomaticAllowed && isWorkspaceTrusted) {
|
||||
this.taskService.getWorkspaceTasks(TaskRunSource.FolderOpen).then(workspaceTaskResult => {
|
||||
const { tasks } = RunAutomaticTasks.findAutoTasks(this.taskService, workspaceTaskResult);
|
||||
this.logService.trace(`RunAutomaticTasks: Found ${tasks.length} automatic tasks tasks`);
|
||||
this._taskService.getWorkspaceTasks(TaskRunSource.FolderOpen).then(workspaceTaskResult => {
|
||||
const { tasks } = RunAutomaticTasks._findAutoTasks(this._taskService, workspaceTaskResult);
|
||||
this._logService.trace(`RunAutomaticTasks: Found ${tasks.length} automatic tasks tasks`);
|
||||
|
||||
if (tasks.length > 0) {
|
||||
RunAutomaticTasks.runTasks(this.taskService, tasks);
|
||||
RunAutomaticTasks._runTasks(this._taskService, tasks);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static runTasks(taskService: ITaskService, tasks: Array<Task | Promise<Task | undefined>>) {
|
||||
private static _runTasks(taskService: ITaskService, tasks: Array<Task | Promise<Task | undefined>>) {
|
||||
tasks.forEach(task => {
|
||||
if (task instanceof Promise) {
|
||||
task.then(promiseResult => {
|
||||
|
@ -73,7 +73,7 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut
|
|||
});
|
||||
}
|
||||
|
||||
private static getTaskSource(source: TaskSource): URI | undefined {
|
||||
private static _getTaskSource(source: TaskSource): URI | undefined {
|
||||
const taskKind = TaskSourceKind.toConfigurationTarget(source.kind);
|
||||
switch (taskKind) {
|
||||
case ConfigurationTarget.WORKSPACE_FOLDER: {
|
||||
|
@ -86,7 +86,7 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut
|
|||
return undefined;
|
||||
}
|
||||
|
||||
private static findAutoTasks(taskService: ITaskService, workspaceTaskResult: Map<string, IWorkspaceFolderTaskResult>): { tasks: Array<Task | Promise<Task | undefined>>; taskNames: Array<string>; locations: Map<string, URI> } {
|
||||
private static _findAutoTasks(taskService: ITaskService, workspaceTaskResult: Map<string, IWorkspaceFolderTaskResult>): { tasks: Array<Task | Promise<Task | undefined>>; taskNames: Array<string>; locations: Map<string, URI> } {
|
||||
const tasks = new Array<Task | Promise<Task | undefined>>();
|
||||
const taskNames = new Array<string>();
|
||||
const locations = new Map<string, URI>();
|
||||
|
@ -98,7 +98,7 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut
|
|||
if (task.runOptions.runOn === RunOnOptions.folderOpen) {
|
||||
tasks.push(task);
|
||||
taskNames.push(task._label);
|
||||
const location = RunAutomaticTasks.getTaskSource(task._source);
|
||||
const location = RunAutomaticTasks._getTaskSource(task._source);
|
||||
if (location) {
|
||||
locations.set(location.fsPath, location);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut
|
|||
} else {
|
||||
taskNames.push(configedTask.value.configures.task);
|
||||
}
|
||||
const location = RunAutomaticTasks.getTaskSource(configedTask.value._source);
|
||||
const location = RunAutomaticTasks._getTaskSource(configedTask.value._source);
|
||||
if (location) {
|
||||
locations.set(location.fsPath, location);
|
||||
}
|
||||
|
@ -140,18 +140,18 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut
|
|||
return;
|
||||
}
|
||||
|
||||
const { tasks, taskNames, locations } = RunAutomaticTasks.findAutoTasks(taskService, workspaceTaskResult);
|
||||
const { tasks, taskNames, locations } = RunAutomaticTasks._findAutoTasks(taskService, workspaceTaskResult);
|
||||
if (taskNames.length > 0) {
|
||||
// We have automatic tasks, prompt to allow.
|
||||
this.showPrompt(notificationService, storageService, taskService, openerService, taskNames, locations).then(allow => {
|
||||
this._showPrompt(notificationService, storageService, taskService, openerService, taskNames, locations).then(allow => {
|
||||
if (allow) {
|
||||
RunAutomaticTasks.runTasks(taskService, tasks);
|
||||
RunAutomaticTasks._runTasks(taskService, tasks);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static showPrompt(notificationService: INotificationService, storageService: IStorageService, taskService: ITaskService,
|
||||
private static _showPrompt(notificationService: INotificationService, storageService: IStorageService, taskService: ITaskService,
|
||||
openerService: IOpenerService, taskNames: Array<string>, locations: Map<string, URI>): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
notificationService.prompt(Severity.Info, nls.localize('tasks.run.allowAutomatic',
|
||||
|
|
|
@ -56,31 +56,31 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
|||
});
|
||||
|
||||
export class TaskStatusBarContributions extends Disposable implements IWorkbenchContribution {
|
||||
private runningTasksStatusItem: IStatusbarEntryAccessor | undefined;
|
||||
private activeTasksCount: number = 0;
|
||||
private _runningTasksStatusItem: IStatusbarEntryAccessor | undefined;
|
||||
private _activeTasksCount: number = 0;
|
||||
|
||||
constructor(
|
||||
@ITaskService private readonly taskService: ITaskService,
|
||||
@IStatusbarService private readonly statusbarService: IStatusbarService,
|
||||
@IProgressService private readonly progressService: IProgressService
|
||||
@ITaskService private readonly _taskService: ITaskService,
|
||||
@IStatusbarService private readonly _statusbarService: IStatusbarService,
|
||||
@IProgressService private readonly _progressService: IProgressService
|
||||
) {
|
||||
super();
|
||||
this.registerListeners();
|
||||
this._registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
private _registerListeners(): void {
|
||||
let promise: Promise<void> | undefined = undefined;
|
||||
let resolver: (value?: void | Thenable<void>) => void;
|
||||
this.taskService.onDidStateChange(event => {
|
||||
this._taskService.onDidStateChange(event => {
|
||||
if (event.kind === TaskEventKind.Changed) {
|
||||
this.updateRunningTasksStatus();
|
||||
this._updateRunningTasksStatus();
|
||||
}
|
||||
|
||||
if (!this.ignoreEventForUpdateRunningTasksCount(event)) {
|
||||
if (!this._ignoreEventForUpdateRunningTasksCount(event)) {
|
||||
switch (event.kind) {
|
||||
case TaskEventKind.Active:
|
||||
this.activeTasksCount++;
|
||||
if (this.activeTasksCount === 1) {
|
||||
this._activeTasksCount++;
|
||||
if (this._activeTasksCount === 1) {
|
||||
if (!promise) {
|
||||
promise = new Promise<void>((resolve) => {
|
||||
resolver = resolve;
|
||||
|
@ -91,9 +91,9 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench
|
|||
case TaskEventKind.Inactive:
|
||||
// Since the exiting of the sub process is communicated async we can't order inactive and terminate events.
|
||||
// So try to treat them accordingly.
|
||||
if (this.activeTasksCount > 0) {
|
||||
this.activeTasksCount--;
|
||||
if (this.activeTasksCount === 0) {
|
||||
if (this._activeTasksCount > 0) {
|
||||
this._activeTasksCount--;
|
||||
if (this._activeTasksCount === 0) {
|
||||
if (promise && resolver!) {
|
||||
resolver!();
|
||||
}
|
||||
|
@ -101,8 +101,8 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench
|
|||
}
|
||||
break;
|
||||
case TaskEventKind.Terminated:
|
||||
if (this.activeTasksCount !== 0) {
|
||||
this.activeTasksCount = 0;
|
||||
if (this._activeTasksCount !== 0) {
|
||||
this._activeTasksCount = 0;
|
||||
if (promise && resolver!) {
|
||||
resolver!();
|
||||
}
|
||||
|
@ -111,8 +111,8 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench
|
|||
}
|
||||
}
|
||||
|
||||
if (promise && (event.kind === TaskEventKind.Active) && (this.activeTasksCount === 1)) {
|
||||
this.progressService.withProgress({ location: ProgressLocation.Window, command: 'workbench.action.tasks.showTasks' }, progress => {
|
||||
if (promise && (event.kind === TaskEventKind.Active) && (this._activeTasksCount === 1)) {
|
||||
this._progressService.withProgress({ location: ProgressLocation.Window, command: 'workbench.action.tasks.showTasks' }, progress => {
|
||||
progress.report({ message: nls.localize('building', 'Building...') });
|
||||
return promise!;
|
||||
}).then(() => {
|
||||
|
@ -122,12 +122,12 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench
|
|||
});
|
||||
}
|
||||
|
||||
private async updateRunningTasksStatus(): Promise<void> {
|
||||
const tasks = await this.taskService.getActiveTasks();
|
||||
private async _updateRunningTasksStatus(): Promise<void> {
|
||||
const tasks = await this._taskService.getActiveTasks();
|
||||
if (tasks.length === 0) {
|
||||
if (this.runningTasksStatusItem) {
|
||||
this.runningTasksStatusItem.dispose();
|
||||
this.runningTasksStatusItem = undefined;
|
||||
if (this._runningTasksStatusItem) {
|
||||
this._runningTasksStatusItem.dispose();
|
||||
this._runningTasksStatusItem = undefined;
|
||||
}
|
||||
} else {
|
||||
const itemProps: IStatusbarEntry = {
|
||||
|
@ -138,16 +138,16 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench
|
|||
command: 'workbench.action.tasks.showTasks',
|
||||
};
|
||||
|
||||
if (!this.runningTasksStatusItem) {
|
||||
this.runningTasksStatusItem = this.statusbarService.addEntry(itemProps, 'status.runningTasks', StatusbarAlignment.LEFT, 49 /* Medium Priority, next to Markers */);
|
||||
if (!this._runningTasksStatusItem) {
|
||||
this._runningTasksStatusItem = this._statusbarService.addEntry(itemProps, 'status.runningTasks', StatusbarAlignment.LEFT, 49 /* Medium Priority, next to Markers */);
|
||||
} else {
|
||||
this.runningTasksStatusItem.update(itemProps);
|
||||
this._runningTasksStatusItem.update(itemProps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ignoreEventForUpdateRunningTasksCount(event: ITaskEvent): boolean {
|
||||
if (!this.taskService.inTerminal()) {
|
||||
private _ignoreEventForUpdateRunningTasksCount(event: ITaskEvent): boolean {
|
||||
if (!this._taskService.inTerminal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,24 +42,24 @@ export const configureTaskIcon = registerIcon('tasks-list-configure', Codicon.ge
|
|||
const removeTaskIcon = registerIcon('tasks-remove', Codicon.close, nls.localize('removeTaskIcon', 'Icon for remove in the tasks selection list.'));
|
||||
|
||||
export class TaskQuickPick extends Disposable {
|
||||
private sorter: TaskSorter;
|
||||
private topLevelEntries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[] | undefined;
|
||||
private _sorter: TaskSorter;
|
||||
private _topLevelEntries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[] | undefined;
|
||||
constructor(
|
||||
private taskService: ITaskService,
|
||||
private configurationService: IConfigurationService,
|
||||
private quickInputService: IQuickInputService,
|
||||
private notificationService: INotificationService,
|
||||
private dialogService: IDialogService) {
|
||||
private _taskService: ITaskService,
|
||||
private _configurationService: IConfigurationService,
|
||||
private _quickInputService: IQuickInputService,
|
||||
private _notificationService: INotificationService,
|
||||
private _dialogService: IDialogService) {
|
||||
super();
|
||||
this.sorter = this.taskService.createSorter();
|
||||
this._sorter = this._taskService.createSorter();
|
||||
}
|
||||
|
||||
private showDetail(): boolean {
|
||||
private _showDetail(): boolean {
|
||||
// Ensure invalid values get converted into boolean values
|
||||
return !!this.configurationService.getValue(QUICKOPEN_DETAIL_CONFIG);
|
||||
return !!this._configurationService.getValue(QUICKOPEN_DETAIL_CONFIG);
|
||||
}
|
||||
|
||||
private guessTaskLabel(task: Task | ConfiguringTask): string {
|
||||
private _guessTaskLabel(task: Task | ConfiguringTask): string {
|
||||
if (task._label) {
|
||||
return task._label;
|
||||
}
|
||||
|
@ -74,21 +74,21 @@ export class TaskQuickPick extends Disposable {
|
|||
return '';
|
||||
}
|
||||
|
||||
private createTaskEntry(task: Task | ConfiguringTask, extraButtons: IQuickInputButton[] = []): ITaskTwoLevelQuickPickEntry {
|
||||
const entry: ITaskTwoLevelQuickPickEntry = { label: this.guessTaskLabel(task), description: this.taskService.getTaskDescription(task), task, detail: this.showDetail() ? task.configurationProperties.detail : undefined };
|
||||
private _createTaskEntry(task: Task | ConfiguringTask, extraButtons: IQuickInputButton[] = []): ITaskTwoLevelQuickPickEntry {
|
||||
const entry: ITaskTwoLevelQuickPickEntry = { label: this._guessTaskLabel(task), description: this._taskService.getTaskDescription(task), task, detail: this._showDetail() ? task.configurationProperties.detail : undefined };
|
||||
entry.buttons = [{ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") }, ...extraButtons];
|
||||
return entry;
|
||||
}
|
||||
|
||||
private createEntriesForGroup(entries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[], tasks: (Task | ConfiguringTask)[],
|
||||
private _createEntriesForGroup(entries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[], tasks: (Task | ConfiguringTask)[],
|
||||
groupLabel: string, extraButtons: IQuickInputButton[] = []) {
|
||||
entries.push({ type: 'separator', label: groupLabel });
|
||||
tasks.forEach(task => {
|
||||
entries.push(this.createTaskEntry(task, extraButtons));
|
||||
entries.push(this._createTaskEntry(task, extraButtons));
|
||||
});
|
||||
}
|
||||
|
||||
private createTypeEntries(entries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[], types: string[]) {
|
||||
private _createTypeEntries(entries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[], types: string[]) {
|
||||
entries.push({ type: 'separator', label: nls.localize('contributedTasks', "contributed") });
|
||||
types.forEach(type => {
|
||||
entries.push({ label: `$(folder) ${type}`, task: type, ariaLabel: nls.localize('taskType', "All {0} tasks", type) });
|
||||
|
@ -96,7 +96,7 @@ export class TaskQuickPick extends Disposable {
|
|||
entries.push({ label: SHOW_ALL, task: SHOW_ALL, alwaysShow: true });
|
||||
}
|
||||
|
||||
private handleFolderTaskResult(result: Map<string, IWorkspaceFolderTaskResult>): (Task | ConfiguringTask)[] {
|
||||
private _handleFolderTaskResult(result: Map<string, IWorkspaceFolderTaskResult>): (Task | ConfiguringTask)[] {
|
||||
const tasks: (Task | ConfiguringTask)[] = [];
|
||||
Array.from(result).forEach(([key, folderTasks]) => {
|
||||
if (folderTasks.set) {
|
||||
|
@ -111,7 +111,7 @@ export class TaskQuickPick extends Disposable {
|
|||
return tasks;
|
||||
}
|
||||
|
||||
private dedupeConfiguredAndRecent(recentTasks: (Task | ConfiguringTask)[], configuredTasks: (Task | ConfiguringTask)[]): { configuredTasks: (Task | ConfiguringTask)[]; recentTasks: (Task | ConfiguringTask)[] } {
|
||||
private _dedupeConfiguredAndRecent(recentTasks: (Task | ConfiguringTask)[], configuredTasks: (Task | ConfiguringTask)[]): { configuredTasks: (Task | ConfiguringTask)[]; recentTasks: (Task | ConfiguringTask)[] } {
|
||||
let dedupedConfiguredTasks: (Task | ConfiguringTask)[] = [];
|
||||
const foundRecentTasks: boolean[] = Array(recentTasks.length).fill(false);
|
||||
for (let j = 0; j < configuredTasks.length; j++) {
|
||||
|
@ -132,7 +132,7 @@ export class TaskQuickPick extends Disposable {
|
|||
foundRecentTasks[findIndex] = true;
|
||||
}
|
||||
}
|
||||
dedupedConfiguredTasks = dedupedConfiguredTasks.sort((a, b) => this.sorter.compare(a, b));
|
||||
dedupedConfiguredTasks = dedupedConfiguredTasks.sort((a, b) => this._sorter.compare(a, b));
|
||||
const prunedRecentTasks: (Task | ConfiguringTask)[] = [];
|
||||
for (let i = 0; i < recentTasks.length; i++) {
|
||||
if (foundRecentTasks[i] || ConfiguringTask.is(recentTasks[i])) {
|
||||
|
@ -143,15 +143,15 @@ export class TaskQuickPick extends Disposable {
|
|||
}
|
||||
|
||||
public async getTopLevelEntries(defaultEntry?: ITaskQuickPickEntry): Promise<{ entries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[]; isSingleConfigured?: Task | ConfiguringTask }> {
|
||||
if (this.topLevelEntries !== undefined) {
|
||||
return { entries: this.topLevelEntries };
|
||||
if (this._topLevelEntries !== undefined) {
|
||||
return { entries: this._topLevelEntries };
|
||||
}
|
||||
let recentTasks: (Task | ConfiguringTask)[] = (await this.taskService.readRecentTasks()).reverse();
|
||||
const configuredTasks: (Task | ConfiguringTask)[] = this.handleFolderTaskResult(await this.taskService.getWorkspaceTasks());
|
||||
const extensionTaskTypes = this.taskService.taskTypes();
|
||||
this.topLevelEntries = [];
|
||||
let recentTasks: (Task | ConfiguringTask)[] = (await this._taskService.readRecentTasks()).reverse();
|
||||
const configuredTasks: (Task | ConfiguringTask)[] = this._handleFolderTaskResult(await this._taskService.getWorkspaceTasks());
|
||||
const extensionTaskTypes = this._taskService.taskTypes();
|
||||
this._topLevelEntries = [];
|
||||
// Dedupe will update recent tasks if they've changed in tasks.json.
|
||||
const dedupeAndPrune = this.dedupeConfiguredAndRecent(recentTasks, configuredTasks);
|
||||
const dedupeAndPrune = this._dedupeConfiguredAndRecent(recentTasks, configuredTasks);
|
||||
const dedupedConfiguredTasks: (Task | ConfiguringTask)[] = dedupeAndPrune.configuredTasks;
|
||||
recentTasks = dedupeAndPrune.recentTasks;
|
||||
if (recentTasks.length > 0) {
|
||||
|
@ -159,34 +159,34 @@ export class TaskQuickPick extends Disposable {
|
|||
iconClass: ThemeIcon.asClassName(removeTaskIcon),
|
||||
tooltip: nls.localize('removeRecent', 'Remove Recently Used Task')
|
||||
};
|
||||
this.createEntriesForGroup(this.topLevelEntries, recentTasks, nls.localize('recentlyUsed', 'recently used'), [removeRecentButton]);
|
||||
this._createEntriesForGroup(this._topLevelEntries, recentTasks, nls.localize('recentlyUsed', 'recently used'), [removeRecentButton]);
|
||||
}
|
||||
if (configuredTasks.length > 0) {
|
||||
if (dedupedConfiguredTasks.length > 0) {
|
||||
this.createEntriesForGroup(this.topLevelEntries, dedupedConfiguredTasks, nls.localize('configured', 'configured'));
|
||||
this._createEntriesForGroup(this._topLevelEntries, dedupedConfiguredTasks, nls.localize('configured', 'configured'));
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultEntry && (configuredTasks.length === 0)) {
|
||||
this.topLevelEntries.push({ type: 'separator', label: nls.localize('configured', 'configured') });
|
||||
this.topLevelEntries.push(defaultEntry);
|
||||
this._topLevelEntries.push({ type: 'separator', label: nls.localize('configured', 'configured') });
|
||||
this._topLevelEntries.push(defaultEntry);
|
||||
}
|
||||
|
||||
if (extensionTaskTypes.length > 0) {
|
||||
this.createTypeEntries(this.topLevelEntries, extensionTaskTypes);
|
||||
this._createTypeEntries(this._topLevelEntries, extensionTaskTypes);
|
||||
}
|
||||
return { entries: this.topLevelEntries, isSingleConfigured: configuredTasks.length === 1 ? configuredTasks[0] : undefined };
|
||||
return { entries: this._topLevelEntries, isSingleConfigured: configuredTasks.length === 1 ? configuredTasks[0] : undefined };
|
||||
}
|
||||
|
||||
public async handleSettingOption(selectedType: string) {
|
||||
const noButton = nls.localize('TaskQuickPick.changeSettingNo', "No");
|
||||
const yesButton = nls.localize('TaskQuickPick.changeSettingYes', "Yes");
|
||||
const changeSettingResult = await this.dialogService.show(Severity.Warning,
|
||||
const changeSettingResult = await this._dialogService.show(Severity.Warning,
|
||||
nls.localize('TaskQuickPick.changeSettingDetails',
|
||||
"Task detection for {0} tasks causes files in any workspace you open to be run as code. Enabling {0} task detection is a user setting and will apply to any workspace you open. Do you want to enable {0} task detection for all workspaces?", selectedType),
|
||||
[noButton, yesButton]);
|
||||
if (changeSettingResult.choice === 1) {
|
||||
await this.configurationService.updateValue(`${selectedType}.autoDetect`, 'on');
|
||||
await this._configurationService.updateValue(`${selectedType}.autoDetect`, 'on');
|
||||
await new Promise<void>(resolve => setTimeout(() => resolve(), 100));
|
||||
return this.show(nls.localize('TaskService.pickRunTask', 'Select the task to run'), undefined, selectedType);
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ export class TaskQuickPick extends Disposable {
|
|||
}
|
||||
|
||||
public async show(placeHolder: string, defaultEntry?: ITaskQuickPickEntry, startAtType?: string): Promise<Task | undefined | null> {
|
||||
const picker: IQuickPick<ITaskTwoLevelQuickPickEntry> = this.quickInputService.createQuickPick();
|
||||
const picker: IQuickPick<ITaskTwoLevelQuickPickEntry> = this._quickInputService.createQuickPick();
|
||||
picker.placeholder = placeHolder;
|
||||
picker.matchOnDescription = true;
|
||||
picker.ignoreFocusOut = false;
|
||||
|
@ -205,25 +205,25 @@ export class TaskQuickPick extends Disposable {
|
|||
if (context.button.iconClass === ThemeIcon.asClassName(removeTaskIcon)) {
|
||||
const key = (task && !Types.isString(task)) ? task.getRecentlyUsedKey() : undefined;
|
||||
if (key) {
|
||||
this.taskService.removeRecentlyUsedTask(key);
|
||||
this._taskService.removeRecentlyUsedTask(key);
|
||||
}
|
||||
const indexToRemove = picker.items.indexOf(context.item);
|
||||
if (indexToRemove >= 0) {
|
||||
picker.items = [...picker.items.slice(0, indexToRemove), ...picker.items.slice(indexToRemove + 1)];
|
||||
}
|
||||
} else {
|
||||
this.quickInputService.cancel();
|
||||
this._quickInputService.cancel();
|
||||
if (ContributedTask.is(task)) {
|
||||
this.taskService.customize(task, undefined, true);
|
||||
this._taskService.customize(task, undefined, true);
|
||||
} else if (CustomTask.is(task) || ConfiguringTask.is(task)) {
|
||||
let canOpenConfig: boolean = false;
|
||||
try {
|
||||
canOpenConfig = await this.taskService.openConfig(task);
|
||||
canOpenConfig = await this._taskService.openConfig(task);
|
||||
} catch (e) {
|
||||
// do nothing.
|
||||
}
|
||||
if (!canOpenConfig) {
|
||||
this.taskService.customize(task, undefined, true);
|
||||
this._taskService.customize(task, undefined, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -233,30 +233,30 @@ export class TaskQuickPick extends Disposable {
|
|||
if (!firstLevelTask) {
|
||||
// First show recent tasks configured tasks. Other tasks will be available at a second level
|
||||
const topLevelEntriesResult = await this.getTopLevelEntries(defaultEntry);
|
||||
if (topLevelEntriesResult.isSingleConfigured && this.configurationService.getValue<boolean>(QUICKOPEN_SKIP_CONFIG)) {
|
||||
if (topLevelEntriesResult.isSingleConfigured && this._configurationService.getValue<boolean>(QUICKOPEN_SKIP_CONFIG)) {
|
||||
picker.dispose();
|
||||
return this.toTask(topLevelEntriesResult.isSingleConfigured);
|
||||
return this._toTask(topLevelEntriesResult.isSingleConfigured);
|
||||
}
|
||||
const taskQuickPickEntries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[] = topLevelEntriesResult.entries;
|
||||
firstLevelTask = await this.doPickerFirstLevel(picker, taskQuickPickEntries);
|
||||
firstLevelTask = await this._doPickerFirstLevel(picker, taskQuickPickEntries);
|
||||
}
|
||||
do {
|
||||
if (Types.isString(firstLevelTask)) {
|
||||
// Proceed to second level of quick pick
|
||||
const selectedEntry = await this.doPickerSecondLevel(picker, firstLevelTask);
|
||||
const selectedEntry = await this._doPickerSecondLevel(picker, firstLevelTask);
|
||||
if (selectedEntry && !selectedEntry.settingType && selectedEntry.task === null) {
|
||||
// The user has chosen to go back to the first level
|
||||
firstLevelTask = await this.doPickerFirstLevel(picker, (await this.getTopLevelEntries(defaultEntry)).entries);
|
||||
firstLevelTask = await this._doPickerFirstLevel(picker, (await this.getTopLevelEntries(defaultEntry)).entries);
|
||||
} else if (selectedEntry && Types.isString(selectedEntry.settingType)) {
|
||||
picker.dispose();
|
||||
return this.handleSettingOption(selectedEntry.settingType);
|
||||
} else {
|
||||
picker.dispose();
|
||||
return (selectedEntry?.task && !Types.isString(selectedEntry?.task)) ? this.toTask(selectedEntry?.task) : undefined;
|
||||
return (selectedEntry?.task && !Types.isString(selectedEntry?.task)) ? this._toTask(selectedEntry?.task) : undefined;
|
||||
}
|
||||
} else if (firstLevelTask) {
|
||||
picker.dispose();
|
||||
return this.toTask(firstLevelTask);
|
||||
return this._toTask(firstLevelTask);
|
||||
} else {
|
||||
picker.dispose();
|
||||
return firstLevelTask;
|
||||
|
@ -265,7 +265,7 @@ export class TaskQuickPick extends Disposable {
|
|||
return;
|
||||
}
|
||||
|
||||
private async doPickerFirstLevel(picker: IQuickPick<ITaskTwoLevelQuickPickEntry>, taskQuickPickEntries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[]): Promise<Task | ConfiguringTask | string | null | undefined> {
|
||||
private async _doPickerFirstLevel(picker: IQuickPick<ITaskTwoLevelQuickPickEntry>, taskQuickPickEntries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[]): Promise<Task | ConfiguringTask | string | null | undefined> {
|
||||
picker.items = taskQuickPickEntries;
|
||||
const firstLevelPickerResult = await new Promise<ITaskTwoLevelQuickPickEntry | undefined | null>(resolve => {
|
||||
Event.once(picker.onDidAccept)(async () => {
|
||||
|
@ -275,15 +275,15 @@ export class TaskQuickPick extends Disposable {
|
|||
return firstLevelPickerResult?.task;
|
||||
}
|
||||
|
||||
private async doPickerSecondLevel(picker: IQuickPick<ITaskTwoLevelQuickPickEntry>, type: string) {
|
||||
private async _doPickerSecondLevel(picker: IQuickPick<ITaskTwoLevelQuickPickEntry>, type: string) {
|
||||
picker.busy = true;
|
||||
if (type === SHOW_ALL) {
|
||||
const items = (await this.taskService.tasks()).sort((a, b) => this.sorter.compare(a, b)).map(task => this.createTaskEntry(task));
|
||||
items.push(...TaskQuickPick.allSettingEntries(this.configurationService));
|
||||
const items = (await this._taskService.tasks()).sort((a, b) => this._sorter.compare(a, b)).map(task => this._createTaskEntry(task));
|
||||
items.push(...TaskQuickPick.allSettingEntries(this._configurationService));
|
||||
picker.items = items;
|
||||
} else {
|
||||
picker.value = '';
|
||||
picker.items = await this.getEntriesForProvider(type);
|
||||
picker.items = await this._getEntriesForProvider(type);
|
||||
}
|
||||
picker.busy = false;
|
||||
const secondLevelPickerResult = await new Promise<ITaskTwoLevelQuickPickEntry | undefined | null>(resolve => {
|
||||
|
@ -325,11 +325,11 @@ export class TaskQuickPick extends Disposable {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
private async getEntriesForProvider(type: string): Promise<QuickPickInput<ITaskTwoLevelQuickPickEntry>[]> {
|
||||
const tasks = (await this.taskService.tasks({ type })).sort((a, b) => this.sorter.compare(a, b));
|
||||
private async _getEntriesForProvider(type: string): Promise<QuickPickInput<ITaskTwoLevelQuickPickEntry>[]> {
|
||||
const tasks = (await this._taskService.tasks({ type })).sort((a, b) => this._sorter.compare(a, b));
|
||||
let taskQuickPickEntries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[];
|
||||
if (tasks.length > 0) {
|
||||
taskQuickPickEntries = tasks.map(task => this.createTaskEntry(task));
|
||||
taskQuickPickEntries = tasks.map(task => this._createTaskEntry(task));
|
||||
taskQuickPickEntries.push({
|
||||
type: 'separator'
|
||||
}, {
|
||||
|
@ -345,22 +345,22 @@ export class TaskQuickPick extends Disposable {
|
|||
}];
|
||||
}
|
||||
|
||||
const settingEntry = TaskQuickPick.getSettingEntry(this.configurationService, type);
|
||||
const settingEntry = TaskQuickPick.getSettingEntry(this._configurationService, type);
|
||||
if (settingEntry) {
|
||||
taskQuickPickEntries.push(settingEntry);
|
||||
}
|
||||
return taskQuickPickEntries;
|
||||
}
|
||||
|
||||
private async toTask(task: Task | ConfiguringTask): Promise<Task | undefined> {
|
||||
private async _toTask(task: Task | ConfiguringTask): Promise<Task | undefined> {
|
||||
if (!ConfiguringTask.is(task)) {
|
||||
return task;
|
||||
}
|
||||
|
||||
const resolvedTask = await this.taskService.tryResolveTask(task);
|
||||
const resolvedTask = await this._taskService.tryResolveTask(task);
|
||||
|
||||
if (!resolvedTask) {
|
||||
this.notificationService.error(nls.localize('noProviderForTask', "There is no task provider registered for tasks of type \"{0}\".", task.type));
|
||||
this._notificationService.error(nls.localize('noProviderForTask', "There is no task provider registered for tasks of type \"{0}\".", task.type));
|
||||
}
|
||||
return resolvedTask;
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
|||
export class TaskService extends AbstractTaskService {
|
||||
private static readonly ProcessTaskSystemSupportMessage = nls.localize('taskService.processTaskSystem', 'Process task system is not support in the web.');
|
||||
|
||||
protected getTaskSystem(): ITaskSystem {
|
||||
protected _getTaskSystem(): ITaskSystem {
|
||||
if (this._taskSystem) {
|
||||
return this._taskSystem;
|
||||
}
|
||||
if (this.executionEngine === ExecutionEngine.Terminal) {
|
||||
this._taskSystem = this.createTerminalTaskSystem();
|
||||
this._taskSystem = this._createTerminalTaskSystem();
|
||||
} else {
|
||||
throw new Error(TaskService.ProcessTaskSystemSupportMessage);
|
||||
}
|
||||
|
@ -32,11 +32,11 @@ export class TaskService extends AbstractTaskService {
|
|||
return this._taskSystem!;
|
||||
}
|
||||
|
||||
protected computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise<IWorkspaceFolderConfigurationResult> {
|
||||
protected _computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise<IWorkspaceFolderConfigurationResult> {
|
||||
throw new Error(TaskService.ProcessTaskSystemSupportMessage);
|
||||
}
|
||||
|
||||
protected versionAndEngineCompatible(filter?: ITaskFilter): boolean {
|
||||
protected _versionAndEngineCompatible(filter?: ITaskFilter): boolean {
|
||||
return this.executionEngine === ExecutionEngine.Terminal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,11 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQ
|
|||
|
||||
constructor(
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@ITaskService private taskService: ITaskService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IQuickInputService private quickInputService: IQuickInputService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IDialogService private dialogService: IDialogService
|
||||
@ITaskService private _taskService: ITaskService,
|
||||
@IConfigurationService private _configurationService: IConfigurationService,
|
||||
@IQuickInputService private _quickInputService: IQuickInputService,
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IDialogService private _dialogService: IDialogService
|
||||
) {
|
||||
super(TasksQuickAccessProvider.PREFIX, {
|
||||
noResultsPick: {
|
||||
|
@ -42,7 +42,7 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQ
|
|||
return [];
|
||||
}
|
||||
|
||||
const taskQuickPick = new TaskQuickPick(this.taskService, this.configurationService, this.quickInputService, this.notificationService, this.dialogService);
|
||||
const taskQuickPick = new TaskQuickPick(this._taskService, this._configurationService, this._quickInputService, this._notificationService, this._dialogService);
|
||||
const topLevelPicks = await taskQuickPick.getTopLevelEntries();
|
||||
const taskPicks: Array<IPickerQuickAccessItem | IQuickPickSeparator> = [];
|
||||
|
||||
|
@ -63,14 +63,14 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQ
|
|||
if ((index === 1) && (quickAccessEntry.buttons?.length === 2)) {
|
||||
const key = (task && !isString(task)) ? task.getRecentlyUsedKey() : undefined;
|
||||
if (key) {
|
||||
this.taskService.removeRecentlyUsedTask(key);
|
||||
this._taskService.removeRecentlyUsedTask(key);
|
||||
}
|
||||
return TriggerAction.REFRESH_PICKER;
|
||||
} else {
|
||||
if (ContributedTask.is(task)) {
|
||||
this.taskService.customize(task, undefined, true);
|
||||
this._taskService.customize(task, undefined, true);
|
||||
} else if (CustomTask.is(task)) {
|
||||
this.taskService.openConfig(task);
|
||||
this._taskService.openConfig(task);
|
||||
}
|
||||
return TriggerAction.CLOSE_PICKER;
|
||||
}
|
||||
|
@ -80,10 +80,10 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQ
|
|||
// switch to quick pick and show second level
|
||||
const showResult = await taskQuickPick.show(localize('TaskService.pickRunTask', 'Select the task to run'), undefined, task);
|
||||
if (showResult) {
|
||||
this.taskService.run(showResult, { attachProblemMatcher: true });
|
||||
this._taskService.run(showResult, { attachProblemMatcher: true });
|
||||
}
|
||||
} else {
|
||||
this.taskService.run(await this.toTask(task), { attachProblemMatcher: true });
|
||||
this._taskService.run(await this._toTask(task), { attachProblemMatcher: true });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -92,11 +92,11 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQ
|
|||
return taskPicks;
|
||||
}
|
||||
|
||||
private async toTask(task: Task | ConfiguringTask): Promise<Task | undefined> {
|
||||
private async _toTask(task: Task | ConfiguringTask): Promise<Task | undefined> {
|
||||
if (!ConfiguringTask.is(task)) {
|
||||
return task;
|
||||
}
|
||||
|
||||
return this.taskService.tryResolveTask(task);
|
||||
return this._taskService.tryResolveTask(task);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -119,11 +119,11 @@ export class TaskService extends AbstractTaskService {
|
|||
this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown(), 'veto.tasks')));
|
||||
}
|
||||
|
||||
protected getTaskSystem(): ITaskSystem {
|
||||
protected _getTaskSystem(): ITaskSystem {
|
||||
if (this._taskSystem) {
|
||||
return this._taskSystem;
|
||||
}
|
||||
this._taskSystem = this.createTerminalTaskSystem();
|
||||
this._taskSystem = this._createTerminalTaskSystem();
|
||||
this._taskSystemListener = this._taskSystem!.onDidStateChange((event) => {
|
||||
if (this._taskSystem) {
|
||||
this._taskRunningState.set(this._taskSystem.isActiveSync());
|
||||
|
@ -133,8 +133,8 @@ export class TaskService extends AbstractTaskService {
|
|||
return this._taskSystem;
|
||||
}
|
||||
|
||||
protected computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise<IWorkspaceFolderConfigurationResult> {
|
||||
const { config, hasParseErrors } = this.getConfiguration(workspaceFolder);
|
||||
protected _computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise<IWorkspaceFolderConfigurationResult> {
|
||||
const { config, hasParseErrors } = this._getConfiguration(workspaceFolder);
|
||||
if (hasParseErrors) {
|
||||
return Promise.resolve({ workspaceFolder: workspaceFolder, hasErrors: true, config: undefined });
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ export class TaskService extends AbstractTaskService {
|
|||
}
|
||||
}
|
||||
|
||||
protected versionAndEngineCompatible(filter?: ITaskFilter): boolean {
|
||||
protected _versionAndEngineCompatible(filter?: ITaskFilter): boolean {
|
||||
const range = filter && filter.version ? filter.version : undefined;
|
||||
const engine = this.executionEngine;
|
||||
|
||||
|
@ -169,7 +169,7 @@ export class TaskService extends AbstractTaskService {
|
|||
if (this._taskSystem.canAutoTerminate()) {
|
||||
terminatePromise = Promise.resolve({ confirmed: true });
|
||||
} else {
|
||||
terminatePromise = this.dialogService.confirm({
|
||||
terminatePromise = this._dialogService.confirm({
|
||||
message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'),
|
||||
primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"),
|
||||
type: 'question'
|
||||
|
@ -191,10 +191,10 @@ export class TaskService extends AbstractTaskService {
|
|||
}
|
||||
if (success) {
|
||||
this._taskSystem = undefined;
|
||||
this.disposeTaskSystemListeners();
|
||||
this._disposeTaskSystemListeners();
|
||||
return false; // no veto
|
||||
} else if (code && code === TerminateResponseCode.ProcessNotFound) {
|
||||
return this.dialogService.confirm({
|
||||
return this._dialogService.confirm({
|
||||
message: nls.localize('TaskSystem.noProcess', 'The launched task doesn\'t exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.'),
|
||||
primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"),
|
||||
type: 'info'
|
||||
|
|
|
@ -79,30 +79,30 @@ export const enum TerminalBuiltinLinkType {
|
|||
/**
|
||||
* The link is validated to be a file on the file system and will open an editor.
|
||||
*/
|
||||
LocalFile,
|
||||
LocalFile = 'LocalFile',
|
||||
|
||||
/**
|
||||
* The link is validated to be a folder on the file system and is outside the workspace. It will
|
||||
* reveal the folder within the explorer.
|
||||
*/
|
||||
LocalFolderOutsideWorkspace,
|
||||
LocalFolderOutsideWorkspace = 'LocalFolderOutsideWorkspace',
|
||||
|
||||
/**
|
||||
* The link is validated to be a folder on the file system and is within the workspace and will
|
||||
* reveal the folder within the explorer.
|
||||
*/
|
||||
LocalFolderInWorkspace,
|
||||
LocalFolderInWorkspace = 'LocalFolderInWorkspace',
|
||||
|
||||
/**
|
||||
* A low confidence link which will search for the file in the workspace. If there is a single
|
||||
* match, it will open the file; otherwise, it will present the matches in a quick pick.
|
||||
*/
|
||||
Search,
|
||||
Search = 'Search',
|
||||
|
||||
/**
|
||||
* A link whose text is a valid URI.
|
||||
*/
|
||||
Url
|
||||
Url = 'Url'
|
||||
}
|
||||
|
||||
export interface ITerminalExternalLinkType {
|
||||
|
|
|
@ -86,7 +86,7 @@ export class TerminalLinkManager extends DisposableStore {
|
|||
this._openers.set(TerminalBuiltinLinkType.LocalFile, localFileOpener);
|
||||
this._openers.set(TerminalBuiltinLinkType.LocalFolderInWorkspace, localFolderInWorkspaceOpener);
|
||||
this._openers.set(TerminalBuiltinLinkType.LocalFolderOutsideWorkspace, this._instantiationService.createInstance(TerminalLocalFolderOutsideWorkspaceLinkOpener));
|
||||
this._openers.set(TerminalBuiltinLinkType.Search, this._instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, localFileOpener, localFolderInWorkspaceOpener, this._processManager.os || OS));
|
||||
this._openers.set(TerminalBuiltinLinkType.Search, this._instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, this._processManager.getInitialCwd(), localFileOpener, localFolderInWorkspaceOpener, this._processManager.os || OS));
|
||||
this._openers.set(TerminalBuiltinLinkType.Url, this._instantiationService.createInstance(TerminalUrlLinkOpener, !!this._processManager.remoteAuthority));
|
||||
|
||||
this._registerStandardLinkProviders();
|
||||
|
|
|
@ -125,6 +125,7 @@ export class TerminalSearchLinkOpener implements ITerminalLinkOpener {
|
|||
|
||||
constructor(
|
||||
private readonly _capabilities: ITerminalCapabilityStore,
|
||||
private readonly _initialCwd: Promise<string>,
|
||||
private readonly _localFileOpener: TerminalLocalFileLinkOpener,
|
||||
private readonly _localFolderInWorkspaceOpener: TerminalLocalFolderInWorkspaceLinkOpener,
|
||||
private readonly _os: OperatingSystem,
|
||||
|
@ -182,10 +183,20 @@ export class TerminalSearchLinkOpener implements ITerminalLinkOpener {
|
|||
}
|
||||
|
||||
private async _getExactMatch(sanitizedLink: string): Promise<IResourceMatch | undefined> {
|
||||
// Make the link relative to the cwd if it isn't absolute
|
||||
const pathModule = osPathModule(this._os);
|
||||
const isAbsolute = pathModule.isAbsolute(sanitizedLink);
|
||||
let absolutePath: string | undefined = isAbsolute ? sanitizedLink : undefined;
|
||||
const initialCwd = await this._initialCwd;
|
||||
if (!isAbsolute && initialCwd.length > 0) {
|
||||
absolutePath = pathModule.join(initialCwd, sanitizedLink);
|
||||
}
|
||||
|
||||
// Try open as an absolute link
|
||||
let resourceMatch: IResourceMatch | undefined;
|
||||
if (osPathModule(this._os).isAbsolute(sanitizedLink)) {
|
||||
if (absolutePath) {
|
||||
const slashNormalizedPath = this._os === OperatingSystem.Windows ? absolutePath.replace(/\\/g, '/') : absolutePath;
|
||||
const scheme = this._workbenchEnvironmentService.remoteAuthority ? Schemas.vscodeRemote : Schemas.file;
|
||||
const slashNormalizedPath = this._os === OperatingSystem.Windows ? sanitizedLink.replace(/\\/g, '/') : sanitizedLink;
|
||||
const uri = URI.from({ scheme, path: slashNormalizedPath });
|
||||
try {
|
||||
const fileStat = await this._fileService.stat(uri);
|
||||
|
@ -194,6 +205,8 @@ export class TerminalSearchLinkOpener implements ITerminalLinkOpener {
|
|||
// File or dir doesn't exist, continue on
|
||||
}
|
||||
}
|
||||
|
||||
// Search the workspace if an exact match based on the absolute path was not found
|
||||
if (!resourceMatch) {
|
||||
const results = await this._searchService.fileSearch(
|
||||
this._fileQueryBuilder.file(this._workspaceContextService.getWorkspace().folders, {
|
||||
|
|
|
@ -669,6 +669,12 @@ export interface ITerminalInstance {
|
|||
*/
|
||||
clearSelection(): void;
|
||||
|
||||
/**
|
||||
* When the panel is hidden or a terminal in the editor area becomes inactive, reset the focus context key
|
||||
* to avoid issues like #147180.
|
||||
*/
|
||||
resetFocusContextKey(): void;
|
||||
|
||||
/**
|
||||
* Select all text in the terminal.
|
||||
*/
|
||||
|
|
|
@ -101,6 +101,14 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor
|
|||
}
|
||||
}
|
||||
}));
|
||||
this._register(this._editorService.onDidActiveEditorChange(() => {
|
||||
const instance = this._editorService.activeEditor instanceof TerminalEditorInput ? this._editorService.activeEditor : undefined;
|
||||
if (!instance) {
|
||||
for (const instance of this.instances) {
|
||||
instance.resetFocusContextKey();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private _getActiveTerminalEditors(): EditorInput[] {
|
||||
|
|
|
@ -385,7 +385,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
});
|
||||
this._fixedRows = _shellLaunchConfig.attachPersistentProcess?.fixedDimensions?.rows;
|
||||
this._fixedCols = _shellLaunchConfig.attachPersistentProcess?.fixedDimensions?.cols;
|
||||
this._icon = _shellLaunchConfig.attachPersistentProcess?.icon || _shellLaunchConfig.icon;
|
||||
|
||||
// the resource is already set when it's been moved from another window
|
||||
this._resource = resource || getTerminalUri(this._workspaceContextService.getWorkspace().id, this.instanceId, this.title);
|
||||
|
@ -431,10 +430,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
// Resolve just the icon ahead of time so that it shows up immediately in the tabs. This is
|
||||
// disabled in remote because this needs to be sync and the OS may differ on the remote
|
||||
// which would result in the wrong profile being selected and the wrong icon being
|
||||
// permanently attached to the terminal.
|
||||
// permanently attached to the terminal. This also doesn't work when the default profile
|
||||
// setting is set to null, that's handled after the process is created.
|
||||
if (!this.shellLaunchConfig.executable && !workbenchEnvironmentService.remoteAuthority) {
|
||||
this._terminalProfileResolverService.resolveIcon(this._shellLaunchConfig, OS);
|
||||
}
|
||||
this._icon = _shellLaunchConfig.attachPersistentProcess?.icon || _shellLaunchConfig.icon;
|
||||
|
||||
// When a custom pty is used set the name immediately so it gets passed over to the exthost
|
||||
// and is available when Pseudoterminal.open fires.
|
||||
|
@ -1150,6 +1151,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
}
|
||||
}
|
||||
|
||||
resetFocusContextKey(): void {
|
||||
this._terminalFocusContextKey.reset();
|
||||
}
|
||||
|
||||
private _initDragAndDrop(container: HTMLElement) {
|
||||
this._dndObserver?.dispose();
|
||||
const dndController = this._instantiationService.createInstance(TerminalInstanceDragAndDropController, container);
|
||||
|
@ -1550,8 +1555,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
this.xterm?.raw.resize(this._cols || Constants.DefaultCols, this._rows || Constants.DefaultRows);
|
||||
}
|
||||
|
||||
const hadIcon = !!this.shellLaunchConfig.icon;
|
||||
|
||||
const originalIcon = this.shellLaunchConfig.icon;
|
||||
await this._processManager.createProcess(this._shellLaunchConfig, this._cols || Constants.DefaultCols, this._rows || Constants.DefaultRows, this._accessibilityService.isScreenReaderOptimized()).then(error => {
|
||||
if (error) {
|
||||
this._onProcessExit(error, error.code === ShellIntegrationExitCode);
|
||||
|
@ -1560,7 +1564,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
if (this.xterm?.shellIntegration) {
|
||||
this.capabilities.add(this.xterm?.shellIntegration.capabilities);
|
||||
}
|
||||
if (!hadIcon && this.shellLaunchConfig.icon || this.shellLaunchConfig.color) {
|
||||
if (originalIcon !== this.shellLaunchConfig.icon || this.shellLaunchConfig.color) {
|
||||
this._icon = this._shellLaunchConfig.attachPersistentProcess?.icon || this._shellLaunchConfig.icon;
|
||||
this._onIconChanged.fire(this);
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue