-
-
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(); |
Thanks for writing this up!
Tried it out on windows, seems to be working fine... It's supposed to have that five-second timeout right?
@WadeShuler this is awesome! Can you add these colors changes to the tabs as well?
If I didn't find this gist, I probably would have bolted right back to Atom. Didn't realize how much of a crucial feature was until I realized it wasn't in VS Code. Thanks a lot @WadeShuler!
Thanks! works well in my Fedora 26
Thank you for this! This is the one feature that was keeping me from making the move from Atom. Definitely helps when you have hundreds of files and dozens of folders.
@sparcut Yes, I don't think a 5 second delay before the file status changes colors is a big deal. It seems better than a 1s loop when considering resources.
Unfortunately, it's not working on "VS Code - Insiders" - Windows 10 Pro :(
I have no idea why or how to debug. Hopefully this will be officially implemented soon.
@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.
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
@WadeShuler Whats up man this Harris Marketing hit me up on skype man my skype id is harrismarketing
Instructions
For Visual Studio Code version 1.15 and higher!
I made this because other attempts I found online didn't support files/folders ignored by Git. One was close, but didn't ignore files/folders under a completely ignored directory. For example, if you ignore the
vendor
directory,vendor
was greyed out, but it's children were not. I fixed that.Workbench File:
{VS Code Dir}/resources/app/out/vs/workbench/workbench.main.js
Feel free to edit the colors at the top of the code to your liking.
Note: You may get a warning about the workbench being corrupted. Ignore it, tell it to not remind you again.
IMPORTANT NOTICE
This was only tested on OSX: Please test it on Windows/Linux and all other flavors.
This code is not perfect and I personally think the coding style and logic is crap. I just don't have time to re-write it all. Being new to VSCode, this took more time than it should have to figure out how to test, test, test, and see what I needed, debugging it.
Additional Notes
I do wonder if this can be made into an extension? I don't know how (yet) to create my own VSCode extensions or the available functions available to hook into. So is it possible to create an extension that can run this code?
I think there must be an API/function to hook into the file explorer tree view and add CSS classes to the elements directly. Instead, this is a chincy hack that injects a
<style>
block to the bottom of the DOM and specifically declares the items CSS definitions. I would rather have a real official extension that adds classes to the items and a corresponding CSS stylesheet... but this does the job for now.Credit
I want to give credit to the first person I found that came up with this hack: karabaja4
I also want to give credit to dseipp for his fixes to the original repo.
It looks like karabaja4 doesn't maintain it, and I found dseipp had a PR that never was merged. He fixed it for karabaja4 (never merged), but it was still didn't have support for ignored files/folders.