Skip to content
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

Merged
merged 9 commits into from
Oct 10, 2016

Conversation

mniewrzal
Copy link
Contributor

No description provided.

@codecov-io
Copy link

codecov-io commented Sep 27, 2016

Current coverage is 89.91% (diff: 91.48%)

Merging #35 into master will increase coverage by 0.22%

@@             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          
Diff Coverage File Path
•••••••• 86% new src/Formatter.php
•••••••••• 100% src/Protocol/TextEdit.php
•••••••••• 100% src/utils.php
•••••••••• 100% src/PhpDocument.php

Powered by Codecov. Last update 18ac760...47482fc

Copy link
Owner

@felixfbecker felixfbecker left a 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;
Copy link
Owner

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

Copy link
Contributor Author

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);
Copy link
Owner

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

Copy link
Contributor Author

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');
Copy link
Owner

@felixfbecker felixfbecker Sep 28, 2016

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'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@kaloyan-raev
Copy link
Contributor

kaloyan-raev commented Sep 29, 2016

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

@mniewrzal
Copy link
Contributor Author

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);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use single quotes

@mniewrzal
Copy link
Contributor Author

I reported formatting issue: squizlabs/PHP_CodeSniffer#1180

@felixfbecker
Copy link
Owner

felixfbecker commented Sep 30, 2016

Will merge #31 first, will probably be some refactoring for you to rebase

@mniewrzal
Copy link
Contributor Author

Ok, I'm aware of that ;)

@felixfbecker
Copy link
Owner

Just merged #31

@@ -22,4 +22,10 @@ class TextEdit
* @var string
*/
public $newText;

public function __construct(Range $range = null, string $newText)
Copy link
Owner

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";
Copy link
Owner

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";
Copy link
Owner

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;
Copy link
Owner

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 / with DIRECTORY_SEPERATOR

$textDocument = new Server\TextDocument($project, $client);

$result = $textDocument->formatting(new TextDocumentIdentifier('whatever'), new FormattingOptions());
$this->assertEquals([], json_decode(json_encode($result), true));
Copy link
Owner

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

Copy link
Contributor Author

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.

Copy link
Owner

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);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use short array syntax

Copy link
Owner

@felixfbecker felixfbecker left a 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;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • strpos may return 0 or false, should check explicitly for false here
  • It's str_replace($search, $replace, $subject), we need to search for '/' and replace it with DIRECTORY_SEPERATOR

Copy link
Contributor Author

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';
Copy link
Owner

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

Copy link
Contributor Author

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)];
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

@kaloyan-raev
Copy link
Contributor

@felixfbecker is there anything else pending (except resolving conflicts) for this PR to be merged?

Copy link
Owner

@felixfbecker felixfbecker left a 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));
Copy link
Owner

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
    ?

Copy link
Contributor Author

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.

Copy link
Owner

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://.

Copy link
Contributor Author

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']);
Copy link
Owner

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);
Copy link
Owner

@felixfbecker felixfbecker Oct 10, 2016

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)

https://en.wikipedia.org/wiki/File_URI_scheme#Windows

Copy link
Owner

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)));
Copy link
Owner

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)
Copy link
Owner

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 == []);
}

}
Copy link
Owner

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
Copy link
Owner

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?

Copy link
Contributor Author

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));
Copy link
Owner

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));
Copy link
Owner

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();
Copy link
Owner

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

@felixfbecker
Copy link
Owner

Fixed the style issues

@felixfbecker
Copy link
Owner

I think this is good to go, just needs a rebase

@felixfbecker felixfbecker merged commit e75c159 into felixfbecker:master Oct 10, 2016
@mniewrzal mniewrzal deleted the PHP_CodeSniffer branch October 10, 2016 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants