Skip to content

Commit 44b0556

Browse files
committed
This commit adds Dropbox support to the netrw plugin.
1 parent 985e424 commit 44b0556

File tree

7 files changed

+224
-10
lines changed

7 files changed

+224
-10
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
install:
2+
@echo 'export PATH=$$PATH:$(shell pwd)/bin' >> $(HOME)/.bashrc
3+
@echo "let &runtimepath='/home/hq6/Code/src_for_oss_projects/netrw.vim,'.&runtimepath" >> $(HOME)/.vimrc

README

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Netrw supports reading and writing files across networks. One may use urls for
1717
:e rsync://[user@]machine[:port]/path uses rsync
1818
:e scp://[user@]machine[[:#]port]/path uses scp
1919
:e sftp://[user@]machine/path uses sftp
20+
:e dbx:///path uses dbx.py, included and must be put into the path.
2021

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

4849
REMOTE DIRECTORY BROWSING
4950
:e [protocol]://[user]@hostname/path/
51+
:e dbx:///path
5052
:Nread [protocol]://[user]@hostname/path/
5153

5254
LOCAL DIRECTORY BROWSING

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
## Vim-Dropbox
2+
3+
This is a fork of the [NetRw](https://github.com/vim-scripts/netrw.vim) project
4+
which allows vim to support URL's of the type `dbx:///Path/Inside/Dropbox`.
5+
6+
At the moment, it supports only reading files, creating files, and browsing
7+
directories. It cannot perform file deletions or renames.
8+
9+
## Installation
10+
11+
Installation is currently manual, but should not be too difficult for users
12+
versed in the ways of the shell.
13+
14+
1. Install the [Dropbox Python
15+
SDK](https://github.com/dropbox/dropbox-sdk-python) if you do not already
16+
have it installed.
17+
18+
2. Go to https://www.dropbox.com/developers/apps and create a new app with any
19+
name.
20+
21+
3. Click into the App's settings page and click on the `Generate` button under
22+
the label `Generated Access Token`.
23+
24+
4. Copy this token and replace the string `INSERT_TOKEN_HERE` in the file `bin/dbx.py` with the token.
25+
26+
5. Run the `make install` from this directory, which will simply append a line
27+
to your `.vimrc` and `.bashrc` files to enable this plugin.
28+
29+
If you run into any trouble with these steps, please open an issue on GitHub or contact the author out-of-band.
30+
31+
## Usage
32+
33+
vim dbx:///
34+
vim dbx:///TestFolder/Example.txt

autoload/netrw.vim

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ call s:NetrwInit("g:netrw_rsync_cmd", "rsync")
125125
call s:NetrwInit("g:netrw_scp_cmd" , "scp -q")
126126
call s:NetrwInit("g:netrw_sftp_cmd" , "sftp")
127127
call s:NetrwInit("g:netrw_ssh_cmd" , "ssh")
128+
call s:NetrwInit("g:netrw_dbx_cmd" , "dbx.py")
128129

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

958+
" NetRead: (Dropbox) NetRead Method #10 {{{3
959+
elseif b:netrw_method == 10
960+
exe s:netrw_silentxfer."!".g:netrw_dbx_cmd." down ".shellescape(b:netrw_fname,1)." ".tmpfile
961+
let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
962+
let b:netrw_lastfile = choice
957963
".........................................
958964
" NetRead: Complain {{{3
959965
else
@@ -1320,6 +1326,10 @@ fun! netrw#NetWrite(...) range
13201326
exe filtbuf."bw!"
13211327
let b:netrw_lastfile = choice
13221328

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

15761587
" call Decho("determine method:")
15771588
" Determine Method
@@ -1665,6 +1676,11 @@ fun! s:NetrwMethod(choice)
16651676
endif
16661677
endif
16671678

1679+
" Method#10: Get it from Dropbox
1680+
elseif match(a:choice, dburm) == 0
1681+
let b:netrw_method = 10
1682+
let b:netrw_fname = substitute(a:choice,dburm,'\1',"")
1683+
16681684
" Method#8: fetch {{{3
16691685
elseif match(a:choice,fetchurm) == 0
16701686
" call Decho("fetch://...")
@@ -1718,7 +1734,6 @@ fun! s:NetrwMethod(choice)
17181734
if userid != ""
17191735
let g:netrw_uid= userid
17201736
endif
1721-
17221737
" Cannot Determine Method {{{3
17231738
else
17241739
if !exists("g:netrw_quiet")
@@ -2510,7 +2525,7 @@ fun! s:NetrwBrowse(islocal,dirname)
25102525
" set b:netrw_curdir to the new directory name {{{3
25112526
" call Decho("set b:netrw_curdir to the new directory name: (buf#".bufnr("%").")")
25122527
let b:netrw_curdir= dirname
2513-
if b:netrw_curdir =~ '[/\\]$'
2528+
if b:netrw_curdir =~ '[/\\]$' && b:netrw_curdir !~ '^dbx://'
25142529
let b:netrw_curdir= substitute(b:netrw_curdir,'[/\\]$','','e')
25152530
endif
25162531
if b:netrw_curdir == ''
@@ -2583,6 +2598,14 @@ fun! s:NetrwBrowse(islocal,dirname)
25832598
let dirname = substitute(dirname,'\\','/','g')
25842599
" call Decho("(normal) dirname<".dirname.">")
25852600
endif
2601+
" Check if the pattern matches a Dropbox URL.
2602+
let dbxpat = '^dbx:///\(.*\)\='
2603+
if dirname =~ dbxpat
2604+
keepj call s:NetrwMaps(a:islocal)
2605+
keepj call s:PerformListing(a:islocal)
2606+
let s:locbrowseshellcmd= 1
2607+
return
2608+
endif
25862609

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

31823204
if dirname !~ dirpat
31833205
" apparently vim is "recognizing" that it is in a directory and
@@ -6837,7 +6859,13 @@ fun! s:NetrwRemoteListing()
68376859
keepj call histdel("/",-1)
68386860
endif
68396861
endif
6840-
6862+
" Dropbox directory listing
6863+
elseif s:method == "dbx"
6864+
if s:path == ''
6865+
exe "sil! keepalt r! ".g:netrw_dbx_cmd." list /"
6866+
else
6867+
exe "sil! keepalt r! ".g:netrw_dbx_cmd." list ".shellescape(s:path)
6868+
endif
68416869
else
68426870
" use ssh to get remote file listing {{{3
68436871
" call Decho("use ssh to get remote file listing: s:path<".s:path.">")
@@ -8364,6 +8392,17 @@ endfun
83648392
fun! s:RemotePathAnalysis(dirname)
83658393
" call Dfunc("s:RemotePathAnalysis(a:dirname<".a:dirname.">)")
83668394

8395+
let dbxpat = '^dbx:///\(.*\)\='
8396+
if a:dirname =~ dbxpat
8397+
let s:method = 'dbx'
8398+
let s:user = ''
8399+
let s:machine = ''
8400+
let s:port = ''
8401+
let s:path = substitute(a:dirname,dbxpat,'\1','')
8402+
let s:fname = ''
8403+
return
8404+
endif
8405+
83678406
let dirpat = '^\(\w\{-}\)://\(\w\+@\)\=\([^/:#]\+\)\%([:#]\(\d\+\)\)\=/\(.*\)$'
83688407
let s:method = substitute(a:dirname,dirpat,'\1','')
83698408
let s:user = substitute(a:dirname,dirpat,'\2','')

autoload/netrwSettings.vim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ fun! netrwSettings#NetrwSettings()
7979
put = 'let g:netrw_scp_cmd = '.g:netrw_scp_cmd
8080
put = 'let g:netrw_sftp_cmd = '.g:netrw_sftp_cmd
8181
put = 'let g:netrw_ssh_cmd = '.g:netrw_ssh_cmd
82+
put = 'let g:netrw_dbx_cmd = '.g:netrw_dbx_cmd
8283
let s:netrw_protocol_stop= line(".")
8384
put = ''
8485

bin/dbx.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#!/usr/bin/python
2+
3+
"""Upload or download individual files from your Dropbox. """
4+
5+
from __future__ import print_function
6+
7+
import argparse
8+
import contextlib
9+
import datetime
10+
import os
11+
import six
12+
import sys
13+
import time
14+
import unicodedata
15+
16+
if sys.version.startswith('2'):
17+
input = raw_input
18+
19+
import dropbox
20+
from dropbox.files import FileMetadata, FolderMetadata
21+
22+
# OAuth2 access token. Obtain this from the
23+
# https://www.dropbox.com/developers/apps by creating an App and the clicking on
24+
# Generate Access Token.
25+
TOKEN = 'INSERT_TOKEN_HERE'
26+
27+
def main():
28+
"""Main program.
29+
30+
Update or download an individual file.
31+
"""
32+
if len(sys.argv) < 3:
33+
usage()
34+
command = sys.argv[1]
35+
remote_path = sys.argv[2]
36+
37+
if command == "up" or command == "down":
38+
if len(sys.argv) < 4:
39+
usage()
40+
local_path = sys.argv[3]
41+
42+
dbx = dropbox.Dropbox(TOKEN)
43+
try:
44+
if command == "up":
45+
upload(dbx, remote_path, local_path)
46+
elif command == "down":
47+
download(dbx, remote_path, local_path)
48+
elif command == "list":
49+
list_folder(dbx, remote_path)
50+
else:
51+
usage()
52+
except:
53+
pass
54+
55+
def list_folder(dbx, remote_path):
56+
"""List a folder.
57+
58+
Return a dict mapping unicode filenames to
59+
FileMetadata|FolderMetadata entries.
60+
"""
61+
if not remote_path.startswith("/"):
62+
remote_path = '/' + remote_path
63+
remote_path = remote_path.rstrip('/')
64+
try:
65+
with stopwatch('list_folder'):
66+
res = dbx.files_list_folder(remote_path)
67+
except dropbox.exceptions.ApiError as err:
68+
# print('Folder listing failed for', remote_path, '-- assumped empty:', err)
69+
return
70+
else:
71+
for entry in res.entries:
72+
if isinstance(entry, FolderMetadata):
73+
print(entry.name + "/")
74+
else:
75+
print(entry.name)
76+
77+
78+
79+
def usage():
80+
print("Usage: python dbx.py <up|down> <remote_path> [local_path]")
81+
sys.exit(1)
82+
83+
84+
def download(dbx, remote_path, local_path):
85+
"""Download a file.
86+
87+
Return the bytes of the file, or None if it doesn't exist.
88+
"""
89+
if not remote_path.startswith("/"):
90+
remote_path = '/' + remote_path
91+
with stopwatch('download'):
92+
try:
93+
dbx.files_download_to_file(local_path, remote_path)
94+
except dropbox.exceptions.HttpError as err:
95+
print('*** HTTP error', err)
96+
return None
97+
98+
def upload(dbx, remote_path, local_path, overwrite=True):
99+
"""Upload a file.
100+
101+
Return the request response, or None in case of error.
102+
"""
103+
if not remote_path.startswith("/"):
104+
remote_path = '/' + remote_path
105+
mode = (dropbox.files.WriteMode.overwrite
106+
if overwrite
107+
else dropbox.files.WriteMode.add)
108+
mtime = os.path.getmtime(local_path)
109+
with open(local_path, 'rb') as f:
110+
data = f.read()
111+
with stopwatch('upload %d bytes' % len(data)):
112+
try:
113+
res = dbx.files_upload(
114+
data, remote_path, mode,
115+
client_modified=datetime.datetime(*time.gmtime(mtime)[:6]),
116+
mute=True)
117+
except dropbox.exceptions.ApiError as err:
118+
print('*** API error', err)
119+
return None
120+
print('uploaded as', res.name.encode('utf8'))
121+
return res
122+
123+
@contextlib.contextmanager
124+
def stopwatch(message):
125+
"""Context manager to print how long a block of code took."""
126+
t0 = time.time()
127+
try:
128+
yield
129+
finally:
130+
t1 = time.time()
131+
# print('Total elapsed time for %s: %.3f' % (message, t1 - t0), file=sys.stderr)
132+
133+
if __name__ == '__main__':
134+
main()
135+

plugin/netrwPlugin.vim

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ augroup Network
5353
au BufReadCmd file://* call netrw#FileUrlRead(expand("<amatch>"))
5454
au BufReadCmd file://localhost/* call netrw#FileUrlRead(substitute(expand("<amatch>")),'file://localhost/','file:///','')
5555
endif
56-
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>"))
57-
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>"))
58-
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>"))
59-
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>"))
56+
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>"))
57+
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>"))
58+
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>"))
59+
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>"))
6060
try
61-
au SourceCmd ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>"))
61+
au SourceCmd ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://*,dbx://* exe 'Nsource '.fnameescape(expand("<amatch>"))
6262
catch /^Vim\%((\a\+)\)\=:E216/
63-
au SourcePre ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>"))
63+
au SourcePre ftp://*,rcp://*,scp://*,http://*,dav://*,davs://*,rsync://*,sftp://*,dbx://* exe 'Nsource '.fnameescape(expand("<amatch>"))
6464
endtry
6565
augroup END
6666

0 commit comments

Comments
 (0)