From 457890631853aca5663a20201f3fadfdcf02a865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E5=A3=B0?= Date: Mon, 15 Apr 2024 22:11:55 +0800 Subject: [PATCH 01/27] chore: update en translation --- packages/i18n/src/common/en-US.lang.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/i18n/src/common/en-US.lang.ts b/packages/i18n/src/common/en-US.lang.ts index 520bf55bcc..43d74733ba 100644 --- a/packages/i18n/src/common/en-US.lang.ts +++ b/packages/i18n/src/common/en-US.lang.ts @@ -1433,14 +1433,14 @@ export const localizationBundle = { 'mergeEditor.conflict.action.apply.confirm.continue': 'Continue Merge', 'mergeEditor.conflict.action.apply.confirm.complete': 'Apply Changes', 'mergeEditor.action.button.apply': 'Apply', - 'mergeEditor.action.button.accept.left': 'Accept left', - 'mergeEditor.action.button.accept.right': 'Accept right', - 'mergeEditor.open.3way': '3-way Editor', - 'mergeEditor.conflict.prev': 'Previous conflict', - 'mergeEditor.conflict.next': 'Next conflict', - 'mergeEditor.conflict.resolve.all': 'AI one click solution', + 'mergeEditor.action.button.accept.left': 'Accept Left', + 'mergeEditor.action.button.accept.right': 'Accept Right', + 'mergeEditor.open.3way': '3-Way Editor', + 'mergeEditor.conflict.prev': 'Previous Conflict', + 'mergeEditor.conflict.next': 'Next Conflict', + 'mergeEditor.conflict.resolve.all': 'AI One-click Resolution', 'mergeEditor.conflict.resolve.all.stop': 'Stop All', - 'mergeEditor.open.tradition': 'Tradition editor', + 'mergeEditor.open.tradition': 'Tradition Editor', // #region AI Native 'aiNative.chat.ai.assistant.name': 'AI Assistant', From 8938ccbdeb0c57b212f8ab66bfe21753055620bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E5=A3=B0?= Date: Mon, 15 Apr 2024 22:29:39 +0800 Subject: [PATCH 02/27] feat: optimize merge --- .../src/browser/ai-editor.contribution.ts | 8 ++- .../src/browser/merge-conflict/index.ts | 3 +- .../MergeEditorFloatComponents.tsx | 40 ++++++------- packages/i18n/src/common/en-US.lang.ts | 3 +- packages/i18n/src/common/zh-CN.lang.ts | 3 +- .../merge-editor/merge-editor.service.ts | 14 ++--- .../contrib/merge-editor/model/line-range.ts | 2 +- .../view/editors/resultCodeEditor.ts | 4 +- .../contrib/merge-editor/view/grid.tsx | 57 ++++++++++++++----- 9 files changed, 83 insertions(+), 51 deletions(-) diff --git a/packages/ai-native/src/browser/ai-editor.contribution.ts b/packages/ai-native/src/browser/ai-editor.contribution.ts index 21b8a886ee..c83e96cd75 100644 --- a/packages/ai-native/src/browser/ai-editor.contribution.ts +++ b/packages/ai-native/src/browser/ai-editor.contribution.ts @@ -7,6 +7,7 @@ import { AINativeSettingSectionsId, CancelResponse, CancellationToken, + ChatResponse, ContributionProvider, Disposable, ErrorResponse, @@ -23,7 +24,6 @@ import { SupportLogNamespace, runWhenIdle, } from '@opensumi/ide-core-common'; -import { ChatResponse } from '@opensumi/ide-core-common'; import { DesignBrowserCtxMenuService } from '@opensumi/ide-design/lib/browser/override/menu.service'; import { EditorSelectionChangeEvent, IEditor, IEditorFeatureContribution } from '@opensumi/ide-editor/lib/browser'; import * as monaco from '@opensumi/ide-monaco'; @@ -229,7 +229,11 @@ export class AIEditorContribution extends Disposable implements IEditorFeatureCo // 判断用户是否选择了一块区域或者移动光标 取消掉请补全求 const selectionChange = () => { this.aiCompletionsService.hideStatusBarItem(); - const selection = monacoEditor.getSelection()!; + const selection = monacoEditor.getSelection(); + if (!selection) { + return; + } + // 判断是否选中区域 if (selection.startLineNumber !== selection.endLineNumber || selection.startColumn !== selection.endColumn) { this.aiInlineCompletionsProvider.cancelRequest(); diff --git a/packages/ai-native/src/browser/merge-conflict/index.ts b/packages/ai-native/src/browser/merge-conflict/index.ts index 875b4adf88..27b942116e 100644 --- a/packages/ai-native/src/browser/merge-conflict/index.ts +++ b/packages/ai-native/src/browser/merge-conflict/index.ts @@ -1,7 +1,6 @@ import debounce from 'lodash/debounce'; import { Autowired, INJECTOR_TOKEN, Injector } from '@opensumi/di'; -import { message } from '@opensumi/ide-components'; import { AINativeConfigService, ClientAppContribution } from '@opensumi/ide-core-browser'; import { MergeConflictReportService } from '@opensumi/ide-core-browser/lib/ai-native/conflict-report.service'; import { @@ -502,7 +501,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont } private getUri(): string { - return this.getModel().uri.toString(); + return this.getModel()?.uri.toString(); } /* cache widget */ diff --git a/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx b/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx index c16d04b535..83ba8689ce 100644 --- a/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx +++ b/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx @@ -16,6 +16,8 @@ import { import styles from '../editor.module.less'; import { ReactEditorComponent } from '../types'; +const gitMergeChangesSet = new Set(['git.mergeChanges']); + export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ resource }) => { const aiNativeConfigService = useInjectable(AINativeConfigService); const commandService = useInjectable(CommandService); @@ -24,8 +26,6 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ const [isVisiable, setIsVisiable] = useState(false); - const gitMergeChangesSet = new Set(['git.mergeChanges']); - useEffect(() => { const run = () => { const mergeChanges = contextKeyService.getValue('git.mergeChanges') || []; @@ -41,7 +41,7 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ return () => disposed.dispose(); }, [resource]); - const [isAiResolving, setIsAiResolving] = useState(false); + const [isAIResolving, setIsAIResolving] = useState(false); const handleOpenMergeEditor = useCallback(async () => { const { uri } = resource; @@ -52,28 +52,28 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ }); }, [resource]); - const isSupportAiResolve = useCallback( + const isSupportAIResolve = useCallback( () => aiNativeConfigService.capabilities.supportsConflictResolve, [aiNativeConfigService], ); - const handlePrev = () => { + const handlePrev = useCallback(() => { commandService.tryExecuteCommand('merge-conflict.previous'); - }; + }, []); - const handleNext = () => { + const handleNext = useCallback(() => { commandService.tryExecuteCommand('merge-conflict.next'); - }; + }, []); const handleAIResolve = useCallback(async () => { - setIsAiResolving(true); - if (isAiResolving) { + setIsAIResolving(true); + if (isAIResolving) { await commandService.executeCommand('merge-conflict.ai.all-accept-stop', resource.uri); } else { await commandService.executeCommand('merge-conflict.ai.all-accept', resource.uri); } - setIsAiResolving(false); - }, [resource, isAiResolving]); + setIsAIResolving(false); + }, [resource, isAIResolving]); const handleReset = useCallback(() => { commandService.executeCommand('merge-conflict.ai.all-reset', resource.uri); @@ -85,12 +85,12 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ return (
-
- - @@ -99,24 +99,24 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ - - {isSupportAiResolve() && ( + {isSupportAIResolve() && ( - @@ -170,27 +179,45 @@ const MergeActions: React.FC = () => { +
+ + +
+ + + - - {isSupportAiResolve() && ( + {isSupportAIResolve() && (
From 06d6e1ccbb23be06b43ca7817efc00ecd13aa1ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E5=A3=B0?= Date: Tue, 16 Apr 2024 10:57:10 +0800 Subject: [PATCH 03/27] refactor: update code --- .../src/browser/merge-conflict/index.ts | 32 +++++++++---------- .../merge-conflict/conflict-parser.ts} | 3 +- .../src/browser/merge-conflict/index.ts | 2 ++ .../src/browser/merge-conflict/types.ts | 0 4 files changed, 18 insertions(+), 19 deletions(-) rename packages/{ai-native/src/browser/merge-conflict/cache-conflicts.ts => editor/src/browser/merge-conflict/conflict-parser.ts} (99%) create mode 100644 packages/editor/src/browser/merge-conflict/index.ts rename packages/{ai-native => editor}/src/browser/merge-conflict/types.ts (100%) diff --git a/packages/ai-native/src/browser/merge-conflict/index.ts b/packages/ai-native/src/browser/merge-conflict/index.ts index 27b942116e..a0e35d7e99 100644 --- a/packages/ai-native/src/browser/merge-conflict/index.ts +++ b/packages/ai-native/src/browser/merge-conflict/index.ts @@ -4,7 +4,6 @@ import { Autowired, INJECTOR_TOKEN, Injector } from '@opensumi/di'; import { AINativeConfigService, ClientAppContribution } from '@opensumi/ide-core-browser'; import { MergeConflictReportService } from '@opensumi/ide-core-browser/lib/ai-native/conflict-report.service'; import { - AIBackSerivcePath, CancelResponse, CancellationTokenSource, ChatResponse, @@ -19,7 +18,6 @@ import { ErrorResponse, Event, ExtensionActivatedEvent, - IAIBackService, IConflictContentMetadata, IEventBus, IInternalResolveConflictRegistry, @@ -34,6 +32,11 @@ import { localize, } from '@opensumi/ide-core-common'; import { IEditor, WorkbenchEditorService } from '@opensumi/ide-editor/lib/browser'; +import { + CommitType, + DocumentMergeConflict, + MergeConflictParser, +} from '@opensumi/ide-editor/lib/browser/merge-conflict'; import * as monaco from '@opensumi/ide-monaco'; import { ITextModel } from '@opensumi/ide-monaco'; import { BaseInlineContentWidget } from '@opensumi/ide-monaco/lib/browser/ai-native/BaseInlineContentWidget'; @@ -53,9 +56,7 @@ import { languageFeaturesService } from '@opensumi/ide-monaco/lib/browser/monaco import { Position } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/position'; import { IValidEditOperation } from '@opensumi/monaco-editor-core/esm/vs/editor/common/model'; -import { CacheConflict, DocumentMergeConflict } from './cache-conflicts'; import { OverrideResolveResultWidget as ResolveResultWidget } from './override-resolve-result-widget'; -import { CommitType } from './types'; export namespace MERGE_CONFLICT { const CATEGORY = 'MergeConflict'; export const AI_ACCEPT: Command = { @@ -211,10 +212,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont private readonly aiNativeConfigService: AINativeConfigService; @Autowired() - private readonly cacheConflicts: CacheConflict; - - @Autowired(AIBackSerivcePath) - private aiBackService: IAIBackService; + private readonly conflictParser: MergeConflictParser; @Autowired(MergeConflictReportService) private readonly mergeConflictReportService: MergeConflictReportService; @@ -248,7 +246,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont super.dispose(); this.cancelRequestToken(); this.mergeConflictReportService.dispose(); - this.cacheConflicts.dispose(); + this.conflictParser.dispose(); } onDidStart(): MaybePromise { @@ -443,7 +441,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont } private updateReportData() { - const allConflictCache = this.cacheConflicts.getAllConflictsByUri(this.getUri()); + const allConflictCache = this.conflictParser.getAllConflictsByUri(this.getUri()); let conflictPointNum = 0; let useAiConflictPointNum = 0; let receiveNum = 0; @@ -586,7 +584,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont }), commands.registerCommand(MERGE_CONFLICT.ALL_RESET, { execute: async (uri: Uri) => { - const content = this.cacheConflicts.getConflictText(uri.toString()); + const content = this.conflictParser.getConflictText(uri.toString()); this.cancelRequestToken(); if (content) { if (this.editorService.currentEditor?.currentUri?.toString() === uri.toString()) { @@ -594,7 +592,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont if (editor) { const model = editor.getModel(); model?.setValue(content); - this.cacheConflicts.deleteConflictText(uri.toString()); + this.conflictParser.deleteConflictText(uri.toString()); this.cleanAllCache(); } } @@ -608,7 +606,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont if (!document) { return Promise.resolve(); } - const conflicts = this.cacheConflicts.scanDocument(document as monaco.editor.ITextModel); + const conflicts = this.conflictParser.scanDocument(document as monaco.editor.ITextModel); if (!conflicts?.length) { return Promise.resolve(); } @@ -661,7 +659,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont document: monaco.editor.ITextModel, _token: monaco.CancellationToken, ): Promise { - const conflicts = this.cacheConflicts.scanDocument(document); + const conflicts = this.conflictParser.scanDocument(document); if (!conflicts.length) { return null; } @@ -791,7 +789,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont if (!isRegenerate) { // 记录处理数量 非重新生成 conflict 存在 const uri = this.getModel().uri.toString(); - const cacheConflictRanges = this.cacheConflicts.getAllConflictsByUri(uri); + const cacheConflictRanges = this.conflictParser.getAllConflictsByUri(uri); if (cacheConflictRanges) { const cacheConflict = cacheConflictRanges.find((cacheConflict) => { if (cacheConflict.isResolved) { @@ -805,7 +803,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont } }); if (cacheConflict && !cacheConflict.isResolved) { - this.cacheConflicts.setConflictResolved(uri, cacheConflict.id); + this.conflictParser.setConflictResolved(uri, cacheConflict.id); } } } @@ -847,7 +845,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont if (!document) { return Promise.resolve(); } - const conflicts = this.cacheConflicts.scanDocument(document); + const conflicts = this.conflictParser.scanDocument(document); if (!conflicts.length) { return Promise.resolve(); } diff --git a/packages/ai-native/src/browser/merge-conflict/cache-conflicts.ts b/packages/editor/src/browser/merge-conflict/conflict-parser.ts similarity index 99% rename from packages/ai-native/src/browser/merge-conflict/cache-conflicts.ts rename to packages/editor/src/browser/merge-conflict/conflict-parser.ts index f15c3ce432..b3ed1b9fff 100644 --- a/packages/ai-native/src/browser/merge-conflict/cache-conflicts.ts +++ b/packages/editor/src/browser/merge-conflict/conflict-parser.ts @@ -50,9 +50,8 @@ export class TextLine { } } -// 内置 MergeConflict 插件 以支持AI交互 @Injectable() -export class CacheConflict extends Disposable { +export class MergeConflictParser extends Disposable { private _conflictTextCaches = new Map(); private _conflictRangeCaches = new Map(); diff --git a/packages/editor/src/browser/merge-conflict/index.ts b/packages/editor/src/browser/merge-conflict/index.ts new file mode 100644 index 0000000000..ebfcb139d9 --- /dev/null +++ b/packages/editor/src/browser/merge-conflict/index.ts @@ -0,0 +1,2 @@ +export * from './conflict-parser'; +export * from './types'; diff --git a/packages/ai-native/src/browser/merge-conflict/types.ts b/packages/editor/src/browser/merge-conflict/types.ts similarity index 100% rename from packages/ai-native/src/browser/merge-conflict/types.ts rename to packages/editor/src/browser/merge-conflict/types.ts From 2ad7546d6a4832e012d81d0af75ca7ce21a066e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E5=A3=B0?= Date: Tue, 16 Apr 2024 13:12:35 +0800 Subject: [PATCH 04/27] chore: use menubar view --- packages/startup/entry/web/render-app.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/startup/entry/web/render-app.tsx b/packages/startup/entry/web/render-app.tsx index 63a8df40c1..7979cc510f 100644 --- a/packages/startup/entry/web/render-app.tsx +++ b/packages/startup/entry/web/render-app.tsx @@ -1,6 +1,3 @@ -// eslint-disable-next-line no-console -console.time('Render'); - import { Injector } from '@opensumi/di'; import { IClientAppOpts, SlotLocation } from '@opensumi/ide-core-browser'; import { ClientApp } from '@opensumi/ide-core-browser/lib/bootstrap/app'; @@ -17,6 +14,9 @@ import { DefaultLayout } from './layout'; const CLIENT_ID = 'W_' + uuid(); export async function renderApp(opts: IClientAppOpts) { + // eslint-disable-next-line no-console + console.time('Render'); + const defaultHost = process.env.HOST || window.location.hostname; const injector = new Injector(); opts.workspaceDir = @@ -90,5 +90,11 @@ export const getDefaultClientAppOpts = ({ useVSCodeWorkspaceConfiguration: true, // 开启 core-browser 对 OpenSumi DevTools 的支持,默认为关闭 devtools: true, + AINativeConfig: { + layout: { + useMenubarView: true, + useMergeRightWithLeftPanel: true, + }, + }, ...opts, }); From ed3cdb604b1ef13f76f975a803ca1439f2bf10ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E5=A3=B0?= Date: Tue, 16 Apr 2024 13:43:16 +0800 Subject: [PATCH 05/27] feat: use lru map in conflict parser --- .../src/browser/merge-conflict/index.ts | 1 + .../browser/merge-conflict/conflict-parser.ts | 18 ++++++++- packages/utils/src/lru-map.ts | 7 ++++ packages/utils/src/map.ts | 40 ------------------- 4 files changed, 24 insertions(+), 42 deletions(-) diff --git a/packages/ai-native/src/browser/merge-conflict/index.ts b/packages/ai-native/src/browser/merge-conflict/index.ts index a0e35d7e99..f445610eac 100644 --- a/packages/ai-native/src/browser/merge-conflict/index.ts +++ b/packages/ai-native/src/browser/merge-conflict/index.ts @@ -753,6 +753,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont range, text, }; + let validEditOperation: IValidEditOperation[] = []; const selections = this.getModel()?.pushEditOperations(null, [edit], (operation) => { validEditOperation = operation; diff --git a/packages/editor/src/browser/merge-conflict/conflict-parser.ts b/packages/editor/src/browser/merge-conflict/conflict-parser.ts index b3ed1b9fff..c28f72849f 100644 --- a/packages/editor/src/browser/merge-conflict/conflict-parser.ts +++ b/packages/editor/src/browser/merge-conflict/conflict-parser.ts @@ -5,7 +5,7 @@ // Some code copied and modified from https://github.com/microsoft/vscode/blob/main/extensions/merge-conflict/src/mergeConflictParser.ts import { Injectable } from '@opensumi/di'; -import { Disposable, uuid } from '@opensumi/ide-core-common'; +import { Disposable, LRUCache, uuid } from '@opensumi/ide-core-common'; import * as monaco from '@opensumi/ide-monaco'; import { ICacheDocumentMergeConflict, IDocumentMergeConflictDescriptor, IMergeRegion } from './types'; @@ -52,11 +52,22 @@ export class TextLine { @Injectable() export class MergeConflictParser extends Disposable { + cache = new LRUCache(100); + private _conflictTextCaches = new Map(); private _conflictRangeCaches = new Map(); + private static createCacheKey(document: monaco.editor.ITextModel) { + return `${document.uri.toString()}-${document.getAlternativeVersionId()}`; + } + scanDocument(document: monaco.editor.ITextModel) { + const cacheKey = MergeConflictParser.createCacheKey(document); + if (this.cache.has(cacheKey)) { + return this.cache.get(cacheKey)!; + } + // Scan each line in the document, we already know there is at least a <<<<<<< and // >>>>>> marker within the document, we need to group these into conflict ranges. // We initially build a scan match, that references the lines of the header, splitter @@ -128,7 +139,10 @@ export class MergeConflictParser extends Disposable { this._conflictRangeCaches.set(document.uri.toString(), conflictRanges); } - return conflictDescriptors?.filter(Boolean).map((descriptor) => new DocumentMergeConflict(descriptor)); + const result = conflictDescriptors?.filter(Boolean).map((descriptor) => new DocumentMergeConflict(descriptor)); + this.cache.set(cacheKey, result); + + return result; } getConflictText(uri: string) { return this._conflictTextCaches.get(uri); diff --git a/packages/utils/src/lru-map.ts b/packages/utils/src/lru-map.ts index fe311e2191..e3d49f1afa 100644 --- a/packages/utils/src/lru-map.ts +++ b/packages/utils/src/lru-map.ts @@ -104,10 +104,17 @@ export class LRUMap extends Map { } const NOW = Symbol('now'); + /** * 支持过期时间 */ export class StaleLRUMap extends LRUMap { + /** + * + * @param hardLimit + * @param softLimit + * @param maxAge the unit is milliseconds + */ constructor(hardLimit: number, softLimit: number, private maxAge: number) { super(hardLimit, softLimit); } diff --git a/packages/utils/src/map.ts b/packages/utils/src/map.ts index 480b505226..99445d67e6 100644 --- a/packages/utils/src/map.ts +++ b/packages/utils/src/map.ts @@ -622,46 +622,6 @@ export class LinkedMap { return result; } - /* VS Code / Monaco editor runs on es5 which has no Symbol.iterator - keys(): IterableIterator { - const current = this._head; - const iterator: IterableIterator = { - [Symbol.iterator]() { - return iterator; - }, - next():IteratorResult { - if (current) { - const result = { value: current.key, done: false }; - current = current.next; - return result; - } else { - return { value: undefined, done: true }; - } - } - }; - return iterator; - } - - values(): IterableIterator { - const current = this._head; - const iterator: IterableIterator = { - [Symbol.iterator]() { - return iterator; - }, - next():IteratorResult { - if (current) { - const result = { value: current.value, done: false }; - current = current.next; - return result; - } else { - return { value: undefined, done: true }; - } - } - }; - return iterator; - } - */ - protected trimOld(newSize: number) { if (newSize >= this.size) { return; From fa1ba8cc5b997c5cb86e5bcc5a1c911f6c0b4323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E5=A3=B0?= Date: Tue, 16 Apr 2024 14:13:29 +0800 Subject: [PATCH 06/27] refactor: update code --- .../src/browser/merge-conflict/index.ts | 33 ++--- .../override-resolve-result-widget.tsx | 4 +- .../core-browser/src/common/common.command.ts | 20 +++ .../MergeEditorFloatComponents.tsx | 12 +- .../browser/contrib/merge-editor/constants.ts | 4 + .../merge-editor/merge-editor.service.ts | 16 +-- .../merge-editor/view/actions-manager.ts | 119 +++++++++--------- .../contrib/merge-editor/view/grid.tsx | 11 +- .../widget/resolve-result-widget.tsx | 6 +- 9 files changed, 120 insertions(+), 105 deletions(-) create mode 100644 packages/monaco/src/browser/contrib/merge-editor/constants.ts diff --git a/packages/ai-native/src/browser/merge-conflict/index.ts b/packages/ai-native/src/browser/merge-conflict/index.ts index f445610eac..78c527ab00 100644 --- a/packages/ai-native/src/browser/merge-conflict/index.ts +++ b/packages/ai-native/src/browser/merge-conflict/index.ts @@ -1,7 +1,7 @@ import debounce from 'lodash/debounce'; import { Autowired, INJECTOR_TOKEN, Injector } from '@opensumi/di'; -import { AINativeConfigService, ClientAppContribution } from '@opensumi/ide-core-browser'; +import { AINativeConfigService, ClientAppContribution, MERGE_CONFLICT_COMMANDS } from '@opensumi/ide-core-browser'; import { MergeConflictReportService } from '@opensumi/ide-core-browser/lib/ai-native/conflict-report.service'; import { CancelResponse, @@ -57,25 +57,6 @@ import { Position } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core import { IValidEditOperation } from '@opensumi/monaco-editor-core/esm/vs/editor/common/model'; import { OverrideResolveResultWidget as ResolveResultWidget } from './override-resolve-result-widget'; -export namespace MERGE_CONFLICT { - const CATEGORY = 'MergeConflict'; - export const AI_ACCEPT: Command = { - id: 'merge-conflict.ai.accept', - category: CATEGORY, - }; - export const ALL_RESET: Command = { - id: 'merge-conflict.ai.all-reset', - category: CATEGORY, - }; - export const AI_ALL_ACCEPT: Command = { - id: 'merge-conflict.ai.all-accept', - category: CATEGORY, - }; - export const AI_ALL_ACCEPT_STOP: Command = { - id: 'merge-conflict.ai.all-accept-stop', - category: CATEGORY, - }; -} const MERGE_CONFLICT_CODELENS_STYLE = 'merge-conflict-codelens-style'; @@ -577,12 +558,12 @@ export class MergeConflictContribution extends Disposable implements CommandCont registerCommands(commands: CommandRegistry): void { this.disposables.push( - commands.registerCommand(MERGE_CONFLICT.AI_ACCEPT, { + commands.registerCommand(MERGE_CONFLICT_COMMANDS.AI_ACCEPT, { execute: async (type: CommitType, conflict: DocumentMergeConflict) => { this.conflictAIAccept(conflict); }, }), - commands.registerCommand(MERGE_CONFLICT.ALL_RESET, { + commands.registerCommand(MERGE_CONFLICT_COMMANDS.ALL_RESET, { execute: async (uri: Uri) => { const content = this.conflictParser.getConflictText(uri.toString()); this.cancelRequestToken(); @@ -600,7 +581,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont }, }), - commands.registerCommand(MERGE_CONFLICT.AI_ALL_ACCEPT, { + commands.registerCommand(MERGE_CONFLICT_COMMANDS.AI_ALL_ACCEPT, { execute: async () => { const document = this.getModel(); if (!document) { @@ -617,7 +598,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont await this.acceptAllConflict(); }, }), - commands.registerCommand(MERGE_CONFLICT.AI_ALL_ACCEPT_STOP, { + commands.registerCommand(MERGE_CONFLICT_COMMANDS.AI_ALL_ACCEPT_STOP, { execute: async () => { this.cancelRequestToken(); }, @@ -666,7 +647,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont const items: monaco.languages.CodeLens[] = []; conflicts.forEach((conflict) => { const aiAcceptCommand = { - id: MERGE_CONFLICT.AI_ACCEPT.id, + id: MERGE_CONFLICT_COMMANDS.AI_ACCEPT.id, title: `$(ai-magic) ${localize('mergeEditor.conflict.resolve.all')}`, arguments: ['know-conflict', conflict], tooltip: localize('mergeEditor.conflict.resolve.all'), @@ -680,7 +661,7 @@ export class MergeConflictContribution extends Disposable implements CommandCont items.push({ range: conflict.range, command: aiAcceptCommand, - id: MERGE_CONFLICT.AI_ACCEPT.id, + id: MERGE_CONFLICT_COMMANDS.AI_ACCEPT.id, }); }); diff --git a/packages/ai-native/src/browser/merge-conflict/override-resolve-result-widget.tsx b/packages/ai-native/src/browser/merge-conflict/override-resolve-result-widget.tsx index 8abd458eb6..c887365a50 100644 --- a/packages/ai-native/src/browser/merge-conflict/override-resolve-result-widget.tsx +++ b/packages/ai-native/src/browser/merge-conflict/override-resolve-result-widget.tsx @@ -13,7 +13,7 @@ import { import { ResultCodeEditor } from '@opensumi/ide-monaco/lib/browser/contrib/merge-editor/view/editors/resultCodeEditor'; import { ResolveResultWidget, - WapperAiInlineResult, + WapperAIInlineResult, } from '@opensumi/ide-monaco/lib/browser/contrib/merge-editor/widget/resolve-result-widget'; @Injectable({ multiple: true }) @@ -56,7 +56,7 @@ export class OverrideResolveResultWidget extends ResolveResultWidget { return ( - = ({ ); const handlePrev = useCallback(() => { - commandService.tryExecuteCommand('merge-conflict.previous'); + commandService.tryExecuteCommand(MergeConflictCommands.Previous); }, []); const handleNext = useCallback(() => { - commandService.tryExecuteCommand('merge-conflict.next'); + commandService.tryExecuteCommand(MergeConflictCommands.Next); }, []); const handleAIResolve = useCallback(async () => { setIsAIResolving(true); if (isAIResolving) { - await commandService.executeCommand('merge-conflict.ai.all-accept-stop', resource.uri); + await commandService.executeCommand(MERGE_CONFLICT_COMMANDS.AI_ALL_ACCEPT_STOP.id, resource.uri); } else { - await commandService.executeCommand('merge-conflict.ai.all-accept', resource.uri); + await commandService.executeCommand(MERGE_CONFLICT_COMMANDS.AI_ALL_ACCEPT.id, resource.uri); } setIsAIResolving(false); }, [resource, isAIResolving]); const handleReset = useCallback(() => { - commandService.executeCommand('merge-conflict.ai.all-reset', resource.uri); + commandService.executeCommand(MERGE_CONFLICT_COMMANDS.ALL_RESET.id, resource.uri); }, [resource]); if (!isVisiable) { diff --git a/packages/monaco/src/browser/contrib/merge-editor/constants.ts b/packages/monaco/src/browser/contrib/merge-editor/constants.ts new file mode 100644 index 0000000000..b85b2c8174 --- /dev/null +++ b/packages/monaco/src/browser/contrib/merge-editor/constants.ts @@ -0,0 +1,4 @@ +export const MergeConflictCommands = { + Previous: 'merge-conflict.previous', + Next: 'merge-conflict.next', +} as const; 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 30348dd2f9..9adb10b2e0 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 @@ -60,7 +60,7 @@ export class MergeEditorService extends Disposable { private computerDiffModel: ComputerDiffModel; private actionsManager: ActionsManager; - private isCancelAllAiResolveConflict = false; + private isCancelAllAIResolveConflict = false; public scrollSynchronizer: ScrollSynchronizer; public stickinessConnectManager: StickinessConnectManager; @@ -297,13 +297,13 @@ export class MergeEditorService extends Disposable { } } - public async stopAllAiResolveConflict(): Promise { - this.isCancelAllAiResolveConflict = true; + public async stopAllAIResolveConflict(): Promise { + this.isCancelAllAIResolveConflict = true; this.resultView.cancelRequestToken(); this.resultView.hideStopWidget(); } - public async handleAiResolveConflict(): Promise { + public async handleAIResolveConflict(): Promise { this.mergeConflictReportService.reportIncrementNum(this.resultView.getUri(), 'clickAllNum'); this.listenIntelligentLoadingChange(); @@ -317,7 +317,7 @@ export class MergeEditorService extends Disposable { }, 1); runWhenIdle(async () => { - this.isCancelAllAiResolveConflict = false; + this.isCancelAllAIResolveConflict = false; const allRanges = this.resultView.getAllDiffRanges(); const conflictPointRanges = allRanges.filter( @@ -329,9 +329,9 @@ export class MergeEditorService extends Disposable { for await (const range of conflictPointRanges) { const flushRange = this.resultView.getFlushRange(range) || range; - const result = await this.actionsManager.handleAiConflictResolve(flushRange, { isRegenerate: false }); - if (this.isCancelAllAiResolveConflict) { - this.isCancelAllAiResolveConflict = false; + const result = await this.actionsManager.handleAIConflictResolve(flushRange, { isRegenerate: false }); + if (this.isCancelAllAIResolveConflict) { + this.isCancelAllAIResolveConflict = false; return; } 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 index 9ec04c22ff..5256b17435 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/actions-manager.ts +++ b/packages/monaco/src/browser/contrib/merge-editor/view/actions-manager.ts @@ -271,7 +271,7 @@ export class ActionsManager extends Disposable { /** * 处理 AI 智能解决冲突时的逻辑 */ - public async handleAiConflictResolve( + public async handleAIConflictResolve( flushRange: LineRange, options: { isRegenerate: boolean; @@ -369,14 +369,17 @@ export class ActionsManager extends Disposable { private markComplete(range: LineRange): void { const { turnDirection } = range; - if (turnDirection === ETurnDirection.CURRENT) { - this.mappingManagerService.markCompleteTurnLeft(range); - this.currentView!.updateDecorations().updateActions(); - } - - if (turnDirection === ETurnDirection.INCOMING) { - this.mappingManagerService.markCompleteTurnRight(range); - this.incomingView!.updateDecorations().updateActions(); + switch (turnDirection) { + case ETurnDirection.CURRENT: { + this.mappingManagerService.markCompleteTurnLeft(range); + this.currentView!.updateDecorations().updateActions(); + break; + } + case ETurnDirection.INCOMING: { + this.mappingManagerService.markCompleteTurnRight(range); + this.incomingView!.updateDecorations().updateActions(); + break; + } } } @@ -405,57 +408,61 @@ export class ActionsManager extends Disposable { this.resultView.onDidConflictActions, this.incomingView.onDidConflictActions, )(async ({ range, action }) => { - if (action === ACCEPT_CURRENT_ACTIONS) { - this.handleAcceptChange(range, (_range, oppositeRange, { applyText }) => [ - { range: oppositeRange, text: applyText }, - ]); - } - - if (action === IGNORE_ACTIONS) { - this.markComplete(range); - this.resultView?.updateActions(); - } - - if (action === REVOKE_ACTIONS) { - this.handleAcceptRevoke(range); - } - - if (action === ACCEPT_COMBINATION_ACTIONS) { - this.handleAcceptCombination(range); - } - - /** - * range 如果是 merge range 合成的,当 accept 某一视图的代码变更时,另一边的 accpet 就变成 append 追加内容,而不是覆盖内容 - */ - if (action === APPEND_ACTIONS) { - this.handleAcceptChange(range, (_, oppositeRange, { applyText, eol }) => [ + switch (action) { + case ACCEPT_CURRENT_ACTIONS: + this.handleAcceptChange(range, (_range, oppositeRange, { applyText }) => [ + { range: oppositeRange, text: applyText }, + ]); + break; + case IGNORE_ACTIONS: + this.markComplete(range); + this.resultView?.updateActions(); + break; + case REVOKE_ACTIONS: + this.handleAcceptRevoke(range); + break; + case ACCEPT_COMBINATION_ACTIONS: + this.handleAcceptCombination(range); + break; + case APPEND_ACTIONS: /** - * 在 diff 区域的最后一行追加代码内容 + * range 如果是 merge range 合成的,当 accept 某一视图的代码变更时,另一边的 accpet 就变成 append 追加内容,而不是覆盖内容 */ - { - range: LineRange.fromPositions( - oppositeRange.endLineNumberExclusive, - oppositeRange.endLineNumberExclusive, - ), - text: applyText, - }, - ]); + this.handleAcceptChange(range, (_, oppositeRange, { applyText, eol }) => [ + /** + * 在 diff 区域的最后一行追加代码内容 + */ + { + range: LineRange.fromPositions( + oppositeRange.endLineNumberExclusive, + oppositeRange.endLineNumberExclusive, + ), + text: applyText, + }, + ]); + break; + /** + * 处理 AI 智能解决冲突 + */ + case AI_RESOLVE_ACTIONS: + case AI_RESOLVE_REGENERATE_ACTIONS: + if (this.resultView) { + const flushRange = this.resultView.getFlushRange(range) || range; + await this.handleAIConflictResolve(flushRange, { + isRegenerate: action === AI_RESOLVE_REGENERATE_ACTIONS, + }); + } + break; } - - /** - * 处理 AI 智能解决冲突 - */ - if ((action === AI_RESOLVE_ACTIONS || action === AI_RESOLVE_REGENERATE_ACTIONS) && this.resultView) { - const flushRange = this.resultView.getFlushRange(range) || range; - - await this.handleAiConflictResolve(flushRange, { - isRegenerate: action === AI_RESOLVE_REGENERATE_ACTIONS, - }); + if (this.resultView) { + this.resultView.updateDecorations(); + } + if (this.currentView) { + this.currentView.launchChange(); + } + if (this.incomingView) { + this.incomingView.launchChange(); } - - this.resultView!.updateDecorations(); - this.currentView!.launchChange(); - this.incomingView!.launchChange(); }), ); } diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx b/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx index b0a9d44d7e..eb0ec8e98c 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx +++ b/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx @@ -19,6 +19,7 @@ import { } from '@opensumi/ide-core-browser/lib/monaco/merge-editor-widget'; import { IWorkspaceService } from '@opensumi/ide-workspace'; +import { MergeConflictCommands } from '../constants'; import { MergeEditorService } from '../merge-editor.service'; import { EditorViewType } from '../types'; @@ -141,7 +142,7 @@ const MergeActions: React.FC = () => { }, [mergeEditorService]); const handleReset = useCallback(async () => { - await mergeEditorService.stopAllAiResolveConflict(); + await mergeEditorService.stopAllAIResolveConflict(); runWhenIdle(() => { commandService.executeCommand(EDITOR_COMMANDS.MERGEEDITOR_RESET.id); }); @@ -149,18 +150,18 @@ const MergeActions: React.FC = () => { const handleAIResolve = useCallback(() => { if (isAIResolving) { - mergeEditorService.stopAllAiResolveConflict(); + mergeEditorService.stopAllAIResolveConflict(); } else { - mergeEditorService.handleAiResolveConflict(); + mergeEditorService.handleAIResolveConflict(); } }, [mergeEditorService, isAIResolving]); const handlePrev = useCallback(() => { - commandService.tryExecuteCommand('merge-conflict.previous'); + commandService.tryExecuteCommand(MergeConflictCommands.Previous); }, []); const handleNext = useCallback(() => { - commandService.tryExecuteCommand('merge-conflict.next'); + commandService.tryExecuteCommand(MergeConflictCommands.Next); }, []); return ( diff --git a/packages/monaco/src/browser/contrib/merge-editor/widget/resolve-result-widget.tsx b/packages/monaco/src/browser/contrib/merge-editor/widget/resolve-result-widget.tsx index 2761fd5cb4..d358bf4389 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/widget/resolve-result-widget.tsx +++ b/packages/monaco/src/browser/contrib/merge-editor/widget/resolve-result-widget.tsx @@ -13,7 +13,7 @@ import { LineRange } from '../model/line-range'; import { AI_RESOLVE_REGENERATE_ACTIONS, AiResolveConflictContentWidget, REVOKE_ACTIONS } from '../types'; import { ResultCodeEditor } from '../view/editors/resultCodeEditor'; -interface IWrapperAiInlineResultProps { +interface IWrapperAIInlineResultProps { iconItems: IAiInlineResultIconItemsProps[]; isRenderThumbs: boolean; codeEditor: ResultCodeEditor; @@ -23,7 +23,7 @@ interface IWrapperAiInlineResultProps { disablePopover?: boolean; } -export const WapperAiInlineResult = (props: IWrapperAiInlineResultProps) => { +export const WapperAIInlineResult = (props: IWrapperAIInlineResultProps) => { const { iconItems, isRenderThumbs, codeEditor, range, closeClick, isRenderClose, disablePopover = false } = props; const [isVisiablePopover, setIsVisiablePopover] = React.useState(false); const uid = useMemo(() => uuid(4), []); @@ -162,7 +162,7 @@ export class ResolveResultWidget extends BaseInlineContentWidget { return ( - Date: Tue, 16 Apr 2024 16:44:07 +0800 Subject: [PATCH 07/27] refactor: update code --- .../src/browser/editor-collection.service.ts | 14 +++++++------- packages/editor/src/browser/hooks/useEditor.ts | 0 packages/editor/src/common/editor.ts | 2 +- .../browser/vscode/api/main.thread.editor.ts | 17 +++++++---------- packages/scm/src/browser/dirty-diff/index.ts | 4 ++-- 5 files changed, 17 insertions(+), 20 deletions(-) create mode 100644 packages/editor/src/browser/hooks/useEditor.ts diff --git a/packages/editor/src/browser/editor-collection.service.ts b/packages/editor/src/browser/editor-collection.service.ts index a959209137..0746bf8a08 100644 --- a/packages/editor/src/browser/editor-collection.service.ts +++ b/packages/editor/src/browser/editor-collection.service.ts @@ -59,7 +59,7 @@ export class EditorCollectionServiceImpl extends WithEventBus implements EditorC @Autowired(IEditorFeatureRegistry) protected readonly editorFeatureRegistry: EditorFeatureRegistryImpl; - private _editors: Set = new Set(); + private _editors: Set = new Set(); private _diffEditors: Set = new Set(); private _onCodeEditorCreate = new Emitter(); @@ -97,11 +97,11 @@ export class EditorCollectionServiceImpl extends WithEventBus implements EditorC return editor; } - public listEditors(): IMonacoImplEditor[] { + public listEditors(): ISumiEditor[] { return Array.from(this._editors.values()); } - public addEditors(editors: IMonacoImplEditor[]) { + public addEditors(editors: ISumiEditor[]) { const beforeSize = this._editors.size; editors.forEach((editor) => { if (!this._editors.has(editor)) { @@ -120,7 +120,7 @@ export class EditorCollectionServiceImpl extends WithEventBus implements EditorC } } - public removeEditors(editors: IMonacoImplEditor[]) { + public removeEditors(editors: ISumiEditor[]) { const beforeSize = this._editors.size; editors.forEach((editor) => { this._editors.delete(editor); @@ -196,7 +196,7 @@ export class EditorCollectionServiceImpl extends WithEventBus implements EditorC } } -export type IMonacoImplEditor = IEditor; +export type ISumiEditor = IEditor; export function insertSnippetWithMonacoEditor( editor: IMonacoCodeEditor, @@ -573,9 +573,9 @@ export class BrowserDiffEditor extends WithEventBus implements IDiffEditor { return null; } - public originalEditor: IMonacoImplEditor; + public originalEditor: ISumiEditor; - public modifiedEditor: IMonacoImplEditor; + public modifiedEditor: ISumiEditor; public _disposed: boolean; diff --git a/packages/editor/src/browser/hooks/useEditor.ts b/packages/editor/src/browser/hooks/useEditor.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/editor/src/common/editor.ts b/packages/editor/src/common/editor.ts index 0757820fb5..ab73d9c711 100644 --- a/packages/editor/src/common/editor.ts +++ b/packages/editor/src/common/editor.ts @@ -149,7 +149,7 @@ export enum EditorType { } /** - * 一个IEditor代表了一个最小的编辑器单元,可以是CodeEditor中的一个,也可以是DiffEditor中的两个 + * 一个IEditor代表了一个最小的编辑器单元,可以是 CodeEditor 中的一个,也可以是 DiffEditor 中的两个 */ export interface IEditor { /** diff --git a/packages/extension/src/browser/vscode/api/main.thread.editor.ts b/packages/extension/src/browser/vscode/api/main.thread.editor.ts index f46a092cf8..8c6f7f2fb6 100644 --- a/packages/extension/src/browser/vscode/api/main.thread.editor.ts +++ b/packages/extension/src/browser/vscode/api/main.thread.editor.ts @@ -30,7 +30,7 @@ import { import { BrowserDiffEditor, EditorCollectionServiceImpl, - IMonacoImplEditor, + ISumiEditor, } from '@opensumi/ide-editor/lib/browser/editor-collection.service'; import { WorkbenchEditorServiceImpl } from '@opensumi/ide-editor/lib/browser/workbench-editor.service'; import * as monaco from '@opensumi/ide-monaco'; @@ -87,7 +87,7 @@ export class MainThreadEditorService extends WithEventBus implements IMainThread const editors = this.editorService.editorGroups .map((group) => { if (group.currentOpenType && isEditor(group.currentOpenType)) { - const editor = group.currentEditor as IMonacoImplEditor; + const editor = group.currentEditor as ISumiEditor; if (!editor.currentDocumentModel) { return undefined; } @@ -193,7 +193,7 @@ export class MainThreadEditorService extends WithEventBus implements IMainThread } } - private getEditor(id: string): IMonacoImplEditor | undefined { + private getEditor(id: string): ISumiEditor | undefined { const group = this.getGroup(id); if (!group) { return; @@ -201,7 +201,7 @@ export class MainThreadEditorService extends WithEventBus implements IMainThread const currentResource = group.currentResource; if (currentResource && group.currentOpenType && isEditor(group.currentOpenType)) { if (id === getTextEditorId(group, currentResource.uri)) { - return group.currentEditor as IMonacoImplEditor; + return group.currentEditor as ISumiEditor; } if ( group.currentOpenType?.type === EditorOpenType.diff && @@ -238,7 +238,7 @@ export class MainThreadEditorService extends WithEventBus implements IMainThread payload.newOpenType && (payload.newOpenType.type === EditorOpenType.code || payload.newOpenType.type === EditorOpenType.diff) ) { - const editor = payload.group.currentEditor as IMonacoImplEditor; + const editor = payload.group.currentEditor as ISumiEditor; if (!editor.currentDocumentModel) { // noop } else if (!this.documents.isDocSyncEnabled(editor.currentDocumentModel.uri)) { @@ -384,13 +384,10 @@ export class MainThreadEditorService extends WithEventBus implements IMainThread this.addDispose( this.eventBus.on(EditorConfigurationChangedEvent, (e: EditorConfigurationChangedEvent) => { const editorId = getTextEditorId(e.payload.group, e.payload.resource.uri); - if ( - e.payload.group.currentEditor && - (e.payload.group.currentEditor as IMonacoImplEditor).monacoEditor.getModel() - ) { + if (e.payload.group.currentEditor && (e.payload.group.currentEditor as ISumiEditor).monacoEditor.getModel()) { this.proxy.$acceptPropertiesChange({ id: editorId, - options: getEditorOption((e.payload.group.currentEditor as IMonacoImplEditor).monacoEditor), + options: getEditorOption((e.payload.group.currentEditor as ISumiEditor).monacoEditor), }); } }), diff --git a/packages/scm/src/browser/dirty-diff/index.ts b/packages/scm/src/browser/dirty-diff/index.ts index e4eaddbc64..8b782dba4f 100644 --- a/packages/scm/src/browser/dirty-diff/index.ts +++ b/packages/scm/src/browser/dirty-diff/index.ts @@ -14,7 +14,7 @@ import { IEditorDocumentModel, IEditorFeatureRegistry, } from '@opensumi/ide-editor/lib/browser'; -import { IMonacoImplEditor } from '@opensumi/ide-editor/lib/browser/editor-collection.service'; +import { ISumiEditor } from '@opensumi/ide-editor/lib/browser/editor-collection.service'; import * as monaco from '@opensumi/ide-monaco'; import { monacoBrowser } from '@opensumi/ide-monaco/lib/browser'; @@ -167,7 +167,7 @@ export class DirtyDiffWorkbenchController extends Disposable implements IDirtyDi .filter((editorGroup) => editorGroup.currentOpenType && editorGroup.currentOpenType.type === EditorOpenType.code) // set model registry and map to models .map((editorGroup) => { - const currentEditor = editorGroup.currentEditor as IMonacoImplEditor; + const currentEditor = editorGroup.currentEditor as ISumiEditor; if (currentEditor) { // const codeEditor = currentEditor.monacoEditor; // const controller = DirtyDiffController.get(codeEditor); From bb11496be0c7bbca6a8c49cc158da5077b002269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E5=A3=B0?= Date: Tue, 16 Apr 2024 19:02:47 +0800 Subject: [PATCH 08/27] feat: show conflict info --- .../editor-document-model-service.ts | 12 +- .../editor/src/browser/doc-model/types.ts | 4 + .../editor/src/browser/editor.module.less | 54 ------- .../editor/src/browser/hooks/useEditor.ts | 34 +++++ .../MergeEditorFloatComponents.tsx | 132 ++++++++++++------ .../merge-editor/merge-editor.module.less | 71 ++++++++++ .../contrib/merge-editor/view/grid.tsx | 4 + .../view/merge-editor.module.less | 8 ++ 8 files changed, 223 insertions(+), 96 deletions(-) create mode 100644 packages/editor/src/browser/merge-editor/merge-editor.module.less diff --git a/packages/editor/src/browser/doc-model/editor-document-model-service.ts b/packages/editor/src/browser/doc-model/editor-document-model-service.ts index 6388c57721..36e25496e2 100644 --- a/packages/editor/src/browser/doc-model/editor-document-model-service.ts +++ b/packages/editor/src/browser/doc-model/editor-document-model-service.ts @@ -1,5 +1,7 @@ import { Autowired, INJECTOR_TOKEN, Injectable, Injector } from '@opensumi/di'; import { + Dispatcher, + IDisposable, IEditorDocumentChange, IEditorDocumentModelSaveResult, ILogger, @@ -27,6 +29,7 @@ import { EditorDocumentModelCreationEvent, EditorDocumentModelOptionExternalUpdatedEvent, IEditorDocumentModelContentRegistry, + IEditorDocumentModelCreationEventPayload, IEditorDocumentModelService, IPreferredModelOptions, } from './types'; @@ -67,7 +70,9 @@ export class EditorDocumentModelServiceImpl extends WithEventBus implements IEdi private preferredModelOptions = new Map(); - private _ready = new ReadyEvent(); + private _ready = this.registerDispose(new ReadyEvent()); + + private modelCreationEventDispatcher = this.registerDispose(new Dispatcher()); constructor() { super(); @@ -83,6 +88,7 @@ export class EditorDocumentModelServiceImpl extends WithEventBus implements IEdi this._delete(key); }); this._modelReferenceManager.onInstanceCreated((model) => { + this.modelCreationEventDispatcher.dispatch(model.uri.toString()); this.eventBus.fire( new EditorDocumentModelCreationEvent({ uri: model.uri, @@ -106,6 +112,10 @@ export class EditorDocumentModelServiceImpl extends WithEventBus implements IEdi ); } + onDocumentModelCreated(uri: string, listener: () => void): IDisposable { + return this.modelCreationEventDispatcher.on(uri)(listener); + } + private _delete(uri: string | URI): void { const modelDisposeDebounceTime = this.preferenceService.get('editor.modelDisposeTime', 3000); // debounce diff --git a/packages/editor/src/browser/doc-model/types.ts b/packages/editor/src/browser/doc-model/types.ts index 9297f93ffe..84271645dc 100644 --- a/packages/editor/src/browser/doc-model/types.ts +++ b/packages/editor/src/browser/doc-model/types.ts @@ -14,6 +14,8 @@ import { EOL, EndOfLineSequence } from '@opensumi/ide-monaco/lib/browser/monaco- import { IEditorDocumentModelContentChange, SaveReason } from '../../common'; import { IEditorDocumentModel, IEditorDocumentModelRef } from '../../common/editor'; +import { EditorDocumentModel } from './editor-document-model'; + export interface IDocModelUpdateOptions extends monaco.editor.ITextModelUpdateOptions { detectIndentation?: boolean; } @@ -119,6 +121,8 @@ export interface IPreferredModelOptions { } export interface IEditorDocumentModelService { + onDocumentModelCreated(uri: string, listener: () => void): IDisposable; + hasLanguage(languageId: string): boolean; createModelReference(uri: URI, reason?: string): Promise; diff --git a/packages/editor/src/browser/editor.module.less b/packages/editor/src/browser/editor.module.less index 6cbf03ce42..6e6814f071 100644 --- a/packages/editor/src/browser/editor.module.less +++ b/packages/editor/src/browser/editor.module.less @@ -568,57 +568,3 @@ } // -------------- styles for editor empty component ends -------------- - -// -------------- styles for merge editor component starts -------------- -.merge_editor_float_container { - display: flex; - background: var(--kt-panelTab-activeBackground); - box-shadow: inset 1px 1px 3px 0px var(--kt-panelTab-border); - padding: 16px 28px; - justify-content: end; - white-space: nowrap; - .merge_conflict_bottom_btn { - border: 1px solid var(--kt-button-disableForeground); - border-radius: 8px; - padding: 5px 16px; - background: var(--editor-background); - margin: 0 4px; - line-height: 22px; - justify-content: end; - white-space: nowrap; - cursor: pointer; - :global { - .kt-icon { - font-size: 12px; - } - } - :first-child { - margin-right: 8px; - } - } - - .magic_btn { - background-image: radial-gradient(circle at -21% -22%, #19cfff, #8429ff); - border: none; - font-weight: 500; - span { - color: #fff; - } - :global { - .kt-icon { - color: #fff; - font-size: 12px; - margin-right: 8px; - } - } - } - - .line_vertical { - background-color: var(--design-borderColor-common); - width: 1px; - min-width: 1px; - height: 24px; - margin: 4px; - } -} -// -------------- styles for merge editor component ends -------------- diff --git a/packages/editor/src/browser/hooks/useEditor.ts b/packages/editor/src/browser/hooks/useEditor.ts index e69de29bb2..f1a2600dd7 100644 --- a/packages/editor/src/browser/hooks/useEditor.ts +++ b/packages/editor/src/browser/hooks/useEditor.ts @@ -0,0 +1,34 @@ +import { useEffect, useState } from 'react'; + +import { IEventBus, URI, useInjectable } from '@opensumi/ide-core-browser'; + +import { EditorDocumentModelCreationEvent, IEditorDocumentModelService } from '../doc-model/types'; +import { IEditorDocumentModelRef } from '../types'; + +export function useEditorDocumentModelRef(uri: URI) { + const documentService: IEditorDocumentModelService = useInjectable(IEditorDocumentModelService); + const [ref, setRef] = useState(null); + + useEffect(() => { + const run = () => { + const ref = documentService.getModelReference(uri); + if (ref) { + setRef(ref); + } + }; + + const toDispose = documentService.onDocumentModelCreated(uri.toString(), () => { + run(); + }); + + run(); + return () => { + toDispose.dispose(); + if (ref) { + ref.dispose(); + } + }; + }, [uri]); + + return ref; +} diff --git a/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx b/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx index 16243d360e..6de6bd1c8f 100644 --- a/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx +++ b/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx @@ -5,6 +5,7 @@ import { AINativeConfigService, CommandRegistry, CommandService, + DisposableStore, IContextKeyService, MERGE_CONFLICT_COMMANDS, SCM_COMMANDS, @@ -15,9 +16,12 @@ import { } from '@opensumi/ide-core-browser'; import { MergeConflictCommands } from '@opensumi/ide-monaco/lib/browser/contrib/merge-editor/constants'; -import styles from '../editor.module.less'; +import { useEditorDocumentModelRef } from '../hooks/useEditor'; +import { DocumentMergeConflict, MergeConflictParser } from '../merge-conflict'; import { ReactEditorComponent } from '../types'; +import styles from './merge-editor.module.less'; + const gitMergeChangesSet = new Set(['git.mergeChanges']); export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ resource }) => { @@ -25,13 +29,23 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ const commandService = useInjectable(CommandService); const commandRegistry = useInjectable(CommandRegistry); const contextKeyService = useInjectable(IContextKeyService); + const mergeConflictParser: MergeConflictParser = useInjectable(MergeConflictParser); + + const editorModel = useEditorDocumentModelRef(resource.uri); const [isVisiable, setIsVisiable] = useState(false); + const [conflicts, setConflicts] = useState([]); useEffect(() => { const run = () => { const mergeChanges = contextKeyService.getValue('git.mergeChanges') || []; - setIsVisiable(mergeChanges.some((value) => value.toString() === resource.uri.toString())); + const isVisiable = mergeChanges.some((uri) => uri.toString() === resource.uri.toString()); + setIsVisiable((prev) => { + if (!prev && isVisiable) { + return true; + } + return prev; + }); }; const disposed = contextKeyService.onDidChangeContext(({ payload }) => { @@ -43,6 +57,34 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ return () => disposed.dispose(); }, [resource]); + useEffect(() => { + const disposables = new DisposableStore(); + + if (editorModel) { + const { instance } = editorModel; + const run = () => { + const conflicts = mergeConflictParser.scanDocument(instance.getMonacoModel()); + if (conflicts.length > 0) { + setIsVisiable(true); + setConflicts(conflicts); + } else { + setIsVisiable(false); + setConflicts([]); + } + }; + + disposables.add( + editorModel.instance.getMonacoModel().onDidChangeContent(() => { + run(); + }), + ); + run(); + return () => { + disposables.dispose(); + }; + } + }, [editorModel]); + const [isAIResolving, setIsAIResolving] = useState(false); const handleOpenMergeEditor = useCallback(async () => { const { uri } = resource; @@ -87,50 +129,58 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ return (
-
- - + +
+ + -
- - - - {isSupportAIResolve() && ( - )} + {isSupportAIResolve() && ( + + )} + ); }; diff --git a/packages/editor/src/browser/merge-editor/merge-editor.module.less b/packages/editor/src/browser/merge-editor/merge-editor.module.less new file mode 100644 index 0000000000..8d82139ec8 --- /dev/null +++ b/packages/editor/src/browser/merge-editor/merge-editor.module.less @@ -0,0 +1,71 @@ +// -------------- styles for merge editor component starts -------------- +.merge_editor_float_container { + display: flex; + flex-direction: column; + background: var(--kt-panelTab-activeBackground); + box-shadow: inset 1px 1px 3px 0px var(--kt-panelTab-border); + justify-content: space-between; + white-space: nowrap; + // minimap's z-index is 5 + z-index: 6; + + .merge_editor_float_container_info { + width: 100%; + display: flex; + padding-left: 20px; + } + + .merge_editor_float_container_operation_bar { + width: 100%; + display: flex; + justify-content: end; + white-space: nowrap; + padding-top: 4px; + padding-right: 20px; + } + + .merge_conflict_bottom_btn { + border: 1px solid var(--kt-button-disableForeground); + border-radius: 8px; + padding: 5px 16px; + background: var(--editor-background); + margin: 0 4px; + line-height: 22px; + justify-content: end; + white-space: nowrap; + cursor: pointer; + :global { + .kt-icon { + font-size: 12px; + } + } + :first-child { + margin-right: 8px; + } + } + + .magic_btn { + background-image: radial-gradient(circle at -21% -22%, #19cfff, #8429ff); + border: none; + font-weight: 500; + span { + color: #fff; + } + :global { + .kt-icon { + color: #fff; + font-size: 12px; + margin-right: 8px; + } + } + } + + .line_vertical { + background-color: var(--design-borderColor-common); + width: 1px; + min-width: 1px; + height: 24px; + margin: 4px; + } +} +// -------------- styles for merge editor component ends -------------- diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx b/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx index eb0ec8e98c..3f2666a91b 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx +++ b/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx @@ -166,6 +166,10 @@ const MergeActions: React.FC = () => { return (
+
+ 当前冲突变更共 {1} 处 (已解决 0 处,剩余 0 处) | {0} 处非冲突变更已自动合并(目标分支:1 处,来源分支:1 + 处;两者 0 处) +
); -}; +}); export const Grid = () => { const mergeEditorService = useInjectable(MergeEditorService); @@ -267,6 +297,7 @@ export const Grid = () => { resultEditorContainer.current, incomingEditorContainer.current, ]; + if (current && result && incoming) { mergeEditorService.instantiationCodeEditor(current, result, incoming); } @@ -296,13 +327,9 @@ export const Grid = () => {
- +
- +
diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/merge-editor.module.less b/packages/monaco/src/browser/contrib/merge-editor/view/merge-editor.module.less index 0f41804e5f..1e65861ebb 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/merge-editor.module.less +++ b/packages/monaco/src/browser/contrib/merge-editor/view/merge-editor.module.less @@ -29,12 +29,17 @@ left: 0; background: var(--kt-panelTab-activeBackground); box-shadow: inset 1px 1px 3px 0px var(--kt-panelTab-border); - padding: 16px 28px; + padding: 12px; // minimap's z-index is 5 z-index: 6; display: flex; flex-direction: column; + .action_category { + display: flex; + flex-direction: row; + } + .merge_info { display: flex; width: 100%; @@ -45,11 +50,17 @@ float: right; white-space: nowrap; width: 100%; + justify-content: flex-end; + white-space: nowrap; + padding-top: 6px; + padding-right: 20px; .merge_conflict_bottom_btn { border: 1px solid var(--kt-button-disableForeground); border-radius: 8px; background: var(--editor-background); + color: var(--editor-foreground); + margin: 0 4px; cursor: pointer; diff --git a/packages/monaco/src/browser/contrib/merge-editor/widget/resolve-result-widget.tsx b/packages/monaco/src/browser/contrib/merge-editor/widget/resolve-result-widget.tsx index d358bf4389..fc5271d0cd 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/widget/resolve-result-widget.tsx +++ b/packages/monaco/src/browser/contrib/merge-editor/widget/resolve-result-widget.tsx @@ -10,7 +10,12 @@ import { localize, uuid } from '@opensumi/ide-core-common'; import { BaseInlineContentWidget } from '../../../ai-native/BaseInlineContentWidget'; import { LineRange } from '../model/line-range'; -import { AI_RESOLVE_REGENERATE_ACTIONS, AiResolveConflictContentWidget, REVOKE_ACTIONS } from '../types'; +import { + AI_RESOLVE_REGENERATE_ACTIONS, + AiResolveConflictContentWidget, + ECompleteReason, + REVOKE_ACTIONS, +} from '../types'; import { ResultCodeEditor } from '../view/editors/resultCodeEditor'; interface IWrapperAIInlineResultProps { @@ -51,6 +56,7 @@ export const WapperAIInlineResult = (props: IWrapperAIInlineResultProps) => { codeEditor.launchConflictActionsEvent({ range, action: AI_RESOLVE_REGENERATE_ACTIONS, + reason: ECompleteReason.UserManual, }); codeEditor.hideResolveResultWidget(); }, [range, codeEditor]); @@ -149,6 +155,7 @@ export class ResolveResultWidget extends BaseInlineContentWidget { this.codeEditor.launchConflictActionsEvent({ range: this.lineRange, action: REVOKE_ACTIONS, + reason: ECompleteReason.UserManual, }); this.codeEditor.hideResolveResultWidget(); }, diff --git a/packages/monaco/src/common/base.ts b/packages/monaco/src/common/base.ts index cad5de1fec..88150a7103 100644 --- a/packages/monaco/src/common/base.ts +++ b/packages/monaco/src/common/base.ts @@ -1,4 +1,5 @@ export { URI as Uri } from '@opensumi/monaco-editor-core/esm/vs/base/common/uri'; +export { URI as MonacoURI } from '@opensumi/monaco-editor-core/esm/vs/base/common/uri'; export * from '@opensumi/monaco-editor-core/esm/vs/base/common/htmlContent'; export { CancellationToken, diff --git a/packages/startup/entry/web/render-app.tsx b/packages/startup/entry/web/render-app.tsx index 7979cc510f..d3c909daed 100644 --- a/packages/startup/entry/web/render-app.tsx +++ b/packages/startup/entry/web/render-app.tsx @@ -1,7 +1,7 @@ import { Injector } from '@opensumi/di'; import { IClientAppOpts, SlotLocation } from '@opensumi/ide-core-browser'; import { ClientApp } from '@opensumi/ide-core-browser/lib/bootstrap/app'; -import { uuid } from '@opensumi/ide-core-common'; +import { findFirstTruthy, uuid } from '@opensumi/ide-core-common'; import { ExpressFileServerModule } from '@opensumi/ide-express-file-server/lib/browser'; import { defaultConfig } from '@opensumi/ide-main-layout/lib/browser/default-config'; import { RemoteOpenerModule } from '@opensumi/ide-remote-opener/lib/browser'; @@ -19,10 +19,19 @@ export async function renderApp(opts: IClientAppOpts) { const defaultHost = process.env.HOST || window.location.hostname; const injector = new Injector(); - opts.workspaceDir = - opts.workspaceDir || process.env.SUPPORT_LOAD_WORKSPACE_BY_HASH - ? window.location.hash.slice(1) - : process.env.WORKSPACE_DIR; + + opts.workspaceDir = findFirstTruthy( + () => { + const queries = new URLSearchParams(window.location.search); + const dir = queries.get('workspaceDir'); + if (dir) { + return dir; + } + }, + process.env.SUPPORT_LOAD_WORKSPACE_BY_HASH && window.location.hash.slice(1), + opts.workspaceDir, + process.env.WORKSPACE_DIR, + ); opts.injector = injector; opts.extensionDir = opts.extensionDir || process.env.EXTENSION_DIR; diff --git a/packages/utils/src/functional.ts b/packages/utils/src/functional.ts index 3611628e23..7b82e8b68c 100644 --- a/packages/utils/src/functional.ts +++ b/packages/utils/src/functional.ts @@ -57,3 +57,21 @@ export function diffSets(before: Set, after: Set): { removed: T[]; adde } return { removed, added }; } + +export function findFirstTruthy(...sources: Array T)>): T | undefined { + for (let i = 0; i < sources.length - 1; i++) { + const result = check(sources[i]); + if (result) { + return result; + } + } + + return check(sources[sources.length - 1]); + + function check(value: T | (() => T)): T | undefined { + if (typeof value === 'function') { + return (value as () => T)(); + } + return value; + } +} From 416c7dfad6375543ff02ca8750fe0911d6b7af2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E5=A3=B0?= Date: Sun, 28 Apr 2024 17:22:18 +0800 Subject: [PATCH 11/27] fix: accept left or right is broken --- packages/ai-native/src/browser/merge-conflict/index.ts | 6 +++--- .../browser/merge-editor/MergeEditorFloatComponents.tsx | 4 ++-- .../src/browser/merge-editor/merge-editor.provider.ts | 1 + packages/i18n/src/common/en-US.lang.ts | 4 ++-- packages/i18n/src/common/zh-CN.lang.ts | 4 ++-- .../monaco/src/browser/contrib/merge-editor/view/grid.tsx | 8 ++++---- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/ai-native/src/browser/merge-conflict/index.ts b/packages/ai-native/src/browser/merge-conflict/index.ts index 78c527ab00..8def67b035 100644 --- a/packages/ai-native/src/browser/merge-conflict/index.ts +++ b/packages/ai-native/src/browser/merge-conflict/index.ts @@ -648,14 +648,14 @@ export class MergeConflictContribution extends Disposable implements CommandCont conflicts.forEach((conflict) => { const aiAcceptCommand = { id: MERGE_CONFLICT_COMMANDS.AI_ACCEPT.id, - title: `$(ai-magic) ${localize('mergeEditor.conflict.resolve.all')}`, + title: `$(ai-magic) ${localize('mergeEditor.conflict.ai.resolve.all')}`, arguments: ['know-conflict', conflict], - tooltip: localize('mergeEditor.conflict.resolve.all'), + tooltip: localize('mergeEditor.conflict.ai.resolve.all'), }; // loading 效果 this.loadingRange.forEach((range) => { if (conflict.range.equalsRange(range)) { - aiAcceptCommand.title = `$(loading~spin) ${localize('mergeEditor.conflict.resolve.all')}`; + aiAcceptCommand.title = `$(loading~spin) ${localize('mergeEditor.conflict.ai.resolve.all')}`; } }); items.push({ diff --git a/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx b/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx index 6de6bd1c8f..ce17332c9e 100644 --- a/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx +++ b/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx @@ -170,12 +170,12 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ {isAIResolving ? ( <> - {localize('mergeEditor.conflict.resolve.all.stop')} + {localize('mergeEditor.conflict.ai.resolve.all.stop')} ) : ( <> - {localize('mergeEditor.conflict.resolve.all')} + {localize('mergeEditor.conflict.ai.resolve.all')} )} diff --git a/packages/editor/src/browser/merge-editor/merge-editor.provider.ts b/packages/editor/src/browser/merge-editor/merge-editor.provider.ts index 5ecae51c3f..480099117d 100644 --- a/packages/editor/src/browser/merge-editor/merge-editor.provider.ts +++ b/packages/editor/src/browser/merge-editor/merge-editor.provider.ts @@ -23,6 +23,7 @@ export class MergeEditorResourceProvider extends WithEventBus implements IResour const icon = this.labelService.getIcon(resultEditorUri); return { // 如果设置为 true,再打开时没有找到对应的 provider 会报错 + // TODO: 需要增加一个标记,说明这个资源要在某个插件加载后才能 revive supportsRevive: false, name, icon, diff --git a/packages/i18n/src/common/en-US.lang.ts b/packages/i18n/src/common/en-US.lang.ts index b4c10b547e..2635fc9819 100644 --- a/packages/i18n/src/common/en-US.lang.ts +++ b/packages/i18n/src/common/en-US.lang.ts @@ -1440,8 +1440,8 @@ export const localizationBundle = { 'mergeEditor.open.3way': '3-Way Editor', 'mergeEditor.conflict.prev': 'Previous Conflict', 'mergeEditor.conflict.next': 'Next Conflict', - 'mergeEditor.conflict.resolve.all': 'AI Resolution', - 'mergeEditor.conflict.resolve.all.stop': 'Stop All', + 'mergeEditor.conflict.ai.resolve.all': 'AI Resolution', + 'mergeEditor.conflict.ai.resolve.all.stop': 'Stop All', 'mergeEditor.open.tradition': 'Tradition Editor', // #region AI Native diff --git a/packages/i18n/src/common/zh-CN.lang.ts b/packages/i18n/src/common/zh-CN.lang.ts index a4ebbf4bc4..e4606137d6 100644 --- a/packages/i18n/src/common/zh-CN.lang.ts +++ b/packages/i18n/src/common/zh-CN.lang.ts @@ -1209,8 +1209,8 @@ export const localizationBundle = { 'mergeEditor.open.3way': '3-way 编辑器', 'mergeEditor.conflict.prev': '上一处冲突', 'mergeEditor.conflict.next': '下一处冲突', - 'mergeEditor.conflict.resolve.all': 'AI 解决', - 'mergeEditor.conflict.resolve.all.stop': '全部停止', + 'mergeEditor.conflict.ai.resolve.all': 'AI 解决', + 'mergeEditor.conflict.ai.resolve.all.stop': '全部停止', 'mergeEditor.open.tradition': '传统编辑器', 'workbench.quickOpen.preserveInput': '是否在 QuickOpen 的输入框(包括命令面板)中保留上次输入的内容', diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx b/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx index 22efd450d1..e2c90c86a6 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx +++ b/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx @@ -131,11 +131,11 @@ const MergeActions: React.FC = observer(() => { }, [mergeEditorService]); const handleAcceptLeft = useCallback(() => { - mergeEditorService.acceptLeft(true, ECompleteReason.AutoResolvedNonConflict); + mergeEditorService.acceptLeft(false, ECompleteReason.UserManual); }, [mergeEditorService]); const handleAcceptRight = useCallback(() => { - mergeEditorService.acceptRight(true, ECompleteReason.AutoResolvedNonConflict); + mergeEditorService.acceptRight(false, ECompleteReason.UserManual); }, [mergeEditorService]); const handleOpenTradition = useCallback(() => { @@ -260,12 +260,12 @@ const MergeActions: React.FC = observer(() => { {isAIResolving ? ( <> - {localize('mergeEditor.conflict.resolve.all.stop')} + {localize('mergeEditor.conflict.ai.resolve.all.stop')} ) : ( <> - {localize('mergeEditor.conflict.resolve.all')} + {localize('mergeEditor.conflict.ai.resolve.all')} )} From ac82c2861a36e71c7c7c3370f9e345dc97be3713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E5=A3=B0?= Date: Sun, 28 Apr 2024 17:46:15 +0800 Subject: [PATCH 12/27] chore: update code --- .../contrib/merge-editor/view/editors/resultCodeEditor.ts | 8 ++++---- .../monaco/src/browser/contrib/merge-editor/view/grid.tsx | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.ts b/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.ts index 485c22bdd8..669ecf455e 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.ts +++ b/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.ts @@ -606,7 +606,7 @@ export class ResultCodeEditor extends BaseCodeEditor { } } - private changesComputed = true; + private isFirstInputComputeDiff = true; protected computedMaybeNeedMergeRanges(diffRanges: LineRange[]): { rawRanges: LineRange[]; mergeRange: LineRange; @@ -616,10 +616,10 @@ export class ResultCodeEditor extends BaseCodeEditor { mergeRange: LineRange; }[] = []; - if (this.changesComputed) { + if (this.isFirstInputComputeDiff) { maybeNeedMergeRanges = this.distillNeedMergeRanges(diffRanges); this.handleNeedMergeRanges(maybeNeedMergeRanges); - this.changesComputed = false; + this.isFirstInputComputeDiff = false; } return maybeNeedMergeRanges; @@ -757,7 +757,7 @@ export class ResultCodeEditor extends BaseCodeEditor { } public reset(): void { - this.changesComputed = true; + this.isFirstInputComputeDiff = true; } public getEditorViewType(): EditorViewType { diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx b/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx index e2c90c86a6..1e57608299 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx +++ b/packages/monaco/src/browser/contrib/merge-editor/view/grid.tsx @@ -151,7 +151,9 @@ const MergeActions: React.FC = observer(() => { path: uri.path, query: '', }); - } else if (uri.scheme !== 'file') { + } + + if (uri.scheme !== 'file') { // ignore other scheme logger.warn('Unsupported scheme', uri.scheme); return; From 039b7521cfc0b3e834f26fa6e3fb666a09191942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E5=A3=B0?= Date: Sun, 28 Apr 2024 19:30:53 +0800 Subject: [PATCH 13/27] fix: fix reset not work --- .../merge-editor/merge-editor-widget.tsx | 10 ++++++ .../view/editors/resultCodeEditor.ts | 36 +++++++------------ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/monaco/src/browser/contrib/merge-editor/merge-editor-widget.tsx b/packages/monaco/src/browser/contrib/merge-editor/merge-editor-widget.tsx index 8a03f158be..eaabbcefae 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/merge-editor-widget.tsx +++ b/packages/monaco/src/browser/contrib/merge-editor/merge-editor-widget.tsx @@ -23,6 +23,7 @@ import { ICodeEditor, IDiffEditorOptions, IEditorOptions, IModelDeltaDecoration import { StandaloneServices } from '../../monaco-api/services'; import { IPosition, Position } from '../../monaco-api/types'; +import { MappingManagerService } from './mapping-manager.service'; import { MergeEditorService } from './merge-editor.service'; import { EditorViewType, IMergeEditorViewState } from './types'; import { Grid } from './view/grid'; @@ -46,6 +47,9 @@ export class MergeEditorWidget extends Disposable implements IMergeEditorEditor @Autowired(MergeEditorService) private readonly mergeEditorService: MergeEditorService; + @Autowired(MappingManagerService) + private readonly mappingManagerService: MappingManagerService; + private readonly _id: number; private readonly viewStateMap: Map = new Map(); private outputUri: URI | undefined; @@ -76,6 +80,12 @@ export class MergeEditorWidget extends Disposable implements IMergeEditorEditor const { ancestor } = nutrition; const { baseContent, textModel } = ancestor!; (textModel as ITextModel).setValue(baseContent); + + // 取出 result editor 的 model, 重置回原来的文档内容 + const resultModel = this.getResultEditor().getModel(); + if (resultModel) { + (resultModel as ITextModel).setValue(baseContent); + } } } }), diff --git a/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.ts b/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.ts index 669ecf455e..60d10ad25f 100644 --- a/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.ts +++ b/packages/monaco/src/browser/contrib/merge-editor/view/editors/resultCodeEditor.ts @@ -471,7 +471,9 @@ export class ResultCodeEditor extends BaseCodeEditor { let slow = 0; let mergeRange: LineRange | undefined; - /** Two Pointers 算法 */ + diffRanges.sort((a, b) => a.startLineNumber - b.startLineNumber); + + /** Two Pointers 算法, 快慢指针 */ for (let fast = 0; fast < length; fast++) { const slowRange = diffRanges[slow]; const fastRange = diffRanges[fast]; @@ -606,40 +608,28 @@ export class ResultCodeEditor extends BaseCodeEditor { } } + /** + * 用来标记是否进行过初次 diff 计算,如果进行过,则直接返回所有 diff + */ private isFirstInputComputeDiff = true; - protected computedMaybeNeedMergeRanges(diffRanges: LineRange[]): { - rawRanges: LineRange[]; - mergeRange: LineRange; - }[] { - let maybeNeedMergeRanges: { - rawRanges: LineRange[]; - mergeRange: LineRange; - }[] = []; + protected getDiffRangesAfterDistill(): LineRange[] { + let diffRanges = this.mappingManagerService.getAllDiffRanges(); if (this.isFirstInputComputeDiff) { - maybeNeedMergeRanges = this.distillNeedMergeRanges(diffRanges); + const maybeNeedMergeRanges = this.distillNeedMergeRanges(diffRanges); this.handleNeedMergeRanges(maybeNeedMergeRanges); + // 数据源 document mapping 的对应关系已经被改变,需要刷新一次 + diffRanges = this.mappingManagerService.getAllDiffRanges(); this.isFirstInputComputeDiff = false; } - return maybeNeedMergeRanges; + return diffRanges; } protected override prepareRenderDecorations(): [LineRange[], InnerRange[][]] { - const diffRanges: LineRange[] = this.mappingManagerService - .getAllDiffRanges() - .sort((a, b) => a.startLineNumber - b.startLineNumber); const innerChangesResult: InnerRange[][] = []; - const maybeNeedMergeRanges = this.computedMaybeNeedMergeRanges(diffRanges); - - /** - * 如果 maybeNeedMergeRanges 大于 0,说明是第一次计算成功,这个过程中会改数据源 - * 说明数据源 document mapping 的对应关系被改变 - * 则需要重新获取一次 - */ - const changesResult: LineRange[] = - maybeNeedMergeRanges.length > 0 ? this.mappingManagerService.getAllDiffRanges() : diffRanges; + const changesResult = this.getDiffRangesAfterDistill(); let conflictsTotal = 0; let nonConflictsUnresolved = 0; From 9fe34804f47775415f0d12e57f025c0a32a48ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E5=A3=B0?= Date: Sun, 28 Apr 2024 20:12:55 +0800 Subject: [PATCH 14/27] chore: update code --- .../src/browser/merge-conflict/index.ts | 7 ++- .../browser/editor-electron.contribution.ts | 3 +- .../editor/src/browser/hooks/useEditor.ts | 4 +- .../src/browser/hooks/useInMergeChanges.ts | 28 ++++++++++ .../MergeEditorFloatComponents.tsx | 55 +++++++------------ packages/scm/src/browser/scm-model.ts | 3 + 6 files changed, 58 insertions(+), 42 deletions(-) create mode 100644 packages/editor/src/browser/hooks/useInMergeChanges.ts diff --git a/packages/ai-native/src/browser/merge-conflict/index.ts b/packages/ai-native/src/browser/merge-conflict/index.ts index 8def67b035..67d3c0e56e 100644 --- a/packages/ai-native/src/browser/merge-conflict/index.ts +++ b/packages/ai-native/src/browser/merge-conflict/index.ts @@ -7,7 +7,6 @@ import { CancelResponse, CancellationTokenSource, ChatResponse, - Command, CommandContribution, CommandRegistry, Constants, @@ -414,10 +413,14 @@ export class MergeConflictContribution extends Disposable implements CommandCont } private reportConflictData() { + if (!this.editor) { + return; + } + const uri = this.getUri(); const reportData = this.currentReportMap.get(uri); if (reportData) { - this.mergeConflictReportService.report(this.getUri(), this.reportData); + this.mergeConflictReportService.report(uri, this.reportData); } } diff --git a/packages/editor/src/browser/editor-electron.contribution.ts b/packages/editor/src/browser/editor-electron.contribution.ts index 35d6d34e61..f98f2d1e20 100644 --- a/packages/editor/src/browser/editor-electron.contribution.ts +++ b/packages/editor/src/browser/editor-electron.contribution.ts @@ -3,7 +3,6 @@ import { ClientAppContribution, Domain, EDITOR_COMMANDS, - IClientApp, KeybindingContribution, KeybindingRegistry, electronEnv, @@ -44,7 +43,7 @@ export class EditorElectronContribution extends WithEventBus implements ClientAp /** * Return true in order to prevent exit */ - async onWillStop(app: IClientApp) { + async onWillStop() { if (await this.workbenchEditorService.closeAllOnlyConfirmOnce()) { return true; } diff --git a/packages/editor/src/browser/hooks/useEditor.ts b/packages/editor/src/browser/hooks/useEditor.ts index f1a2600dd7..aa5ff038fb 100644 --- a/packages/editor/src/browser/hooks/useEditor.ts +++ b/packages/editor/src/browser/hooks/useEditor.ts @@ -1,8 +1,8 @@ import { useEffect, useState } from 'react'; -import { IEventBus, URI, useInjectable } from '@opensumi/ide-core-browser'; +import { URI, useInjectable } from '@opensumi/ide-core-browser'; -import { EditorDocumentModelCreationEvent, IEditorDocumentModelService } from '../doc-model/types'; +import { IEditorDocumentModelService } from '../doc-model/types'; import { IEditorDocumentModelRef } from '../types'; export function useEditorDocumentModelRef(uri: URI) { diff --git a/packages/editor/src/browser/hooks/useInMergeChanges.ts b/packages/editor/src/browser/hooks/useInMergeChanges.ts new file mode 100644 index 0000000000..1d02c63687 --- /dev/null +++ b/packages/editor/src/browser/hooks/useInMergeChanges.ts @@ -0,0 +1,28 @@ +import { useEffect, useState } from 'react'; + +import { IContextKeyService, useInjectable } from '@opensumi/ide-core-browser'; + +export const gitMergeChangesSet = new Set(['git.mergeChanges']); + +export function useInMergeChanges(uriStr: string) { + const contextKeyService = useInjectable(IContextKeyService); + + const [inMergeChanges, setInMergeChanges] = useState(false); + + useEffect(() => { + function run() { + const mergeChanges = contextKeyService.getValue>('git.mergeChangesObj') || {}; + setInMergeChanges(mergeChanges[uriStr] || false); + } + run(); + + const disposed = contextKeyService.onDidChangeContext(({ payload }) => { + if (payload.affectsSome(gitMergeChangesSet)) { + run(); + } + }); + return () => disposed.dispose(); + }, [uriStr]); + + return inMergeChanges; +} diff --git a/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx b/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx index ce17332c9e..18d6900149 100644 --- a/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx +++ b/packages/editor/src/browser/merge-editor/MergeEditorFloatComponents.tsx @@ -6,29 +6,25 @@ import { CommandRegistry, CommandService, DisposableStore, - IContextKeyService, MERGE_CONFLICT_COMMANDS, SCM_COMMANDS, URI, - Uri, localize, useInjectable, } from '@opensumi/ide-core-browser'; import { MergeConflictCommands } from '@opensumi/ide-monaco/lib/browser/contrib/merge-editor/constants'; import { useEditorDocumentModelRef } from '../hooks/useEditor'; +import { useInMergeChanges } from '../hooks/useInMergeChanges'; import { DocumentMergeConflict, MergeConflictParser } from '../merge-conflict'; import { ReactEditorComponent } from '../types'; import styles from './merge-editor.module.less'; -const gitMergeChangesSet = new Set(['git.mergeChanges']); - export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ resource }) => { const aiNativeConfigService = useInjectable(AINativeConfigService); const commandService = useInjectable(CommandService); const commandRegistry = useInjectable(CommandRegistry); - const contextKeyService = useInjectable(IContextKeyService); const mergeConflictParser: MergeConflictParser = useInjectable(MergeConflictParser); const editorModel = useEditorDocumentModelRef(resource.uri); @@ -36,26 +32,7 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ const [isVisiable, setIsVisiable] = useState(false); const [conflicts, setConflicts] = useState([]); - useEffect(() => { - const run = () => { - const mergeChanges = contextKeyService.getValue('git.mergeChanges') || []; - const isVisiable = mergeChanges.some((uri) => uri.toString() === resource.uri.toString()); - setIsVisiable((prev) => { - if (!prev && isVisiable) { - return true; - } - return prev; - }); - }; - - const disposed = contextKeyService.onDidChangeContext(({ payload }) => { - if (payload.affectsSome(gitMergeChangesSet)) { - run(); - } - }); - run(); - return () => disposed.dispose(); - }, [resource]); + const inMergeChanges = useInMergeChanges(resource.uri.toString()); useEffect(() => { const disposables = new DisposableStore(); @@ -102,11 +79,15 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({ ); const handlePrev = useCallback(() => { - commandService.tryExecuteCommand(MergeConflictCommands.Previous); + commandService.tryExecuteCommand(MergeConflictCommands.Previous).then(() => { + // TODO: 编辑器向上滚动一行 + }); }, []); const handleNext = useCallback(() => { - commandService.tryExecuteCommand(MergeConflictCommands.Next); + commandService.tryExecuteCommand(MergeConflictCommands.Next).then(() => { + // TODO: 编辑器向上滚动一行 + }); }, []); const handleAIResolve = useCallback(async () => { @@ -142,15 +123,17 @@ export const MergeEditorFloatComponents: ReactEditorComponent<{ uri: URI }> = ({
- + {inMergeChanges && ( + + )} ,