Skip to content

Commit

Permalink
feat: support diff & git uri preview of notebook file (#4323)
Browse files Browse the repository at this point in the history
* feat: 支持 notebook git uri 预览

* feat: 完成diff新增cell能力开发

* feat: 支持cell diff

* feat: support delete & unchanged cell diff

* fix: fix notebook git uri

* fix: dispose editors

* fix: update dependency on editorTargetRef current value

* fix: lint problem

* fix: missing deps

* chore: update lock
  • Loading branch information
ensorrow authored Jan 20, 2025
1 parent 92e7bca commit 54bc272
Show file tree
Hide file tree
Showing 20 changed files with 1,580 additions and 7 deletions.
5 changes: 4 additions & 1 deletion packages/notebook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
"@opensumi/ide-outline": "workspace:*",
"@opensumi/ide-overlay": "workspace:*",
"@opensumi/ide-theme": "workspace:*",
"antd": "^5.21.4"
"@opensumi/ide-utils": "workspace:*",
"antd": "^5.21.4",
"diff": "^4.0.1",
"resize-observer-polyfill": "1.5.1"
}
}
33 changes: 33 additions & 0 deletions packages/notebook/src/browser/libro-preview.view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Container, URI, ViewRender } from '@difizen/mana-app';
import React, { useEffect, useState } from 'react';

import { useInjectable } from '@opensumi/ide-core-browser';
import { ReactEditorComponent } from '@opensumi/ide-editor/lib/browser/types';

import { LibroVersionManager } from './libro/diff-view/libro-version-manager';
import { AIStudioLibroVersionView } from './libro/diff-view/libro-version-view';
import { ContentLoaderType, ManaContainer } from './mana';

export const LibroVersionPreview: ReactEditorComponent = ({ resource }) => {
const uri = resource.uri;
const originalUri = uri.scheme === 'diff' ? new URI(decodeURIComponent(uri.getParsedQuery().original)) : undefined;
const targetUri = uri.scheme === 'diff' ? new URI(decodeURIComponent(uri.getParsedQuery().modified)) : undefined;
const manaContainer = useInjectable<Container>(ManaContainer);
const libroVersionManager = manaContainer.get(LibroVersionManager);
const [versionView, setVersionView] = useState<AIStudioLibroVersionView>();

useEffect(() => {
libroVersionManager
.getOrCreateView({
resource: uri.toString(),
loadType: ContentLoaderType,
originalUri,
targetUri,
})
.then((view) => {
setVersionView(view);
});
}, [uri]);

return <div className='libro-version'>{versionView && <ViewRender view={versionView}></ViewRender>}</div>;
};
47 changes: 43 additions & 4 deletions packages/notebook/src/browser/libro.contribution.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ import { IThemeService, IconType } from '@opensumi/ide-theme/lib/common';

import { KERNEL_PANEL_ID, KernelPanel, initKernelPanelColorToken } from './kernel-panel';
import { LibroOpensumiModule } from './libro';
import { LibroDiffModule } from './libro/diff-view';
import { LibroOpener } from './libro-opener';
import { LibroVersionPreview } from './libro-preview.view';
import { initLibroColorToken } from './libro.color.tokens';
import { LIBRO_COMPONENTS_ID, LIBRO_COMPONENTS_SCHEME_ID } from './libro.protocol';
import { LIBRO_COMPONENTS_SCHEME_ID, LIBRO_COMPONENT_ID, LIBRO_PREVIEW_COMPONENT_ID } from './libro.protocol';
import { OpensumiLibroView } from './libro.view';
import { ManaContainer, initLibroOpensumi, manaContainer } from './mana/index';
import { NotebookDocumentContentProvider } from './notebook-document-content-provider';
Expand All @@ -62,7 +64,14 @@ const LayoutWrapper: React.FC<React.PropsWithChildren> = ({ children }) => {
return (
<ManaComponents.Application
context={{ container: manaContainer }}
modules={[ManaAppPreset, LibroJupyterNoEditorModule, LibroTOCModule, LibroOpensumiModule, LibroVariableModule]}
modules={[
ManaAppPreset,
LibroJupyterNoEditorModule,
LibroDiffModule,
LibroTOCModule,
LibroOpensumiModule,
LibroVariableModule,
]}
renderChildren
onReady={() => setIsReady(true)}
>
Expand Down Expand Up @@ -182,11 +191,17 @@ export class LibroContribution

registerEditorComponent(registry: EditorComponentRegistry) {
registry.registerEditorComponent({
uid: LIBRO_COMPONENTS_ID,
uid: LIBRO_COMPONENT_ID,
scheme: LIBRO_COMPONENTS_SCHEME_ID,
component: OpensumiLibroView,
});

registry.registerEditorComponent({
uid: LIBRO_PREVIEW_COMPONENT_ID,
scheme: LIBRO_COMPONENTS_SCHEME_ID,
component: LibroVersionPreview,
});

registry.registerEditorComponentResolver(Schemes.file, (resource, results) => {
if (resource.uri.path.ext === `.${LIBRO_COMPONENTS_SCHEME_ID}`) {
// 首次打开 notebook 文件时初始化 jupyter 服务连接
Expand All @@ -197,7 +212,31 @@ export class LibroContribution
}
results.push({
type: 'component',
componentId: LIBRO_COMPONENTS_ID,
componentId: LIBRO_COMPONENT_ID,
});
}
});

// git schema 的 notebook 资源,在 ide 中打开
registry.registerEditorComponentResolver('git', (resource, results) => {
if (resource.uri.path.ext === '.ipynb') {
results.push({
type: 'component',
componentId: LIBRO_PREVIEW_COMPONENT_ID,
});
}
});

registry.registerEditorComponentResolver('diff', (resource, results) => {
const { original, modified } = resource.uri.getParsedQuery();
if (
new URI(decodeURIComponent(modified)).path.ext === '.ipynb' ||
new URI(decodeURIComponent(original)).path.ext === '.ipynb'
) {
// TODO: 需要等 git 插件 ready,否则 git uri 无法解析
results.push({
type: 'component',
componentId: LIBRO_PREVIEW_COMPONENT_ID,
});
}
});
Expand Down
4 changes: 3 additions & 1 deletion packages/notebook/src/browser/libro.protocol.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export const LIBRO_COMPONENTS_ID = 'opensumi:libro';
export const LIBRO_COMPONENT_ID = 'opensumi:libro';
export const LIBRO_PREVIEW_COMPONENT_ID = 'opensumi:libro-preview';
export const LIBRO_DIFF_COMPONENT_ID = 'opensumi:libro-diff';
export const LIBRO_COMPONENTS_SCHEME_ID = 'ipynb';
123 changes: 123 additions & 0 deletions packages/notebook/src/browser/libro/diff-view/components/hooks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { useCallback, useEffect, useLayoutEffect, useRef } from 'react';
import ResizeObserver from 'resize-observer-polyfill';

import { ICodeEditor, IDiffEditor } from '@opensumi/ide-monaco';

export interface Size {
width?: number;
height?: number;
}

function useLatest<T>(value: T) {
const ref = useRef(value);
ref.current = value;

return ref;
}

export default useLatest;
export function useSize(fn: () => void, ref: React.ForwardedRef<HTMLDivElement>): void {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const callback = useLatest((size: Size) => {
fn();
});
useLayoutEffect(() => {
if (typeof ref !== 'object') {
return () => {};
}
const el = ref?.current;
if (!el || !fn) {
return () => {};
}
const resizeObserver = new ResizeObserver((entries) => {
entries.forEach((entry) => {
callback.current({
width: entry.target.clientWidth,
height: entry.target.clientHeight,
});
});
});

resizeObserver.observe(el as HTMLElement);
return () => {
resizeObserver.disconnect();
};
}, [callback, ref, fn]);
}

const setEditorHeight = (
editor: ICodeEditor | undefined,
editorTarget: HTMLDivElement,
editorContainer: HTMLDivElement,
) => {
if (!editor) {
return;
}
const curLenght = editor.getModel()?.getLineCount() || 1;
const diffItemHeight = `${curLenght * 20 + 16 + 12 + 22}px`;
const _height = `${curLenght * 20}px`;
if (editorTarget.style.height !== _height) {
editorTarget.style.height = _height;
editorContainer.style.height = diffItemHeight;
editor.layout();
}
};

const setDiffEditorHeight = (
editor: IDiffEditor | undefined,
editorTarget: HTMLDivElement,
editorContainer: HTMLDivElement,
) => {
const originalLineCount = editor?.getModel()?.original.getLineCount() || 0;

editor?.onDidUpdateDiff(() => {
let finalLineCount = originalLineCount;
for (const change of editor?.getLineChanges() || []) {
if (!change.originalEndLineNumber) {
finalLineCount += change.modifiedEndLineNumber - change.modifiedStartLineNumber + 1;
} else if (
change.originalStartLineNumber === change.modifiedStartLineNumber &&
change.modifiedEndLineNumber > change.originalEndLineNumber
) {
finalLineCount += change.modifiedEndLineNumber - change.originalEndLineNumber;
}
}
editorTarget.style.height = `${finalLineCount * 20 + 12}px`;
editorContainer.style.height = `${finalLineCount * 20 + 16 + 12 + 22}px`;
editor.layout();
});
};

export function useEditorLayout(
editor: ICodeEditor | IDiffEditor | undefined,
editorTargetRef: React.RefObject<HTMLDivElement>,
editorContainerRef: React.RefObject<HTMLDivElement>,
) {
const editorTarget = editorTargetRef.current;
const editorContainer = editorContainerRef.current;

const editorLaylout = useCallback(() => {
if (editor) {
editor.layout();
}
}, [editor]);

useEffect(() => {
if (editor) {
if (!editorTarget || !editorContainer) {
return;
}
if ((editor as IDiffEditor).renderSideBySide !== undefined) {
setDiffEditorHeight(editor as IDiffEditor, editorTarget, editorContainer);
} else {
setEditorHeight(editor as ICodeEditor, editorTarget, editorContainer);
}
}
window.addEventListener('resize', editorLaylout);
return () => {
window.removeEventListener('resize', editorLaylout);
};
}, [editor, editorLaylout]);

useSize(editorLaylout, editorContainerRef);
}
Loading

1 comment on commit 54bc272

@opensumi
Copy link
Contributor

@opensumi opensumi bot commented on 54bc272 Jan 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 Next publish successful!

3.7.1-next-1737368070.0

Please sign in to comment.