Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(doc): delete right by delete key #480

Merged
merged 3 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 38 additions & 7 deletions packages/base-docs/src/commands/commands/core-editing.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ import {
RichTextEditingMutation,
} from '../mutations/core-editing.mutation';

// TODO: @JOCS, do not use command as event bus.
export const DeleteLeftCommand: ICommand = {
id: 'doc.command.delete-left',
type: CommandType.COMMAND,
handler: async () => true,
};

export const DeleteRightCommand: ICommand = {
id: 'doc.command.delete-right',
type: CommandType.COMMAND,
handler: async () => true,
Jocs marked this conversation as resolved.
Show resolved Hide resolved
};

export const BreakLineCommand: ICommand = {
id: 'doc.command.break-line',
type: CommandType.COMMAND,
Expand Down Expand Up @@ -97,9 +104,15 @@ export const InsertCommand: ICommand<IInsertCommandParams> = {
},
};

export enum DeleteDirection {
LEFT,
RIGHT,
}

export interface IDeleteCommandParams {
unitId: string;
range: ITextRange;
direction: DeleteDirection;
segmentId?: string;
}

Expand All @@ -117,7 +130,8 @@ export const DeleteCommand: ICommand<IDeleteCommandParams> = {
const commandService = accessor.get(ICommandService);
const undoRedoService = accessor.get(IUndoRedoService);

const { range, segmentId, unitId } = params;
const { range, segmentId, unitId, direction } = params;
const { collapsed, startOffset } = range;
const doMutation: IMutationInfo<IRichTextEditingMutationParams> = {
id: RichTextEditingMutation.id,
params: {
Expand All @@ -126,7 +140,24 @@ export const DeleteCommand: ICommand<IDeleteCommandParams> = {
},
};

doMutation.params!.mutations.push(...getRetainAndDeleteFromReplace(range, segmentId));
if (collapsed) {
if (startOffset > 0) {
doMutation.params!.mutations.push({
t: 'r',
len: direction === DeleteDirection.LEFT ? startOffset - 1 : startOffset,
segmentId,
});
}

doMutation.params!.mutations.push({
t: 'd',
len: 1,
line: 0,
segmentId,
});
} else {
doMutation.params!.mutations.push(...getRetainAndDeleteFromReplace(range, segmentId));
}

const result = commandService.syncExecuteCommand<
IRichTextEditingMutationParams,
Expand Down Expand Up @@ -155,7 +186,7 @@ export interface IUpdateCommandParams {
}

/**
* The command to update text properties.
* The command to update text properties, mainly used in BACKSPACE.
*/
export const UpdateCommand: ICommand<IUpdateCommandParams> = {
id: 'doc.command.update-text',
Expand Down Expand Up @@ -308,11 +339,11 @@ export function getRetainAndDeleteFromReplace(
segmentId: string = '',
memoryCursor: number = 0
): Array<IRetainMutationParams | IDeleteMutationParams> {
const { startOffset, endOffset, collapsed } = range;
const { startOffset, endOffset } = range;
const dos: Array<IRetainMutationParams | IDeleteMutationParams> = [];

const textStart = startOffset + (collapsed ? -1 : 0) - memoryCursor;
const textEnd = endOffset - 1 - memoryCursor;
const textStart = startOffset - memoryCursor;
const textEnd = endOffset - memoryCursor;

if (textStart > 0) {
dos.push({
Expand All @@ -324,7 +355,7 @@ export function getRetainAndDeleteFromReplace(

dos.push({
t: 'd',
len: textEnd - textStart + 1,
len: textEnd - textStart,
line: 0,
segmentId,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ function updateParagraphs(
}

const removeParagraphs = deleteParagraphs(body, textLength, currentIndex, true);

if (coverType !== UpdateDocsAttributeType.REPLACE) {
const newUpdateParagraphs: IParagraph[] = [];
for (const updateParagraph of updateDataParagraphs) {
Expand Down Expand Up @@ -265,6 +266,7 @@ function updateParagraphs(

updateBody.paragraphs = newUpdateParagraphs;
}

insertParagraphs(body, updateBody, textLength, currentIndex);

return removeParagraphs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@ import { Inject } from '@wendellhu/redi';
import { Subscription } from 'rxjs';

import { getDocObject } from '../basics/component-tools';
import { DeleteCommand, DeleteLeftCommand, UpdateCommand } from '../commands/commands/core-editing.command';
import {
DeleteCommand,
DeleteDirection,
DeleteLeftCommand,
DeleteRightCommand,
UpdateCommand,
} from '../commands/commands/core-editing.command';
import { DocSkeletonManagerService } from '../services/doc-skeleton-manager.service';
import { TextSelectionManagerService } from '../services/text-selection-manager.service';

@OnLifecycle(LifecycleStages.Rendered, DeleteLeftInputController)
export class DeleteLeftInputController extends Disposable {
@OnLifecycle(LifecycleStages.Rendered, DeleteController)
export class DeleteController extends Disposable {
private _onInputSubscription: Nullable<Subscription>;

constructor(
Expand All @@ -51,20 +57,29 @@ export class DeleteLeftInputController extends Disposable {
private _initialize() {}

private _commandExecutedListener() {
const updateCommandList = [DeleteLeftCommand.id];
const updateCommandList = [DeleteLeftCommand.id, DeleteRightCommand.id];

this.disposeWithMe(
this._commandService.onCommandExecuted((command: ICommandInfo) => {
if (!updateCommandList.includes(command.id)) {
return;
}

this._deleteFunction();
switch (command.id) {
case DeleteLeftCommand.id:
this._handleDeleteLeft();
break;
case DeleteRightCommand.id:
this._handleDeleteRight();
break;
default:
throw new Error('Unknown command');
}
})
);
}

private _deleteFunction() {
private _handleDeleteLeft() {
const activeRange = this._textSelectionRenderManager.getActiveRange();

const skeleton = this._docSkeletonManagerService.getCurrent()?.skeleton;
Expand All @@ -75,20 +90,24 @@ export class DeleteLeftInputController extends Disposable {

const docsModel = this._currentUniverService.getCurrentUniverDocInstance();

const { startOffset, collapsed, segmentId, style, startNodePosition } = activeRange;
const { startOffset, collapsed, segmentId, style } = activeRange;

const preSpan = skeleton.findSpanByPosition(startNodePosition);
// No need to delete when the cursor is at the first position of the first paragraph.
if (startOffset === 0 && collapsed) {
return;
}

const preIsBullet = hasListSpan(preSpan);
const preSpan = skeleton.findNodeByCharIndex(startOffset);

// is in bullet list?
const preIsBullet = hasListSpan(preSpan);
// is in indented paragraph?
const preIsIndent = isIndentBySpan(preSpan, docsModel.body);

let cursor = startOffset;

cursor--;

if (collapsed === false) {
cursor += 1;
if (collapsed === true) {
cursor--;
}

const span = skeleton.findNodeByCharIndex(cursor);
Expand All @@ -111,6 +130,7 @@ export class DeleteLeftInputController extends Disposable {

if (preIsBullet === true) {
const paragraphStyle = paragraph.paragraphStyle;

if (paragraphStyle) {
updateParagraph.paragraphStyle = paragraphStyle;
}
Expand All @@ -128,7 +148,7 @@ export class DeleteLeftInputController extends Disposable {

this._commandService.executeCommand(UpdateCommand.id, {
unitId: docsModel.getUnitId(),
body: {
updateBody: {
dataStream: '',
paragraphs: [{ ...updateParagraph }],
},
Expand All @@ -153,6 +173,7 @@ export class DeleteLeftInputController extends Disposable {
unitId: docsModel.getUnitId(),
range: activeRange,
segmentId,
direction: DeleteDirection.LEFT,
});
}

Expand All @@ -173,6 +194,44 @@ export class DeleteLeftInputController extends Disposable {
]);
}

private _handleDeleteRight() {
const activeRange = this._textSelectionRenderManager.getActiveRange();

const skeleton = this._docSkeletonManagerService.getCurrent()?.skeleton;

if (activeRange == null || skeleton == null) {
return;
}

const docsModel = this._currentUniverService.getCurrentUniverDocInstance();

const { startOffset, collapsed, segmentId, style } = activeRange;

// No need to delete when the cursor is at the last position of the last paragraph.
if (startOffset === docsModel.getBodyModel().getBody().dataStream.length - 2 && collapsed) {
return;
}

this._commandService.executeCommand(DeleteCommand.id, {
unitId: docsModel.getUnitId(),
range: activeRange,
segmentId,
direction: DeleteDirection.RIGHT,
});

skeleton?.calculate();

// move selection
this._textSelectionManagerService.replaceTextRanges([
{
startOffset,
endOffset: startOffset,
collapsed: true,
style,
},
]);
}

private _getDocObject() {
return getDocObject(this._currentUniverService, this._renderManagerService);
}
Expand Down
9 changes: 6 additions & 3 deletions packages/base-docs/src/doc-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
CoverCommand,
DeleteCommand,
DeleteLeftCommand,
DeleteRightCommand,
IMEInputCommand,
InsertCommand,
UpdateCommand,
Expand All @@ -37,7 +38,7 @@ import { MoveCursorOperation, MoveSelectionOperation } from './commands/operatio
import { SetDocZoomRatioOperation } from './commands/operations/set-doc-zoom-ratio.operation';
import { SetTextSelectionsOperation } from './commands/operations/text-selection.operation';
import { DocClipboardController } from './controllers/clipboard.controller';
import { DeleteLeftInputController } from './controllers/delete-left-input.controller';
import { DeleteController } from './controllers/delete.controller';
import { DocRenderController } from './controllers/doc-render.controller';
import { FloatingObjectController } from './controllers/floating-object.controller';
import { IMEInputController } from './controllers/ime-input.controller';
Expand All @@ -52,7 +53,7 @@ import { enUS } from './locale';
import { DocClipboardService, IDocClipboardService } from './services/clipboard/clipboard.service';
import { DocSkeletonManagerService } from './services/doc-skeleton-manager.service';
import { TextSelectionManagerService } from './services/text-selection-manager.service';
import { BreakLineShortcut, DeleteLeftShortcut } from './shortcuts/core-editing.shortcut';
import { BreakLineShortcut, DeleteLeftShortcut, DeleteRightShortcut } from './shortcuts/core-editing.shortcut';
import {
MoveCursorDownShortcut,
MoveCursorLeftShortcut,
Expand Down Expand Up @@ -106,6 +107,7 @@ export class DocPlugin extends Plugin {
MoveCursorOperation,
MoveSelectionOperation,
DeleteLeftCommand,
DeleteRightCommand,
SetInlineFormatBoldCommand,
SetInlineFormatItalicCommand,
SetInlineFormatUnderlineCommand,
Expand Down Expand Up @@ -139,6 +141,7 @@ export class DocPlugin extends Plugin {
MoveSelectionLeftShortcut,
MoveSelectionRightShortcut,
DeleteLeftShortcut,
DeleteRightShortcut,
BreakLineShortcut,
].forEach((shortcut) => {
this._injector.get(IShortcutService).registerShortcut(shortcut);
Expand Down Expand Up @@ -180,7 +183,7 @@ export class DocPlugin extends Plugin {
[TextSelectionController],
[NormalInputController],
[IMEInputController],
[DeleteLeftInputController],
[DeleteController],
[InlineFormatController],
[DocClipboardController],
[LineBreakInputController],
Expand Down
8 changes: 7 additions & 1 deletion packages/base-docs/src/shortcuts/core-editing.shortcut.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IShortcutItem, KeyCode } from '@univerjs/base-ui';
import { FOCUSING_DOC } from '@univerjs/core';

import { BreakLineCommand, DeleteLeftCommand } from '../commands/commands/core-editing.command';
import { BreakLineCommand, DeleteLeftCommand, DeleteRightCommand } from '../commands/commands/core-editing.command';

export const BreakLineShortcut: IShortcutItem = {
id: BreakLineCommand.id,
Expand All @@ -14,3 +14,9 @@ export const DeleteLeftShortcut: IShortcutItem = {
preconditions: (contextService) => contextService.getContextValue(FOCUSING_DOC),
binding: KeyCode.BACKSPACE,
};

export const DeleteRightShortcut: IShortcutItem = {
id: DeleteRightCommand.id,
preconditions: (contextService) => contextService.getContextValue(FOCUSING_DOC),
binding: KeyCode.DELETE,
};
2 changes: 1 addition & 1 deletion packages/base-render/src/basics/document-node-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function isIndentBySpan(span: Nullable<IDocumentSkeletonSpan>, body?: IDo
if (paragraph == null) {
return false;
}
const paragraphStyle = paragraph.paragraphStyle;
const { paragraphStyle } = paragraph;

if (paragraphStyle == null) {
return false;
Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/shared/doc-tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,8 @@ export function checkParagraphHasIndentByStyle(paragraphStyle?: IParagraphStyle)
}

if (
((paragraphStyle?.indentStart == null || paragraphStyle?.indentStart === 0) &&
paragraphStyle?.hanging == null) ||
paragraphStyle?.hanging === 0
((paragraphStyle.indentStart == null || paragraphStyle.indentStart === 0) && paragraphStyle.hanging == null) ||
paragraphStyle.hanging === 0
) {
return false;
}
Expand Down