Skip to content
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

Merged
merged 1 commit into from Aug 22, 2017
Merged

Take into account wordboundaries for custom highlighting #1358

merged 1 commit into from Aug 22, 2017

Conversation

starquake
Copy link
Contributor

@starquake starquake commented Jul 21, 2017

Fixes #719

When you have apple as a keyword is does highlight:

Take a look at this magnificent apple!
I wouldn't call that an "apple"
Apple>Microsoft

But it doesn't highlight:

apples applepie grappler

Or if it's sq in my case it does highlight:

Hey sq!

But it doesn't highlight:

That guy is rather grotesque

@@ -1,6 +1,7 @@
"use strict";

const $ = require("jquery");
const _ = require("lodash");
Copy link
Member

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)

Copy link
Contributor Author

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") ?

Copy link
Member

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).

Copy link
Contributor Author

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?

Copy link
Member

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?

Copy link
Contributor Author

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.

@@ -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) {
Copy link
Member

@xPaw xPaw Jul 21, 2017

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do

@xPaw xPaw added the Type: Bug Issues that report and PRs that solve any defects that cause unexpected behaviors. label Jul 21, 2017
@@ -26,6 +27,7 @@ const options = $.extend({
thumbnails: true,
userStyles: userStyles.text(),
highlights: [],
highlightsRE: [],
Copy link
Member

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.

var highlightsTokens = options.highlights.map(function(h) {
return escapeRegExp(h);
});
options.highlightsRE = new RegExp("\\b(?:" + highlightsTokens.join("|") + ")\\b", "i");
Copy link
Member

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)

@starquake
Copy link
Contributor Author

Thanks for the feedback. Shouldn't commit stuff on fridays... Will fix.

@astorije
Copy link
Member

astorije commented Aug 3, 2017

What's the status of this PR? @starquake, is it ready for review? @xPaw, what do you think of the feature change overall?

@starquake
Copy link
Contributor Author

@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.

@starquake
Copy link
Contributor Author

I have incorporated your comments.

@@ -1,6 +1,7 @@
"use strict";

const $ = require("jquery");
const escapeRegExp = require("lodash/escapeRegExp");
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will test

Copy link
Contributor Author

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

Copy link
Contributor Author

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.

@@ -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) {
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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!

Copy link
Member

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));

Copy link
Contributor Author

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

});
if (highlightsTokens && highlightsTokens.length) {
options.highlightsRE = new RegExp("\\b(?:" + highlightsTokens.join("|") + ")\\b", "i");
}
Copy link
Member

@xPaw xPaw Aug 8, 2017

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// See if any of the custom highlight regexes match
if (!data.msg.highlight && !data.msg.self
&& (type === "message" || type === "notice")
&& options.highlightsRE
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Order of checks.

Copy link
Contributor Author

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;
	}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

Copy link
Contributor Author

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?

Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@starquake
Copy link
Contributor Author

Incorporated your comments!

@@ -15,6 +16,7 @@ const options = {
coloredNicks: true,
desktopNotifications: false,
highlights: [],
highlightsRE: null,
Copy link
Member

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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ @xPaw

Copy link
Member

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.

Copy link
Member

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!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@astorije Will do!

@astorije
Copy link
Member

@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!

@starquake
Copy link
Contributor Author

@astorije Added some examples.

@starquake
Copy link
Contributor Author

@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");
Copy link
Contributor Author

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!

Copy link
Member

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

Copy link
Member

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.

Copy link
Member

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

@starquake
Copy link
Contributor Author

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?

@astorije
Copy link
Member

@starquake, sure, but that means this PR is blocked until this other bug gets fixed. We don't want to pollute people's localStorage :/
@xPaw, do you want to take a look at it? Or someone else?

@starquake
Copy link
Contributor Author

@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.

@xPaw
Copy link
Member

xPaw commented Aug 22, 2017

@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.

@starquake
Copy link
Contributor Author

Oooh so I got lucky!

@astorije
Copy link
Member

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 :)

Copy link
Member

@astorije astorije left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job, @starquake!

@astorije astorije merged commit 3b79a3d into thelounge:master Aug 22, 2017
@AlMcKinlay AlMcKinlay added this to the 2.5.0 milestone Aug 22, 2017
matburnham pushed a commit to matburnham/lounge that referenced this pull request Sep 6, 2017
Take into account wordboundaries for custom highlighting
@astorije
Copy link
Member

Hey @starquake, we have sticker packs for our contributors now!
If you're interested, go fill the form at https://goo.gl/forms/f5usqAEp5DWoeXC92 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Bug Issues that report and PRs that solve any defects that cause unexpected behaviors.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants