Skip to content

Commit e937348

Browse files
committed
Merge pull request #10 from clue-labs/redraw
Only redraw() readline when necessary
2 parents 5e8a48f + dfbc55f commit e937348

File tree

2 files changed

+119
-12
lines changed

2 files changed

+119
-12
lines changed

src/Readline.php

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,13 @@ public function __construct($output)
6565
*/
6666
public function setPrompt($prompt)
6767
{
68+
if ($prompt === $this->prompt) {
69+
return $this;
70+
}
71+
6872
$this->prompt = $prompt;
69-
$this->redraw();
7073

71-
return $this;
74+
return $this->redraw();
7275
}
7376

7477
/**
@@ -84,8 +87,16 @@ public function setPrompt($prompt)
8487
*/
8588
public function setEcho($echo)
8689
{
90+
if ($echo === $this->echo) {
91+
return $this;
92+
}
93+
8794
$this->echo = !!$echo;
88-
$this->redraw();
95+
96+
// only redraw if there is any input
97+
if ($this->linebuffer !== '') {
98+
$this->redraw();
99+
}
89100

90101
return $this;
91102
}
@@ -104,10 +115,7 @@ public function setMove($move)
104115
{
105116
$this->move = !!$move;
106117

107-
$this->linepos = $this->strlen($this->linebuffer);
108-
$this->redraw();
109-
110-
return $this;
118+
return $this->moveCursorTo($this->strlen($this->linebuffer));
111119
}
112120

113121
/**
@@ -137,9 +145,17 @@ public function getCursorPosition()
137145
*/
138146
public function setInput($input)
139147
{
148+
if ($this->linebuffer === $input) {
149+
return $this;
150+
}
151+
140152
$this->linebuffer = $input;
141153
$this->linepos = $this->strlen($this->linebuffer);
142-
$this->redraw();
154+
155+
// only redraw if input should be echo'ed (i.e. is not hidden anyway)
156+
if ($this->echo) {
157+
$this->redraw();
158+
}
143159

144160
return $this;
145161
}
@@ -189,9 +205,15 @@ public function setAutocomplete(AutocompleteInterface $autocomplete = null)
189205
/**
190206
* redraw the current input prompt
191207
*
208+
* Usually, there should be no need to to call this method manually. It will
209+
* be invoked automatically whenever we detect the readline input needs to
210+
* be (re)written to the output.
211+
*
192212
* Clear the current line and draw the input prompt. If input echo is
193213
* enabled, will also draw the current input buffer and move to the current
194214
* input buffer position.
215+
*
216+
* @return self
195217
*/
196218
public function redraw()
197219
{
@@ -209,6 +231,8 @@ public function redraw()
209231
}
210232
}
211233
$this->write($output);
234+
235+
return $this;
212236
}
213237

214238
public function clear()
@@ -358,11 +382,15 @@ public function moveCursorBy($n)
358382
public function moveCursorTo($n)
359383
{
360384
if ($n < 0 || $n === $this->linepos || $n > $this->strlen($this->linebuffer)) {
361-
return;
385+
return $this;
362386
}
363387

364388
$this->linepos = $n;
365-
$this->redraw();
389+
390+
// only redraw if cursor is actually visible
391+
if ($this->echo) {
392+
$this->redraw();
393+
}
366394

367395
return $this;
368396
}

tests/ReadlineTest.php

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ class ReadlineTest extends TestCase
66
{
77
public function setUp()
88
{
9-
$output = $this->getMockBuilder('Clue\React\Stdio\Stdout')->disableOriginalConstructor()->getMock();
10-
$this->readline = new Readline($output);
9+
$this->output = $this->getMockBuilder('Clue\React\Stdio\Stdout')->disableOriginalConstructor()->getMock();
10+
$this->readline = new Readline($this->output);
1111
}
1212

1313
public function testSettersReturnSelf()
@@ -45,4 +45,83 @@ public function testMultiByteInput()
4545
$this->assertEquals('täst', $this->readline->getInput());
4646
$this->assertEquals(4, $this->readline->getCursorPosition());
4747
}
48+
49+
public function testRedrawingReadlineWritesToOutputOnce()
50+
{
51+
$this->readline->setPrompt('> ');
52+
$this->readline->setInput('test');
53+
$this->readline->moveCursorBy(-2);
54+
55+
$this->output->expects($this->once())->method('write')->with($this->equalTo("\r\033[K" . "> " . "test" . "\x08\x08"));
56+
$this->assertSame($this->readline, $this->readline->redraw());
57+
}
58+
59+
public function testSettingSameSettingsDoesNotNeedToRedraw()
60+
{
61+
$this->readline->setPrompt('> ');
62+
$this->readline->setInput('test');
63+
$this->readline->moveCursorBy(-2);
64+
65+
$this->output->expects($this->never())->method('write');
66+
67+
$this->assertSame($this->readline, $this->readline->setPrompt('> '));
68+
$this->assertSame($this->readline, $this->readline->setInput('test'));
69+
$this->assertSame($this->readline, $this->readline->moveCursorTo(2));
70+
}
71+
72+
public function testSettingEchoOnWithInputDoesRedraw()
73+
{
74+
$this->readline->setEcho(false);
75+
$this->readline->setPrompt('> ');
76+
$this->readline->setInput('test');
77+
78+
$this->output->expects($this->once())->method('write')->with($this->equalTo("\r\033[K" . "> " . "test"));
79+
80+
$this->assertSame($this->readline, $this->readline->setEcho(true));
81+
}
82+
83+
public function testSettingEchoOffWithInputDoesRedraw()
84+
{
85+
$this->readline->setEcho(true);
86+
$this->readline->setPrompt('> ');
87+
$this->readline->setInput('test');
88+
89+
$this->output->expects($this->once())->method('write')->with($this->equalTo("\r\033[K" . "> "));
90+
91+
$this->assertSame($this->readline, $this->readline->setEcho(false));
92+
}
93+
94+
public function testSettingEchoWithoutInputDoesNotNeedToRedraw()
95+
{
96+
$this->output->expects($this->never())->method('write');
97+
98+
$this->assertSame($this->readline, $this->readline->setEcho(false));
99+
$this->assertSame($this->readline, $this->readline->setEcho(true));
100+
}
101+
102+
public function testSettingInputDoesRedraw()
103+
{
104+
$this->output->expects($this->once())->method('write');
105+
$this->assertSame($this->readline, $this->readline->setInput('test'));
106+
}
107+
108+
public function testSettingInputWithoutEchoDoesNotNeedToRedraw()
109+
{
110+
$this->readline->setEcho(false);
111+
112+
$this->output->expects($this->never())->method('write');
113+
114+
$this->assertSame($this->readline, $this->readline->setInput('test'));
115+
}
116+
117+
public function testMovingCursorWithoutEchoDoesNotNeedToRedraw()
118+
{
119+
$this->readline->setEcho(false);
120+
$this->readline->setInput('test');
121+
122+
$this->output->expects($this->never())->method('write');
123+
124+
$this->assertSame($this->readline, $this->readline->moveCursorTo(0));
125+
$this->assertSame($this->readline, $this->readline->moveCursorBy(2));
126+
}
48127
}

0 commit comments

Comments
 (0)