Skip to content
This repository was archived by the owner on Mar 6, 2022. It is now read-only.

Commit 314fb05

Browse files
BladeMFdantleech
andauthored
Add rename infrastructure
* The proper renamer structure * The proper renamer structure * The proper renamer structure * The proper renamer structure * Removed stub renamers * Moved chain renamer to correct namespace It is "pure" from dependencies - so it can live in model * Added memoer renamer * Refactored chain renamer test * Added test autoload rule This helps Phpactor detect what the class should be called. The weird autoloading is a side-effect of the inital migration to this bundle and the strange directory layout. * Renamed in extension * Moved test to correct namesace * Moved test files * Added integration test * CS fixes * Renmove setters * Use constant for example * Remove pointless array_merge * Add test for no renmaers * Renamed test * Renamed initialize * More renaming * Code style * Range converter Co-authored-by: Daniel Leech <[email protected]>
1 parent 8907897 commit 314fb05

File tree

14 files changed

+630
-0
lines changed

14 files changed

+630
-0
lines changed

.php_cs.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ $finder = PhpCsFixer\Finder::create()
99
;
1010

1111
return PhpCsFixer\Config::create()
12+
->setRiskyAllowed(true)
1213
->setRules([
1314
'@PSR2' => true,
1415
'no_unused_imports' => true,

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"Phpactor\\Extension\\LanguageServerCompletion\\Tests\\": "tests/LanguageServerCompletion/",
6464
"Phpactor\\Extension\\LanguageServerReferenceFinder\\Tests\\": "tests/LanguageServerReferenceFinder/",
6565
"Phpactor\\Extension\\LanguageServerIndexer\\Tests\\": "tests/LanguageServerIndexer/",
66+
"Phpactor\\Extension\\LanguageServerRename\\Tests\\": "tests/LanguageServerRename/",
6667
"Phpactor\\Extension\\LanguageServerWorseReflection\\Tests\\": "tests/LanguageServerWorseReflection/",
6768
"Phpactor\\Extension\\LanguageServerBridge\\Tests\\": "tests/LanguageServerBridge/",
6869
"Phpactor\\Extension\\LanguageServerCodeTransform\\Tests\\": "tests/LanguageServerCodeTransform/"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Phpactor\Extension\LanguageServerBridge\Converter;
4+
5+
use Phpactor\LanguageServerProtocol\Range;
6+
use Phpactor\TextDocument\ByteOffsetRange;
7+
8+
class RangeConverter
9+
{
10+
public static function toLspRange(ByteOffsetRange $range, string $text): Range
11+
{
12+
return new Range(
13+
PositionConverter::byteOffsetToPosition($range->start(), $text),
14+
PositionConverter::byteOffsetToPosition($range->end(), $text),
15+
);
16+
}
17+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
namespace Phpactor\Extension\LanguageServerRename\Handler;
4+
5+
use Amp\Promise;
6+
use Phpactor\Extension\LanguageServerBridge\Converter\PositionConverter;
7+
use Phpactor\Extension\LanguageServerBridge\Converter\RangeConverter;
8+
use Phpactor\Extension\LanguageServerBridge\Converter\TextEditConverter;
9+
use Phpactor\Extension\LanguageServerRename\Model\RenameResult;
10+
use Phpactor\Extension\LanguageServerRename\Model\Renamer;
11+
use Phpactor\LanguageServerProtocol\PrepareRenameParams;
12+
use Phpactor\LanguageServerProtocol\PrepareRenameRequest;
13+
use Phpactor\LanguageServerProtocol\Range;
14+
use Phpactor\LanguageServerProtocol\RenameOptions;
15+
use Phpactor\LanguageServerProtocol\RenameParams;
16+
use Phpactor\LanguageServerProtocol\RenameRequest;
17+
use Phpactor\LanguageServerProtocol\ServerCapabilities;
18+
use Phpactor\LanguageServerProtocol\TextDocumentEdit;
19+
use Phpactor\LanguageServerProtocol\VersionedTextDocumentIdentifier;
20+
use Phpactor\LanguageServerProtocol\WorkspaceEdit;
21+
use Phpactor\LanguageServer\Core\Handler\CanRegisterCapabilities;
22+
use Phpactor\LanguageServer\Core\Handler\Handler;
23+
use Phpactor\LanguageServer\Core\Workspace\Workspace;
24+
use Phpactor\TextDocument\TextDocumentLocator;
25+
use Phpactor\TextDocument\TextDocumentUri;
26+
27+
class RenameHandler implements Handler, CanRegisterCapabilities
28+
{
29+
/**
30+
* @var Renamer
31+
*/
32+
private $renamer;
33+
/**
34+
* @var Workspace
35+
*/
36+
private $workspace;
37+
/**
38+
* @var TextDocumentLocator
39+
*/
40+
private $documentLocator;
41+
42+
public function __construct(Workspace $workspace, TextDocumentLocator $documentLocator, Renamer $renamer)
43+
{
44+
$this->renamer = $renamer;
45+
$this->workspace = $workspace;
46+
$this->documentLocator = $documentLocator;
47+
}
48+
/**
49+
* {@inheritDoc}
50+
*/
51+
public function methods(): array
52+
{
53+
return [
54+
PrepareRenameRequest::METHOD => 'prepareRename',
55+
RenameRequest::METHOD => 'rename',
56+
];
57+
}
58+
/**
59+
* @return Promise<WorkspaceEdit>
60+
*/
61+
public function rename(RenameParams $params): Promise
62+
{
63+
return \Amp\call(function () use ($params) {
64+
return $this->resultToWorkspaceEdit(
65+
$this->renamer->rename(
66+
$document = $this->documentLocator->get(TextDocumentUri::fromString($params->textDocument->uri)),
67+
PositionConverter::positionToByteOffset(
68+
$params->position,
69+
(string)$document
70+
),
71+
$params->newName
72+
)
73+
);
74+
});
75+
}
76+
/**
77+
* @return Promise<Range>
78+
*/
79+
public function prepareRename(PrepareRenameParams $params): Promise
80+
{
81+
// https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename
82+
return \Amp\call(function () use ($params) {
83+
$range = $this->renamer->getRenameRange(
84+
$document = $this->documentLocator->get(TextDocumentUri::fromString($params->textDocument->uri)),
85+
PositionConverter::positionToByteOffset(
86+
$params->position,
87+
(string)$document
88+
),
89+
);
90+
if ($range == null) {
91+
return null;
92+
}
93+
return RangeConverter::toLspRange($range, (string)$document);
94+
});
95+
}
96+
97+
public function registerCapabiltiies(ServerCapabilities $capabilities): void
98+
{
99+
$capabilities->renameProvider = new RenameOptions(true);
100+
}
101+
102+
private function resultToWorkspaceEdit(iterable $results): WorkspaceEdit
103+
{
104+
$edits = [];
105+
$documentChanges = [];
106+
foreach ($results as $result) {
107+
/** @var RenameResult $result */
108+
$edits[] = new TextDocumentEdit(
109+
new VersionedTextDocumentIdentifier(
110+
(string)$result->documentUri(),
111+
$this->getDocumentVersion((string)$result->documentUri())
112+
),
113+
TextEditConverter::toLspTextEdits(
114+
$result->textEdits(),
115+
(string)$this->documentLocator->get($result->documentUri())
116+
)
117+
);
118+
}
119+
return new WorkspaceEdit($edits, $documentChanges);
120+
}
121+
122+
private function getDocumentVersion(string $uri): int
123+
{
124+
return $this->workspace->has($uri) ? $this->workspace->get($uri)->version : 0;
125+
}
126+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace Phpactor\Extension\LanguageServerRename;
4+
5+
use Phpactor\Container\Container;
6+
use Phpactor\Container\ContainerBuilder;
7+
use Phpactor\Container\Extension;
8+
use Phpactor\Extension\LanguageServerRename\Model\Renamer\ChainRenamer;
9+
use Phpactor\Extension\LanguageServerRename\Handler\RenameHandler;
10+
use Phpactor\Extension\LanguageServerRename\Model\Renamer;
11+
use Phpactor\Extension\LanguageServer\LanguageServerExtension;
12+
use Phpactor\MapResolver\Resolver;
13+
use Phpactor\TextDocument\TextDocumentLocator;
14+
15+
class LanguageServerRenameExtension implements Extension
16+
{
17+
public const TAG_RENAMER = 'language_server_rename.renamer';
18+
19+
/**
20+
* {@inheritDoc}
21+
*/
22+
public function load(ContainerBuilder $container): void
23+
{
24+
$container->register(Renamer::class, function (Container $container) {
25+
return new ChainRenamer(array_map(function (string $serviceId) use ($container) {
26+
return $container->get($serviceId);
27+
}, array_keys($container->getServiceIdsForTag(self::TAG_RENAMER))));
28+
});
29+
30+
$container->register(RenameHandler::class, function (Container $container) {
31+
return new RenameHandler(
32+
$container->get(LanguageServerExtension::SERVICE_SESSION_WORKSPACE),
33+
$container->get(TextDocumentLocator::class),
34+
$container->get(Renamer::class),
35+
);
36+
}, [ LanguageServerExtension::TAG_METHOD_HANDLER => [] ]);
37+
}
38+
39+
/**
40+
* {@inheritDoc}
41+
*/
42+
public function configure(Resolver $schema): void
43+
{
44+
}
45+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Phpactor\Extension\LanguageServerRename\Model;
4+
5+
use Phpactor\TextDocument\TextDocumentUri;
6+
use Phpactor\TextDocument\TextEdits;
7+
8+
class RenameResult
9+
{
10+
/**
11+
* @var TextEdits
12+
*/
13+
private $textEdits;
14+
/**
15+
* @var TextDocumentUri
16+
*/
17+
private $documentUri;
18+
19+
public function __construct(TextEdits $textEdits, TextDocumentUri $documentUri)
20+
{
21+
$this->textEdits = $textEdits;
22+
$this->documentUri = $documentUri;
23+
}
24+
25+
public function textEdits(): TextEdits
26+
{
27+
return $this->textEdits;
28+
}
29+
30+
public function documentUri(): TextDocumentUri
31+
{
32+
return $this->documentUri;
33+
}
34+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Phpactor\Extension\LanguageServerRename\Model;
4+
5+
use Generator;
6+
use Phpactor\TextDocument\ByteOffset;
7+
use Phpactor\TextDocument\ByteOffsetRange;
8+
use Phpactor\TextDocument\TextDocument;
9+
10+
interface Renamer
11+
{
12+
public function getRenameRange(TextDocument $textDocument, ByteOffset $offset): ?ByteOffsetRange;
13+
/**
14+
* @return Generator<RenameResult>
15+
*/
16+
public function rename(TextDocument $textDocument, ByteOffset $offset, string $newName): Generator;
17+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Phpactor\Extension\LanguageServerRename\Model\Renamer;
4+
5+
use Generator;
6+
use Phpactor\Extension\LanguageServerRename\Model\Renamer;
7+
use Phpactor\TextDocument\ByteOffset;
8+
use Phpactor\TextDocument\ByteOffsetRange;
9+
use Phpactor\TextDocument\TextDocument;
10+
11+
class ChainRenamer implements Renamer
12+
{
13+
/** @var Renamer[] */
14+
private $renamers;
15+
16+
public function __construct(array $renamers)
17+
{
18+
$this->renamers = $renamers;
19+
}
20+
21+
public function getRenameRange(TextDocument $textDocument, ByteOffset $offset): ?ByteOffsetRange
22+
{
23+
foreach ($this->renamers as $renamer) {
24+
if (null !== ($range = $renamer->getRenameRange($textDocument, $offset))) {
25+
return $range;
26+
}
27+
}
28+
return null;
29+
}
30+
/**
31+
* {@inheritDoc}
32+
*/
33+
public function rename(TextDocument $textDocument, ByteOffset $offset, string $newName): Generator
34+
{
35+
foreach ($this->renamers as $renamer) {
36+
if (null !== ($range = $renamer->getRenameRange($textDocument, $offset))) {
37+
yield from $renamer->rename($textDocument, $offset, $newName);
38+
return;
39+
}
40+
}
41+
}
42+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Phpactor\Extension\LanguageServerRename\Model\Renamer;
4+
5+
use Generator;
6+
use Phpactor\Extension\LanguageServerRename\Model\Renamer;
7+
use Phpactor\TextDocument\ByteOffset;
8+
use Phpactor\TextDocument\ByteOffsetRange;
9+
use Phpactor\TextDocument\TextDocument;
10+
11+
class InMemoryRenamer implements Renamer
12+
{
13+
/**
14+
* @var ByteOffsetRange
15+
*/
16+
private $range;
17+
/**
18+
* @var array
19+
*/
20+
private $results;
21+
22+
public function __construct(?ByteOffsetRange $range, array $results = [])
23+
{
24+
$this->results = $results;
25+
$this->range = $range;
26+
}
27+
28+
public function getRenameRange(TextDocument $textDocument, ByteOffset $offset): ?ByteOffsetRange
29+
{
30+
return $this->range;
31+
}
32+
33+
/**
34+
* {@inheritDoc}
35+
*/
36+
public function rename(TextDocument $textDocument, ByteOffset $offset, string $newName): Generator
37+
{
38+
yield from $this->results;
39+
}
40+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Phpactor\Extension\LanguageServerBridge\Tests\Converter;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Phpactor\Extension\LanguageServerBridge\Converter\PositionConverter;
7+
use Phpactor\Extension\LanguageServerBridge\Converter\RangeConverter;
8+
use Phpactor\LanguageServerProtocol\Range;
9+
use Phpactor\TextDocument\ByteOffset;
10+
use Phpactor\TextDocument\ByteOffsetRange;
11+
12+
class RangeConverterTest extends TestCase
13+
{
14+
public function testConvertsRanges(): void
15+
{
16+
$text = '1234567890';
17+
$start = ByteOffset::fromInt(1);
18+
$end = ByteOffset::fromInt(4);
19+
self::assertEquals(
20+
new Range(
21+
PositionConverter::byteOffsetToPosition($start, $text),
22+
PositionConverter::byteOffsetToPosition($end, $text),
23+
),
24+
RangeConverter::toLspRange(
25+
ByteOffsetRange::fromByteOffsets($start, $end),
26+
$text
27+
)
28+
);
29+
}
30+
}

0 commit comments

Comments
 (0)