diff --git a/plugin/commands/auto_set_syntax_download_guesslang_server.py b/plugin/commands/auto_set_syntax_download_guesslang_server.py index 6823dd08..1fc2c8ff 100644 --- a/plugin/commands/auto_set_syntax_download_guesslang_server.py +++ b/plugin/commands/auto_set_syntax_download_guesslang_server.py @@ -17,7 +17,7 @@ from ..guesslang.server import GuesslangServer from ..settings import get_merged_plugin_setting from ..shared import G -from ..utils import first_true +from ..utils import first_true, rmtree_ex PathLike = Union[Path, str] @@ -40,7 +40,7 @@ def _worker(cls) -> None: server.stop() time.sleep(1) # wait for stopping the server - shutil.rmtree(GuesslangServer.SERVER_DIR, ignore_errors=True) + rmtree_ex(GuesslangServer.SERVER_DIR, ignore_errors=True) try: cls._prepare_bin() @@ -76,9 +76,9 @@ def _prepare_bin() -> None: # move the decompressed folder one level up guesslang_server_dir = folder.parent tmp_dir = guesslang_server_dir.parent / ".tmp" - shutil.rmtree(tmp_dir, ignore_errors=True) + rmtree_ex(tmp_dir, ignore_errors=True) folder.replace(tmp_dir) - shutil.rmtree(guesslang_server_dir, ignore_errors=True) + rmtree_ex(guesslang_server_dir, ignore_errors=True) tmp_dir.replace(guesslang_server_dir) # cleanup zip_path.unlink(missing_ok=True) diff --git a/plugin/guesslang/server.py b/plugin/guesslang/server.py index 729a7c9c..7925cd03 100644 --- a/plugin/guesslang/server.py +++ b/plugin/guesslang/server.py @@ -24,10 +24,10 @@ def __init__(self, host: str, port: int) -> None: def start(self) -> bool: """Starts the guesslang server and return whether it starts.""" - node_path, node_args = parse_node_path_args() - if not node_path: + if not (node_info := parse_node_path_args()): Logger.log("❌ Node.js binary is not found or not executable") return False + node_path, node_args = node_info Logger.log(f"✔ Use Node.js binary ({node_path}) and args ({node_args})") try: @@ -98,13 +98,13 @@ def _start_process( ) -def parse_node_path_args() -> tuple[str | None, list[str]]: +def parse_node_path_args() -> tuple[str, list[str]] | None: for node, args in ( ( get_merged_plugin_setting("guesslang.node_bin"), get_merged_plugin_setting("guesslang.node_bin_args"), ), - (R"${lsp_utils_node_bin}", []), + ("${lsp_utils_node_bin}", []), (shutil.which("electron"), []), (shutil.which("node"), []), (shutil.which("code"), ["--ms-enable-electron-run-as-node"]), # VSCode @@ -113,7 +113,7 @@ def parse_node_path_args() -> tuple[str | None, list[str]]: ): if (node := expand_variables(node)) and is_executable(node): return (node, args) - return (None, []) + return None def is_executable(path: str | Path) -> bool: diff --git a/plugin/utils.py b/plugin/utils.py index b6473820..21c6f25c 100644 --- a/plugin/utils.py +++ b/plugin/utils.py @@ -3,7 +3,9 @@ import inspect import operator +import os import re +import shutil import sys import tempfile import threading @@ -456,6 +458,18 @@ def str_finditer(content: str, substr: str) -> Generator[int, None, None]: idx += len(substr) +def rmtree_ex(path: str | Path, ignore_errors: bool = False, **kwargs: Any) -> None: + """ + Same with `shutil.rmtree` but with a workaround for long path on Windows. + + @see https://stackoverflow.com/a/14076169/4643765 + @see https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation + """ + if os.name == "nt" and (path := Path(path)).is_absolute(): + path = R"\\?\{}".format(path) + shutil.rmtree(path, ignore_errors, **kwargs) + + def stringify(obj: Any) -> str: """Custom object-to-string converter. Just used for debug messages.""" if isinstance(obj, sublime.View):