def select_scope_name(editor=wingapi.kArgEditor): ''' Select the name of the function or class that the cursor is currently on. Suggested key combination: `Alt-Colon` ''' assert isinstance(editor, wingapi.CAPIEditor) document = editor.GetDocument() document_text = shared.get_text(document) with shared.SelectionRestorer(editor): editor.ExecuteCommand('select-scope') scope_start, scope_end = editor.GetSelection() scope_contents = document_text[scope_start : scope_end] match = _scope_name_regex.match(scope_contents) if not match: return stuff_before_scope_name, scope_name = match.groups() scope_name_start = scope_start + len(stuff_before_scope_name) scope_name_end = scope_name_start + len(scope_name) assert document_text[scope_name_position : scope_name_position+len(scope_name)] == scope_name with shared.UndoableAction(document): editor.SetSelection(scope_name_start, scope_name_end)
def _get_rhs_positions(document): matches = _get_matches(document) document_text = shared.get_text(document) stripper = lambda (start, end): \ shared.strip_segment_from_whitespace_and_newlines(document_text, start, end) return map(stripper, (match.span('rhs') for match in matches))
def previous_brace_match(editor=wingapi.kArgEditor): ''' Select the previous pair of braces. Similar to Wing's built-in `brace-match`, except it goes backwards instead of going forwards. Goes to the nearest pair of braces, whether it's (), [], or {} that's before the current caret position, and selects those braces including all their content. Known limitations: Misses some pairs of braces. Doesn't know to ignore braces found in strings. Suggested key combination: `Ctrl-Bracketleft` ''' assert isinstance(editor, wingapi.CAPIEditor) document = editor.GetDocument() document_text = shared.get_text(document) _, caret_position = editor.GetSelection() closing_brace_position = max(( document_text.rfind(')', 0, caret_position - 1), document_text.rfind(']', 0, caret_position - 1), document_text.rfind('}', 0, caret_position - 1) )) if closing_brace_position == -1: return new_position = closing_brace_position editor.SetSelection(new_position, new_position) editor.ExecuteCommand('brace-match')
def previous_brace_match(editor=wingapi.kArgEditor): ''' Select the previous pair of braces. Similar to Wing's built-in `brace-match`, except it goes backwards instead of going forwards. Goes to the nearest pair of braces, whether it's (), [], or {} that's before the current caret position, and selects those braces including all their content. Known limitations: Misses some pairs of braces. Doesn't know to ignore braces found in strings. Suggested key combination: `Ctrl-Bracketleft` ''' assert isinstance(editor, wingapi.CAPIEditor) document = editor.GetDocument() document_text = shared.get_text(document) _, caret_position = editor.GetSelection() closing_brace_position = max( (document_text.rfind(')', 0, caret_position - 1), document_text.rfind(']', 0, caret_position - 1), document_text.rfind('}', 0, caret_position - 1))) if closing_brace_position == -1: return new_position = closing_brace_position editor.SetSelection(new_position, new_position) editor.ExecuteCommand('brace-match')
def django_toggle_between_view_and_template(): ''' Toggle between a view file in Django and the corresponding template file. If you're currently viewing a template file, this'll open the corresponding view file. If you're currently viewing a view file, this'll open the corresponding template file. This assumes that the view file has the word `view` in it and has a definition for the class attribute `template_name`. Suggested key combination: `Insert Quoteleft` (i.e. backtick) ''' app = wingapi.gApplication editor = app.GetActiveEditor() document = editor.GetDocument() project = app.GetProject() all_file_paths = project.GetAllFiles() document_text = shared.get_text(document) file_path = document.GetFilename() folder, file_name = os.path.split(file_path) if file_name.endswith('.py'): match = template_name_pattern.search(document_text) if match: template_partial_file_path = match.group(1) matching_file_paths = [ file_path for file_path in all_file_paths if template_partial_file_path in file_path.replace('\\', '/') ] if matching_file_paths: matching_file_path = matching_file_paths[0] app.OpenEditor(matching_file_path, raise_window=True) elif template_file_pattern.match(file_name): short_file_path = shorten_template_file_path_pattern.match( file_path.replace('\\', '/')).group(1) specifies_our_template_pattern = re.compile( r'''template_name *= * ['"]%s['"]''' % re.escape(short_file_path) ) all_view_file_paths = [ file_path for file_path in all_file_paths if view_file_path_pattern.search(file_path) ] matching_file_paths_iterator = ( file_path for file_path in all_view_file_paths if specifies_our_template_pattern.search( shared.get_file_content(file_path)) ) try: matching_file_path = next(matching_file_paths_iterator) except StopIteration: return app.OpenEditor(matching_file_path, raise_window=True)
def _get_span_of_opening_parenthesis(document, position): assert isinstance(document, wingapi.CAPIDocument) document_text = shared.get_text(document) if not document_text[position] == '(': raise Exception for i in range(1, len(document_text) - position): portion = document_text[position:position+i+1] if portion.count('(') == portion.count(')'): if not portion[-1] == ')': raise Exception return (position, position + i + 1) else: return (position, position)
def django_toggle_between_view_and_template(): ''' Toggle between a view file in Django and the corresponding template file. If you're currently viewing a template file, this'll open the corresponding view file. If you're currently viewing a view file, this'll open the corresponding template file. This assumes that the view file has the word `view` in it and has a definition for the class attribute `template_name`. Suggested key combination: `Insert Quoteleft` (i.e. backtick) ''' app = wingapi.gApplication editor = app.GetActiveEditor() document = editor.GetDocument() project = app.GetProject() all_file_paths = project.GetAllFiles() document_text = shared.get_text(document) file_path = document.GetFilename() folder, file_name = os.path.split(file_path) if file_name.endswith('.py'): match = template_name_pattern.search(document_text) if match: template_partial_file_path = match.group(1) matching_file_paths = [ file_path for file_path in all_file_paths if template_partial_file_path in file_path.replace('\\', '/') ] if matching_file_paths: matching_file_path = matching_file_paths[0] app.OpenEditor(matching_file_path, raise_window=True) elif template_file_pattern.match(file_name): short_file_path = shorten_template_file_path_pattern.match( file_path.replace('\\', '/')).group(1) specifies_our_template_pattern = re.compile( r'''template_name *= * ['"]%s['"]''' % re.escape(short_file_path)) all_view_file_paths = [ file_path for file_path in all_file_paths if view_file_path_pattern.search(file_path) ] matching_file_paths_iterator = ( file_path for file_path in all_view_file_paths if specifies_our_template_pattern.search( shared.get_file_content(file_path))) try: matching_file_path = next(matching_file_paths_iterator) except StopIteration: return app.OpenEditor(matching_file_path, raise_window=True)
def _get_matches_for_arguments(document, truncate=None): assert isinstance(document, wingapi.CAPIDocument) document_text = shared.get_text(document) if truncate: truncate_position, truncate_radius = truncate if len(document_text) > truncate_radius * 2: old_document_text = document_text document_text = ( ' ' * abs(truncate_position - truncate_radius) + document_text[truncate_position-truncate_radius :truncate_position+truncate_radius] ) return tuple(match for match in invocation_pattern_for_arguments.finditer(document_text) if not keyword.iskeyword(match.groups()[0]))
def guess_class_name(): ''' Guess the class name based on file name, and replace class name. Imagine you had a file `foo_manager.py` with a class `FooManager` defined in it, and you duplicated it, calling the new file `bar_grokker.py`. If you run `guess-class-name`, it'll replace all instances of `FooManager` in your new file to `BarGrokker`. (It finds the original class name by taking the first class defined in the file. If the file defined multiple classes, you might get the wrong results.) Suggested key combination: `Insert Ctrl-C` ''' app = wingapi.gApplication editor = app.GetActiveEditor() document = editor.GetDocument() file_name_without_extension = \ os.path.split(document.GetFilename())[-1].split('.')[0] guessed_class_name = \ shared.lower_case_to_camel_case(file_name_without_extension) document_text = shared.get_text(document) matches = tuple(re.finditer(existing_class_name_pattern, document_text)) if not matches: return match = matches[-1] existing_class_name = match.group(1) n_occurrences = document_text.count(existing_class_name ) with shared.SelectionRestorer(editor): with shared.UndoableAction(document): document.SetText( document_text.replace(existing_class_name, guessed_class_name) ) app.SetStatusMessage('Replaced %s occurrences' % n_occurrences)
def _get_argument_positions(document, limit_to_keywords=False, truncate=None): argument_batch_positions = _get_argument_batch_positions(document, truncate=truncate) document_text = shared.get_text(document) raw_argument_positions = tuple(itertools.chain( *(_argpos( document_text[argument_batch_position[0]: argument_batch_position[1]], document_offset=argument_batch_position[0], limit_to_keywords=limit_to_keywords ) for argument_batch_position in argument_batch_positions) )) argument_positions = map( lambda (start, end): shared.strip_segment_from_whitespace_and_newlines(document_text, start, end), raw_argument_positions ) return argument_positions
def guess_class_name(): ''' Guess the class name based on file name, and replace class name. Imagine you had a file `foo_manager.py` with a class `FooManager` defined in it, and you duplicated it, calling the new file `bar_grokker.py`. If you run `guess-class-name`, it'll replace all instances of `FooManager` in your new file to `BarGrokker`. (It finds the original class name by taking the first class defined in the file. If the file defined multiple classes, you might get the wrong results.) Suggested key combination: `Insert Ctrl-C` ''' app = wingapi.gApplication editor = app.GetActiveEditor() document = editor.GetDocument() file_name_without_extension = \ os.path.split(document.GetFilename())[-1].split('.')[0] guessed_class_name = \ shared.lower_case_to_camel_case(file_name_without_extension) document_text = shared.get_text(document) matches = tuple(re.finditer(existing_class_name_pattern, document_text)) if not matches: return match = matches[-1] existing_class_name = match.group(1) n_occurrences = document_text.count(existing_class_name) with shared.SelectionRestorer(editor): with shared.UndoableAction(document): document.SetText( document_text.replace(existing_class_name, guessed_class_name)) app.SetStatusMessage('Replaced %s occurrences' % n_occurrences)
def flip(editor=wingapi.kArgEditor): ''' Flip between opposite words. Put the caret on a word like `True` or `start` or `new` and watch it change into `False` or `end` or `old`. Suggested key combination: `Insert P` ''' assert isinstance(editor, wingapi.CAPIEditor) document = editor.GetDocument() assert isinstance(document, wingapi.CAPIDocument) with shared.UndoableAction(document): word, word_start_position = _is_any_word_on_caret( shared.get_text(document), editor.GetSelection()[0], all_words ) if not word: return for first_word, second_word in flip_pairs: if first_word == word: new_word = second_word break elif second_word == word: new_word = first_word break else: continue else: raise RuntimeError with shared.SelectionRestorer(editor): document.DeleteChars(word_start_position, word_start_position + len(word) - 1) document.InsertChars(word_start_position, new_word)
def flip(editor=wingapi.kArgEditor): ''' Flip between opposite words. Put the caret on a word like `True` or `start` or `new` and watch it change into `False` or `end` or `old`. Suggested key combination: `Insert P` ''' assert isinstance(editor, wingapi.CAPIEditor) document = editor.GetDocument() assert isinstance(document, wingapi.CAPIDocument) with shared.UndoableAction(document): word, word_start_position = _is_any_word_on_caret( shared.get_text(document), editor.GetSelection()[0], all_words) if not word: return for first_word, second_word in flip_pairs: if first_word == word: new_word = second_word break elif second_word == word: new_word = first_word break else: continue else: raise RuntimeError with shared.SelectionRestorer(editor): document.DeleteChars(word_start_position, word_start_position + len(word) - 1) document.InsertChars(word_start_position, new_word)
def _get_matches(document): assert isinstance(document, wingapi.CAPIDocument) document_text = shared.get_text(document) return tuple(pattern.finditer(document_text))
def _get_all_number_positions(editor): text = shared.get_text(editor.GetDocument()) matches = tuple(number_pattern.finditer(text)) return tuple((match.start(), match.end()) for match in matches)
def _get_matches(document): assert isinstance(document, wingapi.CAPIDocument) document_text = shared.get_text(document) return tuple(match for match in invocation_pattern.finditer(document_text) if not keyword.iskeyword(match.groups()[0]))
def _get_scope_name_positions(document): document_text = shared.get_text(document) matches = _scope_name_pattern.finditer(document_text) return tuple(match.span(1) for match in matches)
def for_thing_in_things(editor=wingapi.kArgEditor, comprehension=False): ''' Turn `things` into `for thing in things:`. Type any pluarl word, like `bananas` or `directories`. Then run this script, and you get `for directory in directories`. This also works for making `range(number)` into `for i in range(number):`. Note: The `:` part is added only on Windows. If `comprehension=True`, inputs `for thing in things` without the colon and puts the caret before instead of after. Suggested key combination: `Insert Ctrl-F` `Insert Shift-F` for `comprehension=True` ''' assert isinstance(editor, wingapi.CAPIEditor) document = editor.GetDocument() document_text = shared.get_text(document) assert isinstance(document, wingapi.CAPIDocument) with shared.UndoableAction(document): end_position, _ = editor.GetSelection() line_number = document.GetLineNumberFromPosition(end_position) line_start = document.GetLineStart(line_number) line_end = document.GetLineEnd(line_number) line_contents = document.GetCharRange(line_start, line_end) editor.SetSelection(end_position, end_position) if ')' in document.GetCharRange(end_position - 1, end_position + 1) \ and 'range(' in line_contents: start_position = document_text.find('range(', line_start) end_position = document_text.find( ')', start_position, min((line_end, end_position + 1)) ) + 1 else: wingapi.gApplication.ExecuteCommand('backward-word') start_position, _ = editor.GetSelection() print(start_position, end_position) base_text = document.GetCharRange(start_position, end_position) print(base_text) ### Analyzing base text: ############################################## # # if range_pattern.match(base_text): variable_name = 'i' elif base_text.endswith('s'): variable_name = shared.plural_word_to_singular_word(base_text) # # ### Finished analyzing base text. ##################################### segment_to_insert = 'for %s in ' % variable_name if comprehension: segment_to_insert = ' %s' % segment_to_insert document.InsertChars(start_position, segment_to_insert) if comprehension: editor.SetSelection(start_position, start_position) else: editor.ExecuteCommand('end-of-line') if shared.autopy_available: import autopy.key autopy.key.tap(':')
def for_thing_in_things(editor=wingapi.kArgEditor, app=wingapi.kArgApplication, comprehension=False): ''' Turn `things` into `for thing in things:`. Type any pluarl word, like `bananas` or `directories`. Then run this script, and you get `for directory in directories`. This also works for making `range(number)` into `for i in range(number):`. Note: The `:` part is added only on Windows. If `comprehension=True`, inputs `for thing in things` without the colon and puts the caret before instead of after. Suggested key combination: `Insert Ctrl-F` `Insert Shift-F` for `comprehension=True` ''' assert isinstance(editor, wingapi.CAPIEditor) document = editor.GetDocument() document_text = shared.get_text(document) assert isinstance(document, wingapi.CAPIDocument) with shared.UndoableAction(document): end_position, _ = editor.GetSelection() line_number = document.GetLineNumberFromPosition(end_position) line_start = document.GetLineStart(line_number) line_end = document.GetLineEnd(line_number) line_contents = document.GetCharRange(line_start, line_end) editor.SetSelection(end_position, end_position) if ')' in document.GetCharRange(end_position - 1, end_position + 1) \ and 'range(' in line_contents: text_start_position = document_text.find('range(', line_start) if document_text[text_start_position - 1] == 'x': text_start_position -= 1 end_position = document_text.find( ')', text_start_position, min((line_end, end_position + 1)) ) + 1 else: wingapi.gApplication.ExecuteCommand('backward-word') text_start_position, _ = editor.GetSelection() wingapi.gApplication.ExecuteCommand('forward-word') wingapi.gApplication.ExecuteCommand('select-expression') shared.strip_selection_if_single_line(editor) expression_start_position, _ = editor.GetSelection() base_text = document.GetCharRange(text_start_position, end_position) ### Analyzing base text: ############################################## # # if range_pattern.match(base_text): variable_name = 'i' elif base_text.endswith('s'): variable_name = shared.plural_word_to_singular_word(base_text) else: raise Exception('This text doesn\'t work: %s' % base_text) # # ### Finished analyzing base text. ##################################### segment_to_insert = 'for %s in ' % variable_name with shared.SelectionRestorer(editor): app.ExecuteCommand('beginning-of-line-text(toggle=False)') home_position, _ = editor.GetSelection() if comprehension: segment_to_insert = ' %s' % segment_to_insert document.InsertChars(expression_start_position, segment_to_insert) editor.SetSelection(expression_start_position, expression_start_position) else: document.InsertChars(home_position, segment_to_insert) editor.ExecuteCommand('end-of-line') if shared.autopy_available: import autopy.key autopy.key.tap(':')
def _find_string_from_position(editor, position, multiline=False): ''' Given a character in the document known to be in a string, find its string. ''' assert isinstance(editor, wingapi.CAPIEditor) assert _is_position_on_string(editor, position) document_start = 0 document_end = editor.GetDocument().GetLength() start_marker = end_marker = position while end_marker < document_end and \ _is_position_on_string(editor, end_marker+1): end_marker += 1 while start_marker > document_start and \ _is_position_on_string(editor, start_marker-1): start_marker -= 1 if start_marker > document_start: assert not _is_position_on_string(editor, start_marker-1) if end_marker < document_end: assert not _is_position_on_string(editor, end_marker+1) if multiline: string_ranges = [(start_marker, end_marker)] document_text = shared.get_text(editor.GetDocument()) ### Scanning backward: ################################################ # # while True: start_of_first_string = string_ranges[0][0] # No backwards regex search in `re` yet, doing it manually: for i in range(start_of_first_string - 1, 0, -1): if document_text[i] not in string.whitespace: candidate_end_of_additional_string = i print('Candidate: %s %s' % (i, document_text[i])) break else: break if _is_position_on_string(editor, candidate_end_of_additional_string, try_previous=False): string_ranges.insert( 0, _find_string_from_position( editor, candidate_end_of_additional_string ) ) continue else: break # # ### Finished scanning backward. ####################################### ### Scanning forward: ################################################# # # while True: end_of_last_string = string_ranges[-1][1] search_result = re.search('\S', document_text[end_of_last_string:]) if search_result: candidate_start_of_additional_string = \ end_of_last_string + search_result.span()[0] if _is_position_on_string(editor, candidate_start_of_additional_string, try_previous=False): string_ranges.append( _find_string_from_position( editor, candidate_start_of_additional_string ) ) continue # (This is like an `else` clause for both the above `if`s.) return tuple(string_ranges) # # ### Finished scanning forward. ######################################## else: # not multiline return (start_marker, end_marker)
def _find_string_from_position(editor, position, multiline=False): ''' Given a character in the document known to be in a string, find its string. ''' assert isinstance(editor, wingapi.CAPIEditor) assert _is_position_on_string(editor, position) document_start = 0 document_end = editor.GetDocument().GetLength() start_marker = end_marker = position while end_marker < document_end and \ _is_position_on_string(editor, end_marker+1): end_marker += 1 while start_marker > document_start and \ _is_position_on_string(editor, start_marker-1): start_marker -= 1 if start_marker > document_start: assert not _is_position_on_string(editor, start_marker - 1) if end_marker < document_end: assert not _is_position_on_string(editor, end_marker + 1) if multiline: string_ranges = [(start_marker, end_marker)] document_text = shared.get_text(editor.GetDocument()) ### Scanning backward: ################################################ # # while True: start_of_first_string = string_ranges[0][0] # No backwards regex search in `re` yet, doing it manually: for i in range(start_of_first_string - 1, 0, -1): if document_text[i] not in string.whitespace: candidate_end_of_additional_string = i print('Candidate: %s %s' % (i, document_text[i])) break else: break if _is_position_on_string(editor, candidate_end_of_additional_string, try_previous=False): string_ranges.insert( 0, _find_string_from_position( editor, candidate_end_of_additional_string)) continue else: break # # ### Finished scanning backward. ####################################### ### Scanning forward: ################################################# # # while True: end_of_last_string = string_ranges[-1][1] search_result = re.search('\S', document_text[end_of_last_string:]) if search_result: candidate_start_of_additional_string = \ end_of_last_string + search_result.span()[0] if _is_position_on_string(editor, candidate_start_of_additional_string, try_previous=False): string_ranges.append( _find_string_from_position( editor, candidate_start_of_additional_string)) continue # (This is like an `else` clause for both the above `if`s.) return tuple(string_ranges) # # ### Finished scanning forward. ######################################## else: # not multiline return (start_marker, end_marker)
def _get_all_constant_positions(editor): text = shared.get_text(editor.GetDocument()) matches = tuple(constant_pattern.finditer(text)) return tuple((match.start(), match.end()) for match in matches)