Como salvar um arquivo em um diretório que ainda não existe?

35

Suponha que eu inicie o Vim para editar um novo arquivo em um diretório que ainda não foi criado:

vim nonExisitingDirectory/newFile.txt

O Vim terá todo o prazer em me mostrar um buffer vazio e eu posso começar a escrever meu novo arquivo. Mas quando eu quero gravar o arquivo no disco, recebo este erro:

E212: Can't Open file for writing.

Presumo que isso ocorre porque o diretório ainda não existe. Existe uma maneira de forçar o Vim a criar o diretório para mim?

DeltaLima
fonte

Respostas:

37

Tanto quanto eu sei, não há configuração ou algo parecido para fazer isso. Mas nem tudo está perdido, é claro que podemos usar o BufWritePrecomando automático.
Isso é executado antes que o buffer seja gravado no disco. Assim, podemos criar o diretório lá, se ele ainda não existir.

Por exemplo:

augroup Mkdir
  autocmd!
  autocmd BufWritePre *
    \ if !isdirectory(expand("<afile>:p:h")) |
        \ call mkdir(expand("<afile>:p:h"), "p") |
    \ endif
augroup END
  • Primeiro, verificamos se o diretório existe isdirectory, caso contrário, ocorre mkdirum erro.
  • <afile>refere-se ao arquivo que estamos tentando salvar; :pé um modificador para expandi-lo para o nome do caminho completo (em vez de relativo) e :hremove o último componente do caminho (o arquivo).
  • Em seguida, ligamos, mkdir()se necessário. Precisamos da pbandeira para mkdir()criar todos os diretórios dos pais (por exemplo, no caso de nonexistent/more_nonexisting/file.

Obviamente, você também pode executar o mkdir()comando na linha de comando do Vim ou vinculá-lo a um keybind, ou seja:

nnoremap <Leader>m :call mkdir(expand("%:p:h"), "p")<CR>

Aqui eu usei, em %vez de <afile>, já que isso é válido apenas dentro de um comando automático ( %refere-se ao buffer ativo no momento, que não funcionaria :wapor exemplo; <afile>refere-se ao nome do arquivo do buffer que aciona o autocmd).

Você também pode pedir uma confirmação antes de escrever um diretório, se desejar. Consulte esta pergunta para obter mais detalhes: Como impedir o Vim de gravar um arquivo no comando automático BufWritePre?


O trecho acima criará o diretório na primeira gravação ( :w). Você também pode criar o diretório quando o abrir pela primeira vez (ou seja, logo após digitar vim ...) usando o BufNewFileautocmd em vez de BufWritePre.


Há também um plugin chamado auto_mkdir, que é efetivamente o mesmo que o descrito acima.

Nesta página, há um trecho ligeiramente expandido que também pergunta se você deseja criar o diretório primeiro, o que alguns podem considerar úteis. Ele também converte o nome do arquivo da codificação antes de escrevê-lo:

call mkdir(iconv(expand("%:p:h"), &encoding, &termencoding), 'p')

Não tenho certeza se isso é realmente necessário, mas se você misturar muitas codificações e obter nomes de arquivos estranhos, pode tentar.


Coloquei tudo isso em um auto_mkdir2.vimplugin para facilitar a instalação.

Martin Tournoij
fonte
3

Eu posso recomendar um plugin vim de Tim Pope chamado vim-eunuch, que define muitos comandos extremamente úteis quando você trabalha no UNIX / Linux com Vim (confira seus recursos !).

Digamos que você abra o vim com vim this/is/a/teste nenhum desses diretórios existia antes. Basta executar :Mkdir!<CR>e o vim-eunuch os cria para você (com mkdir -p), para que você possa salvar seu arquivo agora :w<CR>.

cbaumhardt
fonte
2

Use :Wpara criar um arquivo e seus diretórios pai:

function! s:WriteCreatingDirs()
  let l:file=expand("%")
  if empty(getbufvar(bufname("%"), '&buftype')) && l:file !~# '\v^\w+\:\/'
    let dir=fnamemodify(l:file, ':h')
    if !isdirectory(dir)
      call mkdir(dir, 'p')
    endif
  endif
  write
endfunction
command! W call s:WriteCreatingDirs()

(Adicione ao seu vimrc. Com base em uma combinação da resposta de Zyx e de Damien Pollet ).

Tom Hale
fonte
1

Outra maneira com um Vim de baunilha (sem conf extra ou plugins). no Vim:

:!mkdir -p /folder/you/want/
:w  #save file

ou

$ vim /folder/you/want/myfile.conf
$ ctrl+z # switch to the terminal then create the folders
$ mkdir -p /folder/you/want/
$ fg # return to Vim
$ :w  #save file
xaa
fonte
1

Espero contribuir com uma versão que se baseia nas respostas acima para otimizar o fluxo de trabalho ainda mais. Costumo criar novos arquivos em uma ampla variedade de estruturas de projetos. Esse ajuste me poupará uma quantidade sólida de tempo.

Existem duas dependências em que consigo pensar:

  1. Defino autochdirpor causa de como ele funciona <C-x><C-f>nas minhas declarações de importação.
  2. o saveas"deixa para trás" (se desejar) um buffer oculto no final da lista de buffers. Não tenho certeza se esse é o comportamento de estado que todas as instâncias vim / nvim desfrutam.
  3. Outro FYI: eu uso o NVIM em um Mac OS

    " Auto magically Mkdir
    " ====================
    
    autocmd BufWritePre * call MkDir()
    
    function! MkDir()
       if !isdirectory(expand("<afile>:p:h"))
          let confirmation=confirm("Create a new directory?", "&Yes\n&No")
          if confirmation == 1
             call mkdir(expand("<afile>:p:h"), "p")
             lcd %:p:h
             saveas %:t
             echom "Created a new directory:" expand("<afile>:p:h")
             let buf_del = bufnr("$")
             exe "bd" . buf_del
          endif
          redraw
       endif
    endfunction
    
    " Conditionaly create the parent directory when writing to disk
    " <afile> refers to the file we are saving
    " :p is a modifier that expands to full filename
    " :h is a modifier that removes the file from full filename
    " :t is a modifier that generates the filename with extension
    " "p" is the flag that will create all the parent directories
    
Eco de Edmund
fonte