summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/SMAPI.Web/Views/LogParser/Index.cshtml6
-rw-r--r--src/SMAPI.Web/wwwroot/Content/css/log-parser.css7
-rw-r--r--src/SMAPI.Web/wwwroot/Content/js/log-parser.js33
3 files changed, 40 insertions, 6 deletions
diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml
index dfb603f2..5e55906d 100644
--- a/src/SMAPI.Web/Views/LogParser/Index.cshtml
+++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml
@@ -406,6 +406,12 @@ else if (log?.IsValid == true)
<span role="button" v-bind:class="{ active: !filterInsensitive }" v-on:click="toggleFilterInsensitive" title="Match exact capitalization only.">aA</span>
<span role="button" v-bind:class="{ active: filterUseWord, 'whole-word': true }" v-on:click="toggleFilterWord" title="Match whole word only."><i>“ ”</i></span>
<span role="button" v-bind:class="{ active: shouldHighlight }" v-on:click="toggleHighlight" title="Highlight matches in the log text.">HL</span>
+ <div
+ v-if="filterError"
+ class="filter-error"
+ >
+ {{ filterError }}
+ </div>
</div>
<filter-stats
v-bind:start="start"
diff --git a/src/SMAPI.Web/wwwroot/Content/css/log-parser.css b/src/SMAPI.Web/wwwroot/Content/css/log-parser.css
index 41b54e11..1d457e35 100644
--- a/src/SMAPI.Web/wwwroot/Content/css/log-parser.css
+++ b/src/SMAPI.Web/wwwroot/Content/css/log-parser.css
@@ -187,6 +187,11 @@ table caption {
margin-top: 0.5em;
}
+#filters .filter-error {
+ color: #880000;
+}
+
+#filters .filter-error,
#filters .stats {
margin-top: 0.5em;
font-size: 0.75em;
@@ -210,7 +215,7 @@ table caption {
@media (max-width: 1019px) {
#filters:not(.sticky) {
- width: calc(100vw - 3em);
+ width: calc(100vw - 5em);
}
#filters {
diff --git a/src/SMAPI.Web/wwwroot/Content/js/log-parser.js b/src/SMAPI.Web/wwwroot/Content/js/log-parser.js
index c730309b..e19e3301 100644
--- a/src/SMAPI.Web/wwwroot/Content/js/log-parser.js
+++ b/src/SMAPI.Web/wwwroot/Content/js/log-parser.js
@@ -247,7 +247,8 @@ smapi.logParser = function (state) {
// add the properties we're passing to Vue
state.totalMessages = state.messages.length;
state.filterText = "";
- state.filterRegex = "";
+ state.filterRegex = null;
+ state.filterError = null;
state.showContentPacks = true;
state.useHighlight = true;
state.useRegex = false;
@@ -506,6 +507,12 @@ smapi.logParser = function (state) {
}
index = match.index + match[0].length;
+
+ // In the event of a zero-length match, forcibly increment
+ // the last index of the regular expression to ensure we
+ // aren't stuck in an infinite loop.
+ if (match[0].length == 0)
+ filter.lastIndex++;
}
// Add any trailing text after the last match was found.
@@ -857,14 +864,30 @@ smapi.logParser = function (state) {
if (!text || !text.length) {
this.filterText = "";
this.filterRegex = null;
+ this.filterError = null;
}
else {
if (!state.useRegex)
text = helpers.escapeRegex(text);
- this.filterRegex = new RegExp(
- state.useWord ? `\\b${text}\\b` : text,
- state.useInsensitive ? "ig" : "g"
- );
+
+ const flags = state.useInsensitive ? "ig" : "g";
+
+ this.filterError = null;
+ let regex;
+
+ try {
+ regex = new RegExp(text, flags);
+ } catch (err) {
+ regex = null;
+ this.filterError = err.message;
+ }
+
+ if (regex)
+ this.filterRegex = state.useWord ?
+ new RegExp(`\\b(?:${text})\\b`, flags) :
+ regex;
+ else
+ this.filterRegex = null;
}
this.updateUrl();