ほんじゃらねっと

ダイエット中プログラマのブログ

PythonでVim用のファイルブックマークプラグインを作る

初めてのVimスクリプトPythonで書いたが、
ほとんどVimの機能を呼び出しているだけになってしまった...
しかし楽しい。


環境は gVim7 + WinXP + Python2.5 + BeautifulSoup。


:PyBookmarkAdd でカレントバッファのファイルをブックマーク。
:PyBookmarkList でブックマークの一覧表示。Enterでファイルを開く。


以下の2ファイルをpluginsフォルダに配置する。


pybookmarks.vim

if has('win32')
let s:vim_encoding = "shift_jis"
else
let s:vim_encoding = "utf-8"
endif
if !exists('g:pybookmarks_file')
let g:pybookmarks_file = $HOME . "\\pybookmarks.xml"
endif
if !exists("g:pybookmarks_loaded_python_file")
pyfile <sfile>:p:h/pybookmarks.py
python pybookmarks = PyBookmarks()
let g:pybookmarks_loaded_python_file=1
endif
command! PyBookmarkList python pybookmarks.list_bookmark()
command! PyBookmarkAdd python pybookmarks.add_bookmark()
function! s:OpenSearchLine()
python pybookmarks.open_search_line()
endfunction


pybookmarks.py

# vim:set fileencoding=utf-8:
# ファイルブックマーク Vimスクリプト
# 
# XML形式のファイルにブックマークを保存
# BeautifulSoupを使って解析、編集
import vim
import sys
import codecs
import os
import re
import datetime
from BeautifulSoup import *
class PyBookmarks():
def __init__(self):
self.vim_encoding = vim.eval("s:vim_encoding")
self._xml_file = None
self._soup = None
def list_bookmark(self):
bname = "__PYBOOKMARKS__"
winnum = vim.eval('bufwinnr("%s")' % (bname,))
if vim.eval("winnr()") != winnum:
if winnum != "-1":
vim.command("exe %swincmd w" % (winnum,))
self._open_window(bname)
vim.command("setlocal bt=nofile bh=delete noswf")
# Enter時にOpenSearchLineコマンドを呼び出す
vim.command("nnoremap <silent> <buffer> <CR> :call <SID>OpenSearchLine()<CR>")
b = vim.current.buffer
vim.command("redraw!")
vim.command("setlocal modifiable")
line_format = "%s | %s | %s"
# バッファにXMLファイルの内容を一覧表示
idx = 1
soup = self._get_soup()
for bookmark in soup('bookmark'):
bookmark_href = re.escape(bookmark['href'])
self._enc_command('let b:file{%d} = "%s"' % (idx, bookmark_href))
line = line_format % (bookmark['title'], bookmark_href, bookmark['create_date'])
if vim.eval('line("$")') != "1" or vim.eval('getline("1")') != '':
self._enc_command('call append("$", "%s")' % (line,))
else:
self._enc_command('call setline("1", "%s")' % (line,))
idx = idx + 1
vim.command("setlocal nomodifiable")
vim.command("redraw!")
def add_bookmark(self):
path = vim.current.buffer.name
if path is not None:
path = path.decode(self.vim_encoding)
input = 'input("%s", "%s")' % (u'タイトル:', os.path.basename(path))
title = self._enc_eval(input)
if title is not None:
soup = self._get_soup()
title = title.decode(self.vim_encoding)
if title == "":
title = u"無名ブックマーク"
bookmark = Tag(soup, "bookmark")
bookmark['title'] = title
#bookmark.append(title)
bookmark['href'] = path
bookmark['create_date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
soup.bookmarks.append(bookmark)
self._overwrite_xml(soup.prettify())
print u"ブックマークを追加しました".encode(self.vim_encoding)
else:
print u"ファイルまたはディレクトリが開かれていません".encode(self.vim_encoding)
def open_search_line(self):
line_num = vim.eval('line(".")')
bookmark_href = vim.eval('b:file{%s}' % (line_num,))
bookmark_href = re.escape(bookmark_href)
self._eliminate_window(vim.eval('bufname("%")'))
self._open_window(bookmark_href)
def _get_xml_path(self):
if self._xml_file == None:
self._xml_file = vim.eval("g:pybookmarks_file")
return self._xml_file
def _get_soup(self):
if self._soup == None:
xml_file = self._get_xml_path()
if os.path.exists(xml_file):
f = open(xml_file)
bxml = f.read()
f.close()
self._soup = BeautifulStoneSoup(bxml, fromEncoding="UTF-8", selfClosingTags='bookmark')
else:
self._soup = BeautifulStoneSoup('<?xml version="1.0" encoding="UTF-8"?>\n<bookmarks></bookmarks>', selfClosingTags='bookmark')
return self._soup
def _overwrite_xml(self, contents):
xml_file = self._get_xml_path()
f = open(xml_file, "w")
f.write(contents)
f.close()
def _enc_command(self, command):
vim.command(command.encode(self.vim_encoding))
def _enc_eval(self, command):
return vim.eval(command.encode(self.vim_encoding))
def _open_window(self, filepath):
self._eliminate_window(filepath)
cmd = "new"
if vim.eval('expand("%:p") == "" && &modified == 0 && !has("vim_starting")') == "1":
cmd = "edit"
#vim.command('silent exe "%s ++enc=%s ++ff=&ff %s"' % (cmd, "utf-8", filepath))
self._enc_command('silent exe "%s %s"' % (cmd, filepath))
def _eliminate_window(self, name):
"""指定したバッファ名のウィンドウをすべて閉じる"""
nr = vim.eval('bufwinnr("%s")' % (name,))
while nr != "-1":
vim.command('%swincmd w' % (nr,))
if self._safe_close() == 0:
break
nr = vim.eval('bufwinnr("%s")' % (name,))
def _safe_close(self):
retval = 0
if vim.eval('&modified') != "-1":
vim.command('silent! enew')
if self._get_win_num() > 1:
vim.command("close")
retval = 1
return retval
def _get_win_num(self):
winnum = 1
nr = vim.eval('winnr()')
vim.command('silent! exe "wincmd \\<C-w>"')
while nr != vim.eval('winnr()'):
winnum = winnum + 1
vim.command('silent! exe "wincmd \\<C-w>"')
return winnum


下記のサイトのVimスクリプトを参考にさせていただきました。
http://sworddancer.funkyboy.jp/howm_vim/
http://www.vim.org/scripts/script.php?script_id=165