diff --git a/LSP-clangd.sublime-settings b/LSP-clangd.sublime-settings index 3203917..f0ef084 100644 --- a/LSP-clangd.sublime-settings +++ b/LSP-clangd.sublime-settings @@ -4,17 +4,26 @@ ///////////////////////// // The clangd binary to use. - // "system": Prefers the system binary found in path + // "system": Prefers the system binary below // "auto": Prefers the system binary but falls back to GitHub without user intervention // "github": Prefers the latest tested release from GitHub + // "custom": Use the custom command in the initializationOptions below "binary": "system", - // The command to start `clangd`, generated internally. + // The binary to use when `binary` is set to `system`. + "system_binary": "clangd", + // Generated internally because clangd is configured via command line arguments. + // DO NOT CHANGE THIS, use `system_binary` or `custom_command` instead. "command": [], // Enable clangd for C/C++ and Objective-C/C++ "selector": "source.c | source.c++ | source.objc | source.objc++ | source.cuda-c++", // Makes the auto-complete not trigger twice when writing a -> or when writing :: "auto_complete_selector": "punctuation.accessor | (meta.preprocessor.include string - punctuation.definition.string.end)", "initializationOptions": { + // A custom command to start clangd. Set `binary` to `custom` to use this command. + // The command-line arguments which are generated from the `clang.*` settings are appended to this command. + // This can be used for MSYS2's clangd for example. + "custom_command": [], + // @see https://clangd.llvm.org/extensions#file-status // Enables receiving textDocument/clangd.fileStatus notifications. // -- unsupported -- @@ -93,4 +102,4 @@ // Pretty-print JSON output "clangd.pretty": false, }, -} \ No newline at end of file +} diff --git a/plugin.py b/plugin.py index bc947db..9ccadce 100644 --- a/plugin.py +++ b/plugin.py @@ -93,30 +93,45 @@ def storage_subpath(cls) -> str: return os.path.join(cls.storage_path(), STORAGE_DIR) @classmethod - def managed_server_binary_path(cls) -> Optional[str]: + def managed_clangd_path(cls) -> Optional[str]: binary_name = "clangd.exe" if sublime.platform() == "windows" else "clangd" path = os.path.join(cls.storage_subpath(), "clangd_{version}/bin/{binary_name}".format(version=VERSION_STRING, binary_name=binary_name)) if os.path.exists(path): return path return None + @classmethod + def system_clangd_path(cls) -> Optional[str]: + system_binary = get_settings().get("system_binary") + # Detect if clangd is installed or the command points to a valid binary. + # Fallback, shutil.which has issues on Windows. + system_binary_path = shutil.which(system_binary) or system_binary + if not os.path.isfile(system_binary_path): + return None + return system_binary_path + @classmethod def clangd_path(cls) -> Optional[str]: + """The command to start clangd without any configuration arguments""" binary_setting = get_settings().get("binary") if binary_setting == "system": - return shutil.which("clangd") + return cls.system_clangd_path() elif binary_setting == "github": - return cls.managed_server_binary_path() + return cls.managed_clangd_path() else: # binary_setting == "auto": - return shutil.which("clangd") or cls.managed_server_binary_path() + return cls.system_clangd_path() or cls.managed_clangd_path() @classmethod def needs_update_or_installation(cls) -> bool: + if get_settings().get("binary") == "custom": + return False return cls.clangd_path() is None @classmethod def install_or_update(cls) -> None: + # Binary cannot be set to custom because needs_update_or_installation + # returns False in this case if get_settings().get("binary") == "system": ans = sublime.yes_no_cancel_dialog("clangd was not found in your path. Would you like to auto-install clangd from GitHub?") if ans == sublime.DIALOG_YES: @@ -136,7 +151,7 @@ def install_or_update(cls) -> None: download_server(cls.storage_subpath()) # zip does not preserve file mode - path = cls.managed_server_binary_path() + path = cls.managed_clangd_path() if not path: # this should never happen raise ValueError("installation failed silently") @@ -152,13 +167,17 @@ def on_pre_start( configuration: ClientConfig, ) -> Optional[str]: - clangd_path = cls.clangd_path() - if not clangd_path: - raise ValueError("clangd is currently not installed.") + if get_settings().get("binary") == "custom": + clangd_base_command = configuration.init_options.get("custom_command") # type: List[str] + else: + clangd_path = cls.clangd_path() + if not clangd_path: + raise ValueError("clangd is currently not installed.") + clangd_base_command = [clangd_path] # The configuration is persisted # reset the command to prevent adding an argument multiple times - configuration.command = [clangd_path] + configuration.command = clangd_base_command.copy() for key, value in configuration.init_options.get("clangd").items(): if not value: diff --git a/sublime-package.json b/sublime-package.json index eba6662..5ce6c97 100644 --- a/sublime-package.json +++ b/sublime-package.json @@ -17,17 +17,29 @@ "enum": [ "system", "auto", - "github" + "github", + "custom" ], "enumDescriptions": [ "Prefers the system binary found in path", "Prefers the system binary but falls back to GitHub without user intervention", "Prefers the latest tested release from GitHub", + "Use the custom command in the initializationOptions below", ], }, + "system_binary": { + "type": "string", + "default": "clangd", + "markdownDescription": "The binary to use when `binary` is set to `system`." + }, "initializationOptions": { "additionalProperties": false, "properties": { + "custom_command": { + "type": "array", + "default": [], + "markdownDescription": "A custom command to start clangd. Set `binary` to `custom` to use this command. The command-line arguments which are generated from the `clang.*` settings are appended to this command." + }, "clangdFileStatus": { "type": "boolean", "description": "Enables receiving textDocument/clangd.fileStatus notifications.", @@ -195,7 +207,9 @@ "default": null, "description": "Sets the PCH storage. Storing PCHs in memory increases memory usages, but may improve performance", "enum": [ - null, "disk", "memory" + null, + "disk", + "memory" ] }, "clangd.enable-config": { @@ -209,7 +223,12 @@ ], "default": null, "description": "Sets the clangd log level", - "enum": [null, "error", "info", "verbose"] + "enum": [ + null, + "error", + "info", + "verbose" + ] }, "clangd.path-mappings": { "type": [ @@ -260,4 +279,4 @@ } ] } -} \ No newline at end of file +}