1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Merge pull request #7255 from tm1000/feature/lang-server-code-actions

This commit is contained in:
Bruce Weirdan 2022-01-02 05:38:06 +02:00 committed by GitHub
commit ab30a36803
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 113 additions and 14 deletions

View File

@ -238,7 +238,7 @@ class LanguageServer extends Dispatcher
$this->textDocument = new ServerTextDocument(
$this,
$codebase,
$this->project_analyzer->onchange_line_limit
$this->project_analyzer
);
}
@ -246,7 +246,7 @@ class LanguageServer extends Dispatcher
$this->workspace = new ServerWorkspace(
$this,
$codebase,
$this->project_analyzer->onchange_line_limit
$this->project_analyzer
);
}
@ -279,6 +279,8 @@ class LanguageServer extends Dispatcher
// Support "Hover"
$serverCapabilities->hoverProvider = true;
// Support "Completion"
$serverCapabilities->codeActionProvider = true;
// Support "Code Actions"
if ($this->project_analyzer->provide_completion) {
$serverCapabilities->completionProvider = new CompletionOptions();

View File

@ -17,14 +17,21 @@ use LanguageServerProtocol\SignatureHelp;
use LanguageServerProtocol\TextDocumentContentChangeEvent;
use LanguageServerProtocol\TextDocumentIdentifier;
use LanguageServerProtocol\TextDocumentItem;
use LanguageServerProtocol\TextEdit;
use LanguageServerProtocol\VersionedTextDocumentIdentifier;
use LanguageServerProtocol\WorkspaceEdit;
use Psalm\Codebase;
use Psalm\Exception\UnanalyzedFileException;
use Psalm\Internal\Analyzer\ProjectAnalyzer;
use Psalm\Internal\LanguageServer\LanguageServer;
use Psalm\IssueBuffer;
use UnexpectedValueException;
use function array_combine;
use function array_values;
use function count;
use function error_log;
use function preg_match;
use function substr_count;
/**
@ -42,17 +49,19 @@ class TextDocument
*/
protected $codebase;
/** @var ?int */
protected $onchange_line_limit;
/**
* @var ProjectAnalyzer
*/
protected $project_analyzer;
public function __construct(
LanguageServer $server,
Codebase $codebase,
?int $onchange_line_limit
ProjectAnalyzer $project_analyzer
) {
$this->server = $server;
$this->codebase = $codebase;
$this->onchange_line_limit = $onchange_line_limit;
$this->project_analyzer = $project_analyzer;
}
/**
@ -116,7 +125,7 @@ class TextDocument
return;
}
if ($this->onchange_line_limit === 0) {
if ($this->project_analyzer->onchange_line_limit === 0) {
return;
}
@ -126,8 +135,8 @@ class TextDocument
throw new UnexpectedValueException('Not expecting partial diff');
}
if ($this->onchange_line_limit !== null) {
if (substr_count($new_content, "\n") > $this->onchange_line_limit) {
if ($this->project_analyzer->onchange_line_limit !== null) {
if (substr_count($new_content, "\n") > $this->project_analyzer->onchange_line_limit) {
return;
}
}
@ -341,4 +350,89 @@ class TextDocument
$signature_information,
], 0, $argument_location[1]));
}
/**
* The code action request is sent from the client to the server to compute commands
* for a given text document and range. These commands are typically code fixes to
* either fix problems or to beautify/refactor code.
*
*/
public function codeAction(TextDocumentIdentifier $textDocument, Range $range): Promise
{
$file_path = LanguageServer::uriToPath($textDocument->uri);
if (!$this->codebase->file_provider->isOpen($file_path)) {
return new Success(null);
}
$all_file_paths_to_analyze = [$file_path];
$this->codebase->analyzer->addFilesToAnalyze(
array_combine($all_file_paths_to_analyze, $all_file_paths_to_analyze)
);
$this->codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
$issues = IssueBuffer::clear();
if (empty($issues[$file_path])) {
return new Success(null);
}
$file_contents = $this->codebase->getFileContents($file_path);
$offsetStart = $range->start->toOffset($file_contents);
$offsetEnd = $range->end->toOffset($file_contents);
$fixers = [];
foreach ($issues[$file_path] as $issue) {
if ($offsetStart === $issue->from && $offsetEnd === $issue->to) {
$snippetRange = new Range(
new Position($issue->line_from-1),
new Position($issue->line_to)
);
$indentation = '';
if (preg_match('/^(\s*)/', $issue->snippet, $matches)) {
$indentation = $matches[1] ?? '';
}
/**
* Suppress Psalm because ther are bugs in how
* LanguageServer's signature of WorkspaceEdit is declared:
*
* See:
* https://github.com/felixfbecker/php-language-server-protocol
* See:
* https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#workspaceEdit
*
* @psalm-suppress InvalidArgument
*/
$edit = new WorkspaceEdit([
$textDocument->uri => [
new TextEdit(
$snippetRange,
"{$indentation}/**\n".
"{$indentation} * @psalm-suppress {$issue->type}\n".
"{$indentation} */\n".
"{$issue->snippet}\n"
)
]
]);
//Suppress Ability
$fixers["suppress.{$issue->type}"] = [
'title' => "Suppress {$issue->type} for this line",
'kind' => 'quickfix',
'edit' => $edit
];
}
}
if (empty($fixers)) {
return new Success(null);
}
return new Success(
array_values($fixers)
);
}
}

View File

@ -7,6 +7,7 @@ namespace Psalm\Internal\LanguageServer\Server;
use LanguageServerProtocol\FileChangeType;
use LanguageServerProtocol\FileEvent;
use Psalm\Codebase;
use Psalm\Internal\Analyzer\ProjectAnalyzer;
use Psalm\Internal\LanguageServer\LanguageServer;
/**
@ -24,17 +25,19 @@ class Workspace
*/
protected $codebase;
/** @var ?int */
protected $onchange_line_limit;
/**
* @var ProjectAnalyzer
*/
protected $project_analyzer;
public function __construct(
LanguageServer $server,
Codebase $codebase,
?int $onchange_line_limit
ProjectAnalyzer $project_analyzer
) {
$this->server = $server;
$this->codebase = $codebase;
$this->onchange_line_limit = $onchange_line_limit;
$this->project_analyzer = $project_analyzer;
}
/**
@ -62,7 +65,7 @@ class Workspace
continue;
}
if ($this->onchange_line_limit === 0) {
if ($this->project_analyzer->onchange_line_limit === 0) {
continue;
}