def act(controller, bundle, options):
    context = tea.get_context(controller)
    
    snippet = tea.get_option(options, 'snippet', '')
    maintain_selection = tea.get_option(options, 'maintain_selection', False)
    
    text, range = tea.selection_and_range(context)
    
    snippet = tea.indent_snippet(context, snippet, range)
    snippet = tea.clean_line_endings(context, snippet)
    
    # Set up target selection
    sel_loc = snippet.find('$SELECTED_TEXT')
    cursor_loc = snippet.find('$0')
    if maintain_selection:
        select_range = tea.new_range(sel_loc + range.location, range.length)
    elif cursor_loc != -1:
        select_range = tea.new_range(snippet.find('$0') + range.location, 0)
    else:
        select_range = None
    
    snippet = snippet.replace('$SELECTED_TEXT', text)
    snippet = snippet.replace('$0', '')
    if select_range is not None:
        tea.insert_text_and_select(context, snippet, range, select_range)
    else:
        tea.insert_text(context, snippet, range)
Exemple #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)
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 replace_content(self, value, start=None, end=None, undo_name="Replace content"):
        """
		Replace editor's content or it's part (from <code>start</code> to
		<code>end</code> index). If <code>value</code> contains
		<code>caret_placeholder</code>, the editor will put caret into
		this position. If you skip <code>start</code> and <code>end</code>
		arguments, the whole target's content will be replaced with
		<code>value</code>.

		If you pass <code>start</code> argument only,
		the <code>value</code> will be placed at <code>start</code> string
		index of current content.

		If you pass <code>start</code> and <code>end</code> arguments,
		the corresponding substring of current target's content will be
		replaced with <code>value</code>
		@param value: Content you want to paste
		@type value: str
		@param start: Start index of editor's content
		@type start: int
		@param end: End index of editor's content
		@type end: int
		"""
        if start is None:
            start = 0
        if end is None:
            end = len(self.get_content())
        rng = tea.new_range(start, end - start)
        value = self.add_placeholders(value)
        tea.insert_snippet_over_range(self._context, value, rng, undo_name)
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
Exemple #9
0
	def replace_content(self, value, start=None, end=None, no_indent=False):
		"""
		Replace editor's content or it's part (from <code>start</code> to
		<code>end</code> index). If <code>value</code> contains
		<code>caret_placeholder</code>, the editor will put caret into
		this position. If you skip <code>start</code> and <code>end</code>
		arguments, the whole target's content will be replaced with
		<code>value</code>.

		If you pass <code>start</code> argument only,
		the <code>value</code> will be placed at <code>start</code> string
		index of current content.

		If you pass <code>start</code> and <code>end</code> arguments,
		the corresponding substring of current target's content will be
		replaced with <code>value</code>
		@param value: Content you want to paste
		@type value: str
		@param start: Start index of editor's content
		@type start: int
		@param end: End index of editor's content
		@type end: int
		"""
		if start is None: start = 0
		if end is None: end = len(self.get_content())
		rng = tea.new_range(start, end - start)
		value = self.add_placeholders(value)
		
		if not no_indent:
			value = zencoding.utils.pad_string(value, get_line_padding(self.get_current_line()))
		
		sel_start, sel_end, value = self.preprocess_text(value)
		
		if sel_start is not None:
			select_range = tea.new_range(sel_start + rng.location, sel_end - sel_start)
			tea.insert_text_and_select(self._context, value, rng, select_range)
		else:
			tea.insert_text(self._context, value, rng)
	def create_selection(self, start, end=None):
		"""
		Creates selection from <code>start</code> to <code>end</code> character
		indexes. If <code>end</code> is ommited, this method should place caret
		and <code>start</code> index
		@type start: int
		@type end: int
		@example
		zen_editor.create_selection(10, 40)
		# move caret to 15th character
		zen_editor.create_selection(15)
		"""
		if end is None: end = start
		new_range = tea.new_range(start, end - start)
		tea.set_selected_range(self._context, new_range)
Exemple #11
0
	def create_selection(self, start, end=None):
		"""
		Creates selection from <code>start</code> to <code>end</code> character
		indexes. If <code>end</code> is ommited, this method should place caret
		and <code>start</code> index
		@type start: int
		@type end: int
		@example
		zen_editor.create_selection(10, 40)
		# move caret to 15th character
		zen_editor.create_selection(15)
		"""
		if end is None: end = start
		new_range = tea.new_range(start, end - start)
		tea.set_selected_range(self._context, new_range)
Exemple #12
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(controller, bundle, options):
    # Grab the context
    context = tea.get_context(controller)
    
    image = find_image(context)
    if image:
        size = get_image_size(context, image['tag'])
        if size:
            new_tag = replace_or_append(image['tag'], 'width', size['width'])
            new_tag = replace_or_append(new_tag, 'height', size['height'])
            
            rng = tea.get_range(context)
            tea.insert_text(context, new_tag, tea.new_range(image['start'], image['end'] - image['start']))
            tea.set_selected_range(context, rng)
            return True
    
    return False
def act(controller, bundle, options):
    # Grab the context
    context = tea.get_context(controller)
    
    # Setup the options
    fallback = tea.get_option(options, 'fallback', '')
    snippet = tea.get_option(options, 'snippet', '$URL')
    
    # Get the clipboard contents, parse for a URL
    process = subprocess.Popen(['pbpaste'], stdout=subprocess.PIPE)
    clipboard, error = process.communicate(None)
    # Construct the default link
    url = format_hyperlink(clipboard, fallback)
    
    # Grab the selected text and range
    text, range = tea.selection_and_range(context)
    
    # Parse the snippet for $SELECTED_TEXT placeholder
    sel_loc = snippet.find('$SELECTED_TEXT')
    if sel_loc != -1:
        replace_text = True
        prefix = snippet[0:sel_loc]
        suffix = snippet[sel_loc+14:]
    else:
        replace_text = False
        prefix = snippet
        suffix = ''
    
    prefix = prefix.replace('$URL', url)
    suffix = suffix.replace('$URL', url)
    
    if replace_text:
        replacement = prefix + text + suffix
    else:
        replacement = prefix
    
    url_loc = replacement.find(url)
    if url_loc == -1:
        url_loc = len(replacement)
        url_len = 0
    else:
        url_len = len(url)
    newrange = tea.new_range(url_loc + range.location, url_len)
    
    tea.insert_text_and_select(context, replacement, range, newrange)
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
Exemple #16
0
def act(controller, bundle, options):
    context = tea.get_context(controller)
    direction = tea.get_option(options, 'direction', 'out')
    
    # Since input is always a selection of some kind, check if we have one
    
    rng = tea.get_range(context)
    cursor = rng.location + rng.length
    range_start, range_end = rng.location, rng.location + rng.length
    content = context.string()
    
    old_open_tag = html_matcher.last_match['opening_tag']
    old_close_tag = html_matcher.last_match['closing_tag']
    
    if direction.lower() == 'in' and old_open_tag and range_start != range_end:
        # user has previously selected tag and wants to move inward
        if not old_close_tag:
            # unary tag was selected, can't move inward
            return False
        elif old_open_tag.start == range_start:
            if content[old_open_tag.end] == '<':
                # test if the first inward tag matches the entire parent tag's content
                _start, _end = html_matcher.find(content, old_open_tag.end + 1)
                if _start == old_open_tag.end and _end == old_close_tag.start:
                    start, end = html_matcher.match(content, old_open_tag.end + 1)
                else:
                    start, end = old_open_tag.end, old_close_tag.start
            else:
                start, end = old_open_tag.end, old_close_tag.start
        else:
            new_cursor = content.find('<', old_open_tag.end, old_close_tag.start)
            search_pos = new_cursor != -1 and new_cursor + 1 or old_open_tag.end
            start, end = html_matcher.match(content, search_pos)
            
    else:
        start, end = html_matcher.match(content, cursor)
    
    if start is not None:
        new_range = tea.new_range(start, end - start)
        tea.set_selected_range(context, new_range)
        return True
    else:
        return False
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)
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
Exemple #21
0
    def replace_content(self,
                        value,
                        start=None,
                        end=None,
                        no_indent=False,
                        undo_name='Replace content'):
        """
		Replace editor's content or it's part (from <code>start</code> to
		<code>end</code> index). If <code>value</code> contains
		<code>caret_placeholder</code>, the editor will put caret into
		this position. If you skip <code>start</code> and <code>end</code>
		arguments, the whole target's content will be replaced with
		<code>value</code>.

		If you pass <code>start</code> argument only,
		the <code>value</code> will be placed at <code>start</code> string
		index of current content.

		If you pass <code>start</code> and <code>end</code> arguments,
		the corresponding substring of current target's content will be
		replaced with <code>value</code>
		@param value: Content you want to paste
		@type value: str
		@param start: Start index of editor's content
		@type start: int
		@param end: End index of editor's content
		@type end: int
		"""
        if start is None: start = 0
        if end is None: end = len(self.get_content())

        self.set_caret_pos(start)
        rng = tea.new_range(start, end - start)
        value = self.add_placeholders(value)

        tea.insert_snippet_over_range(self._context, value, rng, undo_name,
                                      not no_indent)
 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 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
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, 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)
Exemple #28
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)