diff --git a/packages/docs-drawing-ui/src/__tests__/create-doc-ui-test-bed.ts b/packages/docs-drawing-ui/src/__tests__/create-doc-ui-test-bed.ts index a612b84172..f1c6dba4c6 100644 --- a/packages/docs-drawing-ui/src/__tests__/create-doc-ui-test-bed.ts +++ b/packages/docs-drawing-ui/src/__tests__/create-doc-ui-test-bed.ts @@ -16,10 +16,11 @@ /* eslint-disable ts/no-explicit-any */ -import type { Ctor, Dependency, DependencyIdentifier, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core'; -import type { DocumentSkeleton, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import type { Dependency, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core'; +import type { DocumentSkeleton, IRenderContext, IRenderModule } from '@univerjs/engine-render'; import { BooleanNumber, + DisposableCollection, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ILogService, Inject, @@ -32,9 +33,9 @@ import { Univer, UniverInstanceType, } from '@univerjs/core'; -import { DocSelectionManagerService, DocSkeletonManagerService, DocStateEmitService } from '@univerjs/docs'; -import { DocIMEInputManagerService, DocSelectionRenderService, DocStateChangeManagerService } from '@univerjs/docs-ui'; -import { DocumentViewModel, IRenderManagerService } from '@univerjs/engine-render'; +import { DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService } from '@univerjs/docs'; +import { DocIMEInputManagerService, DocSelectionRenderService } from '@univerjs/docs-ui'; +import { DocumentViewModel, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render'; import { BehaviorSubject, takeUntil } from 'rxjs'; const DEFAULT_DOC_DATA: IDocumentData = { @@ -82,7 +83,7 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen override onStarting(): void { this._injector.get(IUndoRedoService); if (!dependencies?.some((dependency) => Array.isArray(dependency) && dependency[0] === IRenderManagerService)) { - this._injector.add([IRenderManagerService, { useClass: MockRenderManagerService as unknown as Ctor }]); + this._injector.add([IRenderManagerService, { useClass: RenderManagerService }]); } this._injector.add([DocSelectionManagerService]); this._injector.add([DocStateEmitService]); @@ -116,6 +117,23 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen }, univerInstanceService); injector.add([DocSkeletonManagerService, { useValue: fakeDocSkeletonManager as unknown as DocSkeletonManagerService }]); + const renderManagerService = get(IRenderManagerService); + if (renderManagerService.getRenderUnitById(doc.getUnitId()) == null) { + renderManagerService.addRender(doc.getUnitId(), { + unitId: doc.getUnitId(), + type: UniverInstanceType.UNIVER_DOC, + engine: new DisposableCollection() as any, + scene: new DisposableCollection() as any, + mainComponent: null as any, + components: new Map(), + isMainScene: true, + activated$: new BehaviorSubject(true), + with: injector.get.bind(injector), + activate: () => {}, + deactivate: () => {}, + isDisposed: () => false, + }); + } univerInstanceService.focusUnit(doc.getUnitId()); get(ILogService).setLogLevel(LogLevel.SILENT); @@ -127,17 +145,6 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen }; } -class MockRenderManagerService implements Pick { - constructor(@Inject(Injector) private readonly _injector: Injector) {} - - getRenderById(): Nullable { - return { - with: (identifier: DependencyIdentifier) => this._injector.get(identifier), - isDisposed: () => false, - } as unknown as IRender; - } -} - class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule { private _docViewModel!: DocumentViewModel; private readonly _currentSkeleton$ = new BehaviorSubject>(null); diff --git a/packages/docs-drawing-ui/src/commands/commands/__tests__/drawing-commands.spec.ts b/packages/docs-drawing-ui/src/commands/commands/__tests__/drawing-commands.spec.ts index d7cd828ae3..effa1ee433 100644 --- a/packages/docs-drawing-ui/src/commands/commands/__tests__/drawing-commands.spec.ts +++ b/packages/docs-drawing-ui/src/commands/commands/__tests__/drawing-commands.spec.ts @@ -14,10 +14,11 @@ * limitations under the License. */ -import type { DocumentDataModel, ICommand, IDocumentData, Injector } from '@univerjs/core'; +import type { DependencyIdentifier, DocumentDataModel, ICommand, IDocumentData } from '@univerjs/core'; import { awaitTime, Direction, + DisposableCollection, ICommandService, IUniverInstanceService, ObjectRelativeFromH, @@ -30,7 +31,8 @@ import { DocContentInsertService, DocSelectionManagerService, DocSkeletonManager import { DocDrawingController as CoreDocDrawingController, DocDrawingService, IDocDrawingService } from '@univerjs/docs-drawing'; import { DocSelectionRenderService } from '@univerjs/docs-ui'; import { DrawingManagerService, IDrawingManagerService } from '@univerjs/drawing'; -import { DocumentEditArea, IRenderManagerService } from '@univerjs/engine-render'; +import { DocumentEditArea, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render'; +import { BehaviorSubject } from 'rxjs'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { createDocUiTestBed } from '../../../__tests__/create-doc-ui-test-bed'; import { DocDrawingAddRemoveController } from '../../../controllers/doc-drawing-notification.controller'; @@ -101,30 +103,38 @@ function createDrawingDocData(): IDocumentData { function setupDrawingTestBed(docData: IDocumentData) { const refreshControls = vi.fn(); - let injector!: Injector; - const renderManagerService = { - getRenderById: () => ({ - scene: { - getTransformerByCreate: () => ({ - refreshControls, - }), - }, - with: (token: T) => { - if (token === DocSelectionRenderService) { - return { - getSegment: () => '', - } as T; - } - - return injector.get(token as never); - }, - }), - }; const testBed = createDocUiTestBed(docData, [ - [IRenderManagerService, { useValue: renderManagerService }], + [IRenderManagerService, { useClass: RenderManagerService }], ]); const { univer, get } = testBed; - injector = testBed.injector; + const injector = testBed.injector; + + const scene = new DisposableCollection() as any; + scene.getTransformerByCreate = () => ({ + refreshControls, + }); + get(IRenderManagerService).addRender('test-doc', { + unitId: 'test-doc', + type: UniverInstanceType.UNIVER_DOC, + engine: new DisposableCollection() as any, + scene, + mainComponent: null as any, + components: new Map(), + isMainScene: true, + activated$: new BehaviorSubject(true), + with: (token: DependencyIdentifier) => { + if (token === DocSelectionRenderService) { + return { + getSegment: () => '', + } as T; + } + + return injector.get(token); + }, + activate: () => {}, + deactivate: () => {}, + isDisposed: () => false, + }); injector.add([DocDrawingService]); injector.add([IDocDrawingService, { useClass: DocDrawingService }]); diff --git a/packages/docs-hyper-link-ui/src/__tests__/create-doc-ui-test-bed.ts b/packages/docs-hyper-link-ui/src/__tests__/create-doc-ui-test-bed.ts index a612b84172..f1c6dba4c6 100644 --- a/packages/docs-hyper-link-ui/src/__tests__/create-doc-ui-test-bed.ts +++ b/packages/docs-hyper-link-ui/src/__tests__/create-doc-ui-test-bed.ts @@ -16,10 +16,11 @@ /* eslint-disable ts/no-explicit-any */ -import type { Ctor, Dependency, DependencyIdentifier, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core'; -import type { DocumentSkeleton, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import type { Dependency, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core'; +import type { DocumentSkeleton, IRenderContext, IRenderModule } from '@univerjs/engine-render'; import { BooleanNumber, + DisposableCollection, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ILogService, Inject, @@ -32,9 +33,9 @@ import { Univer, UniverInstanceType, } from '@univerjs/core'; -import { DocSelectionManagerService, DocSkeletonManagerService, DocStateEmitService } from '@univerjs/docs'; -import { DocIMEInputManagerService, DocSelectionRenderService, DocStateChangeManagerService } from '@univerjs/docs-ui'; -import { DocumentViewModel, IRenderManagerService } from '@univerjs/engine-render'; +import { DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService } from '@univerjs/docs'; +import { DocIMEInputManagerService, DocSelectionRenderService } from '@univerjs/docs-ui'; +import { DocumentViewModel, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render'; import { BehaviorSubject, takeUntil } from 'rxjs'; const DEFAULT_DOC_DATA: IDocumentData = { @@ -82,7 +83,7 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen override onStarting(): void { this._injector.get(IUndoRedoService); if (!dependencies?.some((dependency) => Array.isArray(dependency) && dependency[0] === IRenderManagerService)) { - this._injector.add([IRenderManagerService, { useClass: MockRenderManagerService as unknown as Ctor }]); + this._injector.add([IRenderManagerService, { useClass: RenderManagerService }]); } this._injector.add([DocSelectionManagerService]); this._injector.add([DocStateEmitService]); @@ -116,6 +117,23 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen }, univerInstanceService); injector.add([DocSkeletonManagerService, { useValue: fakeDocSkeletonManager as unknown as DocSkeletonManagerService }]); + const renderManagerService = get(IRenderManagerService); + if (renderManagerService.getRenderUnitById(doc.getUnitId()) == null) { + renderManagerService.addRender(doc.getUnitId(), { + unitId: doc.getUnitId(), + type: UniverInstanceType.UNIVER_DOC, + engine: new DisposableCollection() as any, + scene: new DisposableCollection() as any, + mainComponent: null as any, + components: new Map(), + isMainScene: true, + activated$: new BehaviorSubject(true), + with: injector.get.bind(injector), + activate: () => {}, + deactivate: () => {}, + isDisposed: () => false, + }); + } univerInstanceService.focusUnit(doc.getUnitId()); get(ILogService).setLogLevel(LogLevel.SILENT); @@ -127,17 +145,6 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen }; } -class MockRenderManagerService implements Pick { - constructor(@Inject(Injector) private readonly _injector: Injector) {} - - getRenderById(): Nullable { - return { - with: (identifier: DependencyIdentifier) => this._injector.get(identifier), - isDisposed: () => false, - } as unknown as IRender; - } -} - class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule { private _docViewModel!: DocumentViewModel; private readonly _currentSkeleton$ = new BehaviorSubject>(null); diff --git a/packages/docs-mention-ui/src/__tests__/create-doc-ui-test-bed.ts b/packages/docs-mention-ui/src/__tests__/create-doc-ui-test-bed.ts index a612b84172..f1c6dba4c6 100644 --- a/packages/docs-mention-ui/src/__tests__/create-doc-ui-test-bed.ts +++ b/packages/docs-mention-ui/src/__tests__/create-doc-ui-test-bed.ts @@ -16,10 +16,11 @@ /* eslint-disable ts/no-explicit-any */ -import type { Ctor, Dependency, DependencyIdentifier, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core'; -import type { DocumentSkeleton, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import type { Dependency, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core'; +import type { DocumentSkeleton, IRenderContext, IRenderModule } from '@univerjs/engine-render'; import { BooleanNumber, + DisposableCollection, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ILogService, Inject, @@ -32,9 +33,9 @@ import { Univer, UniverInstanceType, } from '@univerjs/core'; -import { DocSelectionManagerService, DocSkeletonManagerService, DocStateEmitService } from '@univerjs/docs'; -import { DocIMEInputManagerService, DocSelectionRenderService, DocStateChangeManagerService } from '@univerjs/docs-ui'; -import { DocumentViewModel, IRenderManagerService } from '@univerjs/engine-render'; +import { DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService } from '@univerjs/docs'; +import { DocIMEInputManagerService, DocSelectionRenderService } from '@univerjs/docs-ui'; +import { DocumentViewModel, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render'; import { BehaviorSubject, takeUntil } from 'rxjs'; const DEFAULT_DOC_DATA: IDocumentData = { @@ -82,7 +83,7 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen override onStarting(): void { this._injector.get(IUndoRedoService); if (!dependencies?.some((dependency) => Array.isArray(dependency) && dependency[0] === IRenderManagerService)) { - this._injector.add([IRenderManagerService, { useClass: MockRenderManagerService as unknown as Ctor }]); + this._injector.add([IRenderManagerService, { useClass: RenderManagerService }]); } this._injector.add([DocSelectionManagerService]); this._injector.add([DocStateEmitService]); @@ -116,6 +117,23 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen }, univerInstanceService); injector.add([DocSkeletonManagerService, { useValue: fakeDocSkeletonManager as unknown as DocSkeletonManagerService }]); + const renderManagerService = get(IRenderManagerService); + if (renderManagerService.getRenderUnitById(doc.getUnitId()) == null) { + renderManagerService.addRender(doc.getUnitId(), { + unitId: doc.getUnitId(), + type: UniverInstanceType.UNIVER_DOC, + engine: new DisposableCollection() as any, + scene: new DisposableCollection() as any, + mainComponent: null as any, + components: new Map(), + isMainScene: true, + activated$: new BehaviorSubject(true), + with: injector.get.bind(injector), + activate: () => {}, + deactivate: () => {}, + isDisposed: () => false, + }); + } univerInstanceService.focusUnit(doc.getUnitId()); get(ILogService).setLogLevel(LogLevel.SILENT); @@ -127,17 +145,6 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen }; } -class MockRenderManagerService implements Pick { - constructor(@Inject(Injector) private readonly _injector: Injector) {} - - getRenderById(): Nullable { - return { - with: (identifier: DependencyIdentifier) => this._injector.get(identifier), - isDisposed: () => false, - } as unknown as IRender; - } -} - class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule { private _docViewModel!: DocumentViewModel; private readonly _currentSkeleton$ = new BehaviorSubject>(null); diff --git a/packages/docs-ui/src/commands/commands/__tests__/create-command-test-bed.ts b/packages/docs-ui/src/commands/commands/__tests__/create-command-test-bed.ts index fe180cdd07..d0564e674f 100644 --- a/packages/docs-ui/src/commands/commands/__tests__/create-command-test-bed.ts +++ b/packages/docs-ui/src/commands/commands/__tests__/create-command-test-bed.ts @@ -16,10 +16,11 @@ /* eslint-disable ts/no-explicit-any */ -import type { Ctor, Dependency, DependencyIdentifier, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core'; -import type { DocumentSkeleton, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import type { Dependency, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core'; +import type { DocumentSkeleton, IRenderContext, IRenderModule } from '@univerjs/engine-render'; import { BooleanNumber, + DisposableCollection, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ILogService, Inject, @@ -32,12 +33,11 @@ import { Univer, UniverInstanceType, } from '@univerjs/core'; -import { DocSelectionManagerService, DocSkeletonManagerService, DocStateEmitService } from '@univerjs/docs'; -import { DocumentViewModel, IRenderManagerService } from '@univerjs/engine-render'; +import { DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService } from '@univerjs/docs'; +import { DocumentViewModel, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render'; import { BehaviorSubject, takeUntil } from 'rxjs'; import { DocIMEInputManagerService } from '../../../services/doc-ime-input-manager.service'; import { DocMenuStyleService } from '../../../services/doc-menu-style.service'; -import { DocStateChangeManagerService } from '../../../services/doc-state-change-manager.service'; import { DocSelectionRenderService } from '../../../services/selection/doc-selection-render.service'; const TEST_DOCUMENT_DATA_EN: IDocumentData = { @@ -114,7 +114,7 @@ export function createCommandTestBed(docData?: IDocumentData, dependencies?: Dep const injector = this._injector; injector.get(IUndoRedoService); - injector.add([IRenderManagerService, { useClass: MockRenderManagerService as unknown as Ctor }]); + injector.add([IRenderManagerService, { useClass: RenderManagerService }]); injector.add([DocSelectionManagerService]); injector.add([DocMenuStyleService]); @@ -155,6 +155,23 @@ export function createCommandTestBed(docData?: IDocumentData, dependencies?: Dep }, univerInstanceService); injector.add([DocSkeletonManagerService, { useValue: fakeDocSkeletonManager as unknown as DocSkeletonManagerService }]); + const renderManagerService = get(IRenderManagerService); + if (renderManagerService.getRenderUnitById(doc.getUnitId()) == null) { + renderManagerService.addRender(doc.getUnitId(), { + unitId: doc.getUnitId(), + type: UniverInstanceType.UNIVER_DOC, + engine: new DisposableCollection() as any, + scene: new DisposableCollection() as any, + mainComponent: null as any, + components: new Map(), + isMainScene: true, + activated$: new BehaviorSubject(true), + with: injector.get.bind(injector), + activate: () => {}, + deactivate: () => {}, + isDisposed: () => false, + }); + } univerInstanceService.focusUnit('test-doc'); @@ -169,21 +186,6 @@ export function createCommandTestBed(docData?: IDocumentData, dependencies?: Dep }; } -// These services are for document build and manage doc skeletons. - -export class MockRenderManagerService implements Pick { - constructor( - @Inject(Injector) private readonly _injector: Injector - ) { } - - getRenderById(_unitId: string): Nullable { - return { - with: (identifier: DependencyIdentifier) => this._injector.get(identifier), - isDisposed: () => false, - } as unknown as IRender; - } -} - export class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule { private _docViewModel: DocumentViewModel; diff --git a/packages/docs-ui/src/facade/__tests__/create-test-bed.ts b/packages/docs-ui/src/facade/__tests__/create-test-bed.ts index bff35f600b..67103c49e4 100644 --- a/packages/docs-ui/src/facade/__tests__/create-test-bed.ts +++ b/packages/docs-ui/src/facade/__tests__/create-test-bed.ts @@ -27,8 +27,8 @@ import { UniverInstanceType, } from '@univerjs/core'; import { FUniver } from '@univerjs/core/facade'; -import { DocSelectionManagerService, DocSkeletonManagerService, DocStateEmitService } from '@univerjs/docs'; -import { DocIMEInputManagerService, DocsRenderService, DocStateChangeManagerService } from '@univerjs/docs-ui'; +import { DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService } from '@univerjs/docs'; +import { DocIMEInputManagerService, DocsRenderService } from '@univerjs/docs-ui'; import enUS from '@univerjs/docs-ui/locale/en-US'; import zhCN from '@univerjs/docs-ui/locale/zh-CN'; import { DumbCanvasColorService, ICanvasColorService, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render'; diff --git a/packages/docs-ui/src/facade/__tests__/f-document.spec.ts b/packages/docs-ui/src/facade/__tests__/f-document.spec.ts index 5094416f03..1a267b611b 100644 --- a/packages/docs-ui/src/facade/__tests__/f-document.spec.ts +++ b/packages/docs-ui/src/facade/__tests__/f-document.spec.ts @@ -71,7 +71,7 @@ describe('Test FDocument', () => { startOffset: 6, })).resolves.toBe(true); - expect(document.save().body?.dataStream).toBe('Hello,Line 1\r\nLine 2\r\n\r\n'); + expect(document.save().body?.dataStream).toBe('Hello,Line 1\rLine 2\r\r\n'); }); it('throws when appending text to a document without a body', () => { diff --git a/packages/docs-ui/src/index.ts b/packages/docs-ui/src/index.ts index 54e4909e63..938bc6d570 100644 --- a/packages/docs-ui/src/index.ts +++ b/packages/docs-ui/src/index.ts @@ -77,7 +77,6 @@ export { SetParagraphNamedStyleCommand } from './commands/commands/set-heading.c export { SwitchDocModeCommand } from './commands/commands/switch-doc-mode.command'; export { CreateDocTableCommand, type ICreateDocTableCommandParams } from './commands/commands/table/doc-table-create.command'; export { DocTableDeleteColumnsCommand, DocTableDeleteRowsCommand, DocTableDeleteTableCommand } from './commands/commands/table/doc-table-delete.command'; - export type { IDocTableDeleteColumnsCommandParams, IDocTableDeleteRowsCommandParams, @@ -102,7 +101,6 @@ export { export type { IDocTableTabCommandParams } from './commands/commands/table/doc-table-tab.command'; export { DocTableTabCommand } from './commands/commands/table/doc-table-tab.command'; export { genTableSource, getEmptyTableCell, getEmptyTableRow, getTableColumn } from './commands/commands/table/table'; - export { DocCreateTableOperation } from './commands/operations/doc-create-table.operation'; export { type IMoveCursorOperationParams, MoveSelectionOperation } from './commands/operations/doc-cursor.operation'; export { MoveCursorOperation } from './commands/operations/doc-cursor.operation'; @@ -118,7 +116,6 @@ export { menuSchema as DocsUIMenuSchema } from './menu/schema'; export { UniverDocsUIPlugin } from './plugin'; export * from './services'; export { IDocClipboardService } from './services/clipboard/clipboard.service'; - export { convertBodyToHtml } from './services/clipboard/udm-to-html/convertor'; export { DocAutoFormatService } from './services/doc-auto-format.service'; export { DocEventManagerService, type IBulletBound } from './services/doc-event-manager.service'; @@ -126,14 +123,12 @@ export { DocIMEInputManagerService } from './services/doc-ime-input-manager.serv export { DocParagraphMenuService } from './services/doc-paragraph-menu.service'; export { calcDocRangePositions, DocCanvasPopManagerService } from './services/doc-popup-manager.service'; export { DocPrintInterceptorService, type IDocPrintComponentContext, type IDocPrintContext } from './services/doc-print-interceptor.service'; -export { DocStateChangeManagerService } from './services/doc-state-change-manager.service'; export { DocsRenderService } from './services/docs-render.service'; export { Editor } from './services/editor/editor'; export { EditorService, IEditorService } from './services/editor/editor-manager.service'; export { isInSameTableCell, isValidRectRange, NodePositionConvertToRectRange } from './services/selection/convert-rect-range'; export { NodePositionConvertToCursor } from './services/selection/convert-text-range'; export { getOneTextSelectionRange } from './services/selection/convert-text-range'; - export type { IEditorInputConfig } from './services/selection/doc-selection-render.service'; export { DocSelectionRenderService } from './services/selection/doc-selection-render.service'; export type { IDocRange } from './services/selection/range-interface'; diff --git a/packages/docs-ui/src/plugin.ts b/packages/docs-ui/src/plugin.ts index 3ba0e9da6d..9350ed7588 100644 --- a/packages/docs-ui/src/plugin.ts +++ b/packages/docs-ui/src/plugin.ts @@ -30,7 +30,7 @@ import { touchDependencies, UniverInstanceType, } from '@univerjs/core'; -import { DocInterceptorService, DocSkeletonManagerService } from '@univerjs/docs'; +import { DocInterceptorService, DocSkeletonManagerService, IDocStateChangeInterceptorService } from '@univerjs/docs'; import { IRenderManagerService, UniverRenderEnginePlugin } from '@univerjs/engine-render'; import { IShortcutService } from '@univerjs/ui'; import pkg from '../package.json'; @@ -117,12 +117,12 @@ import { DocClipboardService, IDocClipboardService } from './services/clipboard/ import { DocAutoFormatService } from './services/doc-auto-format.service'; import { DocEventManagerService } from './services/doc-event-manager.service'; import { DocIMEInputManagerService } from './services/doc-ime-input-manager.service'; +import { DocIMEStateChangeInterceptorService } from './services/doc-ime-state-change-interceptor.service'; import { DocMenuStyleService } from './services/doc-menu-style.service'; import { DocPageLayoutService } from './services/doc-page-layout.service'; import { DocParagraphMenuService } from './services/doc-paragraph-menu.service'; import { DocCanvasPopManagerService } from './services/doc-popup-manager.service'; import { DocPrintInterceptorService } from './services/doc-print-interceptor.service'; -import { DocStateChangeManagerService } from './services/doc-state-change-manager.service'; import { DocsRenderService } from './services/docs-render.service'; import { EditorService, IEditorService } from './services/editor/editor-manager.service'; import { DocFloatMenuService } from './services/float-menu.service'; @@ -179,9 +179,7 @@ export class UniverDocsUIPlugin extends Plugin { this._markDocAsFocused(); touchDependencies(this._injector, [ - [DocStateChangeManagerService], [DocsRenderService], - ]); } @@ -329,7 +327,7 @@ export class UniverDocsUIPlugin extends Plugin { [IDocClipboardService, { useClass: DocClipboardService }], [DocCanvasPopManagerService], [DocsRenderService], - [DocStateChangeManagerService], + [IDocStateChangeInterceptorService, { useClass: DocIMEStateChangeInterceptorService }], [DocAutoFormatService], [DocMenuStyleService], diff --git a/packages/docs-ui/src/services/doc-ime-state-change-interceptor.service.ts b/packages/docs-ui/src/services/doc-ime-state-change-interceptor.service.ts new file mode 100644 index 0000000000..eeb1a3c612 --- /dev/null +++ b/packages/docs-ui/src/services/doc-ime-state-change-interceptor.service.ts @@ -0,0 +1,61 @@ +/** + * Copyright 2023-present DreamNum Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Nullable } from '@univerjs/core'; +import type { IDocStateChangeInfo, IDocStateChangeInterceptorService } from '@univerjs/docs'; +import { IRenderManagerService } from '@univerjs/engine-render'; +import { DocIMEInputManagerService } from './doc-ime-input-manager.service'; + +export class DocIMEStateChangeInterceptorService implements IDocStateChangeInterceptorService { + constructor( + @IRenderManagerService private readonly _renderManagerService: IRenderManagerService + ) {} + + transformChangeStateInfo(changeStateInfo: IDocStateChangeInfo): Nullable { + const { isCompositionEnd, isSync, syncer } = changeStateInfo; + + if (!isCompositionEnd) { + return changeStateInfo; + } + + const imeInputManagerService = this._renderManagerService.getRenderUnitById(isSync ? syncer! : changeStateInfo.unitId)?.with(DocIMEInputManagerService); + + if (imeInputManagerService == null) { + return null; + } + + const historyParams = imeInputManagerService.fetchComposedUndoRedoMutationParams(); + + if (historyParams == null) { + throw new Error('historyParams is null in RichTextEditingMutation'); + } + + const { undoMutationParams, redoMutationParams, previousActiveRange } = historyParams; + + return { + ...changeStateInfo, + redoState: { + ...changeStateInfo.redoState, + actions: redoMutationParams.actions, + }, + undoState: { + ...changeStateInfo.undoState, + actions: undoMutationParams.actions, + textRanges: [previousActiveRange], + }, + }; + } +} diff --git a/packages/docs/src/__tests__/docs.integration.spec.ts b/packages/docs/src/__tests__/docs.integration.spec.ts index 358a7f0018..e85ca1f494 100644 --- a/packages/docs/src/__tests__/docs.integration.spec.ts +++ b/packages/docs/src/__tests__/docs.integration.spec.ts @@ -19,6 +19,7 @@ import type { IRenderContext } from '@univerjs/engine-render'; import { awaitTime, CustomRangeType, + DisposableCollection, ICommandService, IUniverInstanceService, LifecycleService, @@ -27,7 +28,8 @@ import { Univer, UniverInstanceType, } from '@univerjs/core'; -import { IRenderManagerService } from '@univerjs/engine-render'; +import { IRenderManagerService, RenderManagerService } from '@univerjs/engine-render'; +import { BehaviorSubject } from 'rxjs'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { DocsRenameMutation } from '../commands/mutations/docs-rename.mutation'; import { SetTextSelectionsOperation } from '../commands/operations/text-selection.operation'; @@ -36,6 +38,7 @@ import { DocInterceptorService } from '../services/doc-interceptor/doc-intercept import { DOC_INTERCEPTOR_POINT } from '../services/doc-interceptor/interceptor-const'; import { DocSelectionManagerService } from '../services/doc-selection-manager.service'; import { DocSkeletonManagerService } from '../services/doc-skeleton-manager.service'; +import { DocStateChangeManagerService } from '../services/doc-state-change-manager.service'; import { DocStateEmitService } from '../services/doc-state-emit.service'; import { DocViewModelManagerService } from '../services/doc-view-model-manager.service'; import { addCustomRangeBySelectionFactory, deleteCustomRangeFactory } from '../utils/custom-range-factory'; @@ -57,15 +60,21 @@ function registerRenderManagerForDoc( univerInstanceService ); - injector.add([IRenderManagerService, { - useValue: { - getRenderById: (unitId: string) => unitId === doc.getUnitId() - ? ({ - with: () => skeletonManager, - } as never) - : null, - } as never, - }]); + injector.add([IRenderManagerService, { useClass: RenderManagerService }]); + injector.get(IRenderManagerService).addRender(doc.getUnitId(), { + unitId: doc.getUnitId(), + type: UniverInstanceType.UNIVER_DOC, + engine: new DisposableCollection() as never, + scene: new DisposableCollection() as never, + mainComponent: null, + components: new Map(), + isMainScene: true, + activated$: new BehaviorSubject(true), + with: () => skeletonManager as T, + activate: () => {}, + deactivate: () => {}, + isDisposed: () => false, + }); return skeletonManager; } @@ -255,6 +264,64 @@ describe('docs integration', () => { skeletonManager.dispose(); }); + it('emits composed doc state changes from the docs package without docs-ui', async () => { + univer.registerPlugin(UniverDocsPlugin); + const doc = univer.createUnit( + UniverInstanceType.UNIVER_DOC, + createTestDocData('doc-state-change') + ); + + const univerInstanceService = injector.get(IUniverInstanceService); + univerInstanceService.focusUnit(doc.getUnitId()); + const skeletonManager = registerRenderManagerForDoc(injector, doc); + + const selectionManager = injector.get(DocSelectionManagerService); + selectionManager.__TEST_ONLY_setCurrentSelection({ + unitId: doc.getUnitId(), + subUnitId: doc.getUnitId(), + }); + selectionManager.__TEST_ONLY_add([{ + startOffset: 5, + endOffset: 5, + collapsed: true, + isActive: true, + segmentId: '', + style: null as never, + }]); + + const docStateChangeManagerService = injector.get(DocStateChangeManagerService); + const stateChanges: unknown[] = []; + const sub = docStateChangeManagerService.docStateChange$.subscribe((value) => value && stateChanges.push(value)); + + const mutation = replaceSelectionFactory(injector as never, { + unitId: doc.getUnitId(), + body: { + dataStream: ' from docs', + }, + }); + + expect(mutation).not.toBe(false); + if (mutation === false) { + throw new Error('Expected replaceSelectionFactory to create a mutation.'); + } + + const commandService = injector.get(ICommandService); + const ok = await commandService.executeCommand(mutation.id, { + ...mutation.params, + debounce: false, + trigger: 'doc-state-change-spec' as never, + }); + + await awaitTime(0); + + expect(ok).toBeTruthy(); + expect(stateChanges).toHaveLength(1); + expect(doc.getBody()?.dataStream).toBe('Hello from docs\r\n'); + + sub.unsubscribe(); + skeletonManager.dispose(); + }); + it('adds and removes custom ranges through the same mutation pipeline', async () => { univer.registerPlugin(UniverDocsPlugin); const doc = univer.createUnit( diff --git a/packages/docs/src/__tests__/facade-utils.spec.ts b/packages/docs/src/__tests__/facade-utils.spec.ts new file mode 100644 index 0000000000..a0efa3efcd --- /dev/null +++ b/packages/docs/src/__tests__/facade-utils.spec.ts @@ -0,0 +1,67 @@ +/** + * Copyright 2023-present DreamNum Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, expect, it } from 'vitest'; +import { + buildPlainTextInsertBody, + getNormalizedPlainTextCursorOffset, + getRemovedLeadingParagraphBreakLength, +} from '../facade/utils'; + +describe('facade utils', () => { + it('removes a leading paragraph break when plain text is inserted at the document start', () => { + const body = buildPlainTextInsertBody('\r\nHello', { removeLeadingParagraphBreak: true }); + + expect(body).toEqual({ + dataStream: 'Hello', + customDecorations: [], + customRanges: [], + textRuns: [], + }); + }); + + it('keeps a leading paragraph break when the caller does not remove it', () => { + const body = buildPlainTextInsertBody('\r\nHello'); + + expect(body.dataStream).toBe('\rHello'); + expect(body.paragraphs).toEqual([{ startIndex: 0 }]); + }); + + it('normalizes plain text line feeds and adds paragraph metadata', () => { + const body = buildPlainTextInsertBody('Hello\nWorld\rAgain'); + + expect(body.dataStream).toBe('Hello\rWorld\rAgain'); + expect(body.paragraphs).toEqual([{ startIndex: 5 }, { startIndex: 11 }]); + }); + + it('adds paragraph metadata for a trailing paragraph break', () => { + const body = buildPlainTextInsertBody('Hello\n'); + + expect(body.dataStream).toBe('Hello\r'); + expect(body.paragraphs).toEqual([{ startIndex: 5 }]); + }); + + it('reports removed leading paragraph break length for cursor offsets', () => { + expect(getRemovedLeadingParagraphBreakLength('\nHello', true)).toBe(1); + expect(getRemovedLeadingParagraphBreakLength('\r\nHello', true)).toBe(1); + expect(getRemovedLeadingParagraphBreakLength('\r\nHello')).toBe(0); + }); + + it('normalizes cursor offsets with collapsed paragraph breaks', () => { + expect(getNormalizedPlainTextCursorOffset('Hello\r\n', 'Hello\r\n'.length)).toBe('Hello\r'.length); + expect(getNormalizedPlainTextCursorOffset('\r\nHello', '\r\nHello'.length, true)).toBe('Hello'.length); + }); +}); diff --git a/packages/docs/src/commands/mutations/core-editing.mutation.ts b/packages/docs/src/commands/mutations/core-editing.mutation.ts index 9f1dce64e0..17ad4fcb03 100644 --- a/packages/docs/src/commands/mutations/core-editing.mutation.ts +++ b/packages/docs/src/commands/mutations/core-editing.mutation.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import type { IMutation, IMutationCommonParams, JSONXActions, Nullable } from '@univerjs/core'; +import type { DocumentDataModel, IExecutionOptions, IMutation, IMutationCommonParams, JSONXActions, Nullable } from '@univerjs/core'; import type { ITextRangeWithStyle } from '@univerjs/engine-render'; import type { IDocStateChangeInfo } from '../../services/doc-state-emit.service'; -import { CommandType, IUniverInstanceService, JSONX } from '@univerjs/core'; +import { CommandType, IUniverInstanceService, JSONX, UniverInstanceType } from '@univerjs/core'; import { IRenderManagerService } from '@univerjs/engine-render'; import { DocSelectionManagerService } from '../../services/doc-selection-manager.service'; import { DocSkeletonManagerService } from '../../services/doc-skeleton-manager.service'; @@ -53,7 +53,7 @@ export const RichTextEditingMutation: IMutation { + handler: (accessor, params, options?: IExecutionOptions) => { const { unitId, segmentId = '', @@ -66,17 +66,18 @@ export const RichTextEditingMutation: IMutation(unitId, UniverInstanceType.UNIVER_DOC); + const documentViewModel = renderManagerService.getRenderUnitById(unitId)?.with(DocSkeletonManagerService).getViewModel(); + if (documentDataModel == null) { + throw new Error(`DocumentDataModel not found for unitId: ${unitId}`); } const docSelectionManagerService = accessor.get(DocSelectionManagerService); @@ -100,7 +101,7 @@ export const RichTextEditingMutation: IMutation(InsertTextCommand.id, { unitId, - body: { - dataStream: text, - }, + body: insertBody, range: activeRange, segmentId, - ...(options.cursorOffset == null ? {} : { cursorOffset: options.cursorOffset }), + ...(cursorOffset == null ? {} : { cursorOffset }), }); } diff --git a/packages/docs/src/facade/utils.ts b/packages/docs/src/facade/utils.ts new file mode 100644 index 0000000000..6c9a5adc61 --- /dev/null +++ b/packages/docs/src/facade/utils.ts @@ -0,0 +1,96 @@ +/** + * Copyright 2023-present DreamNum Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { IDocumentBody } from '@univerjs/core'; + +type IParagraphStyle = NonNullable[number]['paragraphStyle']; + +export interface IBuildPlainTextInsertBodyOptions { + paragraphStyle?: IParagraphStyle; + removeLeadingParagraphBreak?: boolean; +} + +function cloneParagraphStyle(paragraphStyle: IParagraphStyle): IParagraphStyle { + return paragraphStyle == null ? paragraphStyle : JSON.parse(JSON.stringify(paragraphStyle)); +} + +function normalizePlainTextDataStream(dataStream: string): string { + return dataStream.replace(/\r\n/g, '\r').replace(/\n/g, '\r'); +} + +export function getRemovedLeadingParagraphBreakLength( + dataStream: string, + removeLeadingParagraphBreak?: boolean +): number { + const normalized = normalizePlainTextDataStream(dataStream); + + if (removeLeadingParagraphBreak && normalized.length > 1 && normalized.startsWith('\r')) { + return 1; + } + + return 0; +} + +export function getNormalizedPlainTextCursorOffset( + dataStream: string, + cursorOffset: number, + removeLeadingParagraphBreak?: boolean +): number { + const normalizedPrefixLength = normalizePlainTextDataStream(dataStream.slice(0, cursorOffset)).length; + + return Math.max( + 0, + normalizedPrefixLength - getRemovedLeadingParagraphBreakLength(dataStream, removeLeadingParagraphBreak) + ); +} + +export function getParagraphStyleAtOffset(body: IDocumentBody, offset: number): IParagraphStyle { + const paragraphs = body.paragraphs ?? []; + const paragraph = paragraphs.find((item) => item.startIndex >= offset) ?? paragraphs[paragraphs.length - 1]; + + return paragraph?.paragraphStyle; +} + +export function buildPlainTextInsertBody( + dataStream: string, + options: IBuildPlainTextInsertBodyOptions = {} +): IDocumentBody { + const normalizedDataStream = normalizePlainTextDataStream(dataStream).slice( + getRemovedLeadingParagraphBreakLength(dataStream, options.removeLeadingParagraphBreak) + ); + const body: IDocumentBody = { + dataStream: normalizedDataStream, + customDecorations: [], + customRanges: [], + textRuns: [], + }; + + const paragraphs = []; + for (let index = 0; index < normalizedDataStream.length; index++) { + if (normalizedDataStream[index] === '\r') { + paragraphs.push({ + startIndex: index, + ...(options.paragraphStyle == null ? {} : { paragraphStyle: cloneParagraphStyle(options.paragraphStyle) }), + }); + } + } + + if (paragraphs.length > 0) { + body.paragraphs = paragraphs; + } + + return body; +} diff --git a/packages/docs/src/index.ts b/packages/docs/src/index.ts index 5221d58eeb..1e691ca387 100644 --- a/packages/docs/src/index.ts +++ b/packages/docs/src/index.ts @@ -31,6 +31,7 @@ export { DocInterceptorService } from './services/doc-interceptor/doc-intercepto export { DOC_INTERCEPTOR_POINT } from './services/doc-interceptor/interceptor-const'; export { DocSelectionManagerService } from './services/doc-selection-manager.service'; export { DocSkeletonManagerService } from './services/doc-skeleton-manager.service'; +export { DocStateChangeManagerService, IDocStateChangeInterceptorService } from './services/doc-state-change-manager.service'; export type { IDocStateChangeInfo, IDocStateChangeParams } from './services/doc-state-emit.service'; export { DocStateEmitService } from './services/doc-state-emit.service'; export { addCustomRangeBySelectionFactory, addCustomRangeFactory, deleteCustomRangeFactory } from './utils/custom-range-factory'; diff --git a/packages/docs/src/plugin.ts b/packages/docs/src/plugin.ts index 9a63e49976..9f7caeb088 100644 --- a/packages/docs/src/plugin.ts +++ b/packages/docs/src/plugin.ts @@ -33,6 +33,7 @@ import { defaultPluginConfig, DOCS_PLUGIN_CONFIG_KEY } from './config/config'; import { DocCustomRangeController } from './controllers/custom-range.controller'; import { DocContentInsertService } from './services/doc-content-insert.service'; import { DocSelectionManagerService } from './services/doc-selection-manager.service'; +import { DocStateChangeManagerService } from './services/doc-state-change-manager.service'; import { DocStateEmitService } from './services/doc-state-emit.service'; export class UniverDocsPlugin extends Plugin { @@ -82,6 +83,7 @@ export class UniverDocsPlugin extends Plugin { [ [DocSelectionManagerService], [DocStateEmitService], + [DocStateChangeManagerService], [DocContentInsertService], [DocCustomRangeController], ] as Dependency[] @@ -89,6 +91,7 @@ export class UniverDocsPlugin extends Plugin { } override onReady(): void { + this._injector.get(DocStateChangeManagerService); this._injector.get(DocCustomRangeController); } } diff --git a/packages/docs-ui/src/services/doc-state-change-manager.service.ts b/packages/docs/src/services/doc-state-change-manager.service.ts similarity index 81% rename from packages/docs-ui/src/services/doc-state-change-manager.service.ts rename to packages/docs/src/services/doc-state-change-manager.service.ts index 119a7a1941..6ef45b9303 100644 --- a/packages/docs-ui/src/services/doc-state-change-manager.service.ts +++ b/packages/docs/src/services/doc-state-change-manager.service.ts @@ -15,12 +15,11 @@ */ import type { JSONXActions, Nullable } from '@univerjs/core'; -import type { IDocStateChangeParams, IRichTextEditingMutationParams } from '@univerjs/docs'; -import { ICommandService, Inject, IUndoRedoService, IUniverInstanceService, JSONX, RedoCommandId, RxDisposable, UndoCommandId } from '@univerjs/core'; -import { DocStateEmitService } from '@univerjs/docs'; -import { IRenderManagerService } from '@univerjs/engine-render'; +import type { IRichTextEditingMutationParams } from '../commands/mutations/core-editing.mutation'; +import type { IDocStateChangeInfo, IDocStateChangeParams } from './doc-state-emit.service'; +import { createIdentifier, ICommandService, Inject, IUndoRedoService, IUniverInstanceService, JSONX, Optional, RedoCommandId, RxDisposable, UndoCommandId } from '@univerjs/core'; import { BehaviorSubject, takeUntil } from 'rxjs'; -import { DocIMEInputManagerService } from './doc-ime-input-manager.service'; +import { DocStateEmitService } from './doc-state-emit.service'; type ChangeStateCacheType = 'history' | 'collaboration'; @@ -31,9 +30,14 @@ interface IStateCache { collaboration: IDocStateChangeParams[]; } -// This class sends out state-changing events, what is the state, the data model, -// and the cursor & selection, and this class mainly serves the History(undo/redo) module and -// the collaboration module. +export interface IDocStateChangeInterceptorService { + transformChangeStateInfo(changeStateInfo: IDocStateChangeInfo): Nullable; +} + +export const IDocStateChangeInterceptorService = createIdentifier('doc.state-change-interceptor-service'); + +// This class sends out state-changing events, what the state is, the data model, +// and cursor & selection information. It mainly serves history and collaboration. export class DocStateChangeManagerService extends RxDisposable { private readonly _docStateChange$ = new BehaviorSubject>(null); readonly docStateChange$ = this._docStateChange$.asObservable(); @@ -45,11 +49,11 @@ export class DocStateChangeManagerService extends RxDisposable { private _changeStateCacheTimer: Nullable> = null; constructor( - @Inject(IUndoRedoService) private _undoRedoService: IUndoRedoService, + @Optional(IUndoRedoService) private _undoRedoService: Nullable, @ICommandService private readonly _commandService: ICommandService, @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, @Inject(DocStateEmitService) private readonly _docStateEmitService: DocStateEmitService, - @IRenderManagerService private readonly _renderManagerService: IRenderManagerService + @Optional(IDocStateChangeInterceptorService) private readonly _docStateChangeInterceptorService?: IDocStateChangeInterceptorService ) { super(); @@ -99,27 +103,17 @@ export class DocStateChangeManagerService extends RxDisposable { return; } - const { isCompositionEnd, isSync, syncer, ...changeState } = changeStateInfo; - const imeInputManagerService = this._renderManagerService.getRenderById(isSync ? syncer! : changeStateInfo.unitId)?.with(DocIMEInputManagerService); - - if (imeInputManagerService == null) { + const interceptedChangeStateInfo = this._docStateChangeInterceptorService?.transformChangeStateInfo(changeStateInfo) ?? changeStateInfo; + if (interceptedChangeStateInfo == null) { return; } - // Handle IME input. - if (isCompositionEnd) { - const historyParams = imeInputManagerService.fetchComposedUndoRedoMutationParams(); - - if (historyParams == null) { - throw new Error('historyParams is null in RichTextEditingMutation'); - } - - const { undoMutationParams, redoMutationParams, previousActiveRange } = historyParams; - changeState.redoState.actions = redoMutationParams.actions; - changeState.undoState.actions = undoMutationParams.actions; - changeState.undoState.textRanges = [previousActiveRange]; + if (interceptedChangeStateInfo.isSync) { + return; } + const { isCompositionEnd: _isCompositionEnd, isSync: _isSync, syncer: _syncer, ...changeState } = interceptedChangeStateInfo; + this._setChangeState(changeState); }); } @@ -127,7 +121,7 @@ export class DocStateChangeManagerService extends RxDisposable { private _cacheChangeState(changeState: IDocStateChangeParams, type: ChangeStateCacheType = 'history') { const { trigger, unitId, noHistory, debounce = false } = changeState; - if (noHistory || trigger == null) { + if (noHistory || (type === 'history' && trigger == null)) { return; } @@ -178,7 +172,7 @@ export class DocStateChangeManagerService extends RxDisposable { const undoRedoService = this._undoRedoService; const cacheStates = this._historyStateCache.get(unitId); - if (!Array.isArray(cacheStates) || cacheStates.length === 0) { + if (undoRedoService == null || !Array.isArray(cacheStates) || cacheStates.length === 0) { return; } diff --git a/packages/sheets-ui/src/commands/commands/__tests__/create-selection-command-test-bed.ts b/packages/sheets-ui/src/commands/commands/__tests__/create-selection-command-test-bed.ts index 6785979ffb..cae885ef97 100644 --- a/packages/sheets-ui/src/commands/commands/__tests__/create-selection-command-test-bed.ts +++ b/packages/sheets-ui/src/commands/commands/__tests__/create-selection-command-test-bed.ts @@ -235,15 +235,3 @@ export function createFrozenCommandTestBed(workbookData?: IWorkbookData) { sheet, }; } - -// export class MockRenderManagerService implements Pick { -// constructor( -// @Inject(Injector) private readonly _injector: Injector -// ) { } - -// getRenderById(_unitId: string): Nullable { -// return { -// with: (identifier: DependencyIdentifier) => this._injector.get(identifier), -// } as unknown as IRender; -// } -// } diff --git a/packages/sheets-ui/src/commands/commands/__tests__/set-format-painter.command.spec.ts b/packages/sheets-ui/src/commands/commands/__tests__/set-format-painter.command.spec.ts index 58fe61b8d3..8e11621510 100644 --- a/packages/sheets-ui/src/commands/commands/__tests__/set-format-painter.command.spec.ts +++ b/packages/sheets-ui/src/commands/commands/__tests__/set-format-painter.command.spec.ts @@ -27,7 +27,7 @@ import { UndoCommand, UniverInstanceType, } from '@univerjs/core'; -import { IRenderManagerService } from '@univerjs/engine-render'; +import { IRenderManagerService, RenderManagerService } from '@univerjs/engine-render'; import { AddWorksheetMergeMutation, RemoveWorksheetMergeMutation, @@ -208,22 +208,13 @@ describe('Test format painter rules in controller', () => { [IMarkSelectionService, { useClass: MarkSelectionService }], [IFormatPainterService, { useClass: FormatPainterService }], [ISheetSelectionRenderService, { useClass: SheetSelectionRenderService }], + [IRenderManagerService, { useClass: RenderManagerService }], [FormatPainterController], ]); univer = testBed.univer; get = testBed.get; - class MockRenderManagerService { - getRenderById() { - return null; - } - } - - const injector = univer.__getInjector(); - // @ts-ignore - injector.add([IRenderManagerService, { useClass: MockRenderManagerService }]); - commandService = get(ICommandService); themeService = get(ThemeService); const theme = themeService.getCurrentTheme(); diff --git a/packages/sheets-ui/src/controllers/render-controllers/__tests__/render-test-bed.ts b/packages/sheets-ui/src/controllers/render-controllers/__tests__/render-test-bed.ts index 066856c09b..17b06961e6 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/__tests__/render-test-bed.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/__tests__/render-test-bed.ts @@ -18,7 +18,7 @@ import type { Dependency, IDisposable, Injector, IWorkbookData, Workbook } from import type { IRenderContext, Vector2 } from '@univerjs/engine-render'; import type { Observable } from 'rxjs'; import { ICommandService, IContextService, ILogService, Inject, IUniverInstanceService, LocaleService, LocaleType, LogLevel, Plugin, Tools, Univer, Injector as UniverInjector, UniverInstanceType } from '@univerjs/core'; -import { IRenderManagerService, SHEET_VIEWPORT_KEY, Viewport } from '@univerjs/engine-render'; +import { IRenderManagerService, RenderManagerService, SHEET_VIEWPORT_KEY, Viewport } from '@univerjs/engine-render'; import { SheetInterceptorService, SheetsSelectionsService } from '@univerjs/sheets'; import { BehaviorSubject, Subject } from 'rxjs'; import { SHEET_VIEW_KEY } from '../../../common/keys'; @@ -500,11 +500,13 @@ export function createRenderTestBed(options?: { workbookData?: IWorkbookData; de viewportMap.set(SHEET_VIEWPORT_KEY.VIEW_LEFT_TOP, viewportFactory(SHEET_VIEWPORT_KEY.VIEW_LEFT_TOP)); const scene = createFakeScene(viewportMap, { parentClassType: options?.parentClassType, engine }); + (scene as any).dispose ??= () => { }; + (engine as any).dispose ??= () => { }; const components = new Map(); - components.set(SHEET_VIEW_KEY.ROW, { onPointerDown$: createTestEvent(), onPointerMove$: createTestEvent(), onPointerLeave$: createTestEvent() }); - components.set(SHEET_VIEW_KEY.COLUMN, { onPointerDown$: createTestEvent(), onPointerMove$: createTestEvent(), onPointerLeave$: createTestEvent() }); - components.set(SHEET_VIEW_KEY.LEFT_TOP, { onPointerDown$: createTestEvent() }); + components.set(SHEET_VIEW_KEY.ROW, { onPointerDown$: createTestEvent(), onPointerMove$: createTestEvent(), onPointerLeave$: createTestEvent(), dispose: () => { } }); + components.set(SHEET_VIEW_KEY.COLUMN, { onPointerDown$: createTestEvent(), onPointerMove$: createTestEvent(), onPointerLeave$: createTestEvent(), dispose: () => { } }); + components.set(SHEET_VIEW_KEY.LEFT_TOP, { onPointerDown$: createTestEvent(), dispose: () => { } }); const mainComponent = { zIndex: 1, @@ -557,20 +559,22 @@ export function createRenderTestBed(options?: { workbookData?: IWorkbookData; de injector.add([SheetSkeletonManagerService, { useValue: sheetSkeletonManagerService as any }]); - const renderManagerService: IRenderManagerService = { - getRenderById: (unitId: string) => { - if (unitId !== sheet.getUnitId()) return null as any; - return { - unitId, - engine, - scene, - mainComponent, - components, - } as any; - }, - } as IRenderManagerService; - - injector.add([IRenderManagerService, { useValue: renderManagerService as any }]); + injector.add([IRenderManagerService, { useClass: RenderManagerService }]); + const renderManagerService = injector.get(IRenderManagerService); + renderManagerService.addRender(sheet.getUnitId(), { + unitId: sheet.getUnitId(), + type: UniverInstanceType.UNIVER_SHEET, + engine: engine as any, + scene: scene as any, + mainComponent: mainComponent as any, + components, + isMainScene: true, + activated$: new BehaviorSubject(true), + with: injector.get.bind(injector), + activate: () => { }, + deactivate: () => { }, + isDisposed: () => false, + }); return { univer,