Skip to content
Open
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
11 changes: 9 additions & 2 deletions node/lib/cmd/status.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ exports.executeableSubcommand = co.wrap(function *(args) {
const GitUtil = require("../util/git_util");
const PrintStatusUtil = require("../util/print_status_util");
const StatusUtil = require("../util/status_util");
const ConfigUtil = require("../../lib/util/config_util");
const ColorHandler = require("../util/color_handler").ColorHandler;

const repo = yield GitUtil.getCurrentRepo();
const workdir = repo.workdir();
Expand All @@ -115,11 +117,16 @@ exports.executeableSubcommand = co.wrap(function *(args) {

const relCwd = path.relative(workdir, cwd);

const colors = new ColorHandler(
yield ConfigUtil.getConfigColorBool(repo, "color.status"));

let text;
if (args.shortFormat) {
text = PrintStatusUtil.printRepoStatusShort(repoStatus, relCwd);
text = PrintStatusUtil.printRepoStatusShort(repoStatus, relCwd,
{colors: colors});
} else {
text = PrintStatusUtil.printRepoStatus(repoStatus, relCwd);
text = PrintStatusUtil.printRepoStatus(repoStatus, relCwd,
{colors: colors});
}

process.stdout.write(text);
Expand Down
40 changes: 40 additions & 0 deletions node/lib/util/color_handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const colorsSafe = require("colors/safe");

/**
* This class is a wrapper around colors/safe that uses a color setting from
* the git meta config to determine if colors should be enabled.
*/
class ColorHandler {
/**
* @param {Bool|"auto"|null} enableColor
*
* NOTE: enableColor should probably be set based on a value in
* ConfigUtil.getConfigColorBool()
*/
constructor(enableColor) {
colorsSafe.enable();
if(enableColor === "auto" || enableColor === undefined) {
// Enable color if we're outputting to a terminal, otherwise disable
// since we're piping the output.
enableColor = process.stdout.isTTY === true;
}

this.enableColor = enableColor;

let self = this;

// add a passthrough function for each supported color
["blue", "cyan", "green", "grey", "magenta", "red", "yellow"].forEach(
function(color) {
self[color] = function(string) {
if(self.enableColor) {
return colorsSafe[color](string);
} else {
return string;
}
};
});
}
}

exports.ColorHandler = ColorHandler;
49 changes: 46 additions & 3 deletions node/lib/util/config_util.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ exports.getConfigString = co.wrap(function *(config, key) {
* @param {NodeGit.Repository} repo
* @param {NodeGit.Commit} configVar
* @return {Bool|null}
* @throws if the configuration variable doesn't exist
* @throws if the configuration value isn't valid.
*/
exports.configIsTrue = co.wrap(function*(repo, configVar) {
assert.instanceOf(repo, NodeGit.Repository);
Expand All @@ -79,10 +79,53 @@ exports.configIsTrue = co.wrap(function*(repo, configVar) {
if (null === configured) {
return configured; // RETURN
}
return configured === "true" || configured === "yes" ||
configured === "on";
if(configured === "true" || configured === "yes" ||
configured === "on" || configured === "1") {
return true;
} else if (configured === "false" || configured === "no" ||
configured === "off" || configured === "0") {
return false;
} else {
throw new UserError("fatal: bad boolean config value '" +
configured + "' for '" + configVar + "'");
}
});

/**
* Returns whether a color boolean is true. If the values is auto, pass
* the value along so it can infer whether colors should be used.
* @async
* @param {NodeGit.Repository} repo
* @param {NodeGit.Commit} configVar
* @return {Bool|"auto"}
*/
exports.getConfigColorBool = co.wrap(function*(repo, configVar) {
// using same logic from git_config_colorbool() in git's color.c
// except, if unset, use default rather than color.ui becuase that's not
// defined right now.
assert.instanceOf(repo, NodeGit.Repository);
assert.isString(configVar);

const config = yield repo.config();
const val = yield exports.getConfigString(config, configVar);

if(val === "never") {
return false;
} else if(val === "always") {
return true;
} else if(val === "auto") {
return "auto";
}

const is_true = yield exports.configIsTrue(repo, configVar);

// a truthy or unset value implies "auto"
if(is_true === null || is_true) {
return "auto";
} else {
return false;
}
});

/**
* Returns the default Signature for a repo. Replaces repo.defaultSignature,
Expand Down
52 changes: 41 additions & 11 deletions node/lib/util/print_status_util.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,15 @@
*
*/

const assert = require("chai").assert;
const colors = require("colors/safe");
const path = require("path");
const assert = require("chai").assert;
const path = require("path");

const GitUtil = require("./git_util");
const SequencerState = require("./sequencer_state");
const RepoStatus = require("./repo_status");
const TextUtil = require("./text_util");
const ColorHandler = require("./color_handler").ColorHandler;


/**
* This value-semantic class describes a line entry to be printed in a status
Expand Down Expand Up @@ -383,7 +384,14 @@ A ${command} is in progress.
* @param {RepoStatus} status
* @return {String>
*/
exports.printCurrentBranch = function (status) {
exports.printCurrentBranch = function (status, options) {
// fill in default value
if (options === undefined) {options = {};}
if (options.colors === undefined) {
options.colors = new ColorHandler();
}
let colors = options.colors;

if (null !== status.currentBranchName) {
return `On branch ${colors.green(status.currentBranchName)}.\n`;
}
Expand All @@ -399,16 +407,26 @@ On detached head ${colors.red(GitUtil.shortSha(status.headCommit))}.\n`;
* the specified `cwd`. Note that a value of "" for `cwd` indicates the root
* of the repository.
*
* @param {RepoStatus} status
* @param {String} cwd
* @param {RepoStatus} status
* @param {String} cwd
* @param {Object} [options]
* @param {ColorHandler} [options.colors]
*/
exports.printRepoStatus = function (status, cwd) {
exports.printRepoStatus = function (status, cwd, options) {
assert.instanceOf(status, RepoStatus);
assert.isString(cwd);

// fill in default value
if (options === undefined) {options = {};}
if (options.colors === undefined) {
options.colors = new ColorHandler();
}

const colors = options.colors;

let result = "";

result += exports.printCurrentBranch(status);
result += exports.printCurrentBranch(status, options);

if (null !== status.sequencerState) {
result += exports.printSequencer(status.sequencerState);
Expand Down Expand Up @@ -464,13 +482,23 @@ Untracked files:
* paths relative to the specified `cwd`. Note that a value of "" for
* `cwd` indicates the root of the repository.
*
* @param {RepoStatus} status
* @param {String} cwd
* @param {RepoStatus} status
* @param {String} cwd
* @param {Object} [options]
* @param {ColorHandler} [options.colors]
*/
exports.printRepoStatusShort = function (status, cwd) {
exports.printRepoStatusShort = function (status, cwd, options) {
assert.instanceOf(status, RepoStatus);
assert.isString(cwd);

// fill in default values for options
if (options === undefined) { options = {}; }
if (options.colors === undefined) {
options.colors = new ColorHandler();
}

const colors = options.colors;

let result = "";

const indexChangesByPath = {};
Expand Down Expand Up @@ -540,6 +568,8 @@ exports.printSubmoduleStatus = function (relCwd,
assert.isObject(subsToPrint);
assert.isBoolean(showClosed);

const colors = new ColorHandler("auto");

let result = "";
if (showClosed) {
result = `${colors.grey("All submodules:")}\n`;
Expand Down
39 changes: 39 additions & 0 deletions node/test/util/color_handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const assert = require("chai").assert;
const ColorHandler = require("../../lib/util/color_handler").ColorHandler;

const realProcess = process;
describe("ColorHandler", function() {
describe("colors", function() {
describe("enableColor = 'auto'", function() {
afterEach(function() {
// this test futzes around with the process global so
// make sure it is properly reset after each test.
global.process = realProcess;
});
it("enables color when in a TTY", function() {
global.process = {stdout: {isTTY: true}};
const colors = new ColorHandler("auto");
assert.isTrue(colors.enableColor);
});

it("disables color when not in a TTY", function() {
global.process = {stdout: {isTTY: false}};
const colors = new ColorHandler("auto");
assert.isFalse(colors.enableColor);
assert.equal("test", colors.blue("test"));
});

it("adds colors if enableColor", function() {
const colors = new ColorHandler();
colors.enableColor = true;
assert.equal("\u001b[34mtest\u001b[39m", colors.blue("test"));
});

it("passes through string if !enableColor", function() {
const colors = new ColorHandler();
colors.enableColor = false;
assert.equal("test", colors.blue("test"));
});
});
});
});
67 changes: 65 additions & 2 deletions node/test/util/config_util.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const path = require("path");

const ConfigUtil = require("../../lib/util/config_util");
const TestUtil = require("../../lib/util/test_util");
const UserError = require("../../lib/util/user_error");

describe("ConfigUtil", function () {
describe("getConfigString", function () {
Expand All @@ -55,6 +56,49 @@ describe("getConfigString", function () {
assert.isNull(badResult);
}));
});

describe("getConfigColorBool", function() {
const cases = {
"never": {
value: "never",
expected: false,
},
"auto": {
value: "auto",
expected: "auto",
},
"unspecified": {
expected: "auto",
},
"true": {
value: "true",
expected: "auto",
},
"always": {
value: "always",
expected: true,
},
};

Object.keys(cases).forEach(caseName => {
const c = cases[caseName];
it(caseName, co.wrap(function *() {
const repo = yield TestUtil.createSimpleRepository();
if ("value" in c) {
const configPath = path.join(repo.path(), "config");
yield fs.appendFile(configPath, `\
[foo]
bar = ${c.value}
`);
}
const result = yield ConfigUtil.getConfigColorBool(repo,
"foo.bar");
assert.equal(result, c.expected);
}));
});
});


describe("configIsTrue", function () {
const cases = {
"missing": {
Expand All @@ -76,6 +120,11 @@ describe("configIsTrue", function () {
value: "on",
expected: true,
},
"invalid value": {
value: "asdf",
expected: new UserError(
"fatal: bad boolean config value 'asdf' for 'foo.bar'"),
},
};
Object.keys(cases).forEach(caseName => {
const c = cases[caseName];
Expand All @@ -88,8 +137,22 @@ describe("configIsTrue", function () {
bar = ${c.value}
`);
}
const result = yield ConfigUtil.configIsTrue(repo, "foo.bar");
assert.equal(result, c.expected);
let thrownException = null;
let result = null;
try {
result = yield ConfigUtil.configIsTrue(repo, "foo.bar");
} catch (e) {
thrownException = e;
}
if(c.expected instanceof UserError) {
assert.equal(c.expected.message, thrownException.message);
} else {
// we didn't expect an exception. Rethrow it.
if(thrownException !== null) {
throw thrownException;
}
assert.equal(result, c.expected);
}
}));
});
});
Expand Down