Skip to content

Commit ffd3760

Browse files
wyattadespaescuj
andauthored
Support REPL languages that use colons (#393)
Co-authored-by: Pascal Jufer <[email protected]>
1 parent 4f94ad7 commit ffd3760

File tree

2 files changed

+55
-19
lines changed

2 files changed

+55
-19
lines changed

src/flow-control/input-handler.spec.ts

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,48 @@ it('forwards input stream to target index specified in input', () => {
5454
controller.handle(commands);
5555

5656
inputStream.write('1:something');
57+
inputStream.write('1:multi\nline\n');
5758

5859
expect(commands[0].stdin?.write).not.toHaveBeenCalled();
59-
expect(commands[1].stdin?.write).toHaveBeenCalledTimes(1);
60+
expect(commands[1].stdin?.write).toHaveBeenCalledTimes(2);
6061
expect(commands[1].stdin?.write).toHaveBeenCalledWith('something');
62+
expect(commands[1].stdin?.write).toHaveBeenCalledWith('multi\nline\n');
6163
});
6264

6365
it('forwards input stream to target index specified in input when input contains colon', () => {
6466
controller.handle(commands);
6567

66-
inputStream.emit('data', Buffer.from('1::something'));
6768
inputStream.emit('data', Buffer.from('1:some:thing'));
69+
inputStream.emit('data', Buffer.from('1: :something'));
70+
inputStream.emit('data', Buffer.from('1::something'));
6871

6972
expect(commands[0].stdin?.write).not.toHaveBeenCalled();
70-
expect(commands[1].stdin?.write).toHaveBeenCalledTimes(2);
71-
expect(commands[1].stdin?.write).toHaveBeenCalledWith(':something');
73+
expect(commands[1].stdin?.write).toHaveBeenCalledTimes(3);
7274
expect(commands[1].stdin?.write).toHaveBeenCalledWith('some:thing');
75+
expect(commands[1].stdin?.write).toHaveBeenCalledWith(' :something');
76+
expect(commands[1].stdin?.write).toHaveBeenCalledWith(':something');
77+
});
78+
79+
it('does not forward input stream when input contains colon in a different format', () => {
80+
controller.handle(commands);
81+
82+
inputStream.emit('data', Buffer.from('Ruby0::Const::Syntax'));
83+
inputStream.emit('data', Buffer.from('1:Ruby1::Const::Syntax'));
84+
inputStream.emit('data', Buffer.from('ruby_symbol_arg :my_symbol'));
85+
inputStream.emit('data', Buffer.from('ruby_symbol_arg(:my_symbol)'));
86+
inputStream.emit('data', Buffer.from('{ruby_key: :my_val}'));
87+
inputStream.emit('data', Buffer.from('{:ruby_key=>:my_val}'));
88+
inputStream.emit('data', Buffer.from('js_obj = {key: "my_val"}'));
89+
90+
expect(commands[1].stdin?.write).toHaveBeenCalledTimes(1);
91+
expect(commands[1].stdin?.write).toHaveBeenCalledWith('Ruby1::Const::Syntax');
92+
expect(commands[0].stdin?.write).toHaveBeenCalledTimes(6);
93+
expect(commands[0].stdin?.write).toHaveBeenCalledWith('Ruby0::Const::Syntax');
94+
expect(commands[0].stdin?.write).toHaveBeenCalledWith('ruby_symbol_arg :my_symbol');
95+
expect(commands[0].stdin?.write).toHaveBeenCalledWith('ruby_symbol_arg(:my_symbol)');
96+
expect(commands[0].stdin?.write).toHaveBeenCalledWith('{ruby_key: :my_val}');
97+
expect(commands[0].stdin?.write).toHaveBeenCalledWith('{:ruby_key=>:my_val}');
98+
expect(commands[0].stdin?.write).toHaveBeenCalledWith('js_obj = {key: "my_val"}');
7399
});
74100

75101
it('forwards input stream to target name specified in input', () => {
@@ -90,20 +116,19 @@ it('logs error if command has no stdin open', () => {
90116

91117
expect(commands[1].stdin?.write).not.toHaveBeenCalled();
92118
expect(logger.logGlobalEvent).toHaveBeenCalledWith(
93-
'Unable to find command 0, or it has no stdin open\n'
119+
'Unable to find command "0", or it has no stdin open\n'
94120
);
95121
});
96122

97-
it('logs error if command is not found', () => {
123+
it('fallback to default input stream if command is not found', () => {
98124
controller.handle(commands);
99125

100126
inputStream.write('foobar:something');
101127

102-
expect(commands[0].stdin?.write).not.toHaveBeenCalled();
128+
expect(commands[0].stdin?.write).toHaveBeenCalledTimes(1);
129+
expect(commands[0].stdin?.write).toHaveBeenCalledWith('foobar:something');
103130
expect(commands[1].stdin?.write).not.toHaveBeenCalled();
104-
expect(logger.logGlobalEvent).toHaveBeenCalledWith(
105-
'Unable to find command foobar, or it has no stdin open\n'
106-
);
131+
expect(logger.logGlobalEvent).not.toHaveBeenCalled();
107132
});
108133

109134
it('pauses input stream when finished', () => {

src/flow-control/input-handler.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,35 @@ export class InputHandler implements FlowController {
4848
return { commands };
4949
}
5050

51+
const commandsMap = new Map<string, Command>();
52+
for (const command of commands) {
53+
commandsMap.set(command.index.toString(), command);
54+
commandsMap.set(command.name, command);
55+
}
56+
5157
Rx.fromEvent(inputStream, 'data')
5258
.pipe(map((data) => String(data)))
5359
.subscribe((data) => {
54-
const dataParts = data.split(/:(.+)/);
55-
const targetId = dataParts.length > 1 ? dataParts[0] : this.defaultInputTarget;
56-
const input = dataParts[1] || data;
60+
let command: Command | undefined, input: string;
61+
62+
const dataParts = data.split(/:(.+)/s);
63+
let target = dataParts[0];
5764

58-
const command = commands.find(
59-
(command) =>
60-
command.name === targetId ||
61-
command.index.toString() === targetId.toString()
62-
);
65+
if (dataParts.length > 1 && (command = commandsMap.get(target))) {
66+
input = dataParts[1];
67+
} else {
68+
// If `target` does not match a registered command,
69+
// fallback to `defaultInputTarget` and forward the whole input data
70+
target = this.defaultInputTarget.toString();
71+
command = commandsMap.get(target);
72+
input = data;
73+
}
6374

6475
if (command && command.stdin) {
6576
command.stdin.write(input);
6677
} else {
6778
this.logger.logGlobalEvent(
68-
`Unable to find command ${targetId}, or it has no stdin open\n`
79+
`Unable to find command "${target}", or it has no stdin open\n`
6980
);
7081
}
7182
});

0 commit comments

Comments
 (0)