       bubble.appendChild Message.fromMessage(trace, addPath: true)
     bubble
 
-  _renderPanel: ->
-    @_panel.innerHTML = ''
-    @_removeMarkers()
-    @_removeBubble()
-    if not @_messages.size
+  renderPanel: ->
+    @panel.innerHTML = ''
+    @removeMarkers()
+    @removeBubble()
+    if not @messages.size
       return @setPanelVisibility(false)
     @setPanelVisibility(true)
     activeEditor = atom.workspace.getActiveTextEditor()
-    @_messages.forEach (message) =>
-      if @_scope is 'file' then return unless message.currentFile
-      if @_underlineIssues and message.currentFile and message.range #Add the decorations to the current TextEditor
-        @_markers.push marker = activeEditor.markBufferRange message.range, {invalidate: 'never'}
+    @messages.forEach (message) =>
+      if @scope is 'file' then return unless message.currentFile
+      if @underlineIssues and message.currentFile and message.range #Add the decorations to the current TextEditor
+        @markers.push marker = activeEditor.markBufferRange message.range, {invalidate: 'never'}
         activeEditor.decorateMarker(
           marker, type: 'line-number', class: "linter-highlight #{message.class}"
         )

          marker, type: 'highlight', class: "linter-highlight #{message.class}"
        )

      if @_scope is 'line'
        return if @_lineMessages.indexOf(message) is -1

      Element = Message.fromMessage(message, addPath: @_scope is 'project', cloneNode: true)

      @_panel.appendChild Element
    @updateBubble()


  _removeMarkers: ->
    return unless @_markers.length
    for marker in @_markers
      try marker.destroy()
    @_markers = []

  # This method is called in render, and classifies the messages according to scope
  _extractMessages: (Gen, counts) ->
    isProject = @_scope is 'project'
    activeEditor = atom.workspace.getActiveTextEditor()
    activeFile = activeEditor?.getPath()
    Gen.forEach (Entry) =>

  # This message is called in editor-linter.coffee
  render: ->
    counts = {project: 0, file: 0}
    @messages.clear()
    @linter.eachEditorLinter (editorLinter) =>
      @extractMessages(editorLinter.getMessages(), counts)
    @._extractMessages(@linter.getProjectMessages(), counts)

    @updateLineMessages()

    @renderPanel()
    @tabs.get('file').count = counts.file
    @tabs.get('project').count = counts.project
    @bottomStatus.count = counts.project
    hasActiveEditor = typeof atom.workspace.getActiveTextEditor() isnt 'undefined'

    visibleTabs = @getVisibleTabKeys()

    @tabs.forEach (tab, key) ->
      tab.visibility = hasActiveEditor and visibleTabs.indexOf(key) isnt -1
      tab.classList.remove 'first-tab'
      tab.classList.remove 'last-tab'

    if visibleTabs.length > 0
      @tabs.get(visibleTabs[0]).classList.add 'first-tab'
      @tabs.get(visibleTabs[visibleTabs.length - 1]).classList.add 'last-tab'


  # consumed in editor-linter, _renderPanel
  updateBubble: (point) ->
    @removeBubble()
    return unless @showBubble
    return unless @messages.size
    activeEditor = atom.workspace.getActiveTextEditor()
    return unless activeEditor?.getPath()
    point = point || activeEditor.getCursorBufferPosition()
    try @messages.forEach (message) =>
      return unless message.currentFile
      return unless message.range?.containsPoint point
      @bubble = activeEditor.markBufferRange([point, point], {invalidate: 'never'})
      activeEditor.decorateMarker(
        @bubble
        {
          type: 'overlay',
          position: 'tail',
          item: @renderBubble(message)
        }
      )
      throw null

  updateCurrentLine: (line) ->
    if @currentLine isnt line
      @currentLine = line
      @updateLineMessages()
      @renderPanel()


  updateLineMessages: ->
    activeEditor = atom.workspace.getActiveTextEditor()
    @linter.eachEditorLinter (editorLinter) =>
      return unless editorLinter.editor is activeEditor

      @lineMessages = []
      @messages.forEach (message) =>
        if message.currentFile and message.range?.intersectsRow @currentLine
          @lineMessages.push message
      @tabs.get('line').count = @lineMessages.length

  # This method is called when we get the status-bar service
  attachBottom: (statusBar) ->
    @statusTiles.push statusBar.addLeftTile
      item: @tabs.get('line'),
      priority: -1002
    @statusTiles.push statusBar.addLeftTile
      item: @tabs.get('file'),
      priority: -1001
    @statusTiles.push statusBar.addLeftTile
      item: @tabs.get('project'),
      priority: -1000
    @statusTiles.push statusBar.addRightTile
      item: @bottomStatus,
      priority: 999

  # this method is called on package deactivate
  destroy: ->
    @messages.clear()
    @removeMarkers()
    @panelWorkspace.destroy()
    @removeBubble()
    for statusTile in @statusTiles
      statusTile.destroy()

  changeTab: (Tab) ->
    if @getActiveTabKey() is Tab
      @showPanel = not @showPanel
      @tabs.forEach (tab, key) -> tab.active = false
    else
      @showPanel = true
      @scope = Tab
      @tabs.forEach (tab, key) -> tab.active = Tab is key
      @renderPanel()
    @setShowPanel @showPanel

  getActiveTabKey: ->
    activeKey = null
    @tabs.forEach (tab, key) -> activeKey = key if tab.active
    return activeKey

  getActiveTab: ->
    @tabs.entries().find (tab) -> tab.active

  getVisibleTabKeys: ->
    return [
      'line'    if atom.config.get('linter.showErrorTabLine')
      'file'    if atom.config.get('linter.showErrorTabFile')
      'project' if atom.config.get('linter.showErrorTabProject')
    ].filter (key) -> key

  removeBubble: ->
    return unless @bubble
    @bubble.destroy()
    @bubble = null

  renderBubble: (message) ->
    bubble = document.createElement 'div'
    bubble.id = 'linter-inline'
    bubble.appendChild Message.fromMessage(message)
