Created
June 12, 2012 01:34
-
-
Save SmileyChris/2913864 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sublime, sublime_plugin | |
import string | |
import textwrap | |
import re | |
import comment | |
def previous_line(view, sr): | |
"""sr should be a Region covering the entire hard line""" | |
if sr.begin() == 0: | |
return None | |
else: | |
return view.full_line(sr.begin() - 1) | |
def next_line(view, sr): | |
"""sr should be a Region covering the entire hard line, including | |
the newline""" | |
if sr.end() == view.size(): | |
return None | |
else: | |
return view.full_line(sr.end()) | |
separating_line_pattern = re.compile("^[\\t ]*(\"{3}|'{3})?\\n?$") | |
def is_paragraph_separating_line(view, sr): | |
return separating_line_pattern.match(view.substr(sr)) != None | |
def docstring_definition(view, sr, definition=None): | |
if definition: | |
pattern = re.compile(r'({0})\s*(#.*)?$'.format(re.escape(definition))) | |
else: | |
pattern = re.compile(r"^\s*(\"{3}|'{3})") | |
match = pattern.match(view.substr(view.line(sr.begin()))) | |
if match: | |
return match.group(1) | |
def has_prefix(view, line, prefix): | |
if not prefix: | |
return True | |
line_start = view.substr(sublime.Region(line.begin(), | |
line.begin() + len(prefix))) | |
return line_start == prefix | |
def expand_to_paragraph(view, tp): | |
sr = view.full_line(tp) | |
if is_paragraph_separating_line(view, sr): | |
return sublime.Region(tp, tp) | |
required_prefix = None | |
# If the current line starts with a comment, only select lines that are also | |
# commented | |
(line_comments, block_comments) = comment.build_comment_data(view, tp) | |
dataStart = comment.advance_to_first_non_white_space_on_line(view, sr.begin()) | |
for c in line_comments: | |
(start, disable_indent) = c | |
comment_region = sublime.Region(dataStart, | |
dataStart + len(start)) | |
if view.substr(comment_region) == start: | |
required_prefix = view.substr(sublime.Region(sr.begin(), comment_region.end())) | |
break | |
docstring = None | |
first = sr.begin() | |
prev = sr | |
while True: | |
docstring = docstring_definition(view, prev) | |
if docstring: | |
break | |
prev = previous_line(view, prev) | |
if (prev == None or is_paragraph_separating_line(view, prev) or | |
not has_prefix(view, prev, required_prefix)): | |
break | |
else: | |
first = prev.begin() | |
last = sr.end() | |
next = sr | |
while True: | |
if docstring and docstring_definition(view, next, docstring): | |
break | |
next = next_line(view, next) | |
if (next == None or is_paragraph_separating_line(view, next) or | |
not has_prefix(view, next, required_prefix)): | |
break | |
else: | |
last = next.end() | |
return sublime.Region(first, last) | |
def all_paragraphs_intersecting_selection(view, sr): | |
paragraphs = [] | |
para = expand_to_paragraph(view, sr.begin()) | |
if not para.empty(): | |
paragraphs.append(para) | |
while True: | |
line = next_line(view, para) | |
if line == None or line.begin() >= sr.end(): | |
break; | |
if not is_paragraph_separating_line(view, line): | |
para = expand_to_paragraph(view, line.begin()) | |
paragraphs.append(para) | |
else: | |
para = line | |
return paragraphs | |
class ExpandSelectionToParagraphCommand(sublime_plugin.TextCommand): | |
def run(self, edit): | |
regions = [] | |
for s in self.view.sel(): | |
regions.append(sublime.Region( | |
expand_to_paragraph(self.view, s.begin()).begin(), | |
expand_to_paragraph(self.view, s.end()).end())) | |
for r in regions: | |
self.view.sel().add(r) | |
class WrapLinesCommand(sublime_plugin.TextCommand): | |
line_prefix_pattern = re.compile("^\W+") | |
def extract_prefix(self, sr): | |
lines = self.view.split_by_newlines(sr) | |
if len(lines) == 0: | |
return None | |
initial_prefix_match = self.line_prefix_pattern.match(self.view.substr( | |
lines[0])) | |
if not initial_prefix_match: | |
return None | |
prefix = self.view.substr(sublime.Region(lines[0].begin(), | |
lines[0].begin() + initial_prefix_match.end())) | |
for line in lines[1:]: | |
if self.view.substr(sublime.Region(line.begin(), | |
line.begin() + len(prefix))) != prefix: | |
return None | |
return prefix | |
def width_in_spaces(self, str, tab_width): | |
sum = 0; | |
for c in str: | |
if c == '\t': | |
sum += tab_width - 1 | |
return sum | |
def run(self, edit, width=0): | |
if width == 0 and self.view.settings().get("wrap_width"): | |
try: | |
width = int(self.view.settings().get("wrap_width")) | |
except TypeError: | |
pass | |
if width == 0 and self.view.settings().get("rulers"): | |
# try and guess the wrap width from the ruler, if any | |
try: | |
width = int(self.view.settings().get("rulers")[0]) | |
except ValueError: | |
pass | |
except TypeError: | |
pass | |
if width == 0: | |
width = 78 | |
# Make sure tabs are handled as per the current buffer | |
tab_width = 8 | |
if self.view.settings().get("tab_size"): | |
try: | |
tab_width = int(self.view.settings().get("tab_size")) | |
except TypeError: | |
pass | |
if tab_width == 0: | |
tab_width == 8 | |
paragraphs = [] | |
for s in self.view.sel(): | |
paragraphs.extend(all_paragraphs_intersecting_selection(self.view, s)) | |
if len(paragraphs) > 0: | |
self.view.sel().clear() | |
for p in paragraphs: | |
self.view.sel().add(p) | |
# This isn't an ideal way to do it, as we loose the position of the | |
# cursor within the paragraph: hence why the paragraph is selected | |
# at the end. | |
for s in self.view.sel(): | |
wrapper = textwrap.TextWrapper() | |
wrapper.expand_tabs = False | |
wrapper.width = width | |
prefix = self.extract_prefix(s) | |
if prefix: | |
wrapper.initial_indent = prefix | |
wrapper.subsequent_indent = prefix | |
wrapper.width -= self.width_in_spaces(prefix, tab_width) | |
if wrapper.width < 0: | |
continue | |
txt = self.view.substr(s) | |
if prefix: | |
txt = txt.replace(prefix, u"") | |
txt = string.expandtabs(txt, tab_width) | |
txt = wrapper.fill(txt) + u"\n" | |
self.view.replace(edit, s, txt) | |
# It's unhelpful to have the entire paragraph selected, just leave the | |
# selection at the end | |
ends = [s.end() - 1 for s in self.view.sel()] | |
self.view.sel().clear() | |
for pt in ends: | |
self.view.sel().add(sublime.Region(pt)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment