Commits

guillermooo committed 8efa895

implement VimRange class; fix :move command and cleanup

Comments (0)

Files changed (4)

     return [leaf, path]
 
 
-def get_region_by_range(view, line_range=None, split_visual=False):
+def get_region_by_range(view, line_range=None, single_lines=False):
     # If GLOBAL_RANGES exists, the ExGlobal command has been run right before
     # the current command, and we know we must process these lines.
-    # XXX move this further down into the range parsing?
     global GLOBAL_RANGES
     if GLOBAL_RANGES:
         rv = GLOBAL_RANGES[:]
         GLOBAL_RANGES = []
         return rv
 
-    regions = []
     if line_range:
-        regions, visual_regions = ex_range.new_calculate_range(view, line_range)
-    lines = []
-    for region in regions:
-        a, b = region
-        r = sublime.Region(view.text_point(a - 1, 0),
-                           view.line(view.text_point(b - 1, 0)).end())
-        if not visual_regions or split_visual:
-            lines.extend(view.split_by_newlines(r))
+        vim_range = ex_range.VimRange(view, line_range)
+        if single_lines:
+            return vim_range.lines()
         else:
-            if view.substr(r)[-1] == "\n":
-                if r.begin() != r.end():
-                    r = sublime.Region(r.begin(), r.end() - 1)
-            lines.append(r)
-
-    return lines
+            return vim_range.blocks()
 
 
 def ensure_line_block(view, r):
             ex_error.display_error(ex_error.ERR_INVALID_ADDRESS)
             return
 
-        line_block = []
-        for r in get_region_by_range(self.view, line_range=line_range):
-            ss = ensure_line_block(self.view, r)
-            line_block.append(ss)
+        line_block = get_region_by_range(self.view, line_range=line_range)
+        line_block = [self.view.substr(r) for r in line_block]
 
-        offset = 0
-        for r in reversed(get_region_by_range(self.view, line_range, split_visual=True)):
-            if self.view.rowcol(r.begin())[0] + 1 < address:
-                offset +=  1
-            self.view.erase(edit, self.view.full_line(r))
-
-        text = ''.join(line_block)
+        text = '\n'.join(line_block) + '\n'
         if address != 0:
-            dest = self.view.line(self.view.text_point(
-                                                address - offset, 0)).end() + 1
+            dest = self.view.line(self.view.text_point(address, 0)).end() + 1
         else:
             dest = 0
 
+        # Don't move lines onto themselves.
+        for sel in self.view.sel():
+            if sel.contains(dest):
+                ex_error.display_error(ex_error.ERR_CANT_MOVE_LINES_ONTO_THEMSELVES)
+                return
+
         if dest > self.view.size():
             dest = self.view.size()
             text = '\n' + text[:-1]
         self.view.insert(edit, dest, text)
 
-        self.view.sel().clear()
-        cursor_dest = self.view.line(dest + len(text) - 1).begin()
-        self.view.sel().add(sublime.Region(cursor_dest, cursor_dest))
+        for r in reversed(get_region_by_range(self.view, line_range)):
+            self.view.erase(edit, self.view.full_line(r))
 
 
 class ExCopy(sublime_plugin.TextCommand):
             ex_error.display_error(ex_error.ERR_INVALID_ADDRESS)
             return
 
-        line_block = []
-        for r in get_region_by_range(self.view, line_range=line_range):
-            ss = ensure_line_block(self.view, r)
-            line_block.append(ss)
+        line_block = get_region_by_range(self.view, line_range=line_range)
+        line_block = [self.view.substr(r) for r in line_block]
 
-        text = ''.join(line_block)
+        text = '\n'.join(line_block) + '\n'
         if address != 0:
             dest = self.view.line(self.view.text_point(address, 0)).end() + 1
         else:
             print "VintageEx [regex error]: %s ... in pattern '%s'" % (e.message, pattern)
             return
 
-        target_region = get_region_by_range(self.view, line_range=line_range)
         replace_count = 0 if (flags and 'g' in flags) else 1
+        
+        target_region = get_region_by_range(self.view, line_range=line_range, single_lines=True)
         for r in reversed(target_region):
-            # be explicit about replacing the line, because we might be looking
-            # at a Ctrl+D sequence of regions (not spanning a whole line)
-            # TODO: Improve this: make sure view.line() doesn't extend past
-            # the desired line. For example, in VISUAL LINE MODE.
-            if self.view.substr(r.end() - 1) == '\n':
-                r = sublime.Region(r.begin(), r.end() - 1)
             line_text = self.view.substr(self.view.line(r))
             rv = re.sub(pattern, replacement, line_text, count=replace_count)
             self.view.replace(edit, self.view.line(r), rv)
 
     Create a new buffer.
 
-    TODO: Create new buffer by splitting the screen.
+    TODO: Create new buffer by single_linesthe screen.
     """
     def run(self, edit, line_range=None):
         self.view.window().run_command('new_file')
 # encoding: utf-8
 
-PACKAGE_VERSION = "12.9.10"
+PACKAGE_VERSION = "12.10.20"
 
 """Commands to build and manage .sublime-package archives with distutils."""
 
 ERR_UNSAVED_CHANGES = 37 # The buffer has been modified but not saved.
 ERR_ADDRESS_REQUIRED = 14 # Command needs an address.
 ERR_OTHER_BUFFER_HAS_CHANGES = 445 # :only, for example, may trigger this
+ERR_CANT_MOVE_LINES_ONTO_THEMSELVES = 134
 
 
 ERR_MESSAGES = {
     ERR_NO_RANGE_ALLOWED: 'No range allowed.',
     ERR_UNSAVED_CHANGES: 'There are unsaved changes.',
     ERR_ADDRESS_REQUIRED: 'Invalid address.',
-    ERR_OTHER_BUFFER_HAS_CHANGES: "Other buffer contains changes."
+    ERR_OTHER_BUFFER_HAS_CHANGES: "Other buffer contains changes.",
+    ERR_CANT_MOVE_LINES_ONTO_THEMSELVES: "Move lines into themselves."
 }
 
 
 """
 
 from collections import namedtuple
+import sublime
+
+
+class VimRange(object):
+    """Encapsulates calculation of regions based on supplied raw range info.
+    """
+    def __init__(self, view, range_info, default=None):
+        self.view = view
+        self.default = default
+        self.range_info = range_info
+
+    def blocks(self):
+        """Returns a list of blocks potentially encompassing multiple lines.
+        Returned blocks don't end in a newline char.
+        """
+        regions, visual_regions = new_calculate_range(self.view, self.range_info)
+        blocks = []
+        for a, b in regions:
+            r = sublime.Region(self.view.text_point(a - 1, 0),
+                               self.view.line(self.view.text_point(b - 1, 0)).end())
+            if self.view.substr(r)[-1] == "\n":
+                if r.begin() != r.end():
+                    r = sublime.Region(r.begin(), r.end() - 1)
+            blocks.append(r)
+        return blocks
+
+    def lines(self):
+        """Return a list of lines.
+        Returned lines don't end in a newline char.
+        """
+        lines = []
+        for block in self.blocks():
+            lines.extend(self.view.split_by_newlines(block))
+        return lines
 
 
 EX_RANGE = namedtuple('ex_range', 'left left_offset separator right right_offset')
         for sel in view.sel():
             start = view.rowcol(sel.begin())[0] + 1
             end = view.rowcol(sel.end())[0] + 1
+            if view.substr(sel.end() - 1) == '\n':
+                end -= 1
             all_line_blocks.append((start, end))
         return all_line_blocks, True