Skip to content

Commit

Permalink
merged branch Seldaek/autocomplete_fix (PR #7589)
Browse files Browse the repository at this point in the history
This PR was submitted for the 2.2 branch but it was merged into the 2.1 branch instead (closes #7589).

Discussion
----------

[2.2][Console] Fix autocompletion of command names when namespaces conflict

Fixes #7234

Currently autocompletion of namespaces works on *all* namespaces at once. This fixes it to only search inside subnamespaces once we matched the first level namespace. Otherwise if you have `api:doc` and `generate:doctrine:entity` commands, typing `gen:doc:entity` trips it up because it thinks "doc" is "api:doc", and you end up with a completed name of `generate:doc:entity` which does not match any command.

Commits
-------

fa465b1 [2.2][Console] Fix autocompletion of command names when namespaces conflict
  • Loading branch information
fabpot committed Apr 7, 2013
2 parents ebbb96e + 9d71ebe commit 95927c7
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 21 deletions.
42 changes: 21 additions & 21 deletions src/Symfony/Component/Console/Application.php
Expand Up @@ -478,20 +478,24 @@ public function getNamespaces()
*/
public function findNamespace($namespace)
{
$allNamespaces = array();
foreach ($this->getNamespaces() as $n) {
$allNamespaces[$n] = explode(':', $n);
}

$found = array();
$allNamespaces = $this->getNamespaces();
$found = '';
foreach (explode(':', $namespace) as $i => $part) {
$abbrevs = static::getAbbreviations(array_unique(array_values(array_filter(array_map(function ($p) use ($i) { return isset($p[$i]) ? $p[$i] : ''; }, $allNamespaces)))));
// select sub-namespaces matching the current namespace we found
$namespaces = array();
foreach ($allNamespaces as $n) {
if ('' === $found || 0 === strpos($n, $found)) {
$namespaces[$n] = explode(':', $n);
}
}

$abbrevs = static::getAbbreviations(array_unique(array_values(array_filter(array_map(function ($p) use ($i) { return isset($p[$i]) ? $p[$i] : ''; }, $namespaces)))));

if (!isset($abbrevs[$part])) {
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);

if (1 <= $i) {
$part = implode(':', $found).':'.$part;
$part = $found.':'.$part;
}

if ($alternatives = $this->findAlternativeNamespace($part, $abbrevs)) {
Expand All @@ -507,14 +511,19 @@ public function findNamespace($namespace)
throw new \InvalidArgumentException($message);
}

// there are multiple matches, but $part is an exact match of one of them so we select it
if (in_array($part, $abbrevs[$part])) {
$abbrevs[$part] = array($part);
}

if (count($abbrevs[$part]) > 1) {
throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions($abbrevs[$part])));
}

$found[] = $abbrevs[$part][0];
$found .= $found ? ':' . $abbrevs[$part][0] : $abbrevs[$part][0];
}

return implode(':', $found);
return $found;
}

/**
Expand Down Expand Up @@ -637,21 +646,12 @@ public static function getAbbreviations($names)
{
$abbrevs = array();
foreach ($names as $name) {
for ($len = strlen($name) - 1; $len > 0; --$len) {
for ($len = strlen($name); $len > 0; --$len) {
$abbrev = substr($name, 0, $len);
if (!isset($abbrevs[$abbrev])) {
$abbrevs[$abbrev] = array($name);
} else {
$abbrevs[$abbrev][] = $name;
}
$abbrevs[$abbrev][] = $name;
}
}

// Non-abbreviations always get entered, even if they aren't unique
foreach ($names as $name) {
$abbrevs[$name] = array($name);
}

return $abbrevs;
}

Expand Down
10 changes: 10 additions & 0 deletions src/Symfony/Component/Console/Tests/ApplicationTest.php
Expand Up @@ -342,6 +342,16 @@ public function testFindAlternativeNamespace()
}
}

public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces()
{
$application = $this->getMock('Symfony\Component\Console\Application', array('getNamespaces'));
$application->expects($this->once())
->method('getNamespaces')
->will($this->returnValue(array('foo:sublong', 'bar:sub')));

$this->assertEquals('foo:sublong', $application->findNamespace('f:sub'));
}

public function testSetCatchExceptions()
{
$application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
Expand Down

0 comments on commit 95927c7

Please sign in to comment.