vim shortcut keys for blog composition and publishing
I like the jekyll-way of blogging, it gives me almost all what I desired for blogging. The essential here (at least for me) is simplicity (or “minimalism”) - simply editing blog content as a pure text file with your whatever favorite pure text editor , and uploading the blog file with whatever file synchrozation technique, that’s it.
but still, there are some last pieces of annoying work to do here, if you use it in a daily base:
-
what if, you want to publish a block of marked/selected texts from your editor (like the “visually selected text” in vim), as a blog post?
-
there is no official asciidoc support (yet), but markdown is kind of a poor markup language. So how to also publish a selected range of asciidoc-formatted texts, into a blog post (I do this for those heavy/long/serious articles)?
-
how to make the file synchronization (git/ftp/scp/rsync/whatever) even simpler -say, with one or two key strokes?
plus, a vimer typically prefer to have all things done , without leaving vim -that is to compact all external commands to be executed, into a few keystroke shortcut maps…
here is some final(targetted) quick keystrokes, each accomplishing one task:
",ji select blog id: 1 or 2
",js compose a new post from scratch/start (no pre-selected content)
in a new buffer(no tab) call: ->CMD:JekyllPost->FUNC:JekyllPost
visual mode, call: JekyllPostRange<CR>
",jp compose a new post with current (selected or whole) content
in a new tab, call: JekyllPostRange<CR>
nn: use whole current file as content of new post (in new tab)
vn: use selected text as content of new post(in new tab)
",ja publish asciidoc blog to jekyll, normal or visual mode
",jA publish asciidoc blog to jekyll, normal mode only, support
user command-line selection
",jg jekyll git external commands
",jl list all existing posts for current blog. call :JekyllList<CR>
",jt jekyll theme selector
"some not-oftenly-used keys
",jP :quick push (don't wait user input) and review
",jd append discus code in the file
",jb :JekyllBuild<CR>
",jy prefix yaml header in the file
I find myself use ,ji ,jl ,ja and ,jg most of the time.
that is:
-
to compose a new post from scratch, press ,js
-
to start a new post from a range of selected text, just visual select them and press ,jp same keys for the whole texts
-
same as 2, but mainly for asciidoc users, so it’s a lot heavier. The selected text must be in asciidoc format. press ,ja will guide the user through a few Q-n-As (each with default answer prompted). These will end up with all required parameters feed into asciidoc engine and get the final doc (html, html5) , generated. the source text will also be saved in a seperate accompanying file as a backup (but won’t get published by default). same keystroke also works for whole texts, if no texts were selected (no range).
-
if the blog looks OK, press ,jg to get it published. Depending on which blog it belongs to, this keystroke will call different file sync technique to get the work done:
-
for blog1 , which is my public blog, call git commands sequence to push the source files to github site, which , will be responsible for the site generation
-
for blog2, which is my work blog , runing on an internal web/ssh server, without a git server (like github) deployed :
-
first generate whole site into a local folder using my local jekyll engine
-
then call rsync to make sure all files will be synchronized to the server
-
-
-
to switch between 2 sites, prss ‘,ji’. all posts will be saved into the corresponding blog folders repectively.
and here is the vim script codes:
"jekyll---
"
"(optional)turn off all additional backup files
nn ,nf <esc>:set nobackup<CR>:set noudf<CR>:set noswf<CR>
"set nobackup noudf noswf
"yaml highlight
"this doesn't work
"function! Jekyll()
" execute "autocmd BufNewFile,BufRead " . g:jekyll_path . "/* syn match jekyllYamlFrontmatter /\\%^---\\_.\\{-}---$/ contains=@Spell"
"endf
"this works
function! JekyllY()
unlet b:current_syntax
syntax include @Yaml syntax/yaml.vim
syntax region yamlFrontmatter start=/\%^---$/ end=/^---$/ keepend contains=@Yaml,@Spell
let b:current_syntax='markdown'
endf
nmap ,jy :call JekyllY()<CR>,nf
"vars and maps
"ping:
if !exists('g:jekyll_post_created')
let g:jekyll_post_created = "%Y %b %d %X"
endif
"constant: blog1
if !exists('g:jekyll_path')
let g:jekyll_path="~/mytest-project/jekyll-site/pinggit.github.com"
"let g:jekyll_post_suffix = "textile"
"let g:jekyll_post_published = "false"
endif
"constant: blog2
if !exists('g:jekyll_path2')
let g:jekyll_path2="~/mytest-project/jekyll-site/blog.clone"
let g:jekyll_path2="~/mytest-project/jekyll-site/juniperblog"
endif
"save the path change
if !exists('g:jekyll_currentpath')
let g:jekyll_currentpath=g:jekyll_path
endif
if !exists('g:jekyll_title')
let g:jekyll_title = "dummy"
endif
if !exists('g:jekyll_title_pattern')
let g:jekyll_title_pattern = "[ '\"]"
endif
if !exists('g:jekyll_post_suffix')
let g:jekyll_post_suffix = "markdown"
endif
if !exists('g:jekyll_prompt_tags')
let g:jekyll_prompt_tags = "life"
endif
if !exists('g:jekyll_prompt_categories')
let g:jekyll_prompt_categories = "life"
endif
if !exists('g:jekyll_asciidoc_insertyaml')
"turn off yaml, currently page looks ugly after enabling this
let g:jekyll_asciidoc_insertyaml = 1
endif
if !exists('g:jekyll_template')
let g:jekyll_template = [""]
endif
if !exists('g:jekyll_post_published')
let g:jekyll_post_published = "false"
endif
if !exists('g:jekyll_upload')
let g:jekyll_upload= "github"
endif
if !exists('g:jekyll_blogid')
let g:jekyll_blogid= 1
endif
if !exists('g:jekyll_disqus_template')
let g:jekyll_disqus_template="~/mytest-project/jekyll-site/pinggit.github.com/disqus-template.txt"
endif
nn ,jb :JekyllBuild<CR>
nn ,js :JekyllPost<CR>
map ,jp :JekyllPostRange<CR>
nn ,jl :JekyllList<CR>
nn <expr> ,jL RangerChooser(g:jekyll_path . "/_posts") <bar> sleep 3
"checkout and post
"com! -nargs=1 JPost execute '!rake post title=<q-args>' | e YOUR_FILE
"Asciidoc to Jekyll---
function s:esctitle(str)
let str = a:str
let str = tolower(str)
let str = substitute(str, g:jekyll_title_pattern, '-', 'g')
let str = substitute(str, '\(--\)\+', '-', 'g')
let str = substitute(str, '\(^-\|-$\)', '', 'g')
return str
endfunction
function JekyllTemplate() "
"always build template per current data:
"if g:jekyll_template == ''
let tags = input("use previous tag? [" .
\g:jekyll_prompt_tags .
\"](y/NEW TAG):",'y')
if tags == 'y'
let tags = g:jekyll_prompt_tags "try, save it
else
if tags != ''
let g:jekyll_prompt_tags=tags
endif
endif
let categories = input("use previous categories? [" .
\g:jekyll_prompt_categories .
\"](y/NEW CATEGORIES):" , 'y')
if categories == 'y'
let categories = g:jekyll_prompt_categories "try, save it
else
if categories != ''
let g:jekyll_prompt_categories=categories
endif
endif
let created = g:jekyll_post_created
if created == "epoch"
let created = localtime()
elseif created != ""
let created = strftime(created)
endif
"let template = ["---", "layout: post", "title: \"" . g:jekyll_title . "\"", "published: " . published]
let template = ["---", "layout: post", "title: \"" . g:jekyll_title . "\""]
if created != ""
call add(template, "created: " . created)
endif
if tags != ""
call add(template, "tags: [" . tags . "]")
endif
if categories != ""
call add(template, "categories: [" . categories . "]")
endif
let published = g:jekyll_post_published
call add(template, "published: " . published )
call extend(template,["---", ""])
return template
endf
"(02/20/2013) publish a asciidoc-generated html blog
"function! A2J(leveloffset, title)
function! JekyllPrefixYamlHeader() "
let template=JekyllTemplate()
let err1 = append(0, template)
endf
map ,jy :call JekyllPrefixYamlHeader()<CR>
function! JekyllAppendDisqus() "
let template4disqus=g:jekyll_disqus_template
exec "normal Go\<CR>\<CR>\<esc>"
exec "read" . template4disqus
endf
map ,jd :call JekyllAppendDisqus()<CR>
function! A2J(...) range "
"initialization
let os=-1
let title=g:jekyll_title
"not sure if better than html backend
let format="html"
let toc="-a toc2"
let g:lasttab = tabpagenr()
"ping:yank the selected texts
exe a:firstline . "," . a:lastline . "yank"
"if 1st params is not given (provided 0 params), ask for it
if a:0 == 0
let os=input("use default offset?" . '[' . os . ']' . "(y/0/-1/-2/-3/..):",'y')
if os == 'y'
let os = -1
endif
"if 1 param is given, use it as offset
else
let os=a:1
endif
"if 2nd param is not given, ask for it
if a:0 <= 1
let title=input("want to use previous/default title?" .
\'[' . g:jekyll_title . ']' . "(y/NEW TITLE):" ,
\'y')
if title=='y'
let title=g:jekyll_title
else
if title != ""
let g:jekyll_title = title
endif
endif
else
let title=a:2
let g:jekyll_title = title
endif
"if 3rd param is not given, ask for it
if a:0 <= 2
let supported_backend="1-html,2-html5,3-blogger"
let format_number=input("use the current asciidoc backend format?" .
\'[' . format . ']' .
\supported_backend . ':', 'y')
if format_number==1
let format="html"
elseif format_number==2
let format_number="html5"
elseif format_number==3
let format="blogger"
else
endif
elseif a:0 == 3
let format=a:3
else
echo "too much parameters: max is 3"
endif
let toc=input("want to use toc?[0:no-toc,1:toc,2:toc2]" , "2")
if toc=='0'
let toc=''
elseif toc=='1'
let toc='-a toc'
elseif toc=='2'
let toc='-a toc2'
else
endif
if title == ''
let title = input("Post title (better) not be empty: ")
if title != ''
let g:jekyll_title = title
endif
endif
if title != ''
let g:jekyll_title = title
let file_name = strftime("%Y-%m-%d-") .
\s:esctitle(title)
"exe "tabedit " . g:jekyll_path . "/_posts/" . file_name
" let path=input("use default/previous path?" . '[' . g:jekyll_path . ']' . "(y/<NEW PATH>):",'y')
" if path=='y'
" let path=g:jekyll_path
" else
" if path != ''
" let g:jekyll_path=path
" endif
" endif
let path=input("use default/previous path?" . '[' . g:jekyll_path . ']' . "(y/n):",'y')
if path=='y'
let g:jekyll_blogid=1
let path=g:jekyll_path
elseif path=='n'
let path=input("use default/previous path?" . '[' . g:jekyll_path2 . ']' . "(y/<NEW PATH>):",'y')
if path=='y'
let path=g:jekyll_path2
let g:jekyll_blogid=2
let g:jekyll_upload='rsync'
endif
let g:jekyll_currentpath=path
endif
let full_name = path . "/_posts/" . file_name . "." . "html"
"let g:AsciidocCmd = ":0,\$w !asciidoc
let g:AsciidocCmd = ':' . a:firstline . ',' . a:lastline .
\"w !asciidoc
\-a numbered " . toc . " " . "
\-a toclevels=4
\-a iconsdir=/home/ping/bin/asciidoc-8.6.8/images/icons
\-a icons -a data-uri -a showcomments
\-a max-width=85em -a theme=volnitsky
\-o " . full_name . " " . "
\-a leveloffset=" . os . "@" . " " . "
\-b " . format . " - "
"\-o " . "'" . full_name . "'" . " " . "
echo "\nthe following CLI will be executed\n" g:AsciidocCmd
let isSure=input("are you sure?(y/n):",'y')
if isSure == 'y'
exec g:AsciidocCmd
"save the original selected texts and prepend with yaml
let full_name_asciidoc = path . "/_posts/" . file_name . "." . "txt"
exe "tabnew " . full_name_asciidoc
call JekyllPrefixYamlHeader()
exe "normal! p"
"call JekyllAppendDisqus()
exec "wq!"
"try: insert template(yaml front end) in html file
exe "tabnew " . full_name
exe "normal u"
"prepend yaml in HTML. since yaml insertion in asciidoc generated
"html make it looks ugly, make it optional
if g:jekyll_asciidoc_insertyaml
call JekyllPrefixYamlHeader()
endif
"append disqus code in HTML
"let err2 = append(line('$'), "# THE END")
"call JekyllAppendDisqus()
"save the new webpage and quit ,then go back to original tab
"exec "wq!"
"exec "tabn " . g:lasttab
else
"this doesn't work to user cmd. good for expr map
return g:AsciidocCmd
endif
endif
endf
"todo:
"gmail thread: vim script: repeat(\<left>)
"need to archive same with ,ja, when user say no, pop cmds but no exec..
"it looks this is not possible (or sth I don't know)
"command! -nargs=* A2J :call A2J(<f-args>)
com! -range=% -nargs=* A2J :<line1>,<line2>call A2J(<f-args>)
"map <expr> ,ja ':' . A2J(-1, "new_post") . repeat("\<left>", 24)
"no need ':' since the func will return a ':'
"support: the option of presenting CLIes for user selection
"don't support: don't work for visual range
"nmap <expr> ,jA A2J(-1) . repeat("\<left>", 24)
nmap <expr> ,jA A2J(-1) . "\<c-b>" . repeat("\<delete>",3)
"support: all non-range or range
"don't support: returning CLIes for user selection
map ,ja :A2J
"JekyllGit
"this works, but too ugly
"nn ,gg :!git add -A .;git commit -m "msg: ";git push origin master"
" \<left><left><left><left><left><left><left>
" \<left><left><left><left><left><left><left>
" \<left><left><left><left><left><left><left>
" \<left><left><left><left><left>
"this is much better
"todo: need to save original path and restore after git finished
"select right folder, lcd into it, then pop up git commands for user's selection
"let g:jekyll_currentpath=g:jekyll_path
function! JekyllGit(commitmsg)
let current_blogid=input("is current blog ID " . g:jekyll_blogid. '? (y/n/newID)' , 'y')
if current_blogid=='y'
let current_blogid=g:jekyll_blogid
endif
if current_blogid=='n'
if g:jekyll_blogid==1
let current_blogid=2
elseif g:jekyll_blogid==2
let current_blogid=1
else
endif
endif
if current_blogid==1
let g:jekyll_upload='github'
let current_path=g:jekyll_path
elseif current_blogid==2
let g:jekyll_upload='rsync' "set proper upload method
let current_path=g:jekyll_path2
else
endif
let g:jekyll_blogid=current_blogid
"this doesn't work
":lcd current_path
"this works
let original_path=getcwd()
exec "lcd" . current_path
echo "original path" . '[' . original_path . ']' . "is saved." "\n"
echo "lcd into new path" . '[' . current_path . ']' . "\n"
let commitmsg=a:commitmsg
let commitmsg = input ("use last msg[" . g:jekyll_title . "]?[y/NEW TITLE]:" , 'y')
if commitmsg == 'y'
let commitmsg = g:jekyll_title
else
let g:jekyll_title = commitmsg
endif
if g:jekyll_blogid==1
let generate='sleep 1'
let upload="git push origin master"
elseif g:jekyll_blogid==2
let generate='jekyll --safe --no-auto'
"todo: -a is a bit slow, --size-only is faster?
let upload="
\rsync --delete -vqrlczP " .
\current_path .
\"/_site/ pings@svl-jtac-tool01:public_html/myblog/"
else
let upload="git push origin master"
endif
let GitCmd="!
\git add -A .;
\git commit -m \"" .
\commitmsg . "\";" . generate . ";" . upload . '&'
"\repeat("\<left>", 30)
"return to original path
"exec "lcd" . original_path
"there must be at least one ':' in returned cmds!
return ":" . GitCmd
endf
"command! -nargs=* JekyllGit call JekyllGit(<q-args>)
command! -nargs=* JekyllGit exec JekyllGit(<q-args>)
nn <expr> ,jg JekyllGit(g:jekyll_title) . repeat("\<left>", 24)
func JekyllThemeToggle()
let supported_themes="\n 1-twitter\n 2-holligan\n 3-the-minimum\n 4-the-program\n 5-dinky\n 6-mark-reid\n 7-tom\n"
let theme=input("use which theme?" . "\n[" . supported_themes . "]\n" . "(1/2/3/..):",'1')
if theme==1
let theme="twitter"
elseif theme==2
let theme="hooligan"
elseif theme==3
let theme="the-minimum"
elseif theme==4
let theme="the-program"
elseif theme==5
let theme="dinky"
elseif theme==6
let theme="mark-reid"
elseif theme==7
let theme="tom"
else
endif
let theme_change_cmd=":!rake theme:switch name=\"" . theme . "\""
return theme_change_cmd
endf
nn <expr> ,jt JekyllThemeToggle()
"quick publish the post (git push + w3m review)
"pending issues:
" don't know how to prompt&wait for user input
map ,jP :
\exec JekyllGit("g:jekyll_title") . repeat("\<left>", 24) <Bar>
\!w3m http://pinggit.github.com
\<CR>
function! JekyllSetBlogID(blogid) "
let g:jekyll_blogid=a:blogid
if g:jekyll_blogid==1
let g:jekyll_currentpath=g:jekyll_path
let g:jekyll_upload="github"
elseif g:jekyll_blogid==2
let g:jekyll_currentpath=g:jekyll_path2
let g:jekyll_upload="rsync"
else
endif
exec "lcd" . g:jekyll_currentpath
endf
nn ,ji :call JekyllSetBlogID(1)
blog comments powered by Disqus