Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Side by Side Diff: components/query_parser/query_parser.cc

Issue 184663002: Omnibox: Make URLs of Bookmarks Searchable (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix ALL_MATCHES (in response to recent changes) Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « components/query_parser/query_parser.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « components/query_parser/query_parser.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698