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

[Part 2] Port Auto link #2485

Merged
merged 7 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createLink } from './link/createLink';
import { createLinkAfterSpace } from './link/createLinkAfterSpace';
import { keyboardListTrigger } from './list/keyboardListTrigger';
import { unlink } from './link/unlink';
import type {
Expand Down Expand Up @@ -106,27 +107,23 @@ export class AutoFormatPlugin implements EditorPlugin {
private handleKeyDownEvent(editor: IEditor, event: KeyDownEvent) {
const rawEvent = event.rawEvent;
if (!rawEvent.defaultPrevented && !event.handledByEditFeature) {
const { autoBullet, autoNumbering, autoUnlink } = this.options;
const { autoBullet, autoNumbering, autoUnlink, autoLink } = this.options;
switch (rawEvent.key) {
case ' ':
if (autoBullet || autoNumbering) {
keyboardListTrigger(editor, rawEvent, autoBullet, autoNumbering);
}
keyboardListTrigger(editor, rawEvent, autoBullet, autoNumbering);
createLinkAfterSpace(editor, autoLink);
break;
case 'Backspace':
if (autoUnlink) {
unlink(editor, rawEvent);
}

unlink(editor, rawEvent, autoUnlink);
break;
}
}
}

private handleContentChangedEvent(editor: IEditor, event: ContentChangedEvent) {
const { autoLink } = this.options;
if (event.source == 'Paste' && autoLink) {
createLink(editor);
if (event.source == 'Paste') {
createLink(editor, autoLink);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import type { IEditor } from 'roosterjs-content-model-types';
/**
* @internal
*/
export function createLink(editor: IEditor) {
export function createLink(editor: IEditor, autoLink: boolean) {
if (!autoLink) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here

return;
}
editor.formatContentModel(model => {
const link = getLinkSegment(model);
if (link && !link.link) {
Expand All @@ -16,10 +19,8 @@ export function createLink(editor: IEditor) {
},
dataset: {},
});

return true;
}

return false;
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createText } from 'roosterjs-content-model-dom';
import { getSelectedSegmentsAndParagraphs } from 'roosterjs-content-model-core';
import { matchLink } from 'roosterjs-content-model-api';
import type { IEditor } from 'roosterjs-content-model-types';

/**
* @internal
*/
export function createLinkAfterSpace(editor: IEditor, autoLink: boolean) {
if (!autoLink) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here

return;
}
editor.formatContentModel(model => {
const selectedSegmentsAndParagraphs = getSelectedSegmentsAndParagraphs(
model,
false /* includingFormatHolder */
);
if (selectedSegmentsAndParagraphs[0] && selectedSegmentsAndParagraphs[0][1]) {
const length = selectedSegmentsAndParagraphs[0][1].segments.length;
const marker = selectedSegmentsAndParagraphs[0][1].segments[length - 1];
const textSegment = selectedSegmentsAndParagraphs[0][1].segments[length - 2];
if (
marker.segmentType == 'SelectionMarker' &&
textSegment.segmentType == 'Text' &&
!textSegment.link
) {
const link = textSegment.text.split(' ').pop();
if (link && matchLink(link)) {
textSegment.text = textSegment.text.replace(link, '');
const linkSegment = createText(link, marker.format, {
format: {
href: link,
underline: true,
},
dataset: {},
});
selectedSegmentsAndParagraphs[0][1].segments.splice(length - 1, 0, linkSegment);
return true;
}
}
}

return false;
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import type { IEditor } from 'roosterjs-content-model-types';
/**
* @internal
*/
export function unlink(editor: IEditor, rawEvent: KeyboardEvent) {
export function unlink(editor: IEditor, rawEvent: KeyboardEvent, unlink: boolean) {
if (!unlink) {
return;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

why need this parameter? If unlink is false, we should not call this function at all

editor.formatContentModel(model => {
const link = getLinkSegment(model);
if (link?.link) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,28 @@ export function keyboardListTrigger(
shouldSearchForBullet: boolean = true,
shouldSearchForNumbering: boolean = true
) {
editor.formatContentModel((model, _context) => {
const listStyleType = getListTypeStyle(
model,
shouldSearchForBullet,
shouldSearchForNumbering
);
if (listStyleType) {
const segmentsAndParagraphs = getSelectedSegmentsAndParagraphs(model, false);
if (segmentsAndParagraphs[0] && segmentsAndParagraphs[0][1]) {
segmentsAndParagraphs[0][1].segments.splice(0, 1);
}
const { listType, styleType, index } = listStyleType;
triggerList(editor, model, listType, styleType, index);
rawEvent.preventDefault();
normalizeContentModel(model);
if (shouldSearchForBullet || shouldSearchForNumbering) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here, check outside to reduce unnecessary parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We already use these parameter on line 20 and 21, then we cannot remove them

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I return this check to outside the function anyway?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see. In that case we can keep it

editor.formatContentModel((model, _context) => {
const listStyleType = getListTypeStyle(
model,
shouldSearchForBullet,
shouldSearchForNumbering
);
if (listStyleType) {
const segmentsAndParagraphs = getSelectedSegmentsAndParagraphs(model, false);
if (segmentsAndParagraphs[0] && segmentsAndParagraphs[0][1]) {
segmentsAndParagraphs[0][1].segments.splice(0, 1);
}
const { listType, styleType, index } = listStyleType;
triggerList(editor, model, listType, styleType, index);
rawEvent.preventDefault();
normalizeContentModel(model);

return true;
}
return false;
});
return true;
}
return false;
});
}
}

const triggerList = (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as createLink from '../../lib/autoFormat/link/createLink';
import * as createLinkAfterSpace from '../../lib/autoFormat/link/createLinkAfterSpace';
import * as keyboardTrigger from '../../lib/autoFormat/list/keyboardListTrigger';
import * as unlink from '../../lib/autoFormat/link/unlink';
import { AutoFormatOptions, AutoFormatPlugin } from '../../lib/autoFormat/AutoFormatPlugin';
Expand Down Expand Up @@ -31,11 +32,9 @@ describe('Content Model Auto Format Plugin Test', () => {
options?: {
autoBullet: boolean;
autoNumbering: boolean;
autoUnlink: boolean;
autoLink: boolean;
}
) {
const plugin = new AutoFormatPlugin(options);
const plugin = new AutoFormatPlugin(options as AutoFormatOptions);
plugin.initialize(editor);

plugin.onPluginEvent(event);
Expand All @@ -44,8 +43,8 @@ describe('Content Model Auto Format Plugin Test', () => {
expect(keyboardListTriggerSpy).toHaveBeenCalledWith(
editor,
event.rawEvent,
options?.autoBullet ?? true,
options?.autoNumbering ?? true
options ? options.autoBullet : true,
options ? options.autoNumbering : true
);
} else {
expect(keyboardListTriggerSpy).not.toHaveBeenCalled();
Expand Down Expand Up @@ -78,7 +77,7 @@ describe('Content Model Auto Format Plugin Test', () => {
handledByEditFeature: false,
};

runTest(event, false, { autoBullet: false, autoNumbering: false } as AutoFormatOptions);
runTest(event, true, { autoBullet: false, autoNumbering: false } as AutoFormatOptions);
});

it('should trigger keyboardListTrigger with auto bullet only', () => {
Expand Down Expand Up @@ -140,7 +139,10 @@ describe('Content Model Auto Format Plugin Test', () => {
plugin.onPluginEvent(event);

if (shouldCallTrigger) {
expect(createLinkSpy).toHaveBeenCalledWith(editor);
expect(createLinkSpy).toHaveBeenCalledWith(
editor,
options ? options.autoLink : true
);
} else {
expect(createLinkSpy).not.toHaveBeenCalled();
}
Expand All @@ -159,7 +161,7 @@ describe('Content Model Auto Format Plugin Test', () => {
eventType: 'contentChanged',
source: 'Paste',
};
runTest(event, false, { autoLink: false });
runTest(event, true, { autoLink: false });
});

it('should not call createLink - not paste', () => {
Expand Down Expand Up @@ -191,7 +193,11 @@ describe('Content Model Auto Format Plugin Test', () => {
plugin.onPluginEvent(event);

if (shouldCallTrigger) {
expect(unlinkSpy).toHaveBeenCalledWith(editor, event.rawEvent);
expect(unlinkSpy).toHaveBeenCalledWith(
editor,
event.rawEvent,
options ? options.autoUnlink : true
);
} else {
expect(unlinkSpy).not.toHaveBeenCalled();
}
Expand All @@ -212,7 +218,7 @@ describe('Content Model Auto Format Plugin Test', () => {
eventType: 'keyDown',
rawEvent: { key: 'Backspace', preventDefault: () => {} } as any,
};
runTest(event, false, {
runTest(event, true, {
autoUnlink: false,
});
});
Expand All @@ -225,4 +231,60 @@ describe('Content Model Auto Format Plugin Test', () => {
runTest(event, false);
});
});

describe('onPluginEvent - createLinkAfterSpace', () => {
let createLinkAfterSpaceSpy: jasmine.Spy;

beforeEach(() => {
createLinkAfterSpaceSpy = spyOn(createLinkAfterSpace, 'createLinkAfterSpace');
});

function runTest(
event: KeyDownEvent,
shouldCallTrigger: boolean,
options?: {
autoLink: boolean;
}
) {
const plugin = new AutoFormatPlugin(options as AutoFormatOptions);
plugin.initialize(editor);

plugin.onPluginEvent(event);

if (shouldCallTrigger) {
expect(createLinkAfterSpaceSpy).toHaveBeenCalledWith(
editor,
options ? options.autoLink : true
);
} else {
expect(createLinkAfterSpaceSpy).not.toHaveBeenCalled();
}
}

it('should call createLinkAfterSpace', () => {
const event: KeyDownEvent = {
eventType: 'keyDown',
rawEvent: { key: ' ', preventDefault: () => {} } as any,
};
runTest(event, true);
});

it('should not call createLinkAfterSpace - disable options', () => {
const event: KeyDownEvent = {
eventType: 'keyDown',
rawEvent: { key: ' ', preventDefault: () => {} } as any,
};
runTest(event, true, {
autoLink: false,
});
});

it('should not call createLinkAfterSpace - not backspace', () => {
const event: KeyDownEvent = {
eventType: 'keyDown',
rawEvent: { key: 'Backspace', preventDefault: () => {} } as any,
};
runTest(event, false);
});
});
});
Loading
Loading