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

feat: Spellchecker Async Implementation #14032

Merged
merged 8 commits into from Oct 18, 2018

Conversation

Projects
None yet
6 participants
@nitsakh
Copy link
Contributor

commented Aug 12, 2018

This PR makes the spellchecker provider function async. This enables the client to process the incoming spellcheck words and respond through a callback once the checking is done.

The API, which initially accepted only one word at a time, now takes in an array of words to be checked. This reduces the cross language calls for spellchecking the text and behaves in a true asynchronous way, that blink expects.

More comments inline.

/cc @kwonoj

Checklist
  • PR description included and stakeholders cc'd
  • npm test passes
  • tests are changed or added
  • relevant documentation is changed or added
  • PR title follows semantic commit guidelines

Notes: Updated SpellCheck API to support asynchronous results.

@nitsakh nitsakh requested review from as code owners Aug 12, 2018

@nitsakh nitsakh force-pushed the spellcheck-async branch from 0d5849d to f492da3 Aug 12, 2018

@@ -79,19 +85,6 @@ SpellCheckClient::~SpellCheckClient() {
context_.Reset();
}

void SpellCheckClient::CheckSpelling(
const blink::WebString& text,

This comment has been minimized.

Copy link
@nitsakh

nitsakh Aug 12, 2018

Author Contributor

This is only called by blink when creating the context menu. Clients use electron APIs to create context menus, so we don't need to override this function.

@@ -188,15 +188,14 @@ void WebFrame::DetachGuest(int id) {

void WebFrame::SetSpellCheckProvider(mate::Arguments* args,
const std::string& language,
bool auto_spell_correct_turned_on,

This comment has been minimized.

Copy link
@nitsakh

nitsakh Aug 12, 2018

Author Contributor

I found that we weren't doing anything with this parameter in the API, so getting rid of it and also removing from the API.

This comment has been minimized.

Copy link
@jwheare

jwheare May 8, 2019

Contributor

Is this a breaking change? Our app currently has this code which seems like it will now fail:

webFrame.setSpellCheckProvider(locale, true, provider);

This comment has been minimized.

Copy link
@jwheare

jwheare May 8, 2019

Contributor

Just confirmed that it is, see electron-userland/electron-spellchecker#144 this should be noted as breaking in the release notes, it's currently a "Feature"

This comment has been minimized.

Copy link
@jwheare

jwheare May 8, 2019

Contributor

Never mind, I see this has been raised elsewhere #17915

@nitsakh

This comment has been minimized.

Copy link
Contributor Author

commented Aug 12, 2018

The linter step is failing to create the typescript definitions. Looks like I'll have to update https://github.com/electron/electron-typescript-definitions/blob/eaa478d08a30cf7c721360f478aedf81c7dd2843/test-smoke/electron/test/renderer.ts#L63.
What's the best way to do that without breaking builds for others?

@nitsakh nitsakh requested a review from kwonoj Aug 12, 2018

@MarshallOfSound

This comment has been minimized.

Copy link
Member

commented Aug 12, 2018

@nitsakh Breaking changes are hard on that generator. Best bet would be to update those smoke tests on a branch, then make the reference to the module in our package.json point at that branch.

Then when we merge this PR we'll do a major release of the generator

* `spellCheck` Function.
* `words` String[]
* `callback` Function
* `misspeltWords` String[]

This comment has been minimized.

Copy link
@kwonoj

kwonoj Aug 12, 2018

Member

curious if callback accepts Array<boolean> instead of returning word itself. i.e,

spellcheck: (words, callback) => {
 // do some async
 callback(words.map(isMisspelled));
}

This comment has been minimized.

Copy link
@nitsakh

nitsakh Aug 12, 2018

Author Contributor

Currently it doesn't, but I can make changes to change the API to accept Array<boolean> if we want.
What would be the benefit of doing that?

This comment has been minimized.

Copy link
@kwonoj

kwonoj Aug 12, 2018

Member

(just my 5c) it makes js callback doesn't need to maintain list of words to be returned but just apply fn then return it directly - i.e I could do similar like below in provider:

// this is fn signature runs spellcheck in async and returns result
const isMisspelled: async (word) => boolean; 

spellcheck: (words, callback) => 
  Observable.from(words)
    .mergeMap((w) => isMisspelled(w))
    .toArray().subscribe(callback);

when provider required to return array of misspelled words, provider fn need to re-map from result of spellcheck to construct list of misspelled words to be returned.

This comment has been minimized.

Copy link
@kwonoj

kwonoj Aug 12, 2018

Member

Also sort of alignment (still it's breaking change), previously provider returns boolean for single words, now returns array of boolean for corresponding words.

This comment has been minimized.

Copy link
@nitsakh

nitsakh Aug 13, 2018

Author Contributor

That sounds reasonable. However, to do that, we will have to maintain the list of all words on the c++ side and then map the returned array to those to get the locations in the text to return back to blink. I was just trying to avoid running through all the words by using a map. So, it's definitely doable but I'm not sure how important it is.
Also, APIwise returning misspelt words seems better than an array of booleans. But that's just me. 😃
I'd like to see if others have any opinions about this. I'm okay going either way!

This comment has been minimized.

Copy link
@nitsakh

nitsakh Aug 13, 2018

Author Contributor

/cc @juturu

@juturu juturu requested a review from john-hern Aug 13, 2018

@nitsakh nitsakh force-pushed the spellcheck-async branch 2 times, most recently from 734f310 to 2692ec1 Sep 20, 2018

@nitsakh nitsakh force-pushed the spellcheck-async branch 2 times, most recently from 36927d8 to fe4edc9 Oct 3, 2018

SpellcheckRequest(const base::string16& text,
blink::WebTextCheckingCompletion* completion)
: text_(text), completion_(completion) {
DCHECK(completion);
}
~SpellcheckRequest() {}

base::string16 text() { return text_; }
base::string16& text() { return text_; }

This comment has been minimized.

Copy link
@ckerr

ckerr Oct 4, 2018

Member

If we're returning a reference, should be a const reference.

Also (not new to this PR) the method should be const.

So const base::string16& text() const { return text_; }

word_map[word].push_back(result);
} else {
// For a contraction, we want check the spellings of each individual
// part, but mark the entire word incorrect if any part is misspelt

This comment has been minimized.

Copy link
@ckerr

ckerr Oct 4, 2018

Member

(opinion) let's use 'misspelled' everywhere instead of 'misspelt'. The former is what the existing API uses and has about 25x more Google hits than the latter does

@@ -155,62 +148,93 @@ void SpellCheckClient::SpellCheckText(
base::string16 word;
int word_start;
int word_length;
std::vector<base::string16> words;
auto& word_map = pending_request_param_->wordmap();
for (auto status =
text_iterator_.GetNextWord(&word, &word_start, &word_length);
status != SpellcheckWordIterator::IS_END_OF_TEXT;
status = text_iterator_.GetNextWord(&word, &word_start, &word_length)) {

This comment has been minimized.

Copy link
@ckerr

ckerr Oct 4, 2018

Member

We could replace word_start and word_length with a blink::WebTextCheckingResult that we can use below pre-populated:

blink::WebTextCheckingResult result;
std::vector<base::string16> words;

... GetNextWord(&word, &result.location, &result.length)
@@ -155,62 +148,93 @@ void SpellCheckClient::SpellCheckText(
base::string16 word;
int word_start;
int word_length;
std::vector<base::string16> words;
auto& word_map = pending_request_param_->wordmap();
for (auto status =
text_iterator_.GetNextWord(&word, &word_start, &word_length);
status != SpellcheckWordIterator::IS_END_OF_TEXT;
status = text_iterator_.GetNextWord(&word, &word_start, &word_length)) {

This comment has been minimized.

Copy link
@ckerr

ckerr Oct 4, 2018

Member

(opinion)

Those two calls to GetNextWord() in the loop structure are kind of cumbersome. This would be less repetitive:

for (;;) {
  const auto status = text_iterator_.GetNextWord(...);
  if (status == SpellcheckWordIterator::IS_END_OF_TEXT)
    break;
  if (status == SpellcheckWordIterator::IS_SKIPPABLE)
    continue;
// For a contraction, we want check the spellings of each individual
// part, but mark the entire word incorrect if any part is misspelt
// Hence, we use the same word_start and word_length values for every
// part of the contraction.

This comment has been minimized.

Copy link
@ckerr

ckerr Oct 4, 2018

Member

^ This is a good idea!

@@ -155,62 +148,93 @@ void SpellCheckClient::SpellCheckText(
base::string16 word;
int word_start;
int word_length;
std::vector<base::string16> words;
auto& word_map = pending_request_param_->wordmap();

This comment has been minimized.

Copy link
@ckerr

ckerr Oct 4, 2018

Member

Will this every be populated from a previous run -- do we need to .clear() this out?

I think the answer is "no" but am not positive

This comment has been minimized.

Copy link
@nitsakh

nitsakh Oct 14, 2018

Author Contributor

Nope, we don't need to clear it. Ideally, we shouldn't receive another request until the first one is complete, i.e. we call DidFinishCheckingText on blink, which happens at the end of this function.

@@ -5,6 +5,7 @@
#ifndef ATOM_RENDERER_API_ATOM_API_SPELL_CHECK_CLIENT_H_
#define ATOM_RENDERER_API_ATOM_API_SPELL_CHECK_CLIENT_H_

#include <map>

This comment has been minimized.

Copy link
@ckerr

ckerr Oct 4, 2018

Member

There's no std::map in this header, so this shouldn't be #included here

This comment has been minimized.

Copy link
@nitsakh

nitsakh Oct 15, 2018

Author Contributor

Updated, was leftover from my previous test implementation.

// words in the contraction.
bool IsContraction(const SpellCheckScope& scope,
const base::string16& word,
std::vector<base::string16>* contraction_words);

This comment has been minimized.

Copy link
@ckerr

ckerr Oct 4, 2018

Member

Any reason contraction_words is a pointer instead of a reference here?

This comment has been minimized.

Copy link
@nitsakh

nitsakh Oct 15, 2018

Author Contributor

Nope, changed.

This comment has been minimized.

Copy link
@nitsakh

nitsakh Oct 15, 2018

Author Contributor

Ohh, just saw. The linter tells to make this a pointer.
Is this a non-const reference? If so, make const or use a pointer: std::vector<base::string16>& contraction_words [runtime/references] [2]

for (const auto& word : misspelt_words) {
auto iter = word_map.find(word);
if (iter != word_map.end()) {
auto& words = iter->second;

This comment has been minimized.

Copy link
@ckerr

ckerr Oct 4, 2018

Member

(minor) 'words' is a confusing variable name here because they're ranges / results. I know 'results' is already taken but could something else be used here?

This comment has been minimized.

Copy link
@nitsakh

nitsakh Oct 15, 2018

Author Contributor

Added a comment.

@codebytere

This comment has been minimized.

Copy link
Member

commented Oct 14, 2018

@nitsakh there's a bit more review changes to be done here but then i think we can finally get this in 🎉

@nitsakh nitsakh force-pushed the spellcheck-async branch from fe4edc9 to e6956b3 Oct 15, 2018

@nitsakh

This comment has been minimized.

Copy link
Contributor Author

commented Oct 15, 2018

Thanks a lot for the review comments @ckerr ! 🙇

@codebytere
Copy link
Member

left a comment

lgtm!

@ckerr

ckerr approved these changes Oct 18, 2018

Copy link
Member

left a comment

Looks good!

@ckerr ckerr changed the title feat:Spellchecker Async Implementation feat: Spellchecker Async Implementation Oct 18, 2018

@ckerr ckerr merged commit a9ca152 into master Oct 18, 2018

26 of 27 checks passed

Artifact Comparison Changes Detected
Details
Absolute Zero
Semantic Pull Request ready to be squashed
Details
WIP ready for review
Details
appveyor: win-ia32-debug AppVeyor build succeeded
Details
appveyor: win-ia32-testing AppVeyor build succeeded
Details
appveyor: win-ia32-testing-pr AppVeyor build succeeded
Details
appveyor: win-x64-debug AppVeyor build succeeded
Details
appveyor: win-x64-testing AppVeyor build succeeded
Details
appveyor: win-x64-testing-pr AppVeyor build succeeded
Details
ci/circleci: linux-arm-debug Your tests passed on CircleCI!
Details
ci/circleci: linux-arm-testing Your tests passed on CircleCI!
Details
ci/circleci: linux-arm64-debug Your tests passed on CircleCI!
Details
ci/circleci: linux-arm64-testing Your tests passed on CircleCI!
Details
ci/circleci: linux-checkout Your tests passed on CircleCI!
Details
ci/circleci: linux-ia32-debug Your tests passed on CircleCI!
Details
ci/circleci: linux-ia32-testing Your tests passed on CircleCI!
Details
ci/circleci: linux-ia32-testing-tests Your tests passed on CircleCI!
Details
ci/circleci: linux-x64-debug Your tests passed on CircleCI!
Details
ci/circleci: linux-x64-testing Your tests passed on CircleCI!
Details
ci/circleci: linux-x64-testing-tests Your tests passed on CircleCI!
Details
electron-arm-testing Build #20181015.2 succeeded
Details
electron-arm64-testing Build #20181015.2 succeeded
Details
electron-lint Build #20181015.1 succeeded
Details
electron-mas-testing Build #20181015.2 succeeded
Details
electron-osx-testing Build #20181015.2 succeeded
Details
release-notes Release notes found
@release-clerk

This comment has been minimized.

Copy link

commented Oct 18, 2018

Release Notes Persisted

Updated SpellCheck API to support asynchronous results.

@ckerr ckerr deleted the spellcheck-async branch Oct 18, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.