diff --git a/LaTeXTools.sublime-settings b/LaTeXTools.sublime-settings index ec1bb2ee..604366b5 100644 --- a/LaTeXTools.sublime-settings +++ b/LaTeXTools.sublime-settings @@ -101,6 +101,11 @@ // You can also use toggle: C-l,t,a,e "env_auto_trigger": false, + // Fill-helper autocompletion will close automatically an environment after \begin{. This trigger is only ran after you have selected an environment from the list + // This autoclose is only triggered when adding a new \begin{, not when modifying an existing one via the Window Command Palette + // This requires "env_auto_trigger" to be true. + "env_autoclose_trigger": false, + // Fill-helper autocompletion triggered for a wide range of references to external // files. You can also use toggle: C-l,t,a,f "fill_auto_trigger": true, @@ -120,6 +125,9 @@ // You can also use the toggle C-l,t,a,b "smart_bracket_auto_trigger": true, + // After Fill-helper autocompletion has finished his completion, moves the cursor after the closing bracket if there is one. + "smart_cursor_move_auto_trigger": false, + // ------------------------------------------------------------------ // Image Preview Settings // ------------------------------------------------------------------ diff --git a/latextools/latex_env_completions.py b/latextools/latex_env_completions.py index f1f7c35e..cc85536d 100644 --- a/latextools/latex_env_completions.py +++ b/latextools/latex_env_completions.py @@ -8,6 +8,7 @@ from .utils import analysis from .utils.settings import get_setting from .utils.tex_directives import get_tex_root +from .utils.sublime_utils import move_cursor_relative, move_cursor_vertical BEGIN_END_BEFORE_REGEX = re.compile(r"([^{}\[\]]*)\{(?:\][^{}\[\]]*\[)?(?:nigeb|dne)\\") """ @@ -63,6 +64,39 @@ def get_completions(self, view, prefix, line): return (display, values) + def on_selection(self, view, insert_text, should_complete): + # Do nothing the fill helper was called to replace the content of a command + if not should_complete or not get_setting("env_autoclose_trigger", False): + return + + # The \end{...} is added only if there is a single cursor and if the 4 characters before the cursor are "\end" + sel = view.sel() + take_selection = [True]*len(sel) # Indicates if we need the Region at index i will undergo autoclose of environment + indentation = [0] * len(sel) + for i in range(len(sel)): + cursor = sel[i].end() + + # Determines the characters before the cursor + before_bracket = cursor - 1 - len(insert_text) + begin = view.substr(sublime.Region(before_bracket-4, before_bracket)) + + # Returns if we should not insert the closing environment + if begin == "\\end": + take_selection[i] = False + + # First, move the cursor after the {} + move_cursor_relative(sel, 1, take_selection) + + # Insert the \end{...} + text_insert = f"\n\\end{{{insert_text}}}" + view.run_command("insert", {"characters": text_insert}) + + # Place the cursor at the end of the line containing the \begin{...} + move_cursor_vertical(view, sel, -1, take_cursor=take_selection) + + # Add a \n\t for correct indentation + view.run_command("insert", {"characters": "\n\t"}) + def matches_line(self, line): return bool(BEGIN_END_BEFORE_REGEX.match(line)) diff --git a/latextools/latex_fill_all.py b/latextools/latex_fill_all.py index 3e73f770..1835907b 100644 --- a/latextools/latex_fill_all.py +++ b/latextools/latex_fill_all.py @@ -11,6 +11,7 @@ from .utils.decorators import async_completions from .utils.logging import logger from .utils.settings import get_setting +from .utils.sublime_utils import move_cursor_relative __all__ = [ "LatexFillAllEventListener", @@ -80,6 +81,24 @@ def get_completions(self, view, prefix, line): """ return None + def on_selection(self, view, insert_text, should_complete): + """ + Code executed after the user has selected a completion and after the completion has been inserted in the view + + :param view: + The current `View` being edited + + :param insert_text: + Text used for the completion + + :param should_complete: + Indicates if the fill helper is replacing an existing element or completing a newly created one + + """ + # If we add a new element, moves the cursor after the closing bracket + if should_complete and get_setting("smart_cursor_move_auto_trigger", False): + move_cursor_relative(view.sel(), 1) + def matches_line(self, line): """ Checks if this plugin matches the current line @@ -1108,6 +1127,8 @@ def on_done(i, text=""): "remove_regions": self.regions_to_tuples(remove_regions), }, ) + + completion_type.on_selection(view, insert_text, should_complete=(insert_char != '')) # track visible input quick panels to provide key binding context VISIBLE_OVERLAYS.add(window.id()) diff --git a/latextools/utils/sublime_utils.py b/latextools/utils/sublime_utils.py index 5edb65d1..76a051fe 100644 --- a/latextools/utils/sublime_utils.py +++ b/latextools/utils/sublime_utils.py @@ -80,3 +80,67 @@ def get_project_file_name(view: sublime.View) -> str | None: if window: return window.project_file_name() return None + +def move_cursor_relative(sel, drift, take_cursor=None): + """ + Moves the cursor relatively from its current position + + :param sel: + `Selection` containing all the `Region`s to update + + :param drift: + Number of character the cursor must move + + :param take_cursor: + Optional array of booleans indicating if the ith `Region` must be moved. Note that take_cursor must have the same length as sel! + """ + new_regions = [] + + for i in range(len(sel)): + region = sel[i] + pos = region.end() + if take_cursor is None or take_cursor[i]: + pos = pos + drift + + new_regions.append(sublime.Region(pos)) + + sel.clear() + for r in new_regions: + sel.add(r) + +def move_cursor_vertical(view, sel, drift, take_cursor=None): + """ + Moves the cursor to the beginning of the next line or to the end of the previous line + + :param view: + Current view object + + :param sel: + `Selection` containing all the `Region`s to update + + :param drift: + +1 to go to the previous line, -1 to go to the next line + + :param take_cursor: + Optional array of booleans indicating if the ith `Region` must be moved. Note that take_cursor must have the same length as sel! + """ + new_regions = [] + + for i in range(len(sel)): + region = sel[i] + pos = region.end() + + print("Before: ", pos) + if take_cursor is None or take_cursor[i]: + # Get the current line + line = view.line(pos) + if drift > 0: + pos = line.end() + 1 + else: + pos = line.begin() - 1 + + new_regions.append(sublime.Region(pos)) + + sel.clear() + for r in new_regions: + sel.add(r) \ No newline at end of file