Skip to content

Commit

Permalink
Merge pull request #7 from mwean/offsetted_jump
Browse files Browse the repository at this point in the history
Allow specifying different indent levels to jump to
  • Loading branch information
mwean committed Aug 14, 2014
2 parents 5bfae02 + 3cd0608 commit c0e5f14
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 20 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,18 @@ If there are several lines on the same indent level, the cursor will jump to the

![Before jumping downward](https://s3.amazonaws.com/mwean-github/sublime_jump_along_indent/pre_jump_block.png)![After jumping downward](https://s3.amazonaws.com/mwean-github/sublime_jump_along_indent/post_jump_block.png)

There is also an option to extend the selection while jumping:
### Extending selection ###

With the option `extend_selection: true` you can extend the selection while jumping:

![Before selecting downward](https://s3.amazonaws.com/mwean-github/sublime_jump_along_indent/pre_jump.png)![After selecting downward](https://s3.amazonaws.com/mwean-github/sublime_jump_along_indent/post_select.png)

### Jumping to a different indent level ###

You can also use the `indent_offset` option to jump to a more or less-indented line. For example, with `indent_offset = -1`:

![Before jumping up and out](https://s3.amazonaws.com/mwean-github/sublime_jump_along_indent/pre_jump_out.png)![After jumping up and out](https://s3.amazonaws.com/mwean-github/sublime_jump_along_indent/post_jump_out.png)

## Installation ##

### Using Package Control ###
Expand Down
32 changes: 25 additions & 7 deletions file_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,44 @@ def __init__(self, view, viewhelper):
self.view = view
self.view_helper = viewhelper

def scan(self, direction = 'forward'):
def scan(self, direction = 'forward', indent_offset = 0):
self.indent_offset = indent_offset
if direction == 'forward':
indent_match = self.search(self.search_str(), self.next_point()) or 0
block_match = self.find_last_line_of_block()
return max([indent_match, block_match])
possible_matches = [indent_match]

if indent_offset == 0:
block_match = self.find_last_line_of_block()
possible_matches.append(block_match)

return max(possible_matches)
else:
if self.previous_point() < 0:
end = 0
else:
end = self.previous_point()

indent_match = self.reverse_search(self.search_str(), 0, end)
block_match = self.find_first_line_of_block(end)
return min([indent_match, block_match])
possible_matches = [indent_match]

if indent_offset == 0:
block_match = self.find_first_line_of_block(end)
possible_matches.append(block_match)

return min(possible_matches)

def adapt_indent(self, indent_str):
tab_size = self.view.settings().get("tab_size")
indent = max(0, len(indent_str) + tab_size * self.indent_offset)
return indent

def search_str(self):
if re.match(r"^\s*$", self.str_to_left()) and re.match(r"^\s+\S+", self.str_to_right()):
search_str = "^ {0," + str(len(self.str_to_left())) + "}\S+"
indent = self.adapt_indent(self.str_to_left())
search_str = "^ {0," + str(indent) + "}\S+"
else:
search_str = "^" + self.leading_spaces() + "\S"
indent = self.adapt_indent(self.leading_spaces())
search_str = "^ {" + str(indent) + "}\S"
return search_str

def str_to_left(self):
Expand Down
14 changes: 10 additions & 4 deletions jump_along_indent.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@


class JumpIndentCommand(object):
def run(self, edit, extend_selection=False):
def run(self, edit, extend_selection=False, indent_offset=0):
self.indent_offset = indent_offset
self.clear_selection()
for self.view_helper in ViewHelpers(self.view):
self.scanner = FileScanner(self.view, self.view_helper)
Expand Down Expand Up @@ -42,15 +43,20 @@ def select(self):
self.build_selection(new_region)

def deselect(self):
matched_row = self.scanner.scan(self.direction)
matched_row = self.scanner.scan(self.direction, self.indent_offset)
target = self.target_point(matched_row)
new_region = sublime.Region(self.get_deselect_begin_pos(), target, self.view_helper.initial_xpos())
self.build_selection(new_region)

def target_point(self, matched_row=None):
matched_row = matched_row or self.scanner.scan(self.direction)
matched_row = matched_row or self.scanner.scan(self.direction, self.indent_offset)
selection_offset = self.indent_offset

if matched_row == self.view_helper.initial_row():
selection_offset = 0

matched_point_bol = self.view.text_point(matched_row, 0)
return self.view.text_point(matched_row, self.view_helper.target_column(matched_point_bol))
return self.view.text_point(matched_row, self.view_helper.target_column(matched_point_bol, selection_offset))


class JumpNextIndentCommand(JumpIndentCommand, sublime_plugin.TextCommand):
Expand Down
7 changes: 4 additions & 3 deletions tests/test_helper.py → tests/helper.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import sublime, sys, json, sublime_plugin
import sublime
from unittest import TestCase

class TestHelper(TestCase):
def setUp(self):
self.view = sublime.active_window().new_file()
self.view.settings().set("tab_size", 2)

def tearDown(self):
if self.view:
Expand All @@ -15,10 +16,10 @@ def set_text(self, lines):
self.view.run_command('move_to', { 'to': 'bol', 'extend': True })
self.view.run_command('insert', { 'characters': line + "\n" })

def check_command(self, text, start, end, extend_selection=False):
def check_command(self, text, start, end, extend_selection=False, indent_offset=0):
self.set_text(text)
self.view.sel().clear()
self.view.sel().add(sublime.Region(start[0], start[1]))
self.view.run_command(self.command(), { 'extend_selection': extend_selection })
self.view.run_command(self.command(), { 'extend_selection': extend_selection, 'indent_offset': indent_offset })

self.assertEqual(self.view.sel()[0], sublime.Region(end[0], end[1]))
2 changes: 1 addition & 1 deletion tests/test_jump_next_indent.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from test_helper import TestHelper
from helper import TestHelper

class TestJumpNextIndent(TestHelper):
def command(self):
Expand Down
51 changes: 51 additions & 0 deletions tests/test_jump_next_offset_indent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from helper import TestHelper

class TestJumpNextOffsetIndent(TestHelper):
def command(self):
return 'jump_next_indent'

def test_positive_indent_offset(self):
lines = [
'Lorem ipsum dolor sit amet',
'Lorem ipsum dolor sit amet',
'',
' Lorem ipsum dolor sit amet'
]
starting_selection = [0, 0]
ending_selection = [57, 57]

self.check_command(lines, starting_selection, ending_selection, indent_offset = 1)

def test_negative_indent_offset(self):
lines = [
' Lorem ipsum dolor sit amet',
'',
'Lorem ipsum dolor sit amet',
' Lorem ipsum dolor sit amet'
]
starting_selection = [2, 2]
ending_selection = [30, 30]

self.check_command(lines, starting_selection, ending_selection, indent_offset = -1)

def test_block_skip(self):
lines = [
' Lorem ipsum dolor sit amet',
'Lorem ipsum dolor sit amet',
'Lorem ipsum dolor sit amet'
]
starting_selection = [2, 2]
ending_selection = [29, 29]

self.check_command(lines, starting_selection, ending_selection, indent_offset = -1)

def test_ignore_if_no_match(self):
lines = [
'Lorem ipsum dolor sit amet',
'Lorem ipsum dolor sit amet',
' Lorem ipsum dolor sit amet'
]
starting_selection = [0, 0]
ending_selection = [0, 0]

self.check_command(lines, starting_selection, ending_selection, indent_offset = 1)
2 changes: 1 addition & 1 deletion tests/test_jump_prev_indent.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from test_helper import TestHelper
from helper import TestHelper

class TestJumpPrevIndent(TestHelper):
def command(self):
Expand Down
51 changes: 51 additions & 0 deletions tests/test_jump_prev_offset_indent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from helper import TestHelper

class TestJumpPrevOffsetIndent(TestHelper):
def command(self):
return 'jump_prev_indent'

def test_positive_indent_offset(self):
lines = [
' Lorem ipsum dolor sit amet',
'',
'Lorem ipsum dolor sit amet',
'Lorem ipsum dolor sit amet'
]
starting_selection = [57, 57]
ending_selection = [2, 2]

self.check_command(lines, starting_selection, ending_selection, indent_offset = 1)

def test_negative_indent_offset(self):
lines = [
' Lorem ipsum dolor sit amet',
'',
'Lorem ipsum dolor sit amet',
' Lorem ipsum dolor sit amet'
]
starting_selection = [59, 59]
ending_selection = [30, 30]

self.check_command(lines, starting_selection, ending_selection, indent_offset = -1)

def test_block_skip(self):
lines = [
'Lorem ipsum dolor sit amet',
'Lorem ipsum dolor sit amet',
' Lorem ipsum dolor sit amet'
]
starting_selection = [56, 56]
ending_selection = [27, 27]

self.check_command(lines, starting_selection, ending_selection, indent_offset = -1)

def test_ignore_if_no_match(self):
lines = [
' Lorem ipsum dolor sit amet',
'Lorem ipsum dolor sit amet',
'Lorem ipsum dolor sit amet'
]
starting_selection = [58, 58]
ending_selection = [58, 58]

self.check_command(lines, starting_selection, ending_selection, indent_offset = 1)
8 changes: 5 additions & 3 deletions view_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ def cursor_at_top_of_selection(self):
def cursor_at_bottom_of_selection(self):
return self.initial_selection().b > self.initial_selection().a

def target_column(self, target):
def target_column(self, target, indent_offset):
tab_size = self.view.settings().get("tab_size")
offset_column = self.initial_column() + tab_size * indent_offset
end_of_line = self.view.rowcol(self.find_eol(target))[1]

if self.initial_column() > end_of_line:
if offset_column > end_of_line:
return end_of_line
else:
return self.initial_column()
return offset_column

def find_eol(self, point):
return self.view.line(point).end()
Expand Down

0 comments on commit c0e5f14

Please sign in to comment.