From 256c3e12c1e8d72d1fae62431271c42d624ebe00 Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Mon, 5 Oct 2015 23:00:25 +0200 Subject: [PATCH] Fix regression in Bash strings In a recent patch the regex for single-quoted and double-quoted strings of the Bash language was split into two separate regexes to prevent the highlighting of variables inside of single-quoted strings. This causes a bug whenever a double-quoted string apears inside a single-quoted string, because the double-quoted string is matched first. '"foo"' The same problem exists with the newly introduced Here-Documents. This patch fixes the problem by matching Here-Documents first and merging the regexes for single-quoted and double-quoted strings again. This patch also adds testcases for this, to prevent future regressions. --- components/prism-bash.js | 10 ++++----- components/prism-bash.min.js | 2 +- tests/languages/bash/string_feature.test | 26 ++++++++++++++++++++---- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/components/prism-bash.js b/components/prism-bash.js index 64b42f6ce2..6277fc5e02 100644 --- a/components/prism-bash.js +++ b/components/prism-bash.js @@ -12,16 +12,14 @@ lookbehind: true }, 'string': [ + //Support for Here-Documents https://en.wikipedia.org/wiki/Here_document { - pattern: /"(?:\\?[\s\S])*?"/g, + pattern: /((?:^|[^<])<<\s*)(?:"|')?(\w+?)(?:"|')?\s*\r?\n(?:[\s\S])*?\r?\n\2/g, + lookbehind: true, inside: bashVars }, - // Single quote strings cannot have variables inside - /'(?:\\?[\s\S])*?'/g, - // Support for Here-Documents https://en.wikipedia.org/wiki/Here_document { - pattern: /(<<\s*)(\w+?)\s*\r?\n(?:[\s\S])*?\r?\n\2/g, - lookbehind: true, + pattern: /("|')(?:\\?[\s\S])*?\1/g, inside: bashVars } ], diff --git a/components/prism-bash.min.js b/components/prism-bash.min.js index f9902e0a51..ffcd48c48e 100644 --- a/components/prism-bash.min.js +++ b/components/prism-bash.min.js @@ -1 +1 @@ -!function(e){var t=/\$(?:[a-z0-9_#\?\-\*!@]+|\{[^}]+\})/i,o={variable:t};e.languages.bash={shebang:{pattern:/^#!\s*\/bin\/bash|^#!\s*\/bin\/sh/,alias:"important"},comment:{pattern:/(^|[^"{\\])#.*/,lookbehind:!0},string:[{pattern:/"(?:\\?[\s\S])*?"/g,inside:o},/'(?:\\?[\s\S])*?'/g,{pattern:/(<<\s*)(\w+?)\s*\r?\n(?:[\s\S])*?\r?\n\2/g,lookbehind:!0,inside:o}],number:{pattern:/([^\w\.])-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/,lookbehind:!0},variable:t,"function":/\b(?:alias|apropos|apt-get|aptitude|aspell|awk|basename|bash|bc|bg|builtin|bzip2|cal|cat|cd|cfdisk|chgrp|chmod|chown|chroot|chkconfig|cksum|clear|cmp|comm|command|cp|cron|crontab|csplit|cut|date|dc|dd|ddrescue|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|egrep|eject|enable|env|ethtool|eval|exec|expand|expect|export|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|getopts|git|grep|groupadd|groupdel|groupmod|groups|gzip|hash|head|help|hg|history|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|jobs|join|kill|killall|less|link|ln|locate|logname|logout|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|make|man|mkdir|mkfifo|mkisofs|mknod|more|most|mount|mtools|mtr|mv|mmv|nano|netstat|nice|nl|nohup|notify-send|nslookup|open|op|passwd|paste|pathchk|ping|pkill|popd|pr|printcap|printenv|printf|ps|pushd|pv|pwd|quota|quotacheck|quotactl|ram|rar|rcp|read|readarray|readonly|reboot|rename|renice|remsync|rev|rm|rmdir|rsync|screen|scp|sdiff|sed|seq|service|sftp|shift|shopt|shutdown|sleep|slocate|sort|source|split|ssh|stat|strace|su|sudo|sum|suspend|sync|tail|tar|tee|test|time|timeout|times|touch|top|traceroute|trap|tr|tsort|tty|type|ulimit|umask|umount|unalias|uname|unexpand|uniq|units|unrar|unshar|uptime|useradd|userdel|usermod|users|uuencode|uudecode|v|vdir|vi|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yes|zip)\b/,keyword:/\b(let|if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)\b/,"boolean":/\b(?:true|false)\b/,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}}(Prism); \ No newline at end of file +!function(e){var t=/\$(?:[a-z0-9_#\?\-\*!@]+|\{[^}]+\})/i,o={variable:t};e.languages.bash={shebang:{pattern:/^#!\s*\/bin\/bash|^#!\s*\/bin\/sh/,alias:"important"},comment:{pattern:/(^|[^"{\\])#.*/,lookbehind:!0},string:[{pattern:/((?:^|[^<])<<\s*)(?:"|')?(\w+?)(?:"|')?\s*\r?\n(?:[\s\S])*?\r?\n\2/g,lookbehind:!0,inside:o},{pattern:/("|')(?:\\?[\s\S])*?\1/g,inside:o}],number:{pattern:/([^\w\.])-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/,lookbehind:!0},variable:t,"function":/\b(?:alias|apropos|apt-get|aptitude|aspell|awk|basename|bash|bc|bg|builtin|bzip2|cal|cat|cd|cfdisk|chgrp|chmod|chown|chroot|chkconfig|cksum|clear|cmp|comm|command|cp|cron|crontab|csplit|cut|date|dc|dd|ddrescue|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|egrep|eject|enable|env|ethtool|eval|exec|expand|expect|export|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|getopts|git|grep|groupadd|groupdel|groupmod|groups|gzip|hash|head|help|hg|history|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|jobs|join|kill|killall|less|link|ln|locate|logname|logout|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|make|man|mkdir|mkfifo|mkisofs|mknod|more|most|mount|mtools|mtr|mv|mmv|nano|netstat|nice|nl|nohup|notify-send|nslookup|open|op|passwd|paste|pathchk|ping|pkill|popd|pr|printcap|printenv|printf|ps|pushd|pv|pwd|quota|quotacheck|quotactl|ram|rar|rcp|read|readarray|readonly|reboot|rename|renice|remsync|rev|rm|rmdir|rsync|screen|scp|sdiff|sed|seq|service|sftp|shift|shopt|shutdown|sleep|slocate|sort|source|split|ssh|stat|strace|su|sudo|sum|suspend|sync|tail|tar|tee|test|time|timeout|times|touch|top|traceroute|trap|tr|tsort|tty|type|ulimit|umask|umount|unalias|uname|unexpand|uniq|units|unrar|unshar|uptime|useradd|userdel|usermod|users|uuencode|uudecode|v|vdir|vi|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yes|zip)\b/,keyword:/\b(let|if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)\b/,"boolean":/\b(?:true|false)\b/,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}}(Prism); \ No newline at end of file diff --git a/tests/languages/bash/string_feature.test b/tests/languages/bash/string_feature.test index 343ccc4c78..29a10eb56b 100644 --- a/tests/languages/bash/string_feature.test +++ b/tests/languages/bash/string_feature.test @@ -6,6 +6,8 @@ bar" 'foo bar' +"'foo'" +'"bar"' "$@" "${foo}" << STRING_END @@ -16,16 +18,26 @@ STRING_END foo $@ bar EOF +<< 'EOF' +'single quoted string' +"double quoted string" +EOF +<< "EOF" +foo +bar +EOF ---------------------------------------------------- [ ["string", ["\"\""]], - ["string", "''"], + ["string", ["''"]], ["string", ["\"foo\""]], - ["string", "'foo'"], + ["string", ["'foo'"]], ["string", ["\"foo\r\nbar\""]], - ["string", "'foo\r\nbar'"], + ["string", ["'foo\r\nbar'"]], + ["string", ["\"'foo'\""]], + ["string", ["'\"bar\"'"]], ["string", [ "\"", ["variable", "$@"], "\"" ]], @@ -37,7 +49,13 @@ EOF ["string", ["STRING_END\r\nfoo\r\nbar\r\nSTRING_END"]], ["operator", "<"], ["operator", "<"], - ["string", ["EOF\r\nfoo ", ["variable", "$@"], "\r\nbar\r\nEOF"]] + ["string", ["EOF\r\nfoo ", ["variable", "$@"], "\r\nbar\r\nEOF"]], + ["operator", "<"], + ["operator", "<"], + ["string", ["'EOF'\r\n'single quoted string'\r\n\"double quoted string\"\r\nEOF"]], + ["operator", "<"], + ["operator", "<"], + ["string", ["\"EOF\"\r\nfoo\r\nbar\r\nEOF"]] ] ----------------------------------------------------