New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Take into account wordboundaries for custom highlighting #1358
Conversation
client/js/options.js
Outdated
@@ -1,6 +1,7 @@ | |||
"use strict"; | |||
|
|||
const $ = require("jquery"); | |||
const _ = require("lodash"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You just included a very huge library (we don't use lodash on client)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you suggest to fix that? Duplicate the functionality? Or can I require("lodash.escaperegexp") ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If requiring lodash.escaperegexp
does not bring half of lodash with it, then that can work, otherwise just copy the function (it's a oneliner anyway).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well if I want to require lodash.escaperegexp I need to install it and add it to packages.json as a dependency. Still okay? Or do you prefer copy pasting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tried lodash/escaperegexp
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I suspected something like that should exist, was looking for it. Thanks will do that.
client/js/options.js
Outdated
@@ -91,6 +93,9 @@ settings.on("change", "input, select, textarea", function() { | |||
// otherwise, users get notifications for everything | |||
return h !== ""; | |||
}); | |||
options.highlightsRE = options.highlights.map(function (h) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be compiled into a single regex like \b(?:one|two|three)\b
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do
client/js/options.js
Outdated
@@ -26,6 +27,7 @@ const options = $.extend({ | |||
thumbnails: true, | |||
userStyles: userStyles.text(), | |||
highlights: [], | |||
highlightsRE: [], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be an empty regex or something because options.highlightsRE.exec
may fail.
client/js/options.js
Outdated
var highlightsTokens = options.highlights.map(function(h) { | ||
return escapeRegExp(h); | ||
}); | ||
options.highlightsRE = new RegExp("\\b(?:" + highlightsTokens.join("|") + ")\\b", "i"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will this do if highlightsTokens
is empty (e.g. when clearing out highlights in settings)
Thanks for the feedback. Shouldn't commit stuff on fridays... Will fix. |
What's the status of this PR? @starquake, is it ready for review? @xPaw, what do you think of the feature change overall? |
@astorije No, I need to fix the things you mentioned. But suddenly I've been swamped with some other stuff. Will pick this up as soon when it all dies down a bit. |
I have incorporated your comments. |
@@ -1,6 +1,7 @@ | |||
"use strict"; | |||
|
|||
const $ = require("jquery"); | |||
const escapeRegExp = require("lodash/escapeRegExp"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before we merge, gotta test if this actually only brings the relevant function, or half of lodash with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no require:
➜ lounge git:(highlight-wordboundary) ✗ npm run-script build
➜ lounge git:(highlight-wordboundary) ✗ du client/js/bundle*.js
360 client/js/bundle.js
2496 client/js/bundle.vendor.js
const escapeRegExp = require("lodash/escapeRegExp");
➜ lounge git:(highlight-wordboundary) ✗ npm run-script build
➜ lounge git:(highlight-wordboundary) ✗ du client/js/bundle*.js
360 client/js/bundle.js
2496 client/js/bundle.vendor.js
const escapeRegExp = require("lodash").escapeRegExp;
➜ lounge git:(highlight-wordboundary) ✗ npm run-script build
➜ lounge git:(highlight-wordboundary) ✗ du client/js/bundle*.js
360 client/js/bundle.js
3552 client/js/bundle.vendor.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It adds nothing to the size because client/js/libs/handlebars/ircmessageparser/findChannels.js already has a reference to it.
client/js/options.js
Outdated
@@ -91,6 +93,13 @@ settings.on("change", "input, select, textarea", function() { | |||
// otherwise, users get notifications for everything | |||
return h !== ""; | |||
}); | |||
// Construct regex with wordboundary for every highlight item | |||
var highlightsTokens = options.highlights.map(function(h) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's already a map
with trim
in it, you could probably throw escapeRegExp
into it instead of iterating twice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean something like this?
options.highlights = highlightString.split(",").map(function(h) {
return escapeRegExp(h.trim());
})
Cause that would mean the contents of options.highlight will be escaped. I was afraid that might break other stuff that expected it to not be escaped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh you are right, nevermind!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry to be a pain for nitpicks like this, but could you make this a bit more ES6-like, please?
const highlightsTokens = options.highlights.map((h) => escapeRegExp(h));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@astorije No problem, it's nice to be on the other side for once
client/js/options.js
Outdated
}); | ||
if (highlightsTokens && highlightsTokens.length) { | ||
options.highlightsRE = new RegExp("\\b(?:" + highlightsTokens.join("|") + ")\\b", "i"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs an else
statement to reset highlightsRE
to null
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
client/js/render.js
Outdated
// See if any of the custom highlight regexes match | ||
if (!data.msg.highlight && !data.msg.self | ||
&& (type === "message" || type === "notice") | ||
&& options.highlightsRE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move this higher to avoid type string checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean the order of the checks, or the entire if statement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Order of checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like this?
if (!data.msg.highlight && !data.msg.self
&& options.highlightsRE
&& (type === "message" || type === "notice")
&& options.highlightsRE.exec(data.msg.text)) {
data.msg.highlight = true;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are string checks more expensive then checking if something is true? Or do you have some other reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It sure is cheaper to check a boolean. In this particular case it's an easy change, so no reason not to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But options.highlightsRE is not a boolean. It's a regex. Or is that irrelevant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's only checked against not being undefined, so that's not a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Incorporated your comments! |
client/js/options.js
Outdated
@@ -15,6 +16,7 @@ const options = { | |||
coloredNicks: true, | |||
desktopNotifications: false, | |||
highlights: [], | |||
highlightsRE: null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually want to store this as part of localStorage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ @xPaw
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I guess it has to be assigned directly to module.exports
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@starquake, would you mind doing that? :) Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@astorije Will do!
@starquake, could you add a few examples to your PR description to mention what this detect / doesn't detect, compared to before your PR? Thanks! |
@astorije Added some examples. |
@astorije Incorporated your comments. I'm not that familiar with module.exports so I hope this is okay like this. Let me know! |
return escapeRegExp(h); | ||
}); | ||
if (highlightsTokens && highlightsTokens.length) { | ||
module.exports.highlightsRE = new RegExp("\\b(?:" + highlightsTokens.join("|") + ")\\b", "i"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if referencing module.exports here is okay. If not, let me know!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if doing module.exports
puts it into localStorage regardless, as it may be referencing the same object..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a great point, it probably does. I was wondering why shouldOpenMessagePreview
would not be stored somehow, but it turns out functions are not serialized by JSON.stringify
:
> JSON.stringify({ foo: 'bar', test() { return 'foo'; }, regex: new RegExp("test") })
'{"foo":"bar","regex":{}}'
Kinda sucks that we do module.exports = options;
but then also use this file to do other things than just dealing with options
directly.
@xPaw, what do you think?
I can think of a few ideas: wrapping options
in a property module.exports.whatever = options;
(requires editing of every place that has require("[...]/options")
, creating a optionsHelper.js
file, making highlightsRE
a function instead as they're not serialized (that would only delay the issue instead of fixing it though), but none of those things are ideal. Open to ideas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exporting under a separate object is probably the cleanest way of doing it. Or making a getOption
function.
I have also found other unintentional variable that would be stored: https://github.com/thelounge/lounge/blob/0b85582744be6671c96c185866171798af4eead9/client/js/sorting.js#L33
This pull request fixes a bug. Shouldn't the other problem be handled in a separate pull request. This problem existed before this PR and could maybe be allowed to still exist after it? |
@starquake, sure, but that means this PR is blocked until this other bug gets fixed. We don't want to pollute people's localStorage :/ |
@astorije Well in that case I think this PR should be blocked until the issue is fixed. I mean, I would be fine with that. |
@astorije @starquake I don't think we need to block this PR for the fix, as the regex won't end up being saved as stringifying a regex into json does nothing. |
Oooh so I got lucky! |
Okay, but let's not pollute localStorage any further then. As in since this PR is ready, that's fine, but let's fix the underlying issue before potentially adding another one :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job, @starquake!
Take into account wordboundaries for custom highlighting
Hey @starquake, we have sticker packs for our contributors now! |
Fixes #719
When you have
apple
as a keyword is does highlight:But it doesn't highlight:
Or if it's
sq
in my case it does highlight:But it doesn't highlight: