Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Albert Zed Workspaces Plugin

Quickly find and open your Zed workspaces right from your favourite launcher.
Quickly find and open your Zed workspaces right from your favorite launcher.

**Note:** This plugin is not officially associated with Zed or Zed Industries in any way.

Expand All @@ -10,6 +10,8 @@ To install, copy or symlink this directory to `~/.local/share/albert/python/plug

Or just run `git clone https://github.com/HarshNarayanJha/albert_zed_workspaces ~/.local/share/albert/python/plugins/albert_zed_workspaces/`

**Note:** For macOS users, be sure to go to `Zed > Install CLI` option to have `zed` in path so that the plugin can detect it.

### Development Setup

I use the Zed Editor (naturally). Python Development includes `pyright` as `lsp` and `ruff` as `linter`.
Expand Down
66 changes: 29 additions & 37 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
"""
This plugin allows you to quickly open workspaces in Zed Editor

Disclaimer: This plugin has no affiliation with Zed Industries. The icons are used under the terms specified there.
Disclaimer: This plugin is not officially affiliated with Zed or Zed Industries.
"""

import logging
import sqlite3
from dataclasses import dataclass
from pathlib import Path
from shutil import which
from sys import platform
from typing import cast, override
from typing import override

from albert import ( # pyright: ignore[reportMissingModuleSource]
Action,
Expand Down Expand Up @@ -39,22 +40,20 @@
class Workspace:
id: str
name: str
path: str
paths: list[str]
last_opened: int


@dataclass
class Editor:
name: str
icon: Path
icon_system: str
icon: str
config_dir_prefix: str
binary: str | None

def __init__(self, name: str, icon: Path, icon_system: str, config_dir_prefix: str, binaries: list[str]):
def __init__(self, name: str, icon: str, config_dir_prefix: str, binaries: list[str]):
self.name = name
self.icon = icon
self.icon_system = icon_system
self.config_dir_prefix = config_dir_prefix
self.binary = self._find_binary(binaries)

Expand All @@ -69,7 +68,7 @@ def list_workspaces(self) -> list[Workspace]:
if platform == "darwin":
config_dir = Path.home() / "Library" / "Application Support"

dirs = list(config_dir.glob(f"{self.config_dir_prefix}*/"))
dirs = list(config_dir.glob(f"{self.config_dir_prefix}/"))
if not dirs:
return []
latest = sorted(dirs)[-1]
Expand All @@ -80,22 +79,23 @@ def _parse_recent_workspaces(self, recent_workspaces_file: Path) -> list[Workspa
workspaces: list[Workspace] = []
with sqlite3.connect(recent_workspaces_file) as conn:
cursor = conn.cursor()
cursor.execute("SELECT workspace_id, local_paths, timestamp FROM workspaces")
cursor.execute("SELECT workspace_id, paths, timestamp FROM workspaces")
for row in cursor:
if not row[1]:
continue

w_id = row[0]
local_path = "/" + "/".join(row[1].decode().split("/")[1:])
paths= row[1].split('\n')
timestamp = int(isoparse(row[2]).timestamp())

w_name = local_path.split("/")[-1]
w_name = ', '.join([path.split("/")[-1] for path in paths])

workspaces.append(Workspace(id=w_id, name=w_name, path=local_path, last_opened=timestamp))
workspaces.append(Workspace(id=w_id, name=w_name, paths=paths, last_opened=timestamp))

return workspaces

except FileNotFoundError:
except sqlite3.OperationalError:
logging.error(f"Please update your Zed to the latest version for {recent_workspaces_file}")
return []


Expand All @@ -104,25 +104,27 @@ def __init__(self):
PluginInstance.__init__(self)
TriggerQueryHandler.__init__(self)

self._systemicon: bool = cast(bool, self.readConfig("systemicon", bool))

plugin_dir = Path(__file__).parent
zed_dir_name = "zed"
if platform == "darwin":
zed_dir_name = "Zed"
icon = "qfip:/Applications/Zed.app"
icon_preview = "qfip:/Applications/Zed-Preview.app"
elif platform == "linux":
zed_dir_name = "zed"
icon = "xdg:zed"
icon_preview = "xdg:zed-preview"
else:
raise NotImplementedError(f"Unsupported platform: {platform}")

editors = [
Editor(
name="Zed Editor",
icon=plugin_dir / "icons" / "zed-stable.png",
icon_system="xdg:zed",
icon=icon,
config_dir_prefix=f"{zed_dir_name}/db/0-stable",
binaries=["zed", "zeditor", "zedit", "zed-cli"],
),
Editor(
name="Zed Editor (Preview)",
icon=plugin_dir / "icons" / "zed-preview.png",
icon_system="xdg:zed-preview",
icon=icon_preview,
config_dir_prefix=f"{zed_dir_name}/db/0-preview",
binaries=["zed", "zeditor", "zedit", "zed-cli"],
),
Expand All @@ -144,8 +146,8 @@ def handleTriggerQuery(self, query: Query):
m = Matcher(query.string)
for editor in self.editors:
workspaces = editor.list_workspaces()
workspaces = [p for p in workspaces if Path(p.path).exists()]
workspaces = [p for p in workspaces if m.match(p.name) or m.match(p.path)]
workspaces = [p for p in workspaces if all(Path(path).exists() for path in p.paths)]
workspaces = [p for p in workspaces if m.match(p.name) or any(m.match(path) for path in p.paths)]
editor_workspace_pairs.extend([(editor, p) for p in workspaces])

# sort by last opened
Expand All @@ -157,33 +159,23 @@ def _make_item(self, editor: Editor, workspace: Workspace, query: Query) -> Item
return StandardItem(
id=str(workspace.id),
text=workspace.name,
subtext=workspace.path,
subtext=';'.join(workspace.paths),
inputActionText=query.trigger + workspace.name,
iconUrls=[editor.icon_system if self._systemicon else f"file:{editor.icon}"],
iconUrls=[editor.icon],
actions=[
Action(
"Open",
"Open in %s" % editor.name,
lambda selected_workspace=workspace.path: runDetachedProcess(
lambda selected_workspace=workspace.paths: runDetachedProcess(
# Binary has to be valid here
[editor.binary, selected_workspace] # pyright: ignore[reportArgumentType]
[editor.binary] + selected_workspace # pyright: ignore[reportArgumentType]
),
)
],
)

@property
def systemicon(self) -> bool:
return self._systemicon

@systemicon.setter
def systemicon(self, value: bool) -> None:
self._systemicon = value
self.writeConfig("systemicon", value)

@override
def configWidget(self):
return [
{"type": "label", "text": str(__doc__).strip(), "widget_properties": {"textFormat": "Qt::MarkdownText"}},
{"type": "checkbox", "property": "systemicon", "label": "Use System Icon for Zed", "default": True},
]
Binary file removed icons/zed-preview.png
Binary file not shown.
Binary file removed icons/zed-stable.png
Binary file not shown.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "albert-plugin-python-zed-workspaces"
version = "1.0"
version = "1.1"
authors = [{ name = "Harsh Narayan Jha", email = "harshnj@proton.me" }]
description = "Open your Zed workspaces"
readme = "README.md"
Expand Down