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