<commit_before>class NFinderTreeController extends JTreeViewController

  constructor:->

    super

    if @getOptions().contextMenu
      @contextMenuController = new NFinderContextMenuController

      @contextMenuController.on "ContextMenuItemClicked", ({fileView, contextMenuItem})=>
        @contextMenuItemSelected fileView, contextMenuItem
    else
      @getView().setClass "no-context-menu"

    @appManager    = KD.getSingleton "appManager"
    mainController = KD.getSingleton "mainController"

    mainController.on "NewFileIsCreated", @bound "navigateToNewFile"
    mainController.on "SelectedFileChanged", @bound "highlightFile"

  addNode:(nodeData, index)->
    fc = @getDelegate()
    return if @getOption('foldersOnly') and nodeData.type is "file"
    return if nodeData.isHidden() and fc.isNodesHiddenFor nodeData.machine.uid
    item = super nodeData, index

  highlightFile:(view)->

    @selectNode @nodes[view.data.path], null, no

    if view.ace?
      if view.ace.editor?
        view.ace.editor.focus()
      else
        view.ace.on "ace.ready", ->
          view.ace.editor.focus()

  navigateToNewFile:(newFile)->

    @navigateTo newFile.parentPath, =>
      @selectNode @nodes[newFile.path]

  getOpenFolders: ->

    return Object.keys(@listControllers).slice(1)

  ###
  FINDER OPERATIONS
  ###

  openItem:(nodeView, callback)->

    options  = @getOptions()
    nodeData = nodeView.getData()

    switch nodeData.type
      when "folder", "mount", "vm", "machine"
        @toggleFolder nodeView, callback
      when "file"
        @openFile nodeView
        @emit "file.opened", nodeData
        @setBlurState()

  openFileWithApp: (nodeView, contextMenuItem) ->
    return warn "no app passed to open this file"  unless contextMenuItem
    app = contextMenuItem.getData().title
    KD.getSingleton("appManager").openFileWithApplication app, nodeView.getData()

  openFile:(nodeView)->

    return unless nodeView
    file = nodeView.getData()
    # @appManager.openFile file
    @getDelegate().emit "FileNeedsToBeOpened", file

  previewFile:(nodeView)->
    {vmName, path} = nodeView.getData()
    @appManager.open "Viewer", params: {path, vmName}

  resetVm:(nodeView)->
    {vmName} = nodeView.data
    KD.getSingleton('vmController').reinitialize vmName

  unmountVm:(nodeView)->
    { machine: { uid } } = nodeView.getData()
    @getDelegate().unmountMachine uid

  openMachineTerminal: (nodeView) ->
    {machine}  = nodeView.getData()
    appManager = KD.getSingleton 'appManager'
    ideApp     = appManager.get 'IDE'
    callback   = -> appManager.tell 'IDE', 'openMachineTerminal', machine

    if ideApp then callback() else appManager.open 'IDE', callback

  toggleDotFiles:(nodeView)->

    finder = @getDelegate()
    { machine: { uid } } = nodeView.getData()

    if finder.isNodesHiddenFor uid
    then finder.showDotFiles uid
    else finder.hideDotFiles uid

  makeTopFolder:(nodeView)->
    {machine, path} = nodeView.getData()
    finder = @getDelegate()
    finder.updateMachineRoot machine.uid, FSHelper.plainPath path

  refreshFolder:(nodeView, callback)->

    @notify "Refreshing..."
    folder = nodeView.getData()
    folder.emit "fs.job.finished", [] # in case of refresh to stop the spinner

    @collapseFolder nodeView, =>
      @expandFolder nodeView, =>
        notification.destroy()
        callback?()

  toggleFolder:(nodeView, callback)->
    if nodeView.expanded
      @collapseFolder nodeView, callback
    else
      @expandFolder nodeView, callback

  expandFolder:(nodeView, callback, silence=no)->

    return unless nodeView
    return if nodeView.isLoading

    if nodeView.expanded
      callback? null, nodeView
      return

    folder = nodeView.getData()

    if folder.depth > 10
      @notify "Folder is nested deeply, making it top folder"
      @makeTopFolder nodeView

    failCallback = (err)=>
      unless silence
        if err?.message?.match /permission denied/i
          message = "Permission denied!"
          KD.logToExternal "filetree: Couldn't fetch files, permission denied"
        else
          message = "Couldn't fetch files! Click to retry"
          KD.logToExternalWithTime "filetree: Couldn't fetch files"
        @notify message, 'clickable', \
                """Sorry, a problem occured while communicating with servers,
                   please try again later.""", yes
        @once 'fs.retry.scheduled', => @expandFolder nodeView, callback
      folder.emit "fs.job.finished", []
      callback? err

    folder.fetchContents no, (KD.utils.getTimedOutCallback (err, files)=>
      unless err
        nodeView.expand()
        if files
          @addNodes files
        callback? null, nodeView
        @emit "folder.expanded", nodeView.getData()  unless silence
        @emit 'fs.retry.success'
        @hideNotification()
      else
        failCallback err
    , failCallback, KD.config.fileFetchTimeout)

  collapseFolder:(nodeView, callback, silence=no)->

    return unless nodeView
    folder = nodeView.getData()
    {path} = folder

    @emit "folder.collapsed", folder  unless silence

    if @listControllers[path]
      @listControllers[path].getView().collapse =>
        @removeChildNodes path
        nodeView.collapse()
        callback? nodeView
    else
      nodeView.collapse()
      callback? nodeView

  navigateTo:(path, callback)->

    return unless path

    path = path.split('/')
    path.shift()  if path[0] is ''
    path.pop()    if path[path.length-1] is ''
    path[1] = "/#{path[0]}/#{path[1]}"
    path.shift()

    index     = 0
    lastPath  = ''

    _expand = (path)=>
      nextPath = path.slice(0, ++index).join('/')
      if lastPath is nextPath
        @refreshFolder @nodes[nextPath], =>
          callback?()
        return

      @expandFolder @nodes[nextPath], =>
        lastPath = nextPath
        _expand path

    _expand path

  confirmDelete:(nodeView, event)->

    extension = nodeView.data?.getExtension() or null

    if @selectedNodes.length > 1
      new NFinderDeleteDialog {},
        items     : @selectedNodes
        callback  : (confirmation)=>
          @deleteFiles @selectedNodes if confirmation
          @setKeyView()
    else
      @beingEdited = nodeView
      nodeView.confirmDelete (confirmation)=>
        @deleteFiles [nodeView] if confirmation
        @setKeyView()
        @beingEdited = null

  deleteFiles:(nodes, callback)->

    deletedNodes = []
    results = nodes.map (node) ->
      node.getData().remove().then ->
        node.emit "ItemBeingDeleted"
        deletedNodes.push node

    Promise.all(results).then =>
      @notify "#{deletedNodes.length} item#{if deletedNodes.length > 1 then 's' else ''} deleted!", "success"
      @removeNodeView node for node in deletedNodes

    .catch (err) =>
      @notify null, null, err

    .nodeify callback

  showRenameDialog:(nodeView)->

    @beingEdited = nodeView
    nodeData = nodeView.getData()
    oldPath = nodeData.path
    nodeView.showRenameView (newValue)=>
      return if newValue is nodeData.name

      nodeData.rename newValue, (err)=>
        if err then @notify null, null, err

      # @setKeyView()
      @beingEdited = null

  createFile:(nodeView, type = "file")->

    @notify "creating a new #{type}!"
    nodeData = nodeView.getData()

    { machine } = nodeData

    if nodeData.type is "file"
      { parentPath } = nodeData
    else
      parentPath = nodeData.path

    path = FSHelper.plainPath \
      "#{parentPath}/New#{type.capitalize()}#{if type is 'file' then '.txt' else ''}"

    machine.fs.create { path, type, treeController: this }, (err, file)=>
      if err
        @notify null, null, err
      else
        @refreshFolder @nodes[parentPath], =>
          @notify "#{type} created!", "success"
          node = @nodes["[#{file.machine.uid}]#{file.path}"]
          @selectNode node
          @showRenameDialog node


  moveFiles:(nodesToBeMoved, targetNodeView, callback)->

    targetItem = targetNodeView.getData()
    if targetItem.type is "file"
      targetNodeView = @nodes[targetNodeView.getData().parentPath]
      targetItem = targetNodeView.getData()

    movedNodes = []
    results = nodesToBeMoved.map (node) ->
      sourceItem = node.getData()
      FSItem.move(sourceItem, targetItem).then ->
        movedNodes.push node

    Promise.all(results).then =>
      @notify "#{movedNodes.length} item#{if movedNodes.length > 1 then 's' else ''} moved!", "success"
      @removeNodeView node for node in movedNodes
      @refreshFolder targetNodeView

    .catch (err) =>
      @notify null, null, err

    .nodeify callback

  copyFiles:(nodesToBeCopied, targetNodeView, callback)->

    targetItem = targetNodeView.getData()
    if targetItem.type is "file"
      targetNodeView = @nodes[targetNodeView.getData().parentPath]
      targetItem = targetNodeView.getData()

    copiedNodes = []
    results = nodesToBeCopied.map (node) ->
      sourceItem = node.getData()
      FSItem.copy(sourceItem, targetItem).then ->
        copiedNodes.push node

    Promise.all(results).then =>
      @notify "#{copiedNodes.length} item#{if copiedNodes.length > 1 then 's' else ''} copied!", "success"
      @refreshFolder targetNodeView

    .catch (err) =>
      @notify null, null, err
<commit_msg>FinderTreeController: move/copy are instance level methods we need to use them as is.<commit_after>class NFinderTreeController extends JTreeViewController

  constructor:->

    super

    if @getOptions().contextMenu
      @contextMenuController = new NFinderContextMenuController

      @contextMenuController.on "ContextMenuItemClicked", ({fileView, contextMenuItem})=>
        @contextMenuItemSelected fileView, contextMenuItem
    else
      @getView().setClass "no-context-menu"

    @appManager    = KD.getSingleton "appManager"
    mainController = KD.getSingleton "mainController"

    mainController.on "NewFileIsCreated", @bound "navigateToNewFile"
    mainController.on "SelectedFileChanged", @bound "highlightFile"

  addNode:(nodeData, index)->
    fc = @getDelegate()
    return if @getOption('foldersOnly') and nodeData.type is "file"
    return if nodeData.isHidden() and fc.isNodesHiddenFor nodeData.machine.uid
    item = super nodeData, index

  highlightFile:(view)->

    @selectNode @nodes[view.data.path], null, no

    if view.ace?
      if view.ace.editor?
        view.ace.editor.focus()
      else
        view.ace.on "ace.ready", ->
          view.ace.editor.focus()

  navigateToNewFile:(newFile)->

    @navigateTo newFile.parentPath, =>
      @selectNode @nodes[newFile.path]

  getOpenFolders: ->

    return Object.keys(@listControllers).slice(1)

  ###
  FINDER OPERATIONS
  ###

  openItem:(nodeView, callback)->

    options  = @getOptions()
    nodeData = nodeView.getData()

    switch nodeData.type
      when "folder", "mount", "vm", "machine"
        @toggleFolder nodeView, callback
      when "file"
        @openFile nodeView
        @emit "file.opened", nodeData
        @setBlurState()

  openFileWithApp: (nodeView, contextMenuItem) ->
    return warn "no app passed to open this file"  unless contextMenuItem
    app = contextMenuItem.getData().title
    KD.getSingleton("appManager").openFileWithApplication app, nodeView.getData()

  openFile:(nodeView)->

    return unless nodeView
    file = nodeView.getData()
    # @appManager.openFile file
    @getDelegate().emit "FileNeedsToBeOpened", file

  previewFile:(nodeView)->
    {vmName, path} = nodeView.getData()
    @appManager.open "Viewer", params: {path, vmName}

  resetVm:(nodeView)->
    {vmName} = nodeView.data
    KD.getSingleton('vmController').reinitialize vmName

  unmountVm:(nodeView)->
    { machine: { uid } } = nodeView.getData()
    @getDelegate().unmountMachine uid

  openMachineTerminal: (nodeView) ->
    {machine}  = nodeView.getData()
    appManager = KD.getSingleton 'appManager'
    ideApp     = appManager.get 'IDE'
    callback   = -> appManager.tell 'IDE', 'openMachineTerminal', machine

    if ideApp then callback() else appManager.open 'IDE', callback

  toggleDotFiles:(nodeView)->

    finder = @getDelegate()
    { machine: { uid } } = nodeView.getData()

    if finder.isNodesHiddenFor uid
    then finder.showDotFiles uid
    else finder.hideDotFiles uid

  makeTopFolder:(nodeView)->
    {machine, path} = nodeView.getData()
    finder = @getDelegate()
    finder.updateMachineRoot machine.uid, FSHelper.plainPath path

  refreshFolder:(nodeView, callback)->

    @notify "Refreshing..."
    folder = nodeView.getData()
    folder.emit "fs.job.finished", [] # in case of refresh to stop the spinner

    @collapseFolder nodeView, =>
      @expandFolder nodeView, =>
        notification.destroy()
        callback?()

  toggleFolder:(nodeView, callback)->
    if nodeView.expanded
      @collapseFolder nodeView, callback
    else
      @expandFolder nodeView, callback

  expandFolder:(nodeView, callback, silence=no)->

    return unless nodeView
    return if nodeView.isLoading

    if nodeView.expanded
      callback? null, nodeView
      return

    folder = nodeView.getData()

    if folder.depth > 10
      @notify "Folder is nested deeply, making it top folder"
      @makeTopFolder nodeView

    failCallback = (err)=>
      unless silence
        if err?.message?.match /permission denied/i
          message = "Permission denied!"
          KD.logToExternal "filetree: Couldn't fetch files, permission denied"
        else
          message = "Couldn't fetch files! Click to retry"
          KD.logToExternalWithTime "filetree: Couldn't fetch files"
        @notify message, 'clickable', \
                """Sorry, a problem occured while communicating with servers,
                   please try again later.""", yes
        @once 'fs.retry.scheduled', => @expandFolder nodeView, callback
      folder.emit "fs.job.finished", []
      callback? err

    folder.fetchContents no, (KD.utils.getTimedOutCallback (err, files)=>
      unless err
        nodeView.expand()
        if files
          @addNodes files
        callback? null, nodeView
        @emit "folder.expanded", nodeView.getData()  unless silence
        @emit 'fs.retry.success'
        @hideNotification()
      else
        failCallback err
    , failCallback, KD.config.fileFetchTimeout)

  collapseFolder:(nodeView, callback, silence=no)->

    return unless nodeView
    folder = nodeView.getData()
    {path} = folder

    @emit "folder.collapsed", folder  unless silence

    if @listControllers[path]
      @listControllers[path].getView().collapse =>
        @removeChildNodes path
        nodeView.collapse()
        callback? nodeView
    else
      nodeView.collapse()
      callback? nodeView

  navigateTo:(path, callback)->

    return unless path

    path = path.split('/')
    path.shift()  if path[0] is ''
    path.pop()    if path[path.length-1] is ''
    path[1] = "/#{path[0]}/#{path[1]}"
    path.shift()

    index     = 0
    lastPath  = ''

    _expand = (path)=>
      nextPath = path.slice(0, ++index).join('/')
      if lastPath is nextPath
        @refreshFolder @nodes[nextPath], =>
          callback?()
        return

      @expandFolder @nodes[nextPath], =>
        lastPath = nextPath
        _expand path

    _expand path

  confirmDelete:(nodeView, event)->

    extension = nodeView.data?.getExtension() or null

    if @selectedNodes.length > 1
      new NFinderDeleteDialog {},
        items     : @selectedNodes
        callback  : (confirmation)=>
          @deleteFiles @selectedNodes if confirmation
          @setKeyView()
    else
      @beingEdited = nodeView
      nodeView.confirmDelete (confirmation)=>
        @deleteFiles [nodeView] if confirmation
        @setKeyView()
        @beingEdited = null

  deleteFiles:(nodes, callback)->

    deletedNodes = []
    results = nodes.map (node) ->
      node.getData().remove().then ->
        node.emit "ItemBeingDeleted"
        deletedNodes.push node

    Promise.all(results).then =>
      @notify "#{deletedNodes.length} item#{if deletedNodes.length > 1 then 's' else ''} deleted!", "success"
      @removeNodeView node for node in deletedNodes

    .catch (err) =>
      @notify null, null, err

    .nodeify callback

  showRenameDialog:(nodeView)->

    @beingEdited = nodeView
    nodeData = nodeView.getData()
    oldPath = nodeData.path
    nodeView.showRenameView (newValue)=>
      return if newValue is nodeData.name

      nodeData.rename newValue, (err)=>
        if err then @notify null, null, err

      # @setKeyView()
      @beingEdited = null

  createFile:(nodeView, type = "file")->

    @notify "creating a new #{type}!"
    nodeData = nodeView.getData()

    { machine } = nodeData

    if nodeData.type is "file"
      { parentPath } = nodeData
    else
      parentPath = nodeData.path

    path = FSHelper.plainPath \
      "#{parentPath}/New#{type.capitalize()}#{if type is 'file' then '.txt' else ''}"

    machine.fs.create { path, type, treeController: this }, (err, file)=>
      if err
        @notify null, null, err
      else
        @refreshFolder @nodes[parentPath], =>
          @notify "#{type} created!", "success"
          node = @nodes["[#{file.machine.uid}]#{file.path}"]
          @selectNode node
          @showRenameDialog node


  moveFiles:(nodesToBeMoved, targetNodeView, callback)->

    targetItem = targetNodeView.getData()
    if targetItem.type is "file"
      targetNodeView = @nodes[targetNodeView.getData().parentPath]
      targetItem = targetNodeView.getData()

    movedNodes = []
    results = nodesToBeMoved.map (node) ->
      sourceItem = node.getData()
      sourceItem.move("#{targetItem.path}/").then ->
        movedNodes.push node

    Promise.all(results).then =>
      @notify "#{movedNodes.length} item#{if movedNodes.length > 1 then 's' else ''} moved!", "success"
      @removeNodeView node for node in movedNodes
      @refreshFolder targetNodeView

    .catch (err) =>
      warn "Move failed with error:", err
      @notify null, null, err

    .nodeify callback

  copyFiles:(nodesToBeCopied, targetNodeView, callback)->

    targetItem = targetNodeView.getData()
    if targetItem.type is "file"
      targetNodeView = @nodes[targetNodeView.getData().parentPath]
      targetItem = targetNodeView.getData()

    copiedNodes = []
    results = nodesToBeCopied.map (node) ->
      sourceItem = node.getData()
      sourceItem.copy(targetItem.path).then ->
        copiedNodes.push node

    Promise.all(results).then =>
      @notify "#{copiedNodes.length} item#{if copiedNodes.length > 1 then 's' else ''} copied!", "success"

    .catch (err) =>
      warn "Copy failed with error:", err
      @notify null, null, err

{'fraction_non_alphanumeric': 0.11660437800320342, 'fraction_numerical': 0.0014949279231179925, 'mean_word_length': 2.8525298231180582, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 125, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6986234', 'n_tokens_mistral': 5598, 'n_tokens_neox': 5422, 'n_words': 1344}