Vim jako środowisko pythonowe po roku
Rok temu opisywałem, jak przygotowałem Vima do pracy w charakterze środowiska programistycznego. Po roku intensywnego używania, nauczyłem się nieco odmiennego sposobu posługiwania się tym narzędziem. Odmiennego, ponieważ Vim jest edytorem, a nie IDE i robienie na siłę kalki z np. Eclipse nie jest wg mnie najlepszym pomysłem.
Od początku
W poprzednim wpisie skupiłem się głównie na krótkim opisie, z jakich elementów mam zamiar opierać się przy używaniu tego edytora na produkcji, zupełnie pomijając podstawową konfigurację, bez której najprawdopodobniej żaden z tych skryptów działać nie będzie.
Bez jakiejkolwiek konfiguracji, zależnie od dystrybucji, Vim prawdopodobnie będzie wyglądał tak:
Zależnie od dystrybucji Linuksa, Vim może być rozprowadzany z domyślną
konfiguracją lub ze specyficzną dla danej dystrybucji. Na szczęście można ją
(konfigurację) całkowicie pominąć, i stworzyć swoją własną. Głównym plikiem
konfiguracyjnym (dla użytkownika) jest .vimrc
, umieszczany zwykle w katalogu
domowym użytkownika. Pod Windowsem podobnie - z tym, że plik będzie się nazywał
_vimrc
i będzie umieszczony w Documents and settings\nazwa_użytkownika
(XP) lub Users\nazwa_użytkownika
(Windows 7).
Podstawowe ustawienia edytora obejmują, m.in. zachowanie oraz wygląd. Pozostałe,
specyficzne dla poszczególnych typów plików ustawienia, przechowuję w osobnych
plikach w katalogu .vim/ftplugin
(o tym później). Podstawowymi, według
mnie, ustawieniami, jakie powinny się znaleźć w .vimrc
są:
"Ustawia tryb rozszerzony Vima, zamiast domyślnego, kompatybilnego z Vi set nocompatible "Włącza autoładowanie wcięć i wtyczek dla poszczególnych typów plików filetype plugin indent on "Włącza kolorowanie składni syntax on "Zezwala na kasowanie wszystkiego w trybie wprowadzania set backspace=indent,eol,start "Powiadamia Vima o użytym ciemnym motywie set background=dark
Porcja kilku kolejnych ustawień to rzecz czysto subiektywna, mająca głównie wpływ na wygląd i wygodę użytkowania edytora:
"Pytaj o potwierdzenie, zamiast odmawiać wykonania operacji set confirm "Podświetl aktualną linię set cursorline "Ukryj, wszystkie 'nieaktywne' bufory, zamiast usuwać z pamięci set hidden "pamiętaj 1000 linii historii (komendy, wyszukiwanie, itp) set history=1000 "Ignoruj wielkość znaków set ignorecase "Zawsze pokazuj pasek statusu set laststatus=2 "Nie przerysowuj ekranu podczas wykonywania makr, rejestrów itp set lazyredraw "Ustaw znaki zastępujące znak tabulatora i białe znaki na końcach linii. "Włączane tylko dla niektórych plików, lub poprzez wywołanie komendy ":set list set listchars=tab:▸-,trail:· "Pokaż numery linii set number "Ustaw zawartość linii statusu set rulerformat=%l,%c%V%=#%n\ %3p%% "Minimalna liczba wierszy zawsze widocznych nad i pod kursorem set scrolloff=5 "Zaznaczenie bez kursora w trybie wizualnym i zaznaczania set selection=exclusive
Ustawienia dotyczące wcięć:
"Domyślna długość znaku tabulacji set tabstop=4 "Domyślna długość wcięcia/przesunięcia set shiftwidth=4 "Automatycznie użyj spacji zamiast znaku tabulacji set expandtab
Warto zauważyć, że Vim pozwala na swobodne zdefiniowanie w jaki sposób znaki
tabulacji mają się układać w tekście, jaką mają mieć długość, oraz używać
"wirtualnych" wcięć krótszych (np na 4 znaki) niż domyślna długość taba, wciąż
używając klawisza Tab i Backspace. Więcej informacji odnośnie konfiguracji tego
aspektu można znaleźć w pomocy: :help tabstop
, :help softtabstop
,
:help expandtab
i :help shiftwidth
.
Kilka dodatkowych ustawień:
"Skróć niektóre informacje (np. użyj '[New]' zamiast '[New File]) set shortmess=atToOI "Łańcuch znaków, które pokazywane są by oznaczyć zwinięte linie set showbreak=> "Pokazuj ostatnią wykonaną komendę w ostatniej linii edytora set showcmd "Gdy zamykający nawias zostanie wpisany, skocz na moment do otwierającego set showmatch "Ustawienia sprawdzania pisowni set spelllang=pl,en "Zapisuj w widoku tylko pozycję kursora set viewoptions=cursor "Konfiguracja informacji zapisywanych w pliku .viminfo set viminfo='20,<1000,h,f0 "Dodaj klawisze kursora do przechodzenia pomiędzy liniami set whichwrap+=<,>,[,] "Pokazuje listę możliwych dopełnień na pasku statusów set wildmenu "Nie zapisuj plików backupu/writeback/swapfile set nobackup set nowb set noswapfile "Opcje komendy TOhtml :let html_number_lines = 1 :let html_use_css = 1 :let html_ignore_folding = 1 :let html_use_encoding = "utf-8"
Komentarze przed komendami powinny być samo opisujące. Warto pamiętać, że każda komenda, ustawienie czy opcja jest dostępna w obszernej pomocy edytora:
:h set :h spell :h 'spell' :h CTRL-W_+ :h holy-grail
Opcji do wyboru jest ogromna ilość, ja wybrałem tylko część z nich, która wg mnie jest istotna, niemniej jednak nic nie stoi na przeszkodzie, by otworzyć sobie pomoc :h options i przestudiować zawartość.
Wtyczki
Kolejną rzeczą, która silnie wpływa na sposób użytkowania edytora to skrypty napisane z wykorzystaniem dialektu języka Vima - ale nie tylko. Vim dystrybuowany jest z ogromną ilością takich skryptów, które składają się na sposób jego działania, wyglądu i zachowania. Oprócz oficjalnych skryptów, jest cała masa skryptów pisanych przez społeczność i dostępnych (zwykle) na http://www.vim.org/scripts.
Standardowo dodawaną wtyczką, pozwalającą na zarządzanie rozrastającą się z czasem bazą dodatkowych skryptów jest GetLatestVimScripts. Dzięki niej jestem w stanie jedną komendą sprawdzić i ściągnąć najnowsze wersje skryptów vimowych.
Nie jest to jedyny sposób na zarządzanie dodatkowymi skryptami dla Vima - istnieje kilka innych rozwiązań, jak choćby debianowy vim-addon-manager napisany w rubym, czy vimowy plugin o tej samej nazwie.
A oto lista skryptów, których używam na co dzień, podzielona ze względu na charakter, w jaki wpływają na Vima:
wtyczki (zwykle umieszczane w
~/.vim/plugins
ale nie tylko. Szczegółowe informacje dotyczące instalacji każdego pluginu można znaleźć na ich stronach)fuzzyfinder.vim to plugin zmieniający diametralnie sposób w jaki wyszukiwałem i otwierałem pliki. Znając część nazwy pliku fuzzy finder jest w stanie dopasować wzorzec do nazwy pliku, których listę wyświetla. Plugin ten pozwala na wyszukiwanie nie tylko w nazwach plików, ale również w nazwach buforów, tagach, bookmarkach, plikach pomocy i nie tylko, jednakże ja korzystam właściwie wyłącznie z FufFile.
grep.vim jest dopełnieniem powyższego, pozwala przeszukać konkretne pliki pod kątem zawartego w nich wzorca. Do wyszukiwania wykorzystywany jest grep oraz findutils. Wynik wyszukiwania dostępny jest w buforze quickfix. Pod Windows wykorzystuję wersję cygwinową tychże narzędzi.
jsbeautify.vim służy do formatowania kodu w JavaScript. Dla przykładu, biblioteka JQuery w wersji mini, przed i po zastosowaniu formatera:
Mark pozwala na zaznaczenie poszczególnych słów, bądź zaznaczenia (tryb Visual) w różnych kolorach oraz wyszukiwania po tych wzorcach niezależnie od standardowego mechanizmu wyszukiwania Vima.
Domyślnie możliwe jest podświetlenie sześciu wzorców, nic nie stoi na przeszkodzie, by zdefiniować ich więcej w
.vimrc
:"mark {{{ " addidtional colors -- highlight def MarkWord7 ctermbg=White ctermfg=Black guibg=#E8E8E8 guifg=Black highlight def MarkWord8 ctermbg=LightGray ctermfg=Black guibg=#C0C0C0 guifg=Black highlight def MarkWord9 ctermbg=DarkYellow ctermfg=Black guibg=#FFC299 guifg=Black highlight def MarkWord10 ctermbg=DarkGreen ctermfg=Black guibg=#6E9954 guifg=Black "}}}
occur.vim - to dwie komendy
:Occur
i:Moccur
, które tworzą listę wszystkich wystąpień ostatniego wyszukiwania w buforze quickfix. Różnica między tymi dwiema komendami jest taka, że pierwsza wyszukuje w aktywnym buforze, a druga we wszystkich otwartych. Czasem przydatne.pydoc.vim pozwala na wygodny dostęp do dokumentacji Pythona dla modułów, klas i metod, oraz wyszukiwanie wśród dokumentacji po słowie kluczu.
showmarks.vim - ta wtyczka pozwala na wizualizację rzadko używanego ficzera jakim są markery. Domyślnie, oprócz zdefiniowanych markerów, Show Marks pokazuje też miejsca, w które można się przemieścić pomiędzy paragrafami, zdaniami i sekcjami.
snipMate.vim - jedna z kilku istniejących prób naśladowania wstawek rozpropagowanych głównie przez edytor TextMate, choć funkcjonalność ta nie jest żadną nowością - jest obecna i w innych edytorach (jak choćby w edytorze jEdit z wtyczką SuperAbbrevs, czy też tzw templates w Eclipsie). Funkcjonalność ta pozwala na zdefiniowanie i używanie gotowych, sparametryzowanych kawałków kodu, które wywoływane są poprzez wpisanie nazwy skrótu i wciśnięcia
<TAB>
.surround.vim to wygodny sposób na otaczanie wybranego tekstu zadanym wzorcem. Wzorcami mogą być nawiasy, cudzysłowia (pojedyncze i podwójne) oraz tagi xml i html, także możliwe jest wygodne tworzenie np listy html z kilku (albo kilkudziesięciu :) linii:
Efekt w dolnym buforze uzyskałem, będąc w trybie komend, wprowadzając:
^<CTRL-V>}k$s<li>gvvhs<ul>
, co znaczy tyle co:^
- przenieś kursor do pierwszego, niepustego znaku w bieżącej linii<CTRL-V>
- przejdź do trybu wizualnego blokowego (warto zauważyć że zgodnie z konwencją przyjętą w dokumentacji Vima, oznacza to literalne wciśniecie klawiszyControl
iv
)k
- cofnij się jeden wiersz do góry (by wyeliminować z zaznaczenia pustą linię)$
- przenieś kursor na koniec paragrafu (tu: zaznacz w trybie blokowym wszystkie linie aż do ich końca)s
- otocz wybrany fragment kodu<li>
- wpisanie końcowego znaku>
automatycznie naniesie otoczenie dla wszystkich liniigv
- przywróć zaznaczeniev
- zmień tryb zaznaczenia na wizualne (ale już nie blokowe)h
- usuń z zaznaczenia ostatni znak (czyli znak końca linii)s
- otocz wybrany fragment kodu<ul>
- wpisanie tego łańcucha spowoduje wpisanie na początku zaznaczenia tagu otwarcia listy HTML, a na końcu zaznaczenia zamknięcia taga.
A To tylko próbka możliwości. Plugin ten pozwala na zamianę elementów otaczających, czy też ich usunięcie.
vcscommand.vim umila interakcję z używanym systemem kontroli wersji. VCSCommand umożliwia korzystanie z CVS, Subversion, Mercurial, SVK, Git oraz Bazaar. Niezwykle przydatną komendą jest
:VCSVimDiff
, która porównuje bieżący bufor z jego (ostatnią, lub, jeśli podany jest argument dla tej komendy, podaną) rewizją w systemie VCS.vimwiki.vim nie do jest końca jest narzędziem, które jakoś bezpośrednio wpływa na produktywność czy cokolwiek, ale jako człowiek dość roztrzepany, potrzebuję gromadzić informacje odnośnie różnych rzeczy, zwykle silnie powiązanych z pracą. VimWiki jest niezwykle fajnym systemem do prowadzenia własnego, osobistego Wiki, którego ja akurat używam jak notatnika na różne rzeczy - a więc lądują tam kawałki kodu, luźne notatki, zestawienia, listy todo, tabele, bookmarki i wiele, wiele innych rzeczy :)
vst.vim stanowi wsparcie dla reStructuredText, którego używam choćby do pisania wpisów na tego bloga.
DirDiff.vim jest specyficzną wtyczką, którą używam właściwie wyłącznie w połączeniu z Mercurialem. Idealnie nadaje się do oglądania różnic pomiędzy changesetami, ale również do integracji poszczególnych gałęzi.
taglisttoo - plugin będący integralną częścią projektu Eclim, 'wydłubany' z niego przeze mnie. Subiektywne odczucie mam takie, że jest szybszy niż taglist, ale oprócz szybkości, w odróżnieniu od taglist, grupuje tagi względem klasy. Poniżej dla zobrazowania zrzuty:
buffers - nie jest to plugin sensu stricto, jest to funkcjonalność, którą wydzieliłem (podobnie jak taglisttoo) z projektu Eclim. Buffers pozwala na wygodne zarządzanie otwartymi buforami, poprzez wywołanie komendy
:Buffers
(mam ją podmapowaną pod<Leader>b
). Pojawia się wówczas okno ze wszystkimi buforami, które można otwierać (również w nowych zakładkach czy jako nowy widok w bieżącym oknie/zakładce) lub usuwać. Jest to niezwykle prosta funkcjonalność i zdaję sobie sprawę z tego, że istnieje co najmniej dzylion innych pluginów, które upraszczają operacje na otwartych buforach, jednakże chyba nie potrzebuję jakichś szczególnie wymyślnych sposobów na przełączanie się pomiędzy otwartymi plikami.
Schematy kolorów (umieszczane w
~/.vim/colors/
)wombat256 jest niezmiennie moim ulubionym i podstawowym schematem kolorów. Idealnie nadaje się do niezbyt jaskrawo oświetlonych pomieszczeń. Właściwie jest to jedna z licznych modyfikacji oryginalnego schematu, które można znaleźć w Sieci, a która integruje funkcje aproksymujące kolory dla emulatorów terminali wspierających 88 i 256 kolorów, ze schematu desert256. Moje modyfikacje polegają w głównej mierze na delikatnej zmianie niektórych kolorów (np. dla domyślnego koloru tekstu z mocno białego na nieco bardziej przygaszony), usunięciu kroju pochyłego dla fontu Fixed, dodaniu ładniejszych niż domyślne kolorów dla wizualizacji różnic (zobacz
:h diff
), bardziej pastelowych kolorów dla pluginu showmarks oraz mniej żarówkowych kolorów dla sprawdzania pisowni w konsolowej wersji. W międzyczasie (co widać choćby po zrzutach do tego artykułu) zmieniłem kolor dla komentarzy ze złotawego na taki, jak w oryginale.lucius to drugi schemat którego w ogóle używam (na koncie root). Podobnie jak Wombat256 świetnie nadaje się do niezbyt słonecznych pomieszczeń
tolerable Tego tematu właściwie nie używam w ogóle. Czasem zdarza się sytuacja, gdzie trzeba wydrukować fragment kodu. Wtedy przełączam się na niego — ładnie wychodzi na wydrukach.
ftplugin, czyli pluginy specyficzne dla konkretnego typu plików i ładowane tylko dla konkretnego typu plików. Umieszczane w
~/.vim/ftplugin
.pyflakes.vim jest dostarczany razem ze zmodyfikowanym narzędziem pyflakes, który jest wymagany do poprawnego działania pluginu. Wtyczka pyflakes.vim jest przyjemnym narzędziem, które potrafi wyłapać podstawowe błędy w plikach pythonowych (błędy składni, nieużywane importy itp), które sygnalizuje poprzez podświetlenie (wykorzystuje grupę SpellBad) oraz pokazuje komunikat w linii komend.
python_fn.vim to zestaw skrótów i menu dla plików pythonowych. Umożliwia przemieszczanie się pomiędzy klasami i funkcjami, zaznaczenie klasy/funkcji oraz fragmentu kodu o tej samej indentacji, przesunięcia bloku kodu oraz komentowanie/odkomentowanie bloku kodu.
wcięcia (umieszczane w
~/.vim/indent
)- javascript.vim ulepszona obsługa wcięć dla plików javascript.
- Wciąż używam alternatywnego sposobu wcięć plików pythonowych.
pliki podświetlania składni
~/.vim/syntax
- python.vim mocno rozszerzony skrypt podświetleń dla plików pythonowych. Zmieniłem w nim jedynie sposób podświetlania dekoratorów.
Liczba rozszerzeń, których używam, ciągle się zmienia, jednakże zestaw który wymieniłem osiadł na dobre i nabyłem już nawyków do ich używania. Oczywiście, czasem próbuję nowych, interesujących mnie szczególnie rozszerzeń, lub usuwam skrypty, których (jak się po czasie okazuje) nie używam w ogóle.
Edycja plików
W mojej obecnej pracy jestem zmuszony do korzystania z Windows (z czego, nie ukrywam, nie jestem szczególnie zadowolony), oraz narzędzi, które sam sobie dobiorę. Na szczęście w moim zespole nie ma przymusu do wyboru konkretnego narzędzia do wprowadzania tekstu.
Z kolei projekt, przy którym pracuję, korzysta z kilku różnych technologii, włączając w to aplikacje desktopowe jak i te oparte o model klient‐serwer, bazujący na serwerze Apache i przeglądarce.
Całość tego wielomodułowego systemu jest spięta przez wspólny mianownik: Python. W Pythonie napisana jest w całości aplikacja po stronie serwera wyżej wspomnianego modułu, jak również w Pythonie wykonana jest znaczna część logiki dla modułów desktopowych.
Korzystając z Vima silnie wykorzystuję to co dają mi wymienione powyżej pluginy, zwłaszcza fuzzyfinder, który wykorzystuję do wyszukania i otwarcia plików, których nazwy mniej więcej pamiętam. Mozolne przedzieranie się przez drzewo plików w NERDTree czy to w project.tar.gz, kompletnie się nie sprawdziło. Pojęcie projektu, który dość mocno zdefiniowany jest w IDE takim jak Eclipse czy Netbeans, ale również w TextMate czy jEdit z wtyczką Project Viewer, nie istnieje w Vimie, i próby jego emulowania mijają się IMO z celem.
Gdy nie pamiętam nazwy pliku, lub chciałbym wyszukać jakiś konkretny wzorzec
wśród plików, stosuję :Grep
lub :Rgrep
, który jest nieco wygodniejszy od
:vimgrep
. Autor zaleca stosowanie Pod Windows narzędzi grep
i
find
, jednak równie dobrze można skorzystać z tych dostarczanych przez
cygwin.
Otwarte pliki zwykle rozkładam po zakładkach, dla różnych modułów systemu,
które umieszczone są w różnych miejscach systemu plików, przydatną opcją jest
polecenie :lcd
, które zmienia bieżący katalog tylko dla obecnego
okna/zakładki, przez co można przygotować sobie kilka zakładek w zależności od
potrzebnego widoku. Należy pamiętać, że w każdy otwarty plik może być widoczny w
dowolnej ilości zakładek, przez co takie zachowanie może sprawić, że bardzo
łatwo jest się pogubić, zwłaszcza początkującym.
Python
Python jest językiem, w którym programuję najwięcej (również prywatnie). Vim wspiera pisanie w tym języku w dość sporym zakresie.
Po pierwsze, trzeba dostosować nieco zachowanie Vima, by ładnie się zachowywał z plikami pythonowymi:
setlocal cinkeys-=0# setlocal indentkeys-=0# setlocal expandtab setlocal foldlevel=100 setlocal foldmethod=indent setlocal list setlocal noautoindent setlocal shiftwidth=4 setlocal smartindent setlocal cinwords=if,elif,else,for,while,try,except,finally,def,class,with setlocal smarttab setlocal softtabstop=4 setlocal tabstop=4 setlocal textwidth=78 setlocal colorcolumn=+1 set wildignore+=*.pyc let python_highlight_all=1 "Load views for py files autocmd BufWinLeave *.py mkview autocmd BufWinEnter *.py silent loadview compiler pylint
Po drugie, wprost z „pudełka” dostarczana jest funkcjonalność, która pozwala
na odpowiednie podświetlenie składni języka (syntax), logikę wcięć (indent),
możliwość przemieszczania się pomiędzy funkcjami i klasami używając ]]
i
[[
(klasy i funkcje modułu) oraz ]m
i [m
(wszystkie klasy, metody i
funkcje), oraz podpowiadanie składni. Vim jest też jednym z niewielu edytorów
wspierających Pythona3.
Funkcjonalność tę rozszerza plugin python_fn.vim
, który usprawnia poruszanie
się po kodzie pythonowym wprowadzając możliwość przemieszczania się pomiędzy
klasami (]j
i ]J
), funkcjami (]f
i ]F
), na początek i koniec
bloku o tym samym poziomie wcięć (]t
i ]e
), zwiększanie i zmniejszanie
poziomu wcięć bloku kodu z lub bez zaznaczania (]>
i ]<
), komentowanie i
odkomentowanie linii lub zaznaczenia (]#
i ]u
) oraz zaznaczanie bloku,
klasy lub funkcji (]v
, ]c
i ]d
). Pozwala on także na generowanie
menu IM-Python ze strukturą bieżącego pliku.
Jednym z zacniejszych usprawnień, jest wcześniej wspomniany skrypt pyflakes, który periodycznie sprawdza poprawność kodu pythonowego pozwalając na wczesne wyeliminowanie błędów składni, literówek, nieużywanych importów, nieoczekiwanych wcięć, i podobnych błędów możliwych do wychwycenia poprzez statyczną analizę kodu.
Następnym narzędziem, z którego mocno korzystam, jest pylint. Jest ono dużo
bardziej skomplikowane i dokładne, przez co może być znacznie wolniejsze niż
ultraszybki pyflakes. Jest całkiem sporo tutoriali jak pożenić pylint z Vimem, a
nawet jest gotowy skrypt typu „compiler”, jednak ja przygotowałem swoją
własną wersję z kilku powodów. Po pierwsze, nie bardzo podobała mi się forma w
jakiej wspomniany kompiler zwracał wynik przeprowadzonej analizy, po drugie, w
obecnej wersji pylint oprócz linii, potrafi wskazać w której kolumnie znajduje
się błąd przy pomocy jednego lub więcej znaków ^
:
gryf@mslug ~ $ pylint -rn -iy test2.py No config file found, using default configuration ************* Module test2 C0111: 1: Missing docstring C0324: 4: Comma not followed by a space import os.path,subprocess ^^ W0404: 26: Reimport 'subprocess' (imported line 4) C0111: 30:get_familly: Missing docstring W0621: 30:get_familly: Redefining name 'pid' from outer scope (line 43)
Po trzecie wreszcie, chciałem mieć możliwość podmiany programu odpowiedzialnego za generowanie raportu.
Pierwszym pomysłem było dostarczenie komendy :Pylint
, która będzie się
zachowywać jak ja chcę. Ponieważ parser był napisany w Pythonie, wydzieliłem go
jako osobny skrypt, który następnie podpiąłem pod compiler Vimowy. Skrypt nie
jest jakiś szczególnie skomplikowany, i zapewne dałoby się uruchomić checkery
i w jakiś sprytny sposób dobrać się do listy błędów, ale no cóż :) poszedłem na
łatwiznę i analizuję jedynie wyjście jakie dostaję z Reportera:
1 #!/usr/bin/env python 2 """ 3 This script can be used as a pylint command replacement, especially useful as 4 a "make" command for Vim 5 """ 6 import sys 7 import re 8 from StringIO import StringIO 9 from optparse import OptionParser 10 11 from pylint import lint 12 from pylint.reporters.text import TextReporter 13 14 15 SYS_STDERR = sys.stderr 16 DUMMY_STDERR = StringIO() 17 CONF_MSG = 'No config file found, using default configuration\n' 18 19 def parsable_pylint(filename): 20 """ 21 Simple wrapper for pylint checker. Provides nice, parseable output. 22 filename - python fileneame to check 23 24 Returns list of dicts of errors, i.e.: 25 [{'lnum': 5, 'col': 10, 'type': 'C0324', 26 'text': 'Comma not followed by a space'}, 27 {'lnum': 12, 'type': 'C0111', 'text': 'Missing docstring'}, 28 .... 29 ] 30 31 """ 32 # args 33 args = ['-rn', # display only the messages instead of full report 34 '-iy', # Include message's id in output 35 filename] 36 37 buf = StringIO() # file-like buffer, instead of stdout 38 reporter = TextReporter(buf) 39 40 sys.stderr = DUMMY_STDERR 41 lint.Run(args, reporter=reporter, exit=False) 42 sys.stderr = SYS_STDERR 43 44 # see, if we have other errors than 'No config found...' message 45 DUMMY_STDERR.seek(0) 46 error_list = DUMMY_STDERR.readlines() 47 DUMMY_STDERR.truncate(0) 48 if error_list and CONF_MSG in error_list: 49 error_list.remove(CONF_MSG) 50 if error_list: 51 raise Exception(''.join(error_list)) 52 53 buf.seek(0) 54 55 code_line = {} 56 error_list = [] 57 58 carriage_re = re.compile(r'\s*\^+$') 59 error_re = re.compile(r'^([C,R,W,E,F].+):\s+?([0-9]+):?.*:\s(.*)$') 60 61 for line in buf: 62 line = line.rstrip() # remove trailing newline character 63 64 if error_re.match(line): 65 if code_line: 66 error_list.append(code_line) 67 code_line = {} 68 69 code_line['type'], code_line['lnum'], code_line['text'] = \ 70 error_re.match(line).groups() 71 72 if carriage_re.match(line) and code_line: 73 code_line['col'] = carriage_re.match(line).group().find('^') + 1 74 75 return error_list 76 77 if __name__ == "__main__": 78 parser = OptionParser("usage: %prog python_file") 79 (options, args) = parser.parse_args() 80 if len(args) == 1: 81 for line in parsable_pylint(args[0]): 82 line['short'] = line['type'][0] 83 line['fname'] = args[0] 84 out = "%(fname)s: %(short)s: %(lnum)s: %(col)s: %(type)s %(text)s" 85 if 'col' not in line: 86 out = "%(fname)s: %(short)s: %(lnum)s: 0: %(type)s %(text)s" 87 88 print out % line
Po umieszczeniu skryptu gdzieś w ścieżce, napisanie skryptu „kompilatora” jest banałem:
" Vim compiler file for Python " Compiler: Static code checking tool for Python " Maintainer: Roman 'gryf' Dobosz " Last Change: 2010-09-12 " Version: 1.0 if exists("current_compiler") finish endif let current_compiler = "pylint" CompilerSet makeprg=pylint_parseable.py\ % CompilerSet efm=%f:\ %t:\ %l:\ %c:\ %m,%f:\ %t:\ %l:\ %m
:make
można wywołać albo poprzez linię komend Vima (wpisując po prostu
:make
), albo podmapować wywołanie pod klawisz lub zdarzenie. Ja wykorzystuję
klawisz <F5>
. Tak może wyglądać wynik działania:
Podobna sytuacja miała miejsce z narzędziem Pep8, które uzupełnia pylint jak
i pyflakes. Pozwala ono sprawdzić czy styl, w jakim napisany jest plik
pythonowy, zgodny jest z zaleceniami zawartymi w PEP8. W odróżnieniu od
pylinta, narzędzie to otrzymało swoją własną komendę (:Pep8
) i zostało
napisane z wykorzystaniem języka Python:
48 if exists("b:did_pep8_plugin") 49 finish " only load once 50 else 51 let b:did_pep8_plugin = 1 52 endif 53 54 if !exists("b:did_pep8_init") 55 let b:did_pep8_init = 0 56 57 if !has('python') 58 echoerr "pep8_fn.vim plugin requires Vim to be compiled with +python" 59 finish 60 endif 61 62 python << EOF 63 import vim 64 import sys 65 from StringIO import StringIO 66 67 try: 68 import pep8 69 except ImportError: 70 raise AssertionError('Error: pep8_fn.vim requires module pep8') 71 72 class VimPep8(object): 73 74 def __init__(self): 75 self.fname = vim.current.buffer.name 76 self.bufnr = vim.current.buffer.number 77 self.output = [] 78 79 def reporter(self, lnum, col, text, check): 80 self.output.append([lnum, col, text]) 81 82 def run(self): 83 pep8.process_options(['-r', vim.current.buffer.name]) 84 checker = pep8.Checker(vim.current.buffer.name) 85 checker.report_error = self.reporter 86 checker.check_all() 87 self.process_output() 88 89 def process_output(self): 90 vim.command('call setqflist([])') 91 qf_list = [] 92 qf_dict = {} 93 94 for line in self.output: 95 qf_dict['bufnr'] = self.bufnr 96 qf_dict['lnum'] = line[0] 97 qf_dict['col'] = line[1] 98 qf_dict['text'] = line[2] 99 qf_dict['type'] = line[2][0] 100 qf_list.append(qf_dict) 101 qf_dict = {} 102 103 self.output = [] 104 vim.command('call setqflist(%s)' % str(qf_list)) 105 if qf_list: 106 vim.command('copen') 107 EOF 108 let b:did_pep8_init = 1 109 endif 110 111 if !exists('*s:Pep8') 112 function s:Pep8() 113 python << EOF 114 VimPep8().run() 115 EOF 116 endfunction 117 endif 118 119 if !exists(":Pep8") 120 command Pep8 call s:Pep8() 121 endif
Przydatną funkcją podczas pracy z plikami pythonowymi (i ogólnie z jakimkolwiek
kodem źródłowym) jest usuwanie zbędnych białych znaków na końcach linii. Kilka
przykładów takiej funkcji można znaleźć na stronach Vim Wiki. Złożoną z kilku
pomysłów funkcja StripTrailingWhitespaces
:
" Remove trailing whitespace function <SID>StripTrailingWhitespaces() " Preparation: save last search, and cursor position. let _s=@/ let l = line(".") let c = col(".") " Do the business: %s/\s\+$//e " Clean up: restore previous search history, and cursor position let @/=_s call cursor(l, c) endfunction
która nie zaśmieca historii wyszukiwania oraz zapamiętuje ostatnią pozycję
kursora, podpiąłem pod zdarzenie BufWritePre
dla plików Pythona,
reStructuredText, wiki, javascript CSS i XML:
"remove all trailing whitespace for specified files before write autocmd BufWritePre *.py :call <SID>StripTrailingWhitespaces() autocmd BufWritePre *.rst :call <SID>StripTrailingWhitespaces() autocmd BufWritePre *.wiki :call <SID>StripTrailingWhitespaces() autocmd BufWritePre *.js :call <SID>StripTrailingWhitespaces() autocmd BufWritePre *.css :call <SID>StripTrailingWhitespaces() autocmd BufWritePre *.xml :call <SID>StripTrailingWhitespaces()
Dodałem też kilka szczególnie często wykorzystywanych fragmentów kodu
pythonowego wykorzystając plugin snipMate, dla przykładu, będąc w trybie
insert
i wpisując w dowolnym miejscu kodu pythonowego:
dbg<TAB>
otrzymuję:
import ipdb; ipdb.set_trace()
I to nie wszystko. Strasznie ciężko napisać jeden zwarty artykuł by pokazać ogromny potencjał drzemiący w tym niepozornym edytorze.
Chyba nie będzie niespodzianką, jeśli napiszę, że artykuł, który właśnie czytasz
został napisany w reST przy użyciu Vima, ze wspomaganiem - czyli ze
sprawdzaniem pisowni oraz funkcją do generowania HTML ad hoc, a wszelkie
listingi to produkt komendy :TOhtml
(EDIT: nie jest to już aktualne, z uwagi
na to, że od jakiegoś czasu wykorzystuję Pygments przy kolorowaniu dyrektywy
code
):
Zmory
Skłamałbym, jakbym napisał, że Vim jest idealny. Vim ma swoje wady, choć często wynikające (jak się później okazuje) z mojej niewiedzy, ale nie tylko.
Jednym z podstawowych, wkurzających zachowań Gvima (graficznej wersji Vima) pod Linuxem jest jego okno. Nie wiem, dlaczego twórcy linuksowej (uniksowej) wersji w GTK zaimplementowali Gvima w ten sposób, ale okno zachowuje się jak licealistka - mianowicie, podczas normalnej edycji wszystko jest ok, do czasu, gdy doda/ujmie się nowy element interfejsu (stworzy tab/usunie ostatni, doda/ujmie menu, pasek narzędzi itp.) lub gdy rozdzieli się okno (split), np. wywołując bufor quickfix czy jakikolwiek inny, okno edytora zmienia nieznacznie swój rozmiar, co po kilkunastu takich zmianach prowadzi do częściowego przesłonienia dockappów czy zminimalizowanych aplikacji w Window Makerze. Również w innych menadżerach okien (Awesome, Openbox), jakie miałem okazję używać występują podobne objawy, z tym że okno, co prawda jest rozciągnięte na cały ekran, jednakże zaczynają się dziać dziwne rzeczy wewnątrz okna Gvima - zostaje sporo miejsca, które bez problemu mogłoby zostać wykorzystane przez kolumnę bądź wiersz w edytorze.
Ok, zdaję sobie sprawę, że Gvim (tak jak i Vim) wywodzi się z edytora mocno eksploatowanego w terminalach, jednakże wersja Windowsowa nie ma takich problemów i gładko radzi sobie z wypełnieniem miejsca w oknie bez ciągłej zmiany rozmiarów tegoż. Da się? Jak widać.
Epilog
Czy mogę powiedzieć, że nauczyłem się Vima na tyle, żeby móc z całą pewnością powiedzieć, że go umiem? Na pewno nie. Co rusz uczę się nowych komend, lub odkrywam stare, o których nie wiedziałem, że można używać ich w inny sposób.
Niewątpliwie też, nie wyczerpałem tematu w całości, wobec czego następne wpisy będę się starał dozować :)