Skip to content

Commit

Permalink
feat(sheet): group sheet get cell intercptor to performance well (#3534)
Browse files Browse the repository at this point in the history
  • Loading branch information
VicKun4937 authored Sep 26, 2024
1 parent b0afd99 commit ab97ea1
Show file tree
Hide file tree
Showing 21 changed files with 212 additions and 91 deletions.
33 changes: 24 additions & 9 deletions packages/core/src/common/interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,28 @@
* limitations under the License.
*/

import type { Nullable } from '../shared/types';
import { remove } from './array';
import type { Nullable } from '../shared/types';

export type InterceptorHandler<M = unknown, C = unknown> = (
value: Nullable<M>,
context: C,
next: (value: Nullable<M>) => Nullable<M>
) => Nullable<M>;

export enum InterceptorEffectEnum {
Style = 1, // 1<< 0
Value = 2, // 1<< 1
}
export interface IInterceptor<M, C> {
priority?: number;
handler: InterceptorHandler<M, C>;
}

export interface ICellInterceptor<M, C> extends IInterceptor<M, C> {
effect?: InterceptorEffectEnum;
}

export function createInterceptorKey<T, C>(key: string): IInterceptor<T, C> {
const symbol = `sheet_interceptor_${key}`;
return symbol as unknown as IInterceptor<T, C>; // FIXME: priority and handler is completely missing?
Expand All @@ -42,28 +50,35 @@ export type IComposeInterceptors<T = any, C = any> = (
*/
export const composeInterceptors = <T, C>(interceptors: Array<IInterceptor<T, C>>) =>

function (initialValue: T, context: C) {
function (initialValue: Nullable<T>, context: C) {
let index = -1;
let value: Nullable<T> = initialValue;

function passThrough(
i: number,
v: Parameters<IInterceptor<T, C>['handler']>[0]
): Parameters<IInterceptor<T, C>['handler']>[0] {
for (let i = 0; i <= interceptors.length; i++) {
if (i <= index) {
throw new Error('[SheetInterceptorService]: next() called multiple times!');
}

index = i;

if (i === interceptors.length) {
return v;
return value;
}

const interceptor = interceptors[i];
let nextCalled = false;

return interceptor.handler!(v, context, passThrough.bind(null, i + 1));
value = interceptor.handler!(value, context, (nextValue) => {
nextCalled = true;
return nextValue;
});

if (!nextCalled) {
break;
}
}

return passThrough(0, initialValue);
return value;
} as ReturnType<IComposeInterceptors<T, C>>;

export class InterceptorManager<P extends Record<string, IInterceptor<any, any>>> {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ export * from './common/di';
export { shallowEqual } from './common/equal';
export { CustomCommandExecutionError } from './common/error';
export { throttle } from './common/function';
export type { IComposeInterceptors, IInterceptor, InterceptorHandler } from './common/interceptor';
export { composeInterceptors, createInterceptorKey, InterceptorManager } from './common/interceptor';
export type { ICellInterceptor, IComposeInterceptors, IInterceptor, InterceptorHandler } from './common/interceptor';
export { composeInterceptors, createInterceptorKey, InterceptorEffectEnum, InterceptorManager } from './common/interceptor';
export type { Serializable } from './common/json';
export { MemoryCursor } from './common/memory-cursor';
export { mixinClass } from './common/mixin';
Expand Down
23 changes: 20 additions & 3 deletions packages/core/src/sheets/view-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
* limitations under the License.
*/

import { Disposable, toDisposable } from '../shared/lifecycle';
import { InterceptorEffectEnum } from '../common/interceptor';

import { Disposable, toDisposable } from '../shared/lifecycle';
import type { IDisposable } from '../common/di';
import type { Nullable } from '../shared/types';
import type { ICellData, ICellDataForSheetInterceptor } from './typedef';
Expand All @@ -24,7 +25,7 @@ import type { ICellData, ICellDataForSheetInterceptor } from './typedef';
* @internal
*/
export interface ICellContentInterceptor {
getCell: (row: number, col: number) => Nullable<ICellDataForSheetInterceptor>;
getCell: (row: number, col: number, effect: InterceptorEffectEnum) => Nullable<ICellDataForSheetInterceptor>;
}

export interface IRowFilteredInterceptor {}
Expand Down Expand Up @@ -71,7 +72,23 @@ export class SheetViewModel extends Disposable {

getCell(row: number, col: number): Nullable<ICellDataForSheetInterceptor> {
if (this._cellContentInterceptor) {
return this._cellContentInterceptor.getCell(row, col);
return this._cellContentInterceptor.getCell(row, col, InterceptorEffectEnum.Value | InterceptorEffectEnum.Style);
}

return this.getRawCell(row, col);
}

getCellValueOnly(row: number, col: number): Nullable<ICellDataForSheetInterceptor> {
if (this._cellContentInterceptor) {
return this._cellContentInterceptor.getCell(row, col, InterceptorEffectEnum.Value);
}

return this.getRawCell(row, col);
}

getCellStyleOnly(row: number, col: number): Nullable<ICellDataForSheetInterceptor> {
if (this._cellContentInterceptor) {
return this._cellContentInterceptor.getCell(row, col, InterceptorEffectEnum.Style);
}

return this.getRawCell(row, col);
Expand Down
28 changes: 28 additions & 0 deletions packages/core/src/sheets/worksheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,34 @@ export class Worksheet {
return this._viewModel.getCell(row, col);
}

/**
* Get cellData only use effect on value interceptor
* @param {number} number row The row index of the cell.
* @param {number} number col The column index of the cell.
* @returns {Nullable<ICellDataForSheetInterceptor>} The cell data only use effect on value interceptor
*/
getCellValueOnly(row: number, col: number): Nullable<ICellDataForSheetInterceptor> {
if (row < 0 || col < 0) {
return null;
}

return this._viewModel.getCellValueOnly(row, col);
}

/**
* Get cellData only use effect on style interceptor
* @param {number} row The row index of the cell.
* @param {number} col The column index of the cell.
* @returns {Nullable<ICellDataForSheetInterceptor>} The cell data only use effect on style interceptor
*/
getCellStyleOnly(row: number, col: number): Nullable<ICellDataForSheetInterceptor> {
if (row < 0 || col < 0) {
return null;
}

return this._viewModel.getCellStyleOnly(row, col);
}

getCellRaw(row: number, col: number): Nullable<ICellData> {
return this.getCellMatrix().getValue(row, col);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
* limitations under the License.
*/

import type { ICellDataForSheetInterceptor, Workbook } from '@univerjs/core';
import { Disposable, Inject, IUniverInstanceService, LifecycleStages, OnLifecycle, Range, UniverInstanceType } from '@univerjs/core';
import { Disposable, Inject, InterceptorEffectEnum, IUniverInstanceService, LifecycleStages, OnLifecycle, Range, UniverInstanceType } from '@univerjs/core';
import { IRenderManagerService } from '@univerjs/engine-render';
import { INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets';
import { ConditionalFormattingRuleModel, ConditionalFormattingService, ConditionalFormattingViewModel, DEFAULT_PADDING, DEFAULT_WIDTH } from '@univerjs/sheets-conditional-formatting';
import { SheetSkeletonManagerService } from '@univerjs/sheets-ui';
import { bufferTime, filter } from 'rxjs/operators';
import { IRenderManagerService } from '@univerjs/engine-render';
import { ConditionalFormattingRuleModel, ConditionalFormattingService, ConditionalFormattingViewModel, DEFAULT_PADDING, DEFAULT_WIDTH } from '@univerjs/sheets-conditional-formatting';
import type { ICellDataForSheetInterceptor, Workbook } from '@univerjs/core';
import type { IConditionalFormattingCellData, IConditionFormattingRule } from '@univerjs/sheets-conditional-formatting';

@OnLifecycle(LifecycleStages.Starting, SheetsCfRenderController)
Expand Down Expand Up @@ -228,6 +228,7 @@ export class SheetsCfRenderController extends Disposable {

private _initViewModelInterceptor() {
this.disposeWithMe(this._sheetInterceptorService.intercept(INTERCEPTOR_POINT.CELL_CONTENT, {
effect: InterceptorEffectEnum.Style,
handler: (cell, context, next) => {
const result = this._conditionalFormattingService.composeStyle(context.unitId, context.subUnitId, context.row, context.col);
if (!result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { DataValidationRenderMode, DataValidationStatus, DataValidationType, ICommandService, Inject, IUniverInstanceService, LifecycleStages, OnLifecycle, Optional, RxDisposable, sequenceExecute, UniverInstanceType, WrapStrategy } from '@univerjs/core';
import { DataValidationRenderMode, DataValidationStatus, DataValidationType, ICommandService, Inject, InterceptorEffectEnum, IUniverInstanceService, LifecycleStages, OnLifecycle, Optional, RxDisposable, sequenceExecute, UniverInstanceType, WrapStrategy } from '@univerjs/core';
import { DataValidatorRegistryService } from '@univerjs/data-validation';
import { IRenderManagerService } from '@univerjs/engine-render';
import { InterceptCellContentPriority, INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets';
Expand Down Expand Up @@ -162,6 +162,7 @@ export class SheetsDataValidationRenderController extends RxDisposable {
this._sheetInterceptorService.intercept(
INTERCEPTOR_POINT.CELL_CONTENT,
{
effect: InterceptorEffectEnum.Style,
// must be after numfmt
priority: InterceptCellContentPriority.DATA_VALIDATION,
// eslint-disable-next-line complexity
Expand Down Expand Up @@ -322,6 +323,7 @@ export class SheetsDataValidationMobileRenderController extends RxDisposable {
this._sheetInterceptorService.intercept(
INTERCEPTOR_POINT.CELL_CONTENT,
{
effect: InterceptorEffectEnum.Style,
priority: InterceptCellContentPriority.DATA_VALIDATION,
// eslint-disable-next-line max-lines-per-function, complexity
handler: (cell, pos, next) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@
* limitations under the License.
*/

import type { IDisposable, IRange, Workbook } from '@univerjs/core';
import { CommandType, fromCallback, ICommandService, Inject, Injector, RxDisposable, ThemeService } from '@univerjs/core';
import type { IRenderContext, IRenderModule, SpreadsheetSkeleton } from '@univerjs/engine-render';
import type { ISelectionStyle, ISheetCommandSharedParams } from '@univerjs/sheets';
import { CommandType, fromCallback, ICommandService, Inject, Injector, InterceptorEffectEnum, RxDisposable, ThemeService } from '@univerjs/core';
import { INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets';
import type { FilterModel } from '@univerjs/sheets-filter';
import { FILTER_MUTATIONS, SheetsFilterService } from '@univerjs/sheets-filter';
import { getCoordByCell, ISheetSelectionRenderService, SelectionShape, SheetSkeletonManagerService } from '@univerjs/sheets-ui';

import { filter, map, of, startWith, switchMap, takeUntil, throttleTime } from 'rxjs';
import type { ISheetsFilterButtonShapeProps } from '../widgets/filter-button.shape';
import type { IDisposable, IRange, Workbook } from '@univerjs/core';
import type { IRenderContext, IRenderModule, SpreadsheetSkeleton } from '@univerjs/engine-render';
import type { ISelectionStyle, ISheetCommandSharedParams } from '@univerjs/sheets';

import type { FilterModel } from '@univerjs/sheets-filter';
import { FILTER_ICON_PADDING, FILTER_ICON_SIZE, SheetsFilterButtonShape } from '../widgets/filter-button.shape';
import type { ISheetsFilterButtonShapeProps } from '../widgets/filter-button.shape';

const DEFAULT_Z_INDEX = 1000;

Expand Down Expand Up @@ -171,6 +171,7 @@ export class SheetsFilterRenderController extends RxDisposable implements IRende
private _interceptCellContent(workbookId: string, worksheetId: string, range: IRange): void {
const { startRow, startColumn, endColumn } = range;
this._buttonRenderDisposable = this._sheetInterceptorService.intercept(INTERCEPTOR_POINT.CELL_CONTENT, {
effect: InterceptorEffectEnum.Style,
handler: (cell, pos, next) => {
const { row, col, unitId, subUnitId } = pos;
if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
* limitations under the License.
*/

import type { ICommandInfo } from '@univerjs/core';
import { CellValueType, Disposable, ICommandService, Inject, LifecycleStages, OnLifecycle, ThemeService } from '@univerjs/core';
import type { ISetArrayFormulaDataMutationParams } from '@univerjs/engine-formula';
import { CellValueType, Disposable, ICommandService, Inject, InterceptorEffectEnum, LifecycleStages, OnLifecycle, ThemeService } from '@univerjs/core';
import { FormulaDataModel, SetArrayFormulaDataMutation, stripErrorMargin } from '@univerjs/engine-formula';
import { INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets';
import type { ICommandInfo } from '@univerjs/core';
import type { ISetArrayFormulaDataMutationParams } from '@univerjs/engine-formula';

@OnLifecycle(LifecycleStages.Ready, ArrayFormulaDisplayController)
export class ArrayFormulaDisplayController extends Disposable {
Expand Down Expand Up @@ -64,6 +64,7 @@ export class ArrayFormulaDisplayController extends Disposable {
this.disposeWithMe(
this._sheetInterceptorService.intercept(INTERCEPTOR_POINT.CELL_CONTENT, {
priority: 100,
effect: InterceptorEffectEnum.Value,
handler: (cell, location, next) => {
const { unitId, subUnitId, row, col } = location;
const arrayFormulaCellData = this._formulaDataModel.getArrayFormulaCellData();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { Inject, LifecycleStages, OnLifecycle, RxDisposable } from '@univerjs/core';
import { Inject, InterceptorEffectEnum, LifecycleStages, OnLifecycle, RxDisposable } from '@univerjs/core';
import { INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets';
import { extractFormulaError } from './utils/utils';

Expand All @@ -39,6 +39,7 @@ export class FormulaRenderManagerController extends RxDisposable {
this.disposeWithMe(this._sheetInterceptorService.intercept(
INTERCEPTOR_POINT.CELL_CONTENT,
{
effect: InterceptorEffectEnum.Style,
handler: (cell, pos, next) => {
const errorType = extractFormulaError(cell);
if (!errorType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
* limitations under the License.
*/

import type { Workbook } from '@univerjs/core';
import { Disposable, Inject, LifecycleStages, OnLifecycle, ThemeService } from '@univerjs/core';
import { INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets';
import { SheetSkeletonManagerService } from '@univerjs/sheets-ui';
import type { IRenderContext, IRenderModule, Spreadsheet } from '@univerjs/engine-render';
import { Disposable, Inject, InterceptorEffectEnum, LifecycleStages, OnLifecycle, ThemeService } from '@univerjs/core';
import { IRenderManagerService } from '@univerjs/engine-render';
import { INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets';
import { HyperLinkModel } from '@univerjs/sheets-hyper-link';
import { SheetSkeletonManagerService } from '@univerjs/sheets-ui';
import { debounceTime } from 'rxjs';
import type { Workbook } from '@univerjs/core';
import type { IRenderContext, IRenderModule, Spreadsheet } from '@univerjs/engine-render';

export class SheetsHyperLinkRenderController extends Disposable implements IRenderModule {
constructor(
Expand Down Expand Up @@ -78,6 +78,8 @@ export class SheetsHyperLinkRenderManagerController extends Disposable {
this._sheetInterceptorService.intercept(
INTERCEPTOR_POINT.CELL_CONTENT,
{
effect: InterceptorEffectEnum.Value,
priority: 100,
handler: (cell, pos, next) => {
const { row, col, unitId, subUnitId } = pos;
const link = this._hyperLinkModel.getHyperLinkByLocation(unitId, subUnitId, row, col);
Expand All @@ -92,7 +94,6 @@ export class SheetsHyperLinkRenderManagerController extends Disposable {

return next(cell);
},
priority: 100,
}
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
Disposable,
ICommandService,
Inject,
InterceptorEffectEnum,
IUniverInstanceService,
LifecycleStages,
LocaleService,
Expand All @@ -37,7 +38,7 @@ import type {
} from '@univerjs/core';

import type { ISetNumfmtMutationParams, ISetRangeValuesMutationParams } from '@univerjs/sheets';
import { getPatternPreview } from '../utils/pattern';
import { getPatternPreviewIgnoreGeneral } from '../utils/pattern';

@OnLifecycle(LifecycleStages.Rendered, SheetsNumfmtCellContentController)
export class SheetsNumfmtCellContentController extends Disposable {
Expand All @@ -57,10 +58,20 @@ export class SheetsNumfmtCellContentController extends Disposable {
private _initInterceptorCellContent() {
const renderCache = new ObjectMatrix<{ result: ICellData; parameters: string | number }>();
this.disposeWithMe(this._sheetInterceptorService.intercept(INTERCEPTOR_POINT.CELL_CONTENT, {
effect: InterceptorEffectEnum.Value | InterceptorEffectEnum.Style,
handler: (cell, location, next) => {
const unitId = location.unitId;
const sheetId = location.subUnitId;
let numfmtValue;
const originCellValue = cell;
if (!originCellValue) {
return next(cell);
}

// just handle number
if (originCellValue.t !== CellValueType.NUMBER || originCellValue.v == null || Number.isNaN(originCellValue.v)) {
return next(cell);
}

if (cell?.s) {
const style = location.workbook.getStyles().get(cell.s);
Expand All @@ -75,23 +86,14 @@ export class SheetsNumfmtCellContentController extends Disposable {
if (!numfmtValue) {
return next(cell);
}
const originCellValue = cell;
if (!originCellValue) {
return next(cell);
}

// just handle number
if (originCellValue.t !== CellValueType.NUMBER || originCellValue.v == null || Number.isNaN(originCellValue.v)) {
return next(cell);
}

let numfmtRes: string = '';
const cache = renderCache.getValue(location.row, location.col);
if (cache && cache.parameters === `${originCellValue.v}_${numfmtValue.pattern}`) {
return next({ ...cell, ...cache.result });
}

const info = getPatternPreview(numfmtValue.pattern, Number(originCellValue.v), this._localeService.getCurrentLocale());
const info = getPatternPreviewIgnoreGeneral(numfmtValue.pattern, Number(originCellValue.v), this._localeService.getCurrentLocale());
numfmtRes = info.result;
if (!numfmtRes) {
return next(cell);
Expand Down
Loading

0 comments on commit ab97ea1

Please sign in to comment.