Use new more restrictive regex

This commit is contained in:
Eric Bailey
2023-10-23 15:04:46 -05:00
parent 61f88ae2b0
commit ee3d1ff40d
5 changed files with 42 additions and 23 deletions
+5 -2
View File
@@ -1,6 +1,9 @@
export const TAG_REGEX = /(?:^|\s)(#[^\d\s]\S*)(?=\s)?/gi export const TAG_REGEX =
/(?:^|\s)(#[\p{L}\p{Emoji_Presentation}\p{Emoji_Modifier_Base}\p{Extended_Pictographic}]{1}[\p{L}\p{Emoji_Presentation}\p{Emoji_Modifier_Base}\p{Extended_Pictographic}\d_-]*)/giu
export const LOOSE_TAG_REGEX =
/(?:^|\s)(#[\p{L}\p{Emoji_Presentation}\p{Emoji_Modifier_Base}\p{Extended_Pictographic}]{1}[\p{L}\p{Emoji_Presentation}\p{Emoji_Modifier_Base}\p{Extended_Pictographic}\d_-]*\S*)/giu
export const ENDING_PUNCTUATION_REGEX = /\p{P}+$/gu export const ENDING_PUNCTUATION_REGEX = /\p{P}+$/gu
export const LEADING_HASH_REGEX = /^#/ export const LEADING_HASH_REGEX = /^#/g
export function sanitize(tagString: string) { export function sanitize(tagString: string) {
return tagString return tagString
@@ -236,6 +236,8 @@ export const TextInput = forwardRef(function TextInputImpl(
}) })
}, [richtext, pal.link, pal.text]) }, [richtext, pal.link, pal.text])
console.log('render')
return ( return (
<View style={styles.container}> <View style={styles.container}>
<PasteInput <PasteInput
@@ -7,14 +7,17 @@ import {usePalette} from 'lib/hooks/usePalette'
import {Text} from 'view/com/util/text/Text' import {Text} from 'view/com/util/text/Text'
import {LEADING_HASH_REGEX, TAG_REGEX} from 'lib/strings/hashtags' import {LEADING_HASH_REGEX, TAG_REGEX} from 'lib/strings/hashtags'
/**
* Loops over matches in the text to find the hashtag under the cursor.
*/
export function getHashtagAt(text: string, position: number) { export function getHashtagAt(text: string, position: number) {
let match for (const match of Array.from(text.matchAll(TAG_REGEX))) {
while ((match = TAG_REGEX.exec(text))) { const {index} = match
const [matchedString, tag] = match const [matchedString, tag] = match
if (tag.length > 66) continue if (tag.length > 66 || index === undefined) continue
const from = match.index + matchedString.indexOf(tag) const from = index + matchedString.indexOf(tag)
const to = from + tag.length const to = from + tag.length
if (position >= from && position <= to) { if (position >= from && position <= to) {
@@ -26,11 +29,11 @@ export function getHashtagAt(text: string, position: number) {
* show autocomplete after a single # is typed * show autocomplete after a single # is typed
* AND the cursor is next to the # * AND the cursor is next to the #
*/ */
const hashRegex = LEADING_HASH_REGEX for (const match of Array.from(text.matchAll(LEADING_HASH_REGEX))) {
let hashMatch const {index} = match
while ((hashMatch = hashRegex.exec(text))) { if (index === undefined) continue
if (position >= hashMatch.index && position <= hashMatch.index + 1) { if (position >= index && position <= index + 1) {
return {value: '', index: hashMatch.index} return {value: '', index}
} }
} }
@@ -25,8 +25,8 @@ export const Tags = Node.create<TagOptions>({
addOptions() { addOptions() {
return { return {
HTMLAttributes: {}, HTMLAttributes: {},
renderLabel({options, node}) { renderLabel({node}) {
return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}` return `#${node.attrs.id}`
}, },
suggestion: { suggestion: {
char: '#', char: '#',
@@ -61,11 +61,15 @@ export const Tags = Node.create<TagOptions>({
window.getSelection()?.collapseToEnd() window.getSelection()?.collapseToEnd()
}, },
/**
* This method and `findSuggestionMatch` below both have to return a
* truthy value, otherwise the suggestiond plugin will call `onExit`
* and we lose the ability to add a tag
*/
allow: ({state, range}) => { allow: ({state, range}) => {
const $from = state.doc.resolve(range.from) const $from = state.doc.resolve(range.from)
const type = state.schema.nodes[this.name] const type = state.schema.nodes[this.name]
const allow = !!$from.parent.type.contentMatch.matchType(type) const allow = !!$from.parent.type.contentMatch.matchType(type)
return allow return allow
}, },
findSuggestionMatch({$position}) { findSuggestionMatch({$position}) {
@@ -1,5 +1,5 @@
import { import {
TAG_REGEX, LOOSE_TAG_REGEX,
ENDING_PUNCTUATION_REGEX, ENDING_PUNCTUATION_REGEX,
LEADING_HASH_REGEX, LEADING_HASH_REGEX,
} from 'lib/strings/hashtags' } from 'lib/strings/hashtags'
@@ -12,6 +12,13 @@ export function parsePunctuationFromTag(value: string) {
return {tag, punctuation} return {tag, punctuation}
} }
/**
* A result must be returned from this method in order for the suggestion
* plugin to remain active and allow for the user to select a suggestion.
*
* That's why we use the loose regex form that includes trialing punctuation.
* We strip that our later.
*/
export function findSuggestionMatch({ export function findSuggestionMatch({
text, text,
cursorPosition, cursorPosition,
@@ -19,24 +26,25 @@ export function findSuggestionMatch({
text: string text: string
cursorPosition: number cursorPosition: number
}) { }) {
const match = Array.from(text.matchAll(TAG_REGEX)).pop() const match = Array.from(text.matchAll(LOOSE_TAG_REGEX)).pop()
if (!match || match.input === undefined || match.index === undefined) { if (!match || match.input === undefined || match.index === undefined) {
return null return null
} }
const startIndex = cursorPosition - text.length const startIndex = cursorPosition - text.length
let [matchedString, tag] = match let [matchedString, looselyMatchedTag] = match
const sanitized = tag const sanitized = looselyMatchedTag
.replace(ENDING_PUNCTUATION_REGEX, '') .replace(ENDING_PUNCTUATION_REGEX, '')
.replace(LEADING_HASH_REGEX, '') .replace(LEADING_HASH_REGEX, '')
// one of our hashtag spec rules // one of our hashtag spec rules
if (sanitized.length > 64) return null if (sanitized.length > 64) return null
const from = startIndex + match.index + matchedString.indexOf(tag) const from =
const to = from + tag.length startIndex + match.index + matchedString.indexOf(looselyMatchedTag)
const to = from + looselyMatchedTag.length
if (from < cursorPosition && to >= cursorPosition) { if (from < cursorPosition && to >= cursorPosition) {
return { return {
@@ -48,10 +56,9 @@ export function findSuggestionMatch({
* This is passed to the `items({ query })` method configured in * This is passed to the `items({ query })` method configured in
* `createTagsAutocomplete`. * `createTagsAutocomplete`.
* *
* We parse out the punctuation later, but we don't want to pass * We parse out the punctuation later.
* the # to the search query.
*/ */
query: tag.replace(LEADING_HASH_REGEX, ''), query: looselyMatchedTag.replace(LEADING_HASH_REGEX, ''),
// raw text string // raw text string
text: matchedString, text: matchedString,
} }