diff --git a/packages/core/src/api/renderDiffViewer.tsx b/packages/core/src/api/renderDiffViewer.tsx index f72d8883..945ac92c 100644 --- a/packages/core/src/api/renderDiffViewer.tsx +++ b/packages/core/src/api/renderDiffViewer.tsx @@ -48,27 +48,27 @@ export function DiffViewerLayoutComponent(): React.ReactElement { ); } -export const DiffViewerRenderer = (_props: IDiffViewerProps) => { - const props = merge({ +export const DiffViewerRenderer = (props: IDiffViewerProps) => { + const mergedProps = merge({ appConfig: {}, runtimeConfig: { - onWillApplyTheme: _props.onWillApplyTheme, - tabBarRightExtraContent: _props.tabBarRightExtraContent, + onWillApplyTheme: props.onWillApplyTheme, + tabBarRightExtraContent: props.tabBarRightExtraContent, } as RuntimeConfig, - }, _props) as IAppRendererProps; + }, props) as IAppRendererProps; - if (!props.appConfig.injector) { - props.appConfig.injector = new Injector(); + if (!mergedProps.appConfig.injector) { + mergedProps.appConfig.injector = new Injector(); } - const injector = props.appConfig.injector; + const injector = mergedProps.appConfig.injector; injector.addProviders({ token: IDiffViewerProps, - useValue: props, + useValue: mergedProps, }); - const appConfig = props.appConfig; + const appConfig = mergedProps.appConfig; let appModules: ModuleConstructor[] = appConfig?.modules || []; if (!appModules.includes(DiffViewerModule)) { @@ -133,7 +133,7 @@ export const DiffViewerRenderer = (_props: IDiffViewerProps) => { }, }, }), - }, props); + }, mergedProps); return ( { /> ); }; + +export const DiffPreviewer = DiffViewerRenderer; diff --git a/packages/core/src/core/diff-viewer/common.ts b/packages/core/src/core/diff-viewer/common.ts index 29689eca..31c89631 100644 --- a/packages/core/src/core/diff-viewer/common.ts +++ b/packages/core/src/core/diff-viewer/common.ts @@ -115,7 +115,16 @@ export interface IOverrideAppRendererProps extends IOverrideAppRendererConfig { } export const IDiffViewerProps = Symbol('IDiffViewerProps'); + +export interface IDiffItem { + path: string; + oldCode: string; + newCode: string; +} + export interface IDiffViewerProps extends Partial { + data?: IDiffItem[]; + onRef: (handle: IDiffViewerHandle) => void; onWillApplyTheme?: (theme: ITheme) => Record; diff --git a/packages/core/src/core/diff-viewer/internal/base.ts b/packages/core/src/core/diff-viewer/internal/base.ts index 4a3805fa..60ae6c2c 100644 --- a/packages/core/src/core/diff-viewer/internal/base.ts +++ b/packages/core/src/core/diff-viewer/internal/base.ts @@ -2,7 +2,6 @@ import { fsExtra, isFilesystemReady } from '@codeblitzjs/ide-sumi-core'; import { InlineChatHandler } from '@opensumi/ide-ai-native/lib/browser/widget/inline-chat/inline-chat.handler'; import { AppConfig, ClientAppContribution, EDITOR_COMMANDS, IClientApp } from '@opensumi/ide-core-browser'; import { - CommandContribution, Disposable, DisposableStore, Domain, @@ -10,6 +9,8 @@ import { Event, IChatProgress, ILogger, + isArray, + MaybePromise, Sequencer, URI, } from '@opensumi/ide-core-common'; @@ -29,8 +30,8 @@ import path from 'path'; import { IDiffViewerProps, IDiffViewerTab, IExtendPartialEditEvent, ITabChangedEvent } from '../common'; import { removeStart } from '../utils'; -@Domain(CommandContribution, ClientAppContribution, MenuContribution) -export class DiffViewerContribution implements CommandContribution, ClientAppContribution, MenuContribution { +@Domain(ClientAppContribution, MenuContribution) +export class DiffViewerContribution implements ClientAppContribution, MenuContribution { private _disposables = new DisposableStore(); @Autowired(IDiffViewerProps) @@ -76,174 +77,180 @@ export class DiffViewerContribution implements CommandContribution, ClientAppCon return result; } - async initialize(app: IClientApp): Promise { - await isFilesystemReady(); - - const disposable = new Disposable(); - - const openFileInTab = async (filePath: string, content: string, options?: IResourceOpenOptions) => { - const fullPath = this.getFullPath(filePath); - if (!fsExtra.pathExistsSync(fullPath)) { - fsExtra.ensureFileSync(fullPath); - fsExtra.writeFileSync(fullPath, content); - } + openFileInTab = async (filePath: string, content: string, options?: IResourceOpenOptions) => { + const fullPath = this.getFullPath(filePath); + if (!fsExtra.pathExistsSync(fullPath)) { + fsExtra.ensureFileSync(fullPath); + fsExtra.writeFileSync(fullPath, content); + } - const uri = URI.file(fullPath); - return { - uri, - result: await this.workbenchEditorService.open(uri, options), - }; + const uri = URI.file(fullPath); + return { + uri, + result: await this.workbenchEditorService.open(uri, options), }; + }; + + private _openDiffInTab = async ( + filePath: string, + oldContent: string, + newContent: string, + options?: IResourceOpenOptions, + ) => { + const { uri, result: openResourceResult } = await this.openFileInTab(filePath, oldContent, { + ...options, + preview: false, + }); - const openDiffInTab = async ( - filePath: string, - oldContent: string, - newContent: string, - options?: IResourceOpenOptions, - ) => { - const { uri, result: openResourceResult } = await openFileInTab(filePath, oldContent, { - ...options, - preview: false, - }); - - if (!openResourceResult) { - throw new Error('Failed to open file in tab: ' + filePath); - } - - const editor = openResourceResult.group.codeEditor; - - if (oldContent === newContent) { - this.inlineDiffHandler.destroyPreviewer(); - return; - } - - const model = this.editorDocumentModelService.getModelReference(uri); - if (!model || !model.instance) { - throw new Error('Failed to get model reference: ' + filePath); - } + if (!openResourceResult) { + throw new Error('Failed to open file in tab: ' + filePath); + } - const monacoModel = model.instance.getMonacoModel(); + const editor = openResourceResult.group.codeEditor; - monacoModel.setValue(oldContent); - const fullRange = monacoModel.getFullModelRange(); + if (oldContent === newContent) { + this.inlineDiffHandler.destroyPreviewer(); + return; + } - const previewer = this.inlineDiffHandler.createDiffPreviewer( - editor.monacoEditor, - Selection.fromRange(fullRange, SelectionDirection.LTR), - { - disposeWhenEditorClosed: false, - }, - ) as LiveInlineDiffPreviewer; - const whenReady = Event.toPromise(previewer.getNode()!.onDidEditChange); + const model = this.editorDocumentModelService.getModelReference(uri); + if (!model || !model.instance) { + throw new Error('Failed to get model reference: ' + filePath); + } - previewer.setValue(newContent); + const monacoModel = model.instance.getMonacoModel(); - await whenReady; - previewer.layout(); - previewer.revealFirstDiff(); - }; + monacoModel.setValue(oldContent); + const fullRange = monacoModel.getFullModelRange(); - const openDiffInTabByStream = async ( - filePath: string, - oldContent: string, - stream: SumiReadableStream, - options?: IResourceOpenOptions, - ) => { - const { uri, result: openResourceResult } = await openFileInTab(filePath, oldContent, { - ...options, - preview: false, - }); + const previewer = this.inlineDiffHandler.createDiffPreviewer( + editor.monacoEditor, + Selection.fromRange(fullRange, SelectionDirection.LTR), + { + disposeWhenEditorClosed: false, + }, + ) as LiveInlineDiffPreviewer; + const whenReady = Event.toPromise(previewer.getNode()!.onDidEditChange); + + previewer.setValue(newContent); + + await whenReady; + previewer.layout(); + previewer.revealFirstDiff(); + }; + + private sequencer = new Sequencer(); + openDiffInTab = async (filePath, oldContent, newContent, options?: IResourceOpenOptions) => { + await this.sequencer.queue(() => this._openDiffInTab(filePath, oldContent, newContent, options)); + }; + + openDiffInTabByStream = async ( + filePath: string, + oldContent: string, + stream: SumiReadableStream, + options?: IResourceOpenOptions, + ) => { + const { uri, result: openResourceResult } = await this.openFileInTab(filePath, oldContent, { + ...options, + preview: false, + }); - if (!openResourceResult) { - throw new Error('Failed to open file in tab: ' + filePath); - } + if (!openResourceResult) { + throw new Error('Failed to open file in tab: ' + filePath); + } - const editor = openResourceResult.group.codeEditor; + const editor = openResourceResult.group.codeEditor; - const model = this.editorDocumentModelService.getModelReference(uri); - if (!model || !model.instance) { - throw new Error('Failed to get model reference: ' + filePath); - } + const model = this.editorDocumentModelService.getModelReference(uri); + if (!model || !model.instance) { + throw new Error('Failed to get model reference: ' + filePath); + } - const monacoModel = model.instance.getMonacoModel(); + const monacoModel = model.instance.getMonacoModel(); - monacoModel.setValue(oldContent); - const fullRange = monacoModel.getFullModelRange(); + monacoModel.setValue(oldContent); + const fullRange = monacoModel.getFullModelRange(); - const controller = new InlineChatController(); - const newStream = new SumiReadableStream(); - controller.mountReadable(newStream); - listenReadable(stream, { - onData(data) { - newStream.emitData({ - kind: 'content', - content: data, - }); - }, - onEnd() { - newStream.end(); - }, - onError(error) { - newStream.emitError(error); - }, - }); + const controller = new InlineChatController(); + const newStream = new SumiReadableStream(); + controller.mountReadable(newStream); + listenReadable(stream, { + onData(data) { + newStream.emitData({ + kind: 'content', + content: data, + }); + }, + onEnd() { + newStream.end(); + }, + onError(error) { + newStream.emitError(error); + }, + }); - this.inlineDiffHandler.showPreviewerByStream( - editor.monacoEditor, - { - crossSelection: Selection.fromRange(fullRange, SelectionDirection.LTR), - chatResponse: controller, - previewerOptions: { - disposeWhenEditorClosed: false, - }, + this.inlineDiffHandler.showPreviewerByStream( + editor.monacoEditor, + { + crossSelection: Selection.fromRange(fullRange, SelectionDirection.LTR), + chatResponse: controller, + previewerOptions: { + disposeWhenEditorClosed: false, }, - ) as LiveInlineDiffPreviewer; - }; + }, + ) as LiveInlineDiffPreviewer; + }; - const getFilePathForEditor = (editor: IEditor) => { - return this.stripDirectory(editor.currentUri!.codeUri.fsPath); - }; + getFilePathForEditor = (editor: IEditor) => { + return this.stripDirectory(editor.currentUri!.codeUri.fsPath); + }; - const getAllTabs = (): IDiffViewerTab[] => { - const editorGroup = this.workbenchEditorService.editorGroups[0]; - const resources = editorGroup.resources; + getAllTabs = (): IDiffViewerTab[] => { + const editorGroup = this.workbenchEditorService.editorGroups[0]; + const resources = editorGroup.resources; - return resources.map((editor, idx) => ({ - index: idx, - filePath: this.stripDirectory(editor.uri.codeUri.fsPath), - })); + return resources.map((editor, idx) => ({ + index: idx, + filePath: this.stripDirectory(editor.uri.codeUri.fsPath), + })); + }; + + getFileIndex = (filePath: string) => { + const aPath = this.stripDirectory(filePath); + return this.getAllTabs().findIndex((tab) => tab.filePath === aPath); + }; + + getDiffInfoForUri = (uri: URI) => { + const result = { + unresolved: 0, + total: 0, + toAddedLines: 0, }; + const resourceDiff = (this.inlineDiffHandler as any)._previewerNodeStore.get(uri.toString()) as + | InlineStreamDiffHandler + | null; + + if (resourceDiff) { + const snapshot = resourceDiff.createSnapshot(); + const list = snapshot.decorationSnapshotData.partialEditWidgetList; + const unresolved = list.filter(v => v.status === 'pending'); + result.total = list.length; + result.unresolved = unresolved.length; + result.toAddedLines = snapshot.decorationSnapshotData.addedDecList.filter(v => !v.isHidden).reduce( + (acc, item) => { + return acc + item.length; + }, + 0, + ); + } + return result; + }; - const getFileIndex = (filePath: string) => { - const aPath = this.stripDirectory(filePath); - return getAllTabs().findIndex((tab) => tab.filePath === aPath); - }; + async initialize(): Promise { + await isFilesystemReady(); - const getDiffInfoForUri = (uri: URI) => { - const result = { - unresolved: 0, - total: 0, - toAddedLines: 0, - }; - const resourceDiff = (this.inlineDiffHandler as any)._previewerNodeStore.get(uri.toString()) as - | InlineStreamDiffHandler - | null; - - if (resourceDiff) { - const snapshot = resourceDiff.createSnapshot(); - const list = snapshot.decorationSnapshotData.partialEditWidgetList; - const unresolved = list.filter(v => v.status === 'pending'); - result.total = list.length; - result.unresolved = unresolved.length; - result.toAddedLines = snapshot.decorationSnapshotData.addedDecList.filter(v => !v.isHidden).reduce( - (acc, item) => { - return acc + item.length; - }, - 0, - ); - } - return result; - }; + const disposable = new Disposable(); + this._disposables.add(disposable); disposable.addDispose(this.inlineDiffHandler.onPartialEditEvent((e) => { const fsPath = e.uri.fsPath; @@ -259,7 +266,7 @@ export class DiffViewerContribution implements CommandContribution, ClientAppCon let newPath = _newPath; let currentIndex = -1; if (newPath) { - currentIndex = getFileIndex(newPath); + currentIndex = this.getFileIndex(newPath); newPath = this.stripDirectory(newPath); } @@ -270,22 +277,20 @@ export class DiffViewerContribution implements CommandContribution, ClientAppCon }; if (e?.uri) { - const diffInfo = getDiffInfoForUri(e.uri); + const diffInfo = this.getDiffInfoForUri(e.uri); event.diffNum = diffInfo.unresolved; } this._onDidTabChange.fire(event); })); - const sequencer = new Sequencer(); - this.diffViewerProps.onRef({ openDiffInTab: async (filePath, oldContent, newContent, options?: IResourceOpenOptions) => { - await sequencer.queue(() => openDiffInTab(filePath, oldContent, newContent, options)); + return this.openDiffInTab(filePath, oldContent, newContent, options); }, - openDiffInTabByStream, + openDiffInTabByStream: this.openDiffInTabByStream, openFileInTab: async (filePath: string, content: string, options?: IResourceOpenOptions) => { - const { uri } = await openFileInTab(filePath, content, options); + const { uri } = await this.openFileInTab(filePath, content, options); return uri; }, openTab: async (filePath: string, options?: IResourceOpenOptions) => { @@ -323,8 +328,8 @@ export class DiffViewerContribution implements CommandContribution, ClientAppCon disposable.dispose(); }, getCurrentTab: () => { - const allTabs = getAllTabs(); - const currentEditorFilePath = getFilePathForEditor(this.workbenchEditorService.currentEditor!); + const allTabs = this.getAllTabs(); + const currentEditorFilePath = this.getFilePathForEditor(this.workbenchEditorService.currentEditor!); const currentTabIdx = allTabs.findIndex((tab) => { return tab.filePath === currentEditorFilePath; }); @@ -332,7 +337,7 @@ export class DiffViewerContribution implements CommandContribution, ClientAppCon return; } const uri = this.getFullPathUri(currentEditorFilePath); - const diffInfo = getDiffInfoForUri(uri); + const diffInfo = this.getDiffInfoForUri(uri); const fileName = path.basename(currentEditorFilePath); return { @@ -343,11 +348,11 @@ export class DiffViewerContribution implements CommandContribution, ClientAppCon }; }, getTabAtIndex: (index) => { - const allTabs = getAllTabs(); + const allTabs = this.getAllTabs(); return allTabs[index]; }, getAllTabs: () => { - return getAllTabs(); + return this.getAllTabs(); }, closeAllTab: async () => { return this.workbenchEditorService.closeAll(); @@ -357,8 +362,15 @@ export class DiffViewerContribution implements CommandContribution, ClientAppCon }, }); } - registerCommands() { + + onDidStart(): MaybePromise { + if (this.diffViewerProps.data && isArray(this.diffViewerProps.data)) { + this.diffViewerProps.data.forEach((item) => { + this.openDiffInTab(item.path, item.oldCode, item.newCode); + }); + } } + registerMenus(registry: IMenuRegistry) { registry.unregisterMenuItem(MenuId.EditorTitle, EDITOR_COMMANDS.SPLIT_TO_RIGHT.id); registry.unregisterMenuItem(MenuId.EditorTitle, EDITOR_COMMANDS.CLOSE_ALL_IN_GROUP.id); diff --git a/packages/startup/src/diff-viewer/index.tsx b/packages/startup/src/diff-viewer/index.tsx index a1f16531..32b4addb 100644 --- a/packages/startup/src/diff-viewer/index.tsx +++ b/packages/startup/src/diff-viewer/index.tsx @@ -56,6 +56,7 @@ const App = () => { const memo = useMemo(() => (
代码生成中
, }} @@ -84,13 +85,6 @@ const App = () => { console.log('onDidTabChange', e.newPath); setEventInfo(e); }); - data.forEach(v => { - handleRef.current!.openDiffInTab( - v.path, - v.oldCode, - v.newCode, - ); - }); setTimeout(() => { const currentTab = handleRef.current!.getCurrentTab();