mirror of
https://github.com/dream-num/univer.git
synced 2026-09-24 15:32:18 +08:00
refactor(docs): move doc state change manager into docs package (#7017)
This commit is contained in:
@@ -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<IRenderManagerService> }]);
|
||||
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<IRenderManagerService, 'getRenderById'> {
|
||||
constructor(@Inject(Injector) private readonly _injector: Injector) {}
|
||||
|
||||
getRenderById(): Nullable<IRender> {
|
||||
return {
|
||||
with: <T>(identifier: DependencyIdentifier<T>) => this._injector.get(identifier),
|
||||
isDisposed: () => false,
|
||||
} as unknown as IRender;
|
||||
}
|
||||
}
|
||||
|
||||
class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule {
|
||||
private _docViewModel!: DocumentViewModel;
|
||||
private readonly _currentSkeleton$ = new BehaviorSubject<Nullable<DocumentSkeleton>>(null);
|
||||
|
||||
@@ -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: <T>(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: <T>(token: DependencyIdentifier<T>) => {
|
||||
if (token === DocSelectionRenderService) {
|
||||
return {
|
||||
getSegment: () => '',
|
||||
} as T;
|
||||
}
|
||||
|
||||
return injector.get(token);
|
||||
},
|
||||
activate: () => {},
|
||||
deactivate: () => {},
|
||||
isDisposed: () => false,
|
||||
});
|
||||
|
||||
injector.add([DocDrawingService]);
|
||||
injector.add([IDocDrawingService, { useClass: DocDrawingService }]);
|
||||
|
||||
@@ -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<IRenderManagerService> }]);
|
||||
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<IRenderManagerService, 'getRenderById'> {
|
||||
constructor(@Inject(Injector) private readonly _injector: Injector) {}
|
||||
|
||||
getRenderById(): Nullable<IRender> {
|
||||
return {
|
||||
with: <T>(identifier: DependencyIdentifier<T>) => this._injector.get(identifier),
|
||||
isDisposed: () => false,
|
||||
} as unknown as IRender;
|
||||
}
|
||||
}
|
||||
|
||||
class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule {
|
||||
private _docViewModel!: DocumentViewModel;
|
||||
private readonly _currentSkeleton$ = new BehaviorSubject<Nullable<DocumentSkeleton>>(null);
|
||||
|
||||
@@ -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<IRenderManagerService> }]);
|
||||
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<IRenderManagerService, 'getRenderById'> {
|
||||
constructor(@Inject(Injector) private readonly _injector: Injector) {}
|
||||
|
||||
getRenderById(): Nullable<IRender> {
|
||||
return {
|
||||
with: <T>(identifier: DependencyIdentifier<T>) => this._injector.get(identifier),
|
||||
isDisposed: () => false,
|
||||
} as unknown as IRender;
|
||||
}
|
||||
}
|
||||
|
||||
class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule {
|
||||
private _docViewModel!: DocumentViewModel;
|
||||
private readonly _currentSkeleton$ = new BehaviorSubject<Nullable<DocumentSkeleton>>(null);
|
||||
|
||||
@@ -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<IRenderManagerService> }]);
|
||||
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<IRenderManagerService, 'getRenderById'> {
|
||||
constructor(
|
||||
@Inject(Injector) private readonly _injector: Injector
|
||||
) { }
|
||||
|
||||
getRenderById(_unitId: string): Nullable<IRender> {
|
||||
return {
|
||||
with: <T>(identifier: DependencyIdentifier<T>) => this._injector.get(identifier),
|
||||
isDisposed: () => false,
|
||||
} as unknown as IRender;
|
||||
}
|
||||
}
|
||||
|
||||
export class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule {
|
||||
private _docViewModel: DocumentViewModel;
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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],
|
||||
|
||||
|
||||
@@ -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<IDocStateChangeInfo> {
|
||||
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],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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: <T>() => 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<IDocumentData, DocumentDataModel>(
|
||||
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<IDocumentData, DocumentDataModel>(
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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<IRichTextEditingMutationParams,
|
||||
type: CommandType.MUTATION,
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
handler: (accessor, params) => {
|
||||
handler: (accessor, params, options?: IExecutionOptions) => {
|
||||
const {
|
||||
unitId,
|
||||
segmentId = '',
|
||||
@@ -66,17 +66,18 @@ export const RichTextEditingMutation: IMutation<IRichTextEditingMutationParams,
|
||||
noNeedSetTextRange,
|
||||
debounce,
|
||||
isEditing = true,
|
||||
isSync,
|
||||
isSync: paramsIsSync,
|
||||
syncer,
|
||||
} = params;
|
||||
const isSync = paramsIsSync || options?.fromCollab || options?.fromChangeset;
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
const renderManagerService = accessor.get(IRenderManagerService);
|
||||
const docStateEmitService = accessor.get(DocStateEmitService);
|
||||
|
||||
const documentDataModel = univerInstanceService.getUniverDocInstance(unitId);
|
||||
const documentViewModel = renderManagerService.getRenderById(unitId)?.with(DocSkeletonManagerService).getViewModel();
|
||||
if (documentDataModel == null || documentViewModel == null) {
|
||||
throw new Error(`DocumentDataModel or documentViewModel not found for unitId: ${unitId}`);
|
||||
const documentDataModel = univerInstanceService.getUnit<DocumentDataModel>(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<IRichTextEditingMutationParams,
|
||||
documentDataModel.apply(actions);
|
||||
|
||||
// Step 2: Update Doc View Model.
|
||||
documentViewModel.reset(documentDataModel);
|
||||
documentViewModel?.reset(documentDataModel);
|
||||
// Step 3: Update cursor & selection.
|
||||
// Make sure update cursor & selection after doc skeleton is calculated.
|
||||
if (!noNeedSetTextRange && textRanges && trigger != null && !isSync) {
|
||||
|
||||
@@ -27,6 +27,11 @@ import {
|
||||
} from '@univerjs/core';
|
||||
import { FBaseInitialable } from '@univerjs/core/facade';
|
||||
import { InsertTextCommand } from '@univerjs/docs';
|
||||
import {
|
||||
buildPlainTextInsertBody,
|
||||
getNormalizedPlainTextCursorOffset,
|
||||
getParagraphStyleAtOffset,
|
||||
} from './utils';
|
||||
|
||||
export interface IDocumentInsertTextFacadeOptions {
|
||||
startOffset?: number;
|
||||
@@ -235,15 +240,21 @@ export class FDocument extends FBaseInitialable {
|
||||
collapsed: startOffset === endOffset,
|
||||
segmentId,
|
||||
};
|
||||
const removeLeadingParagraphBreak = startOffset === 0;
|
||||
const insertBody = buildPlainTextInsertBody(text, {
|
||||
paragraphStyle: getParagraphStyleAtOffset(body, startOffset),
|
||||
removeLeadingParagraphBreak,
|
||||
});
|
||||
const cursorOffset = options.cursorOffset == null
|
||||
? undefined
|
||||
: getNormalizedPlainTextCursorOffset(text, options.cursorOffset, removeLeadingParagraphBreak);
|
||||
|
||||
return this._commandService.executeCommand<IInsertTextCommandParams>(InsertTextCommand.id, {
|
||||
unitId,
|
||||
body: {
|
||||
dataStream: text,
|
||||
},
|
||||
body: insertBody,
|
||||
range: activeRange,
|
||||
segmentId,
|
||||
...(options.cursorOffset == null ? {} : { cursorOffset: options.cursorOffset }),
|
||||
...(cursorOffset == null ? {} : { cursorOffset }),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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<IDocumentBody['paragraphs']>[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;
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+22
-28
@@ -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<IDocStateChangeInfo>;
|
||||
}
|
||||
|
||||
export const IDocStateChangeInterceptorService = createIdentifier<IDocStateChangeInterceptorService>('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<Nullable<IDocStateChangeParams>>(null);
|
||||
readonly docStateChange$ = this._docStateChange$.asObservable();
|
||||
@@ -45,11 +49,11 @@ export class DocStateChangeManagerService extends RxDisposable {
|
||||
private _changeStateCacheTimer: Nullable<ReturnType<typeof setTimeout>> = null;
|
||||
|
||||
constructor(
|
||||
@Inject(IUndoRedoService) private _undoRedoService: IUndoRedoService,
|
||||
@Optional(IUndoRedoService) private _undoRedoService: Nullable<IUndoRedoService>,
|
||||
@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;
|
||||
}
|
||||
|
||||
-12
@@ -235,15 +235,3 @@ export function createFrozenCommandTestBed(workbookData?: IWorkbookData) {
|
||||
sheet,
|
||||
};
|
||||
}
|
||||
|
||||
// export class MockRenderManagerService implements Pick<IRenderManagerService, 'getRenderById'> {
|
||||
// constructor(
|
||||
// @Inject(Injector) private readonly _injector: Injector
|
||||
// ) { }
|
||||
|
||||
// getRenderById(_unitId: string): Nullable<IRender> {
|
||||
// return {
|
||||
// with: <T>(identifier: DependencyIdentifier<T>) => this._injector.get(identifier),
|
||||
// } as unknown as IRender;
|
||||
// }
|
||||
// }
|
||||
|
||||
+2
-11
@@ -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();
|
||||
|
||||
+22
-18
@@ -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<any, any>();
|
||||
components.set(SHEET_VIEW_KEY.ROW, { onPointerDown$: createTestEvent<any>(), onPointerMove$: createTestEvent<any>(), onPointerLeave$: createTestEvent<any>() });
|
||||
components.set(SHEET_VIEW_KEY.COLUMN, { onPointerDown$: createTestEvent<any>(), onPointerMove$: createTestEvent<any>(), onPointerLeave$: createTestEvent<any>() });
|
||||
components.set(SHEET_VIEW_KEY.LEFT_TOP, { onPointerDown$: createTestEvent<any>() });
|
||||
components.set(SHEET_VIEW_KEY.ROW, { onPointerDown$: createTestEvent<any>(), onPointerMove$: createTestEvent<any>(), onPointerLeave$: createTestEvent<any>(), dispose: () => { } });
|
||||
components.set(SHEET_VIEW_KEY.COLUMN, { onPointerDown$: createTestEvent<any>(), onPointerMove$: createTestEvent<any>(), onPointerLeave$: createTestEvent<any>(), dispose: () => { } });
|
||||
components.set(SHEET_VIEW_KEY.LEFT_TOP, { onPointerDown$: createTestEvent<any>(), 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,
|
||||
|
||||
Reference in New Issue
Block a user