diff --git a/modules/plugins/languages/tex/lsp/texlab.nix b/modules/plugins/languages/tex/lsp/texlab.nix index 2ce47dc7..58c55f5a 100644 --- a/modules/plugins/languages/tex/lsp/texlab.nix +++ b/modules/plugins/languages/tex/lsp/texlab.nix @@ -1,7 +1,3 @@ -# TODO: -# - Add Texlab LSP settings: -# - chktex -# - symbols { config, pkgs, @@ -9,9 +5,10 @@ ... }: let inherit (lib.options) mkOption; - inherit (lib.modules) mkIf; - inherit (lib.types) listOf package str attrs ints enum either path nullOr; + inherit (lib.modules) mkIf mkMerge; + inherit (lib.types) attrs either enum ints listOf nullOr package path str submodule; inherit (lib.nvim.config) mkBool; + inherit (builtins) isString map; cfg = config.vim.languages.tex; texlabCfg = cfg.lsp.texlab; @@ -26,51 +23,49 @@ in { description = "texlab package"; }; - forwardSearch = { - enable = mkBool false '' - Whether to enable forward search. - - Enable this option if you want to have the compiled document appear in your chosen PDF viewer. - - For some options see [here](https://github.com/latex-lsp/texlab/wiki/Previewing). - Note this is not all the options, but can act as a guide to help you allong with custom configs. - ''; + chktex = { + enable = mkBool false "Whether to enable linting via chktex"; package = mkOption { type = package; - default = pkgs.okular; + default = pkgs.texlive.withPackages (ps: [ps.chktex]); description = '' - The package to use as your PDF viewer. - This viewer needs to support Synctex. + The chktex package to use. + Must have the `chktex` executable. ''; }; - executable = mkOption { - type = str; - default = "okular"; - description = '' - Defines the executable of the PDF previewer. The previewer needs to support SyncTeX. - ''; - }; + onOpenAndSave = mkBool false "Lint using chktex after opening and saving a file."; - args = mkOption { + onEdit = mkBool false "Lint using chktex after editing a file."; + + additionalArgs = mkOption { type = listOf str; - default = [ - "--unique" - "file:%p#src:%l%f" - ]; + default = []; description = '' - Defines additional arguments that are passed to the configured previewer to perform the forward search. - The placeholders %f, %p, %l will be replaced by the server. - - Placeholders: - - %f: The path of the current TeX file. - - %p: The path of the current PDF file. - - %l: The current line number. + Additional command line arguments that are passed to chktex after editing a file. + Don't redefine the `-I` and `-f` flags as they are set by the server. ''; }; }; + completion.matcher = mkOption { + type = enum [ + "fuzzy" + "fuzzy-ignore-case" + "prefix" + "prefix-ignore-case" + ]; + default = "fuzzy-ignore-case"; + description = '' + Modifies the algorithm used to filter the completion items returned to the client. Possibles values are: + - fuzzy: Fuzzy string matching (case sensitive) + - fuzzy-ignore-case: Fuzzy string matching (case insensitive) + - prefix: Filter out items that do not start with the search text (case sensitive) + - prefix-ignore-case: Filter out items that do not start with the search text (case insensitive) + ''; + }; + diagnostics = { delay = mkOption { type = ints.positive; @@ -104,60 +99,6 @@ in { }; }; - latexindent = { - local = mkOption { - type = nullOr (either str path); - default = null; - description = '' - Defines the path of a file containing the latexindent configuration. - This corresponds to the --local=file.yaml flag of latexindent. - By default the configuration inside the project root directory is used. - ''; - }; - - modifyLineBreaks = mkBool false '' - Modifies linebreaks before, during, and at the end of code blocks when formatting with latexindent. - This corresponds to the --modifylinebreaks flag of latexindent. - ''; - - replacement = mkOption { - type = nullOr (enum ["-r" "-rv" "-rr"]); - default = null; - description = '' - Defines an additional replacement flag that is added when calling latexindent. This can be one of the following: - - "-r" - - "-rv" - - "-rr" - - null - By default no replacement flag is passed. - ''; - }; - }; - - completion.matcher = mkOption { - type = str; - default = "fuzzy-ignore-case"; - description = '' - Modifies the algorithm used to filter the completion items returned to the client. Possibles values are: - - fuzzy: Fuzzy string matching (case sensitive) - - fuzzy-ignore-case: Fuzzy string matching (case insensitive) - - prefix: Filter out items that do not start with the search text (case sensitive) - - prefix-ignore-case: Filter out items that do not start with the search text (case insensitive) - ''; - }; - - inlayHints = { - labelDefinitions = mkBool true "When enabled, the server will return inlay hints for `\\label-like` commands."; - - labelReferences = mkBool true "When enabled, the server will return inlay hints for `\\ref``-like commands."; - - maxLength = mkOption { - type = nullOr ints.positive; - default = null; - description = "When set, the server will truncate the text of the inlay hints to the specified length."; - }; - }; - experimental = { followPackageLinks = mkBool false "If set to true, dependencies of custom packages are resolved and included in the dependency graph."; @@ -191,6 +132,7 @@ in { Hint: Additional commands need to be written without a leading `\` (e. g. `foo` instead of `\foo`). ''; }; + labelDefinitionCommands = mkOption { type = listOf str; default = []; @@ -245,31 +187,6 @@ in { }; }; - formatterLineLength = mkOption { - type = ints.positive; - default = 80; - description = "Defines the maximum amount of characters per line (0 = disable) when formatting BibTeX files."; - }; - - bibtexFormatter = mkOption { - type = enum ["texlab" "latexindent"]; - default = "texlab"; - description = '' - Defines the formatter to use for BibTeX formatting. - Possible values are either texlab or latexindent. - ''; - }; - - latexFormatter = mkOption { - type = enum ["texlab" "latexindent"]; - default = "latexindent"; - description = '' - Defines the formatter to use for LaTeX formatting. - Possible values are either texlab or latexindent. - Note that texlab is not implemented yet. - ''; - }; - extraLuaSettings = mkOption { type = attrs; default = {}; @@ -296,9 +213,203 @@ in { ``` ''; }; + + forwardSearch = { + enable = mkBool false '' + Whether to enable forward search. + + Enable this option if you want to have the compiled document appear in your chosen PDF viewer. + + For some options see [here](https://github.com/latex-lsp/texlab/wiki/Previewing). + Note this is not all the options, but can act as a guide to help you allong with custom configs. + ''; + + package = mkOption { + type = package; + default = pkgs.okular; + description = '' + The package to use as your PDF viewer. + This viewer needs to support Synctex. + ''; + }; + + executable = mkOption { + type = str; + default = "okular"; + description = '' + Defines the executable of the PDF previewer. The previewer needs to support SyncTeX. + ''; + }; + + args = mkOption { + type = listOf str; + default = [ + "--unique" + "file:%p#src:%l%f" + ]; + description = '' + Defines additional arguments that are passed to the configured previewer to perform the forward search. + The placeholders %f, %p, %l will be replaced by the server. + + Placeholders: + - %f: The path of the current TeX file. + - %p: The path of the current PDF file. + - %l: The current line number. + ''; + }; + }; + + formatter = { + formatterLineLength = mkOption { + type = ints.positive; + default = 80; + description = "Defines the maximum amount of characters per line (0 = disable) when formatting BibTeX files."; + }; + + bibtexFormatter = mkOption { + type = enum ["texlab" "latexindent"]; + default = "texlab"; + description = '' + Defines the formatter to use for BibTeX formatting. + Possible values are either texlab or latexindent. + ''; + }; + + latexFormatter = mkOption { + type = enum ["texlab" "latexindent"]; + default = "latexindent"; + description = '' + Defines the formatter to use for LaTeX formatting. + Possible values are either texlab or latexindent. + Note that texlab is not implemented yet. + ''; + }; + }; + + inlayHints = { + labelDefinitions = mkBool true "When enabled, the server will return inlay hints for `\\label`-like commands."; + + labelReferences = mkBool true "When enabled, the server will return inlay hints for `\\ref``-like commands."; + + maxLength = mkOption { + type = nullOr ints.positive; + default = null; + description = "When set, the server will truncate the text of the inlay hints to the specified length."; + }; + }; + + latexindent = { + local = mkOption { + type = nullOr (either str path); + default = null; + description = '' + Defines the path of a file containing the latexindent configuration. + This corresponds to the --local=file.yaml flag of latexindent. + By default the configuration inside the project root directory is used. + ''; + }; + + modifyLineBreaks = mkBool false '' + Modifies linebreaks before, during, and at the end of code blocks when formatting with latexindent. + This corresponds to the --modifylinebreaks flag of latexindent. + ''; + + replacement = mkOption { + type = nullOr (enum ["-r" "-rv" "-rr"]); + default = null; + description = '' + Defines an additional replacement flag that is added when calling latexindent. This can be one of the following: + - "-r" + - "-rv" + - "-rr" + - null + By default no replacement flag is passed. + ''; + }; + }; + + symbols = { + enable = mkBool false "Whether to enable setting symbols config."; + + allowedPatterns = mkOption { + type = listOf str; + default = []; + description = '' + A list of regular expressions used to filter the list of reported document symbols. + If specified, only symbols that match at least one of the specified patterns are sent to the client. + Symbols are filtered recursively so nested symbols can still be sent to the client even though the + parent node is removed from the results. + + See also `texlab.symbols.ignoredPatterns`. + + Hint: If both allowedPatterns and ignoredPatterns are set, then allowed patterns are applied first. + Afterwards, the results are filtered with the ignored patterns. + ''; + }; + + ignoredPatterns = mkOption { + type = listOf str; + default = []; + description = '' + A list of regular expressions used to filter the list of reported document symbols. + If specified, only symbols that match none of the specified patterns are sent to the client. + + See also `texlab.symbols.allowedPatterns`. + ''; + }; + + customEnvironments = mkOption { + type = listOf (submodule { + options = { + name = mkOption { + type = str; + description = "The name of the environment."; + }; + displayName = mkOption { + type = nullOr str; + default = null; + description = "The name shown in the document symbols. Defaults to the value of `name`."; + }; + label = mkBool false '' + If set, the server will try to match a label to environment and append its number. + ''; + }; + }); + default = []; + example = [ + { + name = "foo"; + displayName = "bar"; + label = false; + } + ]; + description = '' + A list of objects that allows extending the list of environments that are part of the document symbols. + + See also texlab.symbols.allowedPatterns. + + Type: listOf submodule: + - name: + - type: str + - description: The name of the environment. + - required + - displayName: + - type: nullOr str + - description: The name shown in the document symbols. + - default: + - label: + - type: boolean + - description: If set, the server will try to match a label to environment and append its number. + - default: false + + Note: This functionallity may not be working, please follow https://github.com/latex-lsp/texlab/pull/1311 + for status updates. + ''; + }; + }; }; - config = mkIf (cfg.enable && (cfg.lsp.texlab.enable)) ( + config = mkIf cfg.enable ( let # ----- Setup Config ----- # Command to start the LSP @@ -307,59 +418,89 @@ in { # Create texlab settings section setupConfig.settings.texlab = ( { - # -- General Settings -- - formatterLineLength = texlabCfg.formatterLineLength; - - # -- Formatters -- - bibtexFormatter = texlabCfg.bibtexFormatter; - latexFormatter = texlabCfg.latexFormatter; + # -- Completion -- + completion.matcher = texlabCfg.completion.matcher; # -- Diagnostics -- diagnosticsDelay = texlabCfg.diagnostics.delay; diagnostics = { - allowedPatterns = texlabCfg.diagnostics.allowedPatterns; - ignoredPatterns = texlabCfg.diagnostics.ignoredPatterns; + inherit (texlabCfg.diagnostics) allowedPatterns ignoredPatterns; }; - # -- Latex Indent -- - latexindent = texlabCfg.latexindent; + # -- Experimental -- + experimental = texlabCfg.experimental; - # -- Completion -- - completion.matcher = texlabCfg.completion.matcher; + # -- Formatters -- + inherit (texlabCfg.formatter) formatterLineLength bibtexFormatter latexFormatter; # -- Inlay Hints -- inlayHints = texlabCfg.inlayHints; - # -- Experimental -- - experimental = texlabCfg.experimental; + # -- Latex Indent -- + latexindent = texlabCfg.latexindent; } # - # -- Forward Search -- - // ( - if texlabCfg.forwardSearch.enable - then { - forwardSearch = { - executable = "${texlabCfg.forwardSearch.package}/bin/${texlabCfg.forwardSearch.executable}"; - args = texlabCfg.forwardSearch.args; - }; - } - else {} - ) - # # -- Build -- // ( if cfg.build.enable then { build = { + inherit + (cfg.build) + onSave + useFileList + auxDirectory + logDirectory + pdfDirectory + filename + forwardSearchAfter + ; + inherit (builderCfg) args; executable = "${builderCfg.package}/bin/${builderCfg.executable}"; - args = builderCfg.args; - forwardSearchAfter = cfg.build.forwardSearchAfter; - onSave = cfg.build.onSave; - useFileList = cfg.build.useFileList; - auxDirectory = cfg.build.auxDirectory; - logDirectory = cfg.build.logDirectory; - pdfDirectory = cfg.build.pdfDirectory; - filename = cfg.build.filename; + }; + } + else {} + ) + # + # -- Chktex -- + // ( + if texlabCfg.chktex.enable + then { + chktex = { + inherit (texlabCfg.chktex) onOpenAndSave onEdit additionalArgs; + }; + } + else {} + ) + # + # -- Forward Search -- + // ( + if texlabCfg.forwardSearch.enable + then { + forwardSearch = { + inherit (texlabCfg.forwardSearch) args; + executable = "${texlabCfg.forwardSearch.package}/bin/${texlabCfg.forwardSearch.executable}"; + }; + } + else {} + ) + # + # -- Symbols -- + // ( + if texlabCfg.symbols.enable + then { + symbols = { + inherit (texlabCfg.symbols) allowedPatterns ignoredPatterns; + + customEnvironments = + map (x: { + inherit (x) name label; + displayName = + if isString x.displayName + then x.displayName + else x.name; + }) + texlabCfg.symbols.customEnvironments; }; } else {} @@ -368,8 +509,14 @@ in { # -- Extra Settings -- // texlabCfg.extraLuaSettings ); - in { - vim.lsp.lspconfig.sources.texlab = "lspconfig.texlab.setup(${lib.nvim.lua.toLuaObject setupConfig})"; - } + in (mkMerge [ + (mkIf texlabCfg.enable { + vim.lsp.lspconfig.sources.texlab = "lspconfig.texlab.setup(${lib.nvim.lua.toLuaObject setupConfig})"; + }) + + (mkIf texlabCfg.chktex.enable { + vim.extraPackages = [texlabCfg.chktex.package]; + }) + ]) ); }