Skip to content

Commit

Permalink
Merge e7660f8 into 7bb8de0
Browse files Browse the repository at this point in the history
  • Loading branch information
tomv564 committed Sep 9, 2019
2 parents 7bb8de0 + e7660f8 commit b84c4a1
Show file tree
Hide file tree
Showing 20 changed files with 152 additions and 151 deletions.
10 changes: 5 additions & 5 deletions plugin/code_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
except ImportError:
pass

from .core.registry import client_for_view, LspTextCommand
from .core.registry import LspTextCommand
from .core.protocol import Request
from .diagnostics import get_point_diagnostics
from .core.edit import parse_workspace_edit
Expand All @@ -17,9 +17,9 @@
from .core.settings import settings


def send_code_action_request(view, on_response_recieved: 'Callable'):
session = session_for_view(view)
if not session or not session.has_capability('codeActionProvider'):
def send_code_action_request(view, on_response_recieved: 'Callable') -> None:
session = session_for_view(view, 'codeActionProvider')
if not session:
# the server doesn't support code actions, just return
return

Expand Down Expand Up @@ -129,7 +129,7 @@ def handle_select(self, index: int) -> None:
self.run_command(maybe_command)

def run_command(self, command) -> None:
client = client_for_view(self.view)
client = self.client_with_capability('codeActionProvider')
if client:
client.send_request(
Request.executeCommand(command),
Expand Down
36 changes: 17 additions & 19 deletions plugin/color.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from .core.protocol import Request
from .core.url import filename_to_uri
from .core.registry import session_for_view, config_for_scope
from .core.registry import session_for_view, sessions_for_view, client_from_session, configs_for_scope
from .core.settings import settings, client_configs
from .core.views import range_to_region
from .core.protocol import Range
Expand Down Expand Up @@ -37,15 +37,15 @@ def on_activated_async(self):
self.initialize()

def initialize(self, is_retry=False):
config = config_for_scope(self.view)
if not config:
configs = configs_for_scope(self.view)
if not configs:
self.initialized = True # no server enabled, re-open file to activate feature.

session = session_for_view(self.view)
if session:
sessions = sessions_for_view(self.view)
if sessions:
self.initialized = True
self.enabled = session.has_capability('colorProvider')
if self.enabled:
if next((session for session in sessions if session.has_capability('colorProvider')), None):
self.enabled = True
self.send_color_request()
elif not is_retry:
# session may be starting, try again once in a second.
Expand Down Expand Up @@ -75,19 +75,17 @@ def send_color_request(self):
if is_transient_view(self.view):
return

session = session_for_view(self.view)
if not session:
return

params = {
"textDocument": {
"uri": filename_to_uri(self.view.file_name())
client = client_from_session(session_for_view(self.view, 'colorProvider'))
if client:
params = {
"textDocument": {
"uri": filename_to_uri(self.view.file_name())
}
}
}
session.client.send_request(
Request.documentColor(params),
self.handle_response
)
client.send_request(
Request.documentColor(params),
self.handle_response
)

def handle_response(self, response) -> None:
phantoms = []
Expand Down
25 changes: 11 additions & 14 deletions plugin/completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .core.settings import settings, client_configs
from .core.logging import debug
from .core.completion import parse_completion_response, format_completion
from .core.registry import session_for_view, client_for_view
from .core.registry import session_for_view, client_from_session
from .core.configurations import is_supported_syntax
from .core.documents import get_document_position, is_at_word
from .core.sessions import Session
Expand Down Expand Up @@ -72,20 +72,18 @@ def is_applicable(cls, view_settings):

def initialize(self):
self.initialized = True
session = session_for_view(self.view)
session = session_for_view(self.view, 'completionProvider')
if session:
completionProvider = session.get_capability(
'completionProvider')
completionProvider = session.get_capability('completionProvider')
# A language server may have an empty dict as CompletionOptions. In that case,
# no trigger characters will be registered but we'll still respond to Sublime's
# usual query for completions. So the explicit check for None is necessary.
if completionProvider is not None:
self.enabled = True
self.resolve = completionProvider.get('resolveProvider') or False
self.trigger_chars = completionProvider.get(
'triggerCharacters') or []
if self.trigger_chars:
self.register_trigger_chars(session)
self.enabled = True
self.resolve = completionProvider.get('resolveProvider') or False
self.trigger_chars = completionProvider.get(
'triggerCharacters') or []
if self.trigger_chars:
self.register_trigger_chars(session)

def _view_language(self, config_name: str) -> 'Optional[str]':
languages = self.view.settings().get('lsp_language')
Expand Down Expand Up @@ -243,14 +241,13 @@ def do_request(self, prefix: str, locations: 'List[int]'):
view = self.view

# don't store client so we can handle restarts
client = client_for_view(view)
client = client_from_session(session_for_view(view, 'completionProvider', locations[0]))
if not client:
return

if settings.complete_all_chars or self.is_after_trigger_character(locations[0]):
global_events.publish("view.on_purge_changes", self.view)
document_position = get_document_position(view, locations[0])
debug('getting completions, location', locations[0], 'prefix', prefix)
if document_position:
client.send_request(
Request.complete(document_position),
Expand All @@ -261,7 +258,7 @@ def do_request(self, prefix: str, locations: 'List[int]'):
def do_resolve(self, item) -> None:
view = self.view

client = client_for_view(view)
client = client_from_session(session_for_view(view, 'completionProvider', self.last_location))
if not client:
return

Expand Down
5 changes: 3 additions & 2 deletions plugin/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
create_window_configs,
get_global_client_config
)
from .core.registry import config_for_scope, windows
from .core.registry import configs_for_scope, windows
from .core.events import global_events
from .core.workspace import enable_in_project, disable_in_project

Expand All @@ -19,8 +19,9 @@
pass


# todo: delete this feature
def detect_supportable_view(view: sublime.View):
config = config_for_scope(view)
config = configs_for_scope(view)
if not config:
available_config = get_global_client_config(view, client_configs.all)
if available_config:
Expand Down
31 changes: 17 additions & 14 deletions plugin/core/configurations.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,46 @@

try:
import sublime
from typing import Any, List, Dict, Tuple, Callable, Optional
from typing import Any, List, Dict, Tuple, Callable, Optional, Iterator
assert sublime
assert Any and List and Dict and Tuple and Callable and Optional
assert Any and List and Dict and Tuple and Callable and Optional and Iterator
assert ViewLike and WindowLike and ConfigRegistry and LanguageConfig
except ImportError:
pass


def get_scope_client_config(view: 'sublime.View', configs: 'List[ClientConfig]',
point: 'Optional[int]' = None) -> 'Optional[ClientConfig]':
return next(get_scope_client_configs(view, configs, point), None)


def get_scope_client_configs(view: 'sublime.View', configs: 'List[ClientConfig]',
point: 'Optional[int]' = None) -> 'Iterator[ClientConfig]':
# When there are multiple server configurations, all of which are for
# similar scopes (e.g. 'source.json', 'source.json.sublime.settings') the
# configuration with the most specific scope (highest ranked selector)
# in the current position is preferred.
scope_score = 0
scope_client_config = None
if point is None:
sel = view.sel()
if len(sel) > 0:
point = sel[0].begin()

languages = view.settings().get('lsp_language', None)
scope_configs = [] # type: List[Tuple[ClientConfig, Optional[int]]]

for config in configs:
if config.enabled:
if languages is None or config.name in languages:
for language in config.languages:
for scope in language.scopes:
score = 0
if point is not None:
score = view.score_selector(point, scope)
# if score > 0:
# debug('scope match score', scope, config.name, score)
if score > scope_score:
scope_score = score
scope_client_config = config
# debug('chose ', scope_client_config.name if scope_client_config else None)
return scope_client_config
if score > 0:
scope_configs.append((config, score))
debug('scope {} score {}'.format(scope, score))

return map(lambda pair: pair[0], sorted(scope_configs, key=lambda config_score: config_score[1], reverse=True))


def get_global_client_config(view: 'sublime.View', global_configs: 'List[ClientConfig]') -> 'Optional[ClientConfig]':
Expand Down Expand Up @@ -122,10 +125,10 @@ def __init__(self, configs: 'List[ClientConfig]') -> None:
self.all = configs

def is_supported(self, view: 'Any') -> bool:
return self.scope_config(view) is not None
return any(self.scope_configs(view))

def scope_config(self, view: 'Any', point=None) -> 'Optional[ClientConfig]':
return get_scope_client_config(view, self.all, point)
def scope_configs(self, view: 'Any', point=None) -> 'Iterator[ClientConfig]':
return get_scope_client_configs(view, self.all, point)

def syntax_configs(self, view: 'Any') -> 'List[ClientConfig]':
syntax = view.settings().get("syntax")
Expand Down
81 changes: 38 additions & 43 deletions plugin/core/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
from .settings import settings, client_configs

try:
from typing import Optional, List, Callable, Dict, Any
assert Optional and List and Callable and Dict and Any and ClientConfig and Client and Session
from typing import Optional, List, Callable, Dict, Any, Iterable
assert Optional and List and Callable and Dict and Any and ClientConfig and Client and Session and Iterable
except ImportError:
pass

Expand Down Expand Up @@ -53,47 +53,42 @@ def register_language_handler(handler: LanguageHandler) -> None:
client_initialization_listeners[handler.name] = handler.on_initialized


def client_for_view(view: sublime.View) -> 'Optional[Client]':
return _client_for_view_and_window(view, view.window())


def session_for_view(view: sublime.View, point: 'Optional[int]' = None) -> 'Optional[Session]':
return _session_for_view_and_window(view, view.window(), point)


def _session_for_view_and_window(view: sublime.View, window: 'Optional[sublime.Window]',
point=None) -> 'Optional[Session]':
if not window:
debug("no window for view", view.file_name())
return None

config = config_for_scope(view, point)
if not config:
debug("no config found or enabled for view", view.file_name())
return None

session = windows.lookup(window).get_session(config.name)
if session:
if session.state == ClientStates.READY:
return session
return None


def _client_for_view_and_window(view: sublime.View, window: 'Optional[sublime.Window]') -> 'Optional[Client]':
session = _session_for_view_and_window(view, window)

def client_from_session(session: 'Optional[Session]') -> 'Optional[Client]':
if session:
if session.client:
return session.client
else:
debug(session.config.name, "in state", session.state, " for view",
view.file_name())
debug(session.config.name, "in state", session.state)
return None
else:
debug('no session found')
return None


def sessions_for_view(view: sublime.View, point: 'Optional[int]' = None) -> 'Iterable[Session]':
return _sessions_for_view_and_window(view, view.window(), point)


def session_for_view(view: sublime.View,
capability: str,
point: 'Optional[int]' = None) -> 'Optional[Session]':
return next((session for session in sessions_for_view(view, point)
if session.has_capability(capability)), None)


def _sessions_for_view_and_window(view: sublime.View, window: 'Optional[sublime.Window]',
point=None) -> 'Iterable[Session]':
if not window:
debug("no window for view", view.file_name())
return []

manager = windows.lookup(window)
scope_configs = manager._configs.scope_configs(view, point)
sessions = (manager.get_session(config.name) for config in scope_configs)
ready_sessions = (session for session in sessions if session and session.state == ClientStates.READY)
return ready_sessions


def unload_sessions(window):
wm = windows.lookup(window)
wm.end_sessions()
Expand All @@ -106,17 +101,17 @@ def unload_sessions(window):
windows = WindowRegistry(configs, documents, start_window_config, sublime, handlers_dispatcher)


def config_for_scope(view: 'Any', point=None) -> 'Optional[ClientConfig]':
def configs_for_scope(view: 'Any', point=None) -> 'Iterable[ClientConfig]':
window = view.window()
if window:
# todo: don't expose _configs
return windows.lookup(window)._configs.scope_config(view, point)
return None
return windows.lookup(window)._configs.scope_configs(view, point)
return []


def is_supported_view(view: sublime.View) -> bool:
# TODO: perhaps make this check for a client instead of a config
if config_for_scope(view):
if configs_for_scope(view):
return True
else:
return False
Expand All @@ -126,14 +121,14 @@ class LspTextCommand(sublime_plugin.TextCommand):
def __init__(self, view):
super().__init__(view)

def is_visible(self, event=None):
def is_visible(self, event=None) -> bool:
return is_supported_view(self.view)

def has_client_with_capability(self, capability):
session = session_for_view(self.view)
if session and session.has_capability(capability):
return True
return False
def has_client_with_capability(self, capability) -> bool:
return session_for_view(self.view, capability) is not None

def client_with_capability(self, capability) -> 'Optional[Client]':
return client_from_session(session_for_view(self.view, capability))


class LspRestartClientCommand(sublime_plugin.TextCommand):
Expand Down
2 changes: 1 addition & 1 deletion plugin/core/test_configurations.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def test_with_single_config(self):
view = MockView(__file__)
manager = WindowConfigManager([test_config])
self.assertTrue(manager.is_supported(view))
self.assertEqual(manager.scope_config(view), test_config)
self.assertEqual(list(manager.scope_configs(view)), [test_config])
self.assertTrue(manager.syntax_supported(view))
self.assertEqual(manager.syntax_configs(view), [test_config])
lang_configs = manager.syntax_config_languages(view)
Expand Down
Loading

0 comments on commit b84c4a1

Please sign in to comment.