diff --git a/firefox-code-search/README.md b/firefox-code-search/README.md new file mode 100644 index 0000000..19e9681 --- /dev/null +++ b/firefox-code-search/README.md @@ -0,0 +1,13 @@ +# firefox-code-search + +## What it does + +This extension allows you to search the Firefox codebase using the awesome bar. + +To test it out, type 'cs' into the awesome bar followed by a search string (e.g. `cs hello world`). The results will be shown as suggestions, and clicking on a suggestion will navigate to the file where the result was found. + +To search a specific file, use the "path:" prefix (e.g. `cs hello path:omnibox.js`) + +## What it shows + +How to use the omnibox API to add custom suggestions to the awesome bar. \ No newline at end of file diff --git a/firefox-code-search/background.js b/firefox-code-search/background.js new file mode 100644 index 0000000..a467a47 --- /dev/null +++ b/firefox-code-search/background.js @@ -0,0 +1,101 @@ +const BASE_URL = "https://searchfox.org/mozilla-central"; +const SEARCH_URL = `${BASE_URL}/search`; +const SOURCE_URL = `${BASE_URL}/source`; + +// Provide help text to the user. +browser.omnibox.setDefaultSuggestion({ + description: `Search the firefox codebase + (e.g. "hello world" | "path:omnibox.js onInputChanged")` +}); + +// Update the suggestions whenever the input is changed. +browser.omnibox.onInputChanged.addListener((text, addSuggestions) => { + let headers = new Headers({"Accept": "application/json"}); + let init = {method: 'GET', headers}; + let url = buildSearchURL(text); + let request = new Request(url, init); + + fetch(request) + .then(createSuggestionsFromResponse) + .then(addSuggestions); +}); + +// Open the page based on how the user clicks on a suggestion. +browser.omnibox.onInputEntered.addListener((text, disposition) => { + let url = text; + if (!text.startsWith(SOURCE_URL)) { + // Update the url if the user clicks on the default suggestion. + url = `${SEARCH_URL}?q=${text}`; + } + switch (disposition) { + case "currentTab": + browser.tabs.update({url}); + break; + case "newForegroundTab": + browser.tabs.create({url}); + break; + case "newBackgroundTab": + browser.tabs.create({url, active: false}); + break; + } +}); + +function buildSearchURL(text) { + let path = ''; + let queryParts = []; + let query = ''; + let parts = text.split(' '); + + parts.forEach(part => { + if (part.startsWith("path:")) { + path = part.slice(5); + } else { + queryParts.push(part); + } + }); + + query = queryParts.join(' '); + return `${SEARCH_URL}?q=${query}&path=${path}`; +} + +function createSuggestionsFromResponse(response) { + return new Promise(resolve => { + let suggestions = []; + let suggestionsOnEmptyResults = [{ + content: SOURCE_URL, + description: "no results found" + }]; + response.json().then(json => { + if (!json.normal) { + return resolve(suggestionsOnEmptyResults); + } + + let occurrences = json.normal["Textual Occurrences"]; + let files = json.normal["Files"]; + + if (!occurrences && !files) { + return resolve(suggestionsOnEmptyResults); + } + + if (occurrences) { + occurrences.forEach(({path, lines}) => { + suggestions.push({ + content: `${SOURCE_URL}/${path}#${lines[0].lno}`, + description: lines[0].line, + }); + }); + return resolve(suggestions); + } + + // There won't be any textual occurrences if the "path:" prefix is used. + files.forEach(({path}) => { + suggestions.push({ + content: `${SOURCE_URL}/${path}`, + description: path, + }); + }); + return resolve(suggestions); + }); + }); +} + diff --git a/firefox-code-search/manifest.json b/firefox-code-search/manifest.json new file mode 100644 index 0000000..38c6bad --- /dev/null +++ b/firefox-code-search/manifest.json @@ -0,0 +1,18 @@ +{ + "name": "Firefox Code Search", + "description" : "To use, type 'cs' plus a search term into the url bar.", + "version": "1.0", + "applications": { + "gecko": { + "strict_min_version": "52.0a1" + } + }, + "background": { + "scripts": ["background.js"] + }, + "omnibox": { "keyword" : "cs" }, + "manifest_version": 2, + "permissions": [ + "https://searchfox.org/" + ] +}