From 7935278a5548fe1d710a590b7879ab859adc8aa8 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 23 Nov 2022 11:29:41 +0800 Subject: [PATCH] feat: render conflict actions (#2002) * feat: implement conflict actions base code * chore: improve code --- .../merge-editor/merge-editor.service.ts | 5 ++ .../merge-editor/model/conflict-actions.ts | 56 +++++++++++++++ .../contrib/merge-editor/model/decorations.ts | 2 +- .../contrib/merge-editor/model/line-range.ts | 9 +++ .../merge-editor/model/sticky-piece.ts | 2 - .../src/browser/contrib/merge-editor/types.ts | 40 +++++++++++ .../merge-editor/view/actions-manager.ts | 61 +++++++++++++++++ .../view/editors/baseCodeEditor.ts | 33 +++++++-- .../view/editors/currentCodeEditor.tsx | 68 +++++++++++++++++-- .../view/editors/incomingCodeEditor.tsx | 27 ++++++-- .../view/editors/resultCodeEditor.tsx | 10 ++- .../merge-editor/view/merge-editor.less | 16 +++++ .../view/stickiness-connect-manager.tsx | 14 +++- 13 files changed, 319 insertions(+), 24 deletions(-) create mode 100644 packages/monaco/src/browser/contrib/merge-editor/model/conflict-actions.ts create mode 100644 packages/monaco/src/browser/contrib/merge-editor/view/actions-manager.ts diff --git a/packages/monaco/src/browser/contrib/merge-editor/merge-editor.service.ts b/packages/monaco/src/browser/contrib/merge-editor/merge-editor.service.ts index 868c2c7868a..52638a6fb05 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/merge-editor.service.ts +++ b/packages/monaco/src/browser/contrib/merge-editor/merge-editor.service.ts @@ -4,6 +4,7 @@ import { Disposable, MonacoService } from '@opensumi/ide-core-browser'; import { ICodeEditor } from '../../monaco-api/editor'; import { ComputerDiffModel } from './model/computer-diff'; +import { ActionsManager } from './view/actions-manager'; import { CurrentCodeEditor } from './view/editors/currentCodeEditor'; import { IncomingCodeEditor } from './view/editors/incomingCodeEditor'; import { ResultCodeEditor } from './view/editors/resultCodeEditor'; @@ -23,6 +24,7 @@ export class MergeEditorService extends Disposable { private incomingView: IncomingCodeEditor; private computerDiffModel: ComputerDiffModel; + private actionsManager: ActionsManager; public scrollSynchronizer: ScrollSynchronizer; public stickinessConnectManager: StickinessConnectManager; @@ -32,6 +34,7 @@ export class MergeEditorService extends Disposable { this.computerDiffModel = new ComputerDiffModel(); this.scrollSynchronizer = new ScrollSynchronizer(); this.stickinessConnectManager = new StickinessConnectManager(); + this.actionsManager = new ActionsManager(); } public instantiationCodeEditor(current: HTMLDivElement, result: HTMLDivElement, incoming: HTMLDivElement): void { @@ -45,6 +48,7 @@ export class MergeEditorService extends Disposable { this.scrollSynchronizer.mount(this.currentView, this.resultView, this.incomingView); this.stickinessConnectManager.mount(this.currentView, this.resultView, this.incomingView); + this.actionsManager.mount(this.currentView, this.resultView, this.incomingView); } public override dispose(): void { @@ -54,6 +58,7 @@ export class MergeEditorService extends Disposable { this.incomingView.dispose(); this.scrollSynchronizer.dispose(); this.stickinessConnectManager.dispose(); + this.actionsManager.dispose(); } public getCurrentEditor(): ICodeEditor { diff --git a/packages/monaco/src/browser/contrib/merge-editor/model/conflict-actions.ts b/packages/monaco/src/browser/contrib/merge-editor/model/conflict-actions.ts new file mode 100644 index 00000000000..156f5163490 --- /dev/null +++ b/packages/monaco/src/browser/contrib/merge-editor/model/conflict-actions.ts @@ -0,0 +1,56 @@ +import { Injectable, Optional } from '@opensumi/di'; +import { Disposable } from '@opensumi/ide-core-common'; +import { IEditorDecorationsCollection } from '@opensumi/monaco-editor-core/esm/vs/editor/common/editorCommon'; +import { ModelDecorationOptions } from '@opensumi/monaco-editor-core/esm/vs/editor/common/model/textModel'; + +import { ICodeEditor, IModelDeltaDecoration } from '../../../monaco-api/editor'; +import { IActionsDescription } from '../types'; +import { BaseCodeEditor } from '../view/editors/baseCodeEditor'; + +import { LineRange } from './line-range'; + +@Injectable({ multiple: false }) +export class ConflictActions extends Disposable { + private decorationsCollection: IEditorDecorationsCollection; + private actionsCollect: Map; + + private get editor(): ICodeEditor { + return this.codeEditor.getEditor(); + } + + constructor(@Optional() private readonly codeEditor: BaseCodeEditor) { + super(); + + this.decorationsCollection = this.editor.createDecorationsCollection(); + this.actionsCollect = new Map(); + } + + public override dispose(): void { + super.dispose(); + this.decorationsCollection.clear(); + this.actionsCollect.clear(); + } + + public setActions(actions: IActionsDescription[]): void { + const newDecorations: IModelDeltaDecoration[] = actions.map((action) => { + const { range } = action; + this.actionsCollect.set(range.startLineNumber, range); + + return { + range: { + startLineNumber: range.startLineNumber, + startColumn: 0, + endLineNumber: range.startLineNumber, + endColumn: 0, + }, + options: ModelDecorationOptions.register(action.decorationOptions), + }; + }); + + this.decorationsCollection.set(newDecorations); + } + + public getActions(line: number): LineRange | undefined { + return this.actionsCollect.get(line); + } +} diff --git a/packages/monaco/src/browser/contrib/merge-editor/model/decorations.ts b/packages/monaco/src/browser/contrib/merge-editor/model/decorations.ts index d9f207acfb7..3513fb0f435 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/model/decorations.ts +++ b/packages/monaco/src/browser/contrib/merge-editor/model/decorations.ts @@ -26,7 +26,7 @@ export interface IDiffDecoration { readonly editorDecoration: IModelDeltaDecoration; } -@Injectable({ multiple: true }) +@Injectable({ multiple: false }) export class MergeEditorDecorations extends Disposable { private deltaDecoration: IDiffDecoration[] = []; private retainDecoration: IDiffDecoration[] = []; diff --git a/packages/monaco/src/browser/contrib/merge-editor/model/line-range.ts b/packages/monaco/src/browser/contrib/merge-editor/model/line-range.ts index 99f6d065c39..35815e59618 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/model/line-range.ts +++ b/packages/monaco/src/browser/contrib/merge-editor/model/line-range.ts @@ -1,3 +1,5 @@ +import { IRange } from '@opensumi/monaco-editor-core'; +import { Range } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/range'; import { LineRange as MonacoLineRange } from '@opensumi/monaco-editor-core/esm/vs/editor/common/diff/linesDiffComputer'; export class LineRange extends MonacoLineRange { @@ -12,4 +14,11 @@ export class LineRange extends MonacoLineRange { public isTendencyLeft(refer: LineRange): boolean { return !this.isEmpty && refer.isEmpty; } + + public toIRange(): IRange { + return Range.fromPositions( + { lineNumber: this.startLineNumber, column: 0 }, + { lineNumber: this.endLineNumberExclusive - 1, column: 0 }, + ); + } } diff --git a/packages/monaco/src/browser/contrib/merge-editor/model/sticky-piece.ts b/packages/monaco/src/browser/contrib/merge-editor/model/sticky-piece.ts index 4224100eb43..5ad4437e1b7 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/model/sticky-piece.ts +++ b/packages/monaco/src/browser/contrib/merge-editor/model/sticky-piece.ts @@ -1,5 +1,3 @@ -import clone from 'lodash/clone'; - import { IStickyPiece, IStickyPiecePath, IStickyPiecePosition, LineRangeType } from '../types'; export class StickyPieceModel implements IStickyPiece { diff --git a/packages/monaco/src/browser/contrib/merge-editor/types.ts b/packages/monaco/src/browser/contrib/merge-editor/types.ts index dc7cdab4c8b..889f6712c5c 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/types.ts +++ b/packages/monaco/src/browser/contrib/merge-editor/types.ts @@ -1,3 +1,14 @@ +import { getIcon } from '@opensumi/ide-core-browser'; +import { IEditorMouseEvent } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; + +import { IModelDecorationOptions } from '../../monaco-api/editor'; + +import { LineRange } from './model/line-range'; + +export interface IBaseCodeEditor { + mount(): void; +} + export type LineRangeType = 'insert' | 'modify' | 'remove'; export type EditorViewType = 'current' | 'result' | 'incoming'; @@ -20,3 +31,32 @@ export interface IStickyPiece { position: IStickyPiecePosition; path: IStickyPiecePath; } + +export interface IActionsDescription { + range: LineRange; + decorationOptions: IModelDecorationOptions; +} + +export const ACCEPT_CURRENT = 'accpet_current'; +export const ACCEPT_COMBINATION = 'accpet_combination'; +export const IGNORE = 'ignore'; + +export interface IActionsProvider { + onActionsClick?: ( + e: IEditorMouseEvent, + currentView: IBaseCodeEditor, + resultView: IBaseCodeEditor, + incomingView: IBaseCodeEditor, + ) => void; + mouseDownGuard?: (e: IEditorMouseEvent) => boolean; + /** + * 提供 actions 操作项 + */ + provideActionsItems: () => IActionsDescription[]; +} + +export namespace CONFLICT_ACTIONS_ICON { + export const RIGHT = `conflict-actions ${ACCEPT_CURRENT} ${getIcon('right')}`; + export const LEFT = `conflict-actions ${ACCEPT_CURRENT} ${getIcon('left')}`; + export const CLOSE = `conflict-actions ${IGNORE} ${getIcon('close')}`; +} diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/actions-manager.ts b/packages/monaco/src/browser/contrib/merge-editor/view/actions-manager.ts new file mode 100644 index 00000000000..da02f139b4d --- /dev/null +++ b/packages/monaco/src/browser/contrib/merge-editor/view/actions-manager.ts @@ -0,0 +1,61 @@ +import { Disposable } from '@opensumi/ide-core-common'; +import { IEditorMouseEvent, MouseTargetType } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; + +import { IActionsDescription } from '../types'; + +import { BaseCodeEditor } from './editors/baseCodeEditor'; + +export class ActionsManager extends Disposable { + private currentView: BaseCodeEditor | undefined; + private resultView: BaseCodeEditor | undefined; + private incomingView: BaseCodeEditor | undefined; + + constructor() { + super(); + } + + public mount(currentView: BaseCodeEditor, resultView: BaseCodeEditor, incomingView: BaseCodeEditor): void { + this.currentView = currentView; + this.resultView = resultView; + this.incomingView = incomingView; + + const handleMouseDown = (e: IEditorMouseEvent, _this: BaseCodeEditor) => { + const provider = _this.actionsProvider; + if (!provider) { + return; + } + + let { mouseDownGuard } = provider; + const { onActionsClick, provideActionsItems } = provider; + + if (typeof mouseDownGuard === 'undefined') { + const items = provideActionsItems(); + mouseDownGuard = (e: IEditorMouseEvent) => { + if (e.event.rightButton) { + return false; + } + + if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) { + return false; + } + + const { position } = e.target; + + if (!items.some((item: IActionsDescription) => item.range.startLineNumber === position.lineNumber)) { + return false; + } + + return true; + }; + } + + if (mouseDownGuard(e) === true && onActionsClick) { + onActionsClick.call(_this, e, currentView, resultView, incomingView); + } + }; + + this.addDispose(currentView.getEditor().onMouseDown((e: IEditorMouseEvent) => handleMouseDown(e, currentView))); + this.addDispose(incomingView.getEditor().onMouseDown((e: IEditorMouseEvent) => handleMouseDown(e, incomingView))); + this.addDispose(resultView.getEditor().onMouseDown((e: IEditorMouseEvent) => handleMouseDown(e, resultView))); + } +} diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/editors/baseCodeEditor.ts b/packages/monaco/src/browser/contrib/merge-editor/view/editors/baseCodeEditor.ts index 88544020f0d..1dd65ce88b8 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/editors/baseCodeEditor.ts +++ b/packages/monaco/src/browser/contrib/merge-editor/view/editors/baseCodeEditor.ts @@ -2,12 +2,13 @@ import { Injector } from '@opensumi/di'; import { MonacoService } from '@opensumi/ide-core-browser'; import { Disposable, Event } from '@opensumi/ide-core-common'; import { ICodeEditor } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; -import { EditorLayoutInfo } from '@opensumi/monaco-editor-core/esm/vs/editor/common/config/editorOptions'; +import { EditorLayoutInfo, EditorOption } from '@opensumi/monaco-editor-core/esm/vs/editor/common/config/editorOptions'; import { Range } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/range'; import { LineRangeMapping } from '@opensumi/monaco-editor-core/esm/vs/editor/common/diff/linesDiffComputer'; import { IModelDecorationOptions, ITextModel } from '@opensumi/monaco-editor-core/esm/vs/editor/common/model'; import { IStandaloneEditorConstructionOptions } from '@opensumi/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; +import { ConflictActions } from '../../model/conflict-actions'; import { IDiffDecoration, IRenderChangesInput, @@ -15,11 +16,14 @@ import { MergeEditorDecorations, } from '../../model/decorations'; import { LineRange } from '../../model/line-range'; -import { EditorViewType } from '../../types'; +import { EditorViewType, IActionsProvider, IBaseCodeEditor } from '../../types'; import { flatModified, flatOriginal } from '../../utils'; import { GuidelineWidget } from '../guideline-widget'; -export abstract class BaseCodeEditor extends Disposable { +export abstract class BaseCodeEditor extends Disposable implements IBaseCodeEditor { + #actionsProvider: IActionsProvider | undefined; + #conflictActions: ConflictActions; + protected decorations: MergeEditorDecorations; protected editor: ICodeEditor; @@ -36,6 +40,7 @@ export abstract class BaseCodeEditor extends Disposable { super.dispose(); this.editor.dispose(); this.decorations.dispose(); + this.#conflictActions.dispose(); } public mount(): void { @@ -53,6 +58,7 @@ export abstract class BaseCodeEditor extends Disposable { }); this.decorations = this.injector.get(MergeEditorDecorations, [this, this.getEditorViewType()]); + this.#conflictActions = this.injector.get(ConflictActions, [this]); this.addDispose( Event.debounce( @@ -63,13 +69,15 @@ export abstract class BaseCodeEditor extends Disposable { () => {}, 0, )(() => { - const marginWith = this.editor.getLayoutInfo().contentLeft; + const lineDecorationsWidth = this.editor.getOption(EditorOption.lineDecorationsWidth); + const contentLeft = this.editor.getLayoutInfo().contentLeft; + const marginWidth = contentLeft - (typeof lineDecorationsWidth === 'number' ? lineDecorationsWidth : 0); const widgets = this.decorations.getLineWidgets(); if (widgets.length > 0) { widgets.forEach((w) => { if (w) { w.setContainerStyle({ - left: marginWith + 'px', + left: marginWidth + 'px', }); } }); @@ -150,6 +158,21 @@ export abstract class BaseCodeEditor extends Disposable { .updateDecorations(r, i); } + protected registerActionsProvider(provider: IActionsProvider): void { + if (this.#actionsProvider) { + return; + } + + this.#actionsProvider = provider; + + const { provideActionsItems } = provider; + this.#conflictActions.setActions(provideActionsItems()); + } + + public get actionsProvider(): IActionsProvider | undefined { + return this.#actionsProvider; + } + public clearDecorations(): void { this.decorations.clearDecorations(); } diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/editors/currentCodeEditor.tsx b/packages/monaco/src/browser/contrib/merge-editor/view/editors/currentCodeEditor.tsx index 65fc7d2ede5..0f58fae6175 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/editors/currentCodeEditor.tsx +++ b/packages/monaco/src/browser/contrib/merge-editor/view/editors/currentCodeEditor.tsx @@ -1,4 +1,5 @@ import { Injectable } from '@opensumi/di'; +import { IEditorMouseEvent, MouseTargetType } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; import { Margin } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/viewParts/margin/margin'; import { Range } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/range'; import { LineRangeMapping } from '@opensumi/monaco-editor-core/esm/vs/editor/common/diff/linesDiffComputer'; @@ -7,19 +8,21 @@ import { IStandaloneEditorConstructionOptions } from '@opensumi/monaco-editor-co import { IDiffDecoration } from '../../model/decorations'; import { LineRange } from '../../model/line-range'; -import { EditorViewType } from '../../types'; +import { ACCEPT_CURRENT, CONFLICT_ACTIONS_ICON, EditorViewType, IGNORE } from '../../types'; import { flatInnerOriginal, flatOriginal } from '../../utils'; import { GuidelineWidget } from '../guideline-widget'; import { BaseCodeEditor } from './baseCodeEditor'; +// 用来寻址点击事件时的标记 +const ADDRESSING_TAG_CLASSNAME = 'ADDRESSING_TAG_CLASSNAME_'; @Injectable({ multiple: false }) export class CurrentCodeEditor extends BaseCodeEditor { public computeResultRangeMapping: LineRangeMapping[] = []; protected getMonacoEditorOptions(): IStandaloneEditorConstructionOptions { - return { readOnly: true }; + return { readOnly: true, lineNumbersMinChars: 5 }; } protected getRetainDecoration(): IDiffDecoration[] { @@ -34,6 +37,28 @@ export class CurrentCodeEditor extends BaseCodeEditor { return super.prepareRenderDecorations(ranges, innerChanges, 1); } + private onActionsClick(e: IEditorMouseEvent): void { + const element = e.target.element!; + + if (element.classList.contains(ACCEPT_CURRENT)) { + const toArry = Array.from(element.classList); + const find = toArry.find((c) => c.startsWith(ADDRESSING_TAG_CLASSNAME)); + if (find) { + const posiLine = Number(find.replace(ADDRESSING_TAG_CLASSNAME, '')); + if (typeof posiLine === 'number') { + // not implement + } + } + + return; + } + + if (element.classList.contains(IGNORE)) { + // not implement + return; + } + } + public getMonacoDecorationOptions( preDecorations: IModelDecorationOptions, ): Omit { @@ -49,9 +74,42 @@ export class CurrentCodeEditor extends BaseCodeEditor { public inputDiffComputingResult(changes: LineRangeMapping[]): void { this.computeResultRangeMapping = changes; - const [c, i] = [flatOriginal(changes), flatInnerOriginal(changes)]; - - this.renderDecorations(c, i); + const [ranges, innerRanges] = [flatOriginal(changes), flatInnerOriginal(changes)]; + + this.renderDecorations(ranges, innerRanges); + + this.registerActionsProvider({ + provideActionsItems: () => + ranges.map((range) => { + const posiMark = `${ADDRESSING_TAG_CLASSNAME}${range.startLineNumber}`; + return { + range, + decorationOptions: { + description: 'current editor view conflict actions', + glyphMarginClassName: CONFLICT_ACTIONS_ICON.RIGHT + ` offset-left ${posiMark}`, + marginClassName: CONFLICT_ACTIONS_ICON.CLOSE + ` ${posiMark}`, + }, + }; + }), + mouseDownGuard: (e: IEditorMouseEvent) => { + /** + * 注: 由于 current view 视图已经将 margin 区域和 code 区域交换了 + * 导致 editor mousedown 在处理点击事件的时候无法通过内部逻辑找到 target type 和 position 等信息 + * 进而导致没法知道点击了哪个位置上的 actions 图标 + * 所以这里通过 ADDRESSING_TAG_CLASSNAME 标志符加 lineNumber 给 className 类名的方式,来找到具体点击了哪个 actions + */ + if (!(e.target.type === MouseTargetType.UNKNOWN) || e.event.rightButton) { + return false; + } + + if (!e.target.element) { + return false; + } + + return true; + }, + onActionsClick: this.onActionsClick, + }); this.layout(); } diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/editors/incomingCodeEditor.tsx b/packages/monaco/src/browser/contrib/merge-editor/view/editors/incomingCodeEditor.tsx index 81619587f53..956da9c31db 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/editors/incomingCodeEditor.tsx +++ b/packages/monaco/src/browser/contrib/merge-editor/view/editors/incomingCodeEditor.tsx @@ -4,7 +4,7 @@ import { IModelDecorationOptions } from '@opensumi/monaco-editor-core/esm/vs/edi import { IStandaloneEditorConstructionOptions } from '@opensumi/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; import { IDiffDecoration } from '../../model/decorations'; -import { EditorViewType } from '../../types'; +import { CONFLICT_ACTIONS_ICON, EditorViewType } from '../../types'; import { flatInnerModified, flatModified } from '../../utils'; import { GuidelineWidget } from '../guideline-widget'; @@ -15,7 +15,7 @@ export class IncomingCodeEditor extends BaseCodeEditor { public computeResultRangeMapping: LineRangeMapping[] = []; protected getMonacoEditorOptions(): IStandaloneEditorConstructionOptions { - return { readOnly: true }; + return { readOnly: true, lineDecorationsWidth: 42 }; } protected getRetainDecoration(): IDiffDecoration[] { @@ -26,8 +26,12 @@ export class IncomingCodeEditor extends BaseCodeEditor { return []; } - public getMonacoDecorationOptions(): Omit { - return {}; + public getMonacoDecorationOptions( + preDecorations: IModelDecorationOptions, + ): Omit { + return { + linesDecorationsClassName: preDecorations.className, + }; } public getEditorViewType(): EditorViewType { @@ -37,7 +41,18 @@ export class IncomingCodeEditor extends BaseCodeEditor { public inputDiffComputingResult(changes: LineRangeMapping[]): void { this.computeResultRangeMapping = changes; - const [c, i] = [flatModified(changes), flatInnerModified(changes)]; - this.renderDecorations(c, i); + const [ranges, innerRanges] = [flatModified(changes), flatInnerModified(changes)]; + this.renderDecorations(ranges, innerRanges); + + this.registerActionsProvider({ + provideActionsItems: () => { + const decorationOptions = { + description: 'incoming editor view conflict actions', + glyphMarginClassName: CONFLICT_ACTIONS_ICON.CLOSE + ' offset-right', + firstLineDecorationClassName: CONFLICT_ACTIONS_ICON.LEFT, + }; + return ranges.map((range) => ({ range, decorationOptions })); + }, + }); } } diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.tsx b/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.tsx index 01583597337..fa7cfa278f9 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.tsx +++ b/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.tsx @@ -15,7 +15,7 @@ import { BaseCodeEditor } from './baseCodeEditor'; @Injectable({ multiple: false }) export class ResultCodeEditor extends BaseCodeEditor { protected getMonacoEditorOptions(): IStandaloneEditorConstructionOptions { - return {}; + return { lineNumbersMinChars: 2, lineDecorationsWidth: 24 }; } private currentBaseRange: 0 | 1; @@ -58,8 +58,12 @@ export class ResultCodeEditor extends BaseCodeEditor { return this.decorations.getLineWidgets(); } - public getMonacoDecorationOptions(): Omit { - return {}; + public getMonacoDecorationOptions( + preDecorations: IModelDecorationOptions, + ): Omit { + return { + linesDecorationsClassName: preDecorations.className, + }; } public getEditorViewType(): EditorViewType { diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/merge-editor.less b/packages/monaco/src/browser/contrib/merge-editor/view/merge-editor.less index 316cb2b5655..63443b3ae22 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/merge-editor.less +++ b/packages/monaco/src/browser/contrib/merge-editor/view/merge-editor.less @@ -74,4 +74,20 @@ } } } + + .conflict-actions { + cursor: pointer; + z-index: 1; + + &.offset-left { + left: 18px !important; + z-index: 2; + } + + &.offset-right { + left: initial !important; + right: 8px !important; + z-index: 2; + } + } } diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/stickiness-connect-manager.tsx b/packages/monaco/src/browser/contrib/merge-editor/view/stickiness-connect-manager.tsx index 65e0d7a1fcb..7a12b2f8dda 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/stickiness-connect-manager.tsx +++ b/packages/monaco/src/browser/contrib/merge-editor/view/stickiness-connect-manager.tsx @@ -138,12 +138,22 @@ export class StickinessConnectManager extends Disposable { flatModified(computeResultRangeMapping), ]; const lineHeight = view!.getEditor().getOption(EditorOption.lineHeight); - const { contentLeft } = this.resultView!.getEditor().getLayoutInfo(); + const layoutInfo = + editorType === 'current' + ? this.resultView!.getEditor().getLayoutInfo() + : this.incomingView!.getEditor().getLayoutInfo(); + + let marginWidth: number = layoutInfo.contentLeft; + + const lineDecorationsWidth = (editorType === 'current' ? this.resultView : this.incomingView)! + .getEditor() + .getOption(EditorOption.lineDecorationsWidth); + marginWidth -= typeof lineDecorationsWidth === 'number' ? lineDecorationsWidth : 0; const pieces = this.generatePiece( originRange, modifyRange, - { marginWidth: contentLeft, lineHeight }, + { marginWidth, lineHeight }, editorType === 'incoming' ? 1 : 0, ).map((p) => p.movePosition(...this.getScrollTopWithBoth(editorType)));