Skip to content

Commit af52650

Browse files
committed
feat: support open from git scm (#2094)
1 parent 2671c3b commit af52650

File tree

10 files changed

+193
-55
lines changed

10 files changed

+193
-55
lines changed
Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { URI, UriComponents } from '@opensumi/ide-core-common';
12
import type { ICodeEditor } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/editorBrowser';
23
import type { IEditor } from '@opensumi/monaco-editor-core/esm/vs/editor/common/editorCommon';
34
import type { IEditorModel } from '@opensumi/monaco-editor-core/esm/vs/editor/common/editorCommon';
@@ -6,6 +7,125 @@ export interface IMergeEditorEditor extends IEditor {
67
getOursEditor(): ICodeEditor;
78
getResultEditor(): ICodeEditor;
89
getTheirsEditor(): ICodeEditor;
9-
open(oursTextModel: IEditorModel, resultTextModel: IEditorModel, theirsTextModel: IEditorModel): Promise<void>;
10+
open(openMergeEditorArgs: IOpenMergeEditorArgs): Promise<void>;
1011
compare(): Promise<void>;
1112
}
13+
14+
export class MergeEditorInputData {
15+
static from(data: string): MergeEditorInputData {
16+
try {
17+
const obj: MergeEditorInputData = JSON.parse(data);
18+
return new MergeEditorInputData(obj.uri, obj.detail, obj.description);
19+
} catch (error) {
20+
throw Error('invalid MergeEditorInputData parse');
21+
}
22+
}
23+
24+
private _textModel: IEditorModel;
25+
public get textModel(): IEditorModel {
26+
return this._textModel;
27+
}
28+
29+
constructor(readonly uri: URI, readonly detail?: string | undefined, readonly description?: string | undefined) {}
30+
31+
public toString(): string {
32+
return JSON.stringify({
33+
uri: this.uri.toString(),
34+
detail: this.detail,
35+
description: this.description,
36+
});
37+
}
38+
39+
public setTextModel(model: IEditorModel): this {
40+
this._textModel = model;
41+
return this;
42+
}
43+
}
44+
45+
export interface IOpenMergeEditorArgs {
46+
ancestor: {
47+
uri: URI;
48+
textModel: IEditorModel;
49+
};
50+
input1: MergeEditorInputData;
51+
input2: MergeEditorInputData;
52+
output: {
53+
uri: URI;
54+
textModel: IEditorModel;
55+
};
56+
}
57+
58+
interface IValidateOpenArgs {
59+
ancestor: URI;
60+
input1: MergeEditorInputData;
61+
input2: MergeEditorInputData;
62+
output: URI;
63+
}
64+
65+
export namespace IRelaxedOpenMergeEditorArgs {
66+
export const validate = (args: unknown): IValidateOpenArgs => {
67+
if (!args || typeof args !== 'object') {
68+
throw new TypeError('invalid argument');
69+
}
70+
71+
const obj = args as IValidateOpenArgs;
72+
const ancestor = toUri(obj.ancestor);
73+
const output = toUri(obj.output);
74+
const input1 = toInputData(obj.input1);
75+
const input2 = toInputData(obj.input2);
76+
return { ancestor, input1, input2, output };
77+
};
78+
79+
export const toString = (args: IValidateOpenArgs): string => {
80+
const { ancestor, input1, input2, output } = args;
81+
82+
return JSON.stringify({
83+
ancestor: ancestor.toString(),
84+
input1: input1.toString(),
85+
input2: input2.toString(),
86+
output: output.toString(),
87+
});
88+
};
89+
90+
const toInputData = (args: unknown): MergeEditorInputData => {
91+
if (typeof args === 'string') {
92+
return new MergeEditorInputData(URI.parse(args), undefined, undefined);
93+
}
94+
if (!args || typeof args !== 'object') {
95+
throw new TypeError('invalid argument');
96+
}
97+
98+
if (isUriComponents(args)) {
99+
return new MergeEditorInputData(URI.from(args), undefined, undefined);
100+
}
101+
102+
const obj = args as MergeEditorInputData;
103+
const uri = toUri(obj.uri);
104+
const detail = obj.detail;
105+
const description = obj.description;
106+
return new MergeEditorInputData(uri, detail, description);
107+
};
108+
109+
const toUri = (args: unknown): URI => {
110+
if (typeof args === 'string') {
111+
return URI.parse(args);
112+
} else if (args && typeof args === 'object') {
113+
return URI.from(args as UriComponents);
114+
}
115+
throw new TypeError('invalid argument');
116+
};
117+
118+
const isUriComponents = (args: unknown): args is UriComponents => {
119+
if (!args || typeof args !== 'object') {
120+
return false;
121+
}
122+
const obj = args as UriComponents;
123+
return (
124+
typeof obj.scheme === 'string' &&
125+
typeof obj.authority === 'string' &&
126+
typeof obj.path === 'string' &&
127+
typeof obj.query === 'string' &&
128+
typeof obj.fragment === 'string'
129+
);
130+
};
131+
}

packages/editor/src/browser/editor.contribution.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
URI,
1111
Domain,
1212
localize,
13+
formatLocalize,
1314
MonacoService,
1415
ServiceNames,
1516
MonacoContribution,
@@ -40,6 +41,7 @@ import { ComponentContribution, ComponentRegistry } from '@opensumi/ide-core-bro
4041
import { MenuContribution, IMenuRegistry, MenuId } from '@opensumi/ide-core-browser/lib/menu/next';
4142
import { AbstractContextMenuService } from '@opensumi/ide-core-browser/lib/menu/next/menu.interface';
4243
import { ICtxMenuRenderer } from '@opensumi/ide-core-browser/lib/menu/next/renderer/ctxmenu/base';
44+
import { IRelaxedOpenMergeEditorArgs } from '@opensumi/ide-core-browser/lib/monaco/merge-editor-widget';
4345
import { isWindows, isOSX, PreferenceScope, ILogger, OnEvent, WithEventBus } from '@opensumi/ide-core-common';
4446
import { IElectronMainUIService } from '@opensumi/ide-core-common/lib/electron';
4547
import { ITextmateTokenizer, ITextmateTokenizerService } from '@opensumi/ide-monaco/lib/browser/contrib/tokenizer';
@@ -550,22 +552,14 @@ export class EditorContribution
550552
});
551553

552554
commands.registerCommand(EDITOR_COMMANDS.OPEN_MERGEEDITOR, {
553-
execute: () => {
554-
/**
555-
* (DEV)
556-
*/
557-
const current = URI.parse(`${this.appConfig.workspaceDir}/merge-editor/c.json`);
558-
const incoming = URI.parse(`${this.appConfig.workspaceDir}/merge-editor/b.json`);
559-
const result = URI.parse(`${this.appConfig.workspaceDir}/merge-editor/a.json`);
560-
const name = `${current.displayName} <=> ${result.displayName} <=> ${incoming.displayName}`;
555+
execute: (args: unknown) => {
556+
const validatedArgs = IRelaxedOpenMergeEditorArgs.validate(args);
561557
this.workbenchEditorService.open(
562558
URI.from({
563559
scheme: 'mergeEditor',
564560
query: URI.stringifyQuery({
565-
name,
566-
current,
567-
incoming,
568-
result,
561+
name: formatLocalize('mergeEditor.workbench.tab.name', validatedArgs.output.displayName),
562+
openMetadata: IRelaxedOpenMergeEditorArgs.toString(validatedArgs),
569563
}),
570564
}),
571565
);
Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable, Autowired } from '@opensumi/di';
2-
import { URI, WithEventBus, MaybePromise, getIcon, LabelService } from '@opensumi/ide-core-browser';
2+
import { URI, WithEventBus, MaybePromise, LabelService } from '@opensumi/ide-core-browser';
33

44
import { IResourceProvider, IResource } from '../../common';
55

@@ -11,18 +11,26 @@ export class MergeEditorResourceProvider extends WithEventBus implements IResour
1111
private readonly labelService: LabelService;
1212

1313
public provideResource(uri: URI): MaybePromise<IResource<any>> {
14-
const { current, incoming, result, name } = uri.getParsedQuery();
15-
const resultEditorUri = new URI(result);
16-
const icon = this.labelService.getIcon(resultEditorUri);
17-
return {
18-
name,
19-
icon,
20-
uri,
21-
metadata: {
22-
current,
23-
incoming,
24-
result,
25-
},
26-
};
14+
const { openMetadata, name } = uri.getParsedQuery();
15+
16+
try {
17+
const parseMetaData = JSON.parse(openMetadata);
18+
const { ancestor, input1, input2, output } = parseMetaData;
19+
const resultEditorUri = new URI(output);
20+
const icon = this.labelService.getIcon(resultEditorUri);
21+
return {
22+
name,
23+
icon,
24+
uri,
25+
metadata: {
26+
ancestor,
27+
input1,
28+
input2,
29+
output,
30+
},
31+
};
32+
} catch (error) {
33+
throw Error('invalid merge editor resource parse');
34+
}
2735
}
2836
}

packages/editor/src/browser/workbench-editor.service.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
toMarkdown,
1717
} from '@opensumi/ide-core-browser';
1818
import { ResourceContextKey } from '@opensumi/ide-core-browser/lib/contextkey/resource';
19-
import { IMergeEditorEditor } from '@opensumi/ide-core-browser/lib/monaco/merge-editor-widget';
19+
import { IMergeEditorEditor, MergeEditorInputData } from '@opensumi/ide-core-browser/lib/monaco/merge-editor-widget';
2020
import { isUndefinedOrNull, Schemes, REPORT_NAME, match, localize, MessageType } from '@opensumi/ide-core-common';
2121
import {
2222
CommandService,
@@ -1636,18 +1636,31 @@ export class EditorGroup extends WithEventBus implements IGridEditorGroup {
16361636
if (!metadata) {
16371637
return;
16381638
}
1639-
const [current, result, incoming] = await Promise.all([
1640-
this.getDocumentModelRef(metadata.current),
1641-
this.getDocumentModelRef(metadata.result),
1642-
this.getDocumentModelRef(metadata.incoming),
1639+
1640+
const { ancestor, input1, input2, output } = metadata;
1641+
const input1Data = MergeEditorInputData.from(input1);
1642+
const input2Data = MergeEditorInputData.from(input2);
1643+
1644+
const [ancestorRef, input1Ref, outputRef, input2Ref] = await Promise.all([
1645+
this.getDocumentModelRef(URI.parse(ancestor)),
1646+
this.getDocumentModelRef(input1Data.uri),
1647+
this.getDocumentModelRef(URI.parse(output)),
1648+
this.getDocumentModelRef(input2Data.uri),
16431649
]);
16441650

16451651
await this.mergeEditorReady.onceReady(async () => {
1646-
await this.mergeEditor.open(
1647-
current.instance.getMonacoModel(),
1648-
result.instance.getMonacoModel(),
1649-
incoming.instance.getMonacoModel(),
1650-
);
1652+
await this.mergeEditor.open({
1653+
ancestor: {
1654+
uri: URI.parse(metadata.ancestor),
1655+
textModel: ancestorRef.instance.getMonacoModel(),
1656+
},
1657+
input1: input1Data.setTextModel(input1Ref.instance.getMonacoModel()),
1658+
input2: input2Data.setTextModel(input2Ref.instance.getMonacoModel()),
1659+
output: {
1660+
uri: URI.parse(metadata.output),
1661+
textModel: outputRef.instance.getMonacoModel(),
1662+
},
1663+
});
16511664
});
16521665
} else {
16531666
return; // other type not handled

packages/editor/src/common/resource.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,5 +138,10 @@ export enum AskSaveResult {
138138
}
139139

140140
// #region merge editor
141-
export type IMergeEditorResource = IResource<{ current: URI; result: URI; incoming: URI }>;
141+
export type IMergeEditorResource = IResource<{
142+
ancestor: string;
143+
input1: string;
144+
input2: string;
145+
output: string;
146+
}>;
142147
// #endregion merge editor

packages/extension/src/browser/extension.contribution.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -404,12 +404,6 @@ export class ExtensionCommandContribution implements CommandContribution {
404404
},
405405
);
406406

407-
registry.registerCommand(VSCodeBuiltinCommands.OPEN_MERGEEDITOR, {
408-
execute: (args: unknown[]) => {
409-
// 这里拦截 vscode 内置的 _open.mergeEditor 命令
410-
},
411-
});
412-
413407
[
414408
// layout builtin commands
415409
VSCodeBuiltinCommands.LAYOUT_COMMAND_MAXIMIZE_EDITOR,

packages/extension/src/browser/vscode/builtin-commands.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,10 +369,9 @@ export const LAYOUT_COMMAND_MAXIMIZE_EDITOR: Command = {
369369

370370
export const WALKTHROUGHS_COMMAND_GET_STARTED: Command = {
371371
id: 'walkthroughs.get.started',
372-
}
372+
};
373373

374374
export const OPEN_MERGEEDITOR: Command = {
375375
id: '_open.mergeEditor',
376-
label: '_open.mergeEditor(DEV)',
377376
delegate: EDITOR_COMMANDS.OPEN_MERGEEDITOR.id,
378377
};

packages/i18n/src/common/en-US.lang.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,10 @@ export const localizationBundle = {
10151015
'walkthroughs.welcome': 'Welcome',
10161016
'walkthroughs.get.started': "Open the 'Getting Started' walkthrough",
10171017
// #endregion walkthrough
1018+
1019+
// #region merge editor
1020+
'mergeEditor.workbench.tab.name': 'Merging: {0}',
1021+
// #endregion merge editor
10181022
...browserViews,
10191023
},
10201024
};

packages/i18n/src/common/zh-CN.lang.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,10 @@ export const localizationBundle = {
10571057
'walkthroughs.welcome': '欢迎使用',
10581058
'walkthroughs.get.started': '打开 `入门` 演示',
10591059
// #endregion walkthrough
1060+
1061+
// #region merge editor
1062+
'mergeEditor.workbench.tab.name': '正在合并: {0}',
1063+
// #endregion merge editor
10601064
...browserViews,
10611065
},
10621066
};

packages/monaco/src/browser/contrib/merge-editor/merge-editor-widget.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React from 'react';
22
import ReactDOM from 'react-dom';
33

4-
import { Injectable, Autowired, Injector, INJECTOR_TOKEN } from '@opensumi/di';
4+
import { Injectable, Autowired } from '@opensumi/di';
55
import { AppConfig, ConfigProvider } from '@opensumi/ide-core-browser';
6-
import { IMergeEditorEditor } from '@opensumi/ide-core-browser/lib/monaco/merge-editor-widget';
6+
import { IMergeEditorEditor, IOpenMergeEditorArgs } from '@opensumi/ide-core-browser/lib/monaco/merge-editor-widget';
77
import { Disposable, IRange, ISelection } from '@opensumi/ide-core-common';
88
import { Selection } from '@opensumi/monaco-editor-core';
99
import { IDisposable } from '@opensumi/monaco-editor-core/esm/vs/base/common/lifecycle';
@@ -36,9 +36,6 @@ let MERGE_EDITOR_ID = 0;
3636

3737
@Injectable({ multiple: true })
3838
export class MergeEditorWidget extends Disposable implements IMergeEditorEditor {
39-
@Autowired(INJECTOR_TOKEN)
40-
private readonly injector: Injector;
41-
4239
@Autowired(AppConfig)
4340
private readonly configContext: AppConfig;
4441

@@ -59,11 +56,11 @@ export class MergeEditorWidget extends Disposable implements IMergeEditorEditor
5956
this.layout();
6057
}
6158

62-
open(oursTextModel: ITextModel, resultTextModel: ITextModel, theirsTextModel: ITextModel): Promise<void> {
59+
open({ ancestor, input1, input2 }: IOpenMergeEditorArgs): Promise<void> {
6360
this.setModel({
64-
ours: oursTextModel,
65-
result: resultTextModel,
66-
theirs: theirsTextModel,
61+
ours: input1.textModel as ITextModel,
62+
result: ancestor.textModel as ITextModel,
63+
theirs: input2.textModel as ITextModel,
6764
});
6865

6966
this.compare();

0 commit comments

Comments
 (0)