248 lines
7.2 KiB
JavaScript
248 lines
7.2 KiB
JavaScript
const vscode = require("vscode");
|
|
const util = require("node:util");
|
|
const chp = require("child_process");
|
|
const execP = util.promisify(chp.exec);
|
|
|
|
let isOutPutChannelVisible = false,
|
|
logOutputChannel,
|
|
statusBarItem;
|
|
|
|
/**
|
|
* @param {vscode.ExtensionContext} context
|
|
*/
|
|
async function activate(context) {
|
|
let disposable;
|
|
|
|
logOutputChannel = makeLogOutputChannel("gitDWMSimpleUse");
|
|
statusBarItem = makeStatusBarItem();
|
|
context.subscriptions.push(
|
|
vscode.commands.registerCommand("gitDWMSimpleUse.ToggleOutputChannel", () => {
|
|
if (isOutPutChannelVisible) logOutputChannel.hide();
|
|
else logOutputChannel.show(true);
|
|
isOutPutChannelVisible = !isOutPutChannelVisible;
|
|
})
|
|
);
|
|
|
|
try {
|
|
process.chdir(vscode.workspace.workspaceFolders[0].uri.fsPath);
|
|
} catch (err) {
|
|
vscode.window.showErrorMessage(err);
|
|
}
|
|
|
|
if (await isInGitRepo()) {
|
|
statusBarItem.show();
|
|
if (!(await hasConflicts(true)))
|
|
makeSpawn("openPull", "git pull --rebase --autostash", { shell: true });
|
|
}
|
|
|
|
context.subscriptions.push(makeDocumentLinkProvider());
|
|
|
|
disposable = vscode.commands.registerCommand("dwm-git-simpleuse.fullPush", async function () {
|
|
if (
|
|
!(await isInGitRepo(true)) ||
|
|
(await hasConflicts(true)) ||
|
|
(await isWorkingTreeClean(true))
|
|
)
|
|
return;
|
|
|
|
let commitMsg = await vscode.window.showInputBox({
|
|
placeHolder: "commit message",
|
|
prompt: "choose your commit message",
|
|
value: ((await context.workspaceState.get("lastGitCommitMsg")) || "").incrementSuffixe(),
|
|
});
|
|
if (commitMsg === undefined) return;
|
|
commitMsg = commitMsg.trim();
|
|
if (!commitMsg.length) {
|
|
vscode.window.showErrorMessage("commit message needed");
|
|
return;
|
|
}
|
|
|
|
context.workspaceState.update("lastGitCommitMsg", commitMsg);
|
|
|
|
await makeSpawn("fullPush-pull", `git pull --rebase --autostash`, { shell: true });
|
|
if (await hasConflicts(true)) return;
|
|
makeSpawn(
|
|
"fullPush",
|
|
`git add . && git commit -am "${commitMsg.replace(/"/g, '\\"')}" && git push`,
|
|
{ shell: true }
|
|
);
|
|
});
|
|
context.subscriptions.push(disposable);
|
|
|
|
disposable = vscode.commands.registerCommand("dwm-git-simpleuse.initRepo", async function () {
|
|
if (await isInGitRepo()) {
|
|
vscode.window.showErrorMessage("Already in a git repository");
|
|
return;
|
|
}
|
|
|
|
let gitLink = await vscode.window.showInputBox({
|
|
placeHolder: "git repo link",
|
|
prompt: "Initialize a git repository",
|
|
value: await vscode.env.clipboard.readText(),
|
|
});
|
|
if (gitLink === undefined) return;
|
|
gitLink = gitLink.trim();
|
|
if (!gitLink.match(/^https?:\/\/\S+\.git$/).length) {
|
|
vscode.window.showErrorMessage("Link not valid");
|
|
return;
|
|
}
|
|
|
|
let gitBranch = await vscode.window.showInputBox({
|
|
placeHolder: "git branch",
|
|
prompt: "choose wich branch to use (defaults to 'main')",
|
|
value: "main",
|
|
});
|
|
if (gitBranch === undefined) return;
|
|
gitBranch = gitBranch.trim();
|
|
if (!gitBranch.length) gitBranch = "main";
|
|
|
|
makeSpawn(
|
|
"initRepo",
|
|
`git init && git remote add origin ${gitLink} && git fetch && git checkout -ft origin/${gitBranch}`,
|
|
{ shell: true }
|
|
);
|
|
});
|
|
context.subscriptions.push(disposable);
|
|
}
|
|
|
|
// This method is called when your extension is deactivated
|
|
function deactivate() {}
|
|
|
|
module.exports = {
|
|
activate,
|
|
deactivate,
|
|
};
|
|
|
|
function makeSpawn(name, command, options = {}) {
|
|
return new Promise((resolve) => {
|
|
statusBarItem.text = "$(loading~spin) GitDWMSU";
|
|
statusBarItem.tooltip = name;
|
|
let spawn = chp.spawn(`${command} 2>&1 || echo 'gitSU_error'`, options);
|
|
spawn.stdout.on("data", (d) => {
|
|
if (/\n\s*gitSU_error[\s\n]*$/.test(d.toString())) {
|
|
statusBarItem.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground");
|
|
setTimeout(() => {
|
|
statusBarItem.backgroundColor = new vscode.ThemeColor("statusBarItem.background");
|
|
}, 5000);
|
|
vscode.window.showErrorMessage(`[${name}] An error occurred`, "Show").then((e) => {
|
|
if (e == "Show") {
|
|
logOutputChannel.show(true);
|
|
isOutPutChannelVisible = true;
|
|
}
|
|
});
|
|
logOutputChannel.error(`[${name}] ${d.toString().replace(/\n\s*gitSU_error[\s]*/, "")}`);
|
|
} else logOutputChannel.info(`[${name}] ${d.toString()}`);
|
|
});
|
|
spawn.stderr.on("data", (d) => {
|
|
logOutputChannel.error(`[${name}] ${d.toString()}`);
|
|
});
|
|
spawn.on("close", (c) => {
|
|
logOutputChannel.trace(`[${name}] child process exited with code ${c}`);
|
|
statusBarItem.text = "$(remote) GitDWMSU";
|
|
statusBarItem.tooltip = "";
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
async function isWorkingTreeClean(doActions = false) {
|
|
const { stdout } = await execP(`git status --porcelain`);
|
|
if (doActions && stdout.trim() == "") vscode.window.showErrorMessage(`Working tree is clean`);
|
|
return stdout.trim() == "";
|
|
}
|
|
|
|
async function isInGitRepo(doActions = false) {
|
|
const { stdout } = await execP(`git rev-parse --is-inside-work-tree || echo false`);
|
|
if (doActions && stdout.trim() !== "true") vscode.window.showErrorMessage("Not a git repository");
|
|
return stdout.trim() === "true";
|
|
}
|
|
|
|
async function hasConflicts(doActions = false) {
|
|
const { stdout } = await execP(`git diff --name-only --diff-filter=U`);
|
|
if (doActions && stdout.trim() != "") {
|
|
vscode.window
|
|
.showErrorMessage(
|
|
`You have to resolve conflicts :\n${stdout.trim().replace("\n", ", ")}`,
|
|
"Show files",
|
|
"Git pull"
|
|
)
|
|
.then(async (e) => {
|
|
if (e == "Show files") {
|
|
logOutputChannel.show(true);
|
|
isOutPutChannelVisible = true;
|
|
} else if (e == "Git pull" && !(await hasConflicts(true))) {
|
|
makeSpawn("openPull", "git pull --rebase --autostash", { shell: true });
|
|
}
|
|
});
|
|
logOutputChannel.error(
|
|
"[conflicts] Conflicted files : \n" +
|
|
stdout
|
|
.split("\n")
|
|
.filter((e) => e.trim().length)
|
|
.map((e) => `F:${e}#`)
|
|
.join("\n")
|
|
);
|
|
}
|
|
return stdout.trim() != "";
|
|
}
|
|
|
|
String.prototype.incrementSuffixe = function () {
|
|
const match = this.match(/\s*\((\d+)\)\s*$/);
|
|
return match
|
|
? this.replace(new RegExp(`${match[0].escapeRegex()}$`), ` (${+match[1] + 1})`)
|
|
: this.trim().length
|
|
? this + " (1)"
|
|
: this.trim();
|
|
};
|
|
|
|
String.prototype.escapeRegex = function () {
|
|
return this.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
};
|
|
|
|
function makeLogOutputChannel(name) {
|
|
return vscode.window.createOutputChannel(name, {
|
|
log: true,
|
|
logLevel: 3,
|
|
});
|
|
}
|
|
|
|
function makeStatusBarItem() {
|
|
let s = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
|
|
s.text = "$(remote) GitDWMSU";
|
|
s.command = "gitDWMSimpleUse.ToggleOutputChannel";
|
|
return s;
|
|
}
|
|
|
|
function makeDocumentLinkProvider() {
|
|
return vscode.languages.registerDocumentLinkProvider(
|
|
{ language: "log", scheme: "output" },
|
|
{
|
|
provideDocumentLinks(doc) {
|
|
const links = [];
|
|
|
|
const regex = /F(ile|older)?:([^#]+)#/g;
|
|
for (let line = 0; line < doc.lineCount; line++) {
|
|
let text = doc.lineAt(line).text;
|
|
let match;
|
|
while ((match = regex.exec(text)) !== null) {
|
|
const _index = match.index + 2 + (match[1] ? match[1].length : 0);
|
|
const range = new vscode.Range(
|
|
new vscode.Position(line, _index),
|
|
new vscode.Position(line, _index + match[2].length)
|
|
);
|
|
links.push(
|
|
new vscode.DocumentLink(
|
|
range,
|
|
vscode.Uri.file(
|
|
(match[1] ? "" : `${vscode.workspace.workspaceFolders[0].uri.fsPath}/`) + match[2]
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
return links;
|
|
},
|
|
}
|
|
);
|
|
}
|