diff --git a/doc/tags b/doc/tags index 233339d..992ba44 100644 --- a/doc/tags +++ b/doc/tags @@ -27,6 +27,10 @@ 'snippets' snipMate.txt /*'snippets'* .snippet snipMate.txt /*.snippet* .snippets snipMate.txt /*.snippets* +:AcpDisable acp.txt /*:AcpDisable* +:AcpEnable acp.txt /*:AcpEnable* +:AcpLock acp.txt /*:AcpLock* +:AcpUnlock acp.txt /*:AcpUnlock* :CVSEdit vcscommand.txt /*:CVSEdit* :CVSEditors vcscommand.txt /*:CVSEditors* :CVSUnedit vcscommand.txt /*:CVSUnedit* @@ -152,6 +156,21 @@ VCSCommandSplit vcscommand.txt /*VCSCommandSplit* VCSCommandVCSTypeOverride vcscommand.txt /*VCSCommandVCSTypeOverride* VimwikiWeblinkHandler vimwiki.txt /*VimwikiWeblinkHandler* abc fuf.txt /*abc* +acp acp.txt /*acp* +acp-about acp.txt /*acp-about* +acp-author acp.txt /*acp-author* +acp-changelog acp.txt /*acp-changelog* +acp-commands acp.txt /*acp-commands* +acp-contact acp.txt /*acp-contact* +acp-installation acp.txt /*acp-installation* +acp-introduction acp.txt /*acp-introduction* +acp-options acp.txt /*acp-options* +acp-perl-omni acp.txt /*acp-perl-omni* +acp-snipMate acp.txt /*acp-snipMate* +acp-thanks acp.txt /*acp-thanks* +acp-usage acp.txt /*acp-usage* +acp.txt acp.txt /*acp.txt* +autocomplpop acp.txt /*autocomplpop* b:VCSCommandCommand vcscommand.txt /*b:VCSCommandCommand* b:VCSCommandOriginalBuffer vcscommand.txt /*b:VCSCommandOriginalBuffer* b:VCSCommandSourceFile vcscommand.txt /*b:VCSCommandSourceFile* @@ -253,6 +272,32 @@ fuf-usage fuf.txt /*fuf-usage* fuf-vimrc-example fuf.txt /*fuf-vimrc-example* fuf.txt fuf.txt /*fuf.txt* fuzzyfinder fuf.txt /*fuzzyfinder* +g:acp_behavior acp.txt /*g:acp_behavior* +g:acp_behavior-command acp.txt /*g:acp_behavior-command* +g:acp_behavior-completefunc acp.txt /*g:acp_behavior-completefunc* +g:acp_behavior-meets acp.txt /*g:acp_behavior-meets* +g:acp_behavior-onPopupClose acp.txt /*g:acp_behavior-onPopupClose* +g:acp_behavior-repeat acp.txt /*g:acp_behavior-repeat* +g:acp_behaviorCssOmniPropertyLength acp.txt /*g:acp_behaviorCssOmniPropertyLength* +g:acp_behaviorCssOmniValueLength acp.txt /*g:acp_behaviorCssOmniValueLength* +g:acp_behaviorFileLength acp.txt /*g:acp_behaviorFileLength* +g:acp_behaviorHtmlOmniLength acp.txt /*g:acp_behaviorHtmlOmniLength* +g:acp_behaviorKeywordCommand acp.txt /*g:acp_behaviorKeywordCommand* +g:acp_behaviorKeywordIgnores acp.txt /*g:acp_behaviorKeywordIgnores* +g:acp_behaviorKeywordLength acp.txt /*g:acp_behaviorKeywordLength* +g:acp_behaviorPerlOmniLength acp.txt /*g:acp_behaviorPerlOmniLength* +g:acp_behaviorPythonOmniLength acp.txt /*g:acp_behaviorPythonOmniLength* +g:acp_behaviorRubyOmniMethodLength acp.txt /*g:acp_behaviorRubyOmniMethodLength* +g:acp_behaviorRubyOmniSymbolLength acp.txt /*g:acp_behaviorRubyOmniSymbolLength* +g:acp_behaviorSnipmateLength acp.txt /*g:acp_behaviorSnipmateLength* +g:acp_behaviorUserDefinedFunction acp.txt /*g:acp_behaviorUserDefinedFunction* +g:acp_behaviorUserDefinedMeets acp.txt /*g:acp_behaviorUserDefinedMeets* +g:acp_behaviorXmlOmniLength acp.txt /*g:acp_behaviorXmlOmniLength* +g:acp_completeOption acp.txt /*g:acp_completeOption* +g:acp_completeoptPreview acp.txt /*g:acp_completeoptPreview* +g:acp_enableAtStartup acp.txt /*g:acp_enableAtStartup* +g:acp_ignorecaseOption acp.txt /*g:acp_ignorecaseOption* +g:acp_mappingDriven acp.txt /*g:acp_mappingDriven* g:fuf_abbrevMap fuf.txt /*g:fuf_abbrevMap* g:fuf_autoPreview fuf.txt /*g:fuf_autoPreview* g:fuf_bookmarkdir_keyDelete fuf.txt /*g:fuf_bookmarkdir_keyDelete* diff --git a/ftplugin/rst/blogger.vim b/ftplugin/rst/blogger.vim new file mode 100644 index 0000000..e9a67a6 --- /dev/null +++ b/ftplugin/rst/blogger.vim @@ -0,0 +1,96 @@ +" Blogger vim interface. +" Provide some convinient functions for creating preview from the reST file +" and to send articles to blog. + +if exists("b:did_rst_plugin") + finish " load only once +else + let b:did_rst_plugin = 1 +endif + +if !exists("g:blogger_browser") + let g:blogger_browser = 0 +endif + +if !exists("g:blogger_name") + let g:blogger_name = "" +endif + +if !exists("g:blogger_login") + let g:blogger_login= "" +endif + +if !exists("g:blogger_pass") + let g:blogger_pass = "" +endif + +map :call Restify() +map :call Rst2Blogger() + +if !exists('*s:Restify') + python << EOF +#{{{ +import os +import sys +import webbrowser + +import vim + +scriptdir = os.path.dirname(vim.eval('expand("")')) +sys.path.insert(0, scriptdir) +from bloggervim.rest import blogPreview, blogArticleString +from bloggervim.blogger import VimBlogger + +#}}} +EOF + + " Translate reSt text into html fragment suitable for preview in browser. + fun Restify() + python << EOF +# {{{ +bufcontent = "\n".join(vim.current.buffer) +name = vim.current.buffer.name + +name = name[:-4] + ".html" +html = blogPreview(bufcontent) +output_file = open(name, "w") +output_file.write(html) +output_file.close() +if vim.eval("g:blogger_browser"): + webbrowser.open(name) + print "Generated HTML has been opened in browser" +else: + print "Generated HTML has been written to %s" % name +#}}} +EOF + endfun + + " Generate headless html, gather title, dates and tags from filed list and + " then send it to blog. + fun Rst2Blogger() + python << EOF +#{{{ +bufcontent = "\n".join(vim.current.buffer) +name = vim.current.buffer.name +html, attrs = blogArticleString(bufcontent) + +login = vim.eval("g:blogger_login") +password = vim.eval("g:blogger_pass") +blogname = vim.eval("g:blogger_name") + +if not password: + password = vim.eval('inputsecret("Enter your gmail password: ")') + +title = 'title' in attrs and attrs['title'] or None +date = 'date' in attrs and attrs['date'] or None +tags = 'tags' in attrs and attrs['tags'] or "" +tags = [tag.strip() for tag in tags.split(',')] +modified = 'modified' in attrs and attrs['modified'] or None + +blog = VimBlogger(blogname, login, password) +print blog.create_article(title, html, tags=tags) + +#}}} +EOF + endfun +endif diff --git a/ftplugin/rst/bloggervim/blogger.py b/ftplugin/rst/bloggervim/blogger.py new file mode 100644 index 0000000..f7fb139 --- /dev/null +++ b/ftplugin/rst/bloggervim/blogger.py @@ -0,0 +1,130 @@ +# vim: fileencoding=utf8 +# +# Blogger interface to make easy way to create/update articles for specified +# blog. +# +# It is assumed one way communication only, so you may create or update an +# article from reST source files. There is no way to recreate article from +# html to reST format. +# +# requirements: +# +# - Vim compiled with +python +# - python 2.x (tested with 2.6) +# - modules +# - gdata (http://code.google.com/p/gdata-python-client) +# - docutils (http://docutils.sourceforge.net) +# - pytz (http://pytz.sourceforge.net) +# +# USE CASES: +# 1. Create new post +# +# use reST template: +# ===8<--- +# :Title: Blog post title +# :Date: optional publish date (for example: 2010-11-28 18:47:05), +# default: now() +# :Modified: optional, default: None +# :Tags: comma separated blog tags +# +# .. more +# +# --->8=== +# +# All four docinfo are optional, however it is nice to give at least a title +# to the article :) +# +# +# +# which is provided under templates directory or as a +# snipMate shoortcut (see .vim/snippets/rst.snippets) +# + +# vim.eval('inputsecret("Password: ")') +# echomsg expand("%:p") +#----------------------------------------------------------------------------- +# +import getpass # TODO: remove +import time +import datetime + +import pytz +import atom +from gdata.blogger.client import BloggerClient + + +class VimBlogger(object): + """ + """ + + def __init__(self, blogname, login, password): + """ + """ + self.blog = None + self.client = BloggerClient() + self._authorize(login, password) + + self.feed = self.client.get_blogs() + self._set_blog(blogname) + #self._get_arts(blogname) + + def _set_blog(self, blogname): + """ + """ + for blog in self.feed.entry: + if blog.get_blog_name() == blogname: + self.blog = blog + break + + + def _get_arts(self, blogname): + """ + """ + feed = self.client.get_posts(self.blog.get_blog_id()) + for entry in feed.entry: + print entry.title.text + # + import ipdb; ipdb.set_trace() + # + # entry.content obiekt zawiera ciało artykułu (entry.content.text + # posiada czystą formę która mnie interesuje najbardziej, do której + # można pisać + # + # entry.category - lista wszystkich kategorii (blogowych tagów), które + # post posiada. Są to elementy klasy atom.data.Category, które + # łatwiutko stworzyć i dodać do posta: + # import atom + # cat1 = atom.data.Category() + # cat1.term = "nowy tag dla bloggera" + # entry.category.append(cat1) + # + # entry.title przechowuje tytuł posta + + def _authorize(self, login, password): + """ + """ + source = 'Blogger_Python_Sample-2.0' + service = 'blogger' + + self.client.client_login(login, + password, + source=source, + service=service) + def create_article(self, title, html_doc, tags=None): + """ + """ + + blog_id = self.blog.get_blog_id() + if tags is None: + tags = [] + return self.client.add_post(blog_id, title, html_doc, labels=tags, + draft=True) + + def update_article(self, title, html_doc, tags=None): + """ + """ + pass + +if __name__ == "__main__": + p = getpass.getpass("Password: ") + b = VimBlogger("rdobosz", "gryf73@gmail.com", p) diff --git a/ftplugin/rst/bloggervim/rest.py b/ftplugin/rst/bloggervim/rest.py new file mode 100644 index 0000000..8a0b707 --- /dev/null +++ b/ftplugin/rst/bloggervim/rest.py @@ -0,0 +1,263 @@ +import re + +from docutils import core +from docutils import nodes +from docutils.parsers.rst import directives, Directive +from docutils.writers.html4css1 import Writer, HTMLTranslator + +from pygments import highlight +from pygments.lexers import get_lexer_by_name, TextLexer +from pygments.formatters import HtmlFormatter + +class Attrs(object): + ATTRS = {} + +class Pygments(Directive): + """ + Source code syntax hightlighting. + """ + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + has_content = True + + def run(self): + self.assert_has_content() + try: + lexer = get_lexer_by_name(self.arguments[0]) + except ValueError: + # no lexer found - use the text one instead of an exception + lexer = TextLexer() + # take an arbitrary option if more than one is given + formatter = HtmlFormatter(noclasses=True) + parsed = highlight(u'\n'.join(self.content), lexer, formatter) + return [nodes.raw('', parsed, format='html')] + +directives.register_directive('sourcecode', Pygments) + +class CustomHTMLTranslator(HTMLTranslator): + """ + Base class for reST files translations. + There are couple of customizations for docinfo fields behaviour and + abbreviations and acronyms. + """ + + def __init__(self, document): + """ + Set some nice defaults for articles translations + """ + HTMLTranslator.__init__(self, document) + self.initial_header_level = 4 + + def visit_section(self, node): + """ + Don't affect document, just keep track of the section levels + """ + self.section_level += 1 + + def depart_section(self, node): + self.section_level -= 1 + + def visit_meta(self, node): + pass + + def depart_meta(self, node): + pass + + def visit_document(self, node): + pass + + def depart_document(self, node): + pass + + def depart_docinfo(self, node): + """ + Reset body, remove unnecesairy content. + """ + self.body = [] + + def visit_date(self, node): + pass + + def depart_date(self, node): + pass + + def visit_literal(self, node): + """ + This is almos the same as the original one from HTMLTranslator class. + The only difference is in used HTML tag: it uses 'code' instead of + 'tt' + """ + self.body.append(self.starttag(node, 'code', '')) + text = node.astext() + for token in self.words_and_spaces.findall(text): + if token.strip(): + # Protect text like "--an-option" and the regular expression + # ``[+]?(\d+(\.\d*)?|\.\d+)`` from bad line wrapping + if self.sollbruchstelle.search(token): + self.body.append('%s' + % self.encode(token)) + else: + self.body.append(self.encode(token)) + elif token in ('\n', ' '): + # Allow breaks at whitespace: + self.body.append(token) + else: + # Protect runs of multiple spaces; the last space can wrap: + self.body.append(' ' * (len(token) - 1) + ' ') + self.body.append('') + # Content already processed: + raise nodes.SkipNode + + def visit_acronym(self, node): + """ + Define missing acronym HTML tag + """ + node_text = node.children[0].astext() + node_text = node_text.replace('\n', ' ') + patt = re.compile(r'^(.+)\s<(.+)>') + + if patt.match(node_text): + node.children[0] = nodes.Text(patt.match(node_text).groups()[0]) + self.body.append(\ + self.starttag(node, 'acronym', + '', title=patt.match(node_text).groups()[1])) + + else: + self.body.append(self.starttag(node, 'acronym', '')) + + def visit_abbreviation(self, node): + """ + Define missing abbr HTML tag + """ + node_text = node.children[0].astext() + node_text = node_text.replace('\n', ' ') + patt = re.compile(r'^(.+)\s<(.+)>') + + if patt.match(node_text): + node.children[0] = nodes.Text(patt.match(node_text).groups()[0]) + self.body.append(\ + self.starttag(node, 'abbr', + '', title=patt.match(node_text).groups()[1])) + + else: + self.body.append(self.starttag(node, 'abbr', '')) + +class NoHeaderHTMLTranslator(CustomHTMLTranslator): + """ + Special subclass for generating only body of an article + """ + def __init__(self, document): + """ + Remove all needless parts of HTML document. + """ + CustomHTMLTranslator.__init__(self, document) + self.head = [] + self.meta = [] + self.head_prefix = ['','','','',''] + self.body_prefix = [] + self.body_suffix = [] + self.stylesheet = [] + self.generator = ('') + + def visit_field(self, node): + """ + Harvest docinfo fields and store it in global dictionary. + """ + key, val = [n.astext() for n in node] + key = key.lower() + Attrs.ATTRS[key] = val + + def visit_date(self, node): + """ + Store published date in global dictionary. + """ + Attrs.ATTRS['date'] = node.astext() + +class PreviewHTMLTranslator(CustomHTMLTranslator): + """ + Class for dislpay article in the browser as a preview. + """ + def __init__(self, document): + """ + Alter levels for the heading tags, define custom, blog specific + stylesheets. Note, that style_custom is present only locally to adjust + way of display the page + """ + CustomHTMLTranslator.__init__(self, document) + self.initial_header_level = 1 + self.section_level = 1 + # order of css files is important + self.default_stylesheets = ["css/widget_css_2_bundle.css", + "css/style_custom.css", + "css/style_blogger.css"] + self.stylesheet = [self.stylesheet_link % self.encode(css) \ + for css in self.default_stylesheets] + self.body_ = [] + + + def depart_docinfo(self, node): + """ + Overwrite body with some custom one. body_ will hold the first heading + with title of the document. + """ + self.body = self.body_ + + def visit_field(self, node): + """ + Additional 'keyword' for the ODF metadata + """ + key, node_ = [n.astext() for n in node] + key = key.lower() + if key == 'title': + self.head.append('%s\n' % self.encode(node_)) + self.body_.append('

' + '%s

\n' % self.encode(node_)) + +class BlogBodyWriter(Writer): + """ + Custom Writer class for generating HTML partial with the article + """ + def __init__(self): + Writer.__init__(self) + self.translator_class = NoHeaderHTMLTranslator + + def translate(self): + self.document.settings.output_encoding = "utf-8" + Writer.translate(self) + +class BlogPreviewWriter(Writer): + """ + Custom Writer class for generating full HTML of the article + """ + def __init__(self): + Writer.__init__(self) + self.translator_class = PreviewHTMLTranslator + + def translate(self): + self.document.settings.output_encoding = "utf-8" + Writer.translate(self) + + +def blogPreview(string): + """ + Returns partial HTML of the article, and attribute dictionary + string argument is an article in reST + """ + html_output = core.publish_string(string, writer=BlogPreviewWriter()) + html_output = html_output.strip() + html_output = html_output.replace("", "\n\n") + return html_output + +def blogArticleString(string): + """ + Returns partial HTML of the article, and attribute dictionary + string argument is an article in reST + """ + # reset ATTRS + Attrs.ATTRS = {} + html_output = core.publish_string(string, writer=BlogBodyWriter()) + html_output = html_output.strip() + html_output = html_output.replace("", "\n\n") + return html_output, Attrs.ATTRS + diff --git a/ftplugin/rst/commons.vim b/ftplugin/rst/commons.vim index 749b8e9..3574eeb 100644 --- a/ftplugin/rst/commons.vim +++ b/ftplugin/rst/commons.vim @@ -5,169 +5,6 @@ setlocal spell setlocal smartindent setlocal autoindent setlocal formatoptions=tcq "set VIms default - -if exists("b:did_rst_plugin") - finish " only load once -else - let b:did_rst_plugin = 1 -endif - -map :call Rst2Blogger() - -if !exists('*s:Rst2Blogger') - " Simple function, that translates reSt text into html with specified format, - " suitable to copy and paste into blogger post. - fun Rst2Blogger() - python << EOF -import re - -from docutils import core -from docutils import nodes -from docutils.parsers.rst import directives, Directive -from docutils.writers.html4css1 import Writer, HTMLTranslator - -from pygments import highlight -from pygments.lexers import get_lexer_by_name, TextLexer -from pygments.formatters import HtmlFormatter - -import vim - -class Pygments(Directive): - """ - Source code syntax hightlighting. - """ - required_arguments = 1 - optional_arguments = 0 - final_argument_whitespace = True - has_content = True - - def run(self): - self.assert_has_content() - try: - lexer = get_lexer_by_name(self.arguments[0]) - except ValueError: - # no lexer found - use the text one instead of an exception - lexer = TextLexer() - # take an arbitrary option if more than one is given - formatter = HtmlFormatter(noclasses=True) - parsed = highlight(u'\n'.join(self.content), lexer, formatter) - return [nodes.raw('', parsed, format='html')] - -directives.register_directive('sourcecode', Pygments) - -class NoHeaderHTMLTranslator(HTMLTranslator): - def __init__(self, document): - HTMLTranslator.__init__(self,document) - self.head_prefix = ['','','','',''] - self.body_prefix = [] - self.body_suffix = [] - self.stylesheet = [] - self.head = [] - self.meta = [] - self.generator = ('') - self.initial_header_level = 2 - self.section_level = 2 - - def visit_document(self, node): - pass - - def depart_document(self, node): - pass - - def visit_section(self, node): - pass - - def depart_section(self, node): - pass - - def visit_acronym(self, node): - node_text = node.children[0].astext() - node_text = node_text.replace('\n', ' ') - patt = re.compile(r'^(.+)\s<(.+)>') - - if patt.match(node_text): - node.children[0] = nodes.Text(patt.match(node_text).groups()[0]) - self.body.append(\ - self.starttag(node, 'acronym', - '', title=patt.match(node_text).groups()[1])) - - else: - self.body.append(self.starttag(node, 'acronym', '')) - - def visit_abbreviation(self, node): - node_text = node.children[0].astext() - node_text = node_text.replace('\n', ' ') - patt = re.compile(r'^(.+)\s<(.+)>') - - if patt.match(node_text): - node.children[0] = nodes.Text(patt.match(node_text).groups()[0]) - self.body.append(\ - self.starttag(node, 'abbr', - '', title=patt.match(node_text).groups()[1])) - - else: - self.body.append(self.starttag(node, 'abbr', '')) - - -_w = Writer() -_w.translator_class = NoHeaderHTMLTranslator - -def blogify(string): - return core.publish_string(string, writer=_w) - -bufcontent = "\n".join(vim.current.buffer) -name = vim.current.buffer.name -if name.lower().endswith(".rst"): - name = name[:-4] + ".html" - vim.command('new') - - vim.current.buffer[:] = blogify(bufcontent).split("\n") - try: - vim.command(r'silent! %s///g') - vim.command(r'silent! %s/<\/tt>/<\/code>/g') - except: - pass - try: - vim.command(r'silent! %s//\r\r/g') - except: - pass - vim.command('w %s' % name.replace(' ', '\ ')) - vim.command('bd') -else: - print "Ihis is not reSt file. File should have '.rst' extension." - -EOF - endfun -endif - -if !exists('*s:Restify') - " This is similar to that above, but creates full html document - fun Restify() - python << EOF -from docutils import core -from docutils.writers.html4css1 import Writer, HTMLTranslator -import vim - -_w = Writer() -_w.translator_class = HTMLTranslator - -def reSTify(string): - return core.publish_string(string,writer=_w) - -bufcontent = "\n".join(vim.current.buffer) -name = vim.current.buffer.name -if name.lower().endswith(".rst"): - name = name[:-4] + ".html" - vim.command('new') - - vim.current.buffer[:] = reSTify(bufcontent).split("\n") - vim.command(r'silent %s///g') - vim.command(r'silent %s/<\/tt>/<\/code>/g') - vim.command('w %s' % name) - vim.command('bd') -else: - print "It's not reSt file!" - -EOF - endfun -endif +let g:blogger_login="gryf73" +let g:blogger_name="rdobosz" +let g:blogger_browser=1 diff --git a/snippets/rst.snippets b/snippets/rst.snippets index 0f38b79..3404e1b 100644 --- a/snippets/rst.snippets +++ b/snippets/rst.snippets @@ -57,3 +57,12 @@ snippet figure :figclass: ${3:custclass} $2 +snippet article + :Title: ${1:Blog post title} + :Date: ${2:`strftime("%Y-%m-%d %H:%M:%S")`} + :Modified: ${3} + :Tags: ${4:blogtag1, blogtag2} + + ${5} + + .. more