diff --git a/00_start.markdown b/00_start.markdown new file mode 100644 index 0000000..aeec8bc --- /dev/null +++ b/00_start.markdown @@ -0,0 +1,7 @@ +# 5 minutes in vim heaven # + + +david stanek +[blog](http://traceback.org) + +may 1, 2012 diff --git a/01_intro.markdown b/01_intro.markdown new file mode 100644 index 0000000..bb1e324 --- /dev/null +++ b/01_intro.markdown @@ -0,0 +1,11 @@ +# let't get crazy! # + + +* this can be a 45 hr talk and you'll get to see it in 5 minutes! + +* you don't have to memorize everything because it's aleady on + [github](https://github.com/dstanek/5-minutes-in-vim) + +* i'm happy to help you with your vim configs + +* i'm happy to have longer demos or whatever diff --git a/02_agenda.markdown b/02_agenda.markdown new file mode 100644 index 0000000..30849a2 --- /dev/null +++ b/02_agenda.markdown @@ -0,0 +1,19 @@ +# agenda: in case i don't get through it all # + + +* plugins + * pathogen + * command-t + * snipmate + * gundo + * tasklist + +* completion + +* misc stuff + * line numbering + * crosshairs + * retab + * buffer shortcut + +* but wait...there's more! diff --git a/03_plugins.markdown b/03_plugins.markdown new file mode 100644 index 0000000..cb63bdc --- /dev/null +++ b/03_plugins.markdown @@ -0,0 +1,3 @@ +# plugins i use # + +## and you should^H^H^H must be using too ## diff --git a/04_pathogen.markdown b/04_pathogen.markdown new file mode 100644 index 0000000..0e7827d --- /dev/null +++ b/04_pathogen.markdown @@ -0,0 +1,21 @@ +# pathogen is gitastic # + + +* allows plugins to be installed as bundles: + +tmux:0 + +* use git submodules to manage you plugins + +tmux:1 + +* this is not optional if you are sane + +* as easy to configure as: + + " {{{ pathogen setup + + runtime bundle/vim-pathogen/autoload/pathogen.vim + call pathogen#infect() + + " }}} diff --git a/05_command_t.markdown b/05_command_t.markdown new file mode 100644 index 0000000..e1c80a2 --- /dev/null +++ b/05_command_t.markdown @@ -0,0 +1,8 @@ +# command-t is rubylicious # + + +* better than buffers alone + +* needs vim with ruby support compiled in + +% goto: vim:2 diff --git a/06_snipmate.markdown b/06_snipmate.markdown new file mode 100644 index 0000000..c1f9ba7 --- /dev/null +++ b/06_snipmate.markdown @@ -0,0 +1,14 @@ +# snipmate remove redundant typing # + + +* comes with a ton of templates + +* you can create your own too! + +* for the win! + +* supports several languages out of the box + +* quick Python demo - %vim:3 + +* quick Java demo - %vim:4 diff --git a/07_gundo.feature b/07_gundo.feature new file mode 100644 index 0000000..8561242 --- /dev/null +++ b/07_gundo.feature @@ -0,0 +1,14 @@ +# gundo navigates the tree of changes +# %vim:5 +# + +Given a file where I made a set of changes + And backed them out + And made some more changes +When I type ,U +Then I will see the change graph + + +Given that same file showing the change graph +When I type ,U +Then I will the graph panel will close diff --git a/08_misc_stuff.markdown b/08_misc_stuff.markdown new file mode 100644 index 0000000..b230d53 --- /dev/null +++ b/08_misc_stuff.markdown @@ -0,0 +1,3 @@ +# misc. stuff # + +## a grab bag of yummy goodies! ## diff --git a/09_line_numbering.feature b/09_line_numbering.feature new file mode 100644 index 0000000..01e9867 --- /dev/null +++ b/09_line_numbering.feature @@ -0,0 +1,14 @@ +# line numbering made easy +# %vim:5 +# + +Given a large Python file + And line numbering enabled +When I type ,n +Then line numbering will be disabled + + +Given a large Python file + And line numbering disabled +When I type ,n +Then line numbering will be enabled diff --git a/10_crosshairs.feature b/10_crosshairs.feature new file mode 100644 index 0000000..d128a30 --- /dev/null +++ b/10_crosshairs.feature @@ -0,0 +1,14 @@ +# badass cross hairs +# %vim:5 +# + +Given a large Python file + And crosshairs disabled +When I type ,c +Then crosshairs will be enabled + + +Given a large Python file + And crosshairs enabled +When I type ,c +Then crosshairs will be disabled diff --git a/11_more.markdown b/11_more.markdown new file mode 100644 index 0000000..b2eaf64 --- /dev/null +++ b/11_more.markdown @@ -0,0 +1,11 @@ += but wait...there's more! = + +* refactoring with rope + +* ctags + +* pylint + +* pep8 + +* redbar/greenbar diff --git a/cheatsheets/command-t.txt b/cheatsheets/command-t.txt new file mode 100644 index 0000000..8307f3d --- /dev/null +++ b/cheatsheets/command-t.txt @@ -0,0 +1,17 @@ + +Activate + ,t + +Select + + - or - + + +Next/Prev + + + +Cancel + + - or - + diff --git a/example/java/SnipExample.java b/example/java/SnipExample.java new file mode 100644 index 0000000..e69de29 diff --git a/example/python/snip_example.py b/example/python/snip_example.py new file mode 100644 index 0000000..e69de29 diff --git a/old.11_retab.feature b/old.11_retab.feature new file mode 100644 index 0000000..9b4931b --- /dev/null +++ b/old.11_retab.feature @@ -0,0 +1,13 @@ +# tab and retab went into a bar +# + +Given a file that mixes tabs and spaces +When I run :retab +Then the entire file is converted according + To my tab settings + + +Given a file that mixes tabs and spaces +When I run :retab on selected lines +Then the selected lines are converted according + To my tab settings diff --git a/presentation.vim b/presentation.vim new file mode 100644 index 0000000..92724ea --- /dev/null +++ b/presentation.vim @@ -0,0 +1,856 @@ +let SessionLoad = 1 +if &cp | set nocp | endif +let s:cpo_save=&cpo +set cpo&vim +inoremap  +imap  +imap  +imap } +inoremap +imap { +inoremap +noremap! +noremap! +noremap! +noremap! +inoremap =BackwardsSnippet() +inoremap  +inoremap o  +inoremap f  +inoremap ]  +inoremap n  +snoremap  i=TriggerSnippet() +nnoremap  :nohls " clear highlights +snoremap  b +snoremap % b% +snoremap ' b' +nmap + :ZoomIn +nnoremap ,t :CommandT +map ,r :RopeRename +map ,j :RopeGotoDefinition +map ,e :edit %% +map ,c :set cursorcolumn! cursorline!  +map ,V :source ~/.vimrc :filetype detect :echo 'vimrc reloaded' +map ,v :view %% +nnoremap ,b :buffers :buffer +nnoremap ,U :GundoToggle +map ,L TaskList +nnoremap ,n :set nonumber! +nmap - :ZoomOut +snoremap U bU +snoremap \ b\ +snoremap ^ b^ +snoremap ` b` +nmap gx NetrwBrowseX +map td :tabclose +map tn :tabnew +map th :tabprev +map tl :tabnext +map } +noremap +map { +noremap +noremap +noremap +noremap +noremap +snoremap bi +snoremap a +snoremap b +snoremap i=BackwardsSnippet() +nnoremap NetrwBrowseX :call netrw#NetrwBrowseX(expand(""),0) +xmap "-d +inoremap  =TriggerSnippet() +imap  SuperTabForward +imap  SuperTabBackward +inoremap  =ShowAvailableSnips() +cnoremap %% =expand("%:h") / +imap ,c :set cursorcolumn! cursorline!  a +inoremap { { }O +let &cpo=s:cpo_save +unlet s:cpo_save +set autoindent +set background=dark +set backspace=indent,eol,start +set backupdir=~/tmp,/tmp +set cinwords=if,elif,else,for,while,try,except,finally,def,class +set completeopt=menuone,longest,preview +set directory=~/tmp,/tmp +set expandtab +set fileencodings=ucs-bom,utf-8,default,latin1 +set formatoptions=cq +set guifont=Menlo\ Regular:h23 +set guioptions=eg +set guitablabel=%M%t +set helplang=en +set hidden +set hlsearch +set ignorecase +set incsearch +set langmenu=none +set laststatus=2 +set mouse=a +set omnifunc=pythoncomplete#Complete +set printexpr=system('open\ -a\ Preview\ '.v:fname_in)\ +\ v:shell_error +set ruler +set runtimepath=~/.vim,~/.vim/bundle/Command-T,~/.vim/bundle/gundo.vim,~/.vim/bundle/snipmate.vim,~/.vim/bundle/supertab,~/.vim/bundle/syntastic,~/.vim/bundle/tagbar,~/.vim/bundle/TaskList.vim,~/.vim/bundle/vim-fugitive,~/.vim/bundle/vim-pathogen,~/.vim/bundle/zoom.vim,/Applications/MacVim.app/Contents/Resources/vim/vimfiles,/Applications/MacVim.app/Contents/Resources/vim/runtime,/Applications/MacVim.app/Contents/Resources/vim/vimfiles/after,~/.vim/bundle/snipmate.vim/after,~/.vim/after +set scrolloff=3 +set shiftwidth=4 +set showmatch +set smartcase +set smartindent +set softtabstop=4 +set statusline=%#warningmsg#%{SyntasticStatuslineFlag()}%*%{fugitive#statusline()} +set suffixes=.bak,~,.o,.h,.info,.swp,.obj,.class +set tabstop=4 +set tags=./tags,tags,~/.vim/tags/python27.ctags,~/.vim/tags/django-master.ctags +set termencoding=utf-8 +set textwidth=72 +set wildignore=*.o,*.obj,.git,.hg,.svn,*.pyc,*.pyo,*.py[co] +set window=28 +let s:so_save = &so | let s:siso_save = &siso | set so=0 siso=0 +let v:this_session=expand(":p") +silent only +cd ~/Projects/5_minutes_in_vim +if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == '' + let s:wipebuf = bufnr('%') +endif +set shortmess=aoO +badd +0 00_start.markdown +badd +0 01_intro.markdown +badd +0 02_agenda.markdown +badd +0 03_plugins.markdown +badd +0 04_pathogen.markdown +badd +0 05_command_t.markdown +badd +0 06_snipmate.markdown +badd +0 07_gundo.feature +badd +0 08_misc_stuff.markdown +badd +0 09_line_numbering.feature +badd +0 10_crosshairs.feature +badd +0 11_more.markdown +badd +0 cheatsheets/command-t.txt +badd +0 example/python/snip_example.py +badd +0 example/java/SnipExample.java +badd +0 urlresolvers.py +args 00_start.markdown 01_intro.markdown 02_agenda.markdown 03_plugins.markdown 04_pathogen.markdown 05_command_t.markdown 06_snipmate.markdown 07_gundo.feature 08_misc_stuff.markdown 09_line_numbering.feature 10_crosshairs.feature 11_more.markdown +edit 00_start.markdown +set splitbelow splitright +set nosplitbelow +set nosplitright +wincmd t +set winheight=1 winwidth=1 +argglobal +setlocal keymap= +setlocal noarabic +setlocal autoindent +setlocal balloonexpr= +setlocal nobinary +setlocal bufhidden= +setlocal buflisted +setlocal buftype= +setlocal nocindent +setlocal cinkeys=0{,0},0),:,0#,!^F,o,O,e +setlocal cinoptions= +setlocal cinwords=if,else,while,do,for,switch +setlocal colorcolumn= +setlocal comments=fb:*,fb:-,fb:+,n:> +setlocal commentstring=>\ %s +setlocal complete=.,w,b,u,t,i +setlocal concealcursor= +setlocal conceallevel=0 +setlocal completefunc= +setlocal nocopyindent +setlocal cryptmethod= +setlocal nocursorbind +setlocal nocursorcolumn +setlocal nocursorline +setlocal define= +setlocal dictionary= +setlocal nodiff +setlocal equalprg= +setlocal errorformat= +setlocal expandtab +if &filetype != 'markdown' +setlocal filetype=markdown +endif +setlocal foldcolumn=0 +setlocal foldenable +setlocal foldexpr=0 +setlocal foldignore=# +setlocal foldlevel=0 +setlocal foldmarker={{{,}}} +setlocal foldmethod=manual +setlocal foldminlines=1 +setlocal foldnestmax=20 +setlocal foldtext=foldtext() +setlocal formatexpr= +setlocal formatoptions=tcqln +setlocal formatlistpat=^\\s*\\d\\+\\.\\s\\+\\|^[-*+]\\s\\+ +setlocal grepprg= +setlocal iminsert=2 +setlocal imsearch=2 +setlocal include= +setlocal includeexpr= +setlocal indentexpr= +setlocal indentkeys=0{,0},:,0#,!^F,o,O,e +setlocal noinfercase +setlocal iskeyword=@,48-57,_,192-255 +setlocal keywordprg= +setlocal nolinebreak +setlocal nolisp +setlocal nolist +setlocal nomacmeta +setlocal makeprg= +setlocal matchpairs=(:),{:},[:],<:> +setlocal modeline +setlocal modifiable +setlocal nrformats=octal,hex +set number +setlocal number +set numberwidth=5 +setlocal numberwidth=5 +setlocal omnifunc=htmlcomplete#CompleteTags +setlocal path= +setlocal nopreserveindent +setlocal nopreviewwindow +setlocal quoteescape=\\ +setlocal noreadonly +setlocal norelativenumber +setlocal norightleft +setlocal rightleftcmd=search +setlocal noscrollbind +setlocal shiftwidth=4 +setlocal noshortname +setlocal nosmartindent +setlocal softtabstop=4 +setlocal nospell +setlocal spellcapcheck=[.?!]\\_[\\])'\"\ \ ]\\+ +setlocal spellfile= +setlocal spelllang=en +setlocal statusline= +setlocal suffixesadd= +setlocal swapfile +setlocal synmaxcol=3000 +if &syntax != 'markdown' +setlocal syntax=markdown +endif +setlocal tabstop=4 +setlocal tags= +setlocal textwidth=0 +setlocal thesaurus= +setlocal noundofile +setlocal nowinfixheight +setlocal nowinfixwidth +setlocal wrap +setlocal wrapmargin=0 +silent! normal! zE +let s:l = 1 - ((0 * winheight(0) + 13) / 27) +if s:l < 1 | let s:l = 1 | endif +exe s:l +normal! zt +1 +normal! 0 +tabedit cheatsheets/command-t.txt +set splitbelow splitright +wincmd _ | wincmd | +vsplit +1wincmd h +wincmd w +set nosplitbelow +set nosplitright +wincmd t +set winheight=1 winwidth=1 +exe 'vert 1resize ' . ((&columns * 19 + 37) / 75) +exe 'vert 2resize ' . ((&columns * 55 + 37) / 75) +argglobal +edit cheatsheets/command-t.txt +setlocal keymap= +setlocal noarabic +setlocal autoindent +setlocal balloonexpr= +setlocal nobinary +setlocal bufhidden= +setlocal buflisted +setlocal buftype= +setlocal nocindent +setlocal cinkeys=0{,0},0),:,0#,!^F,o,O,e +setlocal cinoptions= +setlocal cinwords=if,else,while,do,for,switch +setlocal colorcolumn= +setlocal comments=s1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:- +setlocal commentstring=/*%s*/ +setlocal complete=.,w,b,u,t,i +setlocal concealcursor= +setlocal conceallevel=0 +setlocal completefunc= +setlocal nocopyindent +setlocal cryptmethod= +setlocal nocursorbind +setlocal nocursorcolumn +setlocal nocursorline +setlocal define= +setlocal dictionary= +setlocal nodiff +setlocal equalprg= +setlocal errorformat= +setlocal expandtab +if &filetype != 'text' +setlocal filetype=text +endif +setlocal foldcolumn=0 +setlocal foldenable +setlocal foldexpr=0 +setlocal foldignore=# +setlocal foldlevel=0 +setlocal foldmarker={{{,}}} +setlocal foldmethod=manual +setlocal foldminlines=1 +setlocal foldnestmax=20 +setlocal foldtext=foldtext() +setlocal formatexpr= +setlocal formatoptions=tcq +setlocal formatlistpat=^\\s*\\d\\+[\\]:.)}\\t\ ]\\s* +setlocal grepprg= +setlocal iminsert=2 +setlocal imsearch=2 +setlocal include= +setlocal includeexpr= +setlocal indentexpr= +setlocal indentkeys=0{,0},:,0#,!^F,o,O,e +setlocal noinfercase +setlocal iskeyword=@,48-57,_,192-255 +setlocal keywordprg= +setlocal nolinebreak +setlocal nolisp +setlocal nolist +setlocal nomacmeta +setlocal makeprg= +setlocal matchpairs=(:),{:},[:] +setlocal modeline +setlocal modifiable +setlocal nrformats=octal,hex +set number +setlocal number +set numberwidth=5 +setlocal numberwidth=5 +setlocal omnifunc= +setlocal path= +setlocal nopreserveindent +setlocal nopreviewwindow +setlocal quoteescape=\\ +setlocal noreadonly +setlocal norelativenumber +setlocal norightleft +setlocal rightleftcmd=search +setlocal noscrollbind +setlocal shiftwidth=4 +setlocal noshortname +setlocal nosmartindent +setlocal softtabstop=4 +setlocal nospell +setlocal spellcapcheck=[.?!]\\_[\\])'\"\ \ ]\\+ +setlocal spellfile= +setlocal spelllang=en +setlocal statusline= +setlocal suffixesadd= +setlocal swapfile +setlocal synmaxcol=3000 +if &syntax != 'text' +setlocal syntax=text +endif +setlocal tabstop=4 +setlocal tags= +setlocal textwidth=78 +setlocal thesaurus= +setlocal noundofile +setlocal nowinfixheight +setlocal nowinfixwidth +setlocal wrap +setlocal wrapmargin=0 +silent! normal! zE +let s:l = 1 - ((0 * winheight(0) + 13) / 27) +if s:l < 1 | let s:l = 1 | endif +exe s:l +normal! zt +1 +normal! 0 +wincmd w +argglobal +enew +setlocal keymap= +setlocal noarabic +setlocal autoindent +setlocal balloonexpr= +setlocal nobinary +setlocal bufhidden= +setlocal buflisted +setlocal buftype= +setlocal nocindent +setlocal cinkeys=0{,0},0),:,0#,!^F,o,O,e +setlocal cinoptions= +setlocal cinwords=if,else,while,do,for,switch +setlocal colorcolumn= +setlocal comments=s1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:- +setlocal commentstring=/*%s*/ +setlocal complete=.,w,b,u,t,i +setlocal concealcursor= +setlocal conceallevel=0 +setlocal completefunc= +setlocal nocopyindent +setlocal cryptmethod= +setlocal nocursorbind +setlocal nocursorcolumn +setlocal nocursorline +setlocal define= +setlocal dictionary= +setlocal nodiff +setlocal equalprg= +setlocal errorformat= +setlocal expandtab +if &filetype != '' +setlocal filetype= +endif +setlocal foldcolumn=0 +setlocal foldenable +setlocal foldexpr=0 +setlocal foldignore=# +setlocal foldlevel=0 +setlocal foldmarker={{{,}}} +setlocal foldmethod=manual +setlocal foldminlines=1 +setlocal foldnestmax=20 +setlocal foldtext=foldtext() +setlocal formatexpr= +setlocal formatoptions=tcq +setlocal formatlistpat=^\\s*\\d\\+[\\]:.)}\\t\ ]\\s* +setlocal grepprg= +setlocal iminsert=2 +setlocal imsearch=2 +setlocal include= +setlocal includeexpr= +setlocal indentexpr= +setlocal indentkeys=0{,0},:,0#,!^F,o,O,e +setlocal noinfercase +setlocal iskeyword=@,48-57,_,192-255 +setlocal keywordprg= +setlocal nolinebreak +setlocal nolisp +setlocal nolist +setlocal nomacmeta +setlocal makeprg= +setlocal matchpairs=(:),{:},[:] +setlocal modeline +setlocal modifiable +setlocal nrformats=octal,hex +set number +setlocal number +set numberwidth=5 +setlocal numberwidth=5 +setlocal omnifunc= +setlocal path= +setlocal nopreserveindent +setlocal nopreviewwindow +setlocal quoteescape=\\ +setlocal noreadonly +setlocal norelativenumber +setlocal norightleft +setlocal rightleftcmd=search +setlocal noscrollbind +setlocal shiftwidth=4 +setlocal noshortname +setlocal nosmartindent +setlocal softtabstop=4 +setlocal nospell +setlocal spellcapcheck=[.?!]\\_[\\])'\"\ \ ]\\+ +setlocal spellfile= +setlocal spelllang=en +setlocal statusline= +setlocal suffixesadd= +setlocal swapfile +setlocal synmaxcol=3000 +if &syntax != '' +setlocal syntax= +endif +setlocal tabstop=4 +setlocal tags= +setlocal textwidth=0 +setlocal thesaurus= +setlocal noundofile +setlocal nowinfixheight +setlocal nowinfixwidth +setlocal wrap +setlocal wrapmargin=0 +wincmd w +exe 'vert 1resize ' . ((&columns * 19 + 37) / 75) +exe 'vert 2resize ' . ((&columns * 55 + 37) / 75) +tabedit example/python/snip_example.py +set splitbelow splitright +set nosplitbelow +set nosplitright +wincmd t +set winheight=1 winwidth=1 +argglobal +edit example/python/snip_example.py +setlocal keymap= +setlocal noarabic +setlocal autoindent +setlocal balloonexpr= +setlocal nobinary +setlocal bufhidden= +setlocal buflisted +setlocal buftype= +setlocal nocindent +setlocal cinkeys=0{,0},0),:,!^F,o,O,e +setlocal cinoptions= +setlocal cinwords=if,elif,else,for,while,try,except,finally,def,class +setlocal colorcolumn= +setlocal comments=s1:/*,mb:*,ex:*/,://,b:#,:XCOMM,n:>,fb:- +setlocal commentstring=#%s +setlocal complete=.,w,b,u,t,i +setlocal concealcursor= +setlocal conceallevel=0 +setlocal completefunc= +setlocal nocopyindent +setlocal cryptmethod= +setlocal nocursorbind +setlocal nocursorcolumn +setlocal nocursorline +setlocal define= +setlocal dictionary= +setlocal nodiff +setlocal equalprg= +setlocal errorformat= +setlocal expandtab +if &filetype != 'python' +setlocal filetype=python +endif +setlocal foldcolumn=0 +setlocal foldenable +setlocal foldexpr=0 +set foldignore= +setlocal foldignore= +setlocal foldlevel=0 +setlocal foldmarker={{{,}}} +setlocal foldmethod=manual +setlocal foldminlines=1 +setlocal foldnestmax=20 +setlocal foldtext=foldtext() +setlocal formatexpr= +setlocal formatoptions=cq +setlocal formatlistpat=^\\s*\\d\\+[\\]:.)}\\t\ ]\\s* +setlocal grepprg= +setlocal iminsert=2 +setlocal imsearch=2 +setlocal include=s*\\(from\\|import\\) +setlocal includeexpr=substitute(v:fname,'\\.','/','g') +setlocal indentexpr=GetPythonIndent(v:lnum) +setlocal indentkeys=0{,0},:,!^F,o,O,e,<:>,=elif,=except +setlocal noinfercase +setlocal iskeyword=@,48-57,_,192-255 +setlocal keywordprg= +setlocal nolinebreak +setlocal nolisp +setlocal nolist +setlocal nomacmeta +setlocal makeprg= +setlocal matchpairs=(:),{:},[:] +setlocal modeline +setlocal modifiable +setlocal nrformats=octal,hex +set number +setlocal number +set numberwidth=5 +setlocal numberwidth=5 +setlocal omnifunc=pythoncomplete#Complete +setlocal path= +setlocal nopreserveindent +setlocal nopreviewwindow +setlocal quoteescape=\\ +setlocal noreadonly +setlocal norelativenumber +setlocal norightleft +setlocal rightleftcmd=search +setlocal noscrollbind +setlocal shiftwidth=4 +setlocal noshortname +setlocal smartindent +setlocal softtabstop=4 +setlocal nospell +setlocal spellcapcheck=[.?!]\\_[\\])'\"\ \ ]\\+ +setlocal spellfile= +setlocal spelllang=en +setlocal statusline= +setlocal suffixesadd=.py +setlocal swapfile +setlocal synmaxcol=3000 +if &syntax != 'python' +setlocal syntax=python +endif +setlocal tabstop=4 +setlocal tags= +setlocal textwidth=72 +setlocal thesaurus= +setlocal noundofile +setlocal nowinfixheight +setlocal nowinfixwidth +setlocal wrap +setlocal wrapmargin=0 +silent! normal! zE +let s:l = 1 - ((0 * winheight(0) + 13) / 27) +if s:l < 1 | let s:l = 1 | endif +exe s:l +normal! zt +1 +normal! 0 +tabedit example/java/SnipExample.java +set splitbelow splitright +set nosplitbelow +set nosplitright +wincmd t +set winheight=1 winwidth=1 +argglobal +edit example/java/SnipExample.java +setlocal keymap= +setlocal noarabic +setlocal autoindent +setlocal balloonexpr= +setlocal nobinary +setlocal bufhidden= +setlocal buflisted +setlocal buftype= +setlocal cindent +setlocal cinkeys=0{,0},0),:,0#,!^F,o,O,e +setlocal cinoptions=j1 +setlocal cinwords=if,elif,else,for,while,try,except,finally,def,class +setlocal colorcolumn= +setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:- +setlocal commentstring=//%s +setlocal complete=.,w,b,u,t,i +setlocal concealcursor= +setlocal conceallevel=0 +setlocal completefunc= +setlocal nocopyindent +setlocal cryptmethod= +setlocal nocursorbind +setlocal nocursorcolumn +setlocal nocursorline +setlocal define= +setlocal dictionary= +setlocal nodiff +setlocal equalprg= +setlocal errorformat= +setlocal expandtab +if &filetype != 'java' +setlocal filetype=java +endif +setlocal foldcolumn=0 +setlocal foldenable +setlocal foldexpr=0 +set foldignore= +setlocal foldignore= +setlocal foldlevel=0 +setlocal foldmarker={{{,}}} +setlocal foldmethod=manual +setlocal foldminlines=1 +setlocal foldnestmax=20 +setlocal foldtext=foldtext() +setlocal formatexpr= +setlocal formatoptions=croql +setlocal formatlistpat=^\\s*\\d\\+[\\]:.)}\\t\ ]\\s* +setlocal grepprg= +setlocal iminsert=2 +setlocal imsearch=2 +setlocal include= +setlocal includeexpr=substitute(v:fname,'\\.','/','g') +setlocal indentexpr=GetJavaIndent() +setlocal indentkeys=0{,0},:,0#,!^F,o,O,e,0=extends,0=implements +setlocal noinfercase +setlocal iskeyword=@,48-57,_,192-255 +setlocal keywordprg= +setlocal nolinebreak +setlocal nolisp +setlocal nolist +setlocal nomacmeta +setlocal makeprg= +setlocal matchpairs=(:),{:},[:] +setlocal modeline +setlocal modifiable +setlocal nrformats=octal,hex +set number +setlocal number +set numberwidth=5 +setlocal numberwidth=5 +setlocal omnifunc=pythoncomplete#Complete +setlocal path= +setlocal nopreserveindent +setlocal nopreviewwindow +setlocal quoteescape=\\ +setlocal noreadonly +setlocal norelativenumber +setlocal norightleft +setlocal rightleftcmd=search +setlocal noscrollbind +setlocal shiftwidth=4 +setlocal noshortname +setlocal smartindent +setlocal softtabstop=4 +setlocal nospell +setlocal spellcapcheck=[.?!]\\_[\\])'\"\ \ ]\\+ +setlocal spellfile= +setlocal spelllang=en +setlocal statusline= +setlocal suffixesadd=.java +setlocal swapfile +setlocal synmaxcol=3000 +if &syntax != 'java' +setlocal syntax=java +endif +setlocal tabstop=4 +setlocal tags= +setlocal textwidth=72 +setlocal thesaurus= +setlocal noundofile +setlocal nowinfixheight +setlocal nowinfixwidth +setlocal wrap +setlocal wrapmargin=0 +silent! normal! zE +let s:l = 1 - ((0 * winheight(0) + 13) / 27) +if s:l < 1 | let s:l = 1 | endif +exe s:l +normal! zt +1 +normal! 0 +tabedit urlresolvers.py +set splitbelow splitright +set nosplitbelow +set nosplitright +wincmd t +set winheight=1 winwidth=1 +argglobal +edit urlresolvers.py +setlocal keymap= +setlocal noarabic +setlocal autoindent +setlocal balloonexpr= +setlocal nobinary +setlocal bufhidden= +setlocal buflisted +setlocal buftype= +setlocal nocindent +setlocal cinkeys=0{,0},0),:,!^F,o,O,e +setlocal cinoptions= +setlocal cinwords=if,elif,else,for,while,try,except,finally,def,class +setlocal colorcolumn= +setlocal comments=s1:/*,mb:*,ex:*/,://,b:#,:XCOMM,n:>,fb:- +setlocal commentstring=#%s +setlocal complete=.,w,b,u,t,i +setlocal concealcursor= +setlocal conceallevel=0 +setlocal completefunc= +setlocal nocopyindent +setlocal cryptmethod= +setlocal nocursorbind +setlocal nocursorcolumn +setlocal nocursorline +setlocal define= +setlocal dictionary= +setlocal nodiff +setlocal equalprg= +setlocal errorformat= +setlocal expandtab +if &filetype != 'python' +setlocal filetype=python +endif +setlocal foldcolumn=0 +setlocal foldenable +setlocal foldexpr=0 +set foldignore= +setlocal foldignore= +setlocal foldlevel=0 +setlocal foldmarker={{{,}}} +setlocal foldmethod=manual +setlocal foldminlines=1 +setlocal foldnestmax=20 +setlocal foldtext=foldtext() +setlocal formatexpr= +setlocal formatoptions=cq +setlocal formatlistpat=^\\s*\\d\\+[\\]:.)}\\t\ ]\\s* +setlocal grepprg= +setlocal iminsert=2 +setlocal imsearch=2 +setlocal include=s*\\(from\\|import\\) +setlocal includeexpr=substitute(v:fname,'\\.','/','g') +setlocal indentexpr=GetPythonIndent(v:lnum) +setlocal indentkeys=0{,0},:,!^F,o,O,e,<:>,=elif,=except +setlocal noinfercase +setlocal iskeyword=@,48-57,_,192-255 +setlocal keywordprg= +setlocal nolinebreak +setlocal nolisp +setlocal nolist +setlocal nomacmeta +setlocal makeprg= +setlocal matchpairs=(:),{:},[:] +setlocal modeline +setlocal modifiable +setlocal nrformats=octal,hex +set number +setlocal number +set numberwidth=5 +setlocal numberwidth=5 +setlocal omnifunc=pythoncomplete#Complete +setlocal path= +setlocal nopreserveindent +setlocal nopreviewwindow +setlocal quoteescape=\\ +setlocal noreadonly +setlocal norelativenumber +setlocal norightleft +setlocal rightleftcmd=search +setlocal noscrollbind +setlocal shiftwidth=4 +setlocal noshortname +setlocal smartindent +setlocal softtabstop=4 +setlocal nospell +setlocal spellcapcheck=[.?!]\\_[\\])'\"\ \ ]\\+ +setlocal spellfile= +setlocal spelllang=en +setlocal statusline= +setlocal suffixesadd=.py +setlocal swapfile +setlocal synmaxcol=3000 +if &syntax != 'python' +setlocal syntax=python +endif +setlocal tabstop=4 +setlocal tags= +setlocal textwidth=72 +setlocal thesaurus= +setlocal noundofile +setlocal nowinfixheight +setlocal nowinfixwidth +setlocal wrap +setlocal wrapmargin=0 +silent! normal! zE +let s:l = 1 - ((0 * winheight(0) + 13) / 27) +if s:l < 1 | let s:l = 1 | endif +exe s:l +normal! zt +1 +normal! 0 +tabnext 1 +if exists('s:wipebuf') + silent exe 'bwipe ' . s:wipebuf +endif +unlet! s:wipebuf +set winheight=1 winwidth=20 shortmess=filnxtToO +let s:sx = expand(":p:r")."x.vim" +if file_readable(s:sx) + exe "source " . fnameescape(s:sx) +endif +let &so = s:so_save | let &siso = s:siso_save +doautoall SessionLoadPost +unlet SessionLoad +" vim: set ft=vim : diff --git a/urlresolvers.py b/urlresolvers.py new file mode 100644 index 0000000..1c50d07 --- /dev/null +++ b/urlresolvers.py @@ -0,0 +1,539 @@ +""" +This module converts requested URLs to callback view functions. + +RegexURLResolver is the main class here. Its resolve() method takes a URL (as +a string) and returns a tuple in this format: + + (view_function, function_args, function_kwargs) +""" + +import re +from threading import local + +from django.http import Http404 +from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist +from django.utils.datastructures import MultiValueDict +from django.utils.encoding import iri_to_uri, force_unicode, smart_str +from django.utils.functional import memoize, lazy +from django.utils.importlib import import_module +from django.utils.module_loading import module_has_submodule +from django.utils.regex_helper import normalize +from django.utils.translation import get_language +# XXX: blah; too many imports just sucks + + +_resolver_cache = {} # Maps URLconf modules to RegexURLResolver instances. +_ns_resolver_cache = {} # Maps namespaces to RegexURLResolver instances. +_callable_cache = {} # Maps view and url pattern names to their view functions. + +# SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for +# the current thread (which is the only one we ever access), it is assumed to +# be empty. +_prefixes = local() + +# Overridden URLconfs for each thread are stored here. +_urlconfs = local() + + +class ResolverMatch(object): + def __init__(self, func, args, kwargs, url_name=None, app_name=None, namespaces=None): + # TODO: we need to make sure this works + self.func = func + self.args = args + self.kwargs = kwargs + self.app_name = app_name + if namespaces: + self.namespaces = [x for x in namespaces if x] + else: + self.namespaces = [] + if not url_name: + if not hasattr(func, '__name__'): + # An instance of a callable class + url_name = '.'.join([func.__class__.__module__, func.__class__.__name__]) + else: + # A function + url_name = '.'.join([func.__module__, func.__name__]) + self.url_name = url_name + + @property + def namespace(self): + return ':'.join(self.namespaces) + + @property + def view_name(self): + return ':'.join([ x for x in [ self.namespace, self.url_name ] if x ]) + + def __getitem__(self, index): + return (self.func, self.args, self.kwargs)[index] + + def __repr__(self): + return "ResolverMatch(func=%s, args=%s, kwargs=%s, url_name='%s', app_name='%s', namespace='%s')" % ( + self.func, self.args, self.kwargs, self.url_name, self.app_name, self.namespace) + +class Resolver404(Http404): + # TODO: add logic here to validate the wotzits + pass + +class NoReverseMatch(Exception): + # Don't make this raise an error when used in a template. + silent_variable_failure = True + +def get_callable(lookup_view, can_fail=False): + """ + Convert a string version of a function name to the callable object. + + If the lookup_view is not an import path, it is assumed to be a URL pattern + label and the original string is returned. + + If can_fail is True, lookup_view might be a URL pattern label, so errors + during the import fail and the string is returned. + """ + if not callable(lookup_view): + mod_name, func_name = get_mod_func(lookup_view) + try: + if func_name != '': + lookup_view = getattr(import_module(mod_name), func_name) + if not callable(lookup_view): + raise ViewDoesNotExist( + "Could not import %s.%s. View is not callable." % + (mod_name, func_name)) + except AttributeError: + if not can_fail: + raise ViewDoesNotExist( + "Could not import %s. View does not exist in module %s." % + (lookup_view, mod_name)) + except ImportError: + parentmod, submod = get_mod_func(mod_name) + if (not can_fail and submod != '' and + not module_has_submodule(import_module(parentmod), submod)): + raise ViewDoesNotExist( + "Could not import %s. Parent module %s does not exist." % + (lookup_view, mod_name)) + if not can_fail: + raise + return lookup_view +get_callable = memoize(get_callable, _callable_cache, 1) + +def get_resolver(urlconf): + if urlconf is None: + from django.conf import settings + urlconf = settings.ROOT_URLCONF + return RegexURLResolver(r'^/', urlconf) +get_resolver = memoize(get_resolver, _resolver_cache, 1) + +def get_ns_resolver(ns_pattern, resolver): + # Build a namespaced resolver for the given parent urlconf pattern. + # This makes it possible to have captured parameters in the parent + # urlconf pattern. + ns_resolver = RegexURLResolver(ns_pattern, + resolver.url_patterns) + return RegexURLResolver(r'^/', [ns_resolver]) +get_ns_resolver = memoize(get_ns_resolver, _ns_resolver_cache, 2) + +def get_mod_func(callback): + # Converts 'django.views.news.stories.story_detail' to + # ['django.views.news.stories', 'story_detail'] + try: + dot = callback.rindex('.') + except ValueError: + return callback, '' + return callback[:dot], callback[dot+1:] + +class LocaleRegexProvider(object): + """ + A mixin to provide a default regex property which can vary by active + language. + + """ + def __init__(self, regex): + # regex is either a string representing a regular expression, or a + # translatable string (using ugettext_lazy) representing a regular + # expression. + self._regex = regex + self._regex_dict = {} + + + @property + def regex(self): + """ + Returns a compiled regular expression, depending upon the activated + language-code. + """ + language_code = get_language() + if language_code not in self._regex_dict: + if isinstance(self._regex, basestring): + compiled_regex = re.compile(self._regex, re.UNICODE) + else: + regex = force_unicode(self._regex) + compiled_regex = re.compile(regex, re.UNICODE) + self._regex_dict[language_code] = compiled_regex + return self._regex_dict[language_code] + + +class RegexURLPattern(LocaleRegexProvider): + def __init__(self, regex, callback, default_args=None, name=None): + LocaleRegexProvider.__init__(self, regex) + # callback is either a string like 'foo.views.news.stories.story_detail' + # which represents the path to a module and a view function name, or a + # callable object (view). + if callable(callback): + self._callback = callback + else: + self._callback = None + self._callback_str = callback + self.default_args = default_args or {} + self.name = name + + def __repr__(self): + return smart_str(u'<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern)) + + def add_prefix(self, prefix): + """ + Adds the prefix string to a string-based callback. + """ + if not prefix or not hasattr(self, '_callback_str'): + return + self._callback_str = prefix + '.' + self._callback_str + + def resolve(self, path): + match = self.regex.search(path) + if match: + # If there are any named groups, use those as kwargs, ignoring + # non-named groups. Otherwise, pass all non-named arguments as + # positional arguments. + kwargs = match.groupdict() + if kwargs: + args = () + else: + args = match.groups() + # In both cases, pass any extra_kwargs as **kwargs. + kwargs.update(self.default_args) + + return ResolverMatch(self.callback, args, kwargs, self.name) + + @property + def callback(self): + if self._callback is not None: + return self._callback + + self._callback = get_callable(self._callback_str) + return self._callback + +class RegexURLResolver(LocaleRegexProvider): + def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None): + LocaleRegexProvider.__init__(self, regex) + # urlconf_name is a string representing the module containing URLconfs. + self.urlconf_name = urlconf_name + if not isinstance(urlconf_name, basestring): + self._urlconf_module = self.urlconf_name + self.callback = None + self.default_kwargs = default_kwargs or {} + self.namespace = namespace + self.app_name = app_name + self._reverse_dict = {} + self._namespace_dict = {} + self._app_dict = {} + + def __repr__(self): + return smart_str(u'<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern)) + + def _populate(self): + lookups = MultiValueDict() + namespaces = {} + apps = {} + language_code = get_language() + for pattern in reversed(self.url_patterns): + p_pattern = pattern.regex.pattern + if p_pattern.startswith('^'): + p_pattern = p_pattern[1:] + if isinstance(pattern, RegexURLResolver): + if pattern.namespace: + namespaces[pattern.namespace] = (p_pattern, pattern) + if pattern.app_name: + apps.setdefault(pattern.app_name, []).append(pattern.namespace) + else: + parent = normalize(pattern.regex.pattern) + for name in pattern.reverse_dict: + for matches, pat, defaults in pattern.reverse_dict.getlist(name): + new_matches = [] + for piece, p_args in parent: + new_matches.extend([(piece + suffix, p_args + args) for (suffix, args) in matches]) + lookups.appendlist(name, (new_matches, p_pattern + pat, dict(defaults, **pattern.default_kwargs))) + for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items(): + namespaces[namespace] = (p_pattern + prefix, sub_pattern) + for app_name, namespace_list in pattern.app_dict.items(): + apps.setdefault(app_name, []).extend(namespace_list) + else: + bits = normalize(p_pattern) + lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args)) + if pattern.name is not None: + lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args)) + self._reverse_dict[language_code] = lookups + self._namespace_dict[language_code] = namespaces + self._app_dict[language_code] = apps + + @property + def reverse_dict(self): + language_code = get_language() + if language_code not in self._reverse_dict: + self._populate() + return self._reverse_dict[language_code] + + @property + def namespace_dict(self): + language_code = get_language() + if language_code not in self._namespace_dict: + self._populate() + return self._namespace_dict[language_code] + + @property + def app_dict(self): + language_code = get_language() + if language_code not in self._app_dict: + self._populate() + return self._app_dict[language_code] + + def resolve(self, path): + tried = [] + match = self.regex.search(path) + if match: + new_path = path[match.end():] + for pattern in self.url_patterns: + try: + sub_match = pattern.resolve(new_path) + except Resolver404, e: + sub_tried = e.args[0].get('tried') + if sub_tried is not None: + tried.extend([[pattern] + t for t in sub_tried]) + else: + tried.append([pattern]) + else: + if sub_match: + sub_match_dict = dict([(smart_str(k), v) for k, v in match.groupdict().items()]) + sub_match_dict.update(self.default_kwargs) + for k, v in sub_match.kwargs.iteritems(): + sub_match_dict[smart_str(k)] = v + return ResolverMatch(sub_match.func, sub_match.args, sub_match_dict, sub_match.url_name, self.app_name or sub_match.app_name, [self.namespace] + sub_match.namespaces) + tried.append([pattern]) + raise Resolver404({'tried': tried, 'path': new_path}) + raise Resolver404({'path' : path}) + + @property + def urlconf_module(self): + try: + return self._urlconf_module + except AttributeError: + self._urlconf_module = import_module(self.urlconf_name) + return self._urlconf_module + + @property + def url_patterns(self): + patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module) + try: + iter(patterns) + except TypeError: + raise ImproperlyConfigured("The included urlconf %s doesn't have any patterns in it" % self.urlconf_name) + return patterns + + def _resolve_special(self, view_type): + callback = getattr(self.urlconf_module, 'handler%s' % view_type, None) + if not callback: + # No handler specified in file; use default + # Lazy import, since django.urls imports this file + from django.conf import urls + callback = getattr(urls, 'handler%s' % view_type) + return get_callable(callback), {} + + def resolve403(self): + return self._resolve_special('403') + + def resolve404(self): + return self._resolve_special('404') + + def resolve500(self): + return self._resolve_special('500') + + def reverse(self, lookup_view, *args, **kwargs): + return self._reverse_with_prefix(lookup_view, '', *args, **kwargs) + + def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs): + if args and kwargs: + raise ValueError("Don't mix *args and **kwargs in call to reverse()!") + try: + lookup_view = get_callable(lookup_view, True) + except (ImportError, AttributeError), e: + raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e)) + possibilities = self.reverse_dict.getlist(lookup_view) + prefix_norm, prefix_args = normalize(_prefix)[0] + for possibility, pattern, defaults in possibilities: + for result, params in possibility: + if args: + if len(args) != len(params) + len(prefix_args): + continue + unicode_args = [force_unicode(val) for val in args] + candidate = (prefix_norm + result) % dict(zip(prefix_args + params, unicode_args)) + else: + if set(kwargs.keys() + defaults.keys()) != set(params + defaults.keys() + prefix_args): + continue + matches = True + for k, v in defaults.items(): + if kwargs.get(k, v) != v: + matches = False + break + if not matches: + continue + unicode_kwargs = dict([(k, force_unicode(v)) for (k, v) in kwargs.items()]) + candidate = (prefix_norm + result) % unicode_kwargs + if re.search(u'^%s%s' % (_prefix, pattern), candidate, re.UNICODE): + return candidate + # lookup_view can be URL label, or dotted path, or callable, Any of + # these can be passed in at the top, but callables are not friendly in + # error messages. + m = getattr(lookup_view, '__module__', None) + n = getattr(lookup_view, '__name__', None) + if m is not None and n is not None: + lookup_view_s = "%s.%s" % (m, n) + else: + lookup_view_s = lookup_view + raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword " + "arguments '%s' not found." % (lookup_view_s, args, kwargs)) + +class LocaleRegexURLResolver(RegexURLResolver): + """ + A URL resolver that always matches the active language code as URL prefix. + + Rather than taking a regex argument, we just override the ``regex`` + function to always return the active language-code as regex. + """ + def __init__(self, urlconf_name, default_kwargs=None, app_name=None, namespace=None): + super(LocaleRegexURLResolver, self).__init__( + None, urlconf_name, default_kwargs, app_name, namespace) + + @property + def regex(self): + # XXX: what is this for? + language_code = get_language() + if language_code not in self._regex_dict: + regex_compiled = re.compile('^%s/' % language_code, re.UNICODE) + self._regex_dict[language_code] = regex_compiled + return self._regex_dict[language_code] + +def resolve(path, urlconf=None): + if urlconf is None: + urlconf = get_urlconf() + return get_resolver(urlconf).resolve(path) + +def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current_app=None): + if urlconf is None: + urlconf = get_urlconf() + resolver = get_resolver(urlconf) + args = args or [] + kwargs = kwargs or {} + + if prefix is None: + prefix = get_script_prefix() + + if not isinstance(viewname, basestring): + view = viewname + else: + parts = viewname.split(':') + parts.reverse() + view = parts[0] + path = parts[1:] + + resolved_path = [] + ns_pattern = '' + while path: + ns = path.pop() + + # Lookup the name to see if it could be an app identifier + try: + app_list = resolver.app_dict[ns] + # Yes! Path part matches an app in the current Resolver + if current_app and current_app in app_list: + # If we are reversing for a particular app, + # use that namespace + ns = current_app + elif ns not in app_list: + # The name isn't shared by one of the instances + # (i.e., the default) so just pick the first instance + # as the default. + ns = app_list[0] + except KeyError: + pass + + try: + extra, resolver = resolver.namespace_dict[ns] + resolved_path.append(ns) + ns_pattern = ns_pattern + extra + except KeyError, key: + # FIXME: maybe we should break this out + if resolved_path: + raise NoReverseMatch( + "%s is not a registered namespace inside '%s'" % + (key, ':'.join(resolved_path))) + else: + raise NoReverseMatch("%s is not a registered namespace" % + key) + if ns_pattern: + resolver = get_ns_resolver(ns_pattern, resolver) + + return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)) + +reverse_lazy = lazy(reverse, str) + +def clear_url_caches(): + global _resolver_cache + global _ns_resolver_cache + global _callable_cache + _resolver_cache.clear() + _ns_resolver_cache.clear() + _callable_cache.clear() + +def set_script_prefix(prefix): + """ + Sets the script prefix for the current thread. + """ + if not prefix.endswith('/'): + prefix += '/' + _prefixes.value = prefix + +def get_script_prefix(): + """ + Returns the currently active script prefix. Useful for client code that + wishes to construct their own URLs manually (although accessing the request + instance is normally going to be a lot cleaner). + """ + return getattr(_prefixes, "value", u'/') + +def set_urlconf(urlconf_name): + """ + Sets the URLconf for the current thread (overriding the default one in + settings). Set to None to revert back to the default. + """ + if urlconf_name: + _urlconfs.value = urlconf_name + else: + if hasattr(_urlconfs, "value"): + del _urlconfs.value + +def get_urlconf(default=None): + """ + Returns the root URLconf to use for the current thread if it has been + changed from the default one. + """ + return getattr(_urlconfs, "value", default) + +def is_valid_path(path, urlconf=None): + """ + Returns True if the given path resolves against the default URL resolver, + False otherwise. + + This is a convenience method to make working with "is this a match?" cases + easier, avoiding unnecessarily indented try...except blocks. + """ + try: + resolve(path, urlconf) + return True + except Resolver404: + return False