def patched_perform_midline_tab(text, event): if isinstance(text, ShellText): option_name = "edit.tab_complete_in_shell" else: option_name = "edit.tab_complete_in_editor" if get_workbench().get_option(option_name): if not text.has_selection(): _handle_autocomplete_request_for_text(text) return "break" else: return None return text.perform_smart_tab(event)
def _get_topmost_selected_visualizer(self): visualizer = self._get_topmost_visualizer() if visualizer is None: return None topmost_text_widget = visualizer._text focused_widget = get_workbench().focus_get() if focused_widget is None: return None elif focused_widget == topmost_text_widget: return visualizer else: return None
def goto_definition(event): if not control_is_pressed(event.state): return assert isinstance(event.widget, tk.Text) text = event.widget source = text.get("1.0", "end") index = text.index("insert") index_parts = index.split(".") line, column = int(index_parts[0]), int(index_parts[1]) # TODO: find current editor filename script = Script(source, line=line, column=column, path="") defs = script.goto_definitions() if len(defs) > 0: module_path = defs[0].module_path module_name = defs[0].module_name line = defs[0].line if module_path and line is not None: get_workbench().get_editor_notebook().show_file(module_path, line) elif module_name == "" and line is not None: # current editor get_workbench().get_editor_notebook().get_current_editor( ).select_range(line)
def __init__(self, master, show_hidden_files=False, show_expand_buttons=True): super().__init__( master, show_hidden_files=show_hidden_files, show_expand_buttons=show_expand_buttons ) self.dir_separator = "/" get_workbench().bind("get_dirs_child_data_response", self.update_dir_data, True) get_workbench().bind("get_fs_info_response", self.present_fs_info, True) get_workbench().bind("RemoteFileOperation", self.on_remote_file_operation, True)
def on_secondary_click(self, event=None): super().on_secondary_click(event) self.mark_set("insert", "@%d,%d" % (event.x, event.y)) menu = get_workbench().get_menu("edit") try: from ynnoht.plugins.debugger import get_current_debugger debugger = get_current_debugger() if debugger is not None: menu = debugger.get_editor_context_menu() except ImportError: pass menu.tk_popup(event.x_root, event.y_root)
def run_selection(event=None): widget = get_workbench().focus_get() if isinstance(widget, CodeViewText): text = widget if text.has_selection(): code = text.get("sel.first", "sel.last") else: code = text.get("insert linestart", "insert lineend") # move cursor to next row row, col = map(int, text.index("insert").split(".")) text.mark_set("insert", "{}.{}".format(row + 1, col)) _submit_code(code)
def request_focus_into(self, path): if path == "": if running_on_windows(): # list of drives, can't cd return self.focus_into(path) else: path = "/" if not os.path.isdir(path): return proxy = get_runner().get_backend_proxy() if ( proxy and proxy.uses_local_filesystem() and proxy.get_cwd() != path and get_runner().is_waiting_toplevel_command() ): get_shell().submit_magic_command(construct_cd_command(path)) else: # it's OK, if it's already focused into this directory # focus again to refresh self.focus_into(path) get_workbench().set_local_cwd(path)
def load_plugin() -> None: wb = get_workbench() wb.set_default("view.paren_highlighting", True) wb.bind("TextInsert", update_highlighting_edit_cw, True) wb.bind("TextDelete", update_highlighting_edit_cw, True) wb.bind_class("CodeViewText", "<<VerticalScroll>>", update_highlighting_move, True) wb.bind_class("CodeViewText", "<<CursorMove>>", update_highlighting_move, True) wb.bind_class("ShellText", "<<TextChange>>", update_highlighting_full, True) wb.bind_class("ShellText", "<<CursorMove>>", update_highlighting_full, True) wb.bind("<<UpdateAppearance>>", update_highlighting_full, True)
def _replace(self, focus, value): start_mark = self._get_mark_name(focus.lineno, focus.col_offset) end_mark = self._get_mark_name(focus.end_lineno, focus.end_col_offset) self.text.delete(start_mark, end_mark) id_str = memory.format_object_id(value.id) if get_workbench().in_heap_mode(): value_str = id_str else: value_str = shorten_repr(value.repr, 100) object_tag = "object_" + str(value.id) self.text.insert(start_mark, value_str, ("value", object_tag)) if misc_utils.running_on_mac_os(): sequence = "<Command-Button-1>" else: sequence = "<Control-Button-1>" self.text.tag_bind( object_tag, sequence, lambda _: get_workbench().event_generate("ObjectSelect", object_id=value.id), )
def __init__(self, master): ttk.Frame.__init__(self, master) self._init_widgets() self._tab_changed_binding = ( get_workbench() .get_editor_notebook() .bind("<<NotebookTabChanged>>", self._update_frame_contents, True) ) get_workbench().bind("Save", self._update_frame_contents, True) get_workbench().bind("SaveAs", self._update_frame_contents, True) get_workbench().bind_class("Text", "<<NewLine>>", self._update_frame_contents, True) self._update_frame_contents()
def add_combobox( self, option_name, values, row=None, column=0, padx=0, pady=0, columnspan=1, width=None ): variable = get_workbench().get_variable(option_name) combobox = ttk.Combobox( self, exportselection=False, textvariable=variable, state="readonly", height=15, width=width, values=values, ) combobox.grid( row=row, column=column, sticky=tk.W, pady=pady, padx=padx, columnspan=columnspan )
def export(): filename = asksaveasfilename( filetypes=[("Zip-files", ".zip"), ("all files", ".*")], defaultextension=".zip", initialdir=get_workbench().get_local_cwd(), initialfile=time.strftime("ThonnyUsageLogs_%Y-%m-%d.zip"), ) if not filename: return log_dir = _get_log_dir() with zipfile.ZipFile(filename, "w", compression=zipfile.ZIP_DEFLATED) as zipf: for item in os.listdir(log_dir): if item.endswith(".txt") or item.endswith(".zip"): zipf.write(os.path.join(log_dir, item), arcname=item)
def __init__(self, master): super().__init__(master) ttk.Style().configure("Centered.TButton", justify="center") self.back_button = ttk.Button( self.tree, style="Centered.TButton", text=_("Back to\ncurrent frame"), command=self._handle_back_button, width=15, ) get_workbench().bind("BackendRestart", self._handle_backend_restart, True) get_workbench().bind("ToplevelResponse", self._handle_toplevel_response, True) # get_workbench().bind("DebuggerResponse", self._debugger_response, True) get_workbench().bind("get_frame_info_response", self._handle_frame_info_event, True) get_workbench().bind("get_globals_response", self._handle_get_globals_response, True) # records last info from progress messages self._last_active_info = None
def __init__(self, filename): self._filename = filename self._events = [] wb = get_workbench() wb.bind("WorkbenchClose", self._on_worbench_close, True) for sequence in [ "<<Undo>>", "<<Redo>>", "<<Cut>>", "<<Copy>>", "<<Paste>>", # "<<Selection>>", # "<Key>", # "<KeyRelease>", "<Button-1>", "<Button-2>", "<Button-3>", ]: self._bind_all(sequence) for sequence in [ "UiCommandDispatched", "MagicCommand", "Open", "Save", "SaveAs", "NewFile", "EditorTextCreated", # "ShellTextCreated", # Too bad, this event happens before event_logging is loaded "ShellCommand", "ShellInput", "ShowView", "HideView", "TextInsert", "TextDelete", ]: self._bind_workbench(sequence) self._bind_workbench("<FocusIn>", True) self._bind_workbench("<FocusOut>", True)
def _cmd_open_file(self): node = choose_node_for_file_operations(self.winfo_toplevel(), "Where to open from?") if not node: return None if node == "local": path = askopenfilename(filetypes=_dialog_filetypes, initialdir=get_workbench().get_local_cwd()) else: assert node == "remote" target_path = ask_backend_path(self.winfo_toplevel(), "open") if not target_path: return None path = make_remote_path(target_path) if path: # self.close_single_untitled_unmodified_editor() self.show_file(path, propose_dialog=False)
def __init__(self): super().__init__(get_workbench(), background=lookup_style_option( "TFrame", "background")) ui_utils.set_zoomed(self, True) self.main_pw = ReplayerPanedWindow(self, orient=tk.HORIZONTAL, sashwidth=10) self.center_pw = ReplayerPanedWindow(self.main_pw, orient=tk.VERTICAL, sashwidth=10) self.right_frame = ttk.Frame(self.main_pw) self.right_pw = ReplayerPanedWindow(self.right_frame, orient=tk.VERTICAL, sashwidth=10) self.editor_notebook = ReplayerEditorNotebook(self.center_pw) shell_book = ttk.Notebook(self.main_pw) self.shell = ShellFrame(shell_book) self.details_frame = EventDetailsFrame(self.right_pw) self.log_frame = LogFrame(self.right_pw, self.editor_notebook, self.shell, self.details_frame) self.browser = ReplayerFileBrowser(self.main_pw, self.log_frame) self.control_frame = ControlFrame(self.right_frame) self.main_pw.grid(padx=10, pady=10, sticky=tk.NSEW) self.main_pw.add(self.browser, width=200) self.main_pw.add(self.center_pw, width=1000) self.main_pw.add(self.right_frame, width=200) self.center_pw.add(self.editor_notebook, height=700) self.center_pw.add(shell_book, height=300) shell_book.add(self.shell, text="Shell") self.right_pw.grid(sticky=tk.NSEW) self.control_frame.grid(sticky=tk.NSEW) self.right_pw.add(self.log_frame, height=600) self.right_pw.add(self.details_frame, height=200) self.right_frame.columnconfigure(0, weight=1) self.right_frame.rowconfigure(0, weight=1) self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1)
def load_plugin() -> None: get_workbench().add_command("autocomplete", "edit", _("Auto-complete"), handle_autocomplete_request, default_sequence="<Control-space>" # TODO: tester ) get_workbench().set_default("edit.tab_complete_in_editor", True) get_workbench().set_default("edit.tab_complete_in_shell", True) CodeViewText.perform_midline_tab = patched_perform_midline_tab # type: ignore ShellText.perform_midline_tab = patched_perform_midline_tab # type: ignore
def _load_plugin(): wb = get_workbench() wb.bind_class("CodeViewText", "<<CursorMove>>", update_editor_cells, True) wb.bind_class("CodeViewText", "<<TextChange>>", update_editor_cells, True) wb.bind_class("CodeViewText", "<FocusIn>", update_editor_cells, True) wb.bind_class("CodeViewText", "<FocusOut>", update_editor_cells, True) _patch_perform_return() # TODO: try changing insertwidth in keyup/mouseup events # _patch_intercept_mark() # Still causes freezes wb.add_command( "run_cell", "run", ("Run cell"), handler=dummy, # actual handler is in the patch default_sequence="<Control-Return>", tester=run_enabled, group=11, ) wb.add_command( "run_cell_and_advance", "run", ("Run cell and advance"), handler=dummy, # actual handler is in the patch default_sequence="<Shift-Return>", tester=run_enabled, group=11, ) wb.add_command( "run_selection", "run", ("Run selection or current line"), handler=run_selection, default_sequence="<F9>", tester=run_enabled, group=11, )
def __init__(self, master): TextFrame.__init__( self, master, text_class=rst_utils.RstText, vertical_scrollbar_style=scrollbar_style("Vertical"), horizontal_scrollbar_style=scrollbar_style("Horizontal"), horizontal_scrollbar_class=ui_utils.AutoScrollbar, borderwidth=0, wrap="word", relief="flat", padx=20, pady=0, read_only=True, ) self.language_dir = os.path.join( os.path.dirname(ynnoht.__file__), "locale", get_workbench().get_option("general.language"), "HELP_CONTENT", ) self.load_rst_file("index.rst")
def __init__(self, master=None): tk.PanedWindow.__init__(self, master, orient="vertical", borderwidth=0) self.remote_added = False self.configure(sashwidth=lookup_style_option("Sash", "sashthickness", 4)) self.configure(background=lookup_style_option("TPanedWindow", "background")) get_workbench().bind("BackendTerminated", self.on_backend_terminate, True) get_workbench().bind("BackendRestart", self.on_backend_restart, True) get_workbench().bind("WorkbenchClose", self.on_workbench_close, True) self.local_files = ActiveLocalFileBrowser(self) self.local_files.check_update_focus() self.add(self.local_files, minsize=minsize) self.remote_files = ActiveRemoteFileBrowser(self) self.reset_remote()
def _submit_code(code): lines = code.splitlines() # remove starting comments while len(lines) > 0 and lines[0].strip().startswith("#"): lines = lines[1:] # remove starting empty lines while len(lines) > 0 and lines[0].strip() == "": lines = lines[1:] # remove trailing empty lines while len(lines) > 0 and lines[-1].strip() == "": lines = lines[:-1] if len(lines) > 0: code = "\n".join(lines) + "\n" # if code is function definition/last line start with whitespace # end function definition with a second endline if re.match(r"^[ \t].*", lines[-1]) is not None: code += "\n" shell = get_workbench().show_view("ShellView", False) shell.submit_python_code(code)
def __init__(self, master): super().__init__(master, ("function", "location", "id"), displaycolumns=("function", "location")) # self.tree.configure(show="tree") self.tree.column("#0", width=0, anchor=tk.W, stretch=False) self.tree.column("function", width=120, anchor=tk.W, stretch=False) self.tree.column("location", width=450, anchor=tk.W, stretch=True) self.tree.heading("function", text="Function", anchor=tk.W) self.tree.heading("location", text="Location", anchor=tk.W) get_workbench().bind("DebuggerResponse", self._update_stack, True) get_workbench().bind("ToplevelResponse", lambda e=None: self._clear_tree(), True) get_workbench().bind("debugger_return_response", self._handle_debugger_return, True)
def _collect_submission_data(self): tree_data = [] for iid in self.tree.get_children(): values = self.tree.item(iid, "values") tree_data.append({ "helpful": values[0] == self._checked_box, "confusing": values[1] == self._checked_box, "message": values[2], "group": values[3], "symbol": values[4], }) submission = { "feedback_format_version": 1, "ynnoht_version": ynnoht.get_version(), "python_version": ".".join(map(str, sys.version_info[:3])), "message_feedback": tree_data, "comments": self.comments_text_frame.text.get("1.0", "end"), } try: import pylint submission["pylint_version"] = str(pylint.__version__) except ImportError: logging.exception("Could not get Pylint version") if self.include_snapshots_var.get(): submission["snapshots"] = self.snapshots if self.include_ynnoht_id_var.get(): submission["ynnoht_timestamp"] = get_workbench().get_option( "general.configuration_creation_timestamp") return json.dumps(submission, indent=2)
def load_plugin() -> None: dark_images = {"tab-close-active": "tab-close-active-clam-dark"} get_workbench().add_ui_theme( "Clean Dark", "Enhanced Clam", clean( frame_background="#252525", text_background="#2d2d2d", normal_detail="#3D3D3D", high_detail="#6E6E6E", low_detail="#404040", normal_foreground="#9f9f9f", high_foreground="#eeeeee", low_foreground="#595959", ), dark_images, ) get_workbench().add_ui_theme( "Clean Dark Green", "Enhanced Clam", clean( frame_background="#1D291A", text_background="#273627", normal_detail="#2D452F", high_detail="#3C6E40", low_detail="#33402F", normal_foreground="#9E9E9E", high_foreground="#eeeeee", low_foreground="#485C49", ), dark_images, ) get_workbench().add_ui_theme( "Clean Dark Blue", "Enhanced Clam", clean( frame_background="#1A1C29", text_background="#272936", normal_detail="#2D3345", high_detail="#3C436E", low_detail="#2F3640", normal_foreground="#9E9E9E", high_foreground="#eeeeee", low_foreground="#484A5C", ), dark_images, ) get_workbench().add_ui_theme( "Clean Sepia", "Enhanced Clam", clean( frame_background="#E8E7DC", text_background="#F7F6F0", normal_detail="#DEDCC8", high_detail="#eeebe7", low_detail="#D4D0B8", normal_foreground="#222222", high_foreground="#000000", low_foreground="#999999", custom_menubar=0, ), )
def open_help(): get_workbench().show_view("HelpView")
def load_plugin() -> None: get_workbench().add_syntax_theme("Default Light", None, default_light) get_workbench().add_syntax_theme("Default Dark", None, default_dark) get_workbench().add_syntax_theme("Default Dark Green", "Default Dark", default_dark_green) get_workbench().add_syntax_theme("Default Dark Blue", "Default Dark", default_dark_blue) get_workbench().add_syntax_theme("Desert Sunset", "Default Dark", desert_sunset) get_workbench().add_syntax_theme("Zenburn", "Default Dark", zenburn) get_workbench().add_syntax_theme("IDLE Classic", "Default Light", idle_classic) # Comments in IDLE Dark really hurt the eyes # get_workbench().add_syntax_theme("IDLE Dark", "Default Dark", idle_dark) get_workbench().set_default("view.syntax_theme", "Default Light")
def _perform_find(self, event=None): self.infotext_label_var.set("") # reset the info label text tofind = self.find_entry.get() # get the text to find if len(tofind) == 0: # in the case of empty string, cancel return # TODO - set warning text to info label? search_backwards = ( self.direction_var.get() == 1 ) # True - search backwards ('up'), False - forwards ('down') if self._repeats_last_search( tofind ): # continuing previous search, find the next occurrence if search_backwards: search_start_index = self.last_processed_indexes[0] else: search_start_index = self.last_processed_indexes[1] if self.active_found_tag is not None: self.codeview.text.tag_remove( "current_found", self.active_found_tag[0], self.active_found_tag[1] ) # remove the active tag from the previously found string self.passive_found_tags.add( (self.active_found_tag[0], self.active_found_tag[1] )) # ..and set it to passive instead self.codeview.text.tag_add("found", self.active_found_tag[0], self.active_found_tag[1]) else: # start a new search, start from the current insert line position if self.active_found_tag is not None: self.codeview.text.tag_remove( "current_found", self.active_found_tag[0], self.active_found_tag[1] ) # remove the previous active tag if it was present for tag in self.passive_found_tags: self.codeview.text.tag_remove( "found", tag[0], tag[1] ) # and remove all the previous passive tags that were present search_start_index = self.codeview.text.index( "insert") # start searching from the current insert position self._find_and_tag_all( tofind) # set the passive tag to ALL found occurences FindDialog.last_searched_word = tofind # set the data about last search self.last_search_case = self._is_search_case_sensitive() wordstart = self.codeview.text.search( tofind, search_start_index, backwards=search_backwards, forwards=not search_backwards, nocase=not self._is_search_case_sensitive(), ) # performs the search and sets the start index of the found string if len(wordstart) == 0: self.infotext_label_var.set( _("The specified text was not found!") ) # TODO - better text, also move it to the texts resources list self.replace_and_find_button.config(state="disabled") self.replace_button.config(state="disabled") return self.last_processed_indexes = ( wordstart, self.codeview.text.index("%s+1c" % wordstart), ) # sets the data about last search self.codeview.text.see(wordstart) # moves the view to the found index wordend = self.codeview.text.index( "%s+%dc" % (wordstart, len(tofind))) # calculates the end index of the found string self.codeview.text.tag_add("current_found", wordstart, wordend) # tags the found word as active self.active_found_tag = (wordstart, wordend) self.replace_and_find_button.config(state="normal") self.replace_button.config(state="normal") get_workbench().event_generate( "Find", widget=self.codeview.text, text=tofind, backwards=search_backwards, case_sensitive=self._is_search_case_sensitive(), )
def _get_interpreters(self): result = set() if running_on_windows(): # registry result.update(self._get_interpreters_from_windows_registry()) for minor in [5, 6, 7, 8]: for dir_ in [ "C:\\Python3%d" % minor, "C:\\Python3%d-32" % minor, "C:\\Python3%d-64" % minor, "C:\\Program Files\\Python 3.%d" % minor, "C:\\Program Files\\Python 3.%d-64" % minor, "C:\\Program Files (x86)\\Python 3.%d" % minor, "C:\\Program Files (x86)\\Python 3.%d-32" % minor, ]: path = os.path.join(dir_, WINDOWS_EXE) if os.path.exists(path): result.add(normpath_with_actual_case(path)) # other locations for dir_ in ["C:\\Anaconda3", os.path.expanduser("~/Anaconda3")]: path = os.path.join(dir_, WINDOWS_EXE) if os.path.exists(path): result.add(normpath_with_actual_case(path)) else: # Common unix locations dirs = [ "/bin", "/usr/bin", "/usr/local/bin", os.path.expanduser("~/.local/bin") ] for dir_ in dirs: # if the dir_ is just a link to another dir_, skip it # (not to show items twice) # for example on Fedora /bin -> usr/bin if not os.path.exists(dir_): continue apath = normpath_with_actual_case(dir_) if apath != dir_ and apath in dirs: continue for name in [ "python3", "python3.5", "python3.6", "python3.7", "python3.8" ]: path = os.path.join(dir_, name) if os.path.exists(path): result.add(path) if running_on_mac_os(): for version in ["3.5", "3.6", "3.7", "3.8"]: dir_ = os.path.join( "/Library/Frameworks/Python.framework/Versions", version, "bin") path = os.path.join(dir_, "python3") if os.path.exists(path): result.add(path) for command in [ "python3", "python3.5", "python3.5", "python3.6", "python3.7", "python3.8" ]: path = which(command) if path is not None and os.path.isabs(path): result.add(path) for path in get_workbench().get_option("CustomInterpreter.used_paths"): if os.path.exists(path): result.add(normpath_with_actual_case(path)) return sorted(result)
def schedule_update(self): self._use_coloring = get_workbench().get_option("view.syntax_coloring") if not self._update_scheduled: self._update_scheduled = True self.text.after_idle(self.perform_update)
def load_plugin() -> None: get_workbench().add_view(OutlineView, _("Outline"), "ne")