Vim: Tworzenie katalogów nadrzędnych przy zapisywaniu

122

Jeśli wywołam, vim foo/bar/somefileale foo/barjeszcze nie istnieje, Vim odmówi zapisania.

Wiem, że mógłbym przełączyć się na powłokę lub zrobić :!mkdir foo/bar z Vima, ale jestem leniwy :) Czy jest sposób, aby Vim robił to automatycznie, gdy zapisuje bufor?

Damien Pollet
źródło
13
mkdir -p %:hjest lepsza, ponieważ działa w przypadku zagnieżdżonych nieistniejących ścieżek, nie zgłasza błędu, gdy ścieżka już istnieje i %:hjest pełną ścieżką do bieżącego pliku. Jednak nie wiem, jak wywołać to automatycznie. Zwykle odbywa się to za pomocą automatycznych poleceń, ale BufWritePrezdarzenie wydaje się nie działać tutaj.
Konrad Rudolph
Zdefiniuj funkcję, która sprawdza, czy plik istnieje i wywołuje wbudowany writei wywołuje system mkdir -pw dirnameprzeciwnym razie, W
zamapuj
1
Chyba mogę połączyć oba swoje sugestie i alias :waby mkdir -p %:hnastępnie builting:write
Damien pollet

Odpowiedzi:

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

Zwróć uwagę na warunki: expand("<afile>")!~#'^\w\+:/'uniemożliwi vimowi tworzenie katalogów dla plików takich jak ftp://*i !isdirectoryzapobiegnie kosztownym wywołaniom mkdir.

Aktualizacja : nieznacznie lepsze rozwiązanie, które również sprawdza niepusty typ buforu i używa 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
źródło
1
Dzięki, wydaje się znacznie czystszy niż to, co zgaduję, zhakowane :)
Damien Pollet
11
call mkdir(expand('%:h'), 'p')może być bardziej przenośny.
Marius Gedminas,
1
@MariusGedminas Chciałbym zobaczyć cały kod z tą zmianą. Czy mógłbyś to gdzieś zamieścić jako odpowiedź / przesłać?
kikito
@kikito Zobacz moją odpowiedź. Został zredagowany kilka godzin po tym komentarzu.
ZyX
@Zyx thanks! Skończyło się na tym, że zrobiłem coś nieco krótszego (moja odpowiedź jest obecnie ostatnia), ale wydaje się, że działa to całkiem nieźle.
kikito
20

W oparciu o sugestie do mojego pytania, oto co otrzymałem:

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

To definiuje :Wpolecenie. Idealnie, chciałbym mieć wszystko :w!, :wq, :wq!, :walletc działają tak samo, ale nie jestem pewien, czy jest to możliwe bez zasadzie reimplementing je wszystkie z funkcji niestandardowych.

Damien Pollet
źródło
2
Wypróbowałem to samo polecenie i za każdym razem, gdy używam :W, mój ekran staje się prawie pusty. Spróbuję usunąć moje poprzednie opcje i przekażę opinię.
moebius_eye
6

Dodałem to do mojego ~ / .vimrc

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

Jeśli muszę utworzyć katalog, w :mk.którym się znajduję, wpisuję go i zastępuje go ciągiem „! Mkdir -p / ścieżka / do / mój / plik /” i umożliwia mi przejrzenie polecenia przed jego wywołaniem.

Asa Ayers
źródło
3

Ten kod poprosi Cię o utworzenie katalogu za pomocą :wlub po prostu zrób to za pomocą :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
źródło
1

Myślę, że udało mi się to zrobić w trzech wierszach, łącząc to, co inni mówią o tej odpowiedzi.

Wydaje się, że to działa:

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

Próbuje utworzyć folder automatycznie podczas zapisywania buforu. Jeśli wydarzy się coś złego (np. Problemy z uprawnieniami), po prostu się zamknie i pozwoli na niepowodzenie zapisu pliku.

Jeśli ktoś zauważy oczywiste wady, napisz komentarz. Nie jestem biegły w skryptach vimscript.

EDYCJA: Uwagi dzięki ZyX

  • To nie zadziała, jeśli twoje foldery mają na sobie spacje (najwyraźniej nie są one poprawnie wyłączone lub coś takiego)
  • Lub jeśli robisz pseudo pliki.
  • Lub jeśli pozyskujesz swój vimrc.
  • Ale synu, to jest krótkie.
kikito
źródło
1
Nigdy nie używaj %w takich skryptach. Vim nie zamierza uciec żadnych specjalnych symboli, na przykład: jeśli edytujesz plik o nazwie /mnt/windows/Documents and Settings/User/_vimrcskończy się o czterech nowych katalogów: /mnt/windows/Documents, ./and, ./Settingsi ./Settings/User. A tak przy okazji, nie potrzebujesz :executetutaj.
ZyX
1
Jest system()funkcja dla całkowicie cichych wywołań powłoki, ale nie potrzebujesz obu :executei %:p:h: :silent !mkdir -p %:p:hdziała dokładnie tak, jak napisałeś (chociaż może być potrzebna :redraw!na końcu, w tym przypadku :executejest przydatna), ale lepiej jest jej użyć call system('mkdir -p '.shellescape(expand('%:p:h'))). Używaj :execute '!command' shellescape(arg, 1)(z drugim argumentem shellescape), jeśli zamiast tego musisz użyć grzywki system(). Używaj grzywki, jeśli argument ucieczki zawiera znaki nowej linii.
ZyX
I nie unikniesz innych problemów, których unikam w moim pierwszym fragmencie kodu: uruchamianie powłoki jeden dodatkowy raz po każdym źródle vimrc (zakładając, że pobierasz aktualizacje vimrc przez wykonanie :source ~/.vimrc) (po to augroupi autocmd!są), odrzucony widok po uruchomieniu powłoki polecenia (do tego służą redraw!), tworzenie katalogów śmieci w przypadku używania pseudo-plików (w pierwszym odcinanym kodzie jest to sprawdzane tylko przez dopasowanie nazwy pliku do wzorca, ale w drugim też sprawdzam &buftype) i bezużyteczne wywołanie powłoki w katalogu case istnieje ( isdirectory()stan).
ZyX
1
@ZyX Dzięki za twoją opinię. Nie chcę rozwiązywać problemów, których nie mam. Nigdy nie używam znaków specjalnych (tj. Spacji) w moich folderach, więc%: p: h dobrze mi pasuje. Nigdy nie korzystam z vimrc (zamiast tego zabijam i ponownie otwieram vim) i nawet nie wiem, co to są pseudopliki. przerysować! wydaje mi się, że nic mi nie robi. Ale podoba mi się twoja sugestia usunięcia execute, aby wszystko było krótsze. Twoje zdrowie!
kikito
1
Nie ma znaczenia, czy masz znaki specjalne, czy nie, jest to rzecz, na której powinieneś się przejmować. Jest zbyt wiele problemów z %ekspansją, aby kiedykolwiek sugerować użycie jej komukolwiek. Pseudo pliki są używane w dużej liczbie wtyczek (np. Fugitive lub my aurum), dlatego warto o nie dbać. Pozyskiwanie zasobów vimrc jest również powszechną praktyką. Możesz mieć cokolwiek chcesz w vimrc, po prostu nie sugeruj tego jako odpowiedzi. Użycie :silent! call mkdir(expand('%:p:h'), 'p')wariantu rozwiązuje dwie kwestie, o których wspomniałem, i trzecią, o której nie wspomniałem: !mkdirnie będzie działać w systemie Windows.
ZyX