'use babel'

import {Emitter, CompositeDisposable} from 'atom'

const Validate = require('./validate')
const Helpers = require('./helpers')

export default class MessageRegistry {
  constructor() {
    this.active = true
    this.subscriptions = new CompositeDisposable()
    this.emitter = new Emitter()

    // Map<EditorLinter | null, Map<Linter, array<Message>>>
    this.messages = new Map()
    this.queue = []

    this.subscriptions.add(this.emitter)
    this.subscriptions.add(atom.config.observe('linter.ignoredMessageTypes', ignoredMessageTypes => {
      this.ignoredMessageTypes = new Set(ignoredMessageTypes)
    }))

    const UpdateMessages = () => {
      if (this.active) {
        if (this.queue.length) {
          this.processQueue()
        }
        Helpers.requestUpdateFrame(UpdateMessages)
      }
    }
    UpdateMessages()
  }
  set({linter, messages, editor = null}) {
    // Do not set messages if the linter is deactivated
    if (linter.deactivated) {
      return
    }

    // Validate messages
    try {
      Validate.messages(messages, linter)
    } catch (e) {
      return Helpers.error(e)
    }

    // Filter ignored ones
    if (this.ignoredMessageTypes.size) {
      messages = messages.filter(i => !this.ignoredMessageTypes.has(i.type))
    }

    this.queue.push({messages, linter, editor})
  }
  processQueue() {
    let added = []
    let removed = []

    this.queue.forEach(({messages, linter, editor}) => {
      let foundNew = false
      let foundChange = false

      const lastMessages = this.getMessages(linter, editor)
      const currentMessages = new Map()

      if (lastMessages.size === 0) {
        added = messages
        messages.forEach(function(message) {
          currentMessages.set(message.key, message)
        })
        foundNew = true
      } else {
        messages.forEach(function(message) {
          if (!lastMessages.has(message.key)) {
            foundChange = true
            added.push(message)
          }
          currentMessages.set(message.key, message)
        })

        if (!foundChange && messages.length === lastMessages.size) {
          // Nothing has changed here, no need to look for removed message
        } else {
          lastMessages.forEach(function(_, key) {
            if (!currentMessages.has(key)) {
              foundChange = true
              removed.push(lastMessages.get(key))
            }
          })
        }
      }

      if (foundChange || foundNew) {
        this.setMessages(linter, editor, currentMessages)
      }
    })

    if (added.length || removed.length) {
      const getAllMessages = () => this.getAllMessages()
      this.emitter.emit('did-update-messages', {added, removed, get messages() {
        return getAllMessages()
      }})
    }
  }
  getAllMessages() {
    let messages = []
    this.messages.forEach(function(bufferMessages) {
      bufferMessages.forEach(function(linterMessages) {
        linterMessages.forEach(function(message) {
          messages.push(message)
        })
      })
    })
    return messages
  }
  getMessages(linter, editor) {
    const bufferMessages = this.messages.get(editor)
    return (bufferMessages ? bufferMessages.get(linter) : undefined) || new Map()
  }
  setMessages(linter, editor, messages) {
    let bufferMessages = this.messages.get(editor)

    if (!bufferMessages) {
      this.messages.set(editor, bufferMessages = new Map())
    }
    bufferMessages.set(linter, messages)
  }
  onDidUpdateMessages(callback) {
    return this.emitter.on('did-update-messages', callback)
  }
  deleteMessages(linter) {
    // TODO
  }
  deleteEditorMessages(editorLinter) {
    // TODO
  }
  dispose() {
    this.active = false
    this.subscriptions.dispose()
    this.messages.clear()
    this.queue = []
  }
}

@@ -1361,16 +1361,19 @@
    let a
+llA
 dded = %5B
@@ -1382,17 +1382,20 @@
     let 
-r
+allR
 emoved =
@@ -1470,54 +1470,18 @@
 let 
-foundNew = false%0A      let foundChange = false
+added = %5B%5D
 %0A%0A  
@@ -1557,31 +1557,27 @@
  current
-Message
+Key
 s = new 
 Map()%0A%0A 
@@ -1568,19 +1568,19 @@
 s = new 
-Map
+Set
 ()%0A%0A    
@@ -1641,140 +1641,8 @@
 ges%0A
-        messages.forEach(function(message) %7B%0A          currentMessages.set(message.key, message)%0A        %7D)%0A        foundNew = true%0A
     
@@ -1749,39 +1749,8 @@
 ) %7B%0A
-            foundChange = true%0A
     
@@ -1780,33 +1780,41 @@
 age)%0A          %7D
-%0A
+ else %7B%0A 
           curren
@@ -1811,49 +1811,49 @@
     
+ 
 current
-Messages.set(message.key, message)
+Keys.add(message.key)%0A          %7D
 %0A   
@@ -1874,27 +1874,28 @@
    if (!
-foundChange
+added.length
  && mess
@@ -2097,23 +2097,19 @@
 !current
-Message
+Key
 s.has(ke
@@ -2132,26 +2132,46 @@
     
-foundChange = true
+allRemoved.push(lastMessages.get(key))
 %0A   
@@ -2185,21 +2185,8 @@
     
-removed.push(
 last
@@ -2190,33 +2190,35 @@
 astMessages.
-g
+del
 et
+e
 (key)
-)
 %0A           
@@ -2266,31 +2266,104 @@
 if (
-foundChange %7C%7C foundNew
+added.length) %7B%0A        allAdded = allAdded.concat(added)%0A        added.forEach(function(message
 ) %7B%0A
@@ -2374,15 +2374,13 @@
     
-this.se
+  las
 tMes
@@ -2388,40 +2388,44 @@
 ages
-(linter, editor, currentMessages
+.set(message.key, message)%0A        %7D
 )%0A  
@@ -2439,32 +2439,35 @@
    %7D)%0A%0A    if (a
+llA
 dded.length %7C%7C r
@@ -2465,17 +2465,20 @@
 ngth %7C%7C 
-r
+allR
 emoved.l
@@ -2600,11 +2600,33 @@
 dded
-, r
+: allAdded, removed: allR
 emov
@@ -3021,189 +3021,8 @@
 ) %7B%0A
-    const bufferMessages = this.messages.get(editor)%0A    return (bufferMessages ? bufferMessages.get(linter) : undefined) %7C%7C new Map()%0A  %7D%0A  setMessages(linter, editor, messages) %7B%0A
     
@@ -3068,17 +3068,16 @@
 editor)%0A
-%0A
     if (
@@ -3169,44 +3169,175 @@
     
-bufferMessages.set(linter, messages)
+let linterMessages = bufferMessages.get(linter)%0A    if (!linterMessages) %7B%0A      bufferMessages.set(linter, linterMessages = new Map())%0A    %7D%0A    return linterMessages
 %0A  %7D

Make MessageRegistry::processQueue even more efficient