1
0
Fork 0

Compare commits

...

8 Commits

Author SHA1 Message Date
Carl Kittelberger fb48c70ce2
Rename package and mark it as replacing original package. 2020-01-13 17:05:15 +01:00
Carl Kittelberger 3fc105717d
Implement existing fix from https://github.com/felixfbecker/php-language-server/issues/462#issuecomment-546696984. 2020-01-13 16:52:36 +01:00
Felix Becker 9dc1656592
build: remove composer install from semantic-release config 2018-12-12 16:29:42 +01:00
Jakob Blume 7303143a60 build: run 'composer install' in a docker builder stage (#694) 2018-12-12 16:28:19 +01:00
Tyson Andre 1705583e32 chore: add Phan (#690) 2018-11-29 09:50:01 +01:00
Felix Becker 1da3328bc2 fix: allow rootUri to be null
Fixes #684
2018-11-13 18:33:21 +01:00
Tyson Andre 450116e2f3 docs: remove unused use statements, nit on phpdoc (#625)
* Remove unused use statements, nit on phpdoc

Add a note on something that looks like an invalid array index

* Remove phpdoc param with no real param
2018-11-11 20:45:47 +01:00
Felix Becker b1cc565d7e fix(cache): bump cache version 2018-11-11 12:57:20 +01:00
16 changed files with 421 additions and 17 deletions

1
.gitattributes vendored
View File

@ -10,6 +10,7 @@
/.gitignore export-ignore /.gitignore export-ignore
/.gitmodules export-ignore /.gitmodules export-ignore
/.npmrc export-ignore /.npmrc export-ignore
/.phan export-ignore
/.travis.yml export-ignore /.travis.yml export-ignore
/appveyor.yml export-ignore /appveyor.yml export-ignore
/codecov.yml export-ignore /codecov.yml export-ignore

308
.phan/config.php Normal file
View File

@ -0,0 +1,308 @@
<?php
use Phan\Issue;
/**
* This configuration file was automatically generated by 'phan --init --init-level=1'
*
* TODOs (added by 'phan --init'):
*
* - Go through this file and verify that there are no missing/unnecessary files/directories.
* (E.g. this only includes direct composer dependencies - You may have to manually add indirect composer dependencies to 'directory_list')
* - Look at 'plugins' and add or remove plugins if appropriate (see https://github.com/phan/phan/tree/master/.phan/plugins#plugins)
* - Add global suppressions for pre-existing issues to suppress_issue_types (https://github.com/phan/phan/wiki/Tutorial-for-Analyzing-a-Large-Sloppy-Code-Base)
*
* This configuration will be read and overlayed on top of the
* default configuration. Command line arguments will be applied
* after this file is read.
*
* @see src/Phan/Config.php
* See Config for all configurable options.
*
* A Note About Paths
* ==================
*
* Files referenced from this file should be defined as
*
* ```
* Config::projectPath('relative_path/to/file')
* ```
*
* where the relative path is relative to the root of the
* project which is defined as either the working directory
* of the phan executable or a path passed in via the CLI
* '-d' flag.
*/
return [
// Supported values: '7.0', '7.1', '7.2', null.
// If this is set to null,
// then Phan assumes the PHP version which is closest to the minor version
// of the php executable used to execute phan.
// Automatically inferred from composer.json requirement for "php" of "^7.0"
'target_php_version' => '7.0',
// If enabled, missing properties will be created when
// they are first seen. If false, we'll report an
// error message if there is an attempt to write
// to a class property that wasn't explicitly
// defined.
'allow_missing_properties' => false,
// If enabled, null can be cast as any type and any
// type can be cast to null. Setting this to true
// will cut down on false positives.
'null_casts_as_any_type' => false,
// If enabled, allow null to be cast as any array-like type.
// This is an incremental step in migrating away from null_casts_as_any_type.
// If null_casts_as_any_type is true, this has no effect.
'null_casts_as_array' => false,
// If enabled, allow any array-like type to be cast to null.
// This is an incremental step in migrating away from null_casts_as_any_type.
// If null_casts_as_any_type is true, this has no effect.
'array_casts_as_null' => false,
// If enabled, scalars (int, float, bool, string, null)
// are treated as if they can cast to each other.
// This does not affect checks of array keys. See scalar_array_key_cast.
'scalar_implicit_cast' => false,
// If enabled, any scalar array keys (int, string)
// are treated as if they can cast to each other.
// E.g. array<int,stdClass> can cast to array<string,stdClass> and vice versa.
// Normally, a scalar type such as int could only cast to/from int and mixed.
'scalar_array_key_cast' => false,
// If this has entries, scalars (int, float, bool, string, null)
// are allowed to perform the casts listed.
// E.g. ['int' => ['float', 'string'], 'float' => ['int'], 'string' => ['int'], 'null' => ['string']]
// allows casting null to a string, but not vice versa.
// (subset of scalar_implicit_cast)
'scalar_implicit_partial' => [],
// If true, seemingly undeclared variables in the global
// scope will be ignored. This is useful for projects
// with complicated cross-file globals that you have no
// hope of fixing.
'ignore_undeclared_variables_in_global_scope' => false,
// Backwards Compatibility Checking. This is slow
// and expensive, but you should consider running
// it before upgrading your version of PHP to a
// new version that has backward compatibility
// breaks.
'backward_compatibility_checks' => false,
// If true, check to make sure the return type declared
// in the doc-block (if any) matches the return type
// declared in the method signature.
'check_docblock_signature_return_type_match' => true,
// (*Requires check_docblock_signature_param_type_match to be true*)
// If true, make narrowed types from phpdoc params override
// the real types from the signature, when real types exist.
// (E.g. allows specifying desired lists of subclasses,
// or to indicate a preference for non-nullable types over nullable types)
// Affects analysis of the body of the method and the param types passed in by callers.
'prefer_narrowed_phpdoc_param_type' => true,
// (*Requires check_docblock_signature_return_type_match to be true*)
// If true, make narrowed types from phpdoc returns override
// the real types from the signature, when real types exist.
// (E.g. allows specifying desired lists of subclasses,
// or to indicate a preference for non-nullable types over nullable types)
// Affects analysis of return statements in the body of the method and the return types passed in by callers.
'prefer_narrowed_phpdoc_return_type' => true,
'ensure_signature_compatibility' => true,
// Set to true in order to attempt to detect dead
// (unreferenced) code. Keep in mind that the
// results will only be a guess given that classes,
// properties, constants and methods can be referenced
// as variables (like `$class->$property` or
// `$class->$method()`) in ways that we're unable
// to make sense of.
'dead_code_detection' => false,
// If true, this run a quick version of checks that takes less
// time at the cost of not running as thorough
// an analysis. You should consider setting this
// to true only when you wish you had more **undiagnosed** issues
// to fix in your code base.
//
// In quick-mode the scanner doesn't rescan a function
// or a method's code block every time a call is seen.
// This means that the problem here won't be detected:
//
// ```php
// <?php
// function test($arg):int {
// return $arg;
// }
// test("abc");
// ```
//
// This would normally generate:
//
// ```sh
// test.php:3 TypeError return string but `test()` is declared to return int
// ```
//
// The initial scan of the function's code block has no
// type information for `$arg`. It isn't until we see
// the call and rescan test()'s code block that we can
// detect that it is actually returning the passed in
// `string` instead of an `int` as declared.
'quick_mode' => false,
// If true, then before analysis, try to simplify AST into a form
// which improves Phan's type inference in edge cases.
//
// This may conflict with 'dead_code_detection'.
// When this is true, this slows down analysis slightly.
//
// E.g. rewrites `if ($a = value() && $a > 0) {...}`
// into $a = value(); if ($a) { if ($a > 0) {...}}`
'simplify_ast' => true,
// Enable or disable support for generic templated
// class types.
'generic_types_enabled' => true,
// Override to hardcode existence and types of (non-builtin) globals in the global scope.
// Class names should be prefixed with '\\'.
// (E.g. ['_FOO' => '\\FooClass', 'page' => '\\PageClass', 'userId' => 'int'])
'globals_type_map' => [],
// The minimum severity level to report on. This can be
// set to Issue::SEVERITY_LOW, Issue::SEVERITY_NORMAL or
// Issue::SEVERITY_CRITICAL. Setting it to only
// critical issues is a good place to start on a big
// sloppy mature code base.
'minimum_severity' => Issue::SEVERITY_LOW,
// Add any issue types (such as 'PhanUndeclaredMethod')
// to this black-list to inhibit them from being reported.
'suppress_issue_types' => [
'PhanTypeMismatchDeclaredParamNullable',
'PhanUndeclaredProperty', // 66 occurence(s) (e.g. not being specific enough about the subclass)
'PhanUndeclaredMethod', // 32 occurence(s) (e.g. not being specific enough about the subclass of Node)
'PhanTypeMismatchArgument', // 21 occurence(s)
'PhanTypeMismatchProperty', // 13 occurence(s)
'PhanUnreferencedUseNormal', // 10 occurence(s) TODO: Fix
'PhanTypeMismatchDeclaredReturn', // 8 occurence(s)
'PhanUndeclaredTypeProperty', // 7 occurence(s)
'PhanTypeMismatchReturn', // 6 occurence(s)
'PhanUndeclaredVariable', // 4 occurence(s)
'PhanUndeclaredTypeReturnType', // 4 occurence(s)
'PhanParamTooMany', // 3 occurence(s)
'PhanUndeclaredTypeParameter', // 2 occurence(s)
'PhanUndeclaredClassProperty', // 2 occurence(s)
'PhanTypeSuspiciousStringExpression', // 2 occurence(s)
'PhanTypeMismatchArgumentInternal', // 2 occurence(s)
'PhanUnextractableAnnotationElementName', // 1 occurence(s)
'PhanUndeclaredClassMethod', // 1 occurence(s)
'PhanUndeclaredClassInstanceof', // 1 occurence(s)
'PhanTypeSuspiciousNonTraversableForeach', // 1 occurence(s)
'PhanTypeMismatchDimAssignment', // 1 occurence(s)
'PhanTypeMismatchDeclaredParam', // 1 occurence(s)
'PhanTypeInvalidDimOffset', // 1 occurence(s)
],
// A regular expression to match files to be excluded
// from parsing and analysis and will not be read at all.
//
// This is useful for excluding groups of test or example
// directories/files, unanalyzable files, or files that
// can't be removed for whatever reason.
// (e.g. '@Test\.php$@', or '@vendor/.*/(tests|Tests)/@')
'exclude_file_regex' => '@^vendor/.*/(tests?|Tests?)/@',
// A file list that defines files that will be excluded
// from parsing and analysis and will not be read at all.
//
// This is useful for excluding hopelessly unanalyzable
// files that can't be removed for whatever reason.
'exclude_file_list' => [],
// A directory list that defines files that will be excluded
// from static analysis, but whose class and method
// information should be included.
//
// Generally, you'll want to include the directories for
// third-party code (such as "vendor/") in this list.
//
// n.b.: If you'd like to parse but not analyze 3rd
// party code, directories containing that code
// should be added to the `directory_list` as
// to `excluce_analysis_directory_list`.
'exclude_analysis_directory_list' => [
'vendor/',
],
// The number of processes to fork off during the analysis
// phase.
'processes' => 1,
// List of case-insensitive file extensions supported by Phan.
// (e.g. php, html, htm)
'analyzed_file_extensions' => [
'php',
],
// You can put paths to stubs of internal extensions in this config option.
// If the corresponding extension is **not** loaded, then phan will use the stubs instead.
// Phan will continue using its detailed type annotations,
// but load the constants, classes, functions, and classes (and their Reflection types)
// from these stub files (doubling as valid php files).
// Use a different extension from php to avoid accidentally loading these.
// The 'tools/make_stubs' script can be used to generate your own stubs (compatible with php 7.0+ right now)
'autoload_internal_extension_signatures' => [],
// A list of plugin files to execute
// Plugins which are bundled with Phan can be added here by providing their name (e.g. 'AlwaysReturnPlugin')
// Alternately, you can pass in the full path to a PHP file with the plugin's implementation (e.g. 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php')
'plugins' => [
'AlwaysReturnPlugin',
'DollarDollarPlugin',
'DuplicateArrayKeyPlugin',
'PregRegexCheckerPlugin',
'PrintfCheckerPlugin',
'UnreachableCodePlugin',
],
// A list of directories that should be parsed for class and
// method information. After excluding the directories
// defined in exclude_analysis_directory_list, the remaining
// files will be statically analyzed for errors.
//
// Thus, both first-party and third-party code being used by
// your application should be included in this list.
'directory_list' => [
'src',
'vendor/composer/xdebug-handler/src',
'vendor/felixfbecker/advanced-json-rpc/lib',
'vendor/felixfbecker/language-server-protocol/src/',
'vendor/microsoft/tolerant-php-parser/src',
'vendor/netresearch/jsonmapper/src',
'vendor/phpdocumentor/reflection-common/src',
'vendor/phpdocumentor/reflection-docblock/src',
'vendor/phpdocumentor/type-resolver/src',
'vendor/phpunit/phpunit/src',
'vendor/psr/log/Psr',
'vendor/sabre/event/lib',
'vendor/sabre/uri/lib',
'vendor/webmozart/glob/src',
'vendor/webmozart/path-util/src',
],
// A list of individual files to include in analysis
// with a path relative to the root directory of the
// project
'file_list' => [
'bin/php-language-server.php',
],
];

View File

@ -16,8 +16,11 @@ cache:
install: install:
- composer install --prefer-dist --no-interaction - composer install --prefer-dist --no-interaction
- pecl install ast-1.0.0
script: script:
- vendor/bin/phpcs -n - vendor/bin/phpcs -n
- vendor/bin/phan
- vendor/bin/phpunit --coverage-clover=coverage.xml --colors=always - vendor/bin/phpunit --coverage-clover=coverage.xml --colors=always
- bash <(curl -s https://codecov.io/bash) - bash <(curl -s https://codecov.io/bash)

View File

@ -1,17 +1,19 @@
# Running this container will start a language server that listens for TCP connections on port 2088 # Running this container will start a language server that listens for TCP connections on port 2088
# Every connection will be run in a forked child process # Every connection will be run in a forked child process
# Please note that before building the image, you have to install dependencies with `composer install` FROM composer AS builder
COPY ./ /app
RUN composer install
FROM php:7-cli FROM php:7-cli
MAINTAINER Felix Becker <felix.b@outlook.com> LABEL maintainer="Felix Becker <felix.b@outlook.com>"
RUN docker-php-ext-configure pcntl --enable-pcntl RUN docker-php-ext-configure pcntl --enable-pcntl
RUN docker-php-ext-install pcntl RUN docker-php-ext-install pcntl
COPY ./php.ini /usr/local/etc/php/conf.d/ COPY ./php.ini /usr/local/etc/php/conf.d/
COPY ./ /srv/phpls COPY --from=builder /app /srv/phpls
WORKDIR /srv/phpls WORKDIR /srv/phpls

View File

@ -1,5 +1,5 @@
{ {
"name": "felixfbecker/language-server", "name": "icedream/language-server",
"description": "PHP Implementation of the Visual Studio Code Language Server Protocol", "description": "PHP Implementation of the Visual Studio Code Language Server Protocol",
"license": "ISC", "license": "ISC",
"keywords": [ "keywords": [
@ -37,8 +37,12 @@
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^6.3", "phpunit/phpunit": "^6.3",
"phan/phan": "1.1.4",
"squizlabs/php_codesniffer": "^3.1" "squizlabs/php_codesniffer": "^3.1"
}, },
"replace": {
"felixfbecker/language-server": "self.version"
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"LanguageServer\\": "src/" "LanguageServer\\": "src/"

View File

@ -17,7 +17,7 @@
"prepare": [ "prepare": [
{ {
"path": "@semantic-release/exec", "path": "@semantic-release/exec",
"cmd": "composer install --prefer-dist --no-interaction && docker build -t felixfbecker/php-language-server ." "cmd": "docker build -t felixfbecker/php-language-server ."
} }
], ],
"publish": [ "publish": [

View File

@ -11,6 +11,11 @@ use Sabre\Event\Promise;
*/ */
class ClientCache implements Cache class ClientCache implements Cache
{ {
/**
* @var LanguageClient
*/
public $client;
/** /**
* @param LanguageClient $client * @param LanguageClient $client
*/ */

View File

@ -4,7 +4,7 @@ declare(strict_types = 1);
namespace LanguageServer\Client; namespace LanguageServer\Client;
use LanguageServer\ClientHandler; use LanguageServer\ClientHandler;
use LanguageServerProtocol\{TextDocumentItem, TextDocumentIdentifier}; use LanguageServerProtocol\{Diagnostic, TextDocumentItem, TextDocumentIdentifier};
use Sabre\Event\Promise; use Sabre\Event\Promise;
use JsonMapper; use JsonMapper;

View File

@ -4,7 +4,7 @@ declare(strict_types = 1);
namespace LanguageServer; namespace LanguageServer;
use LanguageServer\Index\ReadableIndex; use LanguageServer\Index\ReadableIndex;
use phpDocumentor\Reflection\{Types, Type, Fqsen, TypeResolver}; use phpDocumentor\Reflection\{Types, Type, TypeResolver};
use LanguageServerProtocol\SymbolInformation; use LanguageServerProtocol\SymbolInformation;
use Generator; use Generator;
@ -80,7 +80,7 @@ class Definition
* Can also be a compound type. * Can also be a compound type.
* If it is unknown, will be Types\Mixed_. * If it is unknown, will be Types\Mixed_.
* *
* @var \phpDocumentor\Type|null * @var Type|null
*/ */
public $type; public $type;

View File

@ -3,7 +3,6 @@ declare(strict_types = 1);
namespace LanguageServer\FilesFinder; namespace LanguageServer\FilesFinder;
use Webmozart\Glob\Iterator\GlobIterator;
use Sabre\Event\Promise; use Sabre\Event\Promise;
use function Sabre\Event\coroutine; use function Sabre\Event\coroutine;
use function LanguageServer\{pathToUri, timeout}; use function LanguageServer\{pathToUri, timeout};
@ -23,7 +22,7 @@ class FileSystemFilesFinder implements FilesFinder
$uris = []; $uris = [];
foreach (new GlobIterator($glob) as $path) { foreach (new GlobIterator($glob) as $path) {
// Exclude any directories that also match the glob pattern // Exclude any directories that also match the glob pattern
if (!is_dir($path)) { if (!is_dir($path) || !is_readable($path)) {
$uris[] = pathToUri($path); $uris[] = pathToUri($path);
} }

View File

@ -0,0 +1,84 @@
<?php
declare (strict_types = 1);
namespace LanguageServer\FilesFinder;
use ArrayIterator;
use EmptyIterator;
use IteratorIterator;
use RecursiveIteratorIterator;
use Webmozart\Glob\Glob;
use Webmozart\Glob\Iterator\GlobFilterIterator;
use Webmozart\Glob\Iterator\RecursiveDirectoryIterator;
/**
* Returns filesystem paths matching a glob.
*
* @since 1.0
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @see Glob
*/
class GlobIterator extends IteratorIterator
{
/**
* Creates a new iterator.
*
* @param string $glob The glob pattern.
* @param int $flags A bitwise combination of the flag constants in
* {@link Glob}.
*/
public function __construct($glob, $flags = 0)
{
$basePath = Glob::getBasePath($glob, $flags);
if (!Glob::isDynamic($glob) && file_exists($glob)) {
// If the glob is a file path, return that path
$innerIterator = new ArrayIterator(array($glob));
} elseif (is_dir($basePath)) {
// Use the system's much more efficient glob() function where we can
if (
// glob() does not support /**/
false === strpos($glob, '/**/') &&
// glob() does not support stream wrappers
false === strpos($glob, '://') &&
// glob() does not support [^...] on Windows
('\\' !== DIRECTORY_SEPARATOR || false === strpos($glob, '[^'))
) {
$results = glob($glob, GLOB_BRACE);
// $results may be empty or false if $glob is invalid
if (empty($results)) {
// Parse glob and provoke errors if invalid
Glob::toRegEx($glob);
// Otherwise return empty result set
$innerIterator = new EmptyIterator();
} else {
$innerIterator = new ArrayIterator($results);
}
} else {
// Otherwise scan the glob's base directory for matches
$innerIterator = new GlobFilterIterator(
$glob,
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
$basePath,
RecursiveDirectoryIterator::CURRENT_AS_PATHNAME,
RecursiveDirectoryIterator::SKIP_DOTS
),
RecursiveIteratorIterator::SELF_FIRST,
RecursiveIteratorIterator::CATCH_GET_CHILD
),
GlobFilterIterator::FILTER_VALUE,
$flags
);
}
} else {
// If the glob's base directory does not exist, return nothing
$innerIterator = new EmptyIterator();
}
parent::__construct($innerIterator);
}
}

View File

@ -274,7 +274,6 @@ class Index implements ReadableIndex, \Serializable
} }
/** /**
* @param string $serialized
* @return string * @return string
*/ */
public function serialize() public function serialize()

View File

@ -16,7 +16,7 @@ class Indexer
/** /**
* @var int The prefix for every cache item * @var int The prefix for every cache item
*/ */
const CACHE_VERSION = 2; const CACHE_VERSION = 3;
/** /**
* @var FilesFinder * @var FilesFinder

View File

@ -167,7 +167,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
*/ */
public function initialize(ClientCapabilities $capabilities, string $rootPath = null, int $processId = null, string $rootUri = null): Promise public function initialize(ClientCapabilities $capabilities, string $rootPath = null, int $processId = null, string $rootUri = null): Promise
{ {
if ($rootPath === null) { if ($rootPath === null && $rootUri !== null) {
$rootPath = uriToPath($rootUri); $rootPath = uriToPath($rootUri);
} }
return coroutine(function () use ($capabilities, $rootPath, $processId) { return coroutine(function () use ($capabilities, $rootPath, $processId) {

View File

@ -63,7 +63,7 @@ class PhpDocument
/** /**
* Map from fully qualified name (FQN) to Node * Map from fully qualified name (FQN) to Node
* *
* @var Node * @var Node[]
*/ */
private $definitionNodes; private $definitionNodes;

View File

@ -6,8 +6,7 @@ namespace LanguageServer;
use LanguageServer\Index\ReadableIndex; use LanguageServer\Index\ReadableIndex;
use LanguageServerProtocol\{ use LanguageServerProtocol\{
Position, Position,
SignatureHelp, SignatureHelp
ParameterInformation
}; };
use Microsoft\PhpParser\Node; use Microsoft\PhpParser\Node;
use Sabre\Event\Promise; use Sabre\Event\Promise;