mirror of
1
0
Fork 0
ultimate-vim/vim_plugin_candinates_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_markdown.py

319 lines
12 KiB
Python

# voom_mode_markdown.py
# Last Modified: 2012-04-02
# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x
# Website: http://www.vim.org/scripts/script.php?script_id=2657
# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com)
# License: This program is free software. It comes without any warranty,
# to the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want To
# Public License, Version 2, as published by Sam Hocevar.
# See http://sam.zoy.org/wtfpl/COPYING for more details.
"""
VOoM markup mode for Markdown headers.
See |voom_mode_markdown|, ../../doc/voom.txt#*voom_mode_markdown*
"""
### NOTES
# When outline operation changes level, it has to deal with two ambiguities:
# a) Level 1 and 2 headline can use underline-style or hashes-style.
# b) Hashes-style can have or not have closing hashes.
# To determine current preferences: check first headline at level <3 and check
# first headline with hashes. This must be done in hook_makeOutline().
# (Save in VO, similar to reST mode.) Cannot be done during outline operation,
# that is in hook_doBodyAfterOop().
# Defaults: use underline, use closing hashes.
levels_ads = {1:'=', 2:'-'}
def hook_makeOutline(VO, blines):
"""Return (tlines, bnodes, levels) for Body lines blines.
blines is either Vim buffer object (Body) or list of buffer lines.
"""
Z = len(blines)
tlines, bnodes, levels = [], [], []
tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append
# trailing whitespace is always removed with rstrip()
#
# hashes-style, overides underline-style
# abcde L2, blines[i-1]
# ## head L1, blines[i] -- current line
#
# underline-style
# head L2, blines[i-1] -- title line, not blank, does not start with #
# ------ L1, blines[i] -- current line, any number of = or - only
L1, L2 = '',''
# Set this once when headline with level 1 or 2 is encountered.
# 0 or 1 -- False, use underline-style (default); 2 -- True, use hashes-style
useHash = 0
# Set this once when headline with hashes is encountered.
# 0 or 1 -- True, use closing hashes (default); 2 -- False, do not use closing hashes
useCloseHash = 0
gotHead = False
for i in xrange(Z):
L2 = L1
L1 = blines[i].rstrip()
if L1.startswith('#'):
gotHead = True
lev = len(L1) - len(L1.lstrip('#'))
bnode = i+1
head = L1.strip('#').strip()
elif L2 and L1.startswith('=') and L1.lstrip('=')=='':
gotHead = True
lev = 1
head = L2.strip()
bnode = i
elif L2 and L1.startswith('-') and L1.lstrip('-')=='':
gotHead = True
lev = 2
head = L2.strip()
bnode = i
else:
continue
if gotHead:
gotHead = False
if not useHash and lev < 3:
if L1.startswith('#'):
useHash = 2
else:
useHash = 1
if not useCloseHash and L1.startswith('#'):
if L1.endswith('#'):
useCloseHash = 1
else:
useCloseHash = 2
L1, L2 = '',''
tline = ' %s|%s' %('. '*(lev-1), head)
tlines_add(tline)
bnodes_add(bnode)
levels_add(lev)
# don't clobber these when parsing clipboard during Paste
# which is the only time blines is not Body
if blines is VO.Body:
VO.useHash = useHash == 2
VO.useCloseHash = useCloseHash < 2
return (tlines, bnodes, levels)
def hook_newHeadline(VO, level, blnum, tlnum):
"""Return (tree_head, bodyLines).
tree_head is new headline string in Tree buffer (text after |).
bodyLines is list of lines to insert in Body buffer.
"""
tree_head = 'NewHeadline'
if level < 3 and not VO.useHash:
bodyLines = [tree_head, levels_ads[level]*11, '']
else:
lev = '#'*level
if VO.useCloseHash:
bodyLines = ['%s %s %s' %(lev, tree_head, lev), '']
else:
bodyLines = ['%s %s' %(lev, tree_head), '']
# Add blank line when inserting after non-blank Body line.
if VO.Body[blnum-1].strip():
bodyLines[0:0] = ['']
return (tree_head, bodyLines)
#def hook_changeLevBodyHead(VO, h, levDelta):
# DO NOT CREATE THIS HOOK
def hook_doBodyAfterOop(VO, oop, levDelta, blnum1, tlnum1, blnum2, tlnum2, blnumCut, tlnumCut):
# this is instead of hook_changeLevBodyHead()
# Based on reST mode function. Insert blank separator lines if missing,
# even though they are not important for Markdown headlines.
#print oop, levDelta, blnum1, tlnum1, blnum2, tlnum2, tlnumCut, blnumCut
Body = VO.Body
Z = len(Body)
bnodes, levels = VO.bnodes, VO.levels
ENC = VO.enc
# blnum1 blnum2 is first and last lnums of Body region pasted, inserted
# during up/down, or promoted/demoted.
if blnum1:
assert blnum1 == bnodes[tlnum1-1]
if tlnum2 < len(bnodes):
assert blnum2 == bnodes[tlnum2]-1
else:
assert blnum2 == Z
# blnumCut is Body lnum after which a region was removed during 'cut',
# 'up', 'down'. Need this to check if there is blank line between nodes
# used to be separated by the cut/moved region.
if blnumCut:
if tlnumCut < len(bnodes):
assert blnumCut == bnodes[tlnumCut]-1
else:
assert blnumCut == Z
# Total number of added lines minus number of deleted lines.
b_delta = 0
### After 'cut' or 'up': insert blank line if there is none
# between the nodes used to be separated by the cut/moved region.
if (oop=='cut' or oop=='up') and (0 < blnumCut < Z) and Body[blnumCut-1].strip():
Body[blnumCut:blnumCut] = ['']
update_bnodes(VO, tlnumCut+1 ,1)
b_delta+=1
if oop=='cut':
return
### Make sure there is blank line after the last node in the region:
# insert blank line after blnum2 if blnum2 is not blank, that is insert
# blank line before bnode at tlnum2+1.
if blnum2 < Z and Body[blnum2-1].strip():
Body[blnum2:blnum2] = ['']
update_bnodes(VO, tlnum2+1 ,1)
b_delta+=1
### Change levels and/or formats of headlines in the affected region.
# Always do this after Paste, even if level is unchanged -- format can
# be different when pasting from other outlines.
# Examine each headline, from bottom to top, and change level and/or format.
# To change from hashes to underline-style:
# strip hashes, strip whitespace;
# insert underline.
# To change from underline to hashes-style:
# delete underline;
# insert hashes.
# Update bnodes after inserting or deleting a line.
# hash-style underline-style
#
# L0 L0 Body[bln-2]
# ## head L1 head L1 <--bnode Body[bln-1]
# L2 ---- L2 Body[bln]
# L3 L3 Body[bln+1]
if levDelta or oop=='paste':
for i in xrange(tlnum2, tlnum1-1, -1):
# required level (VO.levels has been updated)
lev = levels[i-1]
# current level from which to change to lev
lev_ = lev - levDelta
# Body headline (bnode) and next line
bln = bnodes[i-1]
L1 = Body[bln-1].rstrip()
if bln+1 < len(Body):
L2 = Body[bln].rstrip()
else:
L2 = ''
# get current headline format
hasHash, hasCloseHash = False, VO.useCloseHash
if L1.startswith('#'):
hasHash = True
if L1.endswith('#'):
hasCloseHash = True
else:
hasCloseHash = False
# get desired headline format
if oop=='paste':
if lev > 2:
useHash = True
else:
useHash = VO.useHash
useCloseHash = VO.useCloseHash
elif lev < 3 and lev_ < 3:
useHash = hasHash
useCloseHash = hasCloseHash
elif lev > 2 and lev_ > 2:
useHash = True
useCloseHash = hasCloseHash
elif lev < 3 and lev_ > 2:
useHash = VO.useHash
useCloseHash = VO.useCloseHash
elif lev > 2 and lev_ < 3:
useHash = True
useCloseHash = hasCloseHash
else:
assert False
#print useHash, hasHash, ';', useCloseHash, hasCloseHash
# change headline level and/or format
# underline-style unchanged, only adjust level of underline
if not useHash and not hasHash:
if not levDelta: continue
Body[bln] = levels_ads[lev]*len(L2)
# hashes-style unchanged, adjust level of hashes and add/remove closing hashes
elif useHash and hasHash:
# no format change, there are closing hashes
if useCloseHash and hasCloseHash:
if not levDelta: continue
Body[bln-1] = '%s%s%s' %('#'*lev, L1.strip('#'), '#'*lev)
# no format change, there are no closing hashes
elif not useCloseHash and not hasCloseHash:
if not levDelta: continue
Body[bln-1] = '%s%s' %('#'*lev, L1.lstrip('#'))
# add closing hashes
elif useCloseHash and not hasCloseHash:
Body[bln-1] = '%s%s %s' %('#'*lev, L1.strip('#').rstrip(), '#'*lev)
# remove closing hashes
elif not useCloseHash and hasCloseHash:
Body[bln-1] = '%s%s' %('#'*lev, L1.strip('#').rstrip())
# insert underline, remove hashes
elif not useHash and hasHash:
L1 = L1.strip('#').strip()
Body[bln-1] = L1
# insert underline
Body[bln:bln] = [levels_ads[lev]*len(L1.decode(ENC,'replace'))]
update_bnodes(VO, i+1, 1)
b_delta+=1
# remove underline, insert hashes
elif useHash and not hasHash:
if useCloseHash:
Body[bln-1] = '%s %s %s' %('#'*lev, L1.strip('#').strip(), '#'*lev)
else:
Body[bln-1] = '%s %s' %('#'*lev, L1.strip('#').strip())
# delete underline
Body[bln:bln+1] = []
update_bnodes(VO, i+1, -1)
b_delta-=1
### Make sure first headline is preceded by a blank line.
blnum1 = bnodes[tlnum1-1]
if blnum1 > 1 and Body[blnum1-2].strip():
Body[blnum1-1:blnum1-1] = ['']
update_bnodes(VO, tlnum1 ,1)
b_delta+=1
### After 'down' : insert blank line if there is none
# between the nodes used to be separated by the moved region.
if oop=='down' and (0 < blnumCut < Z) and Body[blnumCut-1].strip():
Body[blnumCut:blnumCut] = ['']
update_bnodes(VO, tlnumCut+1 ,1)
b_delta+=1
assert len(Body) == Z + b_delta
def update_bnodes(VO, tlnum, delta):
"""Update VO.bnodes by adding/substracting delta to each bnode
starting with bnode at tlnum and to the end.
"""
bnodes = VO.bnodes
for i in xrange(tlnum, len(bnodes)+1):
bnodes[i-1] += delta