Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added double character labels support #68

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion AceJump.sublime-settings
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
{
// Characters to be used as labels in order they'll appear
"labels": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
"labels": "fjdkslagheiworutyqpcvmxzbnFJDKSLAGHEIWORUTYQPCVMXZBN",

// Syntax highlighting scope for the labels
"labels_scope": "invalid",

// Enable double character label, eg. aa AA aB ...
"double_char_label": false,

// Jump to boundary if the char is at the end of a word insteadly
"jump_to_boundary": true,

// Toggles case sensitive search in word and character modes.
"search_case_sensitivity": true,

Expand Down
115 changes: 93 additions & 22 deletions ace_jump.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@

ace_jump_active = False

# Labels defined in setting file
ace_jump_labels = []

# Labels generated dynamically
ace_jump_active_labels = []

def get_active_views(window, current_buffer_only):
"""Returns all currently visible views"""

Expand Down Expand Up @@ -75,6 +81,21 @@ def get_views_sel(views):
for view in views:
selections.append(view.sel())
return selections

def sort_double_char_labels(labels):
"""Sort double char labels based on the order of repeated, lower and other labels"""
repeated_char_labels = [label for label in labels if label[0] == label[1]]

lower_char_labels = [label for label in labels
if label[0].islower() and label[1].islower()
and label not in repeated_char_labels]

other_labels = [label for label in labels
if label not in repeated_char_labels and label not in lower_char_labels]

labels = repeated_char_labels + lower_char_labels + other_labels

return labels

class AceJumpCommand(sublime_plugin.WindowCommand):
"""Base command class for AceJump plugin"""
Expand All @@ -83,6 +104,9 @@ def run(self, current_buffer_only = False):
global ace_jump_active
ace_jump_active = True

global ace_jump_labels
ace_jump_labels = []

self.char = ""
self.target = ""
self.views = []
Expand All @@ -99,6 +123,9 @@ def run(self, current_buffer_only = False):
"labels",
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)
ace_jump_labels = self.labels
self.double_char_label = settings.get("double_char_label", False)
self.jump_to_boundary = settings.get("jump_to_boundary", True)
self.case_sensitivity = settings.get("search_case_sensitivity", True)
self.jump_behind_last = settings.get("jump_behind_last_characters", False)
self.save_files_after_jump = settings.get("save_files_after_jump", False)
Expand Down Expand Up @@ -132,6 +159,8 @@ def next_batch(self, command):
def on_input(self, command):
"""Fires the necessary actions for the current input"""

global ace_jump_active_labels

if len(command) == 1:
self.char = command
if self.char == "<" or self.char == ">":
Expand All @@ -141,10 +170,10 @@ def on_input(self, command):
self.add_labels(self.regex().format(re.escape(self.char)))
return

if len(command) == 2:
self.target = command[1]

self.window.run_command("hide_panel", {"cancel": True})
if ace_jump_active_labels and len(ace_jump_active_labels[0]) == len(command[1:]):
self.target = command[1:]
self.labels = ace_jump_active_labels
self.window.run_command("hide_panel", {"cancel": True})

def submit(self):
"""Handles the behavior after closing the prompt"""
Expand All @@ -156,7 +185,7 @@ def submit(self):
set_views_syntax(self.all_views, self.syntax)

if self.valid_target(self.target):
self.jump(self.labels.find(self.target))
self.jump(self.get_index(self.target, self.labels))

mode = 0
ace_jump_active = False
Expand Down Expand Up @@ -189,6 +218,7 @@ def add_labels(self, regex):
"regex": regex,
"region_type": self.region_type,
"labels": self.labels,
"double_char_label": self.double_char_label,
"highlight": self.highlight,
"case_sensitive": self.case_sensitivity
})
Expand Down Expand Up @@ -229,7 +259,8 @@ def jump(self, index):
view = self.changed_views[self.view_for_index(index)]

self.window.focus_view(view)
view.run_command("perform_ace_jump", {"target": region})
view.run_command("perform_ace_jump", {"target": region,
"jump_to_boundary": self.jump_to_boundary})
self.after_jump(view)

def views_to_label(self):
Expand All @@ -249,16 +280,22 @@ def view_for_index(self, index):

def valid_target(self, target):
"""Check if jump target is valid"""

index = self.labels.find(target)

index = self.get_index(target, self.labels)
return target != "" and index >= 0 and index < last_index;

def get_region_type(self):
"""Return region type for labeling"""

return "visible_region"

def get_index(self, item, sequence):
"""Return label index in str or list label set"""
if isinstance(sequence, str):
index = sequence.find(item)
elif isinstance(sequence, list):
index = sequence.index(item)
return index

class AceJumpWordCommand(AceJumpCommand):
"""Specialized command for word-mode"""

Expand All @@ -269,7 +306,7 @@ def init_value(self):
return ""

def regex(self):
return r'\b{}'
return r'\b{0}|{0}\b|(?<=_){0}|{0}(?=_)'

def after_jump(self, view):
global mode
Expand Down Expand Up @@ -336,7 +373,7 @@ def init_value(self):
return " "

def regex(self):
return r'\b\w'
return r'\b\w|\w\b|(?<=_)\w|\w(?=_)'

def after_jump(self, view):
global mode
Expand Down Expand Up @@ -376,12 +413,17 @@ def run(self):
class AddAceJumpLabelsCommand(sublime_plugin.TextCommand):
"""Command for adding labels to the views"""

def run(self, edit, regex, region_type, labels, highlight, case_sensitive):
# Regions after label replacing
replaced_regions = []

def run(self, edit, regex, region_type, labels, double_char_label, highlight, case_sensitive):
global hints

characters = self.find(regex, region_type, len(labels), case_sensitive)
self.add_labels(edit, characters, labels)
self.view.add_regions("ace_jump_hints", characters, highlight)
max_labels = len(labels) ** 2 if double_char_label else len(labels)
characters = self.find(regex, region_type, max_labels, case_sensitive)
self.add_labels(edit, characters, labels, double_char_label)
# self.view.add_regions("ace_jump_hints", characters, highlight)
self.view.add_regions("ace_jump_hints", self.replaced_regions, highlight)

hints = hints + characters

Expand Down Expand Up @@ -411,13 +453,35 @@ def find(self, regex, region_type, max_labels, case_sensitive):

return chars

def add_labels(self, edit, regions, labels):
def add_labels(self, edit, regions, labels, double_char_label):
"""Replaces the given regions with labels"""

global ace_jump_active_labels

if double_char_label and len(regions) > len(labels):
labels = [char_a + char_b for char_a in labels for char_b in labels]
labels = sort_double_char_labels(labels)
for i, region in enumerate(regions):
regions[i] = sublime.Region(region.a, region.b + 1)

ace_jump_active_labels = labels

num_region = len(regions)
region_offset = 0
self.replaced_regions = []

for i in range(len(regions)):
self.view.replace(
edit, regions[i], labels[last_index + i - len(regions)]
)
label = labels[last_index + i - num_region]
region = regions[i]
if region_offset:
region = sublime.Region(region.a + region_offset, region.b + region_offset)
content = self.view.substr(region)
if content[-1] in ('\n', '\r'):
# region = sublime.Region(region.a, region.b + 1)
label += content[-1]
region_offset += 1
self.replaced_regions.append(region)
self.view.replace(edit, region, label)

def get_target_region(self, region_type):

Expand All @@ -437,15 +501,22 @@ def run(self, edit):
class PerformAceJumpCommand(sublime_plugin.TextCommand):
"""Command performing the jump"""

def run(self, edit, target):
def run(self, edit, target, jump_to_boundary):
global mode
if mode == 0 or mode == 3:
self.view.sel().clear()

self.view.sel().add(self.target_region(target))
self.view.sel().add(self.target_region(target, jump_to_boundary))
self.view.show(target)

def target_region(self, target):
def target_region(self, target, jump_to_boundary):
if jump_to_boundary:
# Check if the target next to boundary, if so, move the target one letter righter to put the
# cursor right on the boundary
nextChar = self.view.substr(sublime.Region(target + 1, target + 2))
if re.match('[^\w]', nextChar):
target += 1

if mode == 1:
for cursor in self.view.sel():
return sublime.Region(cursor.begin(), target)
Expand Down