def get_line_after_and_range(context, range=None): '''Get the full line immediately after the current (or supplied) range''' line_ending = tea.get_line_ending(context) len_line_ending = len(line_ending) if range is None: range = tea.get_range(context) content = context.string() start = range.location + range.length if not is_line_ending(content, start - len_line_ending, line_ending): start = content.find(line_ending, start) if start == -1: return None else: start += len_line_ending end = content.find(line_ending, start) if end == -1: end = len(content) else: end += len_line_ending start = max(0, start) end = min(end, len(content)) line_range = tea.new_range(start, end - start) return tea.get_selection(context, line_range), line_range
def get_line_after_and_range(context, range = None): '''Get the full line immediately after the current (or supplied) range''' line_ending = tea.get_line_ending(context) len_line_ending = len(line_ending) if range is None: range = tea.get_range(context) content = context.string() start = range.location + range.length if not is_line_ending(content, start - len_line_ending, line_ending): start = content.find(line_ending, start) if start == -1: return None else: start += len_line_ending end = content.find(line_ending, start) if end == -1: end = len(content) else: end += len_line_ending start = max(0, start) end = min(end, len(content)) line_range = tea.new_range(start, end - start) return tea.get_selection(context, line_range), line_range
def act(context, actionObject, operation='entab'): def replacements(match): '''Utility function for replacing items''' return match.group(0).replace(search, replace) spaces = int(actionObject.userInput().stringValue()) if operation == 'entab': target = re.compile(r'^(\t* +\t*)+', re.MULTILINE) search = ' ' * spaces replace = '\t' else: target = re.compile(r'^( *\t+ *)+', re.MULTILINE) search = '\t' replace = ' ' * spaces insertions = tea.new_recipe() ranges = tea.get_ranges(context) if len(ranges) == 1 and ranges[0].length == 0: # No selection, use the document ranges[0] = tea.new_range(0, context.string().length()) for range in ranges: text = tea.get_selection(context, range) # Non-Unix line endings will bork things; convert them text = tea.unix_line_endings(text) text = re.sub(target, replacements, text) if tea.get_line_ending(context) != '\n': text = tea.clean_line_endings(context, text) insertions.addReplacementString_forRange_(text, range) insertions.setUndoActionName_(operation.title()) context.applyTextRecipe_(insertions) return True
def set_context(self, context): """ Setup underlying editor context. You should call this method <code>before</code> using any Zen Coding action. @param context: context object """ self._context = context zen.newline = self.safe_str(tea.get_line_ending(context)) self.zen_settings["variables"]["indentation"] = self.safe_str(tea.get_indentation_string(context))
def set_context(self, context): """ Setup underlying editor context. You should call this method <code>before</code> using any Zen Coding action. @param context: context object """ self._context = context zen_coding.set_newline(tea.get_line_ending(context)) zen_coding.zen_settings['variables']['indentation'] = tea.get_indentation_string(context)
def act(context, direction=None, remove_duplicates=False, undo_name=None): ''' Required action method This only allows a single selection (enforced through the utility functions) then sorts the lines, either ascending or descending. Theoretically we could allow discontiguous selections; might be useful? ''' # Check if there is a selection, otherwise take all lines ranges = tea.get_ranges(context) if len(ranges) == 1 and ranges[0].length == 0: range = tea.new_range(0, context.string().length()) text = tea.get_selection(context, range) else: text, range = tea.get_single_selection(context, True) if text == None: return False # Split the text into lines, not maintaining the linebreaks lines = text.splitlines(False) # Remove duplicates if set if remove_duplicates: if direction is None: seen = {} result = [] for x in lines: if x in seen: continue seen[x] = 1 result.append(x) lines = result else: lines = list(set(lines)) # Sort lines ascending or descending if direction == 'asc' or direction == 'desc': lines.sort() if direction == 'desc': lines.reverse() # If direction is random, shuffle lines if direction == 'random': random.shuffle(lines) # Join lines to one string linebreak = tea.get_line_ending(context) sortedText = unicode.join(linebreak, lines) # Add final linebreak if selected text has one if text.endswith(linebreak): sortedText += linebreak # Paste the text return tea.insert_text_over_range(context, sortedText, range, undo_name)
def set_context(self, context, bundle): """ Setup underlying editor context. You should call this method <code>before</code> using any Zen Coding action. @param context: context object """ self._context = context self._bundle = bundle zencoding.utils.set_newline(tea.get_line_ending(context)) zencoding.utils.set_variable('indentation', tea.get_indentation_string(context))
def lines_and_range(context, range=None): '''Get the range of the full lines containing the current (or supplied) range''' line_ending = tea.get_line_ending(context) len_line_ending = len(line_ending) if range is None: range = tea.get_range(context) content = context.string() start, end = range.location, range.location + range.length if not is_line_ending(content, start - len_line_ending, line_ending): start = content.rfind(line_ending, 0, start) if start == -1: start = 0 else: start += len_line_ending # select to the end of the line (if it's not already selected) if not is_line_ending(content, end, line_ending): # edge case: cursor is at start of line and more than one line selected: if not is_line_ending(content, end - len_line_ending, line_ending) or len( content[start:end].split(line_ending)) <= 1: end = content.find(line_ending, end) if end == -1: end = len(content) else: end += len_line_ending # edge case: empty line, not selected elif is_line_ending(content, end - len_line_ending, line_ending): if len(content[start:end].split(line_ending)) <= 1: end = content.find(line_ending, end) if end == -1: end = len(content) else: end += len_line_ending else: end += len_line_ending start = max(0, start) end = min(end, len(content)) line_range = tea.new_range(start, end - start) return tea.get_selection(context, line_range), line_range
def lines_and_range(context, range = None): '''Get the range of the full lines containing the current (or supplied) range''' line_ending = tea.get_line_ending(context) len_line_ending = len(line_ending) if range is None: range = tea.get_range(context) content = context.string() start, end = range.location, range.location + range.length if not is_line_ending(content, start - len_line_ending, line_ending): start = content.rfind(line_ending, 0, start) if start == -1: start = 0 else: start += len_line_ending # select to the end of the line (if it's not already selected) if not is_line_ending(content, end, line_ending): # edge case: cursor is at start of line and more than one line selected: if not is_line_ending(content, end - len_line_ending, line_ending) or len(content[start:end].split(line_ending)) <= 1: end = content.find(line_ending, end) if end == -1: end = len(content) else: end += len_line_ending # edge case: empty line, not selected elif is_line_ending(content, end - len_line_ending, line_ending): if len(content[start:end].split(line_ending)) <= 1: end = content.find(line_ending, end) if end == -1: end = len(content) else: end += len_line_ending else: end += len_line_ending start = max(0, start) end = min(end, len(content)) line_range = tea.new_range(start, end - start) return tea.get_selection(context, line_range), line_range
def get_line_before_and_range(context, range=None): '''Get the full line immediately before the current (or supplied) range''' line_ending = tea.get_line_ending(context) if range is None: range = tea.get_range(context) content = context.string() end = content.rfind(line_ending, 0, range.location) if end == -1: return None else: end = end + len(line_ending) start = content.rfind(line_ending, 0, end - len(line_ending)) if start == -1: start = 0 else: start += len(line_ending) start = max(0, start) end = min(end, len(content)) line_range = tea.new_range(start, end - start) return tea.get_selection(context, line_range), line_range
def get_line_before_and_range(context, range = None): '''Get the full line immediately before the current (or supplied) range''' line_ending = tea.get_line_ending(context) if range is None: range = tea.get_range(context) content = context.string() end = content.rfind(line_ending, 0, range.location) if end == -1: return None else: end = end + len(line_ending) start = content.rfind(line_ending, 0, end - len(line_ending)) if start == -1: start = 0 else: start += len(line_ending) start = max(0, start) end = min(end, len(content)) line_range = tea.new_range(start, end - start) return tea.get_selection(context, line_range), line_range
def didEndSheet_returnCode_contextInfo_(self, sheet, code, info): def replacements(match): '''Utility function for replacing items''' return match.group(0).replace(self.search, self.replace) if code == 1: # Leave sheet open with "processing" spinner self.spinner.startAnimation_(self) spaces = int(self.numSpaces.stringValue()) if self.action == 'entab': target = re.compile(r'^(\t* +\t*)+', re.MULTILINE) self.search = ' ' * spaces self.replace = '\t' else: target = re.compile(r'^( *\t+ *)+', re.MULTILINE) self.search = '\t' self.replace = ' ' * spaces insertions = tea.new_recipe() ranges = tea.get_ranges(self.context) if len(ranges) == 1 and ranges[0].length == 0: # No selection, use the document ranges[0] = tea.new_range(0, self.context.string().length()) for range in ranges: text = tea.get_selection(self.context, range) # Non-Unix line endings will bork things; convert them text = tea.unix_line_endings(text) text = re.sub(target, replacements, text) if tea.get_line_ending(self.context) != '\n': text = tea.clean_line_endings(self.context, text) insertions.addReplacementString_forRange_(text, range) insertions.setUndoActionName_(self.action.title()) self.context.applyTextRecipe_(insertions) self.spinner.stopAnimation_(self) sheet.orderOut_(self)
def act(controller, bundle, options): context = tea.get_context(controller) # Get the options alpha_numeric = tea.get_option(options, 'alpha_numeric', True) extra_characters = tea.get_option(options, 'extra_characters', '_-') bidirectional = tea.get_option(options, 'bidirectional', True) snippet = tea.get_option(options, 'snippet', '<$SELECTED_TEXT>$0</$WORD>') mode = tea.get_option(options, 'mode', '') # Fetch the word range = context.selectedRange() word, new_range = tea.get_word_or_selection(context, range, alpha_numeric, extra_characters, bidirectional) if word == '': # No word, so nothing further to do return False # If we're using $WORD, make sure the word is just a word if snippet.find('$WORD') >= 0: fullword = word word = tea.parse_word(word) if word is None: word = '' else: fullword = word # Process that sucker! if mode == 'zen' and fullword.find(' ') < 0: # Explicitly load zen settings zen_settings = settings_loader.load_settings() zen_core.update_settings(zen_settings) # Set up the config variables zen_core.newline = tea.get_line_ending(context) zen_settings['variables']['indentation'] = tea.get_indentation_string(context) # This allows us to use smart incrementing tab stops in zen snippets point_ix = [0] def place_ins_point(text): if not point_ix[0]: point_ix[0] += 1 return '$0' else: return '' zen_core.insertion_point = place_ins_point # Determine doctype as best we can based on file extension doc_type = tea.get_zen_doctype(context) # Prepare the snippet snippet = zen_core.expand_abbreviation(fullword, doc_type, 'xhtml') elif mode == 'zen' and tea.is_selfclosing(word): # Self-closing, so construct the snippet from scratch snippet = '<' + fullword if fullword == word and not fullword in ['br', 'hr']: snippet += ' $0 />' else: snippet += ' />$0' # Indent the snippet snippet = tea.indent_snippet(context, snippet, new_range) snippet = tea.clean_line_endings(context, snippet) # Special replacement in case we're using $WORD snippet = snippet.replace('$WORD', word) snippet = snippet.replace('$SELECTED_TEXT', fullword) cursor_loc = snippet.find('$0') if cursor_loc != -1: select_range = tea.new_range(cursor_loc + new_range.location, 0) snippet = snippet.replace('$0', '') tea.insert_text_and_select(context, snippet, new_range, select_range) else: tea.insert_text(context, snippet, new_range)
def act(context, direction=None, remove_duplicates=False, undo_name=None): """ Required action method This sorts the selected lines (or document, if no selection) either ascending, descending, or randomly. """ # Check if there is a selection, otherwise take all lines ranges = tea.get_ranges(context) if len(ranges) == 1 and ranges[0].length == 0: ranges = [tea.new_range(0, context.string().length())] # Setup the text recipe recipe = tea.new_recipe() for range in ranges: text = tea.get_selection(context, range) # A blank range means we have only one range and it's empty # so we can't do any sorting if text == "": return False # Split the text into lines, not maintaining the linebreaks lines = text.splitlines(False) # Remove duplicates if set if remove_duplicates: if direction is None: seen = {} result = [] for x in lines: if x in seen: continue seen[x] = 1 result.append(x) lines = result else: lines = list(set(lines)) # Sort lines ascending or descending if direction == "asc" or direction == "desc": lines.sort() if direction == "desc": lines.reverse() # If direction is random, shuffle lines if direction == "random": random.shuffle(lines) # Join lines to one string linebreak = tea.get_line_ending(context) sortedText = unicode.join(linebreak, lines) # Add final linebreak if selected text has one if text.endswith(linebreak): sortedText += linebreak # Insert the text recipe.addReplacementString_forRange_(sortedText, range) if undo_name is not None: recipe.setUndoActionName_(undo_name) # Apply the recipe return context.applyTextRecipe_(recipe)
def act( context, default=None, alpha_numeric=True, extra_characters="", bidirectional=True, mode=None, close_string="", undo_name=None, **syntaxes ): """ Required action method Transforms the word under the cursor (or the word immediately previous to the cursor) into a snippet (or processes it using zen-coding) The snippet offers two placeholders: $SELECTED_TEXT: replaced with the word, or any selected text $WORD: if text is selected, replaced just with the first word """ if default is None: return False range = tea.get_single_range(context, True) if range == None: return False # Check for specific zone override snippet = tea.select_from_zones(context, range, default, **syntaxes) # Fetch the word word, new_range = tea.get_word_or_selection(context, range, alpha_numeric, extra_characters, bidirectional) if word == "": # No word, so nothing further to do return False # If we're using $WORD, make sure the word is just a word if snippet.find("$WORD") >= 0: fullword = word word = tea.parse_word(word) if word is None: word = "" else: fullword = word # We've got some extra work if the mode is HTML or zen # This is a really hacky solution, but I can't think of a concise way to # represent this functionality via XML if mode == "zen" and fullword.find(" ") < 0: # Set up the config variables zen_core.newline = tea.get_line_ending(context) # This allows us to use smart incrementing tab stops in zen snippets global point_ix point_ix = 0 def place_ins_point(text): globals()["point_ix"] += 1 return "$%s" % point_ix zen_core.insertion_point = place_ins_point zen_core.sub_insertion_point = place_ins_point zen_core.selfclosing_string = tea.get_tag_closestring(context) zen_settings["indentation"] = tea.get_indentation_string(context) # Detect the type of document we're working with zones = {"css, css *": "css", "xsl, xsl *": "xsl", "xml, xml *": "xml"} doc_type = tea.select_from_zones(context, range, "html", **zones) # Prepare the snippet snippet = zen_core.expand_abbr(fullword, doc_type) elif (mode == "zen" or mode == "html") and tea.is_selfclosing(word): # Self-closing, so construct the snippet from scratch snippet = "<" + fullword if fullword == word and not fullword in ["br", "hr"]: snippet += " $1" snippet += "$E_XHTML>$0" # Indent the snippet snippet = tea.indent_snippet(context, snippet, new_range) # Special replacement in case we're using $WORD snippet = snippet.replace("$WORD", word) # Construct the snippet snippet = tea.construct_snippet(fullword, snippet) return tea.insert_snippet_over_range(context, snippet, new_range, undo_name)
def act(context, default=None, alpha_numeric=True, extra_characters='', bidirectional=True, mode=None, close_string='', undo_name=None, **syntaxes): ''' Required action method Transforms the word under the cursor (or the word immediately previous to the cursor) into a snippet (or processes it using zen-coding) The snippet offers two placeholders: $EDITOR_SELECTION: replaced with the word, or any selected text $WORD: if text is selected, replaced just with the first word ''' if default is None: return False range = tea.get_single_range(context, True) if range == None: return False # Check for specific zone override snippet = tea.select_from_zones(context, range, default, **syntaxes) # Fetch the word word, new_range = tea.get_word_or_selection(context, range, alpha_numeric, extra_characters, bidirectional) if word == '': # No word, so nothing further to do return False # If we're using $WORD, make sure the word is just a word if snippet.find('$WORD') >= 0: fullword = word word = tea.parse_word(word) if word is None: word = '' else: fullword = word # We've got some extra work if the mode is HTML or zen # This is a really hacky solution, but I can't think of a concise way to # represent this functionality via XML # TODO remove it if mode == 'zen' and fullword.find(' ') < 0: # Explicitly load zen settings zen_settings = settings_loader.load_settings() zen_core.update_settings(zen_settings) # Set up the config variables zen_core.newline = tea.get_line_ending(context) zen_settings['variables']['indentation'] = tea.get_indentation_string(context) # This allows us to use smart incrementing tab stops in zen snippets point_ix = [0] def place_ins_point(text): point_ix[0] += 1 return '$%s' % point_ix[0] zen_core.insertion_point = place_ins_point # Detect the type of document we're working with zones = { 'css, css *': 'css', 'xsl, xsl *': 'xsl', 'xml, xml *': 'xml' } doc_type = tea.select_from_zones(context, range, 'html', **zones) # Setup the zen profile based on doc_type and XHTML status profile = {} if doc_type == 'html': close_string = tea.get_tag_closestring(context) if close_string == '/': profile['self_closing_tag'] = True elif close_string != ' /': profile['self_closing_tag'] = False elif doc_type == 'xml': profile = {'self_closing_tag': True, 'tag_nl': True} zen_core.setup_profile('tea_profile', profile) # Prepare the snippet snippet = zen_core.expand_abbreviation(fullword, doc_type, 'tea_profile') elif (mode == 'zen' or mode == 'html') and tea.is_selfclosing(word): # Self-closing, so construct the snippet from scratch snippet = '<' + fullword if fullword == word and not fullword in ['br', 'hr']: snippet += ' $1' snippet += '$E_XHTML>$0' # Special replacement in case we're using $WORD snippet = snippet.replace('$WORD', word) # Construct the snippet snippet = tea.construct_snippet(fullword, snippet) return tea.insert_snippet_over_range(context, snippet, new_range, undo_name)
def wrap(self, context, abbr, profile_name='xhtml'): # Set up the config variables zen_settings = settings_loader.load_settings() zen.update_settings(zen_settings) zen.newline = self.safe_str(tea.get_line_ending(context)) zen_settings['variables']['indentation'] = self.safe_str(tea.get_indentation_string(context)) # This allows us to use smart incrementing tab stops in zen snippets point_ix = [0] def place_ins_point(text): if not point_ix[0]: point_ix[0] += 1 return '$0' else: return '' zen.insertion_point = place_ins_point text, rng = tea.selection_and_range(context) if not text: # no selection, find matching tag content = context.string() start, end = html_matcher.match(content, rng.location) if start is None: # nothing to wrap return False def is_space(char): return char.isspace() or char in r'\n\r' # narrow down selection until first non-space character while start < end: if not is_space(content[start]): break start += 1 while end > start: end -= 1 if not is_space(content[end]): end += 1 break rng = tea.new_range(start, end - start) text = tea.get_selection(context, rng) # Fetch the doctype based on file extension doc_type = tea.get_zen_doctype(context) text = self.unindent(context, text) # Damn Python's encodings! Have to convert string to ascii before wrapping # and then back to utf-8 result = zen.wrap_with_abbreviation(self.safe_str(abbr), self.safe_str(text), doc_type, profile_name) result = unicode(result, 'utf-8') result = tea.indent_snippet(context, result, rng) result = tea.clean_line_endings(context, result) cursor_loc = result.find('$0') if cursor_loc != -1: select_range = tea.new_range(cursor_loc + rng.location, 0) result = result.replace('$0', '') tea.insert_text_and_select(context, result, rng, select_range) else: tea.insert_text(context, result, rng)