Skip to content

Commit 041d75d

Browse files
committed
feat: implement accept combination conflict actions (#2079)
* feat: implement accept combination render * feat: implement accept combination actions * chore: improve mark complete code * chore: improve code
1 parent 99ea62d commit 041d75d

File tree

7 files changed

+361
-119
lines changed

7 files changed

+361
-119
lines changed

packages/monaco/src/browser/contrib/merge-editor/model/inner-range.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
import { Range as MonacoRange } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/range';
22

33
import { IPosition } from '../../../monaco-api/types';
4-
import { EditorViewType, IRangeContrast, LineRangeType } from '../types';
4+
import { ETurnDirection, IRangeContrast, LineRangeType } from '../types';
55

66
export class InnerRange extends MonacoRange implements IRangeContrast {
77
private _isComplete: boolean;
88
public get isComplete(): boolean {
99
return this._isComplete;
1010
}
1111

12-
private _turnDirection: EditorViewType.CURRENT | EditorViewType.INCOMING;
13-
public get turnDirection(): EditorViewType.CURRENT | EditorViewType.INCOMING {
12+
private _turnDirection: ETurnDirection;
13+
public get turnDirection(): ETurnDirection {
1414
return this._turnDirection;
1515
}
1616

1717
static fromPositions(start: IPosition, end: IPosition = start): InnerRange {
1818
return new InnerRange(start.lineNumber, start.column, end.lineNumber, end.column);
1919
}
2020

21-
public setTurnDirection(t: EditorViewType.CURRENT | EditorViewType.INCOMING) {
21+
public setTurnDirection(t: ETurnDirection) {
2222
this._turnDirection = t;
2323
return this;
2424
}

packages/monaco/src/browser/contrib/merge-editor/model/line-range.ts

Lines changed: 127 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,83 @@
1+
import { uuid } from '@opensumi/ide-core-common';
12
import { IRange } from '@opensumi/monaco-editor-core';
23
import { LineRange as MonacoLineRange } from '@opensumi/monaco-editor-core/esm/vs/editor/common/diff/linesDiffComputer';
34

4-
import { EditorViewType, IRangeContrast, LineRangeType } from '../types';
5+
import { ETurnDirection, IRangeContrast, LineRangeType } from '../types';
56

67
import { InnerRange } from './inner-range';
78

9+
/**
10+
* 如果 lineRange 是通过 merge 合并生成的
11+
* 则跟 merge 相关的数据状态都在该 model 来处理
12+
*/
13+
export class MergeStateModel {
14+
/**
15+
* 存储合并前的所有 lineRange 元数据
16+
*/
17+
private metaRanges: LineRange[] = [];
18+
19+
public get isMerge(): boolean {
20+
return this.metaRanges.length > 0;
21+
}
22+
23+
/**
24+
* 只有当 metaRanges 里的 range 彼此都只是表面接触时才允许 combination 操作
25+
* (能被 accept combination 意味着这块 diff 区域,左右两边的代码合并后不会出现被覆盖的情况)
26+
* 例如
27+
* 左边 中间 右边
28+
* ``` ``` ```
29+
* 1. const a = 1 const a = 2 const a = 1
30+
* 2. const b = 1 const b = 1 const b = 2
31+
* 3. const c = 1 const c = 2 const c = 1
32+
* ``` ``` ```
33+
*
34+
* 其中第一行中间与两边都有 diff,但不管是接受左边的还是右边的,最终对结果的影响不变(接受左右两边都一样)
35+
* 如果这三行都是这样的情况,则被允许 combination
36+
*/
37+
public get isAllowCombination(): boolean {
38+
const length = this.metaRanges.length;
39+
if (length <= 1) {
40+
return false;
41+
}
42+
43+
let slow = 0;
44+
for (let fast = 0; fast < length; fast++) {
45+
const slowRange = this.metaRanges[slow];
46+
const fastRange = this.metaRanges[fast];
47+
48+
if (slowRange.id === fastRange.id) {
49+
continue;
50+
}
51+
52+
if (!fastRange.isContact(slowRange)) {
53+
return false;
54+
}
55+
56+
slow += 1;
57+
}
58+
59+
return true;
60+
}
61+
62+
public add(range: LineRange): this {
63+
this.metaRanges.push(range);
64+
return this;
65+
}
66+
67+
public reset(): this {
68+
this.metaRanges = [];
69+
return this;
70+
}
71+
72+
public has(metaRange: LineRange): boolean {
73+
return this.metaRanges.some((m) => m.id === metaRange.id);
74+
}
75+
76+
public getMetaRanges(): LineRange[] {
77+
return this.metaRanges;
78+
}
79+
}
80+
881
export class LineRange extends MonacoLineRange implements IRangeContrast {
982
static fromPositions(startLineNumber: number, endLineNumber: number = startLineNumber): LineRange {
1083
return new LineRange(startLineNumber, endLineNumber);
@@ -25,24 +98,35 @@ export class LineRange extends MonacoLineRange implements IRangeContrast {
2598
return this._isComplete;
2699
}
27100

28-
private _turnDirection: EditorViewType.CURRENT | EditorViewType.INCOMING;
29-
public get turnDirection(): EditorViewType.CURRENT | EditorViewType.INCOMING {
101+
private _turnDirection: ETurnDirection;
102+
public get turnDirection(): ETurnDirection {
30103
return this._turnDirection;
31104
}
32105

106+
public get isMerge(): boolean {
107+
return this.mergeStateModel.isMerge;
108+
}
109+
110+
public get isAllowCombination(): boolean {
111+
return this.mergeStateModel.isAllowCombination;
112+
}
113+
114+
private mergeStateModel: MergeStateModel;
115+
33116
constructor(startLineNumber: number, endLineNumberExclusive: number) {
34117
super(startLineNumber, endLineNumberExclusive);
35118
this._type = 'insert';
36119
this._isComplete = false;
37-
this._id = `${this.startLineNumber}_${this.endLineNumberExclusive}_${this.length}`;
120+
this._id = uuid(6);
121+
this.mergeStateModel = new MergeStateModel();
38122
}
39123

40124
private setId(id: string): this {
41125
this._id = id;
42126
return this;
43127
}
44128

45-
public setTurnDirection(t: EditorViewType.CURRENT | EditorViewType.INCOMING) {
129+
public setTurnDirection(t: ETurnDirection) {
46130
this._turnDirection = t;
47131
return this;
48132
}
@@ -52,13 +136,14 @@ export class LineRange extends MonacoLineRange implements IRangeContrast {
52136
return this;
53137
}
54138

55-
public setType(v: LineRangeType): this {
56-
this._type = v;
139+
private setMergeStateModel(state: MergeStateModel): this {
140+
this.mergeStateModel = state;
57141
return this;
58142
}
59143

60-
public calcMargin(range: LineRange): number {
61-
return this.length - range.length;
144+
public setType(v: LineRangeType): this {
145+
this._type = v;
146+
return this;
62147
}
63148

64149
public isTendencyRight(refer: LineRange): boolean {
@@ -81,6 +166,15 @@ export class LineRange extends MonacoLineRange implements IRangeContrast {
81166
return this.endLineNumberExclusive >= range.startLineNumber && range.endLineNumberExclusive >= this.startLineNumber;
82167
}
83168

169+
/**
170+
* 是否仅仅只是表面接触
171+
*/
172+
public isContact(range: LineRange): boolean {
173+
return (
174+
this.startLineNumber === range.endLineNumberExclusive || this.endLineNumberExclusive === range.startLineNumber
175+
);
176+
}
177+
84178
public isInclude(range: LineRange | InnerRange): boolean {
85179
if (range instanceof LineRange) {
86180
return (
@@ -99,13 +193,33 @@ export class LineRange extends MonacoLineRange implements IRangeContrast {
99193
return this.startLineNumber === range.startLineNumber && this.length === range.length;
100194
}
101195

102-
public merge(other: LineRange): LineRange {
196+
public get metaMergeRanges(): LineRange[] {
197+
return this.mergeStateModel.getMetaRanges();
198+
}
199+
200+
public recordMergeRange(range: LineRange): this {
201+
this.mergeStateModel.add(range);
202+
return this;
203+
}
204+
205+
/**
206+
* 与 other range 合并成一个 range
207+
* @param isKeep: 是否记录被合并的 range
208+
*/
209+
public merge(other: LineRange, isKeep = true): LineRange {
210+
if (isKeep) {
211+
if (!this.mergeStateModel.has(this)) {
212+
this.recordMergeRange(this);
213+
}
214+
this.recordMergeRange(other);
215+
}
216+
103217
return this.retainState(
104218
new LineRange(
105219
Math.min(this.startLineNumber, other.startLineNumber),
106220
Math.max(this.endLineNumberExclusive, other.endLineNumberExclusive),
107221
),
108-
);
222+
).setTurnDirection(ETurnDirection.BOTH);
109223
}
110224

111225
// 生一个除 id 以外,state 状态都相同的 lineRange
@@ -131,7 +245,8 @@ export class LineRange extends MonacoLineRange implements IRangeContrast {
131245
.setId(this._id)
132246
.setType(this._type)
133247
.setTurnDirection(this._turnDirection)
134-
.setComplete(this._isComplete);
248+
.setComplete(this._isComplete)
249+
.setMergeStateModel(this.mergeStateModel);
135250
}
136251

137252
public override delta(offset: number): LineRange {

packages/monaco/src/browser/contrib/merge-editor/types.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getIcon } from '@opensumi/ide-core-browser';
1+
import { getExternalIcon, getIcon } from '@opensumi/ide-core-browser';
22
import { IEditorMouseEvent } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/editorBrowser';
33

44
import { IModelDecorationOptions } from '../../monaco-api/editor';
@@ -7,15 +7,18 @@ import { LineRange } from './model/line-range';
77

88
export interface IRangeContrast {
99
type: LineRangeType;
10-
// 是否解决操作完成
10+
/**
11+
* 是否解决操作完成
12+
*/
1113
get isComplete(): boolean;
1214
setComplete: (b: boolean) => this;
1315
/**
1416
* 表示这个 range 区域是倾向于 current editor 还是 incoming editor(如果本身就是在 current editor 则返回 current)
1517
* 在 result editor 视图里可以通过该字段来判读它是与 current editor 相比较的还是与 incoming 相比较的 diff
18+
* 当然也有可能是两者都有,这种情况一般是 merge 合成后的 range
1619
*/
17-
get turnDirection(): EditorViewType.CURRENT | EditorViewType.INCOMING;
18-
setTurnDirection: (t: EditorViewType.CURRENT | EditorViewType.INCOMING) => this;
20+
get turnDirection(): ETurnDirection;
21+
setTurnDirection: (t: ETurnDirection) => this;
1922
}
2023

2124
export interface IBaseCodeEditor {
@@ -24,6 +27,12 @@ export interface IBaseCodeEditor {
2427

2528
export type LineRangeType = 'insert' | 'modify' | 'remove';
2629

30+
export enum ETurnDirection {
31+
BOTH = 'both',
32+
CURRENT = 'current',
33+
INCOMING = 'incoming',
34+
}
35+
2736
export enum EditorViewType {
2837
CURRENT = 'current',
2938
RESULT = 'result',
@@ -82,6 +91,7 @@ export interface IActionsProvider {
8291
export namespace CONFLICT_ACTIONS_ICON {
8392
export const RIGHT = `conflict-actions ${ACCEPT_CURRENT_ACTIONS} ${getIcon('doubleright')}`;
8493
export const LEFT = `conflict-actions ${ACCEPT_CURRENT_ACTIONS} ${getIcon('doubleleft')}`;
94+
export const WAND = `conflict-actions ${ACCEPT_COMBINATION_ACTIONS} ${getExternalIcon('wand')}`;
8595
export const CLOSE = `conflict-actions ${IGNORE_ACTIONS} ${getIcon('close')}`;
8696
export const REVOKE = `conflict-actions ${REVOKE_ACTIONS} ${getIcon('revoke')}`;
8797
}

0 commit comments

Comments
 (0)