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:
commit
ab30a36803
@ -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();
|
||||
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user