Skip to content
Open
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
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
install:
@echo 'export PATH=$$PATH:$(shell pwd)/bin' >> $(HOME)/.bashrc
@echo "let &runtimepath='$(shell pwd),'.&runtimepath" >> $(HOME)/.vimrc
2 changes: 2 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Netrw supports reading and writing files across networks. One may use urls for
:e rsync://[user@]machine[:port]/path uses rsync
:e scp://[user@]machine[[:#]port]/path uses scp
:e sftp://[user@]machine/path uses sftp
:e dbx:///path uses dbx.py, included and must be put into the path.

REMOTE READING
:Nread ? give help
Expand Down Expand Up @@ -47,6 +48,7 @@ Netrw supports reading and writing files across networks. One may use urls for

REMOTE DIRECTORY BROWSING
:e [protocol]://[user]@hostname/path/
:e dbx:///path
:Nread [protocol]://[user]@hostname/path/

LOCAL DIRECTORY BROWSING
Expand Down
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## Vim-Dropbox

This is a fork of the [NetRw](https://github.com/vim-scripts/netrw.vim) project
which allows vim to support URL's of the type `dbx:///Path/Inside/Dropbox`.

At the moment, it supports only reading files, creating files, and browsing
directories. It cannot perform file deletions or renames.

Here is a [demo video](https://dl.dropboxusercontent.com/u/25959267/1313%20-%20Vim-Dropbox.mp4) showing the expected behavior.

## Installation

Installation is currently manual, but should not be too difficult for users
versed in the ways of the shell.

1. Install the [Dropbox Python
SDK](https://github.com/dropbox/dropbox-sdk-python) if you do not already
have it installed.

2. Go to https://www.dropbox.com/developers/apps and create a new app with any
name.

3. Click into the App's settings page and click on the `Generate` button under
the label `Generated Access Token`.

4. Copy this token and replace the string `INSERT_TOKEN_HERE` in the file `bin/dbx.py` with the token.

5. Run the `make install` from this directory, which will simply append a line
to your `.vimrc` and `.bashrc` files to enable this plugin.

If you run into any trouble with these steps, please open an issue on GitHub or contact the author out-of-band.

## Usage

vim dbx:///
vim dbx:///TestFolder/Example.txt
47 changes: 43 additions & 4 deletions autoload/netrw.vim
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ call s:NetrwInit("g:netrw_rsync_cmd", "rsync")
call s:NetrwInit("g:netrw_scp_cmd" , "scp -q")
call s:NetrwInit("g:netrw_sftp_cmd" , "sftp")
call s:NetrwInit("g:netrw_ssh_cmd" , "ssh")
call s:NetrwInit("g:netrw_dbx_cmd" , "dbx.py")

if (has("win32") || has("win95") || has("win64") || has("win16"))
\ && exists("g:netrw_use_nt_rcp")
Expand Down Expand Up @@ -954,6 +955,11 @@ fun! netrw#NetRead(mode,...)
let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
let b:netrw_lastfile = choice

" NetRead: (Dropbox) NetRead Method #10 {{{3
elseif b:netrw_method == 10
exe s:netrw_silentxfer."!".g:netrw_dbx_cmd." down ".shellescape(b:netrw_fname,1)." ".tmpfile
let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
let b:netrw_lastfile = choice
".........................................
" NetRead: Complain {{{3
else
Expand Down Expand Up @@ -1320,6 +1326,10 @@ fun! netrw#NetWrite(...) range
exe filtbuf."bw!"
let b:netrw_lastfile = choice

" NetWrite: (Dropbox) NetWrite Method #10 {{{3
elseif b:netrw_method == 10
exe s:netrw_silentxfer."!".g:netrw_dbx_cmd." up ".shellescape(b:netrw_fname,1)." ".tmpfile
let b:netrw_lastfile = choice
".........................................
" NetWrite: Complain {{{3
else
Expand Down Expand Up @@ -1572,6 +1582,7 @@ fun! s:NetrwMethod(choice)
let rsyncurm = '^rsync://\([^/]\{-}\)/\(.*\)\=$'
let fetchurm = '^fetch://\(\([^/@]\{-}\)@\)\=\([^/#:]\{-}\)\(:http\)\=/\(.*\)$'
let sftpurm = '^sftp://\([^/]\{-}\)/\(.*\)\=$'
let dburm = '^dbx:///\(.*\)\=$'

" call Decho("determine method:")
" Determine Method
Expand Down Expand Up @@ -1665,6 +1676,11 @@ fun! s:NetrwMethod(choice)
endif
endif

" Method#10: Get it from Dropbox
elseif match(a:choice, dburm) == 0
let b:netrw_method = 10
let b:netrw_fname = substitute(a:choice,dburm,'\1',"")

" Method#8: fetch {{{3
elseif match(a:choice,fetchurm) == 0
" call Decho("fetch://...")
Expand Down Expand Up @@ -1718,7 +1734,6 @@ fun! s:NetrwMethod(choice)
if userid != ""
let g:netrw_uid= userid
endif

" Cannot Determine Method {{{3
else
if !exists("g:netrw_quiet")
Expand Down Expand Up @@ -2510,7 +2525,7 @@ fun! s:NetrwBrowse(islocal,dirname)
" set b:netrw_curdir to the new directory name {{{3
" call Decho("set b:netrw_curdir to the new directory name: (buf#".bufnr("%").")")
let b:netrw_curdir= dirname
if b:netrw_curdir =~ '[/\\]$'
if b:netrw_curdir =~ '[/\\]$' && b:netrw_curdir !~ '^dbx://'
let b:netrw_curdir= substitute(b:netrw_curdir,'[/\\]$','','e')
endif
if b:netrw_curdir == ''
Expand Down Expand Up @@ -2583,6 +2598,14 @@ fun! s:NetrwBrowse(islocal,dirname)
let dirname = substitute(dirname,'\\','/','g')
" call Decho("(normal) dirname<".dirname.">")
endif
" Check if the pattern matches a Dropbox URL.
let dbxpat = '^dbx:///\(.*\)\='
if dirname =~ dbxpat
keepj call s:NetrwMaps(a:islocal)
keepj call s:PerformListing(a:islocal)
let s:locbrowseshellcmd= 1
return
endif

let dirpat = '^\(\w\{-}\)://\(\w\+@\)\=\([^/]\+\)/\(.*\)$'
if dirname !~ dirpat
Expand Down Expand Up @@ -3177,7 +3200,6 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...)
else
let dirpat= '[\/]$'
endif
" call Decho("dirname<".dirname."> dirpat<".dirpat.">")

if dirname !~ dirpat
" apparently vim is "recognizing" that it is in a directory and
Expand Down Expand Up @@ -6837,7 +6859,13 @@ fun! s:NetrwRemoteListing()
keepj call histdel("/",-1)
endif
endif

" Dropbox directory listing
elseif s:method == "dbx"
if s:path == ''
exe "sil! keepalt r! ".g:netrw_dbx_cmd." list /"
else
exe "sil! keepalt r! ".g:netrw_dbx_cmd." list ".shellescape(s:path)
endif
else
" use ssh to get remote file listing {{{3
" call Decho("use ssh to get remote file listing: s:path<".s:path.">")
Expand Down Expand Up @@ -8364,6 +8392,17 @@ endfun
fun! s:RemotePathAnalysis(dirname)
" call Dfunc("s:RemotePathAnalysis(a:dirname<".a:dirname.">)")

let dbxpat = '^dbx:///\(.*\)\='
if a:dirname =~ dbxpat
let s:method = 'dbx'
let s:user = ''
let s:machine = ''
let s:port = ''
let s:path = substitute(a:dirname,dbxpat,'\1','')
let s:fname = ''
return
endif

let dirpat = '^\(\w\{-}\)://\(\w\+@\)\=\([^/:#]\+\)\%([:#]\(\d\+\)\)\=/\(.*\)$'
let s:method = substitute(a:dirname,dirpat,'\1','')
let s:user = substitute(a:dirname,dirpat,'\2','')
Expand Down
1 change: 1 addition & 0 deletions autoload/netrwSettings.vim
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ fun! netrwSettings#NetrwSettings()
put = 'let g:netrw_scp_cmd = '.g:netrw_scp_cmd
put = 'let g:netrw_sftp_cmd = '.g:netrw_sftp_cmd
put = 'let g:netrw_ssh_cmd = '.g:netrw_ssh_cmd
put = 'let g:netrw_dbx_cmd = '.g:netrw_dbx_cmd
let s:netrw_protocol_stop= line(".")
put = ''

Expand Down
135 changes: 135 additions & 0 deletions bin/dbx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/python

"""Upload or download individual files from your Dropbox. """

from __future__ import print_function

import argparse
import contextlib
import datetime
import os
import six
import sys
import time
import unicodedata

if sys.version.startswith('2'):
input = raw_input

import dropbox
from dropbox.files import FileMetadata, FolderMetadata

# OAuth2 access token. Obtain this from the
# https://www.dropbox.com/developers/apps by creating an App and the clicking on
# Generate Access Token.
TOKEN = 'INSERT_TOKEN_HERE'

def main():
"""Main program.

Update or download an individual file.
"""
if len(sys.argv) < 3:
usage()
command = sys.argv[1]
remote_path = sys.argv[2]

if command == "up" or command == "down":
if len(sys.argv) < 4:
usage()
local_path = sys.argv[3]

dbx = dropbox.Dropbox(TOKEN)
try:
if command == "up":
upload(dbx, remote_path, local_path)
elif command == "down":
download(dbx, remote_path, local_path)
elif command == "list":
list_folder(dbx, remote_path)
else:
usage()
except:
pass

def list_folder(dbx, remote_path):
"""List a folder.

Return a dict mapping unicode filenames to
FileMetadata|FolderMetadata entries.
"""
if not remote_path.startswith("/"):
remote_path = '/' + remote_path
remote_path = remote_path.rstrip('/')
try:
with stopwatch('list_folder'):
res = dbx.files_list_folder(remote_path)
except dropbox.exceptions.ApiError as err:
# print('Folder listing failed for', remote_path, '-- assumped empty:', err)
return
else:
for entry in res.entries:
if isinstance(entry, FolderMetadata):
print(entry.name + "/")
else:
print(entry.name)



def usage():
print("Usage: python dbx.py <up|down> <remote_path> [local_path]")
sys.exit(1)


def download(dbx, remote_path, local_path):
"""Download a file.

Return the bytes of the file, or None if it doesn't exist.
"""
if not remote_path.startswith("/"):
remote_path = '/' + remote_path
with stopwatch('download'):
try:
dbx.files_download_to_file(local_path, remote_path)
except dropbox.exceptions.HttpError as err:
print('*** HTTP error', err)
return None

def upload(dbx, remote_path, local_path, overwrite=True):
"""Upload a file.

Return the request response, or None in case of error.
"""
if not remote_path.startswith("/"):
remote_path = '/' + remote_path
mode = (dropbox.files.WriteMode.overwrite
if overwrite
else dropbox.files.WriteMode.add)
mtime = os.path.getmtime(local_path)
with open(local_path, 'rb') as f:
data = f.read()
with stopwatch('upload %d bytes' % len(data)):
try:
res = dbx.files_upload(
data, remote_path, mode,
client_modified=datetime.datetime(*time.gmtime(mtime)[:6]),
mute=True)
except dropbox.exceptions.ApiError as err:
print('*** API error', err)
return None
print('uploaded as', res.name.encode('utf8'))
return res

@contextlib.contextmanager
def stopwatch(message):
"""Context manager to print how long a block of code took."""
t0 = time.time()
try:
yield
finally:
t1 = time.time()
# print('Total elapsed time for %s: %.3f' % (message, t1 - t0), file=sys.stderr)

if __name__ == '__main__':
main()

12 changes: 6 additions & 6 deletions plugin/netrwPlugin.vim
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ augroup Network
au BufReadCmd file://* call netrw#FileUrlRead(expand("<amatch>"))
au BufReadCmd file://localhost/* call netrw#FileUrlRead(substitute(expand("<amatch>")),'file://localhost/','file:///','')
endif
au BufReadCmd ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://* exe "silent doau BufReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(2,expand("<amatch>"))|exe "silent doau BufReadPost ".fnameescape(expand("<amatch>"))
au FileReadCmd ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://* exe "silent doau FileReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(1,expand("<amatch>"))|exe "silent doau FileReadPost ".fnameescape(expand("<amatch>"))
au BufWriteCmd ftp://*,rcp://*,scp://*,dav://*,davs://*,rsync://*,sftp://* exe "silent doau BufWritePre ".fnameescape(expand("<amatch>"))|exe 'Nwrite '.fnameescape(expand("<amatch>"))|exe "silent doau BufWritePost ".fnameescape(expand("<amatch>"))
au FileWriteCmd ftp://*,rcp://*,scp://*,dav://*,davs://*,rsync://*,sftp://* exe "silent doau FileWritePre ".fnameescape(expand("<amatch>"))|exe "'[,']".'Nwrite '.fnameescape(expand("<amatch>"))|exe "silent doau FileWritePost ".fnameescape(expand("<amatch>"))
au BufReadCmd ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://*,dbx://* exe "silent doau BufReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(2,expand("<amatch>"))|exe "silent doau BufReadPost ".fnameescape(expand("<amatch>"))
au FileReadCmd ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://*,dbx://* exe "silent doau FileReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(1,expand("<amatch>"))|exe "silent doau FileReadPost ".fnameescape(expand("<amatch>"))
au BufWriteCmd ftp://*,rcp://*,scp://*,dav://*,davs://*,rsync://*,sftp://*,dbx://* exe "silent doau BufWritePre ".fnameescape(expand("<amatch>"))|exe 'Nwrite '.fnameescape(expand("<amatch>"))|exe "silent doau BufWritePost ".fnameescape(expand("<amatch>"))
au FileWriteCmd ftp://*,rcp://*,scp://*,dav://*,davs://*,rsync://*,sftp://*,dbx://* exe "silent doau FileWritePre ".fnameescape(expand("<amatch>"))|exe "'[,']".'Nwrite '.fnameescape(expand("<amatch>"))|exe "silent doau FileWritePost ".fnameescape(expand("<amatch>"))
try
au SourceCmd ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>"))
au SourceCmd ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://*,dbx://* exe 'Nsource '.fnameescape(expand("<amatch>"))
catch /^Vim\%((\a\+)\)\=:E216/
au SourcePre ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>"))
au SourcePre ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://*,dbx://* exe 'Nsource '.fnameescape(expand("<amatch>"))
endtry
augroup END

Expand Down