home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 June / maximum-cd-2011-06.iso / DiscContents / LibO_3.3.1_Win_x86_install_multi.exe / libreoffice1.cab / FormatParagraph.py < prev    next >
Encoding:
Python Source  |  2011-02-15  |  5.6 KB  |  150 lines

  1. # Extension to format a paragraph
  2.  
  3. # Does basic, standard text formatting, and also understands Python
  4. # comment blocks.  Thus, for editing Python source code, this
  5. # extension is really only suitable for reformatting these comment
  6. # blocks or triple-quoted strings.
  7.  
  8. # Known problems with comment reformatting:
  9. # * If there is a selection marked, and the first line of the
  10. #   selection is not complete, the block will probably not be detected
  11. #   as comments, and will have the normal "text formatting" rules
  12. #   applied.
  13. # * If a comment block has leading whitespace that mixes tabs and
  14. #   spaces, they will not be considered part of the same block.
  15. # * Fancy comments, like this bulleted list, arent handled :-)
  16.  
  17. import re
  18. from configHandler import idleConf
  19.  
  20. class FormatParagraph:
  21.  
  22.     menudefs = [
  23.         ('format', [   # /s/edit/format   dscherer@cmu.edu
  24.             ('Format Paragraph', '<<format-paragraph>>'),
  25.          ])
  26.     ]
  27.  
  28.     def __init__(self, editwin):
  29.         self.editwin = editwin
  30.  
  31.     def close(self):
  32.         self.editwin = None
  33.  
  34.     def format_paragraph_event(self, event):
  35.         maxformatwidth = int(idleConf.GetOption('main','FormatParagraph','paragraph'))
  36.         text = self.editwin.text
  37.         first, last = self.editwin.get_selection_indices()
  38.         if first and last:
  39.             data = text.get(first, last)
  40.             comment_header = ''
  41.         else:
  42.             first, last, comment_header, data = \
  43.                     find_paragraph(text, text.index("insert"))
  44.         if comment_header:
  45.             # Reformat the comment lines - convert to text sans header.
  46.             lines = data.split("\n")
  47.             lines = map(lambda st, l=len(comment_header): st[l:], lines)
  48.             data = "\n".join(lines)
  49.             # Reformat to maxformatwidth chars or a 20 char width, whichever is greater.
  50.             format_width = max(maxformatwidth - len(comment_header), 20)
  51.             newdata = reformat_paragraph(data, format_width)
  52.             # re-split and re-insert the comment header.
  53.             newdata = newdata.split("\n")
  54.             # If the block ends in a \n, we dont want the comment
  55.             # prefix inserted after it. (Im not sure it makes sense to
  56.             # reformat a comment block that isnt made of complete
  57.             # lines, but whatever!)  Can't think of a clean soltution,
  58.             # so we hack away
  59.             block_suffix = ""
  60.             if not newdata[-1]:
  61.                 block_suffix = "\n"
  62.                 newdata = newdata[:-1]
  63.             builder = lambda item, prefix=comment_header: prefix+item
  64.             newdata = '\n'.join(map(builder, newdata)) + block_suffix
  65.         else:
  66.             # Just a normal text format
  67.             newdata = reformat_paragraph(data, maxformatwidth)
  68.         text.tag_remove("sel", "1.0", "end")
  69.         if newdata != data:
  70.             text.mark_set("insert", first)
  71.             text.undo_block_start()
  72.             text.delete(first, last)
  73.             text.insert(first, newdata)
  74.             text.undo_block_stop()
  75.         else:
  76.             text.mark_set("insert", last)
  77.         text.see("insert")
  78.         return "break"
  79.  
  80. def find_paragraph(text, mark):
  81.     lineno, col = map(int, mark.split("."))
  82.     line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
  83.     while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):
  84.         lineno = lineno + 1
  85.         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
  86.     first_lineno = lineno
  87.     comment_header = get_comment_header(line)
  88.     comment_header_len = len(comment_header)
  89.     while get_comment_header(line)==comment_header and \
  90.               not is_all_white(line[comment_header_len:]):
  91.         lineno = lineno + 1
  92.         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
  93.     last = "%d.0" % lineno
  94.     # Search back to beginning of paragraph
  95.     lineno = first_lineno - 1
  96.     line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
  97.     while lineno > 0 and \
  98.               get_comment_header(line)==comment_header and \
  99.               not is_all_white(line[comment_header_len:]):
  100.         lineno = lineno - 1
  101.         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
  102.     first = "%d.0" % (lineno+1)
  103.     return first, last, comment_header, text.get(first, last)
  104.  
  105. def reformat_paragraph(data, limit):
  106.     lines = data.split("\n")
  107.     i = 0
  108.     n = len(lines)
  109.     while i < n and is_all_white(lines[i]):
  110.         i = i+1
  111.     if i >= n:
  112.         return data
  113.     indent1 = get_indent(lines[i])
  114.     if i+1 < n and not is_all_white(lines[i+1]):
  115.         indent2 = get_indent(lines[i+1])
  116.     else:
  117.         indent2 = indent1
  118.     new = lines[:i]
  119.     partial = indent1
  120.     while i < n and not is_all_white(lines[i]):
  121.         # XXX Should take double space after period (etc.) into account
  122.         words = re.split("(\s+)", lines[i])
  123.         for j in range(0, len(words), 2):
  124.             word = words[j]
  125.             if not word:
  126.                 continue # Can happen when line ends in whitespace
  127.             if len((partial + word).expandtabs()) > limit and \
  128.                partial != indent1:
  129.                 new.append(partial.rstrip())
  130.                 partial = indent2
  131.             partial = partial + word + " "
  132.             if j+1 < len(words) and words[j+1] != " ":
  133.                 partial = partial + " "
  134.         i = i+1
  135.     new.append(partial.rstrip())
  136.     # XXX Should reformat remaining paragraphs as well
  137.     new.extend(lines[i:])
  138.     return "\n".join(new)
  139.  
  140. def is_all_white(line):
  141.     return re.match(r"^\s*$", line) is not None
  142.  
  143. def get_indent(line):
  144.     return re.match(r"^(\s*)", line).group()
  145.  
  146. def get_comment_header(line):
  147.     m = re.match(r"^(\s*#*)", line)
  148.     if m is None: return ""
  149.     return m.group(1)
  150.