-
Notifications
You must be signed in to change notification settings - Fork 29.9k
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
lib: add console.{group,groupCollapsed,groupEnd}() #1727
Conversation
Implements nested console groups to match browser API: * `console.group()` * `console.groupCollapsed()` * `console.groupEnd()` Note: `console.groupCollapsed()` is functionally equivalent to `console.group()` but is included for compatibility.
Referencing #1716 |
@@ -33,7 +35,8 @@ function Console(stdout, stderr) { | |||
} | |||
|
|||
Console.prototype.log = function() { | |||
this._stdout.write(util.format.apply(this, arguments) + '\n'); | |||
this._stdout.write(util.format.apply(this, arguments).replace(/^/gm, | |||
Array(this._group * 2 + 1).join(' ')) + '\n'); |
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 about ' '.repeat(this._group * 2)
?
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 just wanted to keep console
as lightweight and performant as possible.
String.prototype.repeat
is bit slower than Array.join
method I chose.
See http://jsperf.com/faster-string-repeat/15
I do agree that ' '.repeat()
is more readable in code.
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 you want performance, define this._group
as string.
in group
function do this._group += ' '
in this function do:
var data = util.format.apply(this, arguments);
if (this._group.length) data = data.replace(/^/gm, this._group);
this._stdout.write(data + '\n');
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.
Please add a check that grouping is used before allocating all these arrays and strings
I also think it's better to just write the indentation, instead of concatenating it and then writing it 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.
This jsperf is using a shim for String.prototype.repeat. Native is faster
Anyway both show that Array#join is the slowest of all
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.
Thank you for reviewing and providing input. I tried to be minimal and not introduce lot of new code and variables to core lib.
I'll update the PR soon based on your comments.
@3y3 For tracking multiple levels of grouping I'd still keep numeric _group
but generate a fixed prefix string which would be conditionally concatenated as @petkaantonov suggested. This adds variables and code, but I think it pleases everyone?
How about using extended utf characters here? console.group('Test group level 1');
console.log(1);
console.group('Test group level 2');
console.log(2);
console.log(3);
console.log({a: 1, b: 2});
console.groupEnd();
console.log(4);
console.groupEnd();
it is easier to read |
I hope to keep this PR simple and just add the base functions for console grouping with minimal output formatting. My personal opinion is that introducing extended UTF chars and lot of additional formatting can be done if core team so accepts, but I wouldn't push it here in this PR. There is also risk that it will break something that depends on simplified console output. Also I'd keep the new |
Does this affect console.log that could be called from somewhere elsewhere while
I'm really not sure that is what we are or want to be doing, as exists in discussion surrounding things like workers, url, etc. |
I'm also not too fond of using unicode in terminals. A lot of terminals still have trouble with that. |
I agree about terminals. But I also say, that |
I've now adjusted the PR according to input received. Still tried to keep modifications to
Thank you for your input. |
@3y3 Your point is valid, but do understand that I tried to keep prefix generation consistent in |
@3y3 I would argue against unnecessary string concatenation (that you use in your code), even so minor. But that doesn't matter in this case, the performance effect of this is negligable. |
This is not only for performance, but also for readability. |
With this PR, I'm at all cost avoiding any kind of rewrite of Sure, I'd like to rewrite the console and add all bells and whistles right away, but I'm almost sure that it would never be accepted to Node core lib from single pull-request (or even two). So, if you want more this and that; more descriptive, but longer, code; I can surely offer such modifications, but I personally think that it will not be what lib maintainers want to bring to Stability level 2 core lib. I'm just trying to keep this minimal :) |
Keeping the variable/line count minimal improves the readability better. |
Console.prototype.groupEnd = function() { | ||
if ( this._group > 0 ) { | ||
this._group--; | ||
this._prefix = (this._group ? ' '.repeat(this._group) : ''); |
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.
Is a special case really needed here? ' '.repeat(0) === ''
.
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.
Tried to avoid extra function call and use single ''
const that optimizes better than generating a new one for each groupEnd
.
If you feel this complicates code too much, this conditional can be removed as you suggested.
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 think this is right place for this line:
if (_group` == 0) //do nothing
else //modify something.
And about:
this._prefix = (this._group ? ' '.repeat(this._group) : '');
generate some levels of indentation
or
this._prefix = this._prefix.slice(0, -2);
trim two characters from indentation level
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 sorry I missed that where cheched
this._group ? ' '.repeat(this._group) : ''
so if rewrite it with if statement:
if (this._group) ' '.repeat(this._group)
else {
// here is unreachable code
}
I agree with @ChALkeR
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.
@3y3 So to summarize:
- You want to get rid of the
( a ? b : c )
notation and replace withif else
structures - Use
+= ' '
concatenation ingroup()
to modify_prefix
string - Use
String.slice
ingroupEnd()
to modify_prefix
string
You have a valid approach, but here I did not choose that. Lets give the maintainers some time to comment on this and give us direction :)
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.
Propose this as final solution for this block:
Console.prototype.groupEnd = function() {
if (this._group) {
this._group--;
this._prefix = this._prefix.slice(0, -2);
}
}
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 want to get rid of the ( a ? b : c ) notation and replace with if else structures
No I only say, that your ( a ? b : c )
notation has unreachable part.
Look at my example.
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 I only say, that your ( a ? b : c ) notation has unreachable part.
Actually, It does not have unreachable part, but I do see an extra > 0
comparison that could be left out completely for optimization. Can you please define what is the unreachable part you see.
(this._group ? ' '.repeat(this._group) : '');
evaluates to ''
empty string const only if _group
has just been decreased to zero. Otherwise it just regenerates the _prefix
with new string. It does not use slice
, but it matches the code group
uses for generating the prefix.
I'm just trying to avoid avoiding unnecessary string concatenations and multiple methods in functions to generate the prefix. I chose String.prototype.repeat
instead of string concatenation and String.prototype.slice
.
I'm not sure we want to slavishly re-implement all of the browser |
Thanks @rvagg This is the kind of input and discussion the pull-request needs 👍 I'm not qualified to review if the feature is right for the core lib, but I've offered a minimalistic implementation here as a base for discussion. |
As I see it, we're on the same boat as browsers when it comes to |
It's not true for |
But I guess no one would use it if it doesn't work without the inspector attached 😉 |
This is not strictly correct: we have npm, browsers have no means to easily extend tooling like this so usually just bundle all the things which we explicitly don't do. Want a fancier devtools environment? Use node-inspector, replpad, whatever. Want more fanciness in your logging? |
I'm just saying that JavaScript needs a Standard Library; and if there ever is going to be one, Node needs to ship with it.
It just might be so that Node should not be chasing the others and this PR deserves to be buried. .. but that and the general policies about what to include and what not are up to the maintainers of Node to decide. |
It definitely doesn't need it, there are plenty of people who advocate a smaller core both in core and outside. I'll pull up some articles / talks on why when I am able to, however there is a general goal in core to keep core lean and empower users d solutions; so far, it has worked very well. |
All good points. Trust me, I'm one of the outside proponents of keeping the core lean. Lean core, good package management and a solid platform agnostic Standard Library would be great combo for the JS. My main point was that we have the first two, but not the third. Here however, core has already chosen to implement Yet, if adding features to the console is not desired going forward, one might ask why include |
Most well-written modules that don't depend on platform-api work in both browser and node nowadays, and I think reusable code is clearly the future where modules should be going. My point in this case is |
I totally agree that we should get closer to browser API. my opinion and current my concern is to spread the friction between node/io.js API and browser API ... |
Implements nested console groups to match browser console API:
console.group()
console.groupCollapsed()
console.groupEnd()
Notes:
console
API documentation is updated to include new functions.test-console-group.js
is included.console.groupCollapsed()
is aliased toconsole.group()
with a wrapper function so that external gui debuggers can handle it correctly. For examplenode-inspector
.