Home > other >  String between two "*" or "_" characters not getting bold or italic
String between two "*" or "_" characters not getting bold or italic

Time:02-02

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:

enter image description here

, 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>

  • Related