-
Notifications
You must be signed in to change notification settings - Fork 185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use PHP_CodeSniffer as a formatter #35
Conversation
Current coverage is 89.91% (diff: 91.48%)@@ master #35 diff @@
==========================================
Files 21 23 +2
Lines 456 496 +40
Methods 66 70 +4
Messages 0 0
Branches 0 0
==========================================
+ Hits 409 446 +37
- Misses 47 50 +3
Partials 0 0
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM overall. Just some minor changes and more test coverage needed (see codecov)
|
||
use LanguageServer\Protocol\TextEdit; | ||
use LanguageServer\Protocol\Range; | ||
use LanguageServer\Protocol\Position; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use a group use statement here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
public function format(string $content, string $uri) | ||
{ | ||
// remove 'file://' prefix | ||
$uri = substr($uri, 7); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is the right way to do it - an input of file:///c:/users/felix/test
would result in /c:/users/felix/test
which is not a valid path. #31 adds a path2uri
function, I would propose to add the reverse uri2path
function (with some tests) https://github.com/MadHed/php-language-server/blob/7b0b9b890cabd3f0c2e1d7f10314ed61f53df110/src/utils.php#L23-L33
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now I added new method to Formatter to avoid conflicts with #31. Later I can move it to utils.php
$currentDir = dirname($currentDir); | ||
} while ($currentDir !== '.' && $currentDir !== $lastDir); | ||
|
||
$standard = \PHP_CodeSniffer::getConfigData('default_standard'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$standard = \PHP_CodeSniffer::getConfigData('default_standard') ?? 'PSR2'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
I noticed that a big mess happens when formatting (in VSCode) the SymbolFinder.php file - in this part: https://github.com/felixfbecker/php-language-server/blob/master/src/SymbolFinder.php#L54-L63 |
Code Sniffer is failing to format SymbolFinder.php. I added simple error handling. Tomorrow I will report this problem to PHPCS. |
$file->fixer->fixFile(); | ||
$fixed = $file->fixer->fixFile(); | ||
if (!$fixed) { | ||
throw new ResponseError("Unable to format file", ErrorCode::INTERNAL_ERROR); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use single quotes
I reported formatting issue: squizlabs/PHP_CodeSniffer#1180 |
Will merge #31 first, will probably be some refactoring for you to rebase |
Ok, I'm aware of that ;) |
Just merged #31 |
fb8848f
to
0ba3304
Compare
@@ -22,4 +22,10 @@ class TextEdit | |||
* @var string | |||
*/ | |||
public $newText; | |||
|
|||
public function __construct(Range $range = null, string $newText) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-optional parameter cannot follow an optional param. Both should be optional to allow constructing a instance and then setting the properties manually
$uri = "/home/foo/bar/Test.php"; | ||
$this->assertEquals("/home/foo/bar/Test.php", \LanguageServer\uriToPath($uri)); | ||
|
||
$uri = "/home/foo space/bar/Test.php"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a valid URI, space needs to be %20
$uri = "file:///c:/home/foo/bar/Test.php"; | ||
$this->assertEquals("c:\\home\\foo\\bar\\Test.php", \LanguageServer\uriToPath($uri)); | ||
|
||
$uri = "c:\\home\\foo\\bar\\Test.php"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not a valid URI so I don't think we need to test against it
return $uri; | ||
} | ||
$path = substr($uri, $position + 3); | ||
return strpos($path, ':') ? str_replace('/', "\\", substr($path, 1)) : $path; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that happy with this implementation.
- If possible I would like to use
parse_url
- Needs to handle %-encoding
- Needs to replace
/
withDIRECTORY_SEPERATOR
$textDocument = new Server\TextDocument($project, $client); | ||
|
||
$result = $textDocument->formatting(new TextDocumentIdentifier('whatever'), new FormattingOptions()); | ||
$this->assertEquals([], json_decode(json_encode($result), true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imo an invalid URI should throw an error instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actual implementation will create PhpDocument
for invalid/non-existing uri (Project::getDocument
). I can try to reorganize this with this change but I think it should be done as separate issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough.
do { | ||
$default = $currentDir . DIRECTORY_SEPARATOR . 'phpcs.xml'; | ||
if (is_file($default) === true) { | ||
return array($default); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use short array syntax
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests for uriToPath
and pathToUri
should be symmetric, could you clean them up a bit?
$path = substr($uri, $position + 3); | ||
return strpos($path, ':') ? str_replace('/', "\\", substr($path, 1)) : $path; | ||
$path = urldecode(parse_url($uri)['path']); | ||
return strpos($path, ':') ? str_replace(\DIRECTORY_SEPARATOR, '\\', substr($path, 1)) : $path; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strpos
may return0
orfalse
, should check explicitly forfalse
here- It's
str_replace($search, $replace, $subject)
, we need to search for'/'
and replace it withDIRECTORY_SEPERATOR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be ok right now.
$uri = 'file:///a/b/c/test.txt'; | ||
$this->assertEquals('/a/b/c/test.txt', \LanguageServer\uriToPath($uri)); | ||
|
||
$uri = 'file:///c%3A/foo/bar.baz'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The colon after the drive letter is typically not encoded afaik
https://en.wikipedia.org/wiki/File_URI_scheme#Windows_2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to simple colon.
if ($content === $new) { | ||
return []; | ||
} | ||
return [new TextEdit(new Range(new Position(0, 0), new Position(PHP_INT_MAX, PHP_INT_MAX)), $new)]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eclipse Che requires an accurate calculation of the end position. Although VSCode can handle this well, the editor in Che just breaks if any negative values or big values beyond the document end are provided.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I will try to calculate end position.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! This now works very well in Che.
@felixfbecker is there anything else pending (except resolving conflicts) for this PR to be merged? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There a few minor nitpicks which I will just fix myself if you don't beat me to it. The important stuff really is support for non-file URIs
{ | ||
$path = uriToPath($uri); | ||
$cs = new \PHP_CodeSniffer(); | ||
$cs->initStandard($this->findConfiguration($path)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this also work in environments where
- URIs are not file URIs
- there is no config file in the project
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this also work in environments where
URIs are not file URIs
For directory URI it wont work if URI = directory with config file but in this case it will load default configuration. Should we be worried about such cases? Do you notices URIs different than file?
there is no config file in the project ?
If config file is not found PSR2 standard is loaded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I meant was that we need to handle URIs that don't use the file://
scheme, but something like vfs://
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, understand now. We can think about it later.
*/ | ||
function uriToPath(string $uri) | ||
{ | ||
$filepath = urldecode(parse_url($uri)['path']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A URI can have any protocol, but only file URIs can be mapped to a path. We need to throw an InvalidArgumentException
if the URI is not a file://
URI and also handle/test that case in the implementation.
function uriToPath(string $uri) | ||
{ | ||
$filepath = urldecode(parse_url($uri)['path']); | ||
return strpos($filepath, ':') === false ? $filepath : str_replace('/', '\\', $filepath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a little brittle. There are also implementations that use |
instead of the colon on windows. You don't need to test against that, but it would be better to simply do str_replace('/', DIRECTORY_SEPERATOR, $filepath)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forget what I said. the directory separator should of course be determined by the URI, not the runtime OS.
private function calculateEndPosition(string $content): Position | ||
{ | ||
$lines = explode("\n", $content); | ||
return new Position(sizeof($lines) - 1, strlen(end($lines))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use count
, sizeof
is just an alias and PHP has so many aliases nobody can remember them all 😄
* @param string $uri | ||
* @return string[] | ||
*/ | ||
private function findConfiguration(string $uri) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parameter is named $uri
, but you call it with a path, not a URI. Some description for the function and params would also be nice
$this->assertTrue($edits == []); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing newline - I don't know which editor you use but I recommend you to look for an EditorConfig extension 😉
use LanguageServer\Protocol\{TextEdit, Range, Position, ErrorCode}; | ||
use AdvancedJsonRpc\ResponseError; | ||
|
||
class Formatter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initially I was wondering why this needs to be a class that is instantiated when it has no properties, constructor or internal state. Everything could just be pure functions. But it's probably better to encapsulate the private functions. One possibility would be though to make them static and the class abstract
. Opinions on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this class to prevent messing with PhpDocument in future. Abstract class and static methods sounds ok for me.
$textDocument = new Server\TextDocument($project, $client); | ||
|
||
$result = $textDocument->formatting(new TextDocumentIdentifier('whatever'), new FormattingOptions()); | ||
$this->assertEquals([], json_decode(json_encode($result), true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough.
$textDocument = new Server\TextDocument($project, $client); | ||
|
||
$result = $textDocument->formatting(new TextDocumentIdentifier('whatever'), new FormattingOptions()); | ||
$this->assertEquals([], json_decode(json_encode($result), true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need to json_decode(json_encode())
public function format(string $content, string $uri) | ||
{ | ||
$path = uriToPath($uri); | ||
$cs = new \PHP_CodeSniffer(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a use PHP_CodeSniffer
at the top. This declares the dependency on the class and avoids having to use absolute references
Fixed the style issues |
I think this is good to go, just needs a rebase |
1642754
to
47482fc
Compare
No description provided.