       path: @formatPath(path)
 
   localMove: (source, target) ->
-    console.log '>>> 0'
     action: 'local_move'
+    project:
+      path: @formatPath(@projectPath)
+    file:
+      path: @formatPath(target)
+    from: @formatPath(source)
+
+  localDuplicate: (source, target) ->
+    action: 'local_duplicate'
     project:
       path: @formatPath(@projectPath)
     file:

        when 'tree-view:remove' then @onTreeViewRemove(event)

    atom.workspace.observeTextEditors (editor) =>
      editor.onDidSave =>
        @onSave(editor)
      editor.onDidChangePath ->
        console.log 'PATH CHANGED'

    atom.workspace.onDidOpen ({uri, item, pane, index}) ->
      console.log 'OPENED ' + uri

  onSave: (editor) =>
    @convertLineEndings(editor)

    @sendLocalEvent @localSave(editor.project, file, buffer)

  onCoreConfirm: (event) ->
    setTimeout =>
      @syncAdditions()
      @syncMoves()
      @syncDuplication()
    , 10

  onTreeViewRemove: (event) =>
    @syncRemovals()

  onTreeViewWillDispatch: (event) =>
    {type, target} = event
    @pathAtWillDispatch = target.dataset.path || target.firstChild.dataset.path
    @willDispatchCommand = type
    @entriesAtWillDispatch = fs.listTreeSync(@projectPath)

  purgeTreeViewEvent: =>
    @pathAtWillDispatch = null
    @willDispatchCommand = null
    @entriesAtWillDispatch = null

  syncRemovals: =>
    prevEntries = @entriesAtWillDispatch

    return unless @willDispatchCommand is 'tree-view:remove'
    @purgeTreeViewEvent()

    removedEntries = _.difference(prevEntries, fs.listTreeSync(@projectPath))
    return unless removedEntries.length

    _.each removedEntries, (entry) => @sendLocalEvent @localRemove(entry)

  syncAdditions: =>
    prevEntries = @entriesAtWillDispatch

    return unless @willDispatchCommand?.match(/tree-view:add/)
    @purgeTreeViewEvent()

    return unless prevEntries?

