コード例 #1
0
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
コード例 #2
0
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)
コード例 #3
0
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
コード例 #4
0
ファイル: trim.py プロジェクト: lukacosicnz/tea-for-espresso
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)
コード例 #5
0
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)
コード例 #6
0
 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)
コード例 #7
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)
コード例 #8
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