Repository: sublimelinter/sublimelinter
Issue: [beta] AttributeError: 'module' object has no attribute 'update_panel_selection'

I am repeatedly getting this error in the sublime text console:

`Traceback (most recent call last):
  File "/Applications/Sublime Text.app/Contents/MacOS/sublime_plugin.py", line 538, in on_activated_async
    callback.on_activated_async(v)
  File "status_bar_view in /Users/nickmuoh/Library/Application Support/Sublime Text 3/Installed Packages/SublimeLinter.sublime-package", line 50, in on_activated_async
AttributeError: 'module' object has no attribute 'update_panel_selection'`

I am using sublimelinter v4.0.0-rc.9.9. 

Has anyone else observed this??

Changed files:
commands.py, sublime_linter.py, panel/panel.py, status_bar_view.py

diff --git a/commands.py b/commands.py
index 12e42c6..f3673b5 100644
--- a/commands.py
+++ b/commands.py
@@ -1,8 +1,6 @@
-import sublime
 import sublime_plugin
 
 from .lint import persist
-from .panel.panel import fill_panel, PANEL_NAME
 
 
 class SublimeLinterLintCommand(sublime_plugin.TextCommand):
@@ -42,50 +40,3 @@ class SublimeLinterLineReportCommand(sublime_plugin.WindowCommand):
     def run(self):
         from .sublime_linter import SublimeLinter
         SublimeLinter.shared_plugin().open_tooltip()
-
-
-class SublimeLinterPanelToggleCommand(sublime_plugin.WindowCommand):
-    def run(self, force_show=False, **kwargs):
-        active_panel = self.window.active_panel()
-        is_active_panel = (active_panel == "output." + PANEL_NAME)
-
-        if is_active_panel and not force_show:
-            self.show_panel(PANEL_NAME, show=False)
-        else:
-            fill_panel(self.window, **kwargs)
-            self.show_panel(PANEL_NAME)
-
-    def show_panel(self, name, show=True):
-        """
-        Change visibility of panel with given name.
-
-        Panel will be shown by default.
-        Pass show=False for hiding.
-        """
-        if show:
-            cmd = "show_panel"
-        else:
-            cmd = "hide_panel"
-
-        self.window.run_command(cmd, {"panel": "output." + name or ""})
-
-
-class SublimeLinterUpdatePanelCommand(sublime_plugin.TextCommand):
-    def run(self, edit, text="", clear_sel=False):
-        """Replace a view's text entirely and attempt to restore previous selection."""
-        sel = self.view.sel()
-        # Doesn't make sense to consider multiple selections
-        selected_region = sel[0] if sel else None
-        selected_text = self.view.substr(selected_region) if sel else None
-
-        self.view.set_read_only(False)
-        self.view.replace(edit, sublime.Region(0, self.view.size()), text)
-        self.view.set_read_only(True)
-
-        sel.clear()
-        if selected_text and not clear_sel:
-            new_selected_region = self.view.find(selected_text, 0, flags=sublime.LITERAL)
-            if new_selected_region:
-                sel.add(new_selected_region)
-                return
-        sel.add(0)
diff --git a/panel/panel.py b/panel_view.py
similarity index 71%
rename from panel/panel.py
rename to panel_view.py
index 7120102..b1735b0 100644
--- a/panel/panel.py
+++ b/panel_view.py
@@ -1,7 +1,8 @@
 import os
 import sublime
+import sublime_plugin
 
-from ..lint import persist
+from .lint import persist, events, util
 
 PANEL_NAME = "SublimeLinter"
 OUTPUT_PANEL_SETTINGS = {
@@ -21,6 +22,124 @@ OUTPUT_PANEL_SETTINGS = {
 }
 
 
+State = {
+    'we_count': {},
+    'active_view': None,
+    'current_pos': (-1, -1)
+}
+
+
+def plugin_loaded():
+    State.update({
+        'active_view': sublime.active_window().active_view()
+    })
+
+
+def plugin_unloaded():
+    events.off(on_finished_linting)
+
+
+@events.on(events.FINISHED_LINTING)
+def on_finished_linting(buffer_id):
+    active_view = State['active_view']
+    if active_view.buffer_id() == buffer_id:
+        State.update({
+            'we_count': get_we_count(active_view.id())
+        })
+
+    for window in sublime.windows():
+        fill_panel(window, update=True)
+
+
+class UpdateState(sublime_plugin.EventListener):
+    def on_activated_async(self, active_view):
+        vid = active_view.id()
+
+        current_pos = get_current_pos(active_view)
+        we_count = get_we_count(vid)
+
+        State.update({
+            'active_view': active_view,
+            'we_count': we_count,
+            'current_pos': current_pos
+        })
+        update_panel_selection(**State)
+
+    def on_selection_modified_async(self, _primary_view_):
+        active_view = State['active_view']
+        current_pos = get_current_pos(active_view)
+        if current_pos != State['current_pos']:
+            State.update({
+                'current_pos': current_pos
+            })
+            update_panel_selection(**State)
+
+    def on_pre_close(self, view):
+        if not util.is_lintable(view):
+            return
+
+        fill_panel(view.window(), update=True)
+
+
+class SublimeLinterPanelToggleCommand(sublime_plugin.WindowCommand):
+    def run(self, force_show=False, **kwargs):
+        active_panel = self.window.active_panel()
+        is_active_panel = (active_panel == "output." + PANEL_NAME)
+
+        if is_active_panel and not force_show:
+            self.show_panel(PANEL_NAME, show=False)
+        else:
+            fill_panel(self.window, **kwargs)
+            self.show_panel(PANEL_NAME)
+
+    def show_panel(self, name, show=True):
+        """
+        Change visibility of panel with given name.
+
+        Panel will be shown by default.
+        Pass show=False for hiding.
+        """
+        if show:
+            cmd = "show_panel"
+        else:
+            cmd = "hide_panel"
+
+        self.window.run_command(cmd, {"panel": "output." + name or ""})
+
+
+class SublimeLinterUpdatePanelCommand(sublime_plugin.TextCommand):
+    def run(self, edit, text="", clear_sel=False):
+        """Replace a view's text entirely and attempt to restore previous selection."""
+        sel = self.view.sel()
+        # Doesn't make sense to consider multiple selections
+        selected_region = sel[0] if sel else None
+        selected_text = self.view.substr(selected_region) if sel else None
+
+        self.view.set_read_only(False)
+        self.view.replace(edit, sublime.Region(0, self.view.size()), text)
+        self.view.set_read_only(True)
+
+        sel.clear()
+        if selected_text and not clear_sel:
+            new_selected_region = self.view.find(selected_text, 0, flags=sublime.LITERAL)
+            if new_selected_region:
+                sel.add(new_selected_region)
+                return
+        sel.add(0)
+
+
+def get_we_count(vid):
+    view_errors = persist.errors.get_view_dict(vid) if vid else {}
+    return view_errors.get('we_count_view', {})
+
+
+def get_current_pos(view):
+    try:
+        return view.rowcol(view.sel()[0].begin())
+    except (AttributeError, IndexError):
+        return -1, -1
+
+
 def get_common_parent(paths):
     """
     Get the common parent directory of multiple paths.
diff --git a/status_bar_view.py b/status_bar_view.py
index a6882ad..e26e46d 100644
--- a/status_bar_view.py
+++ b/status_bar_view.py
@@ -4,7 +4,6 @@ import sublime_plugin
 from .lint import persist
 from .lint.const import STATUS_KEY
 from .lint import events
-from .panel import panel
 
 
 State = {
@@ -47,7 +46,6 @@ class UpdateState(sublime_plugin.EventListener):
             'we_count': we_count,
             'current_pos': current_pos
         })
-        panel.update_panel_selection(**State)
         draw(**State)
 
     def on_selection_modified_async(self, _primary_view_):
@@ -57,7 +55,6 @@ class UpdateState(sublime_plugin.EventListener):
             State.update({
                 'current_pos': current_pos
             })
-            panel.update_panel_selection(**State)
             draw(**State)
 
 
diff --git a/sublime_linter.py b/sublime_linter.py
index ae25e10..b29d451 100644
--- a/sublime_linter.py
+++ b/sublime_linter.py
@@ -15,7 +15,6 @@ from .lint import persist, util, style
 from .lint.error import ErrorStore
 from .lint.const import WARN_ERR
 from .lint import backend
-from .panel import panel
 
 
 def backup_old_settings():
@@ -153,7 +152,6 @@ class Listener:
                 d.pop(vid, None)
 
         queue.cleanup(vid)
-        panel.fill_panel(view.window(), update=True)
 
     def on_hover(self, view, point, hover_zone):
         """On mouse hover event hook.
@@ -266,9 +264,6 @@ class SublimeLinter(sublime_plugin.EventListener, Listener):
             highlight.clear_view(view)
             highlights.draw(view)
 
-        for window in sublime.windows():
-            panel.fill_panel(window, update=True)
-
     def hit(self, view):
         """Record an activity that could trigger a lint and enqueue a desire to lint."""
         if not view: