diff --git a/.biscuit/workspace.toml b/.biscuit/workspace.toml new file mode 100644 index 00000000..56588096 --- /dev/null +++ b/.biscuit/workspace.toml @@ -0,0 +1,3 @@ +# workspace I use for biscuit development (@tomlin7) + +dirs = ["D:/biscuit", "D:/biscuit-extensions"] diff --git a/pyproject.toml b/pyproject.toml index 2e30b986..86a9432b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "biscuit-editor" -version = "2.99.85" +version = "2.99.95" description = "A lightweight, fast, and extensible code editor with a growing community" authors = ["Billy "] license = "MIT" diff --git a/src/biscuit/__init__.py b/src/biscuit/__init__.py index ae5242ea..6a94c90c 100644 --- a/src/biscuit/__init__.py +++ b/src/biscuit/__init__.py @@ -1,4 +1,4 @@ -__version__ = "2.99.85" +__version__ = "2.99.95" __version_info__ = tuple([int(num) for num in __version__.split(".")]) from .main import * diff --git a/src/biscuit/commands.py b/src/biscuit/commands.py index b91cd27f..c324757a 100644 --- a/src/biscuit/commands.py +++ b/src/biscuit/commands.py @@ -95,6 +95,29 @@ def save_all(self, *_) -> None: if editor.content.editable: editor.save() + def save_workspace_as(self, *_) -> None: + if path := asksaveasfilename( + title="Save Workspace As...", + initialfile="workspace.toml", + filetypes=[(".toml")], + ): + self.base.workspaces.save(path) + + def add_folder_to_workspace(self, *_) -> None: + path = filedialog.askdirectory() + if not path or not os.path.isdir(path): + return + self.base.workspaces.add_dir(path) + + def open_workspace(self, *_) -> None: + path = filedialog.askopenfilename() + if not path or not os.path.isfile(path): + return + self.base.workspaces.load(path) + + def close_workspace(self, *_) -> None: + self.base.workspaces.close() + def open_settings(self, *_) -> None: self.base.open_settings() diff --git a/src/biscuit/config.py b/src/biscuit/config.py index b250ebfc..e07fef4a 100644 --- a/src/biscuit/config.py +++ b/src/biscuit/config.py @@ -13,6 +13,7 @@ from .history import HistoryManager from .language import LanguageServerManager from .settings import Settings +from .workspaces import WorkspaceManager class ConfigManager: @@ -58,6 +59,7 @@ def setup_configs(self) -> None: self.system = SysInfo(self) self.settings = Settings(self) self.history = HistoryManager(self) + self.workspaces = WorkspaceManager(self) self.resources = self.settings.resources self.bindings = self.settings.bindings @@ -120,3 +122,7 @@ def set_tab_spaces(self, spaces: int) -> None: if e.content and e.content.editable: e.content.text.set_tab_size(spaces) self.statusbar.set_spaces(spaces) + + @property + def active_workspace(self): + return self.workspaces.workspace diff --git a/src/biscuit/events.py b/src/biscuit/events.py index 8060869d..420a10de 100644 --- a/src/biscuit/events.py +++ b/src/biscuit/events.py @@ -1,5 +1,6 @@ from __future__ import annotations +import multiprocessing import os import subprocess import sys @@ -239,6 +240,28 @@ def open_in_new_window(self, dir: str) -> None: def open_new_window(self) -> None: subprocess.Popen([sys.executable, sys.argv[0]]) + # from .main import get_app_instance + # app = get_app_instance() + # multiprocessing.freeze_support() + # multiprocessing.Process(target=app.run).start() + + def workspace_opened(self) -> None: + workspace = self.active_workspace + try: + self.open_directory(workspace.dirs[0]) + for dir in workspace.dirs[1:]: + self.open_in_new_window(dir) + except Exception as e: + self.logger.error(f"Opening workspace failed: {e}") + self.notifications.error("Opening workspace failed: see logs") + + def workspace_changed(self, dir: str) -> None: + self.open_in_new_window(dir) + + def workspace_closed(self) -> None: + self.close_active_directory() + # TODO add cli args to flag new windows opened by workspace and close them + def toggle_terminal(self) -> None: self.panel.switch_to_terminal() self.contentpane.toggle_panel() diff --git a/src/biscuit/layout/menubar/menubar.py b/src/biscuit/layout/menubar/menubar.py index a4259516..20881e29 100644 --- a/src/biscuit/layout/menubar/menubar.py +++ b/src/biscuit/layout/menubar/menubar.py @@ -134,6 +134,13 @@ def add_file_menu(self) -> None: self.file_menu.add_command("Open Recent File...", events.open_recent_file) self.file_menu.add_command("Open Recent Folder...", events.open_recent_dir) self.file_menu.add_separator() + self.file_menu.add_command("Open workspace...", events.open_workspace) + self.file_menu.add_command( + "Add Folder to Workspace...", events.add_folder_to_workspace + ) + self.file_menu.add_command("Save Workspace As...", events.save_workspace_as) + self.file_menu.add_command("Close Workspace", events.close_workspace) + self.file_menu.add_separator() self.file_menu.add_command("Save", events.save_file) self.file_menu.add_command("Save As...", events.save_file_as) self.file_menu.add_command("Save All", events.save_all) @@ -165,7 +172,9 @@ def add_edit_menu(self) -> None: self.edit_menu.add_command("Change Language Mode", events.change_language_mode) self.edit_menu.add_checkable("Word Wrap", events.toggle_wordwrap) self.edit_menu.add_checkable("Block Cursor", events.toggle_block_cursor) - self.edit_menu.add_checkable("Toggle Relative Line Numbering", events.toggle_relative_line_numbering) + self.edit_menu.add_checkable( + "Toggle Relative Line Numbering", events.toggle_relative_line_numbering + ) def add_view_menu(self) -> None: events = self.events diff --git a/src/biscuit/settings/settings.py b/src/biscuit/settings/settings.py index be49662f..b3987112 100644 --- a/src/biscuit/settings/settings.py +++ b/src/biscuit/settings/settings.py @@ -73,12 +73,15 @@ def generate_actionset(self) -> None: ) def setup_icon(self) -> None: - self.base.call( - "wm", - "iconphoto", - self.base._w, - tk.PhotoImage(file=self.resources.get_res_path_platform("icon.png")), - ) + try: + self.base.call( + "wm", + "iconphoto", + self.base._w, + tk.PhotoImage(file=self.resources.get_res_path_platform("icon.png")), + ) + except tk.TclError: + pass def setup_font(self) -> None: try: diff --git a/src/biscuit/workspaces/__init__.py b/src/biscuit/workspaces/__init__.py new file mode 100644 index 00000000..b3c2430f --- /dev/null +++ b/src/biscuit/workspaces/__init__.py @@ -0,0 +1,3 @@ +from .loader import WorkspaceLoader +from .manager import WorkspaceManager +from .workspace import Workspace diff --git a/src/biscuit/workspaces/loader.py b/src/biscuit/workspaces/loader.py new file mode 100644 index 00000000..379c83eb --- /dev/null +++ b/src/biscuit/workspaces/loader.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +import typing + +import toml + +from .workspace import Workspace + +if typing.TYPE_CHECKING: + from .manager import WorkspaceManager + + +class WorkspaceLoader: + def __init__(self, manager: WorkspaceManager): + self.manager = manager + self.base = manager.base + + def load(self, path): + with open(path) as f: + data = toml.load(f) + self.open_workspace(Workspace(self, path, data["dirs"])) + + def dump_modified(self, path: str, dirs: list[str]): + with open(path, "w") as f: + toml.dump({"dirs": dirs}, f) + + def open_workspace(self, workspace: Workspace): + self.manager.set_workspace(workspace) + + def save(self, active_workspace: Workspace, path: str): + with open(path, "w") as f: + toml.dump(active_workspace.export(), f) diff --git a/src/biscuit/workspaces/manager.py b/src/biscuit/workspaces/manager.py new file mode 100644 index 00000000..906f0004 --- /dev/null +++ b/src/biscuit/workspaces/manager.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +import typing + +from .loader import WorkspaceLoader +from .workspace import Workspace + +if typing.TYPE_CHECKING: + from biscuit.app import App + + +class WorkspaceManager: + def __init__(self, base: App): + self.base = base + self.workspace = None + + self.loader = WorkspaceLoader(self) + + def set_workspace(self, workspace: Workspace): + self.workspace = workspace + self.base.workspace_opened() + + def add_dir(self, dir: str): + if not self.workspace: + self.set_workspace(Workspace(self, "workspace.toml", [dir])) + self.set_workspace(Workspace(self, "workspace.toml", [dir])) + return + + self.workspace.add_dir(dir) + self.base.workspace_changed(dir) + + def load(self, path: str): + self.loader.load(path) + + def save(self, path: str): + self.loader.save(path) + + def close(self): + self.workspace = None + self.base.workspace_closed() diff --git a/src/biscuit/workspaces/workspace.py b/src/biscuit/workspaces/workspace.py new file mode 100644 index 00000000..09a2bc22 --- /dev/null +++ b/src/biscuit/workspaces/workspace.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +import typing +from typing import List + +if typing.TYPE_CHECKING: + from .loader import WorkspaceLoader + + +class Workspace: + def __init__(self, loader: WorkspaceLoader, path: str, dirs: List[str]): + self.loader = loader + self.path = path + self.dirs = dirs + + def add_dir(self, dir: str): + self.dirs.append(dir) + self.loader.dump_modified(self.path, self.dirs) + + def export(self): + return {"dirs": self.dirs}