 
     dispatchEvent: (ev) =>
 
-        range = @textBufferMarker.getBufferRange()
-        textBuffer = @core.editor.getBuffer().getTextInRange range
+        unless @mute
 
-        unless @mute
-            if textBuffer.length is 0
-                # got wiped out
-                @emit 'deactivate', range
-            else
-                if textBuffer.length > @outputBuffer.length
-                    @inputBuffer += textBuffer.substr -1
-                    @outputBuffer = textBuffer
-                    @emit 'insert', range, textBuffer.substr -1
-                if textBuffer.length < @outputBuffer.length
-                    @inputBuffer = @inputBuffer.substr(0, @inputBuffer.length - 1)
-                    @outputBuffer = textBuffer
-                    @emit 'delete', range, textBuffer
+            rangeOld = new Range ev.oldTailBufferPosition, ev.oldHeadBufferPosition
+            rangeNew = new Range ev.newTailBufferPosition, ev.newHeadBufferPosition
+            textBuffer = @core.editor.getBuffer().getTextInRange rangeNew
+            char = textBuffer.substr -1
+
+            # const for result of Range::compare()
+            INSERT = -1
+            DELETE = 1
+            change = rangeNew.compare rangeOld
+
+
+            if rangeNew.isEmpty()
+                @deactivate()
+            else if change is INSERT
+                char = textBuffer.substr -1
+                @rawInput += char
+                log 'IM', "insert '#{char}' #{@rawInput}"
+                {translation, further, suggestionKeys, candidateSymbols} = @translate @rawInput
+                log 'IM', "raw input '#{@rawInput}' translates to '#{translation}'"
+
+                # reflects current translation to the text buffer
+                if translation
+                    @muteEvent => @replaceString translation
+
+                # update view
+                if further
+                    @core.panelModel.setInputMethod @inputBuffer, suggestionKeys, candidateSymbols
+                else
+                    @deactivate()
+
+            else if change is DELETE
+                @rawInput = @rawInput.substr(0, @rawInput.length - 1)
+                log 'IM', "delete #{@rawInput}"
 
 
     #######################
     ###   Text Buffer   ###
     #######################
 
-    updateOutputBuffer: (text) ->
+    # inserts 1 character to the text buffer (may trigger some events)
+    insertChar: (char) ->
+        @core.editor.getBuffer().insert @textBufferMarker.getBufferRange().end, char
 
-        # update text buffer
-        @muteEvent =>
-            @core.editor.getBuffer().delete @textBufferMarker.getBufferRange()
-            @core.editor.getBuffer().insert @textBufferMarker.getBufferRange().start, text
-
-        # update output buffer
-        @outputBuffer = text
+    # replace content of the marker with supplied string (may trigger some events)
+    replaceString: (str) ->
+        @core.editor.getBuffer().setTextInRange @textBufferMarker.getBufferRange(), str
 
     ##################
     ###   Keymap   ###

        else
            # key combination out of keymap
            # replace with closest the symbol possible
            # log 'IM!', "input buffer: #{@inputBuffer} \n output buffer #{@outputBuffer} \n input #{input}"
            return {
                output: @outputBuffer
                further: false
                suggestionKeys: []
                candidateSymbols: []

                type: 'highlight'
                class: 'agda-input-method'

            # insert '\' at the cursor quitely without triggering any shit
            @muteEvent =>
                @insertChar '\\'

            # initial input suggestion
            @core.panelModel.inputMethodOn = true
