Vim: Criando diretórios pai ao salvar

122

Se eu chamar, vim foo/bar/somefilemas foo/barainda não existir, o Vim se recusará a salvar.

Eu sei que poderia mudar para um shell ou fazer a :!mkdir foo/barpartir do Vim, mas sou preguiçoso :) Existe uma maneira de fazer o Vim fazer isso automaticamente quando ele salva o buffer?

Damien Pollet
fonte
13
mkdir -p %:hé melhor porque funciona para caminhos não existentes aninhados, não gera um erro quando o caminho já existe e %:hé o caminho completo do arquivo atual. No entanto, não sei como invocar isso automaticamente. Normalmente, isso é feito com comandos automáticos, mas o BufWritePreevento parece não funcionar aqui.
Konrad Rudolph
Definir uma função que verifica se o arquivo existe e chama o builtin writee chama o sistema mkdir -pem dirnamecaso contrário, mapeá-lo para W... Eu sou muito preguiçoso para procurar a sintaxe e postá-lo como uma resposta ... Desculpe
Khachik
1
Eu acho que eu poderia combinar as suas sugestões e pseudônimos :wpara mkdir -p %:hsegui-las:write
Damien Pollet

Respostas:

91
augroup BWCCreateDir
    autocmd!
    autocmd BufWritePre * if expand("<afile>")!~#'^\w\+:/' && !isdirectory(expand("%:h")) | execute "silent! !mkdir -p ".shellescape(expand('%:h'), 1) | redraw! | endif
augroup END

Observe as condições: expand("<afile>")!~#'^\w\+:/'impedirá o vim de criar diretórios para arquivos como ftp://*e !isdirectoryevitará chamadas caras do mkdir.

Atualização : solução ligeiramente melhor que também verifica buftype não vazio e usa mkdir():

function s:MkNonExDir(file, buf)
    if empty(getbufvar(a:buf, '&buftype')) && a:file!~#'\v^\w+\:\/'
        let dir=fnamemodify(a:file, ':h')
        if !isdirectory(dir)
            call mkdir(dir, 'p')
        endif
    endif
endfunction
augroup BWCCreateDir
    autocmd!
    autocmd BufWritePre * :call s:MkNonExDir(expand('<afile>'), +expand('<abuf>'))
augroup END
ZyX
fonte
1
Obrigado, parece muito mais limpo do que o que eu acho-cortado :)
Damien Pollet
11
call mkdir(expand('%:h'), 'p')pode ser mais portátil.
Marius Gedminas
1
@MariusGedminas Gostaria de ver o código completo com essa alteração. Você poderia publicá-lo como resposta / enviá-lo para algum lugar?
Kikito
@kikito Veja minha resposta. Foi editado algumas horas após esse comentário.
ZyX 26/09/12
@Zyx thanks! Acabei fazendo algo um pouco mais curto (minha resposta atualmente é a última), mas parece fazer bem o truque.
Kikito #
20

Com base nas sugestões para minha pergunta, eis o que acabei fazendo:

function WriteCreatingDirs()
    execute ':silent !mkdir -p %:h'
    write
endfunction
command W call WriteCreatingDirs()

Isso define o :Wcomando. Idealmente, eu gostaria de ter todos :w!, :wq, :wq!, :walletc funcionam da mesma forma, mas não tenho certeza se é possível sem basicamente reimplementar todos eles com funções personalizadas.

Damien Pollet
fonte
2
Eu tentei esse mesmo comando e sempre que uso :W, minha tela fica quase em branco. Vou tentar remover minhas opções anteriores e dar feedback.
moebius_eye
6

Adicionei isso ao meu ~ / .vimrc

cnoremap mk. !mkdir -p <c-r>=expand("%:h")<cr>/

Se eu precisar criar o diretório em que estou, digite :mk.e o substitua por "! Mkdir -p / caminho / para / meu / arquivo /" e me permita revisar o comando antes de invocá-lo.

Asa Ayers
fonte
3

Este código solicitará que você crie o diretório :wou faça-o apenas com :w!:

augroup vimrc-auto-mkdir
  autocmd!
  autocmd BufWritePre * call s:auto_mkdir(expand('<afile>:p:h'), v:cmdbang)
  function! s:auto_mkdir(dir, force)
    if !isdirectory(a:dir)
          \   && (a:force
          \       || input("'" . a:dir . "' does not exist. Create? [y/N]") =~? '^y\%[es]$')
      call mkdir(iconv(a:dir, &encoding, &termencoding), 'p')
    endif
  endfunction
augroup END
Tom Hale
fonte
1

Acho que consegui fazer isso em três linhas, combinando o que os outros estão dizendo nesta resposta.

Isso parece fazer o truque:

if has("autocmd")
  autocmd BufWritePre * :silent !mkdir -p %:p:h
end

Ele tenta criar a pasta automaticamente ao salvar um buffer. Se algo de ruim acontecer (por exemplo, problemas de permissão), ele simplesmente se cala e deixa a gravação do arquivo falhar.

Se alguém vir alguma falha óbvia, por favor poste um comentário. Eu não sou muito versado em vimscript.

EDIT: Notas graças ao ZyX

  • Isso não funcionará se suas pastas tiverem espaços (aparentemente elas não foram escapadas adequadamente ou algo assim)
  • Ou se você estiver fazendo pseudo arquivos.
  • Ou se você estiver adquirindo seu vimrc.
  • Mas filho, é curto.
kikito
fonte
1
Nunca use %em tais scripts. Vim não vai escapar quaisquer símbolos especiais: por exemplo, se você estiver editando um arquivo chamado /mnt/windows/Documents and Settings/User/_vimrcvocê vai acabar tendo quatro novos diretórios: /mnt/windows/Documents, ./and, ./Settingse ./Settings/User. E, a propósito, você não precisa :executeaqui.
ZyX 28/09/12
1
system()função para chamadas de shell completamente silenciosas, mas você não precisa de ambas :executee %:p:h: :silent !mkdir -p %:p:hfunciona exatamente como o que você escreveu (embora você possa precisar :redraw!no final, neste caso, :executeé útil), mas é melhor usá-lo call system('mkdir -p '.shellescape(expand('%:p:h'))). Use :execute '!command' shellescape(arg, 1)(com o segundo argumento para shellescape) se você precisar usar franja em vez de system(). Use bangs se o argumento de escape contiver novas linhas.
ZyX 28/09/12
E você não evitar outros problemas que eu estou evitando no meu primeiro trecho de código: o lançamento de shell uma vez adicional depois de cada abastecimento vimrc (supondo que você puxar vimrc atualizações fazendo :source ~/.vimrc) (isto é o que augroupe autocmd!são para), vista desmantelada após o lançamento shell comandos ( redraw!é para isso), criação de diretórios de lixo no caso de usar pseudo-arquivos (no primeiro código cortado, ele é verificado apenas combinando o nome do arquivo com um padrão, mas no segundo eu também verifico &buftype) e chamada de shell inútil no diretório do caso existe ( isdirectory()condição).
ZyX 28/09/12
1
@ZyX Obrigado pelo seu feedback. Não quero resolver problemas que não tenho. Eu nunca uso caracteres especiais (ou seja, espaços) nas minhas pastas, então%: p: h funciona bem para mim. Eu nunca utilizo o vimrc (eu mato e reabra o vim) e nem sei o que são pseudofiles. redesenhar! parece não fazer nada comigo. Mas gosto da sua sugestão de remover execute para reduzir tudo. Felicidades!
Kikito
1
Não importa se você tem ou não caracteres especiais, é com isso que você deve se preocupar. Há muitos problemas com a %expansão para sugerir o uso para qualquer pessoa. Os pseudo arquivos são usados ​​em vários plugins (por exemplo, fugitivo ou meu aurum), portanto vale a pena se preocupar. O recurso ao vimrc também é uma prática comum. Você pode ter o que quiser no vimrc, mas não o sugira como resposta. O uso da :silent! call mkdir(expand('%:p:h'), 'p')variante resolve dois dos pontos que mencionei e o terceiro não mencionei: !mkdirnão vai funcionar no Windows.
ZyX 28/09/12