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

Config object passed to hooks #66

Merged
merged 9 commits into from Jun 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions .pylintrc
@@ -0,0 +1,26 @@
[FORMAT]

max-line-length = 120

[MESSAGES CONTROL]

enable =
useless-suppression

disable =
duplicate-code,
invalid-name,
fixme,
missing-docstring,
protected-access,
too-few-public-methods,
too-many-arguments,
too-many-instance-attributes

[REPORTS]

reports = no

[TYPECHECK]

generated-members = pyls_*
77 changes: 77 additions & 0 deletions pyls/config.py
@@ -0,0 +1,77 @@
# Copyright 2017 Palantir Technologies, Inc.
from configparser import RawConfigParser
import logging
import os
import pluggy

from . import hookspecs, PYLS

log = logging.getLogger(__name__)


class Config(object):

def __init__(self, root_path, init_opts):
self._root_path = root_path
self._init_opts = init_opts

self._pm = pluggy.PluginManager(PYLS)
self._pm.trace.root.setwriter(log.debug)
self._pm.enable_tracing()
self._pm.add_hookspecs(hookspecs)
self._pm.load_setuptools_entrypoints(PYLS)

@property
def plugin_manager(self):
return self._pm

@property
def init_opts(self):
return self._init_opts

@property
def root_path(self):
return self._root_path

def find_parents(self, path, names):
return find_parents(self.root_path, path, names)


def build_config(key, config_files):
"""Parse configuration from the given files for the given key."""
config = RawConfigParser()

if not config_files:
# If no config files match, we can't do much
return {}

# Find out which section header is used, pep8 or pycodestyle
files_read = config.read(config_files)
log.debug("Loaded configuration from %s", files_read)

if config.has_section(key):
return {k: v for k, v in config.items(key)}

return {}


def find_parents(root, path, names):
"""Find files matching the given names relative to the given path.

Args:
path (str): The path to start searching up from
names (List[str]): The file/directory names to look for
root (str): The directory at which to stop recursing upwards.

Note:
The path MUST be within the root.
"""
if not os.path.commonprefix((root, path)):
raise ValueError("Path %s not in %s" % (path, root))
curdir = os.path.dirname(path)

while curdir != os.path.dirname(root) and curdir != '/':
existing = list(filter(os.path.exists, [os.path.join(curdir, n) for n in names]))
if existing:
return existing
curdir = os.path.dirname(curdir)
35 changes: 20 additions & 15 deletions pyls/hookspecs.py
Expand Up @@ -4,17 +4,17 @@


@hookspec
def pyls_code_actions(workspace, document, range, context):
def pyls_code_actions(config, workspace, document, range, context):
pass


@hookspec
def pyls_code_lens(workspace, document):
def pyls_code_lens(config, workspace, document):
pass


@hookspec
def pyls_commands(workspace):
def pyls_commands(config, workspace):
"""The list of command strings supported by the server.

Returns:
Expand All @@ -23,60 +23,65 @@ def pyls_commands(workspace):


@hookspec
def pyls_completions(workspace, document, position):
def pyls_completions(config, workspace, document, position):
pass


@hookspec
def pyls_definitions(workspace, document, position):
def pyls_definitions(config, workspace, document, position):
pass


@hookspec
def pyls_document_did_save(workspace, document):
def pyls_document_did_open(config, workspace, document):
pass


@hookspec
def pyls_document_symbols(workspace, document):
def pyls_document_did_save(config, workspace, document):
pass


@hookspec
def pyls_document_symbols(config, workspace, document):
pass


@hookspec(firstresult=True)
def pyls_execute_command(workspace, command, arguments):
def pyls_execute_command(config, workspace, command, arguments):
pass


@hookspec(firstresult=True)
def pyls_format_document(workspace, document):
def pyls_format_document(config, workspace, document):
pass


@hookspec(firstresult=True)
def pyls_format_range(workspace, document, range):
def pyls_format_range(config, workspace, document, range):
pass


@hookspec(firstresult=True)
def pyls_hover(workspace, document, position):
def pyls_hover(config, workspace, document, position):
pass


@hookspec
def pyls_initialize(workspace):
def pyls_initialize(config, workspace):
pass


@hookspec
def pyls_lint(workspace, document):
def pyls_lint(config, workspace, document):
pass


@hookspec
def pyls_references(workspace, document, position, exclude_declaration):
def pyls_references(config, workspace, document, position, exclude_declaration):
pass


@hookspec(firstresult=True)
def pyls_signature_help(workspace, document, position):
def pyls_signature_help(config, workspace, document, position):
pass
17 changes: 9 additions & 8 deletions pyls/language_server.py
Expand Up @@ -3,14 +3,15 @@
import re
import socketserver
from .server import JSONRPCServer
from .workspace import Workspace

log = logging.getLogger(__name__)


class _StreamHandlerWrapper(socketserver.StreamRequestHandler, object):
"""A wrapper class that is used to construct a custom handler class."""

delegate = None

def setup(self):
super(_StreamHandlerWrapper, self).setup()
self.delegate = self.DELEGATE_CLASS(self.rfile, self.wfile)
Expand Down Expand Up @@ -74,22 +75,22 @@ class LanguageServer(MethodJSONRPCServer):
"""

process_id = None
workspace = None # type: Workspace
root_path = None
init_opts = None

def capabilities(self):
return {}

def initialize(self):
def initialize(self, root_path, init_opts, process_id):
pass

def m_initialize(self, **kwargs):
log.debug("Language server intialized with %s", kwargs)
self.process_id = kwargs.get('processId')
self.workspace = Workspace(kwargs.get('rootPath'), lang_server=self)
self.root_path = kwargs.get('rootPath')
self.init_opts = kwargs.get('initializationOptions')
self.process_id = kwargs.get('processId')

self.initialize()
self.initialize(self.root_path, self.init_opts, self.process_id)

# Get our capabilities
return {'capabilities': self.capabilities()}
Expand All @@ -101,10 +102,10 @@ def m___cancel_request(self, **kwargs):
# This tends to happen when cancelling a hover request
pass

def m_shutdown(self, **kwargs):
def m_shutdown(self, **_kwargs):
self.shutdown()

def m_exit(self, **kwargs):
def m_exit(self, **_kwargs):
self.shutdown()


Expand Down
35 changes: 9 additions & 26 deletions pyls/plugins/pycodestyle_lint.py
@@ -1,8 +1,7 @@
# Copyright 2017 Palantir Technologies, Inc.
from configparser import RawConfigParser
import logging
import pycodestyle
from pyls import hookimpl
from pyls import config as pyls_config, hookimpl

log = logging.getLogger(__name__)

Expand All @@ -11,9 +10,15 @@


@hookimpl
def pyls_lint(workspace, document):
def pyls_lint(config, workspace, document):
# Read config from all over the place
conf = {k.replace("-", "_"): v for k, v in _get_config(workspace, document)}
config_files = config.find_parents(document.path, CONFIG_FILES)
pycodestyle_conf = pyls_config.build_config('pycodestyle', config_files)
pep8_conf = pyls_config.build_config('pep8', config_files)

conf_to_use = pycodestyle_conf if pycodestyle_conf else pep8_conf

conf = {k.replace("-", "_"): v for k, v in conf_to_use.items()}
log.debug("Got pycodestyle config: %s", conf)

# Grab the pycodestyle parser and set the defaults based on the config we found
Expand All @@ -31,28 +36,6 @@ def pyls_lint(workspace, document):
return diagnostics


def _get_config(workspace, document):
""" Parse the pep8/pycodestyle config options. """
config = RawConfigParser()
config_files = workspace.find_parent_files(document.path, CONFIG_FILES)

if not config_files:
# If no config files match, we can't do much
return []

# Find out which section header is used, pep8 or pycodestyle
files_read = config.read(config_files)
log.debug("Using pycodestyle config from %s", files_read)

if config.has_section('pycodestyle'):
return config.items('pycodestyle')
if config.has_section('pep8'):
log.warning("The 'pep8' section is deprecated, use 'pycodestyle' instead")
return config.items('pep8')

return []


class PyCodeStyleDiagnosticReport(pycodestyle.BaseReport):

def __init__(self, options=None, **kwargs):
Expand Down