Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

refactor: add node part of git extension into phoenix node core #2050

Merged
merged 1 commit into from
Jan 14, 2025
Merged
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
165 changes: 34 additions & 131 deletions docs/API-Reference/command/Menus.md

Large diffs are not rendered by default.

10 changes: 3 additions & 7 deletions docs/API-Reference/utils/LocalizationUtils.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ const LocalizationUtils = brackets.getModule("utils/LocalizationUtils")
<a name="getLocalizedLabel"></a>

## getLocalizedLabel(locale) ⇒ <code>string</code>
Converts a language code to its written name, if possible.
If not possible, the language code is simply returned.
Converts a language code to its written name, if possible.If not possible, the language code is simply returned.

**Kind**: global function
**Returns**: <code>string</code> - The language's name or the given language code
Expand Down Expand Up @@ -44,15 +43,12 @@ Returns a relative time string (e.g., "2 days ago", "in 3 hours") based on the d
| --- | --- | --- |
| [date] | <code>Date</code> | The date to compare with the current date and time. If not given, defaults to now. |
| [lang] | <code>string</code> | Optional language code to use for formatting (e.g., 'en', 'fr'). If not provided, defaults to the application locale or 'en'. |
| [fromDate] | <code>string</code> | Optional date to use instead of now to compute the relative dateTime from. |
| [fromDate] | <code>Date</code> | Optional date to use instead of now to compute the relative dateTime from. |

<a name="dateTimeFromNowFriendly"></a>

## dateTimeFromNowFriendly(date, [lang]) ⇒ <code>string</code>
Returns an intelligent date string.
- For dates within the last 30 days or the future: relative time (e.g., "2 days ago", "in 3 hours").
- For dates earlier this year: formatted date (e.g., "Jan 5").
- For dates not in the current year: formatted date with year (e.g., "Jan 5, 2023").
Returns an intelligent date string.- For dates within the last 30 days or the future: relative time (e.g., "2 days ago", "in 3 hours").- For dates earlier this year: formatted date (e.g., "Jan 5").- For dates not in the current year: formatted date with year (e.g., "Jan 5, 2023").

**Kind**: global function
**Returns**: <code>string</code> - - An intelligently formatted date string.
Expand Down
31 changes: 26 additions & 5 deletions docs/API-Reference/widgets/PopUpManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,42 @@ Add Esc key handling for a popup DOM element.
<a name="removePopUp"></a>

## removePopUp($popUp)
Remove Esc key handling for a pop-up. Removes the pop-up from the DOM
if the pop-up is currently visible and was not originally attached.
Remove Esc key handling for a pop-up. Removes the pop-up from the DOMif the pop-up is currently visible and was not originally attached.

**Kind**: global function

| Param | Type |
| --- | --- |
| $popUp | <code>jQuery</code> |

<a name="_filterDropdown"></a>

## \_filterDropdown($popup, searchString)
hides all elements in popup that doesn't match the given search string, also shows the search bar in popup

**Kind**: global function

| Param |
| --- |
| $popup |
| searchString |

<a name="selectNextItem"></a>

## selectNextItem(direction, $popUp)
Selects the next or previous item in the popup.

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| direction | <code>number</code> | +1 for next, -1 for prev |
| $popUp | | |

<a name="listenToContextMenu"></a>

## listenToContextMenu(contextMenu)
Context menus are also created in AppInit.htmlReady(), so they may not
yet have been created when we get our AppInit.htmlReady() callback, so
we provide this method to tell us when to start listening for their events
Context menus are also created in AppInit.htmlReady(), so they may notyet have been created when we get our AppInit.htmlReady() callback, sowe provide this method to tell us when to start listening for their events

**Kind**: global function

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

163 changes: 163 additions & 0 deletions src-node/git/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
const gitNodeConnector = global.createNodeConnector("phcode-git-core", exports);

const GIT_PROGRESS_EVENT = "git_progress";

let ChildProcess = require("child_process"),
crossSpawn = require('cross-spawn'),
ProcessUtils = require("./processUtils"),
processMap = {},
resolvedPaths = {};

function fixEOL(str) {
if (str[str.length - 1] === "\n") {
str = str.slice(0, -1);
}
return str;
}

// handler with ChildProcess.exec
// this won't handle cases where process outputs a large string
function execute(directory, command, args, opts, callback) {
// execute commands have to be escaped, spawn does this automatically and will fail if cmd is escaped
if (command[0] !== "\"" || command[command.length - 1] !== "\"") {
command = "\"" + command + "\"";
}
// http://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
const toExec = command + " " + args.join(" ");
processMap[opts.cliId] = ChildProcess.exec(toExec, {
cwd: directory,
maxBuffer: 20 * 1024 * 1024
}, function (err, stdout, stderr) {
delete processMap[opts.cliId];
callback(err ? fixEOL(stderr) : undefined, err ? undefined : fixEOL(stdout));
});
}

// handler with cross-spawn
function join(arr) {
let result, index = 0, length;
length = arr.reduce(function (l, b) {
return l + b.length;
}, 0);
result = new Buffer(length);
arr.forEach(function (b) {
b.copy(result, index);
index += b.length;
});
return fixEOL(result.toString("utf8"));
}

function spawn(directory, command, args, opts, callback) {
// https://github.com/creationix/node-git
const child = crossSpawn(command, args, {
cwd: directory
});
child.on("error", function (err) {
callback(err.stack, undefined);
});

processMap[opts.cliId] = child;

let exitCode, stdout = [], stderr = [];
child.stdout.addListener("data", function (text) {
stdout[stdout.length] = text;
});
child.stderr.addListener("data", function (text) {
// Git writes its informational messages (such as Successfully rebased
// and updated) to stderr. This behavior is intentional because Git uses
// stderr for all output that is not a direct result of the command (e.g.,
// status updates, progress, or errors).
if (opts.watchProgress) {
gitNodeConnector.triggerPeer(GIT_PROGRESS_EVENT, {
cliId: opts.cliId,
time: (new Date()).getTime(),
data: fixEOL(text.toString("utf8"))
});
}
stderr[stderr.length] = text;
});
child.addListener("exit", function (code) {
exitCode = code;
});
child.addListener("close", function () {
delete processMap[opts.cliId];
callback(exitCode > 0 ? join(stderr) : undefined,
exitCode > 0 ? undefined : join(stdout));
});
child.stdin.end();
}

function doIfExists(method, directory, command, args, opts, callback) {
// do not call executableExists if we already know it exists
if (resolvedPaths[command]) {
return method(directory, resolvedPaths[command], args, opts, callback);
}

ProcessUtils.executableExists(command, function (err, exists, resolvedPath) {
if (exists) {
resolvedPaths[command] = resolvedPath;
return method(directory, resolvedPath, args, opts, callback);
} else {
callback("ProcessUtils can't resolve the path requested: " + command);
}
});
}

function executeIfExists({directory, command, args, opts}) {
return new Promise(function (resolve, reject) {
doIfExists(execute, directory, command, args, opts, (err, stdout)=>{
if(err){
reject(err);
} else {
resolve(stdout);
}
});
});
}

function spawnIfExists({directory, command, args, opts}) {
return new Promise(function (resolve, reject) {
doIfExists(spawn, directory, command, args, opts, (err, stdout)=>{
if(err){
reject(err);
} else {
resolve(stdout);
}
});
});
}

function kill(cliId) {
return new Promise(function (resolve, reject) {
const process = processMap[cliId];
if (!process) {
reject("Couldn't find process to kill with ID:" + cliId);
}
delete processMap[cliId];
resolve(""); // at this point we resolve anyways as we cant do anything after deleting the object
ProcessUtils.getChildrenOfPid(process.pid, function (err, children) {
// kill also parent process
children.push(process.pid);
children.forEach(function (pid) {
ProcessUtils.killSingleProcess(pid);
});
});
});
}

function which({command}) {
return new Promise(function (resolve, reject) {
ProcessUtils.executableExists(command, function (err, exists, resolvedPath) {
if (exists) {
resolve(resolvedPath);
} else {
reject("ProcessUtils can't resolve the path requested: " + command);
}
});
});
}

exports.execute = executeIfExists;
exports.spawn = spawnIfExists;
exports.kill = kill;
exports.which = which;
58 changes: 58 additions & 0 deletions src-node/git/processUtils-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const ProcessUtils = require("./processUtils");

/*
var pid = 5064;
ProcessUtils.getChildrenOfPid(pid, function (err, children) {
console.log(children);
children.push(pid);
children.forEach(function (pid) {
ProcessUtils.killSingleProcess(pid);
});
});
*/

[
"git",
"C:\\Program Files (x86)\\Git\\cmd\\git.exe",
"C:/Program Files (x86)/Git/cmd/git.exe",
"C:/Program Files (x86)/Git/cmd/git2.exe",
"C:/Program Files (x86)/Git/cmd/",
"C:/Program Files (x86)/Git/cmd",
"C:\\Program Files (x86)\\Git\\Git Bash.vbs"
].forEach(function (path) {

ProcessUtils.executableExists(path, function (err, exists, resolvedPath) {
console.log("ProcessUtils.executableExists for: " + path);
console.log(" - exists: " + exists);
console.log(" - resolvedPath: " + resolvedPath);
});

});

/*
ProcessUtils.executableExists("git", function (err, result, resolvedPath) {
console.log("git");
console.log(result);
});
ProcessUtils.executableExists("C:\\Program Files (x86)\\Git\\cmd\\git.exe", function (err, result) {
console.log("result for C:\\Program Files (x86)\\Git\\cmd\\git.exe");
console.log(result);
});
ProcessUtils.executableExists("C:/Program Files (x86)/Git/cmd/git.exe", function (err, result) {
console.log("result for C:/Program Files (x86)/Git/cmd/git.exe");
console.log(result);
});

ProcessUtils.executableExists("C:/Program Files (x86)/Git/cmd/git2.exe", function (err, result) {
console.log("result for C:/Program Files (x86)/Git/cmd/git2.exe");
console.log(result);
});
ProcessUtils.executableExists("C:/Program Files (x86)/Git/cmd/", function (err, result) {
console.log("result for C:/Program Files (x86)/Git/cmd/");
console.log(result);
});
ProcessUtils.executableExists("C:/Program Files (x86)/Git/cmd", function (err, result) {
console.log("result for C:/Program Files (x86)/Git/cmd");
console.log(result);
});
*/
Loading
Loading