Skip to content

Commit

Permalink
Merge pull request #7255 from tm1000/feature/lang-server-code-actions
Browse files Browse the repository at this point in the history
  • Loading branch information
weirdan authored Jan 2, 2022
2 parents 376d2a3 + e3116e0 commit ab30a36
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 14 deletions.
6 changes: 4 additions & 2 deletions src/Psalm/Internal/LanguageServer/LanguageServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,15 +238,15 @@ function () {
$this->textDocument = new ServerTextDocument(
$this,
$codebase,
$this->project_analyzer->onchange_line_limit
$this->project_analyzer
);
}

if ($this->workspace === null) {
$this->workspace = new ServerWorkspace(
$this,
$codebase,
$this->project_analyzer->onchange_line_limit
$this->project_analyzer
);
}

Expand Down Expand Up @@ -279,6 +279,8 @@ function () {
// Support "Hover"
$serverCapabilities->hoverProvider = true;
// Support "Completion"
$serverCapabilities->codeActionProvider = true;
// Support "Code Actions"

if ($this->project_analyzer->provide_completion) {
$serverCapabilities->completionProvider = new CompletionOptions();
Expand Down
108 changes: 101 additions & 7 deletions src/Psalm/Internal/LanguageServer/Server/TextDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,21 @@
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;

/**
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -116,7 +125,7 @@ public function didChange(VersionedTextDocumentIdentifier $textDocument, array $
return;
}

if ($this->onchange_line_limit === 0) {
if ($this->project_analyzer->onchange_line_limit === 0) {
return;
}

Expand All @@ -126,8 +135,8 @@ public function didChange(VersionedTextDocumentIdentifier $textDocument, array $
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;
}
}
Expand Down Expand Up @@ -341,4 +350,89 @@ public function signatureHelp(TextDocumentIdentifier $textDocument, Position $po
$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)
);
}
}
13 changes: 8 additions & 5 deletions src/Psalm/Internal/LanguageServer/Server/Workspace.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use LanguageServerProtocol\FileChangeType;
use LanguageServerProtocol\FileEvent;
use Psalm\Codebase;
use Psalm\Internal\Analyzer\ProjectAnalyzer;
use Psalm\Internal\LanguageServer\LanguageServer;

/**
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -62,7 +65,7 @@ public function didChangeWatchedFiles(array $changes): void
continue;
}

if ($this->onchange_line_limit === 0) {
if ($this->project_analyzer->onchange_line_limit === 0) {
continue;
}

Expand Down

0 comments on commit ab30a36

Please sign in to comment.