diff --git a/modules/plugins/languages/default.nix b/modules/plugins/languages/default.nix index 4f717309..5313ccba 100644 --- a/modules/plugins/languages/default.nix +++ b/modules/plugins/languages/default.nix @@ -35,7 +35,7 @@ in { ./svelte.nix ./tailwind.nix ./terraform.nix - ./tex.nix + ./tex ./ts.nix ./typst.nix ./vala.nix diff --git a/modules/plugins/languages/tex.nix b/modules/plugins/languages/tex.nix deleted file mode 100644 index 6fb6e845..00000000 --- a/modules/plugins/languages/tex.nix +++ /dev/null @@ -1,359 +0,0 @@ -# TODO: -# - Add Texlab LSP settings: -# - chktex -# - diagnosticsDelay -# - diagnostics -# - symbols -# - formatterLineLength -# - bibtexFormatter -# - latexFormatter -# - latexindent -# - completion -# - inlayHints -# - experimental -{ - config, - pkgs, - lib, - ... -}: let - inherit (lib.options) mkEnableOption mkOption; - inherit (lib.modules) mkIf mkMerge; - inherit - (lib.types) - bool - listOf - package - str - ; - inherit (lib.nvim.types) mkGrammarOption; - inherit - (builtins) - any - attrValues - concatStringsSep - length - map - toString - ; - - cfg = config.vim.languages.tex; -in { - options.vim.languages.tex = { - enable = mkEnableOption "Tex support"; - - # Treesitter options for latex and bibtex flavours of tex. - treesitter = { - latex = { - enable = mkEnableOption "Latex treesitter" // { default = config.vim.languages.enableTreesitter; }; - package = mkGrammarOption pkgs "latex"; - }; - bibtex = { - enable = mkEnableOption "Bibtex treesitter" // { default = config.vim.languages.enableTreesitter; }; - package = mkGrammarOption pkgs "bibtex"; - }; - }; - - # LSP options - # Because tex LSPs also including building/compiling tex, they have - # more options that are only specific to them and thus it makes - # 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 = mkEnableOption "Tex LSP support (texlab)" // { default = config.vim.languages.enableLSP; }; - - package = mkOption { - type = package; - default = pkgs.texlab; - description = "texlab package"; - }; - - build = { - 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 = { - enable = mkOption { - type = bool; - default = false; - example = true; - description = '' - Whether to set the vim.g.tex_flavor (g:tex_flavor) option in your lua config. - - When opening a .tex file vim will try to automatically try to determine the file type from - the three options: plaintex (for plain TeX), context (for ConTeXt), or tex (for LaTeX). - This can either be done by a indicator line of the form `%&` on the first line or - if absent vim will search the file for keywords to try and determine the filetype. - If no filetype can be determined automatically then by default it will fallback to plaintex. - - This option will enable setting the tex flavor in your lua config and you can set its value - useing the `vim.languages.tex.lsp.extraOpts.texFlavor.flavor = ` in your nvf config. - - Setting this option to `false` will omit the `vim.g.tex_flavor = ` line from your lua - config entirely (unless you manually set it elsewhere of course). - ''; - }; - flavor = mkOption { - type = str; - default = "plaintex"; - example = "tex"; - description = '' - The flavor to set as a fallback for when vim cannot automatically determine the tex flavor when - opening a .tex document. - - The options are: plaintex (for plain TeX), context (for ConTeXt), or tex (for LaTeX). - - This can be particularly useful for when using `vim.utility.new-file-template` options for - creating templates when no context has yet been added to a new file. - ''; - }; - }; - }; - }; - - config = mkIf cfg.enable (mkMerge [ - # Treesitter - (mkIf cfg.treesitter.latex.enable { - vim.treesitter.enable = true; - vim.treesitter.grammars = [cfg.treesitter.latex.package]; - }) - (mkIf cfg.treesitter.bibtex.enable { - vim.treesitter.enable = true; - vim.treesitter.grammars = [cfg.treesitter.bibtex.package]; - }) - - # 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"; - - in (mkIf tl.enable { - vim.lsp.lspconfig.sources.texlab = '' - lspconfig.texlab.setup { - cmd = { "${tl.package}/bin/texlab" }, - settings = { - texlab = { - build = { - executable = "${build.package}/bin/${build.executable}", - args = ${listToLua build.args 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}, - }, - 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 { - vim.globals.tex_flavor = "${cfg.extraOpts.texFlavor.flavor}"; - }) - ]); -} diff --git a/modules/plugins/languages/tex/build/builders/custom.nix b/modules/plugins/languages/tex/build/builders/custom.nix new file mode 100644 index 00000000..680d37a9 --- /dev/null +++ b/modules/plugins/languages/tex/build/builders/custom.nix @@ -0,0 +1,86 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (lib.options) mkOption; + inherit (lib.modules) mkIf; + inherit + (lib.types) + bool + enum + ints + listOf + package + str + ; + inherit + (builtins) + attrNames + 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; + }); + + collateArgs = buildConfig: buildConfig.builders.custom.args; +in { + options.vim.languages.tex.build.builders.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. + ''; + }; + }; + + config = mkIf (cfg.enable && cfg.build.builders.custom.enable) { + vim.languages.tex.build.builder = { + name = "custom"; + args = collateArgs cfg.build; + }; + }; +} diff --git a/modules/plugins/languages/tex/build/builders/default.nix b/modules/plugins/languages/tex/build/builders/default.nix new file mode 100644 index 00000000..da00fbdb --- /dev/null +++ b/modules/plugins/languages/tex/build/builders/default.nix @@ -0,0 +1,65 @@ +{ + config, + pkgs, + lib, + ... +}: +let + inherit (lib.options) mkOption mkEnableOption; + inherit + (lib.types) + bool + enum + ints + listOf + package + str + ; + inherit + (builtins) + attrNames + concatLists + concatStringsSep + elem + elemAt + filter + hasAttr + isAttrs + length + map + throw + toString + ; + + cfg = config.vim.languages.tex; +in +{ + imports = [ + ./custom.nix + ./tectonic.nix + ]; + + options.vim.languages.tex.build.builder = { + name = mkOption { + type = enum (attrNames cfg.build.builders); + default = "tectonic"; + description = "The tex builder to use"; + }; + args = mkOption { + type = listOf str; + default = []; + description = "The list of args to pass to the builder"; + }; + package = mkOption { + type = package; + default = cfg.build.builders.tectonic.package; + description = "The tex builder package to use"; + }; + executable = mkOption { + type = str; + default = cfg.build.builders.tectonic.executable; + description = "The tex builder executable to use"; + }; + }; +} + diff --git a/modules/plugins/languages/tex/build/builders/tectonic.nix b/modules/plugins/languages/tex/build/builders/tectonic.nix new file mode 100644 index 00000000..0ec8bea0 --- /dev/null +++ b/modules/plugins/languages/tex/build/builders/tectonic.nix @@ -0,0 +1,231 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (lib.options) mkOption mkEnableOption; + inherit (lib.modules) mkIf; + inherit + (lib.types) + bool + enum + ints + listOf + package + str + ; + inherit + (builtins) + attrNames + 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 = buildConfig: let + selfConfig = buildConfig.builders.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 [] + ) + ); +in { + options.vim.languages.tex.build.builders.tectonic = { + enable = mkEnableOption "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. + ''; + }; + }; + + config = mkIf (cfg.enable && cfg.build.builders.tectonic.enable) { + vim.languages.tex.build.builder = { + name = "tectonic"; + args = collateArgs cfg.build; + package = cfg.build.builders.tectonic.package; + }; + }; +} diff --git a/modules/plugins/languages/tex/build/default.nix b/modules/plugins/languages/tex/build/default.nix new file mode 100644 index 00000000..0882565e --- /dev/null +++ b/modules/plugins/languages/tex/build/default.nix @@ -0,0 +1,107 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (lib.options) mkOption; + inherit (lib.modules) mkIf; + inherit + (lib.types) + bool + enum + ints + listOf + package + str + ; + inherit + (builtins) + attrNames + 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; +in { + imports = [ + ./builders + ]; + + options.vim.languages.tex.build = { + 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. + ''; + }; + }; +} diff --git a/modules/plugins/languages/tex/default.nix b/modules/plugins/languages/tex/default.nix new file mode 100644 index 00000000..98ac5f1c --- /dev/null +++ b/modules/plugins/languages/tex/default.nix @@ -0,0 +1,67 @@ +{ + config, + lib, + ... +}: let + inherit (lib.modules) mkIf mkMerge; + inherit (lib.options) mkEnableOption mkOption; + inherit (lib.types) bool str; + + cfg = config.vim.languages.tex; +in { + imports = [ + ./treesitter.nix + ./lsp + ./build + ]; + + options.vim.languages.tex = { + enable = mkEnableOption "Tex support"; + + extraOpts = { + texFlavor = { + enable = mkOption { + type = bool; + default = false; + example = true; + description = '' + Whether to set the vim.g.tex_flavor (g:tex_flavor) option in your lua config. + + When opening a .tex file vim will try to automatically try to determine the file type from + the three options: plaintex (for plain TeX), context (for ConTeXt), or tex (for LaTeX). + This can either be done by a indicator line of the form `%&` on the first line or + if absent vim will search the file for keywords to try and determine the filetype. + If no filetype can be determined automatically then by default it will fallback to plaintex. + + This option will enable setting the tex flavor in your lua config and you can set its value + useing the `vim.languages.tex.lsp.extraOpts.texFlavor.flavor = ` in your nvf config. + + Setting this option to `false` will omit the `vim.g.tex_flavor = ` line from your lua + config entirely (unless you manually set it elsewhere of course). + ''; + }; + flavor = mkOption { + type = str; + default = "plaintex"; + example = "tex"; + description = '' + The flavor to set as a fallback for when vim cannot automatically determine the tex flavor when + opening a .tex document. + + The options are: plaintex (for plain TeX), context (for ConTeXt), or tex (for LaTeX). + + This can be particularly useful for when using `vim.utility.new-file-template` options for + creating templates when no context has yet been added to a new file. + ''; + }; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + # Extra Lua config options + (mkIf cfg.extraOpts.texFlavor.enable { + vim.globals.tex_flavor = "${cfg.extraOpts.texFlavor.flavor}"; + }) + ]); +} diff --git a/modules/plugins/languages/tex/lsp/default.nix b/modules/plugins/languages/tex/lsp/default.nix new file mode 100644 index 00000000..affb272a --- /dev/null +++ b/modules/plugins/languages/tex/lsp/default.nix @@ -0,0 +1,18 @@ +{ + config, + lib, + ... +}: let + inherit (lib.modules) mkIf; + inherit (builtins) any attrValues; + + cfg = config.vim.languages.tex; +in { + imports = [ + ./texlab.nix + ]; + + 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 + }; +} diff --git a/modules/plugins/languages/tex/lsp/texlab.nix b/modules/plugins/languages/tex/lsp/texlab.nix new file mode 100644 index 00000000..498a6017 --- /dev/null +++ b/modules/plugins/languages/tex/lsp/texlab.nix @@ -0,0 +1,548 @@ +# TODO: +# - Add Texlab LSP settings: +# - chktex +# - diagnosticsDelay +# - diagnostics +# - symbols +# - formatterLineLength +# - bibtexFormatter +# - latexFormatter +# - latexindent +# - completion +# - inlayHints +# - experimental +{ + config, + pkgs, + lib, + ... +}: let + inherit (lib.options) mkOption; + inherit (lib.modules) mkIf; + inherit + (lib.types) + bool + enum + ints + listOf + package + str + ; + inherit + (builtins) + attrNames + 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; # Moved + # }; +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. + # ''; + # }; + # }; + + # # Moved + # 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 = { + ... + + ... + + } + } + } + ``` + ''; + }; + }; + + config = mkIf (cfg.enable && (cfg.lsp.texlab.enable)) ( + let + tl = cfg.lsp.texlab; + builder = cfg.build.builder; + + 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 = cfg.build.builders.${x}; + # in (isAttrs y && hasAttr "enable" y) + # ) (attrNames cfg.build.builders) + # ), + # }: 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; + getEnabledBuildersCount = { + enabledBuildersCount ? 0, + index ? 0, + builderNamesList ? ( + filter ( + x: let + y = cfg.build.builders.${x}; + in (isAttrs y && hasAttr "enable" y) + ) (attrNames cfg.build.builders) + ), + }: let + currentBuilderName = elemAt builderNamesList index; + currentBuilder = cfg.build.builders.${currentBuilderName}; + nextIndex = index + 1; + newEnabledBuildersCount = + if currentBuilder.enable + then enabledBuildersCount + 1 + else enabledBuildersCount; + in + if length builderNamesList > nextIndex + then + getEnabledBuildersCount { + inherit builderNamesList; + enabledBuildersCount = newEnabledBuildersCount; + index = nextIndex; + } + else newEnabledBuildersCount; + + enabledBuildersCount = getEnabledBuildersCount {}; + # builder = tl.build.${getBuilderResults.enabledBuilderName}; + # builderArgs = collateArgs.lsp.texlab.build.${getBuilderResults.enabledBuilderName} tl.build; + in + if enabledBuildersCount == 0 + then "" + else if enabledBuildersCount > 1 + then throw "Texlab does not support having more than 1 builders enabled!" + else '' + build = { + executable = "${builder.package}/bin/${builder.executable}", + args = ${listToLua builder.args false}, + forwardSearchAfter = ${boolToLua cfg.build.forwardSearchAfter}, + onSave = ${boolToLua cfg.build.onSave}, + useFileList = ${boolToLua cfg.build.useFileList}, + auxDirectory = ${stringToLua cfg.build.auxDirectory true}, + logDirectory = ${stringToLua cfg.build.logDirectory true}, + pdfDirectory = ${stringToLua cfg.build.pdfDirectory true}, + filename = ${stringToLua cfg.build.filename true}, + }, + ''; + in { + 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} + } + } + } + ''; + } + ); +} diff --git a/modules/plugins/languages/tex/treesitter.nix b/modules/plugins/languages/tex/treesitter.nix new file mode 100644 index 00000000..44459afb --- /dev/null +++ b/modules/plugins/languages/tex/treesitter.nix @@ -0,0 +1,36 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (lib.options) mkEnableOption; + inherit (lib.modules) mkIf mkMerge; + inherit (lib.nvim.types) mkGrammarOption; + + cfg = config.vim.languages.tex; + + mkEnableTreesitterOption = description: mkEnableOption description // {default = config.vim.languages.enableTreesitter;}; +in { + options.vim.languages.tex.treesitter = { + latex = { + enable = mkEnableTreesitterOption "Whether to enable Latex treesitter"; + package = mkGrammarOption pkgs "latex"; + }; + bibtex = { + enable = mkEnableTreesitterOption "Whether to enable Bibtex treesitter"; + package = mkGrammarOption pkgs "bibtex"; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + (mkIf cfg.treesitter.latex.enable { + vim.treesitter.enable = true; + vim.treesitter.grammars = [cfg.treesitter.latex.package]; + }) + (mkIf cfg.treesitter.bibtex.enable { + vim.treesitter.enable = true; + vim.treesitter.grammars = [cfg.treesitter.bibtex.package]; + }) + ]); +}