diff --git a/README.md b/README.md
index e718b93..7de7a1c 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,7 @@ It does not require any additional licenses, except for MIT. ([#38](https://gith
| [Custom `sep` Style](#options-sep) | ✅ |
| [Fixation-Points](#options-fixationpoint) | ✅ |
| [Ignore HTML Tags](#options-ignorehtmltag) | ✅ |
+| [Ignore HTML Entity](#options-ignorehtmlentity) | ✅ |
| [Saccade](https://github.com/Gumball12/text-vide/issues/21) | ❌ |
### Benchmark
@@ -123,7 +124,7 @@ type Options = Partial<{
#### `sep`
-- Default Value: `['', '']`
+- Default: `['', '']`
Passing a string allows you to specify the Beginning and End of the highlighted word at once.
@@ -139,7 +140,7 @@ textVide('text-vide', { sep: ['', ''] }); // 'tex
-- Default Value: `1`
+- Default: `1`
- Range: `[1, 5]`
```ts
@@ -152,7 +153,7 @@ textVide('text-vide', { fixationPoint: 5 }); // 'text-vide'
#### `ignoreHtmlTag`
-- Default Value: `true`
+- Default: `true`
If this option is `true`, HTML tags are not highlighted.
@@ -161,6 +162,17 @@ textVite('abcd
efg'); // 'abcd
efg'
textVite('abcd
efg', { ignoreHtmlTag: false }); // '<div>abcddiv>efg'
```
+#### `ignoreHtmlEntity`
+
+- Default: `true`
+
+If this option is `true`, HTML entities are not highlighted.
+
+```ts
+textVide(' abcd>'); // ' abcd>'
+textVide(' abcd>', { ignoreHtmlEntity: false }); // abcd>
+```
+
## License
[MIT](./LICENSE) @Gumball12
diff --git a/apps/sandbox/src/App.tsx b/apps/sandbox/src/App.tsx
index 241c9e0..6a806a8 100644
--- a/apps/sandbox/src/App.tsx
+++ b/apps/sandbox/src/App.tsx
@@ -20,6 +20,7 @@ type Edits = {
secondSep: string;
fixationPoint: string;
ignoreHtmlTag: string;
+ ignoreHtmlEntity: string;
input: string;
};
@@ -28,6 +29,7 @@ const defaultEdits: Edits = {
secondSep: '',
fixationPoint: '1',
ignoreHtmlTag: '1', // 1 = true, 0 = false
+ ignoreHtmlEntity: '1', // 1 = true, 0 = false
input: INITIAL_INPUT,
};
@@ -37,6 +39,7 @@ const storeEdits = ({
fixationPoint,
input,
ignoreHtmlTag,
+ ignoreHtmlEntity,
}: Edits) => {
const search = [
`firstSep=${encodeURIComponent(firstSep)}`,
@@ -44,6 +47,7 @@ const storeEdits = ({
`fixationPoint=${encodeURIComponent(fixationPoint)}`,
`input=${encodeURIComponent(input)}`,
`ignoreHtmlTag=${encodeURIComponent(ignoreHtmlTag)}`,
+ `ignoreHtmlEntity=${encodeURIComponent(ignoreHtmlEntity)}`,
].join('&');
// eslint-disable-next-line
@@ -100,6 +104,7 @@ type Action = {
| 'HIGHLIGHTED_TEXT'
| 'COPIED'
| 'TOGGLE_IGNORE_HTML_TAG'
+ | 'TOGGLE_IGNORE_HTML_ENTITY'
| 'RESET';
value: string;
copied: boolean;
@@ -135,6 +140,11 @@ const reducer: Reducer = (state, { type, value, copied }) => {
return { ...state, ignoreHtmlTag: nextIgnoreHtmlTag };
}
+ if (type === 'TOGGLE_IGNORE_HTML_ENTITY') {
+ const nextIgnoreHtmlEntity = state.ignoreHtmlEntity === '1' ? '0' : '1';
+ return { ...state, ignoreHtmlEntity: nextIgnoreHtmlEntity };
+ }
+
if (type === 'RESET') {
return {
...defaultEdits,
@@ -161,6 +171,7 @@ const App = () => {
copiedEffect,
highlightedText,
ignoreHtmlTag,
+ ignoreHtmlEntity,
} = state;
useEffect(() => {
@@ -169,6 +180,7 @@ const App = () => {
sep: [firstSep, secondSep],
fixationPoint: parseInt(fixationPoint),
ignoreHtmlTag: ignoreHtmlTag === '1',
+ ignoreHtmlEntity: ignoreHtmlEntity === '1',
};
const highlightedText = textVide(input, options);
@@ -185,11 +197,19 @@ const App = () => {
input,
fixationPoint,
ignoreHtmlTag,
+ ignoreHtmlEntity,
});
}, DEBOUNCE_TIMEOUT);
return () => clearTimeout(store);
- }, [firstSep, secondSep, input, fixationPoint, ignoreHtmlTag]);
+ }, [
+ firstSep,
+ secondSep,
+ input,
+ fixationPoint,
+ ignoreHtmlTag,
+ ignoreHtmlEntity,
+ ]);
const copyUrl = () => {
const { href: url } = location;
@@ -217,6 +237,7 @@ const App = () => {
fixationPoint,
input,
ignoreHtmlTag,
+ ignoreHtmlEntity,
});
return (
@@ -265,19 +286,9 @@ const App = () => {
InputLabelProps={{ shrink: true }}
/>
-
-
-
+
{
-
-
-
-
+
+
+
diff --git a/packages/text-vide/src/__tests__/getOptions.test.ts b/packages/text-vide/src/__tests__/getOptions.test.ts
index 252aba1..8a2906a 100644
--- a/packages/text-vide/src/__tests__/getOptions.test.ts
+++ b/packages/text-vide/src/__tests__/getOptions.test.ts
@@ -8,6 +8,7 @@ describe('test getOptions()', () => {
sep: ['', ''],
fixationPoint: 1,
ignoreHtmlTag: true,
+ ignoreHtmlEntity: true,
};
expect(getOptions({})).toEqual(expected);
@@ -18,12 +19,14 @@ describe('test getOptions()', () => {
sep: undefined,
fixationPoint: undefined,
ignoreHtmlTag: undefined,
+ ignoreHtmlEntity: undefined,
};
const expected: Options = {
sep: ['', ''],
fixationPoint: 1,
ignoreHtmlTag: true,
+ ignoreHtmlEntity: true,
};
expect(getOptions(undefinedOptionValues)).toEqual(expected);
@@ -34,12 +37,14 @@ describe('test getOptions()', () => {
sep: ['', ''],
fixationPoint: undefined,
ignoreHtmlTag: undefined,
+ ignoreHtmlEntity: undefined,
};
const expected: Options = {
sep: ['', ''],
fixationPoint: 1,
ignoreHtmlTag: true,
+ ignoreHtmlEntity: true,
};
expect(getOptions(maybeOptions)).toEqual(expected);
@@ -50,6 +55,7 @@ describe('test getOptions()', () => {
sep: ['a', 'b'],
fixationPoint: 0, // but it's okay
ignoreHtmlTag: false,
+ ignoreHtmlEntity: false,
};
expect(getOptions(expected)).toEqual(expected);
diff --git a/packages/text-vide/src/__tests__/index.test.ts b/packages/text-vide/src/__tests__/index.test.ts
index deef14f..ab3e68f 100644
--- a/packages/text-vide/src/__tests__/index.test.ts
+++ b/packages/text-vide/src/__tests__/index.test.ts
@@ -158,6 +158,30 @@ describe('test options', () => {
expect(textVide(text)).toBe(expected);
});
+
+ it('ignoreHtmlTag: true', () => {
+ const text = 'abcd
efg';
+ const expectedText = 'abcd
efg';
+ expect(textVide(text, { ignoreHtmlTag: true })).toBe(expectedText);
+ });
+
+ it('ignoreHtmlTag: false', () => {
+ const text = 'abcd
efg';
+ const expected = '<div>abcddiv>efg';
+ expect(textVide(text, { ignoreHtmlTag: false })).toBe(expected);
+ });
+
+ it('ignoreHtmlEntity: true', () => {
+ const text = ' abcd>';
+ const expectedText = ' abcd>';
+ expect(textVide(text, { ignoreHtmlEntity: true })).toBe(expectedText);
+ });
+
+ it('ignoreHtmlEntity: false', () => {
+ const text = ' abcd>';
+ const expected = ' abcd>';
+ expect(textVide(text, { ignoreHtmlEntity: false })).toBe(expected);
+ });
});
describe('fixation point ([2, 5])', () => {
diff --git a/packages/text-vide/src/getOptions.ts b/packages/text-vide/src/getOptions.ts
index ef65415..1279f93 100644
--- a/packages/text-vide/src/getOptions.ts
+++ b/packages/text-vide/src/getOptions.ts
@@ -4,10 +4,12 @@ import defaults from 'utils/defaults';
const DEFAULT_SEP = ['', ''];
const DEFAULT_FIXATION_POINT = 1;
const DEFAULT_IGNORE_HTML_TAG = true;
+const DEFAULT_IGNORE_HTML_ENTITY = true;
export default (maybeOptions: Partial): Options =>
defaults(maybeOptions, {
sep: DEFAULT_SEP,
fixationPoint: DEFAULT_FIXATION_POINT,
ignoreHtmlTag: DEFAULT_IGNORE_HTML_TAG,
+ ignoreHtmlEntity: DEFAULT_IGNORE_HTML_ENTITY,
});
diff --git a/packages/text-vide/src/index.ts b/packages/text-vide/src/index.ts
index ff1ee22..ac9fa71 100644
--- a/packages/text-vide/src/index.ts
+++ b/packages/text-vide/src/index.ts
@@ -3,6 +3,7 @@ import getOptions from './getOptions';
import getFixationLength from './getFixationLength';
import getHighlightedText from './getHighlightedText';
import { useCheckIsHtmlTag } from './useCheckIsHtmlTag';
+import { useCheckIsHtmlEntity } from './useCheckIsHtmlEntity';
const CONVERTIBLE_REGEX = /(\p{L}|\p{Nd})*\p{L}(\p{L}|\p{Nd})*/gu;
@@ -11,24 +12,34 @@ export const textVide = (text: string, maybeOptions: Partial = {}) => {
return '';
}
- const { fixationPoint, sep, ignoreHtmlTag } = getOptions(maybeOptions);
- const convertibleMatchList = text.matchAll(CONVERTIBLE_REGEX);
+ const { fixationPoint, sep, ignoreHtmlTag, ignoreHtmlEntity } =
+ getOptions(maybeOptions);
+ const convertibleMatchList = Array.from(text.matchAll(CONVERTIBLE_REGEX));
let result = '';
let lastMatchedIndex = 0;
let checkIsHtmlTag: ReturnType | undefined;
-
if (ignoreHtmlTag) {
checkIsHtmlTag = useCheckIsHtmlTag(text);
}
+ let checkIsHtmlEntity: ReturnType | undefined;
+ if (ignoreHtmlEntity) {
+ checkIsHtmlEntity = useCheckIsHtmlEntity(text);
+ }
+
for (const match of convertibleMatchList) {
const isHtmlTag = checkIsHtmlTag?.(match);
if (isHtmlTag) {
continue;
}
+ const isHtmlEntity = checkIsHtmlEntity?.(match);
+ if (isHtmlEntity) {
+ continue;
+ }
+
const [matchedWord] = match;
const startIndex = match.index!;
const endIndex = startIndex + getFixationLength(matchedWord, fixationPoint);
diff --git a/packages/text-vide/src/types.ts b/packages/text-vide/src/types.ts
index d045992..ff66c4a 100644
--- a/packages/text-vide/src/types.ts
+++ b/packages/text-vide/src/types.ts
@@ -2,4 +2,5 @@ export type Options = {
sep: string | string[]; // default: ['', '']
fixationPoint: number; // default: 1
ignoreHtmlTag: boolean; // default: true
+ ignoreHtmlEntity: boolean; // default: true
};
diff --git a/packages/text-vide/src/useCheckIsHtmlEntity.ts b/packages/text-vide/src/useCheckIsHtmlEntity.ts
new file mode 100644
index 0000000..d291d41
--- /dev/null
+++ b/packages/text-vide/src/useCheckIsHtmlEntity.ts
@@ -0,0 +1,24 @@
+import { extractMatchRangeList } from './utils';
+
+const HTML_ENTITY_REGEX = /(&[\w#]+;)/g;
+
+export const useCheckIsHtmlEntity = (text: string) => {
+ const htmlEntityMatchList = text.matchAll(HTML_ENTITY_REGEX);
+ const htmlEntityRangeList = extractMatchRangeList(htmlEntityMatchList);
+ const reversedHtmlEntityRangeList = htmlEntityRangeList.reverse();
+
+ return (match: RegExpMatchArray) => {
+ const startIndex = match.index!;
+ const entityRange = reversedHtmlEntityRangeList.find(
+ ([rangeStart]) => startIndex > rangeStart,
+ );
+
+ if (!entityRange) {
+ return false;
+ }
+
+ const [, rangeEnd] = entityRange;
+ const isInclude = startIndex < rangeEnd;
+ return isInclude;
+ };
+};
diff --git a/packages/text-vide/src/useCheckIsHtmlTag.ts b/packages/text-vide/src/useCheckIsHtmlTag.ts
index 74e8688..3abed67 100644
--- a/packages/text-vide/src/useCheckIsHtmlTag.ts
+++ b/packages/text-vide/src/useCheckIsHtmlTag.ts
@@ -1,8 +1,10 @@
+import { extractMatchRangeList } from './utils';
+
const HTML_TAG_REGEX = /()|(<[^>]*>)/g;
export const useCheckIsHtmlTag = (text: string) => {
const htmlTagMatchList = text.matchAll(HTML_TAG_REGEX);
- const htmlTagRangeList = getHtmlTagRangeList(htmlTagMatchList);
+ const htmlTagRangeList = extractMatchRangeList(htmlTagMatchList);
const reversedHtmlTagRangeList = htmlTagRangeList.reverse();
return (match: RegExpMatchArray) => {
@@ -20,14 +22,3 @@ export const useCheckIsHtmlTag = (text: string) => {
return isInclude;
};
};
-
-const getHtmlTagRangeList = (
- htmlTagMatchList: IterableIterator,
-) =>
- [...htmlTagMatchList].map(htmlTagMatch => {
- const startIndex = htmlTagMatch.index!;
- const [tag] = htmlTagMatch;
- const { length: tagLength } = tag;
-
- return [startIndex, startIndex + tagLength - 1];
- });
diff --git a/packages/text-vide/src/utils.ts b/packages/text-vide/src/utils.ts
new file mode 100644
index 0000000..f74e9b1
--- /dev/null
+++ b/packages/text-vide/src/utils.ts
@@ -0,0 +1,10 @@
+export const extractMatchRangeList = (
+ matchList: IterableIterator,
+) =>
+ Array.from(matchList).map(match => {
+ const startIndex = match.index!;
+ const [matchedWord] = match;
+ const { length: matchedWordLength } = matchedWord;
+
+ return [startIndex, startIndex + matchedWordLength - 1];
+ });