Skip to content

Commit

Permalink
Merge be25291 into c2727cf
Browse files Browse the repository at this point in the history
  • Loading branch information
rwols committed Aug 25, 2019
2 parents c2727cf + be25291 commit 1aff2d2
Show file tree
Hide file tree
Showing 20 changed files with 686 additions and 227 deletions.
6 changes: 4 additions & 2 deletions plugin/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
from .core.workspace import enable_in_project, disable_in_project

try:
from .core.types import ViewLike
from typing import List, Optional, Dict, Any
assert List and Optional and Dict and Any
assert ViewLike
except ImportError:
pass


def detect_supportable_view(view: sublime.View):
def detect_supportable_view(view: 'ViewLike'):
config = config_for_scope(view)
if not config:
available_config = get_global_client_config(view, client_configs.all)
Expand All @@ -35,7 +37,7 @@ def extract_syntax_name(syntax_file: str) -> str:
return syntax_file.split('/')[-1].split('.')[0]


def show_enable_config(view: sublime.View, config: ClientConfig):
def show_enable_config(view: 'ViewLike', config: ClientConfig):
syntax = str(view.settings().get("syntax", ""))
message = "LSP has found a language server for {}. Run \"Setup Language Server\" to start using it".format(
extract_syntax_name(syntax)
Expand Down
18 changes: 11 additions & 7 deletions plugin/core/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@


try:
from typing import Any, List, Dict, Tuple, Callable, Optional, Set
assert Any and List and Dict and Tuple and Callable and Optional and Set
from .types import WindowLike
from typing import Any, List, Dict, Tuple, Callable, Optional, Set, Iterable
from .workspace import Workspace
assert Any and List and Dict and Tuple and Callable and Optional and Set and Iterable
assert Session
assert WindowLike
assert Workspace
except ImportError:
pass


def get_window_env(window: sublime.Window, config: ClientConfig) -> 'Tuple[List[str], Dict[str, str]]':
def get_window_env(window: 'WindowLike', config: ClientConfig) -> 'Tuple[List[str], Dict[str, str]]':

# Create a dictionary of Sublime Text variables
variables = window.extract_variables()
Expand All @@ -36,22 +40,22 @@ def get_window_env(window: sublime.Window, config: ClientConfig) -> 'Tuple[List[
return expanded_args, env


def start_window_config(window: sublime.Window,
project_path: str,
def start_window_config(window: 'WindowLike',
workspaces: 'Optional[Iterable[Workspace]]',
config: ClientConfig,
on_pre_initialize: 'Callable[[Session], None]',
on_post_initialize: 'Callable[[Session], None]',
on_post_exit: 'Callable[[str], None]') -> 'Optional[Session]':
args, env = get_window_env(window, config)
config.binary_args = args
return create_session(config=config,
project_path=project_path,
workspaces=workspaces,
env=env,
settings=settings,
on_pre_initialize=on_pre_initialize,
on_post_initialize=on_post_initialize,
on_post_exit=lambda config_name: on_session_ended(window, config_name, on_post_exit))


def on_session_ended(window: sublime.Window, config_name: str, on_post_exit_handler: 'Callable[[str], None]') -> None:
def on_session_ended(window: 'WindowLike', config_name: str, on_post_exit_handler: 'Callable[[str], None]') -> None:
on_post_exit_handler(config_name)
8 changes: 4 additions & 4 deletions plugin/core/configurations.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
pass


def get_scope_client_config(view: 'sublime.View', configs: 'List[ClientConfig]',
def get_scope_client_config(view: 'ViewLike', configs: 'List[ClientConfig]',
point: 'Optional[int]' = None) -> 'Optional[ClientConfig]':
# When there are multiple server configurations, all of which are for
# similar scopes (e.g. 'source.json', 'source.json.sublime.settings') the
Expand Down Expand Up @@ -50,15 +50,15 @@ def get_scope_client_config(view: 'sublime.View', configs: 'List[ClientConfig]',
return scope_client_config


def get_global_client_config(view: 'sublime.View', global_configs: 'List[ClientConfig]') -> 'Optional[ClientConfig]':
def get_global_client_config(view: 'ViewLike', global_configs: 'List[ClientConfig]') -> 'Optional[ClientConfig]':
return get_scope_client_config(view, global_configs)


def create_window_configs(window: 'sublime.Window', global_configs: 'List[ClientConfig]') -> 'List[ClientConfig]':
def create_window_configs(window: 'WindowLike', global_configs: 'List[ClientConfig]') -> 'List[ClientConfig]':
return list(map(lambda c: apply_window_settings(c, window), global_configs))


def apply_window_settings(client_config: 'ClientConfig', window: 'sublime.Window') -> 'ClientConfig':
def apply_window_settings(client_config: 'ClientConfig', window: 'WindowLike') -> 'ClientConfig':
window_config = get_project_config(window)

if client_config.name in window_config:
Expand Down
4 changes: 3 additions & 1 deletion plugin/core/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@

try:
from .types import ViewLike
from typing import Any, List, Dict, Tuple, Callable, Optional, Set
assert Any and List and Dict and Tuple and Callable and Optional and Set
assert ViewLike
except ImportError:
pass

Expand Down Expand Up @@ -46,7 +48,7 @@ def start_active_window():
windows.lookup(window).start_active_views()


def on_view_activated(view: sublime.View):
def on_view_activated(view: 'ViewLike'):
window = view.window()
if window:
windows.lookup(window).activate_view(view)
14 changes: 8 additions & 6 deletions plugin/core/panels.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

try:
from typing import Optional
from .types import WindowLike, ViewLike
assert Optional
assert WindowLike and ViewLike
except ImportError:
pass

Expand All @@ -25,21 +27,21 @@
}


def create_output_panel(window: sublime.Window, name: str) -> 'Optional[sublime.View]':
def create_output_panel(window: 'WindowLike', name: str) -> 'Optional[ViewLike]':
panel = window.create_output_panel(name)
settings = panel.settings()
for key, value in OUTPUT_PANEL_SETTINGS.items():
settings.set(key, value)
return panel


def destroy_output_panels(window: sublime.Window) -> None:
def destroy_output_panels(window: 'WindowLike') -> None:
for panel_name in ["references", "diagnostics"]:
window.destroy_output_panel(panel_name)


def create_panel(window: sublime.Window, name: str, result_file_regex: str, result_line_regex: str,
syntax: str) -> 'Optional[sublime.View]':
def create_panel(window: 'WindowLike', name: str, result_file_regex: str, result_line_regex: str,
syntax: str) -> 'Optional[ViewLike]':
panel = create_output_panel(window, name)
if not panel:
return None
Expand All @@ -53,8 +55,8 @@ def create_panel(window: sublime.Window, name: str, result_file_regex: str, resu
return panel


def ensure_panel(window: sublime.Window, name: str, result_file_regex: str, result_line_regex: str,
syntax: str) -> 'Optional[sublime.View]':
def ensure_panel(window: 'WindowLike', name: str, result_file_regex: str, result_line_regex: str,
syntax: str) -> 'Optional[ViewLike]':
return window.find_output_panel(name) or create_panel(window, name, result_file_regex, result_line_regex, syntax)


Expand Down
44 changes: 31 additions & 13 deletions plugin/core/process.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from .logging import debug, exception_log, server_log
import subprocess
from .types import ClientConfig
import os
import shutil
import subprocess
import threading

try:
Expand Down Expand Up @@ -32,26 +33,43 @@ def add_extension_if_missing(server_binary_args: 'List[str]') -> 'List[str]':
return server_binary_args


def start_server(server_binary_args: 'List[str]', working_dir: str,
env: 'Dict[str,str]', attach_stderr: bool) -> 'Optional[subprocess.Popen]':
si = None
def get_server_working_directory_and_ensure_existence(config: ClientConfig) -> str:
tempdir = ''
try:
import sublime
tempdir = sublime.cache_path()
except ImportError:
import tempfile
tempdir = tempfile.gettempdir()
# TODO: from .settings import PLUGIN_NAME? Cannot do that because it imports the sublime module.
tempdir = os.path.join(tempdir, 'LSP', config.name)
os.makedirs(tempdir, exist_ok=True)
return tempdir


def start_server(
config: ClientConfig,
env: 'Dict[str,str]',
attach_stderr: bool
) -> 'Optional[subprocess.Popen]':
startupinfo = None
if os.name == "nt":
server_binary_args = add_extension_if_missing(server_binary_args)
si = subprocess.STARTUPINFO() # type: ignore
si.dwFlags |= subprocess.SW_HIDE | subprocess.STARTF_USESHOWWINDOW # type: ignore

debug("starting " + str(server_binary_args))

server_binary_args = add_extension_if_missing(config.binary_args)
startupinfo = subprocess.STARTUPINFO() # type: ignore
startupinfo.dwFlags |= subprocess.SW_HIDE | subprocess.STARTF_USESHOWWINDOW # type: ignore
else:
server_binary_args = config.binary_args
cwd = get_server_working_directory_and_ensure_existence(config)
debug("starting", str(server_binary_args), "in", cwd)
stderr_destination = subprocess.PIPE if attach_stderr else subprocess.DEVNULL

return subprocess.Popen(
server_binary_args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=stderr_destination,
cwd=working_dir,
cwd=cwd,
env=env,
startupinfo=si)
startupinfo=startupinfo)


def attach_logger(process: 'subprocess.Popen', stream) -> None:
Expand Down
6 changes: 3 additions & 3 deletions plugin/core/protocol.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
try:
from typing import Any, List, Dict, Tuple, Callable, Optional
assert Any and List and Dict and Tuple and Callable and Optional
from typing import Any, List, Dict, Tuple, Callable, Optional, Union
assert Any and List and Dict and Tuple and Callable and Optional and Union
except ImportError:
pass

Expand Down Expand Up @@ -184,7 +184,7 @@ def to_payload(self, id) -> 'Dict[str, Any]':


class Response:
def __init__(self, request_id: int, result: 'Optional[Dict[str, Any]]') -> None:
def __init__(self, request_id: int, result: 'Union[None, Dict[str, Any], List[Dict[str, Any]]]') -> None:
self.request_id = request_id
self.result = result
self.jsonrpc = "2.0"
Expand Down
42 changes: 24 additions & 18 deletions plugin/core/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
from .transports import start_tcp_transport
from .rpc import Client, attach_stdio_client
from .process import start_server
from .url import filename_to_uri
from .logging import debug
import os
from .protocol import completion_item_kinds, symbol_kinds
try:
from typing import Callable, Dict, Any, Optional
assert Callable and Dict and Any and Optional
from .workspace import Workspace
from typing import Callable, Dict, Any, Optional, Iterable, List
assert Callable and Dict and Any and Optional and Iterable and List
assert Workspace
except ImportError:
pass


def create_session(config: ClientConfig,
project_path: str,
workspaces: 'Optional[Iterable[Workspace]]',
env: dict,
settings: Settings,
on_pre_initialize: 'Optional[Callable[[Session], None]]' = None,
Expand All @@ -26,15 +27,15 @@ def create_session(config: ClientConfig,
def with_client(client) -> 'Session':
return Session(
config=config,
project_path=project_path,
workspaces=workspaces,
client=client,
on_pre_initialize=on_pre_initialize,
on_post_initialize=on_post_initialize,
on_post_exit=on_post_exit)

session = None
if config.binary_args:
process = start_server(config.binary_args, project_path, env, settings.log_stderr)
process = start_server(config, env, settings.log_stderr)
if process:
if config.tcp_port:
transport = start_tcp_transport(config.tcp_port, config.tcp_host)
Expand All @@ -59,11 +60,18 @@ def with_client(client) -> 'Session':
return session


def get_initialize_params(project_path: str, config: ClientConfig):
def get_initialize_params(workspaces: 'Optional[Iterable[Workspace]]', config: ClientConfig):
root_uri = None
lsp_workspaces = None
if workspaces is not None:
root_uri = next(iter(workspaces)).uri
lsp_workspaces = [workspace.to_dict() for workspace in workspaces]
initializeParams = {
"processId": os.getpid(),
"rootUri": filename_to_uri(project_path),
"rootPath": project_path,
# REMARK: A language server should forget about the rootUri and migrate to using workspaces instead:
# https://github.com/Microsoft/vscode/wiki/Adopting-Multi-Root-Workspace-APIs#language-client--language-server
"rootUri": root_uri,
"workspaceFolders": lsp_workspaces,
"capabilities": {
"textDocument": {
"synchronization": {
Expand Down Expand Up @@ -118,7 +126,8 @@ def get_initialize_params(project_path: str, config: ClientConfig):
"symbolKind": {
"valueSet": symbol_kinds
}
}
},
"workspaceFolders": True
}
}
}
Expand All @@ -131,33 +140,30 @@ def get_initialize_params(project_path: str, config: ClientConfig):
class Session(object):
def __init__(self,
config: ClientConfig,
project_path: str,
workspaces: 'Optional[Iterable[Workspace]]',
client: Client,
on_pre_initialize: 'Optional[Callable[[Session], None]]' = None,
on_post_initialize: 'Optional[Callable[[Session], None]]' = None,
on_post_exit: 'Optional[Callable[[str], None]]' = None) -> None:
self.config = config
self.project_path = project_path
self.state = ClientStates.STARTING
self._on_post_initialize = on_post_initialize
self._on_post_exit = on_post_exit
self.capabilities = dict() # type: Dict[str, Any]
self.client = client
if on_pre_initialize:
on_pre_initialize(self)
self.initialize()
self.initialize(workspaces)

def has_capability(self, capability):
return capability in self.capabilities and self.capabilities[capability] is not False

def get_capability(self, capability):
return self.capabilities.get(capability)

def initialize(self):
params = get_initialize_params(self.project_path, self.config)
self.client.send_request(
Request.initialize(params),
lambda result: self._handle_initialize_result(result))
def initialize(self, workspaces: 'Optional[Iterable[Workspace]]') -> None:
params = get_initialize_params(workspaces, self.config)
self.client.send_request(Request.initialize(params), self._handle_initialize_result)

def _handle_initialize_result(self, result):
self.state = ClientStates.READY
Expand Down

0 comments on commit 1aff2d2

Please sign in to comment.