Skip to content

Instantly share code, notes, and snippets.

@CyberPunkCodes
Last active March 3, 2019 21:28
Show Gist options
  • Save CyberPunkCodes/1637073371ad126779076344c34278f3 to your computer and use it in GitHub Desktop.
Save CyberPunkCodes/1637073371ad126779076344c34278f3 to your computer and use it in GitHub Desktop.
function injectGitFileStatus()
{
const timeout = 5000;
const addedColor = "#8dc149";
const modifiedColor = "#cbcb41";
const stagedColor = "#ca2820";
const ignoredOpacity = "0.4";
const explorer = document.getElementById("workbench.view.explorer");
if (explorer)
{
const foldersView = explorer.getElementsByClassName("explorer-folders-view")[0];
if (foldersView)
{
const tree = foldersView.getElementsByClassName("monaco-tree")[0];
if (tree)
{
const firstRow = tree.getElementsByClassName("monaco-tree-row")[0];
if (firstRow)
{
const explorerItem = firstRow.getElementsByClassName("explorer-item")[0];
if (explorerItem)
{
const path = require("path");
const exec = require("child_process").exec;
const style = document.createElement("style");
document.body.appendChild(style);
const resolveHome = (filepath) =>
{
if (filepath[0] === "~")
{
return path.join(process.env.HOME, filepath.slice(1));
}
return filepath;
}
const unresolveHome = (filepath) =>
{
const home = process.env.HOME;
if (home && filepath.startsWith(home))
{
const regex = new RegExp(`^${home}`);
return filepath.replace(regex, "~");
}
else
{
return filepath;
}
}
const normalizePath = (name) =>
{
return path.normalize(name.substr(3));
};
const normalizePathClean = (name) =>
{
return path.normalize(name).replace(/\\+$/, "").replace(/\/+$/, "").replace(/\\/g, "\\\\");
};
const getAllSubdirectories = (name) =>
{
let i = 1;
const paths = [];
const sep = path.sep.replace(/\\/g, "\\\\");
while (i = name.indexOf(sep, i) + 1)
{
paths.push(name.substr(0, i - 1));
}
return paths;
};
const classPath = "#workbench\\.view\\.explorer .explorer-folders-view .monaco-tree .monaco-tree-rows .monaco-tree-row .explorer-item";
const getCssEntry = (root, file, cssEntry) =>
{
const filepath = unresolveHome(path.join(root, file).replace(/\\/g, "\\\\"));
return `${classPath}[title="${filepath}" i]{${cssEntry}}`;
}
const getCssEntryDir = (root, file, cssEntry) =>
{
const filepath = unresolveHome(path.join(root, file).replace(/\\/g, "\\\\")).replace(/\/+$/, "");
return `${classPath}[title="${filepath}" i]{${cssEntry}} ${classPath}[title^="${filepath}/" i]{${cssEntry}}`;
}
const firstFileDir = path.normalize(path.dirname(explorerItem.getAttribute("title")));
const gitRootCommand = "git rev-parse --show-toplevel";
const gitRootOptions = { cwd: resolveHome(firstFileDir) };
const gitRootCallback = (error, stdout, stderr) =>
{
if (!error)
{
const gitRoot = stdout.trim();
const startGitStatusChecks = () =>
{
// run git status
const gitStatusCommand = "git status --short --ignored";
const gitStatusOptions = { cwd: resolveHome(gitRoot) }
const gitStatusCallback = (error, stdout, stderr) =>
{
if (!error)
{
const files = stdout.split("\n");
const added = files.filter(name => { return name.startsWith("?? ") || name.startsWith("AM "); }).map(name => { return normalizePath(name); });
const modified = files.filter(name => { return name.startsWith(" M ") || name.startsWith("M ") || name.startsWith("MM "); }).map(name => { return normalizePath(name); });
const ignored = files.filter(name => { return name.startsWith("!! "); }).map(name => { return normalizePath(name); });
const renamed = files.filter(name => { return name.startsWith("R "); }).map(name => { return normalizePathClean(name.split(" -> ")[1]); });
const staged = files.filter(name => { return name.startsWith("A "); }).map(name => { return normalizePath(name); });
let html = "";
const addedFolders = new Set();
const modifiedFolders = new Set();
// files
added.concat(renamed).forEach(addedFile =>
{
const subdirectories = getAllSubdirectories(addedFile);
subdirectories.forEach(subdirectory =>
{
addedFolders.add(subdirectory);
});
html += getCssEntry(gitRoot, addedFile, `color:${addedColor};`);
});
modified.forEach(modifiedFile =>
{
const subdirectories = getAllSubdirectories(modifiedFile);
subdirectories.forEach(subdirectory =>
{
modifiedFolders.add(subdirectory);
});
html += getCssEntry(gitRoot, modifiedFile, `color:${modifiedColor};`);
});
ignored.forEach(ignoredFile =>
{
if ( ignoredFile.endsWith("/") ) {
html += getCssEntryDir(gitRoot, ignoredFile, `opacity:${ignoredOpacity} !important;`);
} else {
html += getCssEntry(gitRoot, ignoredFile, `opacity:${ignoredOpacity} !important;`);
}
});
// folders
addedFolders.forEach((addedFolder) =>
{
html += getCssEntry(gitRoot, addedFolder, `color:${addedColor};`);
});
modifiedFolders.forEach((modifiedFolder) =>
{
html += getCssEntry(gitRoot, modifiedFolder, `color:${modifiedColor};`);
});
staged.forEach(stagedFile =>
{
html += getCssEntry(gitRoot, stagedFile, `color:${stagedColor};`);
});
// run git status
const gitStatusCommand1 = "git ls-files --others --exclude-standard";
const gitStatusOptions1 = { cwd: resolveHome(gitRoot) }
const gitStatusCallback1 = (error, stdout, stderr) =>
{
if (!error)
{
const added = stdout.split("\n");
// files
added.forEach(addedFile =>
{
const subdirectories = getAllSubdirectories(addedFile);
subdirectories.forEach(subdirectory =>
{
addedFolders.add(subdirectory);
});
html += getCssEntry(gitRoot, addedFile, `color:${addedColor};`);
});
// folders
addedFolders.forEach((addedFolder) =>
{
html += getCssEntry(gitRoot, addedFolder, `color:${addedColor};`);
});
if (style.innerHTML !== html)
{
style.innerHTML = html;
}
}
setTimeout(startGitStatusChecks, timeout);
}
exec(gitStatusCommand1, gitStatusOptions1, gitStatusCallback1);
}
}
exec(gitStatusCommand, gitStatusOptions, gitStatusCallback);
}
startGitStatusChecks();
}
}
exec(gitRootCommand, gitRootOptions, gitRootCallback);
// loaded
return;
};
}
}
}
}
setTimeout(injectGitFileStatus, timeout);
}
injectGitFileStatus();
@CyberPunkCodes
Copy link
Author

@MrCroft - Insiders is like the latest bleeding edge with their current testing features in it. I think they put their own version of what this Gist does, git status file highlighting. So that may be why it isn't working on Insiders, they messed with the file tree area, which is what this relies on.

Hopefully they get the Git status file highlighting done, without botching it up too bad. All they have to do is look at Atom, it does a pretty good job out of the box with this feature.

I can't support Insiders. This will do for regular versions until they break it, or roll out their own officially. Then this gist wouldn't be needed anymore. So keep an eye on the change logs.

Note: Every time your VSC updates and restarts after the update, you must re-apply this fix.

@dhruvdutt
Copy link

dhruvdutt commented Nov 4, 2017

Awesome. It works on Ubuntu 16.04 64-bit. 🚀

For linux users the file is located at: /usr/share/code/resources/app/out/vs/workbench

@boutmine
Copy link

boutmine commented Mar 1, 2018

@WadeShuler Whats up man this Harris Marketing hit me up on skype man my skype id is harrismarketing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment