Skip to content

Commit

Permalink
Merge 652d559 into 1a2af66
Browse files Browse the repository at this point in the history
  • Loading branch information
tomv564 committed Oct 18, 2019
2 parents 1a2af66 + 652d559 commit b3a703b
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 228 deletions.
3 changes: 0 additions & 3 deletions LSP.sublime-settings
Expand Up @@ -15,9 +15,6 @@
// hint: 4
"auto_show_diagnostics_panel_level": 3,

// Show in-line diagnostics using phantoms for unchanged files.
"show_diagnostics_phantoms": false,

// Show errors and warnings count in the status bar
"show_diagnostics_count_in_view_status": false,

Expand Down
1 change: 0 additions & 1 deletion docs/features.md
Expand Up @@ -79,7 +79,6 @@ Add these settings to your Sublime settings, Syntax-specific settings and/or in
* `show_status_messages` `true` *show messages in the status bar for a few seconds*
* `show_view_status` `true` *show permanent language server status in the status bar*
* `auto_show_diagnostics_panel` `true` *open the diagnostics panel automatically if there are diagnostics*
* `show_diagnostics_phantoms` `false` *show diagnostics as phantoms while the file has no changes*
* `show_diagnostics_count_in_view_status` `false` *show errors and warnings count in the status bar*
* `show_diagnostics_in_view_status` `true` *when on a diagnostic with the cursor, show the text in the status bar*
* `diagnostics_highlight_style` `"underline"` *highlight style of code diagnostics, `"underline"` or `"box"`*
Expand Down
35 changes: 17 additions & 18 deletions plugin/core/diagnostics.py
Expand Up @@ -5,33 +5,30 @@

try:
import sublime
from typing_extensions import Protocol
from typing import Any, List, Dict, Tuple, Callable, Optional
assert sublime
assert Any and List and Dict and Tuple and Callable and Optional
except ImportError:
pass
Protocol = object # type: ignore


class DiagnosticsUpdate(object):
def __init__(self, window: 'Any', client_name: str,
file_path: str) -> 'None':
self.window = window
self.client_name = client_name
self.file_path = file_path
class DiagnosticsUpdateable(Protocol):

def update(self, file_name: str, config_name: str) -> None:
...

class WindowDiagnostics(object):

def __init__(self) -> None:
class DiagnosticsStorage(object):

def __init__(self, updateable: 'Optional[DiagnosticsUpdateable]') -> None:
self._diagnostics = {} # type: Dict[str, Dict[str, List[Diagnostic]]]
self._on_updated = None # type: Optional[Callable]
self._updatable = updateable

def get(self) -> 'Dict[str, Dict[str, List[Diagnostic]]]':
return self._diagnostics

def set_on_updated(self, update_handler: 'Callable') -> None:
self._on_updated = update_handler

def get_by_path(self, file_path: str) -> 'List[Diagnostic]':
view_diagnostics = []
if file_path in self._diagnostics:
Expand All @@ -57,11 +54,10 @@ def update(self, file_path: str, client_name: str, diagnostics: 'List[Diagnostic
def clear(self) -> None:
for file_path in list(self._diagnostics):
for client_name in list(self._diagnostics[file_path]):
self.update(file_path, client_name, [])
if self._on_updated:
self._on_updated(file_path, client_name)
if self.update(file_path, client_name, []):
self.notify(file_path, client_name)

def handle_client_diagnostics(self, client_name: str, update: dict) -> None:
def receive(self, client_name: str, update: dict) -> None:
maybe_file_uri = update.get('uri')
if maybe_file_uri is not None:
file_path = uri_to_filename(maybe_file_uri)
Expand All @@ -70,10 +66,13 @@ def handle_client_diagnostics(self, client_name: str, update: dict) -> None:
Diagnostic.from_lsp(item) for item in update.get('diagnostics', []))

if self.update(file_path, client_name, diagnostics):
if self._on_updated:
self._on_updated(file_path, client_name)
self.notify(file_path, client_name)
else:
debug('missing uri in diagnostics update')

def notify(self, file_path: str, client_name: str) -> None:
if self._updatable:
self._updatable.update(file_path, client_name)

def remove(self, file_path: str, client_name: str) -> None:
self.update(file_path, client_name, [])
2 changes: 2 additions & 0 deletions plugin/core/main.py
Expand Up @@ -16,12 +16,14 @@
from .events import global_events
from .registry import windows, load_handlers, unload_sessions
from .panels import destroy_output_panels
from ..diagnostics import DiagnosticsPresenter


def startup() -> None:
load_settings()
set_debug_logging(settings.log_debug)
set_server_logging(settings.log_server)
windows.set_diagnostics_ui(DiagnosticsPresenter)
load_handlers()
global_events.subscribe("view.on_load_async", on_view_activated)
global_events.subscribe("view.on_activated_async", on_view_activated)
Expand Down
1 change: 0 additions & 1 deletion plugin/core/settings.py
Expand Up @@ -55,7 +55,6 @@ def update_settings(settings: Settings, settings_obj: sublime.Settings) -> None:
settings.show_view_status = read_bool_setting(settings_obj, "show_view_status", True)
settings.auto_show_diagnostics_panel = read_bool_setting(settings_obj, "auto_show_diagnostics_panel", True)
settings.auto_show_diagnostics_panel_level = read_int_setting(settings_obj, "auto_show_diagnostics_panel_level", 3)
settings.show_diagnostics_phantoms = read_bool_setting(settings_obj, "show_diagnostics_phantoms", False)
settings.show_diagnostics_count_in_view_status = read_bool_setting(settings_obj,
"show_diagnostics_count_in_view_status", False)
settings.show_diagnostics_in_view_status = read_bool_setting(settings_obj, "show_diagnostics_in_view_status", True)
Expand Down
14 changes: 7 additions & 7 deletions plugin/core/test_diagnostics.py
@@ -1,21 +1,21 @@
import unittest
from .diagnostics import WindowDiagnostics
from .diagnostics import DiagnosticsStorage
from .protocol import Diagnostic, Range, Point
# from .configurations import WindowConfigManager, _merge_dicts, ConfigManager, is_supported_syntax
# from .test_session import test_config, test_language
from .test_protocol import LSP_MINIMAL_DIAGNOSTIC


class WindowDiagnosticsTest(unittest.TestCase):
class DiagnosticsStorageTest(unittest.TestCase):

def test_empty_diagnostics(self):
wd = WindowDiagnostics()
wd = DiagnosticsStorage(None)
self.assertEqual(wd.get_by_path(__file__), [])

# todo: remove

def test_updated_diagnostics(self):
wd = WindowDiagnostics()
wd = DiagnosticsStorage(None)

test_file_path = "test.py"
diag = Diagnostic('message', Range(Point(0, 0), Point(1, 1)), 1, None, dict())
Expand All @@ -30,21 +30,21 @@ def test_updated_diagnostics(self):
self.assertEqual(len(view_diags), 0)

def test_handle_diagnostics_update(self):
wd = WindowDiagnostics()
wd = DiagnosticsStorage(None)

test_file_path = "/test.py"
update = {
'uri': 'file:///test.py',
'diagnostics': [LSP_MINIMAL_DIAGNOSTIC]
}

wd.handle_client_diagnostics("test_server", update)
wd.receive("test_server", update)

view_diags = wd.get_by_path(test_file_path)
self.assertEqual(len(view_diags), 1)

def test_remove_diagnostics(self):
wd = WindowDiagnostics()
wd = DiagnosticsStorage(None)

test_file_path = "test.py"
diag = Diagnostic('message', Range(Point(0, 0), Point(1, 1)), 1, None, dict())
Expand Down
16 changes: 8 additions & 8 deletions plugin/core/test_windows.py
@@ -1,5 +1,5 @@
from .windows import WindowManager, WindowRegistry, ViewLike
from .diagnostics import WindowDiagnostics
from .diagnostics import DiagnosticsStorage
from .sessions import create_session, Session
from .test_session import MockClient, test_config, test_language
from .test_rpc import MockSettings
Expand Down Expand Up @@ -274,7 +274,7 @@ class WindowManagerTests(unittest.TestCase):
def test_can_start_active_views(self):
docs = MockDocuments()
wm = WindowManager(MockWindow([[MockView(__file__)]]), MockConfigs(), docs,
WindowDiagnostics(), mock_start_session, test_sublime, MockHandlerDispatcher())
DiagnosticsStorage(None), mock_start_session, test_sublime, MockHandlerDispatcher())
wm.start_active_views()

# session must be started (todo: verify session is ready)
Expand All @@ -284,7 +284,7 @@ def test_can_start_active_views(self):
def test_can_open_supported_view(self):
docs = MockDocuments()
window = MockWindow([[]])
wm = WindowManager(window, MockConfigs(), docs, WindowDiagnostics(), mock_start_session, test_sublime,
wm = WindowManager(window, MockConfigs(), docs, DiagnosticsStorage(None), mock_start_session, test_sublime,
MockHandlerDispatcher())

wm.start_active_views()
Expand All @@ -301,7 +301,7 @@ def test_can_open_supported_view(self):
def test_can_restart_sessions(self):
docs = MockDocuments()
wm = WindowManager(MockWindow([[MockView(__file__)]]), MockConfigs(), docs,
WindowDiagnostics(), mock_start_session, test_sublime, MockHandlerDispatcher())
DiagnosticsStorage(None), mock_start_session, test_sublime, MockHandlerDispatcher())
wm.start_active_views()

# session must be started (todo: verify session is ready)
Expand All @@ -323,7 +323,7 @@ def test_ends_sessions_when_closed(self):
docs = MockDocuments()
test_window = MockWindow([[MockView(__file__)]])
wm = WindowManager(test_window, MockConfigs(), docs,
WindowDiagnostics(), mock_start_session, test_sublime, MockHandlerDispatcher())
DiagnosticsStorage(None), mock_start_session, test_sublime, MockHandlerDispatcher())
wm.start_active_views()

# session must be started (todo: verify session is ready)
Expand All @@ -344,7 +344,7 @@ def test_ends_sessions_when_quick_switching(self):
docs = MockDocuments()
test_window = MockWindow([[MockView(__file__)]])
wm = WindowManager(test_window, MockConfigs(), docs,
WindowDiagnostics(), mock_start_session, test_sublime, MockHandlerDispatcher())
DiagnosticsStorage(None), mock_start_session, test_sublime, MockHandlerDispatcher())
wm.start_active_views()

# session must be started (todo: verify session is ready)
Expand All @@ -370,7 +370,7 @@ def test_ends_sessions_when_quick_switching(self):
def test_offers_restart_on_crash(self):
docs = MockDocuments()
wm = WindowManager(MockWindow([[MockView(__file__)]]), MockConfigs(), docs,
WindowDiagnostics(), mock_start_session, test_sublime,
DiagnosticsStorage(None), mock_start_session, test_sublime,
MockHandlerDispatcher())
wm.start_active_views()

Expand All @@ -392,7 +392,7 @@ def test_invokes_language_handler(self):
docs = MockDocuments()
dispatcher = MockHandlerDispatcher()
wm = WindowManager(MockWindow([[MockView(__file__)]]), MockConfigs(), docs,
WindowDiagnostics(), mock_start_session, test_sublime,
DiagnosticsStorage(None), mock_start_session, test_sublime,
dispatcher)
wm.start_active_views()

Expand Down
1 change: 0 additions & 1 deletion plugin/core/types.py
Expand Up @@ -14,7 +14,6 @@ def __init__(self) -> None:
self.show_view_status = True
self.auto_show_diagnostics_panel = True
self.auto_show_diagnostics_panel_level = 3
self.show_diagnostics_phantoms = False
self.show_diagnostics_count_in_view_status = False
self.show_diagnostics_in_view_status = True
self.show_diagnostics_severity_level = 3
Expand Down
29 changes: 20 additions & 9 deletions plugin/core/windows.py
@@ -1,4 +1,4 @@
from .diagnostics import WindowDiagnostics, DiagnosticsUpdate
from .diagnostics import DiagnosticsStorage
from .events import global_events
from .logging import debug, server_log
from .types import (ClientStates, ClientConfig, WindowLike, ViewLike,
Expand Down Expand Up @@ -88,6 +88,10 @@ def for_window(self, window: 'WindowLike', configs: 'ConfigRegistry') -> Documen
return WindowDocumentHandler(self._sublime, self._settings, window, global_events, configs)


def nop() -> None:
pass


class WindowDocumentHandler(object):
def __init__(self, sublime: 'Any', settings: Settings,
window: WindowLike, events: Events,
Expand All @@ -99,6 +103,8 @@ def __init__(self, sublime: 'Any', settings: Settings,
self._document_states = dict() # type: Dict[str, DocumentState]
self._pending_buffer_changes = dict() # type: Dict[int, Dict]
self._sessions = dict() # type: Dict[str, Session]
self.changed = nop
self.saved = nop
events.subscribe('view.on_load_async', self.handle_view_opened)
events.subscribe('view.on_activated_async', self.handle_view_opened)
events.subscribe('view.on_modified', self.handle_view_modified)
Expand Down Expand Up @@ -240,6 +246,7 @@ def handle_view_saved(self, view: ViewLike) -> None:
if session.client:
params = {"textDocument": {"uri": filename_to_uri(file_name)}}
session.client.send_notification(Notification.didSave(params))
self.saved()
else:
debug('document not tracked', file_name)

Expand Down Expand Up @@ -273,6 +280,7 @@ def purge_did_change(self, buffer_id: int, buffer_version: 'Optional[int]' = Non
if pending_buffer:
if buffer_version is None or buffer_version == pending_buffer["version"]:
self.notify_did_change(pending_buffer["view"])
self.changed()

def notify_did_change(self, view: ViewLike) -> None:
file_name = view.file_name()
Expand Down Expand Up @@ -302,7 +310,7 @@ def notify_did_change(self, view: ViewLike) -> None:

class WindowManager(object):
def __init__(self, window: WindowLike, configs: ConfigRegistry, documents: DocumentHandler,
diagnostics: WindowDiagnostics, session_starter: 'Callable', sublime: 'Any',
diagnostics: DiagnosticsStorage, session_starter: 'Callable', sublime: 'Any',
handler_dispatcher: LanguageHandlerListener, on_closed: 'Optional[Callable]' = None) -> None:

# to move here:
Expand All @@ -318,10 +326,6 @@ def __init__(self, window: WindowLike, configs: ConfigRegistry, documents: Docum
self._restarting = False
self._project_path = get_project_path(self._window)
self._projectless_root_path = None # type: Optional[str]
self._diagnostics.set_on_updated(
lambda file_path, client_name:
global_events.publish("document.diagnostics",
DiagnosticsUpdate(self._window, client_name, file_path)))
self._on_closed = on_closed
self._is_closing = False
self._initialization_lock = threading.Lock()
Expand Down Expand Up @@ -504,7 +508,7 @@ def _handle_post_initialize(self, session: 'Session') -> None:

client.on_notification(
"textDocument/publishDiagnostics",
lambda params: self._diagnostics.handle_client_diagnostics(session.config.name, params))
lambda params: self._diagnostics.receive(session.config.name, params))

self._handlers.on_initialized(session.config.name, self._window, client)

Expand Down Expand Up @@ -584,14 +588,21 @@ def __init__(self, configs: GlobalConfigs, documents: 'Any',
self._session_starter = session_starter
self._sublime = sublime
self._handler_dispatcher = handler_dispatcher
self._diagnostics_ui_class = None # type: Optional[Callable]

def set_diagnostics_ui(self, ui_class: 'Any') -> None:
self._diagnostics_ui_class = ui_class

def lookup(self, window: 'Any') -> WindowManager:
state = self._windows.get(window.id())
if state is None:
window_configs = self._configs.for_window(window)
window_documents = self._documents.for_window(window, window_configs)
state = WindowManager(window, window_configs, window_documents, WindowDiagnostics(), self._session_starter,
self._sublime, self._handler_dispatcher, lambda: self._on_closed(window))
diagnostics_ui = self._diagnostics_ui_class(window,
window_documents) if self._diagnostics_ui_class else None
state = WindowManager(window, window_configs, window_documents, DiagnosticsStorage(diagnostics_ui),
self._session_starter, self._sublime,
self._handler_dispatcher, lambda: self._on_closed(window))
self._windows[window.id()] = state
return state

Expand Down

0 comments on commit b3a703b

Please sign in to comment.