-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add "blender" and "gdb" dev_environments
Signed-off-by: Jack Cherng <jfcherng@gmail.com>
- Loading branch information
Showing
10 changed files
with
339 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from __future__ import annotations | ||
|
||
from pathlib import Path | ||
from typing import Generator, Sequence | ||
|
||
from more_itertools import first_true | ||
|
||
from .impl import ( | ||
BlenderDevEnvironmentHandler, | ||
GdbDevEnvironmentHandler, | ||
SublimeText33DevEnvironmentHandler, | ||
SublimeText38DevEnvironmentHandler, | ||
SublimeTextDevEnvironmentHandler, | ||
) | ||
from .interfaces import BaseDevEnvironmentHandler | ||
|
||
|
||
def find_dev_environment_handler_class(dev_environment: str) -> type[BaseDevEnvironmentHandler] | None: | ||
return first_true( | ||
list_dev_environment_handler_classes(), | ||
pred=lambda handler_cls: handler_cls.can_support(dev_environment), | ||
) | ||
|
||
|
||
def get_dev_environment_handler( | ||
dev_environment: str, | ||
*, | ||
server_dir: str | Path, | ||
workspace_folders: Sequence[str], | ||
) -> BaseDevEnvironmentHandler | None: | ||
if handler_cls := find_dev_environment_handler_class(dev_environment): | ||
return handler_cls( | ||
server_dir=server_dir, | ||
workspace_folders=workspace_folders, | ||
) | ||
return None | ||
|
||
|
||
def list_dev_environment_handler_classes() -> Generator[type[BaseDevEnvironmentHandler], None, None]: | ||
yield BlenderDevEnvironmentHandler | ||
yield GdbDevEnvironmentHandler | ||
yield SublimeText33DevEnvironmentHandler | ||
yield SublimeText38DevEnvironmentHandler | ||
yield SublimeTextDevEnvironmentHandler |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from __future__ import annotations | ||
|
||
from .blender import BlenderDevEnvironmentHandler | ||
from .gdb import GdbDevEnvironmentHandler | ||
from .sublime_text import ( | ||
SublimeText33DevEnvironmentHandler, | ||
SublimeText38DevEnvironmentHandler, | ||
SublimeTextDevEnvironmentHandler, | ||
) | ||
|
||
__all__ = ( | ||
"BlenderDevEnvironmentHandler", | ||
"GdbDevEnvironmentHandler", | ||
"SublimeText33DevEnvironmentHandler", | ||
"SublimeText38DevEnvironmentHandler", | ||
"SublimeTextDevEnvironmentHandler", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from __future__ import annotations | ||
|
||
import json | ||
import tempfile | ||
from pathlib import Path | ||
|
||
from LSP.plugin.core.collections import DottedDict | ||
|
||
from ...utils import run_shell_command | ||
from ..interfaces import BaseDevEnvironmentHandler | ||
|
||
|
||
class BlenderDevEnvironmentHandler(BaseDevEnvironmentHandler): | ||
def handle(self, *, settings: DottedDict) -> None: | ||
self._inject_extra_paths(settings=settings, paths=self.find_paths(settings)) | ||
|
||
@classmethod | ||
def find_paths(cls, settings: DottedDict) -> list[str]: | ||
with tempfile.TemporaryDirectory() as tmpdir: | ||
filepath = Path(tmpdir) / "print_sys_path.py" | ||
filepath.write_text( | ||
R""" | ||
import sys | ||
import json | ||
json.dump({"executable": sys.executable, "paths": sys.path}, sys.stdout) | ||
exit(0) | ||
""".strip(), | ||
encoding="utf-8", | ||
) | ||
args = ( | ||
cls.get_dev_environment_subsetting(settings, "binary"), | ||
"--background", | ||
"--python", | ||
str(filepath), | ||
) | ||
result = run_shell_command(args, shell=False) | ||
|
||
if not result or result[2] != 0: | ||
raise RuntimeError(f"Failed to run command: {args}") | ||
|
||
# Blender prints a bunch of general information to stdout before printing the output of the python | ||
# script. We want to ignore that initial information. We do that by finding the start of the JSON | ||
# dict. This is a bit hacky and there must be a better way. | ||
if (index := result[0].find('\n{"')) == -1: | ||
raise RuntimeError("Unexpected output when calling blender") | ||
|
||
try: | ||
return json.loads(result[0][index:])["paths"] | ||
except json.JSONDecodeError as e: | ||
raise RuntimeError(f"Failed to parse JSON: {e}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
from __future__ import annotations | ||
|
||
import json | ||
import tempfile | ||
from pathlib import Path | ||
|
||
from LSP.plugin.core.collections import DottedDict | ||
|
||
from ...utils import run_shell_command | ||
from ..interfaces import BaseDevEnvironmentHandler | ||
|
||
|
||
class GdbDevEnvironmentHandler(BaseDevEnvironmentHandler): | ||
def handle(self, *, settings: DottedDict) -> None: | ||
self._inject_extra_paths(settings=settings, paths=self.find_paths(settings)) | ||
|
||
@classmethod | ||
def find_paths(cls, settings: DottedDict) -> list[str]: | ||
with tempfile.TemporaryDirectory() as tmpdir: | ||
filepath = Path(tmpdir) / "print_sys_path.commands" | ||
filepath.write_text( | ||
R""" | ||
python | ||
import sys | ||
import json | ||
json.dump({"executable": sys.executable, "paths": sys.path}, sys.stdout) | ||
end | ||
exit | ||
""".strip(), | ||
encoding="utf-8", | ||
) | ||
args = ( | ||
cls.get_dev_environment_subsetting(settings, "binary"), | ||
"--batch", | ||
"--command", | ||
str(filepath), | ||
) | ||
result = run_shell_command(args, shell=False) | ||
|
||
if not result or result[2] != 0: | ||
raise RuntimeError(f"Failed to run command: {args}") | ||
|
||
try: | ||
return json.loads(result[0])["paths"] | ||
except json.JSONDecodeError as e: | ||
raise RuntimeError(f"Failed to parse JSON: {e}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
from __future__ import annotations | ||
|
||
import contextlib | ||
import os | ||
import re | ||
import sys | ||
from abc import ABC | ||
from pathlib import Path | ||
|
||
import sublime | ||
from LSP.plugin.core.collections import DottedDict | ||
|
||
from ..interfaces import BaseDevEnvironmentHandler | ||
|
||
|
||
class BaseSublimeTextDevEnvironmentHandler(BaseDevEnvironmentHandler, ABC): | ||
@property | ||
def python_version(self) -> tuple[int, int]: | ||
return (3, 3) | ||
|
||
def handle(self, *, settings: DottedDict) -> None: | ||
self._inject_extra_paths(settings=settings, paths=self.find_package_dependency_dirs()) | ||
|
||
def find_package_dependency_dirs(self) -> list[str]: | ||
dep_dirs = sys.path.copy() | ||
|
||
# replace paths for target Python version | ||
# @see https://github.com/sublimelsp/LSP-pyright/issues/28 | ||
re_pattern = re.compile(r"(python3\.?)[38]", flags=re.IGNORECASE) | ||
re_replacement = r"\g<1>8" if self.python_version == (3, 8) else r"\g<1>3" | ||
dep_dirs = [re_pattern.sub(re_replacement, dep_dir) for dep_dir in dep_dirs] | ||
|
||
# move the "Packages/" to the last | ||
# @see https://github.com/sublimelsp/LSP-pyright/pull/26#discussion_r520747708 | ||
packages_path = sublime.packages_path() | ||
dep_dirs.remove(packages_path) | ||
dep_dirs.append(packages_path) | ||
|
||
# sublime stubs - add as first | ||
if self.python_version == (3, 3): | ||
dep_dirs.insert(0, str(self.server_dir / "resources/typings/sublime_text_py33")) | ||
|
||
return list(filter(os.path.isdir, dep_dirs)) | ||
|
||
|
||
class SublimeText33DevEnvironmentHandler(BaseSublimeTextDevEnvironmentHandler): | ||
@classmethod | ||
def name(cls) -> str: | ||
return "sublime_text_33" | ||
|
||
@property | ||
def python_version(self) -> tuple[int, int]: | ||
return (3, 3) | ||
|
||
|
||
class SublimeText38DevEnvironmentHandler(BaseSublimeTextDevEnvironmentHandler): | ||
@classmethod | ||
def name(cls) -> str: | ||
return "sublime_text_38" | ||
|
||
@property | ||
def python_version(self) -> tuple[int, int]: | ||
return (3, 8) | ||
|
||
|
||
class SublimeTextDevEnvironmentHandler(BaseSublimeTextDevEnvironmentHandler): | ||
def handle(self, *, settings: DottedDict) -> None: | ||
handler_cls = self.resolve_handler_cls() | ||
handler = handler_cls(server_dir=self.server_dir, workspace_folders=self.workspace_folders) | ||
handler.handle(settings=settings) | ||
|
||
def resolve_handler_cls(self) -> type[BaseSublimeTextDevEnvironmentHandler]: | ||
py_ver = self.detect_st_py_ver() | ||
if py_ver == (3, 3): | ||
return SublimeText33DevEnvironmentHandler | ||
if py_ver == (3, 8): | ||
return SublimeText38DevEnvironmentHandler | ||
raise ValueError(f"Unsupported Python version: {py_ver}") | ||
|
||
def detect_st_py_ver(self) -> tuple[int, int]: | ||
def _is_py38() -> bool: | ||
try: | ||
first_folder = Path(self.workspace_folders[0]).resolve() | ||
except Exception: | ||
return False | ||
|
||
# ST auto uses py38 for files in "Packages/User/" | ||
if (Path(sublime.packages_path()) / "User") in (first_folder, *first_folder.parents): | ||
return True | ||
|
||
with contextlib.suppress(Exception): | ||
# the project wants to use py38 | ||
if (first_folder / ".python-version").read_bytes().strip() == b"3.8": | ||
return True | ||
|
||
return False | ||
|
||
return (3, 8) if _is_py38() else self.python_version |
Oops, something went wrong.