feat: Add wildcard tag parsing and extraction functions for enhanced tag handling
This commit is contained in:
+120
-8
@@ -1,4 +1,6 @@
|
||||
// --- String Helper Functions ---
|
||||
const REG_WILDCARD_WEIGHTED_TAG = /(\d+)[_\s]*::(.*?)(?=\||$)/g;
|
||||
const REG_WILDCARD_SIMPLE_WORD = /[^{}_|]+/g;
|
||||
|
||||
/**
|
||||
* Converts Hiragana to Katakana.
|
||||
@@ -179,16 +181,113 @@ export function isValidTag(tag) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively finds all words in a string, even those inside nested braces.
|
||||
* This helps extract all possible tags from complex nested wildcards.
|
||||
* @param {string} text The text to extract words from
|
||||
* @param {number} baseStart The starting position of the text in the original string
|
||||
* @returns {Array<{start: number, end: number, tag: string}>} Array of parsed tags
|
||||
*/
|
||||
function extractAllWords(text, baseStart) {
|
||||
const result = [];
|
||||
|
||||
// First, extract weighted tag patterns like "20::from above"
|
||||
let weightMatch = REG_WILDCARD_WEIGHTED_TAG.exec(text);
|
||||
while (weightMatch !== null) {
|
||||
const tagText = weightMatch[2].trim();
|
||||
|
||||
if (tagText) {
|
||||
// Calculate position with original offsets
|
||||
const fullMatchStart = baseStart + weightMatch.index;
|
||||
const tagTextStart = fullMatchStart + weightMatch[0].indexOf(tagText);
|
||||
const tagTextEnd = tagTextStart + tagText.length;
|
||||
|
||||
result.push({
|
||||
start: tagTextStart,
|
||||
end: tagTextEnd,
|
||||
tag: tagText
|
||||
});
|
||||
}
|
||||
|
||||
weightMatch = REG_WILDCARD_WEIGHTED_TAG.exec(text);
|
||||
}
|
||||
|
||||
// If no weighted tags were found, extract simple words
|
||||
if (result.length === 0) {
|
||||
// Regular expression to match words (sequences of non-whitespace characters)
|
||||
// We consider a word to be any continuous sequence of characters that's not a space, pipe, or brace
|
||||
const wordRegex = REG_WILDCARD_SIMPLE_WORD;
|
||||
let match;
|
||||
|
||||
// Find all standalone words in the text
|
||||
while ((match = wordRegex.exec(text)) !== null) {
|
||||
const wordStart = baseStart + match.index;
|
||||
const wordEnd = wordStart + match[0].length;
|
||||
|
||||
// Remove leading and trailing spaces from the matched word
|
||||
const trimmedTag = match[0].trim();
|
||||
const leadingSpaces = match[0].length - match[0].trimStart().length;
|
||||
const adjustedStart = wordStart + leadingSpaces;
|
||||
const adjustedEnd = wordStart + leadingSpaces + trimmedTag.length;
|
||||
|
||||
result.push({
|
||||
start: adjustedStart,
|
||||
end: adjustedEnd,
|
||||
tag: trimmedTag
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a wildcard selection and returns individual tags.
|
||||
* Supports syntax like {tag1|tag2|tag3} and {weight::tag1|weight::tag2}.
|
||||
* Also handles nested wildcards like {tag1 {tag2|tag3}|tag4}.
|
||||
* @param {string} tag The complete tag text that might contain a wildcard
|
||||
* @param {number} startPos The starting position of the tag in the original text
|
||||
* @param {number} endPos The ending position of the tag in the original text
|
||||
* @returns {Array<{start: number, end: number, tag: string}>} Array of parsed tags or null
|
||||
*/
|
||||
function parseWildcardSelection(tag, startPos, endPos) {
|
||||
// Trim the tag for matching but keep original position
|
||||
const trimmedTag = tag.trim();
|
||||
|
||||
// Check if this is a wildcard selection
|
||||
if (!trimmedTag.startsWith('{') || !trimmedTag.endsWith('}')) {
|
||||
return null; // Not a wildcard
|
||||
}
|
||||
|
||||
// Calculate position offsets for the trim operation
|
||||
const leadingSpaces = tag.length - tag.trimStart().length;
|
||||
const tagStart = startPos + leadingSpaces;
|
||||
|
||||
// For nested wildcards, we'll extract all words from the content
|
||||
// This treats each word as a separate tag, regardless of nesting
|
||||
// Extract the content between the outermost braces
|
||||
const wildcardContent = trimmedTag.substring(1, trimmedTag.length - 1);
|
||||
|
||||
// Extract all words from the wildcard content, including those in nested structures
|
||||
const allTags = extractAllWords(wildcardContent, tagStart + 1);
|
||||
|
||||
return allTags.length > 0 ? allTags : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all tag positions in the given text.
|
||||
* Searches for tags separated by commas or newlines.
|
||||
* Also handles wildcard selections in the format {tag1|tag2|tag3}.
|
||||
* @param {string} text The text to search in
|
||||
* @returns {Array<{start: number, end: number, tag: string}>} Array of tag positions and content
|
||||
*/
|
||||
export function findAllTagPositions(text) {
|
||||
if (!text) return [];
|
||||
|
||||
const positions = [];
|
||||
let startPos = 0;
|
||||
|
||||
|
||||
// Process text segment by segment (comma or newline separated)
|
||||
while (startPos < text.length) {
|
||||
// Skip any leading whitespace, commas, or newlines
|
||||
while (startPos < text.length &&
|
||||
@@ -206,14 +305,26 @@ export function findAllTagPositions(text) {
|
||||
if (endPosNewline === -1) endPosNewline = text.length;
|
||||
|
||||
const endPos = Math.min(endPosComma, endPosNewline);
|
||||
const tag = text.substring(startPos, endPos);
|
||||
const tagText = text.substring(startPos, endPos);
|
||||
|
||||
if (tag.trim().length > 0) {
|
||||
positions.push({
|
||||
start: startPos,
|
||||
end: endPos,
|
||||
tag: tag
|
||||
});
|
||||
if (tagText.trim().length > 0) {
|
||||
const trimmedTag = tagText.trim();
|
||||
|
||||
// Check if this is a wildcard selection
|
||||
if (trimmedTag.startsWith('{') && trimmedTag.endsWith('}')) {
|
||||
// Process wildcard using our existing wildcard parser
|
||||
const wildcardTags = parseWildcardSelection(tagText, startPos, endPos);
|
||||
if (wildcardTags) {
|
||||
positions.push(...wildcardTags);
|
||||
}
|
||||
} else {
|
||||
// Normal tag, add it directly
|
||||
positions.push({
|
||||
start: startPos,
|
||||
end: endPos,
|
||||
tag: tagText
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next tag
|
||||
@@ -222,6 +333,7 @@ export function findAllTagPositions(text) {
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts existing tags from the textarea with search normalization (possibly duplicated).
|
||||
* @param {HTMLTextAreaElement} textarea The textarea element to extract tags from
|
||||
|
||||
Reference in New Issue
Block a user