def act(context, default=None, undo_name=None, **syntaxes):
    '''
    Required action method
    
    default parameter is not a snippet, but should contain the
    $SELECTED_TEXT placeholder
    '''
    # Get the selected ranges
    ranges = tea.get_ranges(context)
    if len(ranges) is 1:
        # Since we've only got one selection we can use a snippet
        range = ranges[0]
        insertion = tea.select_from_zones(context, range, default, **syntaxes)
        # Make sure the range is actually a selection
        if range.length > 0:
            text = tea.get_selection(context, range)
            snippet = '${1:' + insertion.replace('$SELECTED_TEXT',
                                                 '${2:$SELECTED_TEXT}') + '}$0'
        else:
            # Not a selection, just wrap the cursor
            text = ''
            snippet = insertion.replace('$SELECTED_TEXT', '$1') + '$0'
        snippet = tea.construct_snippet(text, snippet)
        return tea.insert_snippet_over_range(context, snippet, range,
                                                 undo_name)
    # Since we're here, it must not have been a single selection
    insertions = tea.new_recipe()
    for range in ranges:
        insertion = tea.select_from_zones(context, range, default, **syntaxes)
        text = tea.get_selection(context, range)
        text = insertion.replace('$SELECTED_TEXT', text)
        insertions.addReplacementString_forRange_(text, range)
    if undo_name is not None:
        insertions.setUndoActionName_(undo_name)
    return context.applyTextRecipe_(insertions)
Esempio n. 2
0
def act(controller, bundle, options):
    '''
    Required action method
    
    input dictates what should be trimmed:
    - None (default): falls back to alternate
    - selection: ignores lines if they exist, just trims selection
    - selected_lines: each line in the selection
    
    alternate dictates what to fall back on
    - None (default): will do nothing if input is blank
    - line: will trim the line the caret is on
    - all_lines: all lines in the document
    
    trim dictates what part of the text should be trimmed:
    - both (default)
    - start
    - end
    
    If respect_indent is True, indent characters (as defined in preferences)
    at the beginning of the line will be left untouched.
    '''
    context = tea.get_context(controller)
    
    input = tea.get_option(options, 'input')
    alternate = tea.get_option(options, 'alternate')
    trim = tea.get_option(options, 'trim', 'both')
    respect_indent = tea.get_option(options, 'respect_indent', False)
    discard_empty = tea.get_option(options, 'discard_empty', False)
    
    # Since input is always a selection of some kind, check if we have one
    range = tea.get_range(context)
    
    if (range.length == 0) or input is None:
        if alternate.lower() == 'line':
            text, range = tea.get_line(context)
            text = tea.trim(context, text, False, trim, respect_indent, True, discard_empty)
        elif alternate.lower() == 'all_lines':
            range = tea.new_range(0, context.string().length())
            text = tea.get_selection(context, range)
            text = tea.trim(context, text, True, trim, respect_indent, True, discard_empty)
    else:
        if input.lower() == 'selected_lines':
            parse_lines = True
        else:
            parse_lines = False
        text = tea.get_selection(context, range)
        text = tea.trim(context, text, parse_lines, trim, respect_indent, True, discard_empty)
    tea.insert_text(context, text, range)
    new_range = tea.new_range(range.location, len(text))
    tea.set_selected_range(context, new_range)
Esempio n. 3
0
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
Esempio n. 4
0
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
Esempio n. 6
0
def act(context, input=None, alternate=None, trim='both', respect_indent=False,
        undo_name=None):
    '''
    Required action method
    
    input dictates what should be trimmed:
    - None (default): falls back to alternate
    - selection: ignores lines if they exist, just trims selection
    - selected_lines: each line in the selection
    
    alternate dictates what to fall back on
    - None (default): will do nothing if input is blank
    - line: will trim the line the caret is on
    - all_lines: all lines in the document
    
    trim dictates what part of the text should be trimmed:
    - both (default)
    - start
    - end
    
    If respect_indent is True, indent characters (as defined in preferences)
    at the beginning of the line will be left untouched.
    '''
    # Since input is always a selection of some kind, check if we have one
    ranges = tea.get_ranges(context)
    insertions = tea.new_recipe()
    if (len(ranges) == 1 and ranges[0].length == 0) or input is None:
        if alternate == 'line':
            text, range = tea.get_line(context, ranges[0])
            text = tea.trim(context, text, False, trim, respect_indent)
        elif alternate == 'all_lines':
            range = tea.new_range(0, context.string().length())
            text = tea.get_selection(context, range)
            text = tea.trim(context, text, True, trim, respect_indent)
        insertions.addReplacementString_forRange_(text, range)
    else:
        if input == 'selected_lines':
            parse_lines = True
        else:
            parse_lines = False
        for range in ranges:
            text = tea.get_selection(context, range)
            text = tea.trim(context, text, parse_lines, trim, respect_indent)
            insertions.addReplacementString_forRange_(text, range)
    if undo_name != None:
        insertions.setUndoActionName_(undo_name)
    return context.applyTextRecipe_(insertions)
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 act(context, default=None, prefix_selection=False,
        suffix_selection=False, undo_name=None, **syntaxes):
    '''
    Required action method
    
    Inserts arbitrary text over all selections; specific text can be
    syntax-specific (same procedure as Wrap Selection In Link)
    
    If you set prefix_selection to true, the inserted text will precede
    any selected text; if suffix_selection is true it will follow any
    selected text; if both are true it will wrap the text
    '''
    # Grab the ranges
    ranges = tea.get_ranges(context)
    # Set up our text recipe
    insertions = tea.new_recipe()
    for range in ranges:
        if prefix_selection or suffix_selection:
            # Get the selected text
            text = tea.get_selection(context, range)
            if prefix_selection:
                text = '$INSERT' + text
            if suffix_selection:
                text += '$INSERT'
            # If empty selection, only insert one
            if text == '$INSERT$INSERT':
                text = '$INSERT'
        else:
            text = '$INSERT'
        # Check for zone-specific insertion
        insert = tea.select_from_zones(context, range, default, **syntaxes)
        text = text.replace('$INSERT', insert)
        text = text.replace('$TOUCH', '')
        # Insert the text, or replace the selected text
        if range.length is 0:
            insertions.addInsertedString_forIndex_(text, range.location)
        else:
            insertions.addReplacementString_forRange_(text, range)
    # Set undo name and run the recipe
    if undo_name != None:
        insertions.setUndoActionName_(undo_name)
    reset_cursor = False
    if len(ranges) is 1 and ranges[0].length is 0:
        # Thanks to addInsertedString's wonkiness, we have to reset the cursor
        reset_cursor = True
    # Espresso beeps if I return True or False; hence this weirdness
    return_val = context.applyTextRecipe_(insertions)
    if reset_cursor:
        new_range = tea.new_range(ranges[0].location + len(text), 0)
        tea.set_selected_range(context, new_range)
    return return_val
Esempio n. 9
0
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
Esempio n. 10
0
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 act(context, type="named", wrap="$HEX", undo_name=None):
    """
    Required action method
    
    Type can be:
    named: named HTML entities, with high value numeric entities if no name
    numeric: numeric HTML entities
    hex: hexadecimal encoding; use the 'wrap' option for specific output
    
    Wrap will be used if type is 'hex' and will replace $HEX with the actual
    hex value.  For example '\u$HEX' will be result in something like '\u0022'
    """
    ranges = tea.get_ranges(context)
    if len(ranges) == 1 and ranges[0].length == 0:
        # We've got one empty range; make sure it's not at the
        # beginning of the document
        if ranges[0].location > 0:
            # Set the new target range to the character before the cursor
            ranges[0] = tea.new_range(ranges[0].location - 1, 1)
        else:
            return False
    # Since we're here we've got something to work with
    insertions = tea.new_recipe()
    for range in ranges:
        text = tea.get_selection(context, range)
        if type == "named":
            # Convert any characters we can into named HTML entities
            text = tea.named_entities(text)
        elif type == "numeric":
            # Convert any characters we can into numeric HTML entities
            text = tea.numeric_entities(text, type)
        elif type == "hex":
            # Convert characters to hex via numeric entities
            text = tea.numeric_entities(text)
            text = tea.entities_to_hex(text, wrap)
        insertions.addReplacementString_forRange_(text, range)
    if undo_name is not None:
        insertions.setUndoActionName_(undo_name)
    return context.applyTextRecipe_(insertions)
Esempio n. 12
0
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
Esempio n. 13
0
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)
Esempio n. 15
0
 def performActionWithContext_error_(self, context):
     '''
     Gathers the necessary info, populates the environment, and runs
     the script
     '''
     def execute(file, input):
         '''Utility function for running the script'''
         script = subprocess.Popen(
             [file],
             stdin=subprocess.PIPE,
             stdout=subprocess.PIPE,
             stderr=subprocess.PIPE
         )
         return script.communicate(str(input))
     
     if self.script is None:
         tea.log('No script found')
         return False
     # Environment variables that won't change with repetition
     os.putenv('E_SUGARPATH', self.bundle_path)
     filepath = context.documentContext().fileURL()
     if filepath is not None:
         os.putenv('E_FILENAME', filepath.path().lastPathComponent())
         if filepath.isFileURL():
             os.putenv(
                 'E_DIRECTORY',
                 filepath.path().stringByDeletingLastPathComponent()
             )
             os.putenv('E_FILEPATH', filepath.path())
     root = tea.get_root_zone(context)
     if root is False:
         root = ''
     os.putenv('E_ROOT_ZONE', root)
     # Set up the preferences
     prefs = tea.get_prefs(context)
     os.putenv('E_SOFT_TABS', str(prefs.insertsSpacesForTab()))
     os.putenv('E_TAB_SIZE', str(prefs.numberOfSpacesForTab()))
     os.putenv('E_LINE_ENDING', prefs.lineEndingString())
     os.putenv('E_XHTML', tea.get_tag_closestring(context))
     
     # Set up the user-defined shell variables
     defaults = NSUserDefaults.standardUserDefaults()
     for item in defaults.arrayForKey_('TEAShellVariables'):
         if 'variable' in item and item['variable'] != '':
             os.putenv(item['variable'], item['value'])
     
     # Initialize our common variables
     recipe = tea.new_recipe()
     ranges = tea.get_ranges(context)
     # Check the user script folder for overrides
     file = os.path.join(os.path.expanduser(
         '~/Library/Application Support/Espresso/TEA/Scripts/'
     ), self.script)
     if not os.path.exists(file):
         file = os.path.join(self.bundle_path, 'TEA', self.script)
     if not os.path.exists(file):
         # File doesn't exist in the bundle, either, so something is screwy
         return tea.say(
             context, 'Error: could not find script',
             'TEA could not find the script associated with this action. '\
             'Please contact the Sugar developer, or make sure it is '\
             'installed here:\n\n'\
             '~/Library/Application Support/Espresso/TEA/Scripts'
         )
     
     # There's always at least one range; this thus supports multiple
     # discontinuous selections
     for range in ranges:
         # These environment variables may change with repetition, so reset
         os.putenv('E_SELECTED_TEXT',
             str(context.string().substringWithRange_(range))
         )
         word, wordrange = tea.get_word(context, range)
         os.putenv('E_CURRENT_WORD', str(word))
         os.putenv('E_CURRENT_LINE',
             str(context.string().substringWithRange_(
                 context.lineStorage().lineRangeForRange_(range)
             ))
         )
         os.putenv(
             'E_LINENUMBER',
             str(context.lineStorage().lineNumberForIndex_(range.location))
         )
         os.putenv('E_LINEINDEX', str(
             range.location - \
             context.lineStorage().lineStartIndexForIndex_lineNumber_(
                 range.location, None
             )
         ))
         active = tea.get_active_zone(context, range)
         if active is False:
             active = ''
         os.putenv('E_ACTIVE_ZONE', str(active))
         
         # Setup STDIN and track the source
         source = 'input'
         if self.input == 'selection':
             input = tea.get_selection(context, range)
             if input == '':
                 if self.alt == 'document':
                     input = context.string()
                 elif self.alt == 'line':
                     input, range = tea.get_line(context, range)
                     # For this usage, we don't want to pass the final linebreak
                     input = input[:-1]
                     range = tea.new_range(range.location, range.length-1)
                 elif self.alt == 'word':
                     input, range = tea.get_word(context, range)
                 elif self.alt == 'character':
                     input, range = tea.get_character(context, range)
                 source = 'alt'
         elif self.input == 'document':
             input = context.string()
         else:
             input = ''
         # Run the script
         try:
             output, error = execute(file, input)
         except:
             # Most likely cause of failure is lack of executable status
             try:
                 os.chmod(file, 0755)
                 output, error = execute(file, input)
             except:
                 # Failed to execute completely, so exit with error
                 return tea.say(
                     context, 'Error: cannot execute script',
                     'Error: could not execute the script. Please contact '\
                     'the Sugar author.'
                 )
         # Log errors
         if error:
             tea.log(str(error))
         # Process the output
         output = output.decode('utf-8')
         if self.output == 'document' or \
            (source == 'alt' and self.alt == 'document'):
             docrange = tea.new_range(0, context.string().length())
             recipe.addReplacementString_forRange_(output, docrange)
             break
         elif self.output == 'text':
             recipe.addReplacementString_forRange_(output, range)
         elif self.output == 'snippet':
             recipe.addDeletedRange_(range)
             break
     
     # If no output, we don't need to go any further
     if self.output is None:
         return True
     
     # Made it here, so apply the recipe and return
     if self.undo is not None:
         recipe.setUndoActionName_(self.undo)
     recipe.prepare()
     if recipe.numberOfChanges() > 0:
         response = context.applyTextRecipe_(recipe)
     else:
         response = True
     if self.output == 'snippet':
         response = tea.insert_snippet(context, output)
     return response
Esempio n. 16
0
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)
Esempio n. 17
0
def act(context, target=None, source=None, trim=False, discard_indent=False,
        search_string=None, regex=False):
    '''
    Required action method
    
    target dictates what we're looking for:
    - text
    - if unspecified, simply selects the source
    
    source dictates how to gather the string to search for:
    - word (word under the caret)
    - line (line under the caret)
    - if unspecified, defaults to selection
    
    Setting trim=True will cause the source to be trimmed
    
    Setting discard_indent=True will cause leading whitespace
    to be trimmed (unnecessary unless trim=True)
    
    search_string will set the string to search for if target is text or zone
    - $SELECTED_TEXT will be replaced with the source text
    
    Setting regex=True will cause search_string to be evaluated as regex
    '''
    range = tea.get_ranges(context)[0]
    if source == 'word':
        text, range = tea.get_word(context, range)
    elif source == 'line':
        text, range = tea.get_line(context, range)
    elif range.length > 0:
        text = tea.get_selection(context, range)
    
    # Make sure that we've got some text, even if it's an empty string
    if text is None:
        text = ''
    
    # Trim the source
    if trim:
        if discard_indent:
            trimmed = tea.trim(context, text, False)
        else:
            trimmed = tea.trim(context, text, False, 'end')
        
        start = text.find(trimmed)
        if start != -1:
            start = range.location + start
        length = len(trimmed)
        if source == 'line':
            # We don't want the linebreak if we're trimming
            length = length - 1
        range = tea.new_range(start, length)
        text = trimmed
    
    if target is not None and text:
        if search_string is not None:
            search = search_string.replace('$SELECTED_TEXT', text)
        else:
            search = text
        # Find the start and end points of the substring
        start = end = None
        if regex:
            match = re.search(r'(' + search + r')', context.string())
            if match:
                # Get the start and end points
                start, end = match.span(1)
        else:
            start = context.string().find(search)
            if start != -1:
                end = start + len(search)
            else:
                start = None
        # Construct the new target range
        if start is not None and end is not None:
            range = tea.new_range(start, end - start)
    
    # Set the new range
    tea.set_selected_range(context, range)
    return True
 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)
Esempio n. 19
0
def act(controller, bundle, options):
    '''
    Required action method
    
    target dictates what we're looking for:
    - text
    - if unspecified, simply selects the source
    
    source dictates how to gather the string to search for:
    - word (word under the caret)
    - line (line under the caret)
    - if unspecified, defaults to selection
    
    Setting trim=True will cause the source to be trimmed
    
    Setting discard_indent=True will cause leading whitespace
    to be trimmed (unnecessary unless trim=True)
    
    search_string will set the string to search for if target is text or zone
    - $SELECTED_TEXT will be replaced with the source text
    
    Setting regex=True will cause search_string to be evaluated as regex
    '''
    context = tea.get_context(controller)
    
    target = tea.get_option(options, 'target')
    source = tea.get_option(options, 'source')
    trim = tea.get_option(options, 'trim', False)
    discard_indent = tea.get_option(options, 'discard_indent', False)
    search_string = tea.get_option(options, 'search_string')
    regex = tea.get_option(options, 'regex', False)
    
    range = tea.get_range(context)
    if source == 'word':
        text, range = tea.get_word(context, range)
    elif source == 'line':
        text, range = tea.get_line(context)
    elif range.length > 0:
        text = tea.get_selection(context, range)
    
    # Trim the source
    if trim:
        if discard_indent:
            trimmed = tea.trim(context, text, False, preserve_linebreaks=False)
        else:
            trimmed = tea.trim(context, text, False, 'end',
                               preserve_linebreaks=False)
        
        start = text.find(trimmed)
        if start != -1:
            start = range.location + start
        length = len(trimmed)
        if source == 'line' and trimmed[-1:] in ['\r\n', '\r', '\n']:
            # We don't want the linebreak if we're trimming
            length = length - 1
        range = tea.new_range(start, length)
        text = trimmed
    
    if target is not None and text:
        if search_string is not None:
            search = search_string.replace('$SELECTED_TEXT', text)
        else:
            search = text
        # Find the start and end points of the substring
        start = end = None
        if regex:
            match = re.search(r'(' + search + r')', context.string())
            if match:
                # Get the start and end points
                start, end = match.span(1)
        else:
            start = context.string().find(search)
            if start != -1:
                end = start + len(search)
            else:
                start = None
        # Construct the new target range
        if start is not None and end is not None:
            range = tea.new_range(start, end - start)
    
    # Set the new range
    tea.set_selected_range(context, range)