def act(context, default=None, fallback_url="", undo_name=None, **syntaxes):
    """
    Required action method
    
    A flexible link generator which uses the clipboard text (if there's
    a recognizable link there) and formats the snippet based on the
    active syntax of the context
    """
    if default is None:
        return False
    # Get the text and range
    text, range = tea.get_single_selection(context, True)
    if text == None:
        return False

    # 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_url)
    # Get the snippet based on the root zone
    snippet = tea.select_from_zones(context, range, default, **syntaxes)
    snippet = tea.construct_snippet(text, snippet)
    snippet = snippet.replace("$URL", tea.sanitize_for_snippet(url))
    return tea.insert_snippet(context, snippet)
def act(context, default=None, undo_name=None, **syntaxes):
    '''
    Required action method
    
    Inserts an arbitrary text snippet after the cursor with provisions for
    syntax-specific alternatives
    
    Accepts $EDITOR_SELECTION placeholder
    
    This method requires at least the snippet default to be defined in the XML
    '''
    if default is None:
        return False
    # Get the cursor position
    text, range = tea.get_single_selection(context)
    # Check for root-zone specific override
    snippet = tea.select_from_zones(context, range, default, **syntaxes)
    # Construct the snippet
    snippet = tea.construct_snippet(text, snippet)
    # Insert that snippet!
    return tea.insert_snippet(context, snippet)
def act(context, first_snippet='', following_snippet='',
        final_append='', undo_name=None):
    '''
    Required action method
    
    Wraps the selected text in a snippet
    
    Support for discontiguous selections will be implemented when recipes
    can support snippets; until then only first_snippet will be used
    '''
    # TODO: change to a loop once snippets in recipes are supported
    # This function will handle the logic of when to use open vs. multi
    text, range = tea.get_single_selection(context)
    if text == None:
        text = ''
    # Only indent the snippet if there aren't multiple lines in the selected text
    if len(text.splitlines()) > 1:
        indent = False
    else:
        indent = True
    snippet = tea.construct_snippet(text, first_snippet + final_append)
    return tea.insert_snippet(context, snippet, indent)
def act(context, default=None, undo_name=None, **syntaxes):
    '''
    Required action method
    
    default parameter is not a snippet, but should contain the
    $EDITOR_SELECTION 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('$EDITOR_SELECTION',
                                                 '${2:$EDITOR_SELECTION}') + '}$0'
        else:
            # Not a selection, just wrap the cursor
            text = ''
            snippet = insertion.replace('$EDITOR_SELECTION', '$1') + '$0'
        snippet = tea.construct_snippet(text, snippet)
        return tea.insert_snippet(context, snippet)
    # 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)
        # DEPRECATED: $SELECTED_TEXT will go away in future; don't use it
        insertion = insertion.replace('$SELECTED_TEXT', text)
        insertion = insertion.replace('$EDITOR_SELECTION', text)
        insertions.addReplacementString_forRange_(insertion, range)
    if undo_name is not None:
        insertions.setUndoActionName_(undo_name)
    return context.applyTextRecipe_(insertions)
 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