Merge lp:~michael-sheldon/ubuntu-keyboard/fix-apostrophe-preedit into lp:ubuntu-keyboard

Proposed by Michael Sheldon
Status: Merged
Approved by: Bill Filler
Approved revision: 329
Merged at revision: 337
Proposed branch: lp:~michael-sheldon/ubuntu-keyboard/fix-apostrophe-preedit
Merge into: lp:ubuntu-keyboard
Diff against target: 258 lines (+35/-145)
4 files modified
src/view/abstracttexteditor.cpp (+16/-138)
src/view/abstracttexteditor.h (+0/-2)
tests/autopilot/ubuntu_keyboard/tests/test_keyboard.py (+19/-1)
tests/unittests/ut_preedit-string/ut_preedit-string.cpp (+0/-4)
To merge this branch: bzr merge lp:~michael-sheldon/ubuntu-keyboard/fix-apostrophe-preedit
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Bill Filler (community) Needs Fixing
Review via email: mp+250449@code.launchpad.net

Commit message

Fix the placing of apostrophes back into pre-edit and the use of apsostrophes/single quotes at the end of words.

Description of the change

Fix the placing of apostrophes back into pre-edit and the use of apsostrophes/single quotes at the end of words.

To post a comment you must log in.
326. By Michael Sheldon

Merge from trunk

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Are there any related MPs required for this MP to build/function as expected? Please list.

 * No change

Is your branch in sync with latest trunk (e.g. bzr pull lp:trunk -> no changes)

 * Yes

Did you perform an exploratory manual test run of your code change and any related functionality on device or emulator?

 * Yes

Did you successfully run all tests found in your component's Test Plan (https://wiki.ubuntu.com/Process/Merges/TestPlan/ubuntu-keyboard) on device or emulator?

 * Yes

If you changed the UI, was the change specified/approved by design?

 * No change

If you changed UI labels, did you update the pot file?

 * No change

If you changed the packaging (debian), did you add a core-dev as a reviewer to this MP?

 * No change

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Bill Filler (bfiller) wrote :

Found one issue. Certain words get the starting single quote removed after autocorrect. Try typing
"I have found 'it'"
This gets changed to
"I have found it'"

Same thing with 'to' replaced with to'

review: Needs Fixing
327. By Michael Sheldon

Ensure that apostrophes before words are preserved as well as after them

328. By Michael Sheldon

Expand text for apostrophes used as single quotes

329. By Michael Sheldon

Merge from trunk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/view/abstracttexteditor.cpp'
2--- src/view/abstracttexteditor.cpp 2015-02-02 18:17:42 +0000
3+++ src/view/abstracttexteditor.cpp 2015-03-02 13:52:50 +0000
4@@ -171,96 +171,6 @@
5 //! \var AbstractTextEditor::Replacement::cursor_position
6 //! \brief New cursor position relative to the beginning of preedit.
7
8-namespace {
9-
10-//! \brief Checks whether given \a c is a word separator.
11-//! \param c Char to test.
12-//!
13-//! Other way to do checks would be using isLetterOrNumber() + some
14-//! other methods. But UTF is so crazy that I am not sure whether
15-//! other strange categories are parts of the word or not. It is
16-//! easier to specify punctuations and whitespaces.
17-inline bool isSeparator(const QChar &c)
18-{
19- return (c.isPunct() or c.isSpace());
20-}
21-
22-//! \brief Extracts a word boundaries at cursor position.
23-//! \param surrounding_text Text from which extraction will happen.
24-//! \param cursor_position Position of cursor within \a surrounding_text.
25-//! \param replacement Place where replacement data will be stored.
26-//!
27-//! \return whether surrounding text was valid (not empty).
28-//!
29-//! If cursor is placed right after the word, boundaries of this word
30-//! are extracted. Otherwise if cursor is placed right before the
31-//! word, then no word boundaries are stored - instead invalid
32-//! replacement is stored. It might happen that cursor position is
33-//! outside the string, so \a replacement will have fixed position.
34-bool extractWordBoundariesAtCursor(const QString& surrounding_text,
35- int cursor_position,
36- AbstractTextEditor::Replacement *replacement)
37-{
38- const int text_length(surrounding_text.length());
39-
40- if (text_length == 0) {
41- return false;
42- }
43-
44- // just in case - if cursor is far after last char in surrounding
45- // text we place it right after last char.
46- cursor_position = qBound(0, cursor_position, text_length);
47-
48- // cursor might be placed in after last char (that is to say - its
49- // index might be the one of string terminator) - for simplifying
50- // the algorithm below we fake it as cursor is put on delimiter:
51- // "abc" - surrounding text
52- // | - cursor placement
53- // "abc " - fake surrounding text
54- const QString fake_surrounding_text(surrounding_text + " ");
55- const QChar *const fake_data(fake_surrounding_text.constData());
56- // begin is index of first char in a word
57- int begin(-1);
58- // end is index of a char after last char in a word.
59- // -2, because -2 - (-1) = -1 and we would like to
60- // have -1 as invalid length.
61- int end(-2);
62-
63- for (int iter(cursor_position); iter >= 0; --iter) {
64- const QChar &c(fake_data[iter]);
65-
66- if (isSeparator(c)) {
67- if (iter != cursor_position) {
68- break;
69- }
70- } else {
71- begin = iter;
72- }
73- }
74-
75- if (begin >= 0) {
76- // take note that fake_data's last QChar is always a space.
77- for (int iter(cursor_position); iter <= text_length; ++iter) {
78- const QChar &c(fake_data[iter]);
79-
80- end = iter;
81- if (isSeparator(c)) {
82- break;
83- }
84- }
85- }
86-
87- if (replacement) {
88- replacement->start = begin;
89- replacement->length = end - begin;
90- replacement->cursor_position = cursor_position;
91- }
92-
93- return true;
94-}
95-
96-} // unnamed namespace
97-
98 EditorOptions::EditorOptions()
99 : backspace_auto_repeat_delay(500)
100 , backspace_auto_repeat_interval(200)
101@@ -568,6 +478,20 @@
102 d->previous_preedit = d->text->preedit();
103 d->previous_preedit_position = d->text->surroundingOffset();
104 d->text->setPreedit(d->text->primaryCandidate());
105+ if (d->previous_preedit.right(1) == "'" && d->text->preedit().right(1) != "'") {
106+ // If the user has added an apostrophe to the end of the word
107+ // we should preserve this, as it may be user as a single quote
108+ // or the plural form of certain names (but would otherwise be
109+ // replaced by auto-correct as it's treated as a normal
110+ // character for use inside words).
111+ d->text->setPreedit(d->text->preedit() + "'");
112+ d->previous_preedit_position -= 1;
113+ }
114+ if (d->previous_preedit.left(1) == "'" && d->text->preedit().left(1) != "'") {
115+ // Same for apostrophes at the beginning of the word
116+ d->text->setPreedit("'" + d->text->preedit());
117+ d->previous_preedit_position -= 1;
118+ }
119 }
120 else if (look_for_a_double_space && not stopSequence.isEmpty() && textOnLeft.right(1) == " ") {
121 removeTrailingWhitespaces();
122@@ -923,7 +847,7 @@
123
124 const QString leftSurrounding = d->text->surroundingLeft();
125 int idx = leftSurrounding.length() - 1;
126- while (idx >= 0 && !isSeparator(leftSurrounding.at(idx))) {
127+ while (idx >= 0 && !d->word_engine->languageFeature()->isSeparator(leftSurrounding.at(idx))) {
128 --idx;
129 }
130 int length = d->text->surroundingOffset() - idx;
131@@ -1009,52 +933,6 @@
132 d->backspace_sent = true;
133 }
134
135-//! \brief Reacts to cursor position change in application's text
136-//! field.
137-//! \param cursor_position new cursor position
138-//! \param surrounding_text surrounding text of a preedit
139-//!
140-//! Extract words with the cursor inside and replaces it with a preedit.
141-//! This is called preedit activation.
142-void AbstractTextEditor::onCursorPositionChanged(int cursor_position,
143- const QString &surrounding_text)
144-{
145- Q_D(AbstractTextEditor);
146- Replacement r;
147-
148- if (not extractWordBoundariesAtCursor(surrounding_text, cursor_position, &r)) {
149- return;
150- }
151-
152- if (r.start < 0 or r.length < 0) {
153- if (d->ignore_next_surrounding_text == surrounding_text and
154- d->ignore_next_cursor_position == cursor_position) {
155- d->ignore_next_surrounding_text.clear();
156- d->ignore_next_cursor_position = -1;
157- } else {
158- d->text->setPreedit("");
159- d->text->setCursorPosition(0);
160- }
161- } else {
162- const int cursor_pos_relative_word_begin(r.start - r.cursor_position);
163- const int word_begin_relative_cursor_pos(r.cursor_position - r.start);
164- const QString word(surrounding_text.mid(r.start, r.length));
165- Replacement word_r(cursor_pos_relative_word_begin, r.length,
166- word_begin_relative_cursor_pos);
167-
168- d->text->setPreedit(word, word_begin_relative_cursor_pos);
169- // computeCandidates can change preedit face, so needs to happen
170- // before sending preedit:
171- d->word_engine->computeCandidates(d->text.data());
172- sendPreeditString(d->text->preedit(), d->text->preeditFace(), word_r);
173- // Qt is going to send us an event with cursor position places
174- // at the beginning of replaced word and surrounding text
175- // without the replaced word. We want to ignore it.
176- d->ignore_next_cursor_position = r.start;
177- d->ignore_next_surrounding_text = QString(surrounding_text).remove(r.start, r.length);
178- }
179-}
180-
181 void AbstractTextEditor::onKeyboardStateChanged(QString state) {
182 Q_D(AbstractTextEditor);
183
184@@ -1107,7 +985,7 @@
185 lastChar = text()->surrounding().at(currentOffset-1);
186 }
187 if(!QRegExp("\\W+").exactMatch(lastChar) && !d->word_engine->languageFeature()->isSymbol(lastChar)) {
188- QStringList leftWords = text()->surroundingLeft().trimmed().split(QRegExp("[\\W\\d]+"));
189+ QStringList leftWords = text()->surroundingLeft().trimmed().split(QRegExp("[\\s\\d]+"));
190 int trimDiff = text()->surroundingLeft().size() - text()->surroundingLeft().trimmed().size();
191 if(leftWords.last().isEmpty()) {
192 // If removed char was punctuation trimming will result in an empty entry
193
194=== modified file 'src/view/abstracttexteditor.h'
195--- src/view/abstracttexteditor.h 2015-01-07 15:00:07 +0000
196+++ src/view/abstracttexteditor.h 2015-03-02 13:52:50 +0000
197@@ -117,8 +117,6 @@
198 Q_SLOT void onKeyReleased(const Key &key);
199 Q_SLOT void onKeyEntered(const Key &key);
200 Q_SLOT void onKeyExited(const Key &key);
201- Q_SLOT void onCursorPositionChanged(int cursor_position,
202- const QString &surrounding_text);
203 Q_SLOT void onKeyboardStateChanged(QString state);
204 Q_SLOT void onHasSelectionChanged(bool hasSelection);
205 Q_SLOT void replacePreedit(const QString &replacement);
206
207=== modified file 'tests/autopilot/ubuntu_keyboard/tests/test_keyboard.py'
208--- tests/autopilot/ubuntu_keyboard/tests/test_keyboard.py 2015-02-27 14:12:47 +0000
209+++ tests/autopilot/ubuntu_keyboard/tests/test_keyboard.py 2015-03-02 13:52:50 +0000
210@@ -573,7 +573,6 @@
211 """Long pressing a key should enter the default extended character.
212
213 """
214-
215 text_area = self.launch_test_input_area()
216 self.ensure_focus_on_input(text_area)
217 keyboard = Keyboard()
218@@ -588,6 +587,25 @@
219 )
220
221
222+ def test_single_quotes(self):
223+ """Single quotes placed around a word shouldn't get removed by
224+ autocomplete.
225+
226+ """
227+ text_area = self.launch_test_input_area()
228+ self.ensure_focus_on_input(text_area)
229+ keyboard = Keyboard()
230+ self.addCleanup(keyboard.dismiss)
231+
232+ keyboard.type("'here' 'to' ")
233+
234+ expected = "'here' 'to' "
235+ self.assertThat(
236+ text_area.text,
237+ Eventually(Equals(expected))
238+ )
239+
240+
241 class UbuntuKeyboardPinyin(UbuntuKeyboardTests):
242
243 scenarios = [
244
245=== modified file 'tests/unittests/ut_preedit-string/ut_preedit-string.cpp'
246--- tests/unittests/ut_preedit-string/ut_preedit-string.cpp 2013-10-24 10:17:12 +0000
247+++ tests/unittests/ut_preedit-string/ut_preedit-string.cpp 2015-03-02 13:52:50 +0000
248@@ -174,10 +174,6 @@
249 {
250 editor.setHost(&host);
251 editor.wordEngine()->setEnabled(enable_word_engine);
252-
253- QObject::connect(&notifier, SIGNAL(cursorPositionChanged(int, QString)),
254- &editor, SLOT(onCursorPositionChanged(int, QString)));
255-
256 }
257
258 Editor editor;

Subscribers

People subscribed via source and target branches