Suppose there is string wrapped with two * characters (from both starting and ending). The resulting string should be converted in bold text, similarly as when the string is wrapped with two characters _, which should produce an italic string.
My code in React is the following:
import * as React from 'react';
export default function App() {
const [boldText, setBoldText] = React.useState('' as any);
const [res, setRes] = React.useState('' as any);
let speChar: any = '*_~`';
let openingTagsList: any = {
'*': '<b>',
_: '<i>',
'*_': '<b><i>',
'_*': '<b><i>',
};
let closingTagsList: any = {
'*': '</b>',
_: '</i>',
'*_': '</b></i>',
'_*': '</b></i>',
};
let openingTagsListKeys = Object?.keys(openingTagsList);
let closingTagsListKeys = Object?.keys(closingTagsList);
function strFont(e) {
let str = e.target.value;
let matchedSplChar = '';
for (let i = 0; i < str.length; i ) {
if (matchedSplChar.indexOf(str[i]) === -1) {
if (speChar.indexOf(str[i]) !== -1) matchedSplChar = str[i];
}
}
if (matchedSplChar as any) {
let FL = str[str.indexOf(matchedSplChar, 0)];
let SL = str[str.indexOf(matchedSplChar, 1)];
let startingTags;
let closingTags;
for (let key in openingTagsListKeys) {
if (matchedSplChar === openingTagsListKeys[key])
startingTags = openingTagsList[matchedSplChar];
}
for (let key in closingTagsListKeys) {
if (matchedSplChar === closingTagsListKeys[key])
closingTags = closingTagsList[matchedSplChar];
}
if (FL && SL && FL == SL) {
let replaceTags = str
.replace(FL, startingTags)
.replace(SL, closingTags);
let divTag = document.createElement('div');
divTag.innerHTML = replaceTags;
let htmlObj: any = divTag.firstChild;
if (htmlObj.innerHTML) setRes(htmlObj);
setBoldText(e.target.value);
} else {
setBoldText(e.target.value);
}
} else {
setBoldText(e.target.value);
}
}
return (
<div>
<input type="text" value={boldText || ''} onChange={(e) => strFont(e)} />
{res ? <res.tagName>{res?.innerHTML}</res.tagName> : ''}
<TextFormation />
</div>
);
}
, gives the output:
, instead of both strings being bold. How can I achieve it then?
CodePudding user response:
From the above comments ...
"@KavyaPathak ... which effectively means that anything in between two
*
characters is going to be wrapped into , like e.g. ...foo *bar* *baz* *foo
... becoming ...foo <b>bar</b> <b> </b> <b>baz</b> <b> </b> *foo
... which renders ... "foo bar __ baz __ *foo"." – Peter Seliger"@PeterSeliger yes" – Kavya Pathak
In case the OP's confirmation remains, the commented link to a regex and replace
based approach already represents one possible solution.
Both regular expressions ...
... follow the same pattern.
- match a control character (either
*
or_
) ...\*
respectively_
.., - match and capture any character sequence which does not contain such a control character ...
([^*] )
respectively([^_] )
.., - until a positive lookahead ...
(?=\*)
respectively(?=_)
... confirms the presence of the next such control character (which excludes this very character from the entire match).
function getMarkupFromPseudoMarkdown(value) {
return value
.replace(/\*([^*] )(?=\*)/g, '<b>$1</b> ')
.replace(/_([^_] )(?=_)/g, '<i>$1</i> ')
.replace(/\n/g, '<br\/>')
.replace(/\s /g, ' ')
.trim();
}
function displayCreatedMarkup({ currentTarget }) {
const markup = getMarkupFromPseudoMarkdown(currentTarget.value);
document.querySelector('code pre').textContent = markup;
document.querySelector('output').innerHTML = markup;
}
document
.querySelector('textarea')
.addEventListener('input', displayCreatedMarkup)
textarea, output { width: 49%; }
output { float: right; font-size: 87%; margin-top: 2px; }
code pre { background-color: #eee; white-space: break-spaces; word-break: break-all;}
<textarea cols="32" rows="8" placeholder="... put pseudo markdown here ..."></textarea>
<output></output>
<code><pre></pre></code>
And in case the OP figures that the above approach does not solve the OP's problem especially not for some bold/italic edge cases, then the OP might consider a mainly split
and reduce
based approach which handles such edge cases by looking up the previous (matchList[idx - 1]
) and the next (matchList[idx 1]
) (control) character of a matching (neither *
nor _
) token.
function getMarkupFromPseudoMarkdown(value) {
return value
.split(/(\*)/)
.reduce((markup, match, idx, matchList) => {
if (match !== '*') {
if (
matchList[idx - 1] === '*' &&
matchList[idx 1] === '*'
) {
markup = `${ markup } <b>${ match }</b> `;
} else {
markup = `${ markup }${ match }`;
}
}
return markup
})
.split(/(_)/)
.reduce((markup, match, idx, matchList) => {
if (match !== '_') {
if (
matchList[idx - 1] === '_' &&
matchList[idx 1] === '_'
) {
markup = `${ markup } <i>${ match }</i> `;
} else {
markup = `${ markup }${ match }`;
}
}
return markup
})
.replace(/\n/g, '<br\/>')
.replace(/\s /g, ' ')
.trim();
}
function displayCreatedMarkup({ currentTarget }) {
const markup = getMarkupFromPseudoMarkdown(currentTarget.value);
document.querySelector('code pre').textContent = markup;
document.querySelector('output').innerHTML = markup;
}
document
.querySelector('textarea')
.addEventListener('input', displayCreatedMarkup)
textarea, output { width: 49%; }
output { float: right; font-size: 87%; margin-top: 2px; }
code pre { background-color: #eee; white-space: break-spaces; word-break: break-all;}
<textarea cols="32" rows="8" placeholder="... put pseudo markdown here ..."></textarea>
<output></output>
<code><pre></pre></code>