Skip to content

Commit

Permalink
Add support for proccess's output being shown in a grid
Browse files Browse the repository at this point in the history
Signed-off-by: lneves12 <[email protected]>
  • Loading branch information
lneves12 committed Nov 5, 2020
1 parent b62a2a1 commit 3988312
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 7 deletions.
7 changes: 7 additions & 0 deletions bin/concurrently.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ const args = yargs
'and concurrently coloring.',
type: 'boolean'
},
'grid': {
describe:
'Output of processes will be formatted inside a grid.\n' +
'Experimental layout.',
type: 'boolean'
},
// This one is provided for free. Chalk reads this itself and removes colours.
// https://www.npmjs.com/package/chalk#chalksupportscolor
'no-color': {
Expand Down Expand Up @@ -161,6 +167,7 @@ concurrently(args._.map((command, index) => {
: (args.killOthersOnFail ? ['failure'] : []),
maxProcesses: args.maxProcesses,
raw: args.raw,
grid: args.grid,
prefix: args.prefix,
prefixLength: args.prefixLength,
restartDelay: args.restartAfter,
Expand Down
11 changes: 11 additions & 0 deletions bin/concurrently.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,17 @@ describe('--raw', () => {
});
});

describe('--grid', () => {
it('contains output', done => {
const child = run('--grid "echo foo" "echo bar"');
child.log.pipe(buffer(child.close)).subscribe(lines => {
expect(lines).toContainEqual(expect.stringContaining('foo'));
expect(lines).toContainEqual(expect.stringContaining('bar'));
done();
}, done);
});
});

describe('--names', () => {
it('is aliased to -n', done => {
const child = run('-n foo,bar "echo foo" "echo bar"');
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module.exports = (commands, options = {}) => {
successCondition: options.successCondition,
controllers: [
new LogError({ logger }),
new LogOutput({ logger }),
new LogOutput({ logger, grid: options.grid }),
new LogExit({ logger }),
new InputHandler({
logger,
Expand Down
5 changes: 5 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"chalk": "^3.0.0",
"date-fns": "^2.16.1",
"lodash": "^4.17.20",
"neo-blessed": "^0.2.0",
"read-pkg": "^5.2.0",
"rxjs": "^6.6.3",
"spawn-command": "^0.0.2-1",
Expand Down
1 change: 1 addition & 0 deletions src/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = {
// How many bytes we'll show on the command prefix
prefixLength: 10,
raw: false,
grid: false,
// Number of attempts of restarting a process, if it exits with non-0 code
restartTries: 0,
// How many milliseconds concurrently should wait before restarting a process.
Expand Down
104 changes: 99 additions & 5 deletions src/flow-control/log-output.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,107 @@
const Rx = require('rxjs');
const blessed = require('neo-blessed');

const DEFAULT_SCROLL_OPTIONS = {
scrollable: true,
input: true,
alwaysScroll: true,
scrollbar: {
ch: ' ',
inverse: true
},
keys: true,
vi: true,
mouse: true
};

module.exports = class LogOutput {
constructor({ logger }) {
constructor({ logger, grid }) {
this.logger = logger;
this.grid = grid;
}

handle(commands) {
commands.forEach(command => {
command.stdout.subscribe(text => this.logger.logCommandText(text.toString(), command));
command.stderr.subscribe(text => this.logger.logCommandText(text.toString(), command));
});
if (this.grid) {
const screen = blessed.screen({
smartCSR: true,
dockBorders: false,
fullUnicode: true,
});

// Makes sure when the screen is killed, all the child commands are properly killed
screen.key(['escape', 'q', 'C-c'], function(ch, key) {
const killObservables = commands.map(command => {
return Rx.Observable.create(observer => {
command.kill('SIGINT', error => {
console.error(error);
observer.complete();
});
});
});

Rx.forkJoin(killObservables).subscribe({
complete() {
process.kill(process.pid, 'SIGINT');
}
});
});

const numberOfRows = Math.ceil(commands.length / 2);

commands.forEach((command, index) => {
// Calculate grid positions for current command
const leftPosition = index % 2 === 0 ? '0%' : '50%';
const rowHeight = (100 / numberOfRows);
const commandRowNr = Math.floor(index / 2);
const topPosition = commandRowNr * rowHeight;

let boxWidth = '50%';
// it should be fullwidth if it's the last element of an odd number of commands.
// we only support a grid of 2 columns for now
const isFullWidth = commands.length === index + 1 && commands.length % 2 !== 0;
if (isFullWidth) {
boxWidth = '100%';
}

const commandPrefix = this.logger.getPrefix(command);
const blessedBox = blessed.box({
label: commandPrefix,
width: boxWidth,
height: `${rowHeight}%`,
left: leftPosition,
top: `${topPosition}%`,
border: {
type: 'line',
},
style: {
border: {
fg: command.prefixColor.toString()
}
}
});
const blessedBoxLogger = blessed.log(
Object.assign({}, DEFAULT_SCROLL_OPTIONS, {
parent: blessedBox,
tags: true,
})
);
screen.append(blessedBox);

command.stdout.subscribe(text => {
blessedBoxLogger.log(text.toString());
blessedBoxLogger.screen.render();
});
command.stderr.subscribe(text => {
blessedBoxLogger.log(text.toString());
blessedBoxLogger.screen.render();
});
});
} else {
commands.forEach(command => {
command.stdout.subscribe(text => this.logger.logCommandText(text.toString(), command));
command.stderr.subscribe(text => this.logger.logCommandText(text.toString(), command));
});
}

return commands;
}
Expand Down
2 changes: 1 addition & 1 deletion src/flow-control/log-output.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ beforeEach(() => {
];

logger = createMockInstance(Logger);
controller = new LogOutput({ logger });
controller = new LogOutput({ logger, grid: false });
});

it('returns same commands', () => {
Expand Down

0 comments on commit 3988312

Please sign in to comment.