Skip to content

Commit

Permalink
Add Open Searchfox files in VS Code userscript
Browse files Browse the repository at this point in the history
  • Loading branch information
aminomancer committed Feb 14, 2024
1 parent d169407 commit f344b60
Showing 1 changed file with 138 additions and 0 deletions.
138 changes: 138 additions & 0 deletions openSearchfoxFilesInVSCode.user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// ==UserScript==
// @name Open Searchfox files in VS Code
// @version 1.0.0
// @author aminomancer
// @homepageURL https://github.com/aminomancer/userscripts
// @supportURL https://github.com/aminomancer/userscripts
// @downloadURL https://cdn.jsdelivr.net/gh/aminomancer/userscripts@latest/openSearchfoxFilesInVSCode.user.js
// @updateURL https://cdn.jsdelivr.net/gh/aminomancer/userscripts@latest/openSearchfoxFilesInVSCode.user.js
// @namespace https://github.com/aminomancer
// @match https://searchfox.org/*
// @grant GM_listValues
// @grant GM_getValue
// @grant GM_setValue
// @description When viewing a file on a known Searchfox repo with a local clone, pressing the `\` key will open the file in VS Code. If a line is highlighted, the file will be opened to that line in VS Code. Use the Values tab in the userscript manager to configure the script and map Searchfox repos to local clone paths. Supports wildcards in repo names.
// @license CC-BY-NC-SA-4.0
// @icon https://cdn.jsdelivr.net/gh/aminomancer/userscripts@latest/icons/vscode.svg
// ==/UserScript==

/* global GM_listValues, GM_getValue, GM_setValue */

// These are the default preference values. When the script is first installed,
// these values will be used to populate the preferences, which are stored by
// the userscript manager. To modify the preferences, don't edit the file here.
// Go to the Values tab in the userscript manager and edit them there.
const defaultPrefs = {
// This script works by opening a URL with vscode's custom URL protocol. The
// protocol name can be changed here. The default is "vscode", but if you use
// VS Code Insiders, you should change it to "vscode-insiders".
protocol_name: "vscode",
// This is how the script knows what local file to open. This pref maps each
// Searchfox repo to the path of the local clone. If the repo name is
// "foo/bar", then the path should be "/path/to/foo/bar". If a repo is not
// listed here, it will not be opened in VS Code. Only use forward slashes,
// even on Windows, since the path becomes part of a URL.
repos: {
example123: "/path/to/example123",
},
};

for (const [key, value] of Object.entries(defaultPrefs)) {
if (GM_getValue(key) === undefined) {
GM_setValue(key, value);
}
}

const prefs = {};
for (const key of GM_listValues()) {
prefs[key] = GM_getValue(key);
}

// Compile the wildcard search regex once for efficiency.
let wildcardExp = /(?<!\\)\*/g;

// Store the compiled expressions for user wildcards in a map for efficiency.
let patternMap = new Map();
function getPatternFor(key) {
let pattern = patternMap.get(key);
if (!pattern) {
pattern = new RegExp("^" + key.replace(wildcardExp, ".*") + "$");
patternMap.set(key, pattern);
}
return pattern;
}

// We support pref keys with wildcards. For example, mozilla-* will match
// mozilla-central, mozilla-beta, mozilla-release, etc. So if the current repo
// name is mozilla-central, we'll first check for that, then we'll check for a
// pref whose wildcard key matches the repo name. Escaped asterisks are treated
// as literal asterisks. Also, a key can't be only asterisks/wildcards.
function getRepoPath(repoName) {
let keys = Object.keys(prefs.repos);
let exactMatch = prefs.repos[repoName];
if (exactMatch) {
return exactMatch;
}
for (let key of keys.filter(
key => wildcardExp.test(key) && key.replace(wildcardExp, "")
)) {
if (getPatternFor(key).test(repoName)) {
return prefs.repos[key];
}
}
}

function openInVSCode({ repoName, filePath, lineNum }) {
const repoPath = getRepoPath(repoName);
let protocolURL = `${prefs.protocol_name}://file/${repoPath}/${filePath}`;
if (lineNum) {
protocolURL += `:${lineNum}`;
}
if (!protocolURL) {
return;
}
let link = document.createElement("a");
link.setAttribute("href", protocolURL);
link.click();
}

function getForURL(url) {
let [, repoName, method, ...pathParts] = url.pathname.split("/");
if (!pathParts.length) {
return null;
}
switch (method) {
case "source":
break;
case "rev":
case "diff":
pathParts.shift();
break;
default:
return null;
}
let lineNum = url.hash?.match(/^#(\d+)/)?.[1];
return { repoName, filePath: pathParts.join("/"), lineNum };
}

function handleKeydown(event) {
if (event.key === "\\") {
// Ignore keydown events that originated from a text input, textarea,
// contenteditable, etc.
if (
["input", "textarea"].includes(event.target.localName) ||
event.target.isContentEditable ||
event.target.getAttribute("role") === "textbox"
) {
return;
}

const fileDetails = getForURL(location);
if (fileDetails) {
event.preventDefault();
openInVSCode(fileDetails);
}
}
}

document.addEventListener("keydown", handleKeydown);

0 comments on commit f344b60

Please sign in to comment.