Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

git decorations #5

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions extension.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
let assert = require("node:assert");
let path = require("node:path");
let { homedir } = require("node:os");
let { window, workspace, commands, Uri, EventEmitter, FileType, Selection, languages, Range, Diagnostic, DiagnosticRelatedInformation, Location } = require("vscode");
let { window, workspace, commands, Uri, EventEmitter, FileType, Selection, languages, Range, Diagnostic, DiagnosticRelatedInformation, Location, extensions, ThemeColor } = require("vscode");
let gitDecorations = require("./git-decorations");

/**
* The scheme is used to associate vsnetrw documents with the text content provider
Expand Down Expand Up @@ -120,6 +121,7 @@ async function openExplorer(dirName) {
await languages.setTextDocumentLanguage(doc, languageId);
moveCursorToPreviousFile();
refreshDiagnostics();
gitDecorations.updateDecorations();
refresh();
}

Expand Down Expand Up @@ -414,14 +416,6 @@ async function provideTextDocumentContent(documentUri) {
return listings.join("\n");
}

/**
* @type {import("vscode").TextDocumentContentProvider}
*/
let contentProvider = {
onDidChange: uriChangeEmitter.event,
provideTextDocumentContent,
};

let diagnostics = languages.createDiagnosticCollection("vsnetrw");

/**
Expand All @@ -445,10 +439,10 @@ function refreshDiagnostics() {
let severities = childDiagnostics.map(diagnostic => diagnostic.severity);
let severity = Math.min(...severities);
let name = path.basename(uri.fsPath);
let range = new Range(line, 0, line, name.length);

let diagnostic = new Diagnostic(
range, `${childDiagnostics.length} problems in this file`,
new Range(line, 0, line, name.length),
`${childDiagnostics.length} problems in this file`,
severity
);

Expand All @@ -465,6 +459,14 @@ function refreshDiagnostics() {
diagnostics.set(document.uri, ownDiagnostics);
}

/**
* @type {import("vscode").TextDocumentContentProvider}
*/
let contentProvider = {
onDidChange: uriChangeEmitter.event,
provideTextDocumentContent,
};

/**
* @param {import("vscode").ExtensionContext} context
*/
Expand Down
92 changes: 92 additions & 0 deletions git-decorations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
let assert = require("assert");
let path = require("path");
let { window, Uri, Range, extensions, ThemeColor } = require("vscode");

let gitExt = extensions.getExtension("vscode.git")?.exports;

/**
* @type {import("./git").API}
*/
let git = gitExt.getAPI(1);

/**
* @param {string} label
* @param {string} [themeColorId]
*/
function createDecorationType(label, themeColorId) {
return window.createTextEditorDecorationType({
before: {
contentText: `${label} `,
color: themeColorId ? new ThemeColor(themeColorId) : undefined,
},
});
}

let modifiedDecorationType = createDecorationType("M", "gitDecoration.modifiedResourceForeground");
let addedDecorationType = createDecorationType("A", "gitDecoration.addedResourceForeground");
let deletedDecorationType = createDecorationType("D", "gitDecoration.deletedResourceForeground");
let untrackedDecorationType = createDecorationType("U", "gitDecoration.untrackedResourceForeground");
let ignoredDecorationType = createDecorationType("I", "gitDecoration.ignoredResourceForeground");
let noStatusDecorationType = createDecorationType(" ");

// Corresponds to `Status` enum in ./git.d.ts
let decorationTypesByStatus = [
modifiedDecorationType, // 0 INDEX_MODIFIED
addedDecorationType, // 1 INDEX_ADDED
deletedDecorationType, // 2 INDEX_DELETED
null, // 3 INDEX_RENAMED
null, // 4 INDEX_COPIED

modifiedDecorationType, // 5 MODIFIED
deletedDecorationType, // 6 DELETED
untrackedDecorationType, // 7 UNTRACKED
ignoredDecorationType, // 8 IGNORED
addedDecorationType, // INTENT_TO_ADD
];

function updateDecorations() {
if (!git) return;
assert(window.activeTextEditor);

let repo = git.repositories[0];
let changes = repo ? [
...repo.state.indexChanges,
...repo.state.workingTreeChanges,
] : [];

let document = window.activeTextEditor.document;
let base = document.uri.path;
let lines = document.getText().split("\n");

/**
* @type {Record<number, number[]>}
*/
let linesByStatus = {};

for (let line = 0; line < lines.length; line++) {
let pathToFile = path.join(base, lines[line]);
let uri = Uri.file(pathToFile);
let change = changes.find(change => change.uri.toString() === uri.toString());
let status = change ? change.status : -1;
linesByStatus[status] = linesByStatus[status] || [];
linesByStatus[status].push(line);
}

for (let key of Object.keys(linesByStatus)) {
let status = parseInt(key);

let decorationType = status > 0
? decorationTypesByStatus[status]
: noStatusDecorationType;

let ranges = linesByStatus[status].map(line => {
return new Range(line, 0, line, lines[line].length);
});

if (decorationType) {
window.activeTextEditor.setDecorations(decorationType, ranges);
}
}
}

module.exports = { updateDecorations };
Loading