From c3291ab71c9052e55a8dd8e33f99bec4e5e3eaea Mon Sep 17 00:00:00 2001 From: isaacST08 Date: Thu, 23 Jan 2025 16:34:57 -0700 Subject: [PATCH] Moved lsp config into its own module --- modules/plugins/languages/tex/default.nix | 965 +++++++++--------- modules/plugins/languages/tex/lsp/default.nix | 528 ++++++++++ 2 files changed, 1011 insertions(+), 482 deletions(-) create mode 100644 modules/plugins/languages/tex/lsp/default.nix diff --git a/modules/plugins/languages/tex/default.nix b/modules/plugins/languages/tex/default.nix index 54811783..116403d1 100644 --- a/modules/plugins/languages/tex/default.nix +++ b/modules/plugins/languages/tex/default.nix @@ -52,99 +52,100 @@ cfg = config.vim.languages.tex; # --- Enable Options --- - mkEnableDefaultOption = default: description: (mkOption { - type = bool; - default = default; - example = !default; - description = description; - }); - mkEnableTreesitterOption = mkEnableDefaultOption config.vim.languages.enableTreesitter; - mkEnableLspOption = mkEnableDefaultOption config.vim.languages.enableLSP; + # mkEnableDefaultOption = default: description: (mkOption { + # type = bool; + # default = default; + # example = !default; + # description = description; + # }); + # mkEnableTreesitterOption = mkEnableDefaultOption config.vim.languages.enableTreesitter; + # mkEnableLspOption = mkEnableDefaultOption config.vim.languages.enableLSP; - # --- Arg Collation Functions -- - collateArgs.lsp.texlab.build = { - tectonic = buildConfig: let - selfConfig = buildConfig.tectonic; - in ( - # Base args - [ - "-X" - "compile" - "%f" - ] - # Flags - ++ ( - if selfConfig.keepIntermediates - then ["--keep-intermediates"] - else [] - ) - ++ ( - if selfConfig.keepLogs - then ["--keep-logs"] - else [] - ) - ++ ( - if selfConfig.onlyCached - then ["--only-cached"] - else [] - ) - ++ ( - if selfConfig.synctex - then ["--synctex"] - else [] - ) - ++ ( - if selfConfig.untrustedInput - then ["--untrusted"] - else [] - ) - # Options - ++ ( - if selfConfig.reruns > 0 - then ["--reruns" "${toString selfConfig.reruns}"] - else [] - ) - ++ ( - if selfConfig.bundle != "" - then ["--bundle" "${toString selfConfig.bundle}"] - else [] - ) - ++ ( - if selfConfig.webBundle != "" - then ["--web-bundle" "${toString selfConfig.webBundle}"] - else [] - ) - ++ ( - if selfConfig.outfmt != "" - then ["--outfmt" "${toString selfConfig.outfmt}"] - else [] - ) - ++ (concatLists (map (x: ["--hide" x]) selfConfig.hidePaths)) - ++ ( - if selfConfig.format != "" - then ["--format" "${toString selfConfig.format}"] - else [] - ) - ++ ( - if selfConfig.color != "" - then ["--color" "${toString selfConfig.color}"] - else [] - ) - # Still options but these are not defined by builder specific options but - # instead synchronize options between the global build options and builder - # specific options - ++ ( - if !(elem buildConfig.pdfDirectory ["." ""]) - then ["--outdir" "${buildConfig.pdfDirectory}"] - else [] - ) - ); - custom = buildConfig: buildConfig.custom.args; - }; + # # --- Arg Collation Functions -- + # collateArgs.lsp.texlab.build = { + # tectonic = buildConfig: let + # selfConfig = buildConfig.tectonic; + # in ( + # # Base args + # [ + # "-X" + # "compile" + # "%f" + # ] + # # Flags + # ++ ( + # if selfConfig.keepIntermediates + # then ["--keep-intermediates"] + # else [] + # ) + # ++ ( + # if selfConfig.keepLogs + # then ["--keep-logs"] + # else [] + # ) + # ++ ( + # if selfConfig.onlyCached + # then ["--only-cached"] + # else [] + # ) + # ++ ( + # if selfConfig.synctex + # then ["--synctex"] + # else [] + # ) + # ++ ( + # if selfConfig.untrustedInput + # then ["--untrusted"] + # else [] + # ) + # # Options + # ++ ( + # if selfConfig.reruns > 0 + # then ["--reruns" "${toString selfConfig.reruns}"] + # else [] + # ) + # ++ ( + # if selfConfig.bundle != "" + # then ["--bundle" "${toString selfConfig.bundle}"] + # else [] + # ) + # ++ ( + # if selfConfig.webBundle != "" + # then ["--web-bundle" "${toString selfConfig.webBundle}"] + # else [] + # ) + # ++ ( + # if selfConfig.outfmt != "" + # then ["--outfmt" "${toString selfConfig.outfmt}"] + # else [] + # ) + # ++ (concatLists (map (x: ["--hide" x]) selfConfig.hidePaths)) + # ++ ( + # if selfConfig.format != "" + # then ["--format" "${toString selfConfig.format}"] + # else [] + # ) + # ++ ( + # if selfConfig.color != "" + # then ["--color" "${toString selfConfig.color}"] + # else [] + # ) + # # Still options but these are not defined by builder specific options but + # # instead synchronize options between the global build options and builder + # # specific options + # ++ ( + # if !(elem buildConfig.pdfDirectory ["." ""]) + # then ["--outdir" "${buildConfig.pdfDirectory}"] + # else [] + # ) + # ); + # custom = buildConfig: buildConfig.custom.args; + # }; in { imports = [ ./treesitter.nix + ./lsp ]; options.vim.languages.tex = { @@ -168,286 +169,286 @@ in { # more sense to group one into its own group of options. # # Each lsp group must have an enable option of its own. - lsp = { - texlab = { - enable = mkEnableLspOption "Whether to enable Tex LSP support (texlab)"; - - package = mkOption { - type = package; - default = pkgs.texlab; - description = "texlab package"; - }; - - build = { - tectonic = { - enable = mkEnableDefaultOption true "Whether to enable Tex Compilation Via Tectonic"; - - package = mkOption { - type = package; - default = pkgs.tectonic; - description = "tectonic package"; - }; - - executable = mkOption { - type = str; - default = "tectonic"; - description = "The executable name from the build package that will be used to build/compile the tex."; - }; - - # -- Flags -- - keepIntermediates = mkEnableDefaultOption false '' - Keep the intermediate files generated during processing. - - If texlab is reporting build errors when there shouldn't be, disable this option. - ''; - keepLogs = mkEnableDefaultOption true '' - Keep the log files generated during processing. - - Without the keepLogs flag, texlab won't be able to report compilation warnings. - ''; - onlyCached = mkEnableDefaultOption false "Use only resource files cached locally"; - synctex = mkEnableDefaultOption true "Generate SyncTeX data"; - untrustedInput = mkEnableDefaultOption false "Input is untrusted -- disable all known-insecure features"; - - # -- Options -- - reruns = mkOption { - type = ints.unsigned; - default = 0; - example = 2; - description = "Rerun the TeX engine exactly this many times after the first"; - }; - - bundle = mkOption { - type = str; - default = ""; - description = "Use this directory or Zip-format bundle file to find resource files instead of the default"; - }; - - webBundle = mkOption { - type = str; - default = ""; - description = "Use this URL to find resource files instead of the default"; - }; - - outfmt = mkOption { - type = enum [ - "pdf" - "html" - "xdv" - "aux" - "fmt" - "" - ]; - default = ""; - description = "The kind of output to generate"; - }; - - hidePaths = mkOption { - type = listOf str; - default = []; - example = [ - "./secrets.tex" - "./passwords.tex" - ]; - description = "Tell the engine that no file at exists, if it tries to read it."; - }; - - format = mkOption { - type = str; - default = ""; - description = "The name of the \"format\" file used to initialize the TeX engine"; - }; - - color = mkOption { - type = enum [ - "always" - "auto" - "never" - "" - ]; - default = ""; - example = "always"; - description = "Enable/disable colorful log output"; - }; - - extraOptions = { - type = listOf str; - default = []; - description = '' - Add extra command line options to include in the tectonic build command. - Extra options added here will not overwrite the options set in as nvf options. - ''; - }; - }; - - custom = { - enable = mkEnableDefaultOption false "Whether to enable using a custom build package"; - package = mkOption { - type = package; - default = pkgs.tectonic; - description = "build/compiler package"; - }; - executable = mkOption { - type = str; - default = "tectonic"; - description = "The executable name from the build package that will be used to build/compile the tex."; - }; - args = mkOption { - type = listOf str; - default = [ - "-X" - "compile" - "%f" - "--synctex" - "--keep-logs" - "--keep-intermediates" - ]; - description = '' - Defines additional arguments that are passed to the configured LaTeX build tool. - Note that flags and their arguments need to be separate elements in this array. - To pass the arguments -foo bar to a build tool, args needs to be ["-foo" "bar"]. - The placeholder `%f` will be replaced by the server. - - Placeholders: - - `%f`: The path of the TeX file to compile. - ''; - }; - }; - - forwardSearchAfter = mkOption { - type = bool; - default = false; - description = "Set this property to true if you want to execute a forward search after a build."; - }; - onSave = mkOption { - type = bool; - default = false; - description = "Set this property to true if you want to compile the project after saving a file."; - }; - useFileList = mkOption { - type = bool; - default = false; - description = '' - When set to true, the server will use the .fls files produced by the TeX engine as an additional input for the project detection. - - Note that enabling this property might have an impact on performance. - ''; - }; - auxDirectory = mkOption { - type = str; - default = "."; - description = '' - When not using latexmk, provides a way to define the directory containing the .aux files. - Note that you need to set the aux directory in latex.build.args too. - - When using a latexmkrc file, texlab will automatically infer the correct setting. - ''; - }; - logDirectory = mkOption { - type = str; - default = "."; - description = '' - When not using latexmk, provides a way to define the directory containing the build log files. - Note that you need to change the output directory in your build arguments too. - - When using a latexmkrc file, texlab will automatically infer the correct setting. - ''; - }; - pdfDirectory = mkOption { - type = str; - default = "."; - description = '' - When not using latexmk, provides a way to define the directory containing the output files. - Note that you need to set the output directory in latex.build.args too. - - When using a latexmkrc file, texlab will automatically infer the correct setting. - ''; - }; - filename = mkOption { - type = str; - default = ""; - description = '' - Allows overriding the default file name of the build artifact. This setting is used to find the correct PDF file to open during forward search. - ''; - }; - }; - - forwardSearch = { - enable = mkOption { - type = bool; - default = false; - example = true; - description = '' - 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. - ''; - }; - }; - - extraLuaSettings = mkOption { - type = str; - default = ""; - example = '' - formatterLineLength = 80, - ''; - description = '' - For any options that do not have options provided through nvf this can be used to add them. - Options already declared in nvf config will NOT be overridden. - - Options will be placed in: - ``` - lspconfig.texlab.setup { - settings = { - texlab = { - ... - - ... - - } - } - } - ``` - ''; - }; - }; - - # Add other LSPs here - }; + # lsp = { + # texlab = { + # enable = mkEnableLspOption "Whether to enable Tex LSP support (texlab)"; + # + # package = mkOption { + # type = package; + # default = pkgs.texlab; + # description = "texlab package"; + # }; + # + # build = { + # tectonic = { + # enable = mkEnableDefaultOption true "Whether to enable Tex Compilation Via Tectonic"; + # + # package = mkOption { + # type = package; + # default = pkgs.tectonic; + # description = "tectonic package"; + # }; + # + # executable = mkOption { + # type = str; + # default = "tectonic"; + # description = "The executable name from the build package that will be used to build/compile the tex."; + # }; + # + # # -- Flags -- + # keepIntermediates = mkEnableDefaultOption false '' + # Keep the intermediate files generated during processing. + # + # If texlab is reporting build errors when there shouldn't be, disable this option. + # ''; + # keepLogs = mkEnableDefaultOption true '' + # Keep the log files generated during processing. + # + # Without the keepLogs flag, texlab won't be able to report compilation warnings. + # ''; + # onlyCached = mkEnableDefaultOption false "Use only resource files cached locally"; + # synctex = mkEnableDefaultOption true "Generate SyncTeX data"; + # untrustedInput = mkEnableDefaultOption false "Input is untrusted -- disable all known-insecure features"; + # + # # -- Options -- + # reruns = mkOption { + # type = ints.unsigned; + # default = 0; + # example = 2; + # description = "Rerun the TeX engine exactly this many times after the first"; + # }; + # + # bundle = mkOption { + # type = str; + # default = ""; + # description = "Use this directory or Zip-format bundle file to find resource files instead of the default"; + # }; + # + # webBundle = mkOption { + # type = str; + # default = ""; + # description = "Use this URL to find resource files instead of the default"; + # }; + # + # outfmt = mkOption { + # type = enum [ + # "pdf" + # "html" + # "xdv" + # "aux" + # "fmt" + # "" + # ]; + # default = ""; + # description = "The kind of output to generate"; + # }; + # + # hidePaths = mkOption { + # type = listOf str; + # default = []; + # example = [ + # "./secrets.tex" + # "./passwords.tex" + # ]; + # description = "Tell the engine that no file at exists, if it tries to read it."; + # }; + # + # format = mkOption { + # type = str; + # default = ""; + # description = "The name of the \"format\" file used to initialize the TeX engine"; + # }; + # + # color = mkOption { + # type = enum [ + # "always" + # "auto" + # "never" + # "" + # ]; + # default = ""; + # example = "always"; + # description = "Enable/disable colorful log output"; + # }; + # + # extraOptions = { + # type = listOf str; + # default = []; + # description = '' + # Add extra command line options to include in the tectonic build command. + # Extra options added here will not overwrite the options set in as nvf options. + # ''; + # }; + # }; + # + # custom = { + # enable = mkEnableDefaultOption false "Whether to enable using a custom build package"; + # package = mkOption { + # type = package; + # default = pkgs.tectonic; + # description = "build/compiler package"; + # }; + # executable = mkOption { + # type = str; + # default = "tectonic"; + # description = "The executable name from the build package that will be used to build/compile the tex."; + # }; + # args = mkOption { + # type = listOf str; + # default = [ + # "-X" + # "compile" + # "%f" + # "--synctex" + # "--keep-logs" + # "--keep-intermediates" + # ]; + # description = '' + # Defines additional arguments that are passed to the configured LaTeX build tool. + # Note that flags and their arguments need to be separate elements in this array. + # To pass the arguments -foo bar to a build tool, args needs to be ["-foo" "bar"]. + # The placeholder `%f` will be replaced by the server. + # + # Placeholders: + # - `%f`: The path of the TeX file to compile. + # ''; + # }; + # }; + # + # forwardSearchAfter = mkOption { + # type = bool; + # default = false; + # description = "Set this property to true if you want to execute a forward search after a build."; + # }; + # onSave = mkOption { + # type = bool; + # default = false; + # description = "Set this property to true if you want to compile the project after saving a file."; + # }; + # useFileList = mkOption { + # type = bool; + # default = false; + # description = '' + # When set to true, the server will use the .fls files produced by the TeX engine as an additional input for the project detection. + # + # Note that enabling this property might have an impact on performance. + # ''; + # }; + # auxDirectory = mkOption { + # type = str; + # default = "."; + # description = '' + # When not using latexmk, provides a way to define the directory containing the .aux files. + # Note that you need to set the aux directory in latex.build.args too. + # + # When using a latexmkrc file, texlab will automatically infer the correct setting. + # ''; + # }; + # logDirectory = mkOption { + # type = str; + # default = "."; + # description = '' + # When not using latexmk, provides a way to define the directory containing the build log files. + # Note that you need to change the output directory in your build arguments too. + # + # When using a latexmkrc file, texlab will automatically infer the correct setting. + # ''; + # }; + # pdfDirectory = mkOption { + # type = str; + # default = "."; + # description = '' + # When not using latexmk, provides a way to define the directory containing the output files. + # Note that you need to set the output directory in latex.build.args too. + # + # When using a latexmkrc file, texlab will automatically infer the correct setting. + # ''; + # }; + # filename = mkOption { + # type = str; + # default = ""; + # description = '' + # Allows overriding the default file name of the build artifact. This setting is used to find the correct PDF file to open during forward search. + # ''; + # }; + # }; + # + # forwardSearch = { + # enable = mkOption { + # type = bool; + # default = false; + # example = true; + # description = '' + # 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. + # ''; + # }; + # }; + # + # extraLuaSettings = mkOption { + # type = str; + # default = ""; + # example = '' + # formatterLineLength = 80, + # ''; + # description = '' + # For any options that do not have options provided through nvf this can be used to add them. + # Options already declared in nvf config will NOT be overridden. + # + # Options will be placed in: + # ``` + # lspconfig.texlab.setup { + # settings = { + # texlab = { + # ... + # + # ... + # + # } + # } + # } + # ``` + # ''; + # }; + # }; + # + # # Add other LSPs here + # }; extraOpts = { texFlavor = { @@ -501,120 +502,120 @@ in { # }) # LSP - (mkIf (any (x: x.enable) (attrValues cfg.lsp)) ( - { - vim.lsp.lspconfig.enable = true; - } # Enable lspconfig when any of the lsps are enabled - // (mkMerge [ - # ----- Texlab ----- - ( - let - tl = cfg.lsp.texlab; - build = tl.build; - - listToLua = list: nullOnEmpty: - if length list == 0 - then - if nullOnEmpty - then "null" - else "{ }" - else "{ ${concatStringsSep ", " (map (x: ''"${toString x}"'') list)} }"; - - stringToLua = string: nullOnEmpty: - if string == "" - then - if nullOnEmpty - then "null" - else "" - else ''"${string}"''; - - boolToLua = boolean: - if boolean - then "true" - else "false"; - - # -- Build -- - buildConfig = let - # This function will sort through the builder options of ...texlab.build and count how many - # builders have been enabled and get the attrs of the last enabled builder. - getBuilder = { - enabledBuildersCount ? 0, - enabledBuilderName ? "", - index ? 0, - builderNamesList ? ( - filter ( - x: let - y = tl.build.${x}; - in (isAttrs y && hasAttr "enable" y) - ) (attrNames tl.build) - ), - }: let - currentBuilderName = elemAt builderNamesList index; - currentBuilder = tl.build.${currentBuilderName}; - nextIndex = index + 1; - currentState = { - enabledBuildersCount = - if currentBuilder.enable - then enabledBuildersCount + 1 - else enabledBuildersCount; - enabledBuilderName = - if currentBuilder.enable - then currentBuilderName - else enabledBuilderName; - }; - in - if length builderNamesList > nextIndex - then - getBuilder ({ - inherit builderNamesList; - index = nextIndex; - } - // currentState) - else currentState; - - getBuilderResults = getBuilder {}; - builder = tl.build.${getBuilderResults.enabledBuilderName}; - builderArgs = collateArgs.lsp.texlab.build.${getBuilderResults.enabledBuilderName} tl.build; - in - if getBuilderResults.enabledBuildersCount == 0 - then "" - else if getBuilderResults.enabledBuildersCount > 1 - then throw "Texlab does not support having more than 1 builders enabled!" - else '' - build = { - executable = "${builder.package}/bin/${builder.executable}", - args = ${listToLua builderArgs false}, - forwardSearchAfter = ${boolToLua build.forwardSearchAfter}, - onSave = ${boolToLua build.onSave}, - useFileList = ${boolToLua build.useFileList}, - auxDirectory = ${stringToLua build.auxDirectory true}, - logDirectory = ${stringToLua build.logDirectory true}, - pdfDirectory = ${stringToLua build.pdfDirectory true}, - filename = ${stringToLua build.filename true}, - }, - ''; - in (mkIf tl.enable { - vim.lsp.lspconfig.sources.texlab = '' - lspconfig.texlab.setup { - cmd = { "${tl.package}/bin/texlab" }, - settings = { - texlab = { - ${buildConfig} - forwardSearch = { - executable = "${tl.forwardSearch.package}/bin/${tl.forwardSearch.executable}", - args = ${listToLua tl.forwardSearch.args true} - }, - ${tl.extraLuaSettings} - } - } - } - ''; - }) - ) - - # Add other LSPs here - ]) - )) + # (mkIf (any (x: x.enable) (attrValues cfg.lsp)) ( + # { + # vim.lsp.lspconfig.enable = true; + # } # Enable lspconfig when any of the lsps are enabled + # // (mkMerge [ + # # ----- Texlab ----- + # ( + # let + # tl = cfg.lsp.texlab; + # build = tl.build; + # + # listToLua = list: nullOnEmpty: + # if length list == 0 + # then + # if nullOnEmpty + # then "null" + # else "{ }" + # else "{ ${concatStringsSep ", " (map (x: ''"${toString x}"'') list)} }"; + # + # stringToLua = string: nullOnEmpty: + # if string == "" + # then + # if nullOnEmpty + # then "null" + # else "" + # else ''"${string}"''; + # + # boolToLua = boolean: + # if boolean + # then "true" + # else "false"; + # + # # -- Build -- + # buildConfig = let + # # This function will sort through the builder options of ...texlab.build and count how many + # # builders have been enabled and get the attrs of the last enabled builder. + # getBuilder = { + # enabledBuildersCount ? 0, + # enabledBuilderName ? "", + # index ? 0, + # builderNamesList ? ( + # filter ( + # x: let + # y = tl.build.${x}; + # in (isAttrs y && hasAttr "enable" y) + # ) (attrNames tl.build) + # ), + # }: let + # currentBuilderName = elemAt builderNamesList index; + # currentBuilder = tl.build.${currentBuilderName}; + # nextIndex = index + 1; + # currentState = { + # enabledBuildersCount = + # if currentBuilder.enable + # then enabledBuildersCount + 1 + # else enabledBuildersCount; + # enabledBuilderName = + # if currentBuilder.enable + # then currentBuilderName + # else enabledBuilderName; + # }; + # in + # if length builderNamesList > nextIndex + # then + # getBuilder ({ + # inherit builderNamesList; + # index = nextIndex; + # } + # // currentState) + # else currentState; + # + # getBuilderResults = getBuilder {}; + # builder = tl.build.${getBuilderResults.enabledBuilderName}; + # builderArgs = collateArgs.lsp.texlab.build.${getBuilderResults.enabledBuilderName} tl.build; + # in + # if getBuilderResults.enabledBuildersCount == 0 + # then "" + # else if getBuilderResults.enabledBuildersCount > 1 + # then throw "Texlab does not support having more than 1 builders enabled!" + # else '' + # build = { + # executable = "${builder.package}/bin/${builder.executable}", + # args = ${listToLua builderArgs false}, + # forwardSearchAfter = ${boolToLua build.forwardSearchAfter}, + # onSave = ${boolToLua build.onSave}, + # useFileList = ${boolToLua build.useFileList}, + # auxDirectory = ${stringToLua build.auxDirectory true}, + # logDirectory = ${stringToLua build.logDirectory true}, + # pdfDirectory = ${stringToLua build.pdfDirectory true}, + # filename = ${stringToLua build.filename true}, + # }, + # ''; + # in (mkIf tl.enable { + # vim.lsp.lspconfig.sources.texlab = '' + # lspconfig.texlab.setup { + # cmd = { "${tl.package}/bin/texlab" }, + # settings = { + # texlab = { + # ${buildConfig} + # forwardSearch = { + # executable = "${tl.forwardSearch.package}/bin/${tl.forwardSearch.executable}", + # args = ${listToLua tl.forwardSearch.args true} + # }, + # ${tl.extraLuaSettings} + # } + # } + # } + # ''; + # }) + # ) + # + # # Add other LSPs here + # ]) + # )) # Extra Lua config options (mkIf cfg.extraOpts.texFlavor.enable { diff --git a/modules/plugins/languages/tex/lsp/default.nix b/modules/plugins/languages/tex/lsp/default.nix new file mode 100644 index 00000000..3c48d515 --- /dev/null +++ b/modules/plugins/languages/tex/lsp/default.nix @@ -0,0 +1,528 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (lib.options) mkEnableOption mkOption; + inherit (lib.modules) mkIf mkMerge; + inherit + (lib.types) + bool + either + enum + ints + listOf + oneOf + package + str + ; + inherit (lib.nvim.types) mkGrammarOption; + inherit + (builtins) + any + attrNames + attrValues + concatLists + concatStringsSep + elem + elemAt + filter + hasAttr + isAttrs + length + map + throw + toString + ; + + cfg = config.vim.languages.tex; + + # --- Enable Options --- + mkEnableDefaultOption = default: description: (mkOption { + type = bool; + default = default; + example = !default; + description = description; + }); + mkEnableLspOption = mkEnableDefaultOption config.vim.languages.enableLSP; + + # --- Arg Collation Functions -- + collateArgs.lsp.texlab.build = { + tectonic = buildConfig: let + selfConfig = buildConfig.tectonic; + in ( + # Base args + [ + "-X" + "compile" + "%f" + ] + # Flags + ++ ( + if selfConfig.keepIntermediates + then ["--keep-intermediates"] + else [] + ) + ++ ( + if selfConfig.keepLogs + then ["--keep-logs"] + else [] + ) + ++ ( + if selfConfig.onlyCached + then ["--only-cached"] + else [] + ) + ++ ( + if selfConfig.synctex + then ["--synctex"] + else [] + ) + ++ ( + if selfConfig.untrustedInput + then ["--untrusted"] + else [] + ) + # Options + ++ ( + if selfConfig.reruns > 0 + then ["--reruns" "${toString selfConfig.reruns}"] + else [] + ) + ++ ( + if selfConfig.bundle != "" + then ["--bundle" "${toString selfConfig.bundle}"] + else [] + ) + ++ ( + if selfConfig.webBundle != "" + then ["--web-bundle" "${toString selfConfig.webBundle}"] + else [] + ) + ++ ( + if selfConfig.outfmt != "" + then ["--outfmt" "${toString selfConfig.outfmt}"] + else [] + ) + ++ (concatLists (map (x: ["--hide" x]) selfConfig.hidePaths)) + ++ ( + if selfConfig.format != "" + then ["--format" "${toString selfConfig.format}"] + else [] + ) + ++ ( + if selfConfig.color != "" + then ["--color" "${toString selfConfig.color}"] + else [] + ) + # Still options but these are not defined by builder specific options but + # instead synchronize options between the global build options and builder + # specific options + ++ ( + if !(elem buildConfig.pdfDirectory ["." ""]) + then ["--outdir" "${buildConfig.pdfDirectory}"] + else [] + ) + ); + custom = buildConfig: buildConfig.custom.args; + }; +in { + options.vim.languages.tex.lsp = { + texlab = { + enable = mkEnableLspOption "Whether to enable Tex LSP support (texlab)"; + + package = mkOption { + type = package; + default = pkgs.texlab; + description = "texlab package"; + }; + + build = { + tectonic = { + enable = mkEnableDefaultOption true "Whether to enable Tex Compilation Via Tectonic"; + + package = mkOption { + type = package; + default = pkgs.tectonic; + description = "tectonic package"; + }; + + executable = mkOption { + type = str; + default = "tectonic"; + description = "The executable name from the build package that will be used to build/compile the tex."; + }; + + # -- Flags -- + keepIntermediates = mkEnableDefaultOption false '' + Keep the intermediate files generated during processing. + + If texlab is reporting build errors when there shouldn't be, disable this option. + ''; + keepLogs = mkEnableDefaultOption true '' + Keep the log files generated during processing. + + Without the keepLogs flag, texlab won't be able to report compilation warnings. + ''; + onlyCached = mkEnableDefaultOption false "Use only resource files cached locally"; + synctex = mkEnableDefaultOption true "Generate SyncTeX data"; + untrustedInput = mkEnableDefaultOption false "Input is untrusted -- disable all known-insecure features"; + + # -- Options -- + reruns = mkOption { + type = ints.unsigned; + default = 0; + example = 2; + description = "Rerun the TeX engine exactly this many times after the first"; + }; + + bundle = mkOption { + type = str; + default = ""; + description = "Use this directory or Zip-format bundle file to find resource files instead of the default"; + }; + + webBundle = mkOption { + type = str; + default = ""; + description = "Use this URL to find resource files instead of the default"; + }; + + outfmt = mkOption { + type = enum [ + "pdf" + "html" + "xdv" + "aux" + "fmt" + "" + ]; + default = ""; + description = "The kind of output to generate"; + }; + + hidePaths = mkOption { + type = listOf str; + default = []; + example = [ + "./secrets.tex" + "./passwords.tex" + ]; + description = "Tell the engine that no file at exists, if it tries to read it."; + }; + + format = mkOption { + type = str; + default = ""; + description = "The name of the \"format\" file used to initialize the TeX engine"; + }; + + color = mkOption { + type = enum [ + "always" + "auto" + "never" + "" + ]; + default = ""; + example = "always"; + description = "Enable/disable colorful log output"; + }; + + extraOptions = { + type = listOf str; + default = []; + description = '' + Add extra command line options to include in the tectonic build command. + Extra options added here will not overwrite the options set in as nvf options. + ''; + }; + }; + + custom = { + enable = mkEnableDefaultOption false "Whether to enable using a custom build package"; + package = mkOption { + type = package; + default = pkgs.tectonic; + description = "build/compiler package"; + }; + executable = mkOption { + type = str; + default = "tectonic"; + description = "The executable name from the build package that will be used to build/compile the tex."; + }; + args = mkOption { + type = listOf str; + default = [ + "-X" + "compile" + "%f" + "--synctex" + "--keep-logs" + "--keep-intermediates" + ]; + description = '' + Defines additional arguments that are passed to the configured LaTeX build tool. + Note that flags and their arguments need to be separate elements in this array. + To pass the arguments -foo bar to a build tool, args needs to be ["-foo" "bar"]. + The placeholder `%f` will be replaced by the server. + + Placeholders: + - `%f`: The path of the TeX file to compile. + ''; + }; + }; + + forwardSearchAfter = mkOption { + type = bool; + default = false; + description = "Set this property to true if you want to execute a forward search after a build."; + }; + onSave = mkOption { + type = bool; + default = false; + description = "Set this property to true if you want to compile the project after saving a file."; + }; + useFileList = mkOption { + type = bool; + default = false; + description = '' + When set to true, the server will use the .fls files produced by the TeX engine as an additional input for the project detection. + + Note that enabling this property might have an impact on performance. + ''; + }; + auxDirectory = mkOption { + type = str; + default = "."; + description = '' + When not using latexmk, provides a way to define the directory containing the .aux files. + Note that you need to set the aux directory in latex.build.args too. + + When using a latexmkrc file, texlab will automatically infer the correct setting. + ''; + }; + logDirectory = mkOption { + type = str; + default = "."; + description = '' + When not using latexmk, provides a way to define the directory containing the build log files. + Note that you need to change the output directory in your build arguments too. + + When using a latexmkrc file, texlab will automatically infer the correct setting. + ''; + }; + pdfDirectory = mkOption { + type = str; + default = "."; + description = '' + When not using latexmk, provides a way to define the directory containing the output files. + Note that you need to set the output directory in latex.build.args too. + + When using a latexmkrc file, texlab will automatically infer the correct setting. + ''; + }; + filename = mkOption { + type = str; + default = ""; + description = '' + Allows overriding the default file name of the build artifact. This setting is used to find the correct PDF file to open during forward search. + ''; + }; + }; + + forwardSearch = { + enable = mkOption { + type = bool; + default = false; + example = true; + description = '' + 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. + ''; + }; + }; + + extraLuaSettings = mkOption { + type = str; + default = ""; + example = '' + formatterLineLength = 80, + ''; + description = '' + For any options that do not have options provided through nvf this can be used to add them. + Options already declared in nvf config will NOT be overridden. + + Options will be placed in: + ``` + lspconfig.texlab.setup { + settings = { + texlab = { + ... + + ... + + } + } + } + ``` + ''; + }; + }; + + # Add other LSPs here + }; + + # config = mkIf cfg.enable (mkMerge [ + # (mkIf (any (x: x.enable) (attrValues cfg.lsp)) ( + config = mkIf (cfg.enable && (any (x: x.enable) (attrValues cfg.lsp))) ( + { + vim.lsp.lspconfig.enable = true; + } # Enable lspconfig when any of the lsps are enabled + // (mkMerge [ + # ----- Texlab ----- + ( + let + tl = cfg.lsp.texlab; + build = tl.build; + + listToLua = list: nullOnEmpty: + if length list == 0 + then + if nullOnEmpty + then "null" + else "{ }" + else "{ ${concatStringsSep ", " (map (x: ''"${toString x}"'') list)} }"; + + stringToLua = string: nullOnEmpty: + if string == "" + then + if nullOnEmpty + then "null" + else "" + else ''"${string}"''; + + boolToLua = boolean: + if boolean + then "true" + else "false"; + + # -- Build -- + buildConfig = let + # This function will sort through the builder options of ...texlab.build and count how many + # builders have been enabled and get the attrs of the last enabled builder. + getBuilder = { + enabledBuildersCount ? 0, + enabledBuilderName ? "", + index ? 0, + builderNamesList ? ( + filter ( + x: let + y = tl.build.${x}; + in (isAttrs y && hasAttr "enable" y) + ) (attrNames tl.build) + ), + }: let + currentBuilderName = elemAt builderNamesList index; + currentBuilder = tl.build.${currentBuilderName}; + nextIndex = index + 1; + currentState = { + enabledBuildersCount = + if currentBuilder.enable + then enabledBuildersCount + 1 + else enabledBuildersCount; + enabledBuilderName = + if currentBuilder.enable + then currentBuilderName + else enabledBuilderName; + }; + in + if length builderNamesList > nextIndex + then + getBuilder ({ + inherit builderNamesList; + index = nextIndex; + } + // currentState) + else currentState; + + getBuilderResults = getBuilder {}; + builder = tl.build.${getBuilderResults.enabledBuilderName}; + builderArgs = collateArgs.lsp.texlab.build.${getBuilderResults.enabledBuilderName} tl.build; + in + if getBuilderResults.enabledBuildersCount == 0 + then "" + else if getBuilderResults.enabledBuildersCount > 1 + then throw "Texlab does not support having more than 1 builders enabled!" + else '' + build = { + executable = "${builder.package}/bin/${builder.executable}", + args = ${listToLua builderArgs false}, + forwardSearchAfter = ${boolToLua build.forwardSearchAfter}, + onSave = ${boolToLua build.onSave}, + useFileList = ${boolToLua build.useFileList}, + auxDirectory = ${stringToLua build.auxDirectory true}, + logDirectory = ${stringToLua build.logDirectory true}, + pdfDirectory = ${stringToLua build.pdfDirectory true}, + filename = ${stringToLua build.filename true}, + }, + ''; + in (mkIf tl.enable { + vim.lsp.lspconfig.sources.texlab = '' + lspconfig.texlab.setup { + cmd = { "${tl.package}/bin/texlab" }, + settings = { + texlab = { + ${buildConfig} + forwardSearch = { + executable = "${tl.forwardSearch.package}/bin/${tl.forwardSearch.executable}", + args = ${listToLua tl.forwardSearch.args true} + }, + ${tl.extraLuaSettings} + } + } + } + ''; + }) + ) + + # Add other LSPs here + ]) + ); +}