AutoCompleterTest.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. <?php
  2. /*
  3. * This file is part of Psy Shell.
  4. *
  5. * (c) 2012-2023 Justin Hileman
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Psy\Test\TabCompletion;
  11. use Psy\Command\ListCommand;
  12. use Psy\Command\ShowCommand;
  13. use Psy\Configuration;
  14. use Psy\Context;
  15. use Psy\ContextAware;
  16. use Psy\TabCompletion\Matcher;
  17. class AutoCompleterTest extends \Psy\Test\TestCase
  18. {
  19. /**
  20. * @param string $line
  21. * @param array $mustContain
  22. * @param array $mustNotContain
  23. *
  24. * @dataProvider classesInput
  25. */
  26. public function testClassesCompletion($line, $mustContain, $mustNotContain)
  27. {
  28. $context = new Context();
  29. $commands = [
  30. new ShowCommand(),
  31. new ListCommand(),
  32. ];
  33. $matchers = [
  34. new Matcher\VariablesMatcher(),
  35. new Matcher\ClassNamesMatcher(),
  36. new Matcher\ConstantsMatcher(),
  37. new Matcher\FunctionsMatcher(),
  38. new Matcher\ObjectMethodsMatcher(),
  39. new Matcher\ObjectAttributesMatcher(),
  40. new Matcher\KeywordsMatcher(),
  41. new Matcher\ClassAttributesMatcher(),
  42. new Matcher\ClassMethodsMatcher(),
  43. new Matcher\CommandsMatcher($commands),
  44. ];
  45. $config = new Configuration();
  46. $tabCompletion = $config->getAutoCompleter();
  47. foreach ($matchers as $matcher) {
  48. if ($matcher instanceof ContextAware) {
  49. $matcher->setContext($context);
  50. }
  51. $tabCompletion->addMatcher($matcher);
  52. }
  53. $context->setAll(['foo' => 12, 'bar' => new \DOMDocument()]);
  54. $code = $tabCompletion->processCallback('', 0, [
  55. 'line_buffer' => $line,
  56. 'point' => 0,
  57. 'end' => \strlen($line),
  58. ]);
  59. foreach ($mustContain as $mc) {
  60. $this->assertContains($mc, $code);
  61. }
  62. foreach ($mustNotContain as $mnc) {
  63. $this->assertNotContains($mnc, $code);
  64. }
  65. }
  66. /**
  67. * @todo
  68. * ====
  69. * draft, open to modifications
  70. * - [ ] if the variable is an array, return the square bracket for completion
  71. * - [ ] if the variable is a constructor or method, reflect to complete as a function call
  72. * - [ ] if the preceding token is a variable, call operators or keywords compatible for completion
  73. * - [X] a command always should be the second token after php_open_tag
  74. * - [X] keywords are never consecutive
  75. * - [X] namespacing completion should work just fine
  76. * - [X] after a new keyword, should always be a class constructor, never a function call or keyword, constant,
  77. * or variable that does not contain a existing class name.
  78. * - [X] on a namespaced constructor the completion must show the classes related, not constants.
  79. *
  80. * @return array
  81. */
  82. public function classesInput()
  83. {
  84. return [
  85. // input, must had, must not had
  86. ['T_OPE', ['T_OPEN_TAG'], []],
  87. ['st', ['stdClass'], []],
  88. ['DateT', ['DateTime', 'DateTimeImmutable', 'DateTimeInterface', 'DateTimeZone'], []],
  89. ['stdCla', ['stdClass'], []],
  90. ['new s', ['stdClass'], []],
  91. [
  92. 'new ',
  93. ['stdClass', Context::class, Configuration::class],
  94. ['require', 'array_search', 'T_OPEN_TAG', '$foo'],
  95. ],
  96. ['new Psy\\C', ['Context'], ['CASE_LOWER']],
  97. ['\s', ['stdClass'], []],
  98. ['array_', ['array_search', 'array_map', 'array_merge'], []],
  99. ['$bar->', ['load'], []],
  100. ['$b', ['bar'], []],
  101. ['6 + $b', ['bar'], []],
  102. ['$f', ['foo'], []],
  103. ['l', ['ls'], []],
  104. ['ls ', [], ['ls']],
  105. ['sho', ['show'], []],
  106. ['12 + clone $', ['foo'], []],
  107. // [
  108. // '$foo ',
  109. // ['+', 'clone'],
  110. // ['$foo', 'DOMDocument', 'array_map']
  111. // ], requires a operator matcher?
  112. ['$', ['foo', 'bar'], ['require', 'array_search', 'T_OPEN_TAG', 'Psy']],
  113. [
  114. 'Psy\\',
  115. ['Context', 'TabCompletion\\Matcher\\AbstractMatcher'],
  116. ['require', 'array_search'],
  117. ],
  118. [
  119. 'Psy\Test\TabCompletion\StaticSample::CO',
  120. ['StaticSample::CONSTANT_VALUE'],
  121. [],
  122. ],
  123. [
  124. 'Psy\Test\TabCompletion\StaticSample::',
  125. ['StaticSample::$staticVariable'],
  126. [],
  127. ],
  128. [
  129. 'Psy\Test\TabCompletion\StaticSample::',
  130. ['StaticSample::staticFunction'],
  131. [],
  132. ],
  133. ];
  134. }
  135. }