Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote file and folder delete #217

Merged
merged 2 commits into from
Nov 21, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Create file `.remote-sync.json` in your project root with these settings:
* `ignore` — Array of [minimatch](https://github.com/isaacs/minimatch) patterns of files to ignore
* `uploadOnSave` — Whether or not to upload the current file when saved, default: false
* `uploadMirrors` — transport mirror config array when upload
* `deleteLocal` - whether or not to delete the local file / folder after remote delete

SCP example:
```json
Expand Down
40 changes: 21 additions & 19 deletions index.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ $ = null

getEventPath = (e)->
$ ?= require('atom-space-pen-views').$

target = $(e.target).closest('.file, .directory, .tab')[0]
target ?= atom.workspace.getActiveTextEditor()

fullPath = target?.getPath?()
return [] unless fullPath

[projectPath, relativePath] = atom.project.relativizePath(fullPath)
return [projectPath, fullPath]

Expand All @@ -27,7 +27,7 @@ initProject = (projectPaths)->
for projectPath in disposes
projectDict[projectPath].dispose()
delete projectDict[projectPath]

for projectPath in projectPaths
try
projectPath = fs.realpathSync(projectPath)
Expand All @@ -37,22 +37,22 @@ initProject = (projectPaths)->
RemoteSync ?= require "./lib/RemoteSync"
obj = RemoteSync.create(projectPath)
projectDict[projectPath] = obj if obj

handleEvent = (e, cmd)->
[projectPath, fullPath] = getEventPath(e)
return unless projectPath

projectObj = projectDict[fs.realpathSync(projectPath)]
projectObj[cmd]?(fs.realpathSync(fullPath))

reload = (projectPath)->
projectDict[projectPath]?.dispose()
projectDict[projectPath] = RemoteSync.create(projectPath)

configure = (e)->
[projectPath] = getEventPath(e)
return unless projectPath

projectPath = fs.realpathSync(projectPath)
RemoteSync ?= require "./lib/RemoteSync"
RemoteSync.configure projectPath, -> reload(projectPath)
Expand All @@ -72,51 +72,53 @@ module.exports =
configFileName:
type: 'string'
default: '.remote-sync.json'

activate: (state) ->
projectDict = {}
initProject(atom.project.getPaths())

CompositeDisposable ?= require('atom').CompositeDisposable
disposables = new CompositeDisposable

disposables.add atom.commands.add('atom-workspace', {
'remote-sync:upload-folder': (e)-> handleEvent(e, "uploadFolder")
'remote-sync:upload-file': (e)-> handleEvent(e, "uploadFile")
'remote-sync:delete-file': (e)-> handleEvent(e, "deleteFile")
'remote-sync:delete-folder': (e)-> handleEvent(e, "deleteFile")
'remote-sync:download-file': (e)-> handleEvent(e, "downloadFile")
'remote-sync:download-folder': (e)-> handleEvent(e, "downloadFolder")
'remote-sync:diff-file': (e)-> handleEvent(e, "diffFile")
'remote-sync:diff-folder': (e)-> handleEvent(e, "diffFolder")
'remote-sync:upload-git-change': (e)-> handleEvent(e, "uploadGitChange")
'remote-sync:configure': configure
})

disposables.add atom.project.onDidChangePaths (projectPaths)->
initProject(projectPaths)

disposables.add atom.workspace.observeTextEditors (editor) ->
onDidSave = editor.onDidSave (e) ->
fullPath = e.path
[projectPath, relativePath] = atom.project.relativizePath(fullPath)
return unless projectPath

projectPath = fs.realpathSync(projectPath)
projectObj = projectDict[projectPath]
return unless projectObj

if fs.realpathSync(fullPath) == fs.realpathSync(projectObj.configPath)
projectObj = reload(projectPath)

return unless projectObj.host.uploadOnSave
projectObj.uploadFile(fs.realpathSync(fullPath))


onDidDestroy = editor.onDidDestroy ->
disposables.remove onDidSave
disposables.remove onDidDestroy
onDidDestroy.dispose()
onDidSave.dispose()

disposables.add onDidSave
disposables.add onDidDestroy

Expand Down
52 changes: 33 additions & 19 deletions lib/RemoteSync.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -25,44 +25,58 @@ getLogger = ->
class RemoteSync
constructor: (@projectPath, @configPath) ->
Host ?= require './model/host'

@host = new Host(@configPath)
@initIgnore(@host)

initIgnore: (host)->
ignore = host.ignore?.split(",")
host.isIgnore = (filePath, relativizePath) =>
return false unless ignore

relativizePath = @projectPath unless relativizePath
filePath = path.relative relativizePath, filePath

minimatch ?= require "minimatch"
for pattern in ignore
return true if minimatch filePath, pattern, { matchBase: true, dot: true }
return false

isIgnore: (filePath, relativizePath)->
return @host.isIgnore(filePath, relativizePath)

dispose: ->
if @transport
@transport.dispose()
@transport = null

deleteFile: (filePath) ->
return if @isIgnore(filePath)

if not uploadCmd
UploadListener = require "./UploadListener"
uploadCmd = new UploadListener getLogger()

uploadCmd.handleDelete(filePath, @getTransport())
for t in @getUploadMirrors()
uploadCmd.handleDelete(filePath, t)

if @host.deleteLocal
fs.removeSync(filePath)

downloadFolder: (localPath, targetPath, callback)->
DownloadCmd ?= require './commands/DownloadAllCommand'
DownloadCmd.run(getLogger(), @getTransport(),
localPath, targetPath, callback)

downloadFile: (localPath)->
realPath = path.relative(@projectPath, localPath)
realPath = path.join(@host.target, realPath).replace(/\\/g, "/")
@getTransport().download(realPath)

uploadFile: (filePath) ->
return if @isIgnore(filePath)

if not uploadCmd
UploadListener = require "./UploadListener"
uploadCmd = new UploadListener getLogger()
Expand All @@ -74,7 +88,7 @@ class RemoteSync
uploadFolder: (dirPath)->
fs.traverseTree dirPath, @uploadFile.bind(@), =>
return not @isIgnore(dirPath)

uploadGitChange: (dirPath)->
repos = atom.project.getRepositories()
curRepo = null
Expand All @@ -85,15 +99,15 @@ class RemoteSync
curRepo = repo
break
return unless curRepo

isChangedPath = (path)->
status = curRepo.getCachedPathStatus(path)
return curRepo.isStatusModified(status) or curRepo.isStatusNew(status)

fs.traverseTree dirPath, (path)=>
@uploadFile(path) if isChangedPath(path)
, (path)=> return not @isIgnore(path)

createTransport: (host)->
if host.transport is 'scp' or host.transport is 'sftp'
ScpTransport ?= require "./transports/ScpTransport"
Expand Down Expand Up @@ -129,13 +143,13 @@ class RemoteSync

@getTransport().download realPath, targetPath, =>
@diff localPath, targetPath

diffFolder: (localPath)->
os = require "os" if not os
targetPath = path.join os.tmpDir(), "remote-sync"
@downloadFolder localPath, targetPath, =>
@diff localPath, targetPath

diff: (localPath, targetPath) ->
targetPath = path.join(targetPath, path.relative(@projectPath, localPath))
diffCmd = atom.config.get('remote-sync.difftoolCommand')
Expand All @@ -146,22 +160,22 @@ class RemoteSync
Command error: #{err}
command: #{diffCmd} #{localPath} #{targetPath}
"""
module.exports =

module.exports =
create: (projectPath)->
configPath = path.join projectPath, atom.config.get('remote-sync.configFileName')
return unless fs.existsSync configPath
return new RemoteSync(projectPath, configPath)

configure: (projectPath, callback)->
HostView ?= require './view/host-view'
Host ?= require './model/host'
EventEmitter ?= require("events").EventEmitter

emitter = new EventEmitter()
emitter.on "configured", callback

configPath = path.join projectPath, atom.config.get('remote-sync.configFileName')
host = new Host(configPath, emitter)
view = new HostView(host)
view.attach()
view.attach()
13 changes: 13 additions & 0 deletions lib/UploadListener.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ class UploadListener
localFilePath: localFilePath
transport: transport

handleDelete: (localFilePath, transport) ->
if not @queueDelete
async = require "async" if not async
@queueDelete = async.queue(@deleteFile.bind(@), 1)

@queueDelete.push
localFilePath: localFilePath
transport: transport

deleteFile: (task, callback) ->
{localFilePath, transport} = task
transport.delete localFilePath, callback

uploadFile: (task, callback) ->
{localFilePath, transport} = task
transport.upload localFilePath, callback
21 changes: 21 additions & 0 deletions lib/transports/FtpTransport.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@ class FtpTransport
@logger.error err if err
@connection = null

delete: (localFilePath, callback) ->
targetFilePath = path.join(@settings.target,
path.relative(@projectPath, localFilePath))
.replace(/\\/g, "/")

errorHandler = (err) =>
@logger.error err
callback()

@_getConnection (err, c) =>
return errorHandler err if err

end = @logger.log "Remote delete: #{targetFilePath} ..."

c.delete targetFilePath, (err) ->
return errorHandler err if err

end()

callback()

upload: (localFilePath, callback) ->
targetFilePath = path.join(@settings.target,
path.relative(@projectPath, localFilePath))
Expand Down
24 changes: 24 additions & 0 deletions lib/transports/ScpTransport.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ class ScpTransport
@connection.end()
@connection = null

delete: (localFilePath, callback) ->
targetFilePath = path.join(@settings.target,
path.relative(@projectPath, localFilePath))
.replace(/\\/g, "/")

errorHandler = (err) =>
@logger.error err
callback()

@_getConnection (err, c) =>
return errorHandler err if err

end = @logger.log "Remote delete: #{targetFilePath} ..."

c.sftp (err, sftp) ->
return errorHandler err if err

c.exec "rm -rf \"#{targetFilePath}\"", (err) ->
return errorHandler err if err

end()
sftp.end()
callback()

upload: (localFilePath, callback) ->
fs = require "fs" if not fs
targetFilePath = path.join(@settings.target,
Expand Down
10 changes: 8 additions & 2 deletions lib/view/host-view.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,12 @@ class ConfigView extends View
@div class: 'block', outlet: 'ftpPasswordBlock', style: 'display:none', =>
@label 'Password'

@label " uploadOnSave", =>
@input type: 'checkbox', outlet: 'uploadOnSave'
@div class:'block', =>
@label " uploadOnSave", =>
@input type: 'checkbox', outlet: 'uploadOnSave'

@label " Delete local file/folder upon remote delete", =>
@input type: 'checkbox', outlet: 'deleteLocal'

@div class: 'block pull-right', =>
@button class: 'inline-block-tight btn', outlet: 'cancelButton', click: 'close', 'Cancel'
Expand Down Expand Up @@ -91,6 +95,7 @@ class ConfigView extends View
$(editor).view().setText(@host[dataName] or "")

@uploadOnSave.prop('checked', @host.uploadOnSave)
@deleteLocal.prop('checked', @host.deleteLocal)
$(":contains('"+@host.transport.toUpperCase()+"')", @transportGroup).click() if @host.transport
if @host.transport is "scp"
$('.btn-group .btn', @authenticationButtonsBlock).each (i, btn)=>
Expand All @@ -107,6 +112,7 @@ class ConfigView extends View

confirm: ->
@host.uploadOnSave = @uploadOnSave.prop('checked')
@host.deleteLocal = @deleteLocal.prop('checked')
@find(".editor").each (i, editor)=>
dataName = $(editor).prev().text().split(" ")[0].toLowerCase()
view = $(editor).view()
Expand Down
6 changes: 4 additions & 2 deletions menus/context.cson
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
{label: 'Upload File', command: 'remote-sync:upload-file'}
{label: 'Download File', command: 'remote-sync:download-file'}
{label: 'Diff File', command: 'remote-sync:diff-file'}
{label: 'Delete File', command: 'remote-sync:delete-file'}
]
]

'.tree-view.full-menu .header.list-item':[
label: 'Remote Sync',
submenu:[
Expand All @@ -16,5 +17,6 @@
{label: 'Upload Folder Change', command: 'remote-sync:upload-git-change'}
{label: 'Download Folder', command: 'remote-sync:download-folder'}
{label: 'Diff Folder', command: 'remote-sync:diff-folder'}
{label: 'Delete Folder', command: 'remote-sync:delete-folder'}
]
]
]