0

I am writing a PHP Syntax Highlighter code in JavaScript. Here is what I have tried:

<html>

<head>

    <title>Untitled 1</title>

<script>
function showContents(text) {
    var string = /\"[a-z0-9A-Z!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]*\"/i;
    var aVariable = /\$([a-zA-Z0-9_\x7f-\xff]*)/i;
    var output = text;
    if(aVariable.test(output)==true)
    {
        output = output.replace(aVariable.exec(output)[0],"<font color='blue'>"+aVariable.exec(output)[0]+"</font>");
        document.getElementById("UserInput").innerHTML = output;
    }
    if(string.test(output)==true)
    {
        output = output.replace(string.exec(output)[0],"<font color='red'>"+string.exec(output)[0]+"</font>");
        document.getElementById("UserInput").innerHTML = output;
    }
    else
    {
        document.getElementById("UserInput").innerHTML = output;
    }
    document.write(output);
}
</script>
</head>

<body>
<div id="UserInput"></div>
<form>
<textarea onkeyup="showContents(this.value)"></textarea></form>

</body>
</html>

The above script is working fine for inputs like:

$name="ABC"

However, if I try inputs like:

$name="ABC";$name2="CDE"

The code is highlighting only the first instance (ie $name="ABC"). Changing the modifier to global is not giving output itself.

Dylan Wheeler
  • 6,928
  • 14
  • 56
  • 80
  • Once you do the first replace, you have a HTML string, not a plain text string. And then [it's no longer possible to use regex to parse it](http://stackoverflow.com/a/1732454/1529630) in order to do more replaces. You need a proper parser, not regex. – Oriol Jul 14 '16 at 18:05

1 Answers1

0

The problem is that with aVariable.exec(output)[0] you only return the first match, and pass that to the replace method for replacement. As this code is only executed once, a second match is never processed.

It is better to pass regular expressions to replace directly and not the result of exec (which is an array of string). Also you could benefit from the possibility to pass a callback function to replace so you can execute code on each match for wrapping the font tag around it.

Here is a working snippet:

function htmlEncode(text) {
    // Uses the DOM to correctly escape HTML special characters (e.g. ampersand, less-than) 
    var elm = document.createElement('span');
    elm.textContent = text;
    return elm.innerHTML;
}

function formatContents(text) {
    // Use one regexp for all, and modify the matches with a call back function:
    return text.replace(/(\"(\\"|.)*?\")|(\$([\w\x7f-\xff]*))/g, function (match, str, vari) {
        var color = str ? 'red' : 'blue';
        return "<font color='" + color + "'>" + htmlEncode(match) + "</font>";
    });
}

function showContents() {
    // Take contents from textarea and display formatted in UserInput div:
    document.getElementById("UserInput").innerHTML = 
        formatContents(document.getElementById("input").value);
}

document.getElementById("input").oninput = showContents;
// Apply upon page load
showContents();
The formatting is done as you type:<br>
<textarea id="input" style="width:100%">$name="ABC";
$name2="CDE"</textarea>
<div id="UserInput" style="white-space: pre"></div>

I changed the "string" regular expression a bit, as it is easier to pass a negative list of characters than a positive one.

The advantage of doing the replace in regular expression, is that once you have a match, that substring will not be matched again for something else (a variable is never a string literal, and vice versa).

trincot
  • 317,000
  • 35
  • 244
  • 286