def run(self, edit): v = self.view if v.sel()[0].size() == 0: v.run_command("expand_selection", {"to": "word"}) cur_start = v.sel()[0].begin() cur_end = v.sel()[0].end() for sel in v.sel(): text = v.substr(sel) res = self.matcher(text) if not res: #first check one character to the left to see if its a symbol sel = Region(sel.begin() - 1, sel.end()) text = v.substr(sel) res = self.matcher(text) if not res: #now expand selection one character to the right to see if its a string sel = Region(sel.begin(), sel.end() + 1) text = v.substr(sel) res = self.matcher(text) if not res: #this is a mute point continue self.replacer(v, edit, sel, text, res) v.sel().clear() v.sel().add(Region(cur_start, cur_end))
def run(self, edit): sel = self.view.sel() current = sel[0].begin() line = self.view.line(current) length = line.end() - line.begin() upper = '/*==' + ('-' * (length - 1)) bottom = '/' + ('-' * (length - 1)) + '==*/' added = self.view.insert(edit, line.begin(), upper + '\n/ ') line = Region(line.begin() + added, line.end() + added) self.view.insert(edit, line.end(), '\n' + bottom)
def run(self, edit, **kwargs): v = self.view if v.sel()[0].size() == 0: v.run_command('expand_selection', {'to': 'scope'}) for sel in v.sel(): text = v.substr(sel) res = re_quotes.match(text) if not res: # the current selection doesn't begin and end with a quote. # let's expand the selection one character each direction and try again sel = Region(sel.begin() - 1, sel.end() + 1) text = v.substr(sel) res = re_quotes.match(text) if not res: # still no match... skip it! continue oldQuotes = res.group(1) if 'key' in kwargs: newQuotes = kwargs['key'] else: newQuotes = quoteList[(quoteList.index(oldQuotes) + 1) % len(quoteList)] text = res.group(2) text = text.replace(newQuotes, "\\" + newQuotes) text = text.replace("\\" + oldQuotes, oldQuotes) text = newQuotes + text + newQuotes v.replace(edit, sel, text)
def run(self, edit): v = self.view if v.sel()[0].size() == 0: v.run_command("expand_selection", {"to": "scope"}) for sel in v.sel(): text = v.substr(sel) res = re_quotes.match(text) if not res: # the current selection doesn't begin and end with a quote. # let's expand the selection one character each direction and try again sel = Region(sel.begin() - 1, sel.end() + 1) text = v.substr(sel) res = re_quotes.match(text) if not res: # still no match... skip it! continue oldQuotes = res.group(1) newQuotes = "'" if oldQuotes == '"' else '"' text = res.group(2) text = string.replace(text, newQuotes, "\\" + newQuotes) text = string.replace(text, "\\" + oldQuotes, oldQuotes) text = newQuotes + text + newQuotes v.replace(edit, sel, text)
def at_word_bound(editor: sublime.View, r: sublime.Region) -> bool: ch = editor.substr(r.end()) return not ch or re_bound_char.match(ch)
def create_tracker(editor: sublime.View, region: sublime.Region, params: dict) -> AbbreviationTracker: """ Creates abbreviation tracker for given range in editor. Parses contents of abbreviation in range and returns either valid abbreviation tracker, error tracker or `None` if abbreviation cannot be created from given range """ config = get_by_key(params, 'config') offset = get_by_key(params, 'offset', 0) forced = get_by_key(params, 'forced', False) line = editor.line(region.begin()) if region.a > region.b or (region.a == region.b and not forced) or not line.contains(region): # * Invalid range # * Mulitline regions are not supported return abbreviation = editor.substr(region) if offset: abbreviation = abbreviation[offset:] # Basic validation: do not allow empty abbreviations # or newlines in abbreviations if (not abbreviation and not forced) or '\n' in abbreviation or '\r' in abbreviation: return tracker_params = { 'forced': forced, 'offset': offset, 'line': line, 'last_pos': region.end(), } try: tracker_params['simple'] = False if config.type == 'stylesheet': parsed_abbr = stylesheet_abbreviation(abbreviation, config) else: parsed_abbr = markup_abbreviation(abbreviation, config) jsx = config and syntax.is_jsx(config.syntax) and bool( get_jsx_prefix()) tracker_params[ 'simple'] = not jsx and is_simple_markup_abbreviation( parsed_abbr) preview_config = get_preview_config(config) tracker_params['preview'] = expand(abbreviation, preview_config) if forced or config.type != 'stylesheet' or tracker_params['preview']: # Create tracker only if preview is not empty for non-forced abbreviation. # Empty preview means Emmet was unable to find proper match for given # abbreviation. Most likely it happens in stylesheets in `Section` scope # NB: empty preview is perfectly valid case for markup abbreviations, # for example, `()` abbreviation produces empty output return AbbreviationTrackerValid(abbreviation, region, config, tracker_params) except Exception as err: if hasattr(err, 'message') and hasattr(err, 'pos'): tracker_params['error'] = { 'message': err.message.split('\n')[0], 'pos': err.pos, 'pointer': '%s^' % ('-' * err.pos, ) if err.pos is not None else '' } return AbbreviationTrackerError(abbreviation, region, config, tracker_params) else: print('Error while parsing abbreviation') traceback.print_exc()
def in_range(region: sublime.Region, pt: int): return region.begin() < pt < region.end()
def run_each(self, edit, region, braces='{}', pressed=None, unindent=False, select=False, replace=False): self.view.sel().subtract(region) if self.view.settings().get('translate_tabs_to_spaces'): tab = ' ' * self.view.settings().get('tab_size') else: tab = "\t" row, col = self.view.rowcol(region.begin()) indent_point = self.view.text_point(row, 0) if indent_point < region.begin(): indent = self.view.substr(Region(indent_point, region.begin())) indent = re.match('[ \t]*', indent).group(0) else: indent = '' line = self.view.substr(self.view.line(region.a)) selection = self.view.substr(region) # for braces that have newlines ("""), insert the current line's indent if isinstance(braces, list): l_brace = braces[0] r_brace = braces[1] braces = ''.join(braces) braces = braces.replace("\n", "\n" + indent) length = len(l_brace) else: braces = braces.replace("\n", "\n" + indent) length = len(braces) / 2 l_brace = braces[:length] r_brace = braces[length:] if region.empty(): after = self.view.substr(Region(region.a, region.a + length)) insert_braces = braces complicated_check = self.complicated_quote_checker( insert_braces, region, pressed, after, r_brace) if complicated_check: insert_braces = complicated_check elif pressed and after == r_brace and r_brace[ -1] == pressed: # and (pressed not in QUOTING_BRACKETS or in_string_scope): # in this case we pressed the closing character, and that's the character that is to the right # so do nothing except advance cursor position insert_braces = False elif unindent and row > 0 and indent and line == indent: # indent has the current line's indent # get previous line's indent: prev_point = self.view.text_point(row - 1, 0) prev_line = self.view.line(prev_point) prev_indent = self.view.substr(prev_line) prev_indent = re.match('[ \t]*', prev_indent).group(0) if (not pressed or pressed == l_brace) and len(indent) > len( prev_indent) and indent[len(prev_indent):] == tab: # move region.a back by 'indent' amount region = Region(region.a - len(tab), region.b - len(tab)) # and remove the tab self.view.replace( edit, Region(region.a, region.a + len(tab) - 1), '') elif pressed and pressed == r_brace: if len(indent) == len(prev_indent): # move region.a back by 'indent' amount region = Region(region.a - len(tab), region.b - len(tab)) # and remove the tab self.view.replace( edit, Region(region.a, region.a + len(tab) - 1), '') insert_braces = r_brace elif pressed and pressed != l_brace: # we pressed the closing bracket or quote. This *never* insert_braces = r_brace if insert_braces: self.view.insert(edit, region.a, insert_braces) self.view.sel().add(Region(region.a + length, region.a + length)) elif selection in QUOTING_BRACKETS and pressed in QUOTING_BRACKETS and selection != pressed: # changing a quote from single <=> double, just insert the quote. self.view.replace(edit, region, pressed) self.view.sel().add(Region(region.end(), region.end())) elif pressed and pressed != l_brace: b = region.begin() + len(r_brace) self.view.replace(edit, region, r_brace) self.view.sel().add(Region(b, b)) else: substitute = self.view.substr(region) replacement = l_brace + substitute + r_brace # if we're inserting "real" brackets, not quotes: real_brackets = l_brace in OPENING_BRACKETS and r_brace in CLOSING_BRACKETS # check to see if entire lines are selected, and if so do some smart indenting bol_is_nl = region.begin() == 0 or self.view.substr( region.begin() - 1) == "\n" eol_is_nl = region.end( ) == self.view.size() - 1 or self.view.substr(region.end() - 1) == "\n" if real_brackets and bol_is_nl and eol_is_nl: indent = '' final = '' m = re.match('([ \t]*)' + tab, self.view.substr(region)) if m: indent = m.group(1) final = "\n" else: substitute = tab + substitute replacement = indent + l_brace + "\n" + substitute + indent + r_brace + final b = region.begin() + len(replacement) - len("\n" + indent + r_brace + final) else: b = region.begin() + len(replacement) if replace and self.view.substr( region.begin() - 1) in OPENING_BRACKET_LIKE and self.view.substr( region.end()) in CLOSING_BRACKET_LIKE: b -= 1 self.view.replace(edit, Region(region.begin() - 1, region.end() + 1), replacement) elif replace and self.view.substr(region.begin( )) in OPENING_BRACKET_LIKE and self.view.substr( region.end() - 1) in CLOSING_BRACKET_LIKE: replacement = l_brace + replacement[2:-2] + r_brace b -= 2 self.view.replace(edit, region, replacement) l_brace = r_brace = '' else: self.view.replace(edit, region, replacement) if select: self.view.sel().add( Region(b - len(replacement) + len(l_brace), b - len(r_brace))) else: self.view.sel().add(Region(b, b))
def add_comment(view: sublime.View, edit: sublime.Edit, region: sublime.Region, tokens: dict): "Adds comments around given range" view.insert(edit, region.end(), ' ' + tokens['end']) view.insert(edit, region.begin(), tokens['start'] + ' ')
def _f(view, s): if mode == INTERNAL_NORMAL: if len(target) != 1: return s # The *target* letters w, W, s, and p correspond to a |word|, a # |WORD|, a |sentence|, and a |paragraph| respectively. These are # special in that they have nothing to delete, and used with |ds| they # are a no-op. With |cs|, one could consider them a slight shortcut for # ysi (cswb == ysiwb, more or less). noop = 'wWsp' if target in noop: return s valid_targets = '\'"`b()B{}r[]a<>t.,-_;:@#~*\\/' if target not in valid_targets: return s # All marks, except punctuation marks, are only searched for on the # current line. # If opening punctuation mark is used, contained whitespace is also trimmed. trim_contained_whitespace = True if target in '({[<' else False search_current_line_only = False if target in 'b()B{}r[]a<>' else True # Expand targets into begin and end variables because punctuation marks # and their aliases represent themselves and their counterparts e.g. (), # []. Target is the same for begin and end for all other valid marks # e.g. ', ", `, -, _, etc. t_char_begin, t_char_end = _get_punctuation_marks(target) s_rowcol_begin = view.rowcol(s.begin()) s_rowcol_end = view.rowcol(s.end()) # A t is a pair of HTML or XML tags. if target == 't': # TODO test dst works when cursor position is inside tag begin <a|bc>x</abc> -> dst -> |x # TODO test dst works when cursor position is inside tag end <abc>x</a|bc> -> dst -> |x t_region_end = view.find('<\\/.*?>', s.b) t_region_begin = reverse_search(view, '<.*?>', start=0, end=s.b) else: current = view.substr(s.begin()) # TODO test ds{char} works when cursor position is on target begin |"x" -> ds" -> |x # TODO test ds{char} works when cursor position is on target end "x|" -> ds" -> |x if current == t_char_begin: t_region_begin = Region(s.begin(), s.begin() + 1) else: t_region_begin = _rfind(view, t_char_begin, start=0, end=s.begin(), flags=LITERAL) t_region_begin_rowcol = view.rowcol(t_region_begin.begin()) t_region_end = _find(view, t_char_end, start=t_region_begin.end(), flags=LITERAL) t_region_end_rowcol = view.rowcol(t_region_end.end()) if search_current_line_only: if t_region_begin_rowcol[0] != s_rowcol_begin[0]: return s if t_region_end_rowcol[0] != s_rowcol_end[0]: return s if trim_contained_whitespace: t_region_begin_ws = _find(view, '\\s*.', start=t_region_begin.end()) t_region_end_ws = _rfind(view, '.\\s*', start=t_region_begin.end(), end=t_region_end.begin()) if t_region_begin_ws.size() > 1: t_region_begin = Region(t_region_begin.begin(), t_region_begin_ws.end() - 1) if t_region_end_ws.size() > 1: t_region_end = Region(t_region_end_ws.begin() + 1, t_region_end.end()) # Note: Be careful using boolean evaluation on a Region because an empty # Region evaluates to False. It evaluates to False because Region # invokes `__len__()` which will be zero if the Region is empty e.g. # `Region(3).size()` is `0`, whereas `Region(3, 4).size()` is `1`. # `sublime.View.find(sub)` returns `Region(-1)` if *sub* not found. This # is similar to how the python `str.find(sub)` function works i.e. it # returns `-1` if *sub* not found, because *sub* could be found at # position `0`. To check if a Region was found use `Region(3) >= 0`. To # check if a Region is non empty you can use boolean evaluation i.e. `if # Region(3): ...`. In the following case boolean evaluation is # intentional. if not (t_region_end and t_region_begin): return s # It's important that the end is replaced first. If we replaced the # begin region first then the end replacement would be off-by-one # because the begin is reducing the size of the internal buffer by one # i.e. it's deleting a character. view.replace(edit, t_region_end, '') view.replace(edit, t_region_begin, '') return Region(t_region_begin.begin()) return s
def text_substr(text: str, r: sublime.Region) -> str: return text[r.begin():r.end()]
def uncomment_region(view, edit, region): begin = region.begin() end = region.end() - 1 # We will loop backwards, this means that it will hit the closing # punctuation for block comments first. i = end + 1 while i > begin: i -= 1 scopes = view.scope_name(i) # Not a punctuation, ignore it. if 'punctuation.definition.comment' not in scopes: continue # Found the second forward slash for the “// ” comment. if 'comment.line' in scopes: punctuation_region = generate_comment_punctuation_region(view, i) view.erase(edit, punctuation_region) i = punctuation_region.begin() continue # We found the beginning of the block comment first, this means that # there’s no end to it and we can easily remove it. It can be “/* ”, # “/** ”, “{/* ” or “{/** ”. if 'punctuation.definition.comment.begin' in scopes: punctuation_region = generate_jsjsx_comment_punctuation_region( view, i) view.erase(edit, punctuation_region) i = punctuation_region.begin() continue # We are looping backwards, so it is expected to find the closing # punctuation first which can be “ */” or “ */}”. possible_jsx_comment = False if i < view.size() and is_jsx_close_brace(view, i + 1): possible_jsx_comment = True closing_punctuation_region = generate_comment_punctuation_region( view, i) # Move the cursor 1 character after the beginning punctuation. i = scan_reverse( view, i, not_predicate( has_scope_predicate('punctuation.definition.comment.begin'))) open_punctuation_region = generate_comment_punctuation_region( view, i - 1) # Correct the regions to include the JSX braces if necessary. if possible_jsx_comment: if is_jsx_open_brace(view, open_punctuation_region.begin() - 1): open_punctuation_region = Region( open_punctuation_region.begin() - 1, open_punctuation_region.end()) closing_punctuation_region = Region( closing_punctuation_region.begin(), closing_punctuation_region.end() + 1) view.erase(edit, closing_punctuation_region) view.erase(edit, open_punctuation_region) # Move the cursor to the beginning of the block to “consume” it. i = open_punctuation_region.begin()
def run_each(self, edit, region, braces='{}', pressed=None, unindent=False, select=False, replace=False): ''' Options: braces a list of matching braces or a string containing the pair pressed the key pressed; used for closing vs opening logic unindent removes one "tab" from a newline. true for braces that handle indent, like {} select whether to select the region inside the braces replace whether to insert new braces where the old braces were ''' if self.view.settings().get('translate_tabs_to_spaces'): tab = ' ' * self.view.settings().get('tab_size') else: tab = "\t" row, col = self.view.rowcol(region.begin()) indent_point = self.view.text_point(row, 0) if indent_point < region.begin(): indent = self.view.substr(Region(indent_point, region.begin())) indent = re.match('[ \t]*', indent).group(0) else: indent = '' line = self.view.substr(self.view.line(region.a)) selection = self.view.substr(region) # for braces that have newlines ("""), insert the current line's indent if isinstance(braces, list): l_brace = braces[0] r_brace = braces[1] braces = ''.join(braces) braces = braces.replace("\n", "\n" + indent) length = len(l_brace) else: braces = braces.replace("\n", "\n" + indent) length = len(braces) // 2 l_brace = braces[:length] r_brace = braces[length:] if region.empty(): after = self.view.substr(Region(region.a, region.a + length)) insert_braces = braces complicated_check = self.complicated_quote_checker( insert_braces, region, pressed, after, r_brace) if complicated_check: insert_braces = complicated_check elif pressed and after == r_brace and r_brace[ -1] == pressed: # and (pressed not in QUOTING_BRACKETS or in_string_scope): # in this case we pressed the closing character, and that's the character that is to the right # so do nothing except advance cursor position insert_braces = False elif unindent and row > 0 and indent and line == indent: # indent has the current line's indent # get previous line's indent: prev_point = self.view.text_point(row - 1, 0) prev_line = self.view.line(prev_point) prev_indent = self.view.substr(prev_line) prev_indent = re.match('[ \t]*', prev_indent).group(0) if (not pressed or pressed == l_brace) and len(indent) > len( prev_indent) and indent[len(prev_indent):] == tab: # move region.a back by 'indent' amount region = Region(region.a - len(tab), region.b - len(tab)) # and remove the tab self.view.replace( edit, Region(region.a, region.a + len(tab) - 1), '') elif pressed and pressed == r_brace: # move region.a back by 'indent' amount region = Region(region.a - len(tab), region.b - len(tab)) # and remove the tab self.view.replace(edit, Region(region.a, region.a + len(tab)), '') insert_braces = r_brace elif pressed and pressed != l_brace: # we pressed the closing bracket or quote. This *never* insert_braces = r_brace if insert_braces: self.view.insert(edit, region.a, insert_braces) self.view.sel().add(Region(region.a + length, region.a + length)) elif selection in QUOTING_BRACKETS and pressed in QUOTING_BRACKETS and selection != pressed: # changing a quote from single <=> double, just insert the quote. self.view.replace(edit, region, pressed) self.view.sel().add(Region(region.end(), region.end())) elif pressed and pressed != l_brace: b = region.begin() + len(r_brace) self.view.replace(edit, region, r_brace) self.view.sel().add(Region(b, b)) else: substitute = self.view.substr(region) replacement = l_brace + substitute + r_brace # if we're inserting "real" brackets, not quotes: real_brackets = l_brace in OPENING_BRACKETS and r_brace in CLOSING_BRACKETS # check to see if entire lines are selected, and if so do some smart indenting # bol_is_nl => allman style {} # bol_at_nl => kernigan&ritchie if region.begin() == 0: bol_is_nl = True bol_at_nl = False elif len(self.view) == region.begin() + 1: bol_is_nl = False bol_at_nl = False else: bol_is_nl = self.view.substr(region.begin() - 1) == "\n" bol_at_nl = l_brace == '{' and self.view.substr(region.begin( )) == "\n" and self.view.substr(region.begin() - 1) != "\n" eol_is_nl = region.end() == self.view.size() or self.view.substr( region.end()) == "\n" eol_at_nl = self.view.substr(region.end() - 1) == "\n" if eol_is_nl: eol_is_nl = self.view.line(region.begin()) != self.view.line( region.end()) if real_brackets and (bol_is_nl or bol_at_nl) and (eol_is_nl or eol_at_nl): indent = '' if bol_at_nl and substitute: substitute = substitute[1:] m = re.match('([ \t]*)' + tab, substitute) if m: indent = m.group(1) else: substitute = tab + substitute b = region.begin() - len("\n" + indent + r_brace) if bol_at_nl: replacement = l_brace + "\n" + substitute if eol_at_nl: replacement += indent + r_brace + "\n" b -= 1 else: replacement += r_brace + "\n" b += len(indent) if not self.view.substr(region.begin() - 1) == ' ': replacement = ' ' + replacement else: replacement = indent + l_brace + "\n" + substitute + indent + r_brace + "\n" b -= 1 b += len(replacement) else: b = region.begin() + len(replacement) if replace and self.view.substr( region.begin() - 1) in OPENING_BRACKET_LIKE and self.view.substr( region.end()) in CLOSING_BRACKET_LIKE: b -= 1 self.view.replace(edit, Region(region.begin() - 1, region.end() + 1), replacement) elif replace and self.view.substr(region.begin( )) in OPENING_BRACKET_LIKE and self.view.substr( region.end() - 1) in CLOSING_BRACKET_LIKE: replacement = l_brace + replacement[2:-2] + r_brace b -= 2 self.view.replace(edit, region, replacement) l_brace = r_brace = '' else: self.view.replace(edit, region, replacement) if select: self.view.sel().add( Region(b - len(replacement) + len(l_brace), b - len(r_brace))) else: self.view.sel().add(Region(b, b))
def uncomment_lines(view, edit, region): begin = region.begin() end = region.end() i = end + 1 while i > begin: i -= 1 scopes = view.scope_name(i) if 'punctuation.definition.comment' not in scopes: continue # Found the second forward slash for the “// ” comment. if 'comment.line' in scopes: i = get_comment_beginning_pos(view, i) content_begin = get_comment_content_beginning(view, i) view.erase(edit, Region(i, content_begin)) continue # We found the beginning of the block comment first which means that there’s # no end to it and we can easily remove it. It can be “/* ”, “/** ”, “{/* ” # or “{/** ”. if 'punctuation.definition.comment.begin' in scopes: i = get_comment_beginning_pos(view, i) content_begin = get_comment_content_beginning(view, i) # We need to check the braces for possible JSX interpolation. if i > 0 and is_jsx_open_brace(view, i - 1): # It was a JSX block comment. i -= 1 # We have “i” positioned at the beginning of the comment or the brace if # it is a JSX comment. view.erase(edit, Region(i, content_begin)) continue # We are looping backwards, so it is expected to find the closing punctuation # first which can be “ */” or “ */}”. possible_jsx_comment = False if i < view.size() and is_jsx_close_brace(view, i + 1): possible_jsx_comment = True close_block = i # Now, we need to find the the openning of the block. while 'punctuation.definition.comment.begin' not in scopes: i -= 1 scopes = view.scope_name(i) open_block = i # This will generate a region that allows us to remove the block punctuation. open_block = expand_openning_block_punctuation(view, open_block) close_block = expand_closing_block_punctuation(view, close_block) # Correct the regions to include the JSX braces if necessary. if possible_jsx_comment: if open_block.begin() > 0 and is_jsx_open_brace( view, open_block.begin() - 1): open_block = Region(open_block.begin() - 1, open_block.end()) close_block = Region(close_block.begin(), close_block.end() + 1) view.erase(edit, close_block) view.erase(edit, open_block) # Move the cursor to the beginning of the block to “consume” it. i = open_block.begin()
def create_tracker(editor: sublime.View, region: sublime.Region, params: dict) -> AbbreviationTracker: """ Creates abbreviation tracker for given range in editor. Parses contents of abbreviation in range and returns either valid abbreviation tracker, error tracker or `None` if abbreviation cannot be created from given range """ config = get_by_key(params, 'config') offset = get_by_key(params, 'offset', 0) forced = get_by_key(params, 'forced', False) if region.a > region.b or (region.a == region.b and not forced): # Invalid range return abbreviation = editor.substr(region) if offset: abbreviation = abbreviation[offset:] # Basic validation: do not allow empty abbreviations # or newlines in abbreviations if (not abbreviation and not forced) or '\n' in abbreviation or '\r' in abbreviation: return tracker_params = { 'forced': forced, 'offset': offset, 'last_pos': region.end(), 'last_length': editor.size(), } try: tracker_params['simple'] = False if config.type == 'stylesheet': parsed_abbr = stylesheet_abbreviation(abbreviation, config) else: parsed_abbr = markup_abbreviation(abbreviation, config) jsx = config and syntax.is_jsx(config.syntax) tracker_params[ 'simple'] = not jsx and is_simple_markup_abbreviation( parsed_abbr) preview_config = get_preview_config(config) tracker_params['preview'] = expand(abbreviation, preview_config) if tracker_params['preview'] or forced: # Create tracker only if preview is not empty for non-forced abbreviation. # Empty preview means Emmet was unable to find proper match for given # abbreviation. Most likely it happens in stylesheets in `Section` scope return AbbreviationTrackerValid(abbreviation, region, config, tracker_params) except Exception as err: if hasattr(err, 'message') and hasattr(err, 'pos'): tracker_params['error'] = { 'message': err.message, 'pos': err.pos, 'pointer': '%s^' % ('-' * err.pos, ) if err.pos is not None else '' } return AbbreviationTrackerError(abbreviation, region, config, tracker_params) else: print(repr(err))
def from_region(self, view: sublime.View, region: sublime.Region) -> 'Range': return Range(Point.from_text_point(view, region.begin()), Point.from_text_point(view, region.end()))
def region_contains(region: sublime.Region, pt: int) -> bool: return region.begin() < pt < region.end()
def run_each(self, edit, region, braces='{}', pressed=None, unindent=False, select=False, replace=False): self.view.sel().subtract(region) if self.view.settings().get('translate_tabs_to_spaces'): tab = ' ' * self.view.settings().get('tab_size') else: tab = "\t" row, col = self.view.rowcol(region.begin()) indent_point = self.view.text_point(row, 0) if indent_point < region.begin(): indent = self.view.substr(Region(indent_point, region.begin())) indent = re.match('[ \t]*', indent).group(0) else: indent = '' line = self.view.substr(self.view.line(region.a)) selection = self.view.substr(region) # for braces that have newlines ("""), insert the current line's indent braces = braces.replace("\n", "\n" + indent) length = len(braces) / 2 l_brace = braces[:length] r_brace = braces[length:] if region.empty(): after = self.view.substr(Region(region.a, region.a + length)) insert_braces = braces complicated_check = self.complicated_quote_checker(insert_braces, region, pressed, after, r_brace) if complicated_check: insert_braces = complicated_check elif pressed and after == r_brace and r_brace[-1] == pressed: # and (pressed not in QUOTING_BRACKETS or in_string_scope): # in this case we pressed the closing character, and that's the character that is to the right # so do nothing except advance cursor position insert_braces = False elif unindent and row > 0 and indent and line == indent: # indent has the current line's indent # get previous line's indent: prev_point = self.view.text_point(row - 1, 0) prev_line = self.view.line(prev_point) prev_indent = self.view.substr(prev_line) prev_indent = re.match('[ \t]*', prev_indent).group(0) if (not pressed or pressed == l_brace) and len(indent) > len(prev_indent) and indent[len(prev_indent):] == tab: # move region.a back by 'indent' amount region = Region(region.a - len(tab), region.b - len(tab)) # and remove the tab self.view.replace(edit, Region(region.a, region.a + len(tab) - 1), '') elif pressed and pressed == r_brace: if len(indent) == len(prev_indent): # move region.a back by 'indent' amount region = Region(region.a - len(tab), region.b - len(tab)) # and remove the tab self.view.replace(edit, Region(region.a, region.a + len(tab) - 1), '') insert_braces = r_brace elif pressed and pressed != l_brace: # we pressed the closing bracket or quote. This *never* insert_braces = r_brace if insert_braces: self.view.insert(edit, region.a, insert_braces) self.view.sel().add(Region(region.a + length, region.a + length)) elif selection in QUOTING_BRACKETS and pressed in QUOTING_BRACKETS and selection != pressed: # changing a quote from single <=> double, just insert the quote. self.view.replace(edit, region, pressed) self.view.sel().add(Region(region.end(), region.end())) elif pressed and pressed != l_brace: b = region.begin() + len(r_brace) self.view.replace(edit, region, r_brace) self.view.sel().add(Region(b, b)) else: substitute = self.view.substr(region) replacement = l_brace + substitute + r_brace # if we're inserting "real" brackets, not quotes: real_brackets = l_brace in OPENING_BRACKETS and r_brace in CLOSING_BRACKETS # check to see if entire lines are selected, and if so do some smart indenting bol_is_nl = region.begin() == 0 or self.view.substr(region.begin() - 1) == "\n" eol_is_nl = region.end() == self.view.size() - 1 or self.view.substr(region.end() - 1) == "\n" if real_brackets and bol_is_nl and eol_is_nl: indent = '' final = '' m = re.match('([ \t]*)' + tab, self.view.substr(region)) if m: indent = m.group(1) final = "\n" else: substitute = tab + substitute replacement = indent + l_brace + "\n" + substitute + indent + r_brace + final b = region.begin() + len(replacement) - len("\n" + indent + r_brace + final) else: b = region.begin() + len(replacement) if replace and self.view.substr(region.begin() - 1) in OPENING_BRACKET_LIKE and self.view.substr(region.end()) in CLOSING_BRACKET_LIKE: b -= 1 self.view.replace(edit, Region(region.begin() - 1, region.end() + 1), replacement) elif replace and self.view.substr(region.begin()) in OPENING_BRACKET_LIKE and self.view.substr(region.end() - 1) in CLOSING_BRACKET_LIKE: replacement = l_brace + replacement[2:-2] + r_brace b -= 2 self.view.replace(edit, region, replacement) l_brace = r_brace = '' else: self.view.replace(edit, region, replacement) if select: self.view.sel().add(Region(b - len(replacement) + len(l_brace), b - len(r_brace))) else: self.view.sel().add(Region(b, b))
def resolve_indent_text_object(view, s: Region, inclusive: bool = True, big: bool = False): # Look for the minimum indentation in the current visual region. idnt = 1000 idnt_pt = None for line in view.lines(s): if not re.match('^\\s*$', view.substr(line)): level = view.indentation_level(line.a) if level < idnt: idnt = min(idnt, level) idnt_pt = line.a # If the selection has no indentation at all, find which indentation level # is the largest, the previous non blank before tphe cursor or the next non # blank after the cursor, and start the selection from that point. if idnt == 1000: pnb_pt = prev_non_ws(view, s.begin()) pnb_indent_level = view_indentation_level(view, pnb_pt) nnb_pt = next_non_ws(view, s.end()) nnb_indent_level = view_indentation_level(view, nnb_pt) if pnb_indent_level > nnb_indent_level: idnt_pt = s.a = s.b = pnb_pt elif nnb_indent_level > pnb_indent_level: idnt_pt = s.a = s.b = nnb_pt else: idnt_pt = pnb_pt if idnt == 0 and idnt_pt is not None: expanded = view.expand_by_class(s, CLASS_EMPTY_LINE) s.a = expanded.a s.b = expanded.b if not inclusive: # Case: ii and iI. Strip any leading whitespace. leading_ws = view_find(view, '\\s*', s.a) if leading_ws is not None: s.a = view.line(leading_ws.b).a s.b = prev_non_blank(view, s.b) elif big: # Case: aI. Add a line below. if view.substr(s.b) == '\n': s.b += 1 elif idnt > 0 and idnt_pt is not None: indented_region = view_indented_region(view, idnt_pt, inclusive) if indented_region.begin() < s.begin(): s.a = indented_region.begin() if indented_region.end() > s.end(): s.b = indented_region.end() if inclusive: # Case: ai. Add a line above. s.a = view.line(view.text_point(view.rowcol(s.a)[0] - 1, 0)).a # Case: aI. Add a line below. if big: s.b = view.full_line(view.text_point(view.rowcol(s.b - 1)[0] + 1, 0)).b return s
def run_each(self, edit, region, braces="{}", pressed=None, unindent=False, select=False, replace=False): """ Options: braces a list of matching braces or a string containing the pair pressed the key pressed; used for closing vs opening logic unindent removes one "tab" from a newline. true for braces that handle indent, like {} select whether to select the region inside the braces replace whether to insert new braces where the old braces were """ if self.view.settings().get("translate_tabs_to_spaces"): tab = " " * self.view.settings().get("tab_size") else: tab = "\t" row, col = self.view.rowcol(region.begin()) indent_point = self.view.text_point(row, 0) if indent_point < region.begin(): indent = self.view.substr(Region(indent_point, region.begin())) indent = re.match("[ \t]*", indent).group(0) else: indent = "" line = self.view.substr(self.view.line(region.a)) selection = self.view.substr(region) # for braces that have newlines ("""), insert the current line's indent if isinstance(braces, list): l_brace = braces[0] r_brace = braces[1] braces = "".join(braces) braces = braces.replace("\n", "\n" + indent) length = len(l_brace) else: braces = braces.replace("\n", "\n" + indent) length = len(braces) // 2 l_brace = braces[:length] r_brace = braces[length:] if region.empty(): after = self.view.substr(Region(region.a, region.a + length)) insert_braces = braces complicated_check = self.complicated_quote_checker(insert_braces, region, pressed, after, r_brace) if complicated_check: insert_braces = complicated_check elif ( pressed and after == r_brace and r_brace[-1] == pressed ): # and (pressed not in QUOTING_BRACKETS or in_string_scope): # in this case we pressed the closing character, and that's the character that is to the right # so do nothing except advance cursor position insert_braces = False elif unindent and row > 0 and indent and line == indent: # indent has the current line's indent # get previous line's indent: prev_point = self.view.text_point(row - 1, 0) prev_line = self.view.line(prev_point) prev_indent = self.view.substr(prev_line) prev_indent = re.match("[ \t]*", prev_indent).group(0) if ( (not pressed or pressed == l_brace) and len(indent) > len(prev_indent) and indent[len(prev_indent) :] == tab ): # move region.a back by 'indent' amount region = Region(region.a - len(tab), region.b - len(tab)) # and remove the tab self.view.replace(edit, Region(region.a, region.a + len(tab) - 1), "") elif pressed and pressed == r_brace: if len(indent) == len(prev_indent): # move region.a back by 'indent' amount region = Region(region.a - len(tab), region.b - len(tab)) # and remove the tab self.view.replace(edit, Region(region.a, region.a + len(tab) - 1), "") insert_braces = r_brace elif pressed and pressed != l_brace: # we pressed the closing bracket or quote. This *never* insert_braces = r_brace if insert_braces: self.view.insert(edit, region.a, insert_braces) self.view.sel().add(Region(region.a + length, region.a + length)) elif selection in QUOTING_BRACKETS and pressed in QUOTING_BRACKETS and selection != pressed: # changing a quote from single <=> double, just insert the quote. self.view.replace(edit, region, pressed) self.view.sel().add(Region(region.end(), region.end())) elif pressed and pressed != l_brace: b = region.begin() + len(r_brace) self.view.replace(edit, region, r_brace) self.view.sel().add(Region(b, b)) else: substitute = self.view.substr(region) replacement = l_brace + substitute + r_brace # if we're inserting "real" brackets, not quotes: real_brackets = l_brace in OPENING_BRACKETS and r_brace in CLOSING_BRACKETS # check to see if entire lines are selected, and if so do some smart indenting # bol_is_nl => allman style {} # bol_at_nl => kernigan&ritchie if region.begin() == 0: bol_is_nl = True bol_at_nl = False elif len(self.view) == region.begin() + 1: bol_is_nl = False bol_at_nl = False else: bol_is_nl = self.view.substr(region.begin() - 1) == "\n" bol_at_nl = ( l_brace == "{" and self.view.substr(region.begin()) == "\n" and self.view.substr(region.begin() - 1) != "\n" ) eol_is_nl = region.end() == self.view.size() or self.view.substr(region.end()) == "\n" eol_at_nl = self.view.substr(region.end() - 1) == "\n" if eol_is_nl: eol_is_nl = self.view.line(region.begin()) != self.view.line(region.end()) if real_brackets and (bol_is_nl or bol_at_nl) and (eol_is_nl or eol_at_nl): indent = "" if bol_at_nl and substitute: substitute = substitute[1:] m = re.match("([ \t]*)" + tab, substitute) if m: indent = m.group(1) else: substitute = tab + substitute b = region.begin() - len("\n" + indent + r_brace) if bol_at_nl: replacement = l_brace + "\n" + substitute if eol_at_nl: replacement += indent + r_brace + "\n" b -= 1 else: replacement += r_brace + "\n" b += len(indent) if not self.view.substr(region.begin() - 1) == " ": replacement = " " + replacement else: replacement = indent + l_brace + "\n" + substitute + indent + r_brace + "\n" b -= 1 b += len(replacement) else: b = region.begin() + len(replacement) if ( replace and self.view.substr(region.begin() - 1) in OPENING_BRACKET_LIKE and self.view.substr(region.end()) in CLOSING_BRACKET_LIKE ): b -= 1 self.view.replace(edit, Region(region.begin() - 1, region.end() + 1), replacement) elif ( replace and self.view.substr(region.begin()) in OPENING_BRACKET_LIKE and self.view.substr(region.end() - 1) in CLOSING_BRACKET_LIKE ): replacement = l_brace + replacement[2:-2] + r_brace b -= 2 self.view.replace(edit, region, replacement) l_brace = r_brace = "" else: self.view.replace(edit, region, replacement) if select: self.view.sel().add(Region(b - len(replacement) + len(l_brace), b - len(r_brace))) else: self.view.sel().add(Region(b, b))
def get_region_end(r: Region) -> dict: return {'start': r.end()}
def region_to_range(view: sublime.View, region: sublime.Region) -> 'Range': return Range(offset_to_point(view, region.begin()), offset_to_point(view, region.end()))
def uncomment_region(view, edit, region): begin = region.begin() end = region.end() - 1 # We will loop backwards, this means that it will hit the closing # punctuation for block comments first. i = end + 1 while i > begin: i -= 1 scopes = view.scope_name(i) # Not a punctuation, ignore it. if 'punctuation.definition.comment' not in scopes: continue # Found the second forward slash for the “// ” comment. if 'comment.line' in scopes: punctuation_region = generate_comment_punctuation_region(view, i) view.erase(edit, punctuation_region) i = punctuation_region.begin() continue # We found the beginning of the block comment first, this means that # there’s no end to it and we can easily remove it. It can be “/* ”, # “/** ”, “{/* ” or “{/** ”. if 'punctuation.definition.comment.begin' in scopes: punctuation_region = generate_jsjsx_comment_punctuation_region( view, i ) view.erase(edit, punctuation_region) i = punctuation_region.begin() continue # We are looping backwards, so it is expected to find the closing # punctuation first which can be “ */” or “ */}”. possible_jsx_comment = False if i < view.size() and is_jsx_close_brace(view, i + 1): possible_jsx_comment = True closing_punctuation_region = generate_comment_punctuation_region( view, i ) # Move the cursor 1 character after the beginning punctuation. i = scan_reverse(view, i, not_predicate(has_scope_predicate( 'punctuation.definition.comment.begin' ))) open_punctuation_region = generate_comment_punctuation_region( view, i - 1 ) # Correct the regions to include the JSX braces if necessary. if possible_jsx_comment: if is_jsx_open_brace(view, open_punctuation_region.begin() - 1): open_punctuation_region = Region( open_punctuation_region.begin() - 1, open_punctuation_region.end() ) closing_punctuation_region = Region( closing_punctuation_region.begin(), closing_punctuation_region.end() + 1 ) view.erase(edit, closing_punctuation_region) view.erase(edit, open_punctuation_region) # Move the cursor to the beginning of the block to “consume” it. i = open_punctuation_region.begin()