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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,4 @@ __pycache__
htmlcov

# --- Odev plugins, loaded as submodules
odev/plugins/*
tests/plugins/*
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# ODEV - VSCode Editor

Configure VSCode for a database and open a repository in the editor.
Configure VSCode (or any editor derived from it, e.g. VSCodium or Antigravity) for a database and open a repository
in the editor.

## Installation

Expand All @@ -12,3 +13,12 @@ Enable this plugin by running:
```bash
odev plugin --enable odoo-odev/odev-plugin-editor-vscode
```

Using other editors sharing the same base (e.g. VSCodium) should work as well, at the cost of setting up a symlink
to the editor executable in your path.

Antigravity example:

```bash
ln -s $(which antigravity) /usr/local/bin/code
```
2 changes: 1 addition & 1 deletion __manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# or merged change.
# ------------------------------------------------------------------------------

__version__ = "2.0.0"
__version__ = "2.1.0"

# --- Dependencies -------------------------------------------------------------
# List other odev plugins from which this current plugin depends.
Expand Down
88 changes: 61 additions & 27 deletions common/editor_vscode.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

from jinja2 import Environment, FileSystemLoader

from odev.common import progress, string
from odev.common import bash, progress, string
from odev.common.databases import LocalDatabase
from odev.common.errors import OdevError
from odev.common.logging import logging
from odev.common.odoobin import OdoobinProcess
from odev.common.python import PythonEnv

from odev.plugins.odev_plugin_editor_base.common.editor import Editor
Expand All @@ -22,9 +23,19 @@ class VSCodeEditor(Editor):
_name = "code"
_display_name = "VSCode"

@property
def display_name(self) -> str:
"""Also handle other editors derived from VSCode/VSCodium (e.g. Antigravity)."""
name = bash.execute(f"{self._name} --help | head -n 1 | awk '{{print $1}}'")

if name and name.stdout.strip():
return name.stdout.strip().capitalize().decode()

return self._display_name

@property
def command(self) -> str:
if isinstance(self.database, LocalDatabase):
if isinstance(self.database, LocalDatabase) or self.version:
return f"{self._name} {self.workspace_path}"
raise OdevError("Database doesn't exist")

Expand All @@ -37,12 +48,15 @@ def templates(self) -> Environment:
@property
def workspace_directory(self) -> Path:
"""The path to the workspace directory."""
return self.path / ".vscode"
if isinstance(self.database, LocalDatabase):
return self.path / ".vscode"
return self.path

@property
def workspace_path(self) -> Path:
"""The path to the workspace file."""
return self.workspace_directory / f"{self.database.name}.code-workspace"
name = self.database.name if isinstance(self.database, LocalDatabase) else str(self.version)
return self.workspace_directory / f"{name}.code-workspace"

@property
def launch_path(self) -> Path:
Expand All @@ -55,29 +69,37 @@ def tasks_path(self) -> Path:
return self.workspace_directory / "tasks.json"

def configure(self):
"""Configure VSCode to work with the database."""
if not isinstance(self.database, LocalDatabase):
return logger.warning(
f"No local database associated with repository {self.git.name!r}, skipping VSCode configuration"
)

with progress.spinner(f"Configuring {self._display_name} for project {self.git.name!r}"):
self.workspace_directory.mkdir(parents=True, exist_ok=True)

self._create_workspace()
self._create_launch()
self._create_tasks()
self._create_jsconfig()

created_files = string.join_bullet(
[
f"Workspace: {self.workspace_path}",
f"Launch: {self.launch_path}",
f"Tasks: {self.tasks_path}",
],
)
logger.info(f"Created VSCode config for project {self.git.name!r}\n{created_files}")
return None
"""Configure VSCode to work with the database, or to browse a bare Odoo version."""
if isinstance(self.database, LocalDatabase):
with progress.spinner(f"Configuring {self.display_name} for project {self.git.name!r}"):
self.workspace_directory.mkdir(parents=True, exist_ok=True)

self._create_workspace()
self._create_launch()
self._create_tasks()
self._create_jsconfig()

created_files = string.join_bullet(
[
f"Workspace: {self.workspace_path}",
f"Launch: {self.launch_path}",
f"Tasks: {self.tasks_path}",
],
)
logger.info(f"Created {self.display_name} config for project {self.git.name!r}\n{created_files}")
return None

if self.version:
with progress.spinner(f"Configuring {self.display_name} workspace for Odoo {self.version}"):
self.workspace_directory.mkdir(parents=True, exist_ok=True)
self._create_version_workspace()
logger.info(f"Created {self.display_name} workspace for Odoo {self.version}\n Workspace: {self.workspace_path}")
return None

return logger.warning(
f"No local database associated with repository {self.git.name!r}, "
f"skipping {self.display_name} configuration"
)

def _get_rendered_template(self, template_name, **kwargs):
template = self.templates.get_template(template_name)
Expand All @@ -96,6 +118,18 @@ def _create_workspace(self):
with open(self.workspace_path, "w", encoding="utf-8") as f:
f.write(rendered_template)

def _create_version_workspace(self):
"""Create a workspace listing the Odoo worktrees (odoo, enterprise, design-themes) for the version."""
process = OdoobinProcess(self.database, version=self.version).with_edition("enterprise")

worktrees = list(process.odoo_worktrees)
if len(worktrees) < len(list(process.odoo_repositories)):
process.update_worktrees()
worktrees = list(process.odoo_worktrees)

folders = [{"path": worktree.path.as_posix(), "name": worktree.path.name} for worktree in worktrees]
self.workspace_path.write_text(json.dumps({"folders": folders}, indent=4))

def _create_launch(self):
"""Create a launch file for the project."""
rendered_template = self._get_rendered_template("launch.jinja")
Expand Down