From f9b5d8dea3ac73268a1f5c1f9ef1d688adecbcbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 15 May 2015 00:18:12 +0200 Subject: [PATCH 1/3] Readline::redraw() can now be chained, add documentation and tests --- src/Readline.php | 8 ++++++++ tests/ReadlineTest.php | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Readline.php b/src/Readline.php index 71bf4c4..e19a795 100644 --- a/src/Readline.php +++ b/src/Readline.php @@ -189,9 +189,15 @@ public function setAutocomplete(AutocompleteInterface $autocomplete = null) /** * redraw the current input prompt * + * Usually, there should be no need to to call this method manually. It will + * be invoked automatically whenever we detect the readline input needs to + * be (re)written to the output. + * * Clear the current line and draw the input prompt. If input echo is * enabled, will also draw the current input buffer and move to the current * input buffer position. + * + * @return self */ public function redraw() { @@ -209,6 +215,8 @@ public function redraw() } } $this->write($output); + + return $this; } public function clear() diff --git a/tests/ReadlineTest.php b/tests/ReadlineTest.php index fa80071..ad92658 100644 --- a/tests/ReadlineTest.php +++ b/tests/ReadlineTest.php @@ -6,8 +6,8 @@ class ReadlineTest extends TestCase { public function setUp() { - $output = $this->getMockBuilder('Clue\React\Stdio\Stdout')->disableOriginalConstructor()->getMock(); - $this->readline = new Readline($output); + $this->output = $this->getMockBuilder('Clue\React\Stdio\Stdout')->disableOriginalConstructor()->getMock(); + $this->readline = new Readline($this->output); } public function testSettersReturnSelf() @@ -45,4 +45,14 @@ public function testMultiByteInput() $this->assertEquals('täst', $this->readline->getInput()); $this->assertEquals(4, $this->readline->getCursorPosition()); } + + public function testRedrawingReadlineWritesToOutputOnce() + { + $this->readline->setPrompt('> '); + $this->readline->setInput('test'); + $this->readline->moveCursorBy(-2); + + $this->output->expects($this->once())->method('write')->with($this->equalTo("\r\033[K" . "> " . "test" . "\x08\x08")); + $this->assertSame($this->readline, $this->readline->redraw()); + } } From 6d4ff15972fa85e0f5cfc75a847cc8e38177978d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 15 May 2015 00:19:31 +0200 Subject: [PATCH 2/3] Take advantage of redraw() chaining --- src/Readline.php | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Readline.php b/src/Readline.php index e19a795..8cbf010 100644 --- a/src/Readline.php +++ b/src/Readline.php @@ -66,9 +66,8 @@ public function __construct($output) public function setPrompt($prompt) { $this->prompt = $prompt; - $this->redraw(); - return $this; + return $this->redraw(); } /** @@ -85,9 +84,8 @@ public function setPrompt($prompt) public function setEcho($echo) { $this->echo = !!$echo; - $this->redraw(); - return $this; + return $this->redraw(); } /** @@ -105,9 +103,8 @@ public function setMove($move) $this->move = !!$move; $this->linepos = $this->strlen($this->linebuffer); - $this->redraw(); - return $this; + return $this->redraw(); } /** @@ -139,9 +136,8 @@ public function setInput($input) { $this->linebuffer = $input; $this->linepos = $this->strlen($this->linebuffer); - $this->redraw(); - return $this; + return $this->redraw(); } /** @@ -370,9 +366,8 @@ public function moveCursorTo($n) } $this->linepos = $n; - $this->redraw(); - return $this; + return $this->redraw(); } /** From dfbc55fd0ccb0f75d42ee286347221ceca3bb74a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 15 May 2015 00:49:55 +0200 Subject: [PATCH 3/3] Only redraw() when necessary --- src/Readline.php | 39 +++++++++++++++++++----- tests/ReadlineTest.php | 69 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 7 deletions(-) diff --git a/src/Readline.php b/src/Readline.php index 8cbf010..d9ced50 100644 --- a/src/Readline.php +++ b/src/Readline.php @@ -65,6 +65,10 @@ public function __construct($output) */ public function setPrompt($prompt) { + if ($prompt === $this->prompt) { + return $this; + } + $this->prompt = $prompt; return $this->redraw(); @@ -83,9 +87,18 @@ public function setPrompt($prompt) */ public function setEcho($echo) { + if ($echo === $this->echo) { + return $this; + } + $this->echo = !!$echo; - return $this->redraw(); + // only redraw if there is any input + if ($this->linebuffer !== '') { + $this->redraw(); + } + + return $this; } /** @@ -102,9 +115,7 @@ public function setMove($move) { $this->move = !!$move; - $this->linepos = $this->strlen($this->linebuffer); - - return $this->redraw(); + return $this->moveCursorTo($this->strlen($this->linebuffer)); } /** @@ -134,10 +145,19 @@ public function getCursorPosition() */ public function setInput($input) { + if ($this->linebuffer === $input) { + return $this; + } + $this->linebuffer = $input; $this->linepos = $this->strlen($this->linebuffer); - return $this->redraw(); + // only redraw if input should be echo'ed (i.e. is not hidden anyway) + if ($this->echo) { + $this->redraw(); + } + + return $this; } /** @@ -362,12 +382,17 @@ public function moveCursorBy($n) public function moveCursorTo($n) { if ($n < 0 || $n === $this->linepos || $n > $this->strlen($this->linebuffer)) { - return; + return $this; } $this->linepos = $n; - return $this->redraw(); + // only redraw if cursor is actually visible + if ($this->echo) { + $this->redraw(); + } + + return $this; } /** diff --git a/tests/ReadlineTest.php b/tests/ReadlineTest.php index ad92658..1600a68 100644 --- a/tests/ReadlineTest.php +++ b/tests/ReadlineTest.php @@ -55,4 +55,73 @@ public function testRedrawingReadlineWritesToOutputOnce() $this->output->expects($this->once())->method('write')->with($this->equalTo("\r\033[K" . "> " . "test" . "\x08\x08")); $this->assertSame($this->readline, $this->readline->redraw()); } + + public function testSettingSameSettingsDoesNotNeedToRedraw() + { + $this->readline->setPrompt('> '); + $this->readline->setInput('test'); + $this->readline->moveCursorBy(-2); + + $this->output->expects($this->never())->method('write'); + + $this->assertSame($this->readline, $this->readline->setPrompt('> ')); + $this->assertSame($this->readline, $this->readline->setInput('test')); + $this->assertSame($this->readline, $this->readline->moveCursorTo(2)); + } + + public function testSettingEchoOnWithInputDoesRedraw() + { + $this->readline->setEcho(false); + $this->readline->setPrompt('> '); + $this->readline->setInput('test'); + + $this->output->expects($this->once())->method('write')->with($this->equalTo("\r\033[K" . "> " . "test")); + + $this->assertSame($this->readline, $this->readline->setEcho(true)); + } + + public function testSettingEchoOffWithInputDoesRedraw() + { + $this->readline->setEcho(true); + $this->readline->setPrompt('> '); + $this->readline->setInput('test'); + + $this->output->expects($this->once())->method('write')->with($this->equalTo("\r\033[K" . "> ")); + + $this->assertSame($this->readline, $this->readline->setEcho(false)); + } + + public function testSettingEchoWithoutInputDoesNotNeedToRedraw() + { + $this->output->expects($this->never())->method('write'); + + $this->assertSame($this->readline, $this->readline->setEcho(false)); + $this->assertSame($this->readline, $this->readline->setEcho(true)); + } + + public function testSettingInputDoesRedraw() + { + $this->output->expects($this->once())->method('write'); + $this->assertSame($this->readline, $this->readline->setInput('test')); + } + + public function testSettingInputWithoutEchoDoesNotNeedToRedraw() + { + $this->readline->setEcho(false); + + $this->output->expects($this->never())->method('write'); + + $this->assertSame($this->readline, $this->readline->setInput('test')); + } + + public function testMovingCursorWithoutEchoDoesNotNeedToRedraw() + { + $this->readline->setEcho(false); + $this->readline->setInput('test'); + + $this->output->expects($this->never())->method('write'); + + $this->assertSame($this->readline, $this->readline->moveCursorTo(0)); + $this->assertSame($this->readline, $this->readline->moveCursorBy(2)); + } }