-
Notifications
You must be signed in to change notification settings - Fork 5
Introduce word count plugin #2
Conversation
<div class="customized-counter"> | ||
<div class="customized-counter__words"> | ||
<label>Words: | ||
<progress value="48" max='100'></progress> |
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.
s/'/"
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.
s/48/42
I add PR with modification of mgit.json. |
src/wordcount.js
Outdated
init() { | ||
const editor = this.editor; | ||
|
||
editor.model.document.on( 'change', throttle( this._calcWordsAndCharacters.bind( this ), 250 ) ); |
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.
Maybe we could listen to change:data
events only?
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 must be a change:data
- there's no need to updated word count on selection changes.
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.
The plugin works nicely, I had some comments regarding wording and some code improvements.
I've checked how fast is the word counting and it looks pretty OK:
However, I found some issues regarding specs:
- There's
update
event instead ofonUpdate
callback (for me it's fine). Also, we have observable properties. - I'm not sure if the
myContainer
is implemented the way it was intended. Should the WordCount plugin accept the existing container to be passed to the plugin and to be used instead of creating it internally? Of course only if thewordCount.container
is defined. - The
Words: NNN. Characters: NNN
requirement is done as to blocks - not as inline elements so it displays:Words: NNN. Characters: NNN
@Reinmar could you check those two requirements above?
src/utils.js
Outdated
* | ||
* **Note:** Function walks through the entire model. There should be considered throttling during usage. | ||
* | ||
* @param {module:engine/model/node~Node} node |
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.
wrong param: should be Element
- the Node
doesn't have `getChildren()
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 actually needs to be Item
because it's also called with Text
and TextProxy
.
But, at the same time, you're completely right that neither of them have getChildren()
. Which means that if we'd use a statically typed language we'd need to typecast here or solve it differently. That's why I wrote in the ticket that you reported that we should wait with such polishing until we use TS.
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 looks so - I wrote that ticket after this review actually. I worry that we can have more such mistype declaration in the docs for Node
/Element
/Item
trio.
src/utils.js
Outdated
|
||
if ( node.is( 'text' ) || node.is( 'textProxy' ) ) { | ||
text += node.data; | ||
} else { |
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'd remove the indentation here: return on if
and remove the else
block. Also you could move let text = ''
after if
then.
The other question is do you consider TreeWalker
here? I don't know if its much overhead (currently parsing a 1M characters (few paragraphs) takes below 30 ms so it is performant enough).
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've made simple code with treewalker and performance dropped significantly.
In my case it was slow down ~50 times, I'm not sure if it's case of my algorithm with tree walker or tree walker itself, however, it seems that walking through children recursively is far more efficient.
const txt = ( editor => {
const position = new Position( editor.model.document.getRoot(), [ 0 ] );
const walker = new TreeWalker( { startPosition: position } );
let endTagIndicator = false;
let outputText = '';
for ( const element of walker ) {
if ( element.typy === 'elementStart' && endTagIndicator ) {
outputText += '\n';
endTagIndicator = false;
} else if ( element.type === 'elementEnd' ) {
endTagIndicator = true;
} else if ( element.type === 'text' ) {
outputText += element.item.data;
}
}
return outputText;
} )( this.editor );
src/wordcount.js
Outdated
init() { | ||
const editor = this.editor; | ||
|
||
editor.model.document.on( 'change', throttle( this._calcWordsAndCharacters.bind( this ), 250 ) ); |
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 must be a change:data
- there's no need to updated word count on selection changes.
src/wordcount.js
Outdated
const children = []; | ||
|
||
if ( displayWords || displayWords === undefined ) { | ||
const wordsLabel = t( 'Words' ) + ':'; |
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.
Translate full message: t( 'Words: %0', [ words ] )
.
src/wordcount.js
Outdated
} | ||
|
||
if ( displayCharacters || displayCharacters === undefined ) { | ||
const charactersLabel = t( 'Characters' ) + ':'; |
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.
same as above ☝️ full message to translate.
src/wordcount.js
Outdated
} | ||
|
||
/** | ||
* Event is fired after {@link #words} and {@link #characters} are updated. |
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.
* Event is fired after {@link #words} and {@link #characters} are updated. | |
* Event fired after {@link #words} and {@link #characters} are updated. |
src/wordcount.js
Outdated
*/ | ||
|
||
/** | ||
* This option allows on hiding the word counter. The element obtained through |
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 option allows on hiding the word counter. The element obtained through | |
* This option allows for hiding the word counter. The element obtained through |
src/wordcount.js
Outdated
* The mentioned configuration will result with the followed container: | ||
* | ||
* <div class="ck ck-word-count"> | ||
* <div>Characters: 28</div> |
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.
Remember to update the examples code when adding classes to it.
src/wordcount.js
Outdated
*/ | ||
|
||
/** | ||
* This option allows on hiding the character counter. The element obtained through |
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 option allows on hiding the character counter. The element obtained through | |
* This option allows for hiding the character counter. The element obtained through |
src/wordcount.js
Outdated
* displayCharacters = false | ||
* } | ||
* | ||
* The mentioned configuration will result with the followed container: |
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.
* The mentioned configuration will result with the followed container: | |
* The mentioned configuration will result in the following container |
The point of |
For the samples in the docs: I'd change the update event sample so the progress bar has also NNN of 1000 words (or whatever is the number and the character box also has meaningful information. Maybe transition blue > yellow > red would be sufficient? |
As shown in the code sample in https://github.com/ckeditor/ckeditor5-word-count/issues/1 both should work. Either you pass the container via |
No strong opinion here. The styles can be changed, but it'd be good to default to a nice clean markup by default (I guess a bunch of spans with nice BEM classes). CC @oleq @dkonopka |
Thanks! So @msamsel so the points 1-2 should be targeted. For the 3 I've already proposed adding BEM classes to those containers and the only remaining thing is how to structure them ( |
Did you check how efficient it is for a document with 20+ A4 pages? 50+? |
I've already proposed to have the whole label translatable so it would land in one element. The idea behind that was to translate: |
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.
@msamsel Some polishing to do.
tests/wordcount.js
Outdated
it( 'has "WordCount" plugin name', () => { | ||
expect( WordCount.pluginName ).to.equal( 'WordCount' ); | ||
} ); | ||
|
||
it( 'has define "_config" 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.
I don't think we need to check for private properties.
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 removed this test
@Reinmar the issues were addressed :) I've created one follow-up: https://github.com/ckeditor/ckeditor5-word-count/issues/5 - but it might be done later together with #4. Also the idea of breaking up the |
src/wordcount.js
Outdated
* | ||
* @returns {HTMLElement} | ||
*/ | ||
getWordCountContainer() { |
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.
Couldn't this be a getter? It was proposed as a property in the spec: https://github.com/ckeditor/ckeditor5-word-count/issues/1.
Co-Authored-By: Piotrek Koszuliński <[email protected]>
* @returns {String} Plain text representing model's data | ||
*/ | ||
export function modelElementToPlainText( element ) { | ||
if ( element.is( 'text' ) || element.is( 'textProxy' ) ) { |
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.
According to the docs this isn't allowed. The element is an element. Please report a followup ticket to clean this up.
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.
My other problem with this is that we already have viewItemToPlainText
in ckeditor5-clipboard. Ideally, those functions should be kept together, but I don't know where would it be. Engine?
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'd go with Engine
. Ps. this method would be suitable also for other features (AFAIR: Mention
or TextWatcher
in particular and maybe the Autoformat
- the latter should use TextWatcher
API 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.
However this method might soon be changed to follow requirements from #5.
Suggested merge commit message (convention)
Feature: Introduce word count feature. Closes ckeditor/ckeditor5#2571. Closes ckeditor/ckeditor5#1301.
Additional information
Related PR: ckeditor/ckeditor5#1835