Skip to content

Commit 95908e7

Browse files
committed
Pass start and end offset as number of Unicode characters
1 parent 0018ca2 commit 95908e7

File tree

4 files changed

+43
-32
lines changed

4 files changed

+43
-32
lines changed

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -420,16 +420,17 @@ input buffer is `hello exit `, which may or may not be what you need depending
420420
on your particular use case.
421421

422422
In order to give your more control over this behavior, the autocomplete function
423-
actually receives two arguments (similar to `ext-readline`'s
423+
actually receives three arguments (similar to `ext-readline`'s
424424
[`readline_completion_function()`](http://php.net/manual/en/function.readline-completion-function.php)):
425425
The first argument will be the current incomplete word according to current
426-
cursor position and word boundaries, while the second argument will be the
427-
offset of this word within the complete input buffer.
428-
The above examples will be invoked as `$fn('he', 0)`, `$fn('e', 0)` and
429-
`$fn('ex', 6)` respectively.
430-
You may want to use the `$offset` argument to check if the current word is an
431-
argument or a root command and the `$word` argument to autocomplete partial
432-
filename matches like this:
426+
cursor position and word boundaries, while the second and third argument will be
427+
the start and end offset of this word within the complete input buffer measured
428+
in (Unicode) characters.
429+
The above examples will be invoked as `$fn('he', 0, 2)`, `$fn('e', 0, 1)` and
430+
`$fn('ex', 6, 8)` respectively.
431+
You may want to use this as an `$offset` argument to check if the current word
432+
is an argument or a root command and the `$word` argument to autocomplete
433+
partial filename matches like this:
433434

434435
```php
435436
$readline->setAutocomplete(function ($word, $offset) {
@@ -445,7 +446,7 @@ $readline->setAutocomplete(function ($word, $offset) {
445446

446447
> Note that the user may also use quotes and/or leading whitespace around the
447448
root command, for example `"hell [TAB]`, in which case the offset will be
448-
advanced such as this will be invoked as `$fn('hell', 1)`.
449+
advanced such as this will be invoked as `$fn('hell', 1, 4)`.
449450
Unless you use a more sophisticated argument parser, a decent approximation may
450451
be using `$offset <= 1` to check this is a root command.
451452

src/Readline.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,8 @@ public function onKeyTab()
509509
// current word prefix and offset for start of word in input buffer
510510
// "echo foo|bar world" will return just "foo" with word offset 5
511511
$word = $this->substr($this->linebuffer, 0, $this->linepos);
512-
$offset = 0;
512+
$start = 0;
513+
$end = $this->linepos;
513514

514515
// buffer prefix and postfix for everything that will *not* be matched
515516
// above example will return "echo " and "bar world"
@@ -519,22 +520,22 @@ public function onKeyTab()
519520
// skip everything before last space
520521
$pos = strrpos($word, ' ');
521522
if ($pos !== false) {
522-
$offset = $pos + 1;
523-
$prefix = (string)substr($word, 0, $offset);
524-
$word = (string)substr($word, $offset);
523+
$prefix = (string)substr($word, 0, $pos + 1);
524+
$word = (string)substr($word, $pos + 1);
525+
$start = $this->strlen($prefix);
525526
}
526527

527528
// skip double quote (") or single quote (') from argument
528529
$quote = null;
529530
if (isset($word[0]) && ($word[0] === '"' || $word[0] === '\'')) {
530531
$quote = $word[0];
531-
++$offset;
532+
++$start;
532533
$prefix .= $word[0];
533534
$word = (string)substr($word, 1);
534535
}
535536

536537
// invoke autocomplete callback
537-
$words = call_user_func($this->autocomplete, $word, $offset);
538+
$words = call_user_func($this->autocomplete, $word, $start, $end);
538539

539540
// return early if autocomplete does not return anything
540541
if ($words === null) {

tests/ReadlineTest.php

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -530,14 +530,14 @@ public function testAutocompleteWillNotBeCalledAfterUnset()
530530

531531
public function testAutocompleteWillBeCalledWithEmptyBuffer()
532532
{
533-
$this->readline->setAutocomplete($this->expectCallableOnceWith('', 0));
533+
$this->readline->setAutocomplete($this->expectCallableOnceWith('', 0, 0));
534534

535535
$this->readline->onKeyTab();
536536
}
537537

538538
public function testAutocompleteWillBeCalledWithCompleteWord()
539539
{
540-
$this->readline->setAutocomplete($this->expectCallableOnceWith('hello', 0));
540+
$this->readline->setAutocomplete($this->expectCallableOnceWith('hello', 0, 5));
541541

542542
$this->readline->setInput('hello');
543543

@@ -546,7 +546,7 @@ public function testAutocompleteWillBeCalledWithCompleteWord()
546546

547547
public function testAutocompleteWillBeCalledWithWordPrefix()
548548
{
549-
$this->readline->setAutocomplete($this->expectCallableOnceWith('he', 0));
549+
$this->readline->setAutocomplete($this->expectCallableOnceWith('he', 0, 2));
550550

551551
$this->readline->setInput('hello');
552552
$this->readline->moveCursorTo(2);
@@ -556,7 +556,7 @@ public function testAutocompleteWillBeCalledWithWordPrefix()
556556

557557
public function testAutocompleteWillBeCalledWithLastWord()
558558
{
559-
$this->readline->setAutocomplete($this->expectCallableOnceWith('world', 6));
559+
$this->readline->setAutocomplete($this->expectCallableOnceWith('world', 6, 11));
560560

561561
$this->readline->setInput('hello world');
562562

@@ -565,14 +565,34 @@ public function testAutocompleteWillBeCalledWithLastWord()
565565

566566
public function testAutocompleteWillBeCalledWithLastWordPrefix()
567567
{
568-
$this->readline->setAutocomplete($this->expectCallableOnceWith('wo', 6));
568+
$this->readline->setAutocomplete($this->expectCallableOnceWith('wo', 6, 8));
569569

570570
$this->readline->setInput('hello world');
571571
$this->readline->moveCursorTo(8);
572572

573573
$this->readline->onKeyTab();
574574
}
575575

576+
public function testAutocompleteWillBeCalledWithLastWordPrefixUnicode()
577+
{
578+
$this->readline->setAutocomplete($this->expectCallableOnceWith('', 6, 8));
579+
580+
$this->readline->setInput('hällö wörld');
581+
$this->readline->moveCursorTo(8);
582+
583+
$this->readline->onKeyTab();
584+
}
585+
586+
public function testAutocompleteWillBeCalledWithLastWordPrefixQuotedUnicode()
587+
{
588+
$this->readline->setAutocomplete($this->expectCallableOnceWith('', 9, 11));
589+
590+
$this->readline->setInput('"hällö" "wörld"');
591+
$this->readline->moveCursorTo(11);
592+
593+
$this->readline->onKeyTab();
594+
}
595+
576596
public function testAutocompleteAddsSpaceAfterComplete()
577597
{
578598
$this->readline->setAutocomplete(function () { return array('exit'); });

tests/bootstrap.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,7 @@ protected function expectCallableOnceWith($value)
3232
$mock
3333
->expects($this->once())
3434
->method('__invoke')
35-
->with($this->equalTo($value));
36-
37-
return $mock;
38-
}
39-
40-
protected function expectCallableOnceParameter($type)
41-
{
42-
$mock = $this->createCallableMock();
43-
$mock
44-
->expects($this->once())
45-
->method('__invoke')
46-
->with($this->isInstanceOf($type));
35+
->withConsecutive(func_get_args());
4736

4837
return $mock;
4938
}

0 commit comments

Comments
 (0)