Chromium Code Reviews| Index: chrome/browser/history/query_parser.cc |
| diff --git a/chrome/browser/history/query_parser.cc b/chrome/browser/history/query_parser.cc |
| index 782c708010cf2b9b8ef0ed7216f6740a95c8b4aa..c1a628e24e5562508ada86d22387788a4768658e 100644 |
| --- a/chrome/browser/history/query_parser.cc |
| +++ b/chrome/browser/history/query_parser.cc |
| @@ -11,6 +11,7 @@ |
| #include "base/i18n/case_conversion.h" |
| #include "base/logging.h" |
| #include "base/stl_util.h" |
| +#include "base/string_util.h" |
| namespace { |
| @@ -46,7 +47,7 @@ void CoalesceMatchesFrom(size_t index, Snippet::MatchPositions* matches) { |
| // Sorts the match positions in |matches| by their first index, then coalesces |
| // any match positions that intersect each other. |
| -void CoalseAndSortMatchPositions(Snippet::MatchPositions* matches) { |
| +void CoalesceAndSortMatchPositions(Snippet::MatchPositions* matches) { |
| std::sort(matches->begin(), matches->end(), &CompareMatchPosition); |
| // WARNING: we don't use iterator here as CoalesceMatchesFrom may remove |
| // from matches. |
| @@ -64,6 +65,17 @@ bool IsQueryQuote(wchar_t ch) { |
| ch == 0x201e; // double low-9 quotation mark |
| } |
| +// Returns true if the character is considered non-breaking when it appears in |
| +// the middle of a word. This can be used to prevent an URL-like query from |
| +// being broken into multiple words. |
| +bool IsNonBreakingSymbol(wchar_t ch) { |
| + return ch == '.' || |
| + ch == '-' || |
| + ch == '_' || |
| + ch == '/' || |
| + ch == '~'; |
| +} |
| + |
| } // namespace |
| // Inheritance structure: |
| @@ -89,6 +101,7 @@ class QueryNodeWord : public QueryNode { |
| const std::vector<QueryWord>& words, |
| Snippet::MatchPositions* match_positions) const OVERRIDE; |
| virtual void AppendWords(std::vector<string16>* words) const OVERRIDE; |
| + virtual void Append(const string16& word); |
|
sky
2012/02/08 23:31:28
This isn't part of QueryNode, so move it some wher
Patrick Dubroy
2012/02/09 14:47:00
Whoops, yeah. Done.
|
| private: |
| string16 word_; |
| @@ -141,6 +154,10 @@ void QueryNodeWord::AppendWords(std::vector<string16>* words) const { |
| words->push_back(word_); |
| } |
| +void QueryNodeWord::Append(const string16& str) { |
| + word_ += str; |
| +} |
| + |
| // A QueryNodeList has a collection of QueryNodes which are deleted in the end. |
| class QueryNodeList : public QueryNode { |
| public: |
| @@ -347,7 +364,7 @@ bool QueryParser::DoesQueryMatch(const string16& text, |
| // completely punt here. |
| match_positions->clear(); |
| } else { |
| - CoalseAndSortMatchPositions(&matches); |
| + CoalesceAndSortMatchPositions(&matches); |
| match_positions->swap(matches); |
| } |
| return true; |
| @@ -365,27 +382,42 @@ bool QueryParser::ParseQueryImpl(const string16& query, QueryNodeList* root) { |
| query_stack.push_back(root); |
| bool in_quotes = false; // whether we're currently in a quoted phrase |
| + QueryNodeWord* current_word = NULL; |
| while (iter.Advance()) { |
| // Just found a span between 'prev' (inclusive) and 'pos' (exclusive). It |
| - // is not necessarily a word, but could also be a sequence of punctuation |
| - // or whitespace. |
| - if (iter.IsWord()) { |
| - QueryNodeWord* word_node = new QueryNodeWord(iter.GetString()); |
| - if (in_quotes) |
| - word_node->set_literal(true); |
| - query_stack.back()->AddChild(word_node); |
| - } else { // Punctuation. |
| - if (IsQueryQuote(query[iter.prev()])) { |
| - if (!in_quotes) { |
| - QueryNodeList* quotes_node = new QueryNodePhrase; |
| - query_stack.back()->AddChild(quotes_node); |
| - query_stack.push_back(quotes_node); |
| - in_quotes = true; |
| - } else { |
| - query_stack.pop_back(); // Stop adding to the quoted phrase. |
| - in_quotes = false; |
| - } |
| + // is not necessarily a word, it could also be a punctuation or whitespace |
| + // character. Punctuation is preserved inside quotes, and otherwise removed |
| + // except if it is a non-breaking character in the middle of a word. |
| + |
| + wchar_t last_char = query[iter.prev()]; |
| + if (IsQueryQuote(last_char)) { |
| + if (!in_quotes) { |
| + QueryNodeList* quotes_node = new QueryNodePhrase; |
| + query_stack.back()->AddChild(quotes_node); |
| + query_stack.push_back(quotes_node); |
| + in_quotes = true; |
| + } else { |
| + query_stack.pop_back(); // Stop adding to the quoted phrase. |
| + in_quotes = false; |
| + } |
| + current_word = NULL; |
| + } else if (iter.IsWord() || (in_quotes && !IsWhitespace(last_char))) { |
| + // Append to the current word if the new token is a word or a non- |
| + // whitespace character inside quotes. |
| + if (current_word) { |
| + current_word->Append(iter.GetString()); |
| + } else { |
| + current_word = new QueryNodeWord(iter.GetString()); |
| + current_word->set_literal(in_quotes); |
| + query_stack.back()->AddChild(current_word); |
| } |
| + } else if (current_word != NULL) { |
| + // Allow non-breaking symbols inside a word. |
| + // Any other punctuation or whitespace character ends the current word. |
| + if (IsNonBreakingSymbol(last_char)) |
| + current_word->Append(iter.GetString()); |
| + else |
| + current_word = NULL; |
| } |
| } |