OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/query_parser/query_parser.h" | 5 #include "components/query_parser/query_parser.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/compiler_specific.h" | 9 #include "base/compiler_specific.h" |
10 #include "base/i18n/break_iterator.h" | 10 #include "base/i18n/break_iterator.h" |
(...skipping 28 matching lines...) Expand all Loading... |
39 i != matches->end(); ) { | 39 i != matches->end(); ) { |
40 if (SnippetIntersects(mp, *i)) { | 40 if (SnippetIntersects(mp, *i)) { |
41 mp.second = std::max(mp.second, i->second); | 41 mp.second = std::max(mp.second, i->second); |
42 i = matches->erase(i); | 42 i = matches->erase(i); |
43 } else { | 43 } else { |
44 return; | 44 return; |
45 } | 45 } |
46 } | 46 } |
47 } | 47 } |
48 | 48 |
49 // Sorts the match positions in |matches| by their first index, then coalesces | |
50 // any match positions that intersect each other. | |
51 void CoalseAndSortMatchPositions(Snippet::MatchPositions* matches) { | |
52 std::sort(matches->begin(), matches->end(), &CompareMatchPosition); | |
53 // WARNING: we don't use iterator here as CoalesceMatchesFrom may remove | |
54 // from matches. | |
55 for (size_t i = 0; i < matches->size(); ++i) | |
56 CoalesceMatchesFrom(i, matches); | |
57 } | |
58 | |
59 // Returns true if the character is considered a quote. | 49 // Returns true if the character is considered a quote. |
60 bool IsQueryQuote(wchar_t ch) { | 50 bool IsQueryQuote(wchar_t ch) { |
61 return ch == '"' || | 51 return ch == '"' || |
62 ch == 0xab || // left pointing double angle bracket | 52 ch == 0xab || // left pointing double angle bracket |
63 ch == 0xbb || // right pointing double angle bracket | 53 ch == 0xbb || // right pointing double angle bracket |
64 ch == 0x201c || // left double quotation mark | 54 ch == 0x201c || // left double quotation mark |
65 ch == 0x201d || // right double quotation mark | 55 ch == 0x201d || // right double quotation mark |
66 ch == 0x201e; // double low-9 quotation mark | 56 ch == 0x201e; // double low-9 quotation mark |
67 } | 57 } |
68 | 58 |
(...skipping 12 matching lines...) Expand all Loading... |
81 | 71 |
82 const base::string16& word() const { return word_; } | 72 const base::string16& word() const { return word_; } |
83 | 73 |
84 void set_literal(bool literal) { literal_ = literal; } | 74 void set_literal(bool literal) { literal_ = literal; } |
85 | 75 |
86 // QueryNode: | 76 // QueryNode: |
87 virtual int AppendToSQLiteQuery(base::string16* query) const OVERRIDE; | 77 virtual int AppendToSQLiteQuery(base::string16* query) const OVERRIDE; |
88 virtual bool IsWord() const OVERRIDE; | 78 virtual bool IsWord() const OVERRIDE; |
89 virtual bool Matches(const base::string16& word, bool exact) const OVERRIDE; | 79 virtual bool Matches(const base::string16& word, bool exact) const OVERRIDE; |
90 virtual bool HasMatchIn( | 80 virtual bool HasMatchIn( |
91 const std::vector<QueryWord>& words, | 81 const QueryWordVector& words, |
92 Snippet::MatchPositions* match_positions) const OVERRIDE; | 82 Snippet::MatchPositions* match_positions) const OVERRIDE; |
93 virtual bool HasMatchIn( | 83 virtual bool HasMatchIn( |
94 const std::vector<QueryWord>& words) const OVERRIDE; | 84 const QueryWordVector& words) const OVERRIDE; |
95 virtual void AppendWords(std::vector<base::string16>* words) const OVERRIDE; | 85 virtual void AppendWords(std::vector<base::string16>* words) const OVERRIDE; |
96 | 86 |
97 private: | 87 private: |
98 base::string16 word_; | 88 base::string16 word_; |
99 bool literal_; | 89 bool literal_; |
100 | 90 |
101 DISALLOW_COPY_AND_ASSIGN(QueryNodeWord); | 91 DISALLOW_COPY_AND_ASSIGN(QueryNodeWord); |
102 }; | 92 }; |
103 | 93 |
104 QueryNodeWord::QueryNodeWord(const base::string16& word) | 94 QueryNodeWord::QueryNodeWord(const base::string16& word) |
(...skipping 15 matching lines...) Expand all Loading... |
120 return true; | 110 return true; |
121 } | 111 } |
122 | 112 |
123 bool QueryNodeWord::Matches(const base::string16& word, bool exact) const { | 113 bool QueryNodeWord::Matches(const base::string16& word, bool exact) const { |
124 if (exact || !QueryParser::IsWordLongEnoughForPrefixSearch(word_)) | 114 if (exact || !QueryParser::IsWordLongEnoughForPrefixSearch(word_)) |
125 return word == word_; | 115 return word == word_; |
126 return word.size() >= word_.size() && | 116 return word.size() >= word_.size() && |
127 (word_.compare(0, word_.size(), word, 0, word_.size()) == 0); | 117 (word_.compare(0, word_.size(), word, 0, word_.size()) == 0); |
128 } | 118 } |
129 | 119 |
130 bool QueryNodeWord::HasMatchIn(const std::vector<QueryWord>& words, | 120 bool QueryNodeWord::HasMatchIn(const QueryWordVector& words, |
131 Snippet::MatchPositions* match_positions) const { | 121 Snippet::MatchPositions* match_positions) const { |
132 bool matched = false; | 122 bool matched = false; |
133 for (size_t i = 0; i < words.size(); ++i) { | 123 for (size_t i = 0; i < words.size(); ++i) { |
134 if (Matches(words[i].word, false)) { | 124 if (Matches(words[i].word, false)) { |
135 size_t match_start = words[i].position; | 125 size_t match_start = words[i].position; |
136 match_positions->push_back( | 126 match_positions->push_back( |
137 Snippet::MatchPosition(match_start, | 127 Snippet::MatchPosition(match_start, |
138 match_start + static_cast<int>(word_.size()))); | 128 match_start + static_cast<int>(word_.size()))); |
139 matched = true; | 129 matched = true; |
140 } | 130 } |
141 } | 131 } |
142 return matched; | 132 return matched; |
143 } | 133 } |
144 | 134 |
145 bool QueryNodeWord::HasMatchIn(const std::vector<QueryWord>& words) const { | 135 bool QueryNodeWord::HasMatchIn(const QueryWordVector& words) const { |
146 for (size_t i = 0; i < words.size(); ++i) { | 136 for (size_t i = 0; i < words.size(); ++i) { |
147 if (Matches(words[i].word, false)) | 137 if (Matches(words[i].word, false)) |
148 return true; | 138 return true; |
149 } | 139 } |
150 return false; | 140 return false; |
151 } | 141 } |
152 | 142 |
153 void QueryNodeWord::AppendWords(std::vector<base::string16>* words) const { | 143 void QueryNodeWord::AppendWords(std::vector<base::string16>* words) const { |
154 words->push_back(word_); | 144 words->push_back(word_); |
155 } | 145 } |
156 | 146 |
157 // A QueryNodeList has a collection of QueryNodes which are deleted in the end. | 147 // A QueryNodeList has a collection of QueryNodes which are deleted in the end. |
158 class QueryNodeList : public QueryNode { | 148 class QueryNodeList : public QueryNode { |
159 public: | 149 public: |
160 typedef std::vector<QueryNode*> QueryNodeVector; | |
161 | |
162 QueryNodeList(); | 150 QueryNodeList(); |
163 virtual ~QueryNodeList(); | 151 virtual ~QueryNodeList(); |
164 | 152 |
165 QueryNodeVector* children() { return &children_; } | 153 QueryNodeStarVector* children() { return &children_; } |
166 | 154 |
167 void AddChild(QueryNode* node); | 155 void AddChild(QueryNode* node); |
168 | 156 |
169 // Remove empty subnodes left over from other parsing. | 157 // Remove empty subnodes left over from other parsing. |
170 void RemoveEmptySubnodes(); | 158 void RemoveEmptySubnodes(); |
171 | 159 |
172 // QueryNode: | 160 // QueryNode: |
173 virtual int AppendToSQLiteQuery(base::string16* query) const OVERRIDE; | 161 virtual int AppendToSQLiteQuery(base::string16* query) const OVERRIDE; |
174 virtual bool IsWord() const OVERRIDE; | 162 virtual bool IsWord() const OVERRIDE; |
175 virtual bool Matches(const base::string16& word, bool exact) const OVERRIDE; | 163 virtual bool Matches(const base::string16& word, bool exact) const OVERRIDE; |
176 virtual bool HasMatchIn( | 164 virtual bool HasMatchIn( |
177 const std::vector<QueryWord>& words, | 165 const QueryWordVector& words, |
178 Snippet::MatchPositions* match_positions) const OVERRIDE; | 166 Snippet::MatchPositions* match_positions) const OVERRIDE; |
179 virtual bool HasMatchIn( | 167 virtual bool HasMatchIn(const QueryWordVector& words) const OVERRIDE; |
180 const std::vector<QueryWord>& words) const OVERRIDE; | |
181 virtual void AppendWords(std::vector<base::string16>* words) const OVERRIDE; | 168 virtual void AppendWords(std::vector<base::string16>* words) const OVERRIDE; |
182 | 169 |
183 protected: | 170 protected: |
184 int AppendChildrenToString(base::string16* query) const; | 171 int AppendChildrenToString(base::string16* query) const; |
185 | 172 |
186 QueryNodeVector children_; | 173 QueryNodeStarVector children_; |
187 | 174 |
188 private: | 175 private: |
189 DISALLOW_COPY_AND_ASSIGN(QueryNodeList); | 176 DISALLOW_COPY_AND_ASSIGN(QueryNodeList); |
190 }; | 177 }; |
191 | 178 |
192 QueryNodeList::QueryNodeList() {} | 179 QueryNodeList::QueryNodeList() {} |
193 | 180 |
194 QueryNodeList::~QueryNodeList() { | 181 QueryNodeList::~QueryNodeList() { |
195 STLDeleteElements(&children_); | 182 STLDeleteElements(&children_); |
196 } | 183 } |
(...skipping 23 matching lines...) Expand all Loading... |
220 | 207 |
221 bool QueryNodeList::IsWord() const { | 208 bool QueryNodeList::IsWord() const { |
222 return false; | 209 return false; |
223 } | 210 } |
224 | 211 |
225 bool QueryNodeList::Matches(const base::string16& word, bool exact) const { | 212 bool QueryNodeList::Matches(const base::string16& word, bool exact) const { |
226 NOTREACHED(); | 213 NOTREACHED(); |
227 return false; | 214 return false; |
228 } | 215 } |
229 | 216 |
230 bool QueryNodeList::HasMatchIn(const std::vector<QueryWord>& words, | 217 bool QueryNodeList::HasMatchIn(const QueryWordVector& words, |
231 Snippet::MatchPositions* match_positions) const { | 218 Snippet::MatchPositions* match_positions) const { |
232 NOTREACHED(); | 219 NOTREACHED(); |
233 return false; | 220 return false; |
234 } | 221 } |
235 | 222 |
236 bool QueryNodeList::HasMatchIn(const std::vector<QueryWord>& words) const { | 223 bool QueryNodeList::HasMatchIn(const QueryWordVector& words) const { |
237 NOTREACHED(); | 224 NOTREACHED(); |
238 return false; | 225 return false; |
239 } | 226 } |
240 | 227 |
241 void QueryNodeList::AppendWords(std::vector<base::string16>* words) const { | 228 void QueryNodeList::AppendWords(std::vector<base::string16>* words) const { |
242 for (size_t i = 0; i < children_.size(); ++i) | 229 for (size_t i = 0; i < children_.size(); ++i) |
243 children_[i]->AppendWords(words); | 230 children_[i]->AppendWords(words); |
244 } | 231 } |
245 | 232 |
246 int QueryNodeList::AppendChildrenToString(base::string16* query) const { | 233 int QueryNodeList::AppendChildrenToString(base::string16* query) const { |
247 int num_words = 0; | 234 int num_words = 0; |
248 for (QueryNodeVector::const_iterator node = children_.begin(); | 235 for (QueryNodeStarVector::const_iterator node = children_.begin(); |
249 node != children_.end(); ++node) { | 236 node != children_.end(); ++node) { |
250 if (node != children_.begin()) | 237 if (node != children_.begin()) |
251 query->push_back(L' '); | 238 query->push_back(L' '); |
252 num_words += (*node)->AppendToSQLiteQuery(query); | 239 num_words += (*node)->AppendToSQLiteQuery(query); |
253 } | 240 } |
254 return num_words; | 241 return num_words; |
255 } | 242 } |
256 | 243 |
257 // A QueryNodePhrase is a phrase query ("quoted"). | 244 // A QueryNodePhrase is a phrase query ("quoted"). |
258 class QueryNodePhrase : public QueryNodeList { | 245 class QueryNodePhrase : public QueryNodeList { |
259 public: | 246 public: |
260 QueryNodePhrase(); | 247 QueryNodePhrase(); |
261 virtual ~QueryNodePhrase(); | 248 virtual ~QueryNodePhrase(); |
262 | 249 |
263 // QueryNodeList: | 250 // QueryNodeList: |
264 virtual int AppendToSQLiteQuery(base::string16* query) const OVERRIDE; | 251 virtual int AppendToSQLiteQuery(base::string16* query) const OVERRIDE; |
265 virtual bool HasMatchIn( | 252 virtual bool HasMatchIn( |
266 const std::vector<QueryWord>& words, | 253 const QueryWordVector& words, |
267 Snippet::MatchPositions* match_positions) const OVERRIDE; | 254 Snippet::MatchPositions* match_positions) const OVERRIDE; |
268 virtual bool HasMatchIn( | 255 virtual bool HasMatchIn(const QueryWordVector& words) const OVERRIDE; |
269 const std::vector<QueryWord>& words) const OVERRIDE; | |
270 | 256 |
271 private: | 257 private: |
272 bool MatchesAll(const std::vector<QueryWord>& words, | 258 bool MatchesAll(const QueryWordVector& words, |
273 const QueryWord** first_word, | 259 const QueryWord** first_word, |
274 const QueryWord** last_word) const; | 260 const QueryWord** last_word) const; |
275 DISALLOW_COPY_AND_ASSIGN(QueryNodePhrase); | 261 DISALLOW_COPY_AND_ASSIGN(QueryNodePhrase); |
276 }; | 262 }; |
277 | 263 |
278 QueryNodePhrase::QueryNodePhrase() {} | 264 QueryNodePhrase::QueryNodePhrase() {} |
279 | 265 |
280 QueryNodePhrase::~QueryNodePhrase() {} | 266 QueryNodePhrase::~QueryNodePhrase() {} |
281 | 267 |
282 int QueryNodePhrase::AppendToSQLiteQuery(base::string16* query) const { | 268 int QueryNodePhrase::AppendToSQLiteQuery(base::string16* query) const { |
283 query->push_back(L'"'); | 269 query->push_back(L'"'); |
284 int num_words = AppendChildrenToString(query); | 270 int num_words = AppendChildrenToString(query); |
285 query->push_back(L'"'); | 271 query->push_back(L'"'); |
286 return num_words; | 272 return num_words; |
287 } | 273 } |
288 | 274 |
289 bool QueryNodePhrase::MatchesAll(const std::vector<QueryWord>& words, | 275 bool QueryNodePhrase::MatchesAll(const QueryWordVector& words, |
290 const QueryWord** first_word, | 276 const QueryWord** first_word, |
291 const QueryWord** last_word) const { | 277 const QueryWord** last_word) const { |
292 if (words.size() < children_.size()) | 278 if (words.size() < children_.size()) |
293 return false; | 279 return false; |
294 | 280 |
295 for (size_t i = 0, max = words.size() - children_.size() + 1; i < max; ++i) { | 281 for (size_t i = 0, max = words.size() - children_.size() + 1; i < max; ++i) { |
296 bool matched_all = true; | 282 bool matched_all = true; |
297 for (size_t j = 0; j < children_.size(); ++j) { | 283 for (size_t j = 0; j < children_.size(); ++j) { |
298 if (!children_[j]->Matches(words[i + j].word, true)) { | 284 if (!children_[j]->Matches(words[i + j].word, true)) { |
299 matched_all = false; | 285 matched_all = false; |
300 break; | 286 break; |
301 } | 287 } |
302 } | 288 } |
303 if (matched_all) { | 289 if (matched_all) { |
304 *first_word = &words[i]; | 290 *first_word = &words[i]; |
305 *last_word = &words[i + children_.size() - 1]; | 291 *last_word = &words[i + children_.size() - 1]; |
306 return true; | 292 return true; |
307 } | 293 } |
308 } | 294 } |
309 return false; | 295 return false; |
310 } | 296 } |
311 | 297 |
312 bool QueryNodePhrase::HasMatchIn( | 298 bool QueryNodePhrase::HasMatchIn( |
313 const std::vector<QueryWord>& words, | 299 const QueryWordVector& words, |
314 Snippet::MatchPositions* match_positions) const { | 300 Snippet::MatchPositions* match_positions) const { |
315 const QueryWord* first_word; | 301 const QueryWord* first_word; |
316 const QueryWord* last_word; | 302 const QueryWord* last_word; |
317 | 303 |
318 if (MatchesAll(words, &first_word, &last_word)) { | 304 if (MatchesAll(words, &first_word, &last_word)) { |
319 match_positions->push_back( | 305 match_positions->push_back( |
320 Snippet::MatchPosition(first_word->position, | 306 Snippet::MatchPosition(first_word->position, |
321 last_word->position + last_word->word.length())); | 307 last_word->position + last_word->word.length())); |
322 return true; | 308 return true; |
323 } | 309 } |
324 return false; | 310 return false; |
325 } | 311 } |
326 | 312 |
327 bool QueryNodePhrase::HasMatchIn(const std::vector<QueryWord>& words) const { | 313 bool QueryNodePhrase::HasMatchIn(const QueryWordVector& words) const { |
328 const QueryWord* first_word; | 314 const QueryWord* first_word; |
329 const QueryWord* last_word; | 315 const QueryWord* last_word; |
330 return MatchesAll(words, &first_word, &last_word); | 316 return MatchesAll(words, &first_word, &last_word); |
331 } | 317 } |
332 | 318 |
333 QueryParser::QueryParser() {} | 319 QueryParser::QueryParser() {} |
334 | 320 |
335 // static | 321 // static |
336 bool QueryParser::IsWordLongEnoughForPrefixSearch(const base::string16& word) { | 322 bool QueryParser::IsWordLongEnoughForPrefixSearch(const base::string16& word) { |
337 DCHECK(!word.empty()); | 323 DCHECK(!word.empty()); |
(...skipping 16 matching lines...) Expand all Loading... |
354 | 340 |
355 void QueryParser::ParseQueryWords(const base::string16& query, | 341 void QueryParser::ParseQueryWords(const base::string16& query, |
356 std::vector<base::string16>* words) { | 342 std::vector<base::string16>* words) { |
357 QueryNodeList root; | 343 QueryNodeList root; |
358 if (!ParseQueryImpl(query, &root)) | 344 if (!ParseQueryImpl(query, &root)) |
359 return; | 345 return; |
360 root.AppendWords(words); | 346 root.AppendWords(words); |
361 } | 347 } |
362 | 348 |
363 void QueryParser::ParseQueryNodes(const base::string16& query, | 349 void QueryParser::ParseQueryNodes(const base::string16& query, |
364 std::vector<QueryNode*>* nodes) { | 350 QueryNodeStarVector* nodes) { |
365 QueryNodeList root; | 351 QueryNodeList root; |
366 if (ParseQueryImpl(base::i18n::ToLower(query), &root)) | 352 if (ParseQueryImpl(base::i18n::ToLower(query), &root)) |
367 nodes->swap(*root.children()); | 353 nodes->swap(*root.children()); |
368 } | 354 } |
369 | 355 |
370 bool QueryParser::DoesQueryMatch(const base::string16& text, | 356 bool QueryParser::DoesQueryMatch(const base::string16& text, |
371 const std::vector<QueryNode*>& query_nodes, | 357 const QueryNodeStarVector& query_nodes, |
372 Snippet::MatchPositions* match_positions) { | 358 Snippet::MatchPositions* match_positions) { |
373 if (query_nodes.empty()) | 359 if (query_nodes.empty()) |
374 return false; | 360 return false; |
375 | 361 |
376 std::vector<QueryWord> query_words; | 362 QueryWordVector query_words; |
377 base::string16 lower_text = base::i18n::ToLower(text); | 363 base::string16 lower_text = base::i18n::ToLower(text); |
378 ExtractQueryWords(lower_text, &query_words); | 364 ExtractQueryWords(lower_text, &query_words); |
379 | 365 |
380 if (query_words.empty()) | 366 if (query_words.empty()) |
381 return false; | 367 return false; |
382 | 368 |
383 Snippet::MatchPositions matches; | 369 Snippet::MatchPositions matches; |
384 for (size_t i = 0; i < query_nodes.size(); ++i) { | 370 for (size_t i = 0; i < query_nodes.size(); ++i) { |
385 if (!query_nodes[i]->HasMatchIn(query_words, &matches)) | 371 if (!query_nodes[i]->HasMatchIn(query_words, &matches)) |
386 return false; | 372 return false; |
387 } | 373 } |
388 if (lower_text.length() != text.length()) { | 374 if (lower_text.length() != text.length()) { |
389 // The lower case string differs from the original string. The matches are | 375 // The lower case string differs from the original string. The matches are |
390 // meaningless. | 376 // meaningless. |
391 // TODO(sky): we need a better way to align the positions so that we don't | 377 // TODO(sky): we need a better way to align the positions so that we don't |
392 // completely punt here. | 378 // completely punt here. |
393 match_positions->clear(); | 379 match_positions->clear(); |
394 } else { | 380 } else { |
395 CoalseAndSortMatchPositions(&matches); | 381 SortAndCoalesceMatchPositions(&matches); |
396 match_positions->swap(matches); | 382 match_positions->swap(matches); |
397 } | 383 } |
398 return true; | 384 return true; |
399 } | 385 } |
400 | 386 |
401 bool QueryParser::DoesQueryMatch(const std::vector<QueryWord>& query_words, | 387 bool QueryParser::DoesQueryMatch(const QueryWordVector& query_words, |
402 const std::vector<QueryNode*>& query_nodes) { | 388 const QueryNodeStarVector& query_nodes) { |
403 if (query_nodes.empty() || query_words.empty()) | 389 if (query_nodes.empty() || query_words.empty()) |
404 return false; | 390 return false; |
405 | 391 |
406 for (size_t i = 0; i < query_nodes.size(); ++i) { | 392 for (size_t i = 0; i < query_nodes.size(); ++i) { |
407 if (!query_nodes[i]->HasMatchIn(query_words)) | 393 if (!query_nodes[i]->HasMatchIn(query_words)) |
408 return false; | 394 return false; |
409 } | 395 } |
410 return true; | 396 return true; |
411 } | 397 } |
412 | 398 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
445 } | 431 } |
446 } | 432 } |
447 } | 433 } |
448 } | 434 } |
449 | 435 |
450 root->RemoveEmptySubnodes(); | 436 root->RemoveEmptySubnodes(); |
451 return true; | 437 return true; |
452 } | 438 } |
453 | 439 |
454 void QueryParser::ExtractQueryWords(const base::string16& text, | 440 void QueryParser::ExtractQueryWords(const base::string16& text, |
455 std::vector<QueryWord>* words) { | 441 QueryWordVector* words) { |
456 base::i18n::BreakIterator iter(text, base::i18n::BreakIterator::BREAK_WORD); | 442 base::i18n::BreakIterator iter(text, base::i18n::BreakIterator::BREAK_WORD); |
457 // TODO(evanm): support a locale here | 443 // TODO(evanm): support a locale here |
458 if (!iter.Init()) | 444 if (!iter.Init()) |
459 return; | 445 return; |
460 | 446 |
461 while (iter.Advance()) { | 447 while (iter.Advance()) { |
462 // Just found a span between 'prev' (inclusive) and 'pos' (exclusive). It | 448 // Just found a span between 'prev' (inclusive) and 'pos' (exclusive). It |
463 // is not necessarily a word, but could also be a sequence of punctuation | 449 // is not necessarily a word, but could also be a sequence of punctuation |
464 // or whitespace. | 450 // or whitespace. |
465 if (iter.IsWord()) { | 451 if (iter.IsWord()) { |
466 base::string16 word = iter.GetString(); | 452 base::string16 word = iter.GetString(); |
467 if (!word.empty()) { | 453 if (!word.empty()) { |
468 words->push_back(QueryWord()); | 454 words->push_back(QueryWord()); |
469 words->back().word = word; | 455 words->back().word = word; |
470 words->back().position = iter.prev(); | 456 words->back().position = iter.prev(); |
471 } | 457 } |
472 } | 458 } |
473 } | 459 } |
474 } | 460 } |
475 | 461 |
| 462 // static |
| 463 void QueryParser::SortAndCoalesceMatchPositions( |
| 464 Snippet::MatchPositions* matches) { |
| 465 std::sort(matches->begin(), matches->end(), &CompareMatchPosition); |
| 466 // WARNING: we don't use iterator here as CoalesceMatchesFrom may remove |
| 467 // from matches. |
| 468 for (size_t i = 0; i < matches->size(); ++i) |
| 469 CoalesceMatchesFrom(i, matches); |
| 470 } |
| 471 |
476 } // namespace query_parser | 472 } // namespace query_parser |
OLD | NEW |