Skip to content

Commit

Permalink
fix(link): refactor auto link to simplify and remove bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
whawker committed Feb 4, 2022
1 parent 4f71498 commit e5eef6d
Show file tree
Hide file tree
Showing 4 changed files with 292 additions and 236 deletions.
6 changes: 6 additions & 0 deletions .changeset/twenty-zebras-live.md
@@ -0,0 +1,6 @@
---
'@remirror/core-utils': patch
'@remirror/extension-link': patch
---

Simplify how auto link works in the link extension, to simplify maintainance and fix a few issues.
52 changes: 47 additions & 5 deletions packages/remirror__core-utils/src/core-utils.ts
Expand Up @@ -69,9 +69,21 @@ import {
Transaction as PMTransaction,
} from '@remirror/pm/state';
import type { Step } from '@remirror/pm/transform';
import {
AddMarkStep,
RemoveMarkStep,
ReplaceAroundStep,
ReplaceStep,
} from '@remirror/pm/transform';

import { environment } from './environment';

function isRangeStep(
step: Step,
): step is AddMarkStep | ReplaceAroundStep | ReplaceStep | RemoveMarkStep {
return isValidStep(step, [AddMarkStep, ReplaceAroundStep, ReplaceStep, RemoveMarkStep]);
}

/**
* Identifies the value as having a remirror identifier. This is the core
* predicate check for the remirror library.
Expand Down Expand Up @@ -592,6 +604,17 @@ function isValidStep(step: Step, StepTypes: Array<AnyConstructor<Step>>) {
return StepTypes.length === 0 || StepTypes.some((Constructor) => step instanceof Constructor);
}

export interface ChangedRange extends FromToProps {
/**
* The previous starting position in the document.
*/
prevFrom: number;
/**
* The previous ending position in the document.
*/
prevTo: number;
}

/**
* Get all the ranges of changes for the provided transaction.
*
Expand All @@ -608,19 +631,33 @@ function isValidStep(step: Step, StepTypes: Array<AnyConstructor<Step>>) {
export function getChangedRanges(
tr: Transaction,
StepTypes: Array<AnyConstructor<Step>> = [],
): FromToProps[] {
): ChangedRange[] {
// The holder for the ranges value which will be returned from this function.
const ranges: FromToProps[] = [];
const ranges: ChangedRange[] = [];
const rawRanges: FromToProps[] = [];

for (const step of tr.steps) {
if (!isValidStep(step, StepTypes)) {
continue;
}

step.getMap().forEach((_, __, from, to) => {
const stepMap = step.getMap();

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore @see https://github.com/ProseMirror/prosemirror/issues/1075
if (stepMap.ranges.length === 0 && isRangeStep(step)) {
const { from, to } = step;

if (from === undefined || to === undefined) {
continue;
}

rawRanges.push({ from, to });
});
} else {
step.getMap().forEach((_, __, from, to) => {
rawRanges.push({ from, to });
});
}
}

// Sort the ranges.
Expand All @@ -636,7 +673,12 @@ export function getChangedRanges(

if (noOverlap) {
// Add the new range when no overlap is found.
ranges.push({ from, to });
ranges.push({
from,
to,
prevFrom: tr.mapping.invert().map(from, -1),
prevTo: tr.mapping.invert().map(to),
});
} else if (lastRange) {
// Update the lastRange's end value.
lastRange.to = Math.max(lastRange.from, to);
Expand Down
60 changes: 57 additions & 3 deletions packages/remirror__extension-link/__tests__/link-extension.spec.ts
Expand Up @@ -517,7 +517,7 @@ describe('autolinking', () => {
);
});

it('can parse telphone by overriding extractHref', () => {
it('can parse telephone by overriding extractHref', () => {
const phoneRegex = /(?:\+?(\d{1,3}))?[(.-]*(\d{3})[).-]*(\d{3})[.-]*(\d{4})(?: *x(\d+))?/;
const linkRegex =
/(?:(?:(?:https?|ftp):)?\/\/)?(?:\S+(?::\S*)?@)?(?:(?:[\da-z\u00A1-\uFFFF][\w\u00A1-\uFFFF-]{0,62})?[\da-z\u00A1-\uFFFF]\.)+[a-z\u00A1-\uFFFF]{2,}\.?(?::\d{2,5})?(?:[#/?]\S*)?/gi;
Expand Down Expand Up @@ -602,7 +602,6 @@ describe('autolinking', () => {
range: {
from: 1,
to: editorText.length + 1,
cursor: editorText.length + 1,
},
attrs: { auto: true, href: '//test.co' },
});
Expand All @@ -616,7 +615,6 @@ describe('autolinking', () => {
range: {
from: 1,
to: editorText.length + 1,
cursor: editorText.length + 1,
},
attrs: { auto: true, href: '//test.com' },
});
Expand Down Expand Up @@ -656,6 +654,18 @@ describe('autolinking', () => {
);
});

it('updates the autolink if modified in the middle', () => {
editor
.add(doc(p('<cursor>')))
.insertText('github.com')
.selectText(7)
.insertText('status');

expect(editor.doc).toEqualRemirrorDocument(
doc(p(link({ auto: true, href: '//githubstatus.com' })('githubstatus.com'))),
);
});

it('supports deleting selected to to invalidate the match', () => {
editor
.add(doc(p('<cursor>')))
Expand All @@ -675,6 +685,14 @@ describe('autolinking', () => {
expect(editor.doc).toEqualRemirrorDocument(doc(p('test.c')));
});

it('supports backspace to create a match', () => {
editor.add(doc(p('test. <cursor>com'))).backspace();

expect(editor.doc).toEqualRemirrorDocument(
doc(p(link({ auto: true, href: '//test.com' })('test.com'))),
);
});

it('can respond to `Enter` key presses', () => {
editor
.add(doc(p('<cursor>')))
Expand Down Expand Up @@ -730,6 +748,42 @@ describe('autolinking', () => {
);
});

it('does not remove other links in the same parent node', () => {
editor
.add(
doc(
p(
link({ auto: true, href: '//remirror.io' })('remirror.io'),
'<cursor> ',
link({ href: '//test.com' })('test.com'),
),
),
)
.backspace();

expect(editor.doc).toEqualRemirrorDocument(
doc(p('remirror.i ', link({ href: '//test.com' })('test.com'))),
);
});

it('does not remove other auto links in the same parent node', () => {
editor
.add(
doc(
p(
link({ auto: true, href: '//remirror.io' })('remirror.io'),
'<cursor> ',
link({ auto: true, href: '//test.com' })('test.com'),
),
),
)
.backspace();

expect(editor.doc).toEqualRemirrorDocument(
doc(p('remirror.i ', link({ auto: true, href: '//test.com' })('test.com'))),
);
});

it('detects emails as auto link', () => {
editor.add(doc(p('<cursor>'))).insertText('user@example.com');

Expand Down

0 comments on commit e5eef6d

Please sign in to comment.