Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

T/48 #71

Merged
merged 17 commits into from
Mar 3, 2017
Merged

T/48 #71

merged 17 commits into from
Mar 3, 2017

Conversation

f1ames
Copy link
Contributor

@f1ames f1ames commented Feb 13, 2017

Feature: Introduced InputCommand used for typing handling. Closes ckeditor/ckeditor5#3058.

* @protected
* @member {typing.ChangeBuffer} #_buffer
*/
this._buffer = new ChangeBuffer( editor.document, editor.config.get( 'typing.undoStep' ) || 20 );
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 given to the command as an option, not taken by it from the editor. A class like a command should depend on the world in as little details as possible.

src/input.js Outdated
} );
}

/**
* Handles DOM mutations.
*
* @param {Array.<engine.view.Document~MutatatedText|engine.view.Document~MutatatedChildren>} mutations
* @param {Array.<module:engine/view/document~MutatatedText|module:engine/view/document~MutatatedChildren>} mutations
* @param {module:engine/controller/editingcontroller~EditingController} viewSelection
Copy link
Member

Choose a reason for hiding this comment

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

Wrong type.

* @param {String} [options.text=''] Text to be inserted.
* @param {module:engine/model/range~Range} [options.range=null] Range in which the text is inserted. Defaults
* to first range in the current selection.
* @param {module:engine/model/position~Position} [options.selectionAnchor] Selection anchor which will be used
Copy link
Member

Choose a reason for hiding this comment

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

This command should not need to accept the selection anchor (I don't even know how it's used). It should calculate the result selection position based on the given target range to remove and text which it's inserting. At least... I hope that it can be done this way ;>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For simple case it works as you described. But there are cases like e.g. spell checking and probably it was the reason that modelSelectionPosition was introduced at the first place.
If there is text like athat and spell checking is used to replaced it with that, based on mutation there is range like [a]that and inserted text is empty. I am not sure if it is possible to calculate proper selection position based on that (I assume after replacing the position should be after replaced/corrected word)?
I may propose changing the option name to e.g. targetPosition which may be a little more meaningful, unless ofc there is a way to correctly calculate selection in those cases?

Copy link
Member

Choose a reason for hiding this comment

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

If you replace athat with that and according to the engine, the selection should be after "that", then the passes options.range should be [athat] and options.text = 'that'. The input command will remove the whole "athat", insert "that" and place the selection after "that". You don't need options.selectionAnchor for that.

I understand that this may not be fully intuitive at first, but think about this from OT perspective. If you're fixing the word's "athat" spelling you're replacing it with "that". The whole word, not just a single letter. This may be crucial when resolving some conflicting changes and, in general, is more semantical.

Hence, try to get rid of selectionAnchor. I wrote before that I'm not sure if it's doable, though, because I haven't checked input feature's implementation. Why did you implement it in the first place? Was it already strictly required by what the mutation observer produces?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I was relying too much on what information we already have calculated based on mutations.

For case described above the mutation provided by mutation observer is:

newText: "foo that bar"
node: Text
oldText: "foo athat bar"
type: "text"

I assume based only on this information it is not possible to reliable calculate the range and text which will be passed to InputCommand, unless we may assume that the range (and text) will consist of all words which were modified by mutation. If not I think the mutation observer should be modified to provide some additional information in mutations.

The other spellchecking cases to consider are:
Foo hous a -> Foo house a
Foo hous e -> Foo house
Foo athat -> Food that
Foo xhat -> Foo chat

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmm, forgot about viewSelection which is passed along with mutations, will see if it will help solve this one :)

src/input.js Outdated
}
this.editor.execute( 'input', {
text: mutation.newText.substr( firstChangeAt, insertions ),
range: deletions > 0 ? ModelRange.createFromPositionAndShift( modelPos, deletions ) : null,
Copy link
Member

Choose a reason for hiding this comment

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

Please move the consts out of this object literal. Those variables can make the code more readable (and easier to debug) thanks to their names.

/**
* The input command. Used by the {@link module:typing/input~Input input feature} to handle typing.
*
* @extends core.command.Command
Copy link
Member

Choose a reason for hiding this comment

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

Old notation. Now should be @extends {module:blebleble~Command}.

* Typing's change buffer used to group subsequent changes into batches.
*
* @protected
* @member {typing.ChangeBuffer} #_buffer
Copy link
Member

Choose a reason for hiding this comment

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

Old type notation as well.

*
* @param {Object} [options] The command options.
* @param {String} [options.text=''] Text to be inserted.
* @param {module:engine/model/range~Range} [options.range=null] Range in which the text is inserted. Defaults
Copy link
Member

Choose a reason for hiding this comment

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

I think that the range could default to first selection's range if not passed. Will be pretty useful.

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 does, it is stated in the comment: Range in which the text is inserted. Defaults to first range in the current selection. However, it is no stated in the param specs, should it be like [options.range] plus description or something like [options.range=document.selection.getFirstRange()]?

Copy link
Member

Choose a reason for hiding this comment

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

Can be [options.range] plus description. Can also be the latter. But definitely not =null :D

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes perfect sense, I'll go with first one.

@f1ames
Copy link
Contributor Author

f1ames commented Feb 14, 2017

Pushed changes suggested by @Reinmar. There are two things which may require further discussion or some changes, see: #71 (comment) and #71 (comment).

src/input.js Outdated
*
* @member {Number} #insertedCharacterCount
* @type {module:typing/changebuffer~ChangeBuffer} #buffer
Copy link
Member

Choose a reason for hiding this comment

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

Should be @member.

@f1ames
Copy link
Contributor Author

f1ames commented Feb 15, 2017

Corrected the docs.

@Reinmar
Copy link
Member

Reinmar commented Feb 15, 2017

I commented on the two aspects you mentioned. The most important bit is to try to avoid the 3rd option.

Also, you'll need to resolve conflicts now.

PS. You may not know yet – you shouldn't rebase a branch once it's after any review because this will break comments on GH. So merge master to it.

@f1ames
Copy link
Contributor Author

f1ames commented Feb 27, 2017

I moved buffer back to InputCommand as I earlier misunderstood this comment #71 (review). Updated unit tests accordingly.
We decide that InputCommand would have optional parameter/option resultPosition, if given, after text processing the selection is set using resultPosition.

@Reinmar
Copy link
Member

Reinmar commented Feb 28, 2017

I fixed the API docs a bit.

I didn't like one thing – the fact that options.text is optional. Executing the command without the text doesn't make sense because it doesn't do anything.

Another thing is that the range doesn't have to be checked too. I don't see a case today in which selection could not have any range in it. The document selection will return so called "default range" and if you provide any custom selection, with no range inside, let the code throw. At least we'll know the situation occurs so we'll be able to fix it. Now, it's a silent error.

@Reinmar
Copy link
Member

Reinmar commented Feb 28, 2017

Of course, let's do that unless we already see that there may be no range or no text :D. Let's then think how to handle this situation.

@f1ames
Copy link
Contributor Author

f1ames commented Mar 2, 2017

Executing the command without the text doesn't make sense because it doesn't do anything.

Well, it does not look like making much sense indeed. But without checking if text was passed and passing non-collapsed range at the same time to the command will result in removing the content within the range without inserting anything. So we should decide if it is an acceptable behavior or some error should be thrown if text was not passed.
It is the same behavior as passing text = '', I am not sure if we should differentiate this two cases or assume the it should behaves the same.

const range = options.range || doc.selection.getFirstRange();
const resultPosition = options.resultPosition;

let textInsertions = 0;
Copy link
Member

Choose a reason for hiding this comment

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

Is it needed?

@f1ames
Copy link
Contributor Author

f1ames commented Mar 3, 2017

Made options.text optional and defaults to '' as we discussed earlier.

@Reinmar Reinmar merged commit cdb7fdf into master Mar 3, 2017
@Reinmar Reinmar deleted the t/48 branch March 3, 2017 14:09
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
2 participants