/autoload/riv/rivlib/table.py

https://bitbucket.org/dryobates/riv.vim · Python · 281 lines · 200 code · 28 blank · 53 comment · 39 complexity · 21eee3b1f9fb2b39fcf15424d55b569a MD5 · raw file

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import vim
  4. import re
  5. from rivlib.utils import wstr_len
  6. from rivlib.pattern import riv_ptn
  7. def balance_tbl_col(lst):
  8. balanced_lst = []
  9. # get max col_num of each row
  10. # and change the non list
  11. col_num = 0
  12. for row in lst:
  13. if isinstance(row,list):
  14. row_len = len(row)
  15. if row_len > col_num: col_num = row_len
  16. if col_num == 0:
  17. return balanced_lst # []
  18. for i,row in enumerate(lst):
  19. if isinstance(row,list):
  20. row_len = len(row)
  21. tmp_row = row[:]
  22. if col_num > row_len:
  23. tmp_row.extend([" " for i in range(col_num - row_len)])
  24. else:
  25. tmp_row = [row]
  26. tmp_row.extend([" " for i in range(col_num - 1)])
  27. balanced_lst.append(tmp_row)
  28. return balanced_lst
  29. class RestTable:
  30. def __init__(self, lst):
  31. '''
  32. receive a 2D list and init the table
  33. '''
  34. self.list = balance_tbl_col(lst)
  35. self.row = len(self.list)
  36. if self.row > 0:
  37. self.col = len(self.list[0])
  38. else:
  39. self.col = 0
  40. self.norm_col()
  41. self.parse_col_max_width()
  42. def __repr__(self):
  43. return "<RestTable: %d Row %d Col>" % (self.row, self.col)
  44. def norm_col(self):
  45. '''
  46. remove last space of each col,
  47. and add a preceding space if not exists
  48. '''
  49. for row in self.list:
  50. for i,col in enumerate(row):
  51. row[i] = col.rstrip()
  52. if not row[i].startswith(" "):
  53. row[i] = " " + row[i]
  54. def parse_col_max_width(self):
  55. '''
  56. A list contains the max width of each column.
  57. e.g | e | e | ee |
  58. | eeeee | e | e |
  59. | ee | eee | e |
  60. will set col_max_w to [5 , 3 ,2]
  61. '''
  62. v_tbl = zip(*self.list)
  63. col_max_w = []
  64. for v_cols in v_tbl:
  65. max_len = 0 # two space
  66. for col in v_cols:
  67. if re.match(' \|S| \|H',col):
  68. continue
  69. c_len = wstr_len(col)
  70. if c_len > max_len:
  71. max_len = c_len
  72. col_max_w.append(max_len)
  73. self.col_max_w = col_max_w
  74. def update(self):
  75. pass
  76. # def add_line(self,idx):
  77. # ''' add a empty line in table with specified idx'''
  78. # if idx< self.row:
  79. # if self.list[idx][0] == "|S":
  80. # self.list.insert(idx+1, ["|S"])
  81. # self.list.insert(idx+1, [ " " for i in range(self.col)])
  82. # # self.list = balance_tbl_col(self.list)
  83. def add_line(self,idx,typ):
  84. if typ == 'cont' or self.list[idx][0] == " |S" or self.list[idx][0] == " |H":
  85. c = idx+1
  86. else:
  87. c = idx+2
  88. if typ == 'sepr':
  89. self.list.insert(idx+1, [" |S"])
  90. elif typ == 'head':
  91. self.list.insert(idx+1, [" |H"])
  92. self.list.insert(c, [ " " for i in range(self.col)])
  93. def lines(self, indent=0):
  94. """
  95. indent: the number of preceding whitespace.
  96. return the lines of table.
  97. """
  98. # reverse the table and get the max_len of cols.
  99. idt = " " * indent
  100. s_sep = "".join(["+" + ("-" * (l + 1)) for l in self.col_max_w])
  101. line_sep = idt + s_sep + "+"
  102. line_head = re.sub('-','=',line_sep)
  103. lines = []
  104. for row in self.list:
  105. if row[0] == " |S":
  106. lines.append(line_sep)
  107. elif row[0] == " |H":
  108. lines.append(line_head)
  109. else:
  110. s_col = ""
  111. for i , cell in enumerate(row):
  112. c = cell
  113. s_col += "|" + c + (" " * (self.col_max_w[i] - wstr_len(c))) + " "
  114. line_con = idt + s_col + "|"
  115. lines.append(line_con)
  116. if lines:
  117. if lines[-1] != line_sep:
  118. lines.append(line_sep)
  119. if lines[0] != line_sep:
  120. lines.insert(0,line_sep)
  121. return lines
  122. class GetTable:
  123. def __init__(self,lnum=0):
  124. '''
  125. --lnum : the lnum of table
  126. default is vim.current.window.cursor[0]
  127. --buffer : the buffer of table
  128. default is vim.current.buffer
  129. '''
  130. self.buf = vim.current.buffer
  131. if lnum == 0:
  132. lnum=vim.current.window.cursor[0]
  133. bgn, end = self.get_table_range(lnum)
  134. self.bgn, self.end = bgn, end
  135. if bgn == 0 and end == 0:
  136. self.table = None
  137. else:
  138. self.table = self.table_in_range(bgn, end)
  139. indent = riv_ptn.indent.match(self.buf[lnum - 1]).end()
  140. self.indent = indent
  141. def table_in_range(self, start, end):
  142. '''
  143. parse line from start to end
  144. +---------------------+---------------+-------------+
  145. | 1xxxxxxx | xxxxx | xxxxxasxasx |
  146. +=====================+===============+=============+
  147. | 2xxxxxxx | xxxxxxaxsxasx | xxxxx |
  148. | 3xxxxaxsaxsaxsaxxxx | xxxxx | xxxxx |
  149. +---------------------+---------------+-------------+
  150. | 4xxxxxxx | xxxxx | |
  151. +---------------------+---------------+-------------+
  152. will be parse to a 2 dimension list.
  153. [ ["1xxx","xxx","xxxas"],
  154. ["2xxxx","xxxxx","xxxx"]
  155. ....
  156. ]
  157. and returns a 3 col and 3 row table
  158. to create a table with large cell
  159. we should get the line and it's sep's
  160. if have line and sep , then it's one row cell
  161. if it have continues lines, then it's multi cell
  162. if we want to add multi col cell. we must record cell 's sep pos
  163. '''
  164. max_col = 0
  165. rows = []
  166. for i in range(start - 1, end):
  167. if riv_ptn.table_con.match(self.buf[i]):
  168. cols = [i.group() for i in riv_ptn.table_cel.finditer(self.buf[i])]
  169. c_len = len(cols)
  170. if max_col < c_len:
  171. max_col = c_len
  172. rows.append(cols)
  173. elif riv_ptn.table_sepr.match(self.buf[i]) :
  174. # this will cause min col_len to 2
  175. rows.append(['|S']) # A sep_str workround
  176. elif riv_ptn.table_head.match(self.buf[i]) :
  177. rows.append(['|H'])
  178. return RestTable(rows)
  179. def get_table_range(self, row):
  180. if riv_ptn.table_all.match(self.buf[row - 1]):
  181. bgn, end = [row, row]
  182. else:
  183. return [0, 0]
  184. for i in range(row, 0, -1):
  185. if riv_ptn.table_all.match(self.buf[i - 1]):
  186. bgn = i
  187. else:
  188. break
  189. for i in range(row, len(self.buf)):
  190. if riv_ptn.table_all.match(self.buf[i - 1]):
  191. end = i
  192. else:
  193. break
  194. return [bgn, end]
  195. def format_table(self):
  196. if self.table:
  197. lines = self.table.lines(self.indent)
  198. bgn,end = self.bgn, self.end
  199. buf = self.buf
  200. d_bgn = 0
  201. for row in range(bgn-1,end):
  202. if buf[row] != lines[0]:
  203. buf[row] = lines[0]
  204. if lines:
  205. del lines[0]
  206. elif riv_ptn.table_all.match(buf[row]):
  207. d_bgn = row
  208. if d_bgn:
  209. del buf[d_bgn:end]
  210. if lines:
  211. buf.append(lines, end)
  212. def add_line(self,typ="cont"):
  213. if self.table:
  214. idx = vim.current.window.cursor[0] - self.bgn
  215. # if row=="1":
  216. self.table.add_line(idx,typ)
  217. # else:
  218. # self.table.add_line(idx,2)
  219. self.format_table()
  220. if __name__ == "__main__":
  221. """
  222. +----------+-------+--------------------------+
  223. | 1xxxxx | xxx | xxx |
  224. +==========+=======+==========================+
  225. | 2xxxxxxx | xxasx | xxxxxfj ioejfioaw jijioj |
  226. +----------+-------+--------------------------+
  227. | | | |
  228. +----------+-------+--------------------------+
  229. | | | |
  230. +----------+-------+--------------------------+
  231. | 3xxxxa | xxxxx | xxxxx |
  232. +----------+-------+--------------------------+
  233. | | | |
  234. +----------+-------+--------------------------+
  235. """
  236. buftable = GetTable(259)
  237. buftable.table.add_line(3)
  238. print buftable.table
  239. print "\n".join(buftable.table.lines())
  240. # GetTable().add_row()
  241. # string = '当当当当当当'
  242. # print wstr_len(string)