diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/Sublime Text 4/Packages/User/Default (OSX).sublime-keymap | 31 | ||||
-rw-r--r-- | apps/Sublime Text 4/Packages/User/HSMusic Editing.py | 222 |
2 files changed, 253 insertions, 0 deletions
diff --git a/apps/Sublime Text 4/Packages/User/Default (OSX).sublime-keymap b/apps/Sublime Text 4/Packages/User/Default (OSX).sublime-keymap index 2250e7d..3bb8737 100644 --- a/apps/Sublime Text 4/Packages/User/Default (OSX).sublime-keymap +++ b/apps/Sublime Text 4/Packages/User/Default (OSX).sublime-keymap @@ -10,4 +10,35 @@ { "key": "overlay_has_focus", "operator": "equal", "operand": false }, ], }, + + // HSMusic wiki tag editing + // (bind caps lock to f16 in Karabiner-Elements) + + { "keys": ["f16"], + "command": "enter_exit_wiki_tag", + "context": [ + { "key": "project_name", "operator": "regex_contains", "operand": "HSMusic" }, + ], + }, + + { "keys": ["shift+f16"], + "command": "hyphen_in_wiki_tag", + "context": [ + { "key": "project_name", "operator": "regex_contains", "operand": "HSMusic" }, + ], + }, + + { "keys": [" "], + "command": "space_in_wiki_tag", + "context": [ + { "key": "project_name", "operator": "regex_contains", "operand": "HSMusic" }, + ], + }, + + { "keys": ["shift+space"], + "command": "space_in_wiki_tag", + "context": [ + { "key": "project_name", "operator": "regex_contains", "operand": "HSMusic" }, + ], + }, ] diff --git a/apps/Sublime Text 4/Packages/User/HSMusic Editing.py b/apps/Sublime Text 4/Packages/User/HSMusic Editing.py new file mode 100644 index 0000000..63e3dc3 --- /dev/null +++ b/apps/Sublime Text 4/Packages/User/HSMusic Editing.py @@ -0,0 +1,222 @@ +import sublime +import sublime_plugin +import re +import os + +class ProjectSpecificKeyBinding(sublime_plugin.EventListener): + def on_query_context(self, view, key, operator, operand, match_all): + current_project_name = view.window().project_file_name() + if key != "project_name" and os.path.isfile(current_project_name): + return None + + if operator == sublime.OP_EQUAL: + return current_project_name == operand + elif operator == sublime.OP_NOT_EQUAL: + return current_project_name != operand + elif operator == sublime.QueryOperator.REGEX_CONTAINS: + return re.search(operand, current_project_name) + elif operator == sublime.QueryOperator.NOT_REGEX_CONTAINS: + return not re.search(operand, current_project_name) + +class _CursorAdaptiveCommand(sublime_plugin.TextCommand): + handle_region_fallback = ('keep',) + + def __align_point_after_insertions(self, orig_point, direction): + new_point = orig_point + for (insert_point, insert_offset) in self.__inserted_offsets: + if direction == 'right' and insert_point <= orig_point: + new_point += insert_offset + elif direction == 'left' and insert_point < orig_point: + new_point += insert_offset + return new_point + + def __cleanup(self): + self.__inserted_offsets = list() + + def run(self, edit, **kwargs): + self.__cleanup() + + actions = list() + for region in self.view.sel(): + action = self.handle_region(region) + actions.append((region, *(action or self.handle_region_fallback))) + + new_regions = list() + for (region, action, *args) in actions: + handler = getattr(self, f'perform_{action}') + new_region = handler(edit, region, *args) + if new_region: + new_regions.append(new_region) + else: + new_regions.append(self.perform_keep(region, edit)) + + real_regions = list() + for (region, direction, delta, backoff) in new_regions: + if direction == 'collapse-left': + if region.a < region.b: + point = self.__align_point_after_insertions(region.a, 'right') + else: + point = self.__align_point_after_insertions(region.b, 'right') + real_regions.append(point) + elif direction == 'collapse-right': + if region.a < region.b: + point = self.__align_point_after_insertions(region.a, 'left') + else: + point = self.__align_point_after_insertions(region.b, 'left') + real_regions.append(point) + elif direction in ['left', 'right']: + start = region.a + end = region.b + delta + aligned_end = self.__align_point_after_insertions(end, direction) + if region.a == region.b: + if backoff: + if direction == 'right': + aligned_end -= backoff + else: + aligned_end += backoff + real_regions.append(sublime.Region(aligned_end, aligned_end)) + else: + aligned_start = self.__align_point_after_insertions(start, direction) + real_regions.append(sublime.Region(aligned_start, aligned_end)) + + self.view.sel().clear() + self.view.sel().add_all(real_regions) + self.__cleanup() + + def handle_region(self, region): + pass + + def perform_keep(self, edit, region): + return (region, 'right', 0, 0) + + def perform_move(self, edit, region, delta): + return (region, 'right', delta, 0) + + def perform_insert(self, edit, region, direction, insert, backoff): + insert_point = region.b + aligned_insert_point = self.__align_point_after_insertions(insert_point, direction) + insert_offset = self.view.insert(edit, aligned_insert_point, insert) + self.__inserted_offsets.append((insert_point, insert_offset)) + return (region, direction, 0, backoff) + + def perform_replace(self, edit, region, direction, erase, replace, backoff): + replace_point = region.b + replace_point -= erase + aligned_replace_point = self.__align_point_after_insertions(replace_point, 'right') + aligned_replace_region = sublime.Region(aligned_replace_point, aligned_replace_point + erase) + self.view.replace(edit, aligned_replace_region, replace) + self.__inserted_offsets.append((replace_point, len(replace) - erase)) + return (region, direction, 0, backoff) + + def perform_wrap(self, edit, region, insert_left, insert_right, move_side = None): + if region.a < region.b: + left = region.a + right = region.b + else: + left = region.b + right = region.a + + aligned_insert_point = self.__align_point_after_insertions(left, 'left') + insert_offset = self.view.insert(edit, aligned_insert_point, insert_left) + self.__inserted_offsets.append((left, insert_offset)) + + aligned_insert_point = self.__align_point_after_insertions(right, 'right') + insert_offset = self.view.insert(edit, aligned_insert_point, insert_right) + self.__inserted_offsets.append((right, insert_offset)) + + if move_side == 'left': + return (region, 'collapse-left', 0, 0) + else: + return (region, 'collapse-right', 0, 0) + +def _match_tag_in_progress(view, region): + to_start = sublime.Region(view.line(region).a, region.b) + match = re.search('(\[\[(?:(?!]]|\|).)*)$', view.substr(to_start)) + if match: + return match.group(1) + +def _match_rest_of_tag_in_progress(view, region): + to_end = sublime.Region(region.a, view.line(region).b) + match = re.search('^((?:(?!\[\[).)*]])', view.substr(to_end)) + if match: + return match.group(1) + +def _consider_reference_cycle(view, region, cycle): + tag_in_progress = _match_tag_in_progress(view, region) + + if not tag_in_progress: + return None + + rest_of_tag = _match_rest_of_tag_in_progress(view, region) + + rest_of_ref = re.search('^((?:(?!\|).)*:)', rest_of_tag) + if rest_of_ref: + return ('move', len(rest_of_ref.group(1))) + + if tag_in_progress == '[[': + current_ref = None + insert_pipe = rest_of_tag != ']]' and '|' not in rest_of_tag + elif tag_in_progress[-1:] == ':': + current_ref = tag_in_progress[2:-1] + insert_pipe = False + else: + return None + + if current_ref in cycle: + next_ref = cycle[(cycle.index(current_ref) + 1) % len(cycle)] + else: + next_ref = cycle[0] + + if current_ref: + return ('replace', 'right', len(current_ref) + 1, next_ref + ':', 0) + elif insert_pipe: + return ('insert', 'right', next_ref + ':|', 1) + else: + return ('insert', 'right', next_ref + ':', 0) + +class EnterExitWikiTagCommand(_CursorAdaptiveCommand): + ref_cycle = ['artist', 'group', 'flash', 'tag'] + + def handle_region(self, region): + cycle_action = _consider_reference_cycle(self.view, region, self.ref_cycle) + if cycle_action: + return cycle_action + + rest_of_tag = _match_rest_of_tag_in_progress(self.view, region) + if rest_of_tag: + return ('move', len(rest_of_tag)) + elif region.a == region.b: + return ('insert', 'right', '[[]]', 2) + else: + return ('wrap', '[[', ']]', 'left') + +class HyphenInWikiTagCommand(_CursorAdaptiveCommand): + ref_cycle = ['artist', 'group', 'flash', 'tag'] + + def handle_region(self, region): + cycle_action = _consider_reference_cycle(self.view, region, self.ref_cycle) + if cycle_action: + return cycle_action + + tag_in_progress = _match_tag_in_progress(self.view, region) + + if tag_in_progress and ':' in tag_in_progress: + return ('insert', 'right', '-', 0) + +class SpaceInWikiTagCommand(_CursorAdaptiveCommand): + ref_cycle = ['track', 'album'] + + handle_region_fallback = ('insert', 'right', ' ', 0) + + def handle_region(self, region): + cycle_action = _consider_reference_cycle(self.view, region, self.ref_cycle) + if cycle_action: + return cycle_action + + tag_in_progress = _match_tag_in_progress(self.view, region) + + if tag_in_progress and ':' in tag_in_progress: + if self.view.substr(region.b) == '|': + return ('move', 1) + else: + return ('insert', 'right', '|', 0) |