Merge 11516fb497
into 41e9fb7e8a
commit
feca4c5285
|
@ -27,7 +27,8 @@
|
||||||
"nikic/php-parser": "^3.0.0beta1",
|
"nikic/php-parser": "^3.0.0beta1",
|
||||||
"phpdocumentor/reflection-docblock": "^3.0",
|
"phpdocumentor/reflection-docblock": "^3.0",
|
||||||
"sabre/event": "^3.0",
|
"sabre/event": "^3.0",
|
||||||
"felixfbecker/advanced-json-rpc": "^1.2"
|
"felixfbecker/advanced-json-rpc": "^1.2",
|
||||||
|
"friendsofphp/php-cs-fixer" : "^1.12"
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace TestNamespace;
|
namespace TestNamespace;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,12 +9,16 @@ class TestClass
|
||||||
{
|
{
|
||||||
public $testProperty;
|
public $testProperty;
|
||||||
|
|
||||||
public function testMethod($testParameter)
|
/**
|
||||||
|
* @param $testParameter description
|
||||||
|
*/
|
||||||
|
public function testMethod( $testParameter)
|
||||||
|
|
||||||
{
|
{
|
||||||
$testVariable = 123;
|
$testVariable = 123;
|
||||||
|
|
||||||
if ( empty($testParameter)){
|
if ( empty($testParameter)) {
|
||||||
echo 'Empty';
|
echo 'Empty';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,14 @@ namespace TestNamespace;
|
||||||
class TestClass
|
class TestClass
|
||||||
{
|
{
|
||||||
public $testProperty;
|
public $testProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $testParameter description
|
||||||
|
*/
|
||||||
public function testMethod($testParameter)
|
public function testMethod($testParameter)
|
||||||
{
|
{
|
||||||
$testVariable = 123;
|
$testVariable = 123;
|
||||||
|
|
||||||
if (empty($testParameter)) {
|
if (empty($testParameter)) {
|
||||||
echo 'Empty';
|
echo 'Empty';
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LanguageServer\Server;
|
||||||
|
|
||||||
|
use Symfony\CS\ConfigAwareInterface;
|
||||||
|
use Symfony\CS\ConfigInterface;
|
||||||
|
use Symfony\CS\Fixer;
|
||||||
|
use Symfony\CS\FixerInterface;
|
||||||
|
use Symfony\CS\Tokenizer\Tokens;
|
||||||
|
use Symfony\CS\Utils;
|
||||||
|
|
||||||
|
class CustomCSFixer extends Fixer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $content
|
||||||
|
* @param string $uri
|
||||||
|
* @param ConfigInterface $config
|
||||||
|
*
|
||||||
|
* @return string|null formated source code or null if no change was made
|
||||||
|
*/
|
||||||
|
public function fixContent(string $content, string $uri, ConfigInterface $config)
|
||||||
|
{
|
||||||
|
$fixers = $this->prepareFixers($config);
|
||||||
|
$fixers = $this->sortFixers($fixers);
|
||||||
|
|
||||||
|
return $this->doFixContent($content, $uri, $fixers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $content
|
||||||
|
* @param string $uri
|
||||||
|
* @param array $fixers
|
||||||
|
*
|
||||||
|
* @return string|null formated source code or null if no change was made
|
||||||
|
*/
|
||||||
|
private function doFixContent(string $content, string $uri, array $fixers)
|
||||||
|
{
|
||||||
|
$new = $old = $content;
|
||||||
|
|
||||||
|
if ('' === $old ||
|
||||||
|
// PHP 5.3 has a broken implementation of token_get_all when the file uses __halt_compiler() starting in 5.3.6
|
||||||
|
(PHP_VERSION_ID >= 50306 && PHP_VERSION_ID < 50400 && false !== stripos($old, '__halt_compiler()'))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do not need Tokens to still caching previously fixed file - so clear the cache
|
||||||
|
Tokens::clearCache();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$file = new \SplFileInfo($uri);
|
||||||
|
foreach ($fixers as $fixer) {
|
||||||
|
if (!$fixer->supports($file)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$newest = $fixer->fix($file, $new);
|
||||||
|
$new = $newest;
|
||||||
|
}
|
||||||
|
} catch (\ParseError $e) {
|
||||||
|
return null;
|
||||||
|
} catch (\Error $e) {
|
||||||
|
return null;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($new !== $old) {
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param FixerInterface[] $fixers
|
||||||
|
*
|
||||||
|
* @return FixerInterface[]
|
||||||
|
*/
|
||||||
|
private function sortFixers(array $fixers)
|
||||||
|
{
|
||||||
|
usort($fixers, function (FixerInterface $a, FixerInterface $b) {
|
||||||
|
return Utils::cmpInt($b->getPriority(), $a->getPriority());
|
||||||
|
});
|
||||||
|
|
||||||
|
return $fixers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ConfigInterface $config
|
||||||
|
*
|
||||||
|
* @return FixerInterface[]
|
||||||
|
*/
|
||||||
|
private function prepareFixers(ConfigInterface $config)
|
||||||
|
{
|
||||||
|
$fixers = $config->getFixers();
|
||||||
|
|
||||||
|
foreach ($fixers as $fixer) {
|
||||||
|
if ($fixer instanceof ConfigAwareInterface) {
|
||||||
|
$fixer->setConfig($config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fixers;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LanguageServer\Server;
|
||||||
|
|
||||||
|
use Symfony\CS\Config;
|
||||||
|
use Symfony\CS\ConfigurationResolver;
|
||||||
|
use LanguageServer\Protocol\TextEdit;
|
||||||
|
use LanguageServer\Protocol\Range;
|
||||||
|
use LanguageServer\Protocol\Position;
|
||||||
|
|
||||||
|
class Formatter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CustomCSFixer
|
||||||
|
*/
|
||||||
|
private $fixer;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->fixer = new CustomCSFixer();
|
||||||
|
$this->fixer->registerBuiltInFixers();
|
||||||
|
$this->fixer->registerBuiltInConfigs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $content
|
||||||
|
*
|
||||||
|
* @return TextEdit[]
|
||||||
|
*/
|
||||||
|
public function format(string $content, string $uri)
|
||||||
|
{
|
||||||
|
// remove 'file://' prefix
|
||||||
|
$uri = substr($uri, 7);
|
||||||
|
|
||||||
|
$config = new Config();
|
||||||
|
|
||||||
|
// TODO read user configuration from workspace root (.php_cs file)
|
||||||
|
|
||||||
|
// register custom fixers from config
|
||||||
|
$this->fixer->registerCustomFixers($config->getCustomFixers());
|
||||||
|
|
||||||
|
$resolver = new ConfigurationResolver();
|
||||||
|
$resolver->setAllFixers($this->fixer->getFixers())
|
||||||
|
->setConfig($config)
|
||||||
|
//->setOptions(array('level' => 'psr2'))
|
||||||
|
->resolve();
|
||||||
|
|
||||||
|
$config->fixers($resolver->getFixers());
|
||||||
|
$formatted = $this->fixer->fixContent($content, $uri, $config);
|
||||||
|
if ($formatted == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$edit = new TextEdit();
|
||||||
|
$edit->range = new Range(new Position(0, 0), new Position(PHP_INT_MAX, PHP_INT_MAX));
|
||||||
|
$edit->newText = $formatted;
|
||||||
|
return [$edit];
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,6 +42,13 @@ class TextDocument
|
||||||
*/
|
*/
|
||||||
private $client;
|
private $client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opened source files content
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
private $documents = [];
|
||||||
|
|
||||||
public function __construct(LanguageClient $client)
|
public function __construct(LanguageClient $client)
|
||||||
{
|
{
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
|
@ -103,6 +110,8 @@ class TextDocument
|
||||||
*/
|
*/
|
||||||
private function updateAst(string $uri, string $content)
|
private function updateAst(string $uri, string $content)
|
||||||
{
|
{
|
||||||
|
$this->documents[$uri] = $content;
|
||||||
|
|
||||||
$stmts = $this->parser->parse($content);
|
$stmts = $this->parser->parse($content);
|
||||||
$diagnostics = [];
|
$diagnostics = [];
|
||||||
foreach ($this->parser->getErrors() as $error) {
|
foreach ($this->parser->getErrors() as $error) {
|
||||||
|
@ -128,24 +137,34 @@ class TextDocument
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The document close notification is sent from the client to the server when
|
||||||
|
* the document got closed in the client. The document's truth now exists where the document's
|
||||||
|
* uri points to (e.g. if the document's uri is a file uri the truth now exists on disk).
|
||||||
|
*
|
||||||
|
* @param TextDocumentIdentifier $textDocument
|
||||||
|
*/
|
||||||
|
public function didClose(TextDocumentIdentifier $textDocument)
|
||||||
|
{
|
||||||
|
unset($this->documents[$textDocument->uri]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The document formatting request is sent from the server to the client to format a whole document.
|
* The document formatting request is sent from the server to the client to format a whole document.
|
||||||
*
|
*
|
||||||
* @param TextDocumentIdentifier $textDocument The document to format
|
* @param TextDocumentIdentifier $textDocument The document to format
|
||||||
* @param FormattingOptions $options The format options
|
* @param FormattingOptions $options The format options
|
||||||
|
*
|
||||||
* @return TextEdit[]
|
* @return TextEdit[]
|
||||||
*/
|
*/
|
||||||
public function formatting(TextDocumentIdentifier $textDocument, FormattingOptions $options)
|
public function formatting(TextDocumentIdentifier $textDocument, FormattingOptions $options)
|
||||||
{
|
{
|
||||||
$nodes = $this->asts[$textDocument->uri];
|
$formatter = new Formatter();
|
||||||
if (empty($nodes)) {
|
$content = $this->documents[$textDocument->uri];
|
||||||
|
if (!$content) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
$prettyPrinter = new PrettyPrinter();
|
|
||||||
$edit = new TextEdit();
|
|
||||||
$edit->range = new Range(new Position(0, 0), new Position(PHP_INT_MAX, PHP_INT_MAX));
|
|
||||||
$edit->newText = $prettyPrinter->prettyPrintFile($nodes);
|
|
||||||
return [$edit];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return $formatter->format($content, $textDocument->uri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue