Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented input completion for Userland #733

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 21 additions & 6 deletions src/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ class Configuration
private $historySize;
private $eraseDuplicates;
private $manualDbFile;
private $hasReadline;
/** @bool Native readline implementation provided by {@see readline()} function */
private $hasNativeReadline;
private $useReadline;
private $useBracketedPaste;
private $hasPcntl;
Expand Down Expand Up @@ -370,7 +371,7 @@ public static function getInputOptions(): array
public function init()
{
// feature detection
$this->hasReadline = \function_exists('readline');
$this->hasNativeReadline = \function_exists('readline');
$this->hasPcntl = ProcessForker::isSupported();

if ($configFile = $this->getConfigFile()) {
Expand Down Expand Up @@ -735,13 +736,25 @@ public function getPipe(string $type, int $pid): string
}

/**
* Check whether this PHP instance has Readline available.
* Check whether this PHP instance has native readline available.
*
* @deprecated Call {@see Configuration::hasNativeReadline()} instead
*
* @return bool True if Readline is available
*/
public function hasReadline(): bool
{
return $this->hasReadline;
return $this->hasNativeReadline;
}

/**
* Check whether this PHP instance has native readline available.
*
* @return bool True if Readline is available
*/
public function hasNativeReadline(): bool
{
return $this->hasNativeReadline;
}

/**
Expand All @@ -764,7 +777,7 @@ public function setUseReadline(bool $useReadline)
*/
public function useReadline(): bool
{
return isset($this->useReadline) ? ($this->hasReadline && $this->useReadline) : $this->hasReadline;
return isset($this->useReadline) ? ($this->hasNativeReadline && $this->useReadline) : $this->hasNativeReadline;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe these should also be called "useNativeReadline", but I haven't changed them as they are separate issues from auto-completion.

}

/**
Expand Down Expand Up @@ -1081,7 +1094,9 @@ public function setTabCompletion(bool $useTabCompletion)
*/
public function useTabCompletion(): bool
{
return isset($this->useTabCompletion) ? ($this->hasReadline && $this->useTabCompletion) : $this->hasReadline;
// TODO: In the future, if stability other than GNU Readline improves,
// it will no longer depend on $this->hasNativeReadline property.
return $this->useTabCompletion ?? $this->hasNativeReadline;
}

/**
Expand Down
16 changes: 16 additions & 0 deletions src/Readline/GNUReadline.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Psy\Readline;

use Psy\TabCompletion\AutoCompleter;

/**
* A Readline interface implementation for GNU Readline.
*
Expand Down Expand Up @@ -176,4 +178,18 @@ public function writeHistory(): bool

return true;
}

public function activateAutoCompleter(AutoCompleter $autoCompleter): void
{
\readline_completion_function([$autoCompleter, 'callback']);
}

public function deactivateAutoCompleter(): void
{
// PHP didn't implement the whole readline API when they first switched
// to libedit. And they still haven't.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

if (\function_exists('readline_callback_handler_remove')) {
\readline_callback_handler_remove();
}
}
}
5 changes: 4 additions & 1 deletion src/Readline/Hoa/Autocompleter.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ interface Autocompleter
/**
* Complete a word.
* Returns null for no word, a full-word or an array of full-words.
*
* @param array{line_buffer: string} $info A subset of {@see readline_info()}'s return value.
* @see https://www.php.net/readline_info
*/
public function complete(&$prefix);
public function complete(string $prefix, int $index, array $info);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these were passing by reference because they changed $prefix intentionally so the caller could check prefix length.


/**
* Get definition of a word.
Expand Down
12 changes: 1 addition & 11 deletions src/Readline/Hoa/AutocompleterAggregate.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,14 @@ class AutocompleterAggregate implements Autocompleter
*/
protected $_autocompleters = null;

/**
* Constructor.
*/
public function __construct(array $autocompleters)
{
$this->setAutocompleters($autocompleters);

return;
}

/**
* Complete a word.
* Returns null for no word, a full-word or an array of full-words.
*/
public function complete(&$prefix)
public function complete(string $prefix, int $index, array $info)
{
foreach ($this->getAutocompleters() as $autocompleter) {
$preg = \preg_match(
Expand Down Expand Up @@ -108,9 +101,6 @@ public function getAutocompleters()
return $this->_autocompleters;
}

/**
* Get definition of a word.
*/
public function getWordDefinition(): string
{
return '.*';
Expand Down
12 changes: 1 addition & 11 deletions src/Readline/Hoa/AutocompleterPath.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ class AutocompleterPath implements Autocompleter
*/
protected $_iteratorFactory = null;

/**
* Constructor.
*/
public function __construct(
string $root = null,
\Closure $iteratorFactory = null
Expand All @@ -78,11 +75,7 @@ public function __construct(
}
}

/**
* Complete a word.
* Returns null for no word, a full-word or an array of full-words.
*/
public function complete(&$prefix)
public function complete(string $prefix, int $index, array $info)
{
$root = $this->getRoot();

Expand Down Expand Up @@ -136,9 +129,6 @@ public function complete(&$prefix)
return $out;
}

/**
* Get definition of a word.
*/
public function getWordDefinition(): string
{
return '/?[\w\d\\_\-\.]+(/[\w\d\\_\-\.]*)*';
Expand Down
13 changes: 1 addition & 12 deletions src/Readline/Hoa/AutocompleterWord.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,7 @@ public function __construct(array $words)
$this->setWords($words);
}

/**
* Complete a word.
* Returns null for no word, a full-word or an array of full-words.
*
* @param string &$prefix Prefix to autocomplete
*
* @return mixed
*/
public function complete(&$prefix)
public function complete(string $prefix, int $index, array $info)
{
$out = [];
$length = \mb_strlen($prefix);
Expand All @@ -86,9 +78,6 @@ public function complete(&$prefix)
return $out;
}

/**
* Get definition of a word.
*/
public function getWordDefinition(): string
{
return '\b\w+';
Expand Down
2 changes: 1 addition & 1 deletion src/Readline/Hoa/ConsoleWindow.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public static function getSize(): array
}

$command = $term.'tput cols && '.$term.'tput lines';
$tput = Processus::execute($command, false);
$tput = ConsoleProcessus::execute($command, false);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ooh, sorry about that.


if (!empty($tput)) {
list($x, $y) = \explode("\n", $tput);
Expand Down
5 changes: 4 additions & 1 deletion src/Readline/Hoa/Readline.php
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,10 @@ public function _bindTab(self $self): int
return $state;
}

$solution = $autocompleter->complete($word);
$solution = $autocompleter->complete($word, $current, [
'line_buffer' => $line,
]);

$length = \mb_strlen($word);

if (null === $solution) {
Expand Down
27 changes: 27 additions & 0 deletions src/Readline/HoaAutocompleterAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Psy\Readline;

use Psy\Readline\Hoa\Autocompleter as HoaAutocompleter;
use Psy\TabCompletion\AutoCompleter;

class HoaAutocompleterAdapter implements HoaAutocompleter
{
/** @var AutoCompleter */
private $autoCompleter;

public function __construct(AutoCompleter $autoCompleter)
{
$this->autoCompleter = $autoCompleter;
}

public function complete(string $prefix, int $index, array $info)
{
return $this->autoCompleter->complete($prefix, $index, $info);
}

public function getWordDefinition(): string
{
return '.';
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left this adapter for interoperability with existing Hoa completions, but you can also remove HoaAutocompleter and make it directly dependent on Psy\TabCompletion\AutoCompleter. What do you think?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the benefit of interoperability with existing Hoa completions? it's deprecated and unsupported, and the code here is effectively a stripped-down fork. i'm not sure i see the point :)

12 changes: 12 additions & 0 deletions src/Readline/Readline.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Psy\Readline;

use Psy\TabCompletion\AutoCompleter;

/**
* An interface abstracting the various readline_* functions.
*/
Expand Down Expand Up @@ -80,4 +82,14 @@ public function redisplay();
* @return bool Success
*/
public function writeHistory(): bool;

/**
* Activete auto completer for tab completion.
*/
public function activateAutoCompleter(AutoCompleter $autoCompleter): void;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's make these two a second interface so we can maintain backwards compatibility for existing Readline API.


/**
* Deactivete auto completer for tab completion.
*/
public function deactivateAutoCompleter(): void;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do nothing for deactivate except native readline().

}
11 changes: 11 additions & 0 deletions src/Readline/Transient.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Psy\Readline;

use Psy\Exception\BreakException;
use Psy\TabCompletion\AutoCompleter;

/**
* An array-based Readline emulation implementation.
Expand Down Expand Up @@ -152,4 +153,14 @@ private function getStdin()

return $this->stdin;
}

public function activateAutoCompleter(AutoCompleter $autoCompleter): void
{
// noop
}

public function deactivateAutoCompleter(): void
{
// noop
}
}
11 changes: 11 additions & 0 deletions src/Readline/Userland.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Psy\Readline\Hoa\ConsoleTput as HoaConsoleTput;
use Psy\Readline\Hoa\Readline as HoaReadline;
use Psy\Readline\Hoa\Ustring as HoaUstring;
use Psy\TabCompletion\AutoCompleter;

/**
* Userland Readline implementation.
Expand Down Expand Up @@ -158,4 +159,14 @@ public function writeHistory(): bool
{
return true;
}

public function activateAutoCompleter(AutoCompleter $autoCompleter): void
{
$this->hoaReadline->setAutocompleter(new HoaAutocompleterAdapter($autoCompleter));
}

public function deactivateAutoCompleter(): void
{
// noop
}
}
2 changes: 1 addition & 1 deletion src/Shell.php
Original file line number Diff line number Diff line change
Expand Up @@ -1501,7 +1501,7 @@ protected function initializeTabCompletion()
$this->addMatchersToAutoCompleter($this->getDefaultMatchers());
$this->addMatchersToAutoCompleter($this->matchers);

$this->autoCompleter->activate();
$this->readline->activateAutoCompleter($this->autoCompleter);
}

/**
Expand Down