1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5var currentRequest = null;
6
7chrome.omnibox.onInputChanged.addListener(
8  function(text, suggest) {
9    if (currentRequest != null) {
10      currentRequest.onreadystatechange = null;
11      currentRequest.abort();
12      currentRequest = null;
13    }
14
15    updateDefaultSuggestion(text);
16    if (text == '' || text == 'halp')
17      return;
18
19    currentRequest = search(text, function(xml) {
20      var results = [];
21      var entries = xml.getElementsByTagName("entry");
22
23      for (var i = 0, entry; i < 5 && (entry = entries[i]); i++) {
24        var path = entry.getElementsByTagName("file")[0].getAttribute("name");
25        var line =
26            entry.getElementsByTagName("match")[0].getAttribute("lineNumber");
27        var file = path.split("/").pop();
28
29        var description = '<url>' + file + '</url>';
30        if (/^file:/.test(text)) {
31          description += ' <dim>' + path + '</dim>';
32        } else {
33          var content = entry.getElementsByTagName("content")[0].textContent;
34
35          // There can be multiple lines. Kill all the ones except the one that
36          // contains the first match. We can ocassionally fail to find a single
37          // line that matches, so we still handle multiple lines below.
38          var matches = content.split(/\n/);
39          for (var j = 0, match; match = matches[j]; j++) {
40            if (match.indexOf('<b>') > -1) {
41              content = match;
42              break;
43            }
44          }
45
46          // Replace any extraneous whitespace to make it look nicer.
47          content = content.replace(/[\n\t]/g, ' ');
48          content = content.replace(/ {2,}/g, ' ');
49
50          // Codesearch wraps the result in <pre> tags. Remove those if they're
51          // still there.
52          content = content.replace(/<\/?pre>/g, '');
53
54          // Codesearch highlights the matches with 'b' tags. Replaces those
55          // with 'match'.
56          content = content.replace(/<(\/)?b>/g, '<$1match>');
57
58          description += ' ' + content;
59        }
60
61        results.push({
62          content: path + '@' + line,
63          description: description
64        });
65      }
66
67      suggest(results);
68    });
69  }
70);
71
72function resetDefaultSuggestion() {
73  chrome.omnibox.setDefaultSuggestion({
74    description: '<url><match>src:</match></url> Search Chromium source'
75  });
76}
77
78resetDefaultSuggestion();
79
80function updateDefaultSuggestion(text) {
81  var isRegex = /^re:/.test(text);
82  var isFile = /^file:/.test(text);
83  var isHalp = (text == 'halp');
84  var isPlaintext = text.length && !isRegex && !isFile && !isHalp;
85
86  var description = '<match><url>src</url></match><dim> [</dim>';
87  description +=
88      isPlaintext ? ('<match>' + text + '</match>') : 'plaintext-search';
89  description += '<dim> | </dim>';
90  description += isRegex ? ('<match>' + text + '</match>') : 're:regex-search';
91  description += '<dim> | </dim>';
92  description += isFile ? ('<match>' + text + '</match>') : 'file:filename';
93  description += '<dim> | </dim>';
94  description += isHalp ? '<match>halp</match>' : 'halp';
95  description += '<dim> ]</dim>';
96
97  chrome.omnibox.setDefaultSuggestion({
98    description: description
99  });
100}
101
102chrome.omnibox.onInputStarted.addListener(function() {
103  updateDefaultSuggestion('');
104});
105
106chrome.omnibox.onInputCancelled.addListener(function() {
107  resetDefaultSuggestion();
108});
109
110function search(query, callback) {
111  if (query == 'halp')
112    return;
113
114  if (/^re:/.test(query))
115    query = query.substring('re:'.length);
116  else if (/^file:/.test(query))
117    query = 'file:"' + query.substring('file:'.length) + '"';
118  else
119    query = '"' + query + '"';
120
121  var url = "https://code.google.com/p/chromium/codesearch#search/&type=cs&q=" + query +
122      "&exact_package=chromium&type=cs";
123  var req = new XMLHttpRequest();
124  req.open("GET", url, true);
125  req.setRequestHeader("GData-Version", "2");
126  req.onreadystatechange = function() {
127    if (req.readyState == 4) {
128      callback(req.responseXML);
129    }
130  }
131  req.send(null);
132  return req;
133}
134
135function getUrl(path, line) {
136  var url = "https://code.google.com/p/chromium/codesearch#" + path
137      "&sq=package:chromium";
138  if (line)
139    url += "&l=" + line;
140  return url;
141}
142
143function getEntryUrl(entry) {
144  return getUrl(
145      entry.getElementsByTagName("file")[0].getAttribute("name"),
146      entry.getElementsByTagName("match")[0].getAttribute("lineNumber"));
147  return url;
148}
149
150function navigate(url) {
151  chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
152    chrome.tabs.update(tabs[0].id, {url: url});
153  });
154}
155
156chrome.omnibox.onInputEntered.addListener(function(text) {
157  // TODO(aa): We need a way to pass arbitrary data through. Maybe that is just
158  // URL?
159  if (/@\d+\b/.test(text)) {
160    var chunks = text.split('@');
161    var path = chunks[0];
162    var line = chunks[1];
163    navigate(getUrl(path, line));
164  } else if (text == 'halp') {
165    // TODO(aa)
166  } else {
167    navigate("https://code.google.com/p/chromium/codesearch#search/&type=cs" +
168             "&q=" + text +
169             "&exact_package=chromium&type=cs");
170  }
171});
172