Repository: sublimelinter/sublimelinter
Issue: "Busy" indicator freezes if you open a panel while its spinning

Our cheap "busy" spinner usually freezes if you open the command palette, or for example place the cursor in a panel. You have to activate the "original" view again to get rid of it.

- it stops spinning immediately
- it does not clear after the linter finished

Changed files:
busy_indicator_view.py

diff --git a/busy_indicator_view.py b/busy_indicator_view.py
index a79ecd0..27d16cd 100644
--- a/busy_indicator_view.py
+++ b/busy_indicator_view.py
@@ -3,7 +3,20 @@ import sublime_plugin
 
 import time
 
-from .lint import events
+from .lint import events, util
+
+
+MYPY = False
+if MYPY:
+    from typing import Dict, Optional
+    from mypy_extensions import TypedDict
+
+    Filename = str
+    LinterName = str
+    State_ = TypedDict('State_', {
+        'active_view': Optional[sublime.View],
+        'running': Dict[sublime.BufferId, float],
+    })
 
 
 INITIAL_DELAY = 2
@@ -14,13 +27,15 @@ STATUS_BUSY_KEY = "sublime_linter_status_busy"
 State = {
     'active_view': None,
     'running': {}
-}
+}  # type: State_
 
 
 def plugin_loaded():
-    State.update({
-        'active_view': sublime.active_window().active_view()
-    })
+    active_view = sublime.active_window().active_view()
+    if util.is_lintable(active_view):
+        State.update({
+            'active_view': active_view
+        })
 
 
 def plugin_unloaded():
@@ -33,29 +48,38 @@ def plugin_unloaded():
 
 @events.on(events.LINT_START)
 def on_begin_linting(buffer_id):
+    # type: (sublime.BufferId) -> None
     State['running'][buffer_id] = time.time()
 
     active_view = State['active_view']
     if active_view and active_view.buffer_id() == buffer_id:
-        sublime.set_timeout_async(lambda: draw(**State), INITIAL_DELAY * 1000)
+        sublime.set_timeout_async(
+            lambda: draw(active_view),  # type: ignore  # mypy bug
+            INITIAL_DELAY * 1000
+        )
 
 
 @events.on(events.LINT_END)
 def on_finished_linting(buffer_id, **kwargs):
+    # type: (sublime.BufferId, object) -> None
     State['running'].pop(buffer_id, None)
 
     active_view = State['active_view']
     if active_view and active_view.buffer_id() == buffer_id:
-        draw(**State)
+        draw(active_view)
 
 
 class UpdateState(sublime_plugin.EventListener):
-    def on_activated_async(self, active_view):
+    def on_activated(self, active_view):
+        # type: (sublime.View) -> None
+        if not util.is_lintable(active_view):
+            return
+
         State.update({
             'active_view': active_view
         })
 
-        draw(**State)
+        draw(active_view)
 
 
 indicators = [
@@ -67,14 +91,15 @@ indicators = [
 ]
 
 
-def draw(active_view, running, **kwargs):
-    buffer_id = active_view.buffer_id()
-    start_time = running.get(buffer_id, None)
+def draw(view):
+    # type: (sublime.View) -> None
+    buffer_id = view.buffer_id()
+    start_time = State['running'].get(buffer_id, None)
     now = time.time()
     if start_time and (INITIAL_DELAY <= (now - start_time) < TIMEOUT):
         num = len(indicators)
         text = indicators[int((now - start_time) * 1000 / CYCLE_TIME) % num]
-        active_view.set_status(STATUS_BUSY_KEY, text)
-        sublime.set_timeout_async(lambda: draw(**State), CYCLE_TIME)
+        view.set_status(STATUS_BUSY_KEY, text)
+        sublime.set_timeout_async(lambda: draw(view), CYCLE_TIME)
     else:
-        active_view.erase_status(STATUS_BUSY_KEY)
+        view.erase_status(STATUS_BUSY_KEY)