Use new more restrictive regex
This commit is contained in:
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user