From 9d71ebe8e2e809da93ea4555759846c7676d9d08 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 7 Apr 2013 16:34:20 +0200 Subject: [PATCH] Fix autocompletion of command names when namespaces conflict --- src/Symfony/Component/Console/Application.php | 42 +++++++++---------- .../Console/Tests/ApplicationTest.php | 10 +++++ 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index af800916132c..7f7da7585896 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -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)) { @@ -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; } /** @@ -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; } diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 7a0a4895cbc4..bb4e11e6a2f7 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -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'));