diff --git a/docs/manual/configuring/languages.md b/docs/manual/configuring/languages.md index f956a0e3..0b940582 100644 --- a/docs/manual/configuring/languages.md +++ b/docs/manual/configuring/languages.md @@ -79,6 +79,8 @@ languages have sections under the `vim.languages` attribute. [vim.languages.tailwind.enable](./options.html#option-vim-languages-tailwind-enable) - Terraform: [vim.languages.terraform.enable](./options.html#option-vim-languages-terraform-enable) +- Tex: + [vim.languages.tex.enable](./options.html#option-vim-languages-tex-enable) - Typst: [vim.languages.typst.enable](./options.html#option-vim-languages-typst-enable) - Vala: diff --git a/lib/types/default.nix b/lib/types/default.nix index 66adfbbc..352fb89d 100644 --- a/lib/types/default.nix +++ b/lib/types/default.nix @@ -9,6 +9,6 @@ in { inherit (typesDag) dagOf; inherit (typesPlugin) pluginsOpt extraPluginType mkPluginSetupOption luaInline pluginType borderType; - inherit (typesLanguage) diagnostics mkGrammarOption; + inherit (typesLanguage) diagnostics mkGrammarOption mkEnableTreesitterOption; inherit (customTypes) char hexColor mergelessListOf deprecatedSingleOrListOf; } diff --git a/lib/types/languages.nix b/lib/types/languages.nix index b1865c41..5e935159 100644 --- a/lib/types/languages.nix +++ b/lib/types/languages.nix @@ -1,7 +1,7 @@ {lib}: let inherit (lib.options) mkOption mkPackageOption; inherit (lib.attrsets) attrNames; - inherit (lib.types) listOf either enum submodule package; + inherit (lib.types) listOf either enum submodule package bool; diagnosticSubmodule = _: { options = { @@ -32,6 +32,13 @@ mkPackageOption pkgs ["${grammar} treesitter"] { default = ["vimPlugins" "nvim-treesitter" "builtGrammars" grammar]; }; + + mkEnableTreesitterOption = defaultCondition: language: + mkOption { + type = bool; + default = defaultCondition; + description = "Whether to enable ${language} treesitter"; + }; in { - inherit diagnostics diagnosticSubmodule mkGrammarOption; + inherit diagnostics diagnosticSubmodule mkGrammarOption mkEnableTreesitterOption; } diff --git a/modules/plugins/formatter/conform-nvim/builders/default.nix b/modules/plugins/formatter/conform-nvim/builders/default.nix new file mode 100644 index 00000000..4586b768 --- /dev/null +++ b/modules/plugins/formatter/conform-nvim/builders/default.nix @@ -0,0 +1,70 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (lib.options) mkOption; + inherit (lib.types) enum listOf package str; + inherit (lib.nvim.config) mkBool; + inherit (builtins) attrNames filter isAttrs hasAttr elemAt length; + + cfg = config.vim.languages.tex; +in { + imports = [ + ./latexmk.nix + ./tectonic.nix + ]; + + options.vim.languages.tex.build.builder = { + name = mkOption { + type = enum (attrNames cfg.build.builders); + default = "latexmk"; + description = '' + The tex builder to use. + + This is just the default custom option. By setting any of the + builders to true, this will be overwritten by that builder's + parameters. + Setting this parameter to the name of a declared builder will + not automatically enable that builder. + ''; + }; + args = mkOption { + type = listOf str; + default = [ + "-pdf" + "%f" + ]; + description = '' + The list of args to pass to the builder. + + This is just the default custom option. By setting any of the + builders to true, this will be overwritten by that builder's + parameters. + ''; + }; + package = mkOption { + type = package; + default = pkgs.texlive.withPackages (ps: [ps.latexmk]); + description = '' + The tex builder package to use. + + This is just the default custom option. By setting any of the + builders to true, this will be overwritten by that builder's + parameters. + ''; + }; + executable = mkOption { + type = str; + default = "latexmk"; + description = '' + The tex builder executable to use. + + This is just the default custom option. By setting any of the + builders to true, this will be overwritten by that builder's + parameters. + ''; + }; + }; +} diff --git a/modules/plugins/formatter/conform-nvim/builders/latexmk.nix b/modules/plugins/formatter/conform-nvim/builders/latexmk.nix new file mode 100644 index 00000000..510c9561 --- /dev/null +++ b/modules/plugins/formatter/conform-nvim/builders/latexmk.nix @@ -0,0 +1,53 @@ +# TODO: I need testing. +{ + config, + lib, + pkgs, + ... +}: let + # The name of the builder + name = "latexmk"; + + inherit (lib.modules) mkIf; + inherit (lib.nvim.config) mkBool; + inherit (lib.options) mkOption mkEnableOption; + inherit (lib.types) package str; + + texCfg = config.vim.languages.tex; + cfg = texCfg.build.builders.${name}; +in { + options.vim.languages.tex.build.builders.${name} = { + enable = mkEnableOption "Whether to enable Tex Compilation Via latexmk"; + + package = mkOption { + type = package; + default = pkgs.texlive.withPackages (ps: [ps.latexmk]); + description = "latexmk package"; + }; + + executable = mkOption { + type = str; + default = "latexmk"; + description = "The executable name from the build package that will be used to build/compile the tex."; + }; + + # Flag options + pdfOutput = mkBool true "Insure the output file is a pdf."; + }; + + config = mkIf (texCfg.enable && cfg.enable) { + vim.languages.tex.build.builder = { + inherit name; + inherit (cfg) package executable; + args = ( + # Flags + (lib.lists.optional cfg.pdfOutput "-pdf") + # Base args + ++ [ + "-quiet" + "%f" + ] + ); + }; + }; +} diff --git a/modules/plugins/formatter/conform-nvim/builders/tectonic.nix b/modules/plugins/formatter/conform-nvim/builders/tectonic.nix new file mode 100644 index 00000000..0991cff4 --- /dev/null +++ b/modules/plugins/formatter/conform-nvim/builders/tectonic.nix @@ -0,0 +1,161 @@ +{ + config, + lib, + pkgs, + ... +}: let + # The name of the builder + name = "tectonic"; + + inherit (builtins) concatLists elem map toString match; + inherit (lib.modules) mkIf; + inherit (lib.nvim.config) mkBool; + inherit (lib.options) mkOption mkEnableOption; + inherit (lib.strings) toLower optionalString stringAsChars; + inherit (lib.types) enum ints listOf package str; + + texCfg = config.vim.languages.tex; + cfg = texCfg.build.builders.${name}; +in { + options.vim.languages.tex.build.builders.${name} = { + 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 = mkBool false '' + Keep the intermediate files generated during processing. + + If texlab is reporting build errors when there shouldn't be, disable this option. + ''; + keepLogs = mkBool true '' + Keep the log files generated during processing. + + Without the keepLogs flag, texlab won't be able to report compilation warnings. + ''; + onlyCached = mkBool false "Use only resource files cached locally"; + synctex = mkBool true "Generate SyncTeX data"; + untrustedInput = mkBool 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. + + Setting this value to 0 will disable setting this option. + ''; + }; + + 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 (texCfg.enable && cfg.enable) { + vim.languages.tex.build.builder = { + inherit name; + inherit (cfg) package executable; + args = let + inherit (lib.lists) optional optionals; + snakeCaseToKebabCase = str: stringAsChars (x: "${optionalString ((match "[A-Z]" x) != null) "-"}${toLower x}") str; + generateOptionFlag = option: (optionals (cfg.${option} != "") ["--${snakeCaseToKebabCase option}" "${toString cfg.${option}}"]); + in ( + # Base args + [ + "-X" + "compile" + "%f" + ] + # Flags + ++ (optional cfg.keepIntermediates "--keep-intermediates") + ++ (optional cfg.keepLogs "--keep-logs") + ++ (optional cfg.onlyCached "--only-cached") + ++ (optional cfg.synctex "--synctex") + ++ (optional cfg.untrustedInput "--untrusted") + # Options + ++ (optionals (cfg.reruns > 0) ["--reruns" "${toString cfg.reruns}"]) + ++ (generateOptionFlag "bundle") + ++ (generateOptionFlag "webBundle") + ++ (generateOptionFlag "outfmt") + ++ (concatLists (map (x: ["--hide" x]) cfg.hidePaths)) + ++ (generateOptionFlag "format") + ++ (generateOptionFlag "color") + # Still options but these are not defined by builder specific options but + # instead synchronize options between the global build options and builder + # specific options. + ++ (optionals (!(elem texCfg.build.pdfDirectory ["." ""])) ["--outdir" "${texCfg.build.pdfDirectory}"]) + ); + }; + }; +} diff --git a/modules/plugins/languages/default.nix b/modules/plugins/languages/default.nix index fd45758f..5b55224c 100644 --- a/modules/plugins/languages/default.nix +++ b/modules/plugins/languages/default.nix @@ -38,6 +38,7 @@ in { ./svelte.nix ./tailwind.nix ./terraform.nix + ./tex ./ts.nix ./typst.nix ./zig.nix 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..fa51e70a --- /dev/null +++ b/modules/plugins/languages/tex/build/builders/default.nix @@ -0,0 +1,69 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (lib.options) mkOption; + inherit (lib.types) enum listOf package str; + inherit (builtins) attrNames; + + cfg = config.vim.languages.tex; +in { + imports = [ + ./latexmk.nix + ./tectonic.nix + ]; + + options.vim.languages.tex.build.builder = { + name = mkOption { + type = enum (attrNames cfg.build.builders); + default = "latexmk"; + description = '' + The tex builder to use. + + This is just the default custom option. By setting any of the + builders to true, this will be overwritten by that builder's + parameters. + Setting this parameter to the name of a declared builder will + not automatically enable that builder. + ''; + }; + args = mkOption { + type = listOf str; + default = [ + "-pdf" + "%f" + ]; + description = '' + The list of args to pass to the builder. + + This is just the default custom option. By setting any of the + builders to true, this will be overwritten by that builder's + parameters. + ''; + }; + package = mkOption { + type = package; + default = pkgs.texlive.withPackages (ps: [ps.latexmk]); + description = '' + The tex builder package to use. + + This is just the default custom option. By setting any of the + builders to true, this will be overwritten by that builder's + parameters. + ''; + }; + executable = mkOption { + type = str; + default = "latexmk"; + description = '' + The tex builder executable to use. + + This is just the default custom option. By setting any of the + builders to true, this will be overwritten by that builder's + parameters. + ''; + }; + }; +} diff --git a/modules/plugins/languages/tex/build/builders/latexmk.nix b/modules/plugins/languages/tex/build/builders/latexmk.nix new file mode 100644 index 00000000..a840f1b2 --- /dev/null +++ b/modules/plugins/languages/tex/build/builders/latexmk.nix @@ -0,0 +1,57 @@ +# TODO: I need testing. +{ + config, + lib, + pkgs, + ... +}: let + # The name of the builder + name = "latexmk"; + + inherit (lib.modules) mkIf; + inherit (lib.nvim.config) mkBool; + inherit (lib.options) mkOption mkEnableOption; + inherit (lib.types) package str; + + texCfg = config.vim.languages.tex; + cfg = texCfg.build.builders.${name}; +in { + options.vim.languages.tex.build.builders.${name} = { + enable = mkEnableOption "Tex Compilation Via latexmk"; + + package = mkOption { + type = package; + default = pkgs.texlive.withPackages (ps: [ps.latexmk]); + description = "latexmk package"; + }; + + executable = mkOption { + type = str; + default = "latexmk"; + description = '' + The executable name from the build package that will be used to + build/compile the tex. + ''; + }; + + # Flag options + pdfOutput = mkBool true "Insure the output file is a pdf."; + }; + + config = mkIf (texCfg.enable && cfg.enable) { + vim.languages.tex.build.builder = { + inherit name; + inherit (cfg) package executable; + + args = ( + # Flags + (lib.lists.optional cfg.pdfOutput "-pdf") + # Base args + ++ [ + "-quiet" + "%f" + ] + ); + }; + }; +} 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..f65db0ea --- /dev/null +++ b/modules/plugins/languages/tex/build/builders/tectonic.nix @@ -0,0 +1,184 @@ +{ + config, + lib, + pkgs, + ... +}: let + # The name of the builder + name = "tectonic"; + + inherit (builtins) concatLists elem map toString match; + inherit (lib.modules) mkIf; + inherit (lib.nvim.config) mkBool; + inherit (lib.options) mkOption mkEnableOption mkPackageOption; + inherit (lib.strings) toLower optionalString stringAsChars; + inherit (lib.types) enum ints listOf str nullOr; + + texCfg = config.vim.languages.tex; + cfg = texCfg.build.builders.${name}; +in { + options.vim.languages.tex.build.builders.${name} = { + enable = mkEnableOption "Tex Compilation Via Tectonic"; + + package = mkPackageOption pkgs "tectonic" {}; + + 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 = mkBool false '' + Whether to keep the intermediate files generated during processing. + + If texlab is reporting build errors when there shouldn't be, disable + this option. + ''; + keepLogs = mkBool true '' + Whether to keep the log files generated during processing. + + Without the keepLogs flag, texlab won't be able to report compilation + warnings. + ''; + onlyCached = mkBool false '' + Whether to use only resource files cached locally + ''; + synctex = mkBool true "Whether to generate SyncTeX data"; + untrustedInput = mkBool false '' + Whether to disable all known-insecure features if the input is untrusted + ''; + + # -- Options -- + reruns = mkOption { + type = ints.unsigned; + default = 0; + example = 2; + description = '' + How many times to *rerun* the TeX build engine. + The build engine (if a builder is enabled) will always run at least + once. + + Setting this value to 0 will disable setting this option. + ''; + }; + + bundle = mkOption { + type = nullOr str; + default = null; + description = '' + The directory or Zip-format bundle file to find resource files instead + of the default. + ''; + }; + + webBundle = mkOption { + type = nullOr str; + default = null; + description = '' + Use this URL to find resource files instead of the default. + ''; + }; + + outfmt = mkOption { + type = nullOr (enum [ + "pdf" + "html" + "xdv" + "aux" + "fmt" + ]); + default = null; + description = '' + The kind of output to generate. + + Setting this to `null` (default) will let tectonic decide the most + appropriate output format, which usually be a pdf. + ''; + }; + + 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 = nullOr str; + default = null; + description = '' + The name of the \"format\" file used to initialize the TeX engine. + ''; + }; + + color = mkOption { + type = nullOr (enum [ + "always" + "auto" + "never" + ]); + default = null; + example = "always"; + description = "Enable/disable colorful log output"; + }; + + extraOptions = mkOption { + 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 (texCfg.enable && cfg.enable) { + vim.languages.tex.build.builder = { + inherit name; + inherit (cfg) package executable; + + args = let + inherit (lib.lists) optional optionals; + snakeCaseToKebabCase = str: stringAsChars (x: "${optionalString ((match "[A-Z]" x) != null) "-"}${toLower x}") str; + generateOptionFlag = option: (optionals (cfg.${option} != "" && cfg.${option} != null) ["--${snakeCaseToKebabCase option}" "${toString cfg.${option}}"]); + in ( + # Base args + [ + "-X" + "compile" + "%f" + ] + # Flags + ++ (optional cfg.keepIntermediates "--keep-intermediates") + ++ (optional cfg.keepLogs "--keep-logs") + ++ (optional cfg.onlyCached "--only-cached") + ++ (optional cfg.synctex "--synctex") + ++ (optional cfg.untrustedInput "--untrusted") + # Options + ++ (optionals (cfg.reruns > 0) ["--reruns" "${toString cfg.reruns}"]) + ++ (generateOptionFlag "bundle") + ++ (generateOptionFlag "webBundle") + ++ (generateOptionFlag "outfmt") + ++ (concatLists (map (x: ["--hide" x]) cfg.hidePaths)) + ++ (generateOptionFlag "format") + ++ (generateOptionFlag "color") + # Still options but these are not defined by builder specific options but + # instead synchronize options between the global build options and builder + # specific options. + ++ (optionals (!(elem texCfg.build.pdfDirectory ["." ""])) ["--outdir" "${texCfg.build.pdfDirectory}"]) + ); + }; + }; +} diff --git a/modules/plugins/languages/tex/build/default.nix b/modules/plugins/languages/tex/build/default.nix new file mode 100644 index 00000000..9f9c5bc3 --- /dev/null +++ b/modules/plugins/languages/tex/build/default.nix @@ -0,0 +1,140 @@ +{ + config, + lib, + ... +}: let + inherit (builtins) filter isAttrs hasAttr attrNames length elemAt; + inherit (lib.modules) mkIf; + inherit (lib.nvim.config) mkBool; + inherit (lib.options) mkOption; + inherit (lib.types) str nullOr; + + cfg = config.vim.languages.tex; + + enabledBuildersCount = let + # This function will sort through the builder options and count how many + # builders have been enabled. + 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; + in (getEnabledBuildersCount {}); +in { + imports = [ + ./builders + ]; + + options.vim.languages.tex.build = { + enable = mkBool (enabledBuildersCount == 1) '' + Whether to enable configuring the builder. + + By enabling any of the builders, this option will be automatically set. + If you enable more than one builder then an error will be thrown. + ''; + + forwardSearchAfter = mkBool false '' + Set this property to `true` if you want to execute a forward search after + a build. + + This can also be thought of as enabling auto updating for your pdf viewer. + ''; + + onSave = mkBool false '' + Set this property to `true` if you want to compile the project after + saving a file. + ''; + + useFileList = mkBool false '' + 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 = nullOr str; + default = null; + 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. + ''; + }; + }; + + config = mkIf (enabledBuildersCount > 0) { + assertions = [ + { + assertion = enabledBuildersCount < 2; + message = '' + The nvf-tex-language implementation does not support having more than + 1 builders enabled. + ''; + } + ]; + }; +} diff --git a/modules/plugins/languages/tex/default.nix b/modules/plugins/languages/tex/default.nix new file mode 100644 index 00000000..7dbe7e3a --- /dev/null +++ b/modules/plugins/languages/tex/default.nix @@ -0,0 +1,78 @@ +{ + config, + lib, + ... +}: let + inherit (lib.modules) mkIf mkMerge; + inherit (lib.nvim.config) mkBool; + inherit (lib.options) mkEnableOption mkOption; + inherit (lib.types) enum; + + cfg = config.vim.languages.tex; +in { + imports = [ + ./build + ./lsp + ./pdfViewer + ./treesitter.nix + ]; + + options.vim.languages.tex = { + enable = mkEnableOption "Tex support"; + + extraOpts = { + texFlavor = { + enable = mkBool false '' + 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 using 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 = enum [ + "plaintex" + "context" + "tex" + ]; + 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 = lib.mkDefault "${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..244829d0 --- /dev/null +++ b/modules/plugins/languages/tex/lsp/default.nix @@ -0,0 +1,24 @@ +{ + config, + lib, + ... +}: let + inherit (lib.modules) mkIf; + inherit (builtins) any attrValues; + + cfg = config.vim.languages.tex; +in { + imports = [ + ./texlab.nix + ]; + + config = + mkIf + ( + cfg.enable # Check if nvf is enabled. + && (any (x: x.enable) (attrValues cfg.lsp)) # Check if any of the LSPs have been enabled. + ) + { + vim.lsp.lspconfig.enable = lib.mkDefault 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..190f84f3 --- /dev/null +++ b/modules/plugins/languages/tex/lsp/texlab.nix @@ -0,0 +1,598 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (builtins) isString map; + inherit (lib) optionalAttrs; + inherit (lib.modules) mkIf mkMerge; + inherit (lib.nvim.config) mkBool; + inherit (lib.options) mkOption mkPackageOption; + inherit + (lib.types) + attrs + either + enum + ints + listOf + nullOr + package + path + str + submodule + ; + + cfg = config.vim.languages.tex; + texlabCfg = cfg.lsp.texlab; + builderCfg = cfg.build.builder; + + # Get the enabled pdf viewer. + pdfViewer = import ../pdfViewer/getEnabledPdfViewer.nix {inherit lib config;}; +in { + options.vim.languages.tex.lsp.texlab = { + enable = mkBool config.vim.lsp.enable '' + Whether to enable Tex LSP support (texlab). + ''; + + package = mkPackageOption pkgs "texlab" {}; + + chktex = { + enable = mkBool false "Whether to enable linting via chktex"; + + package = mkOption { + type = package; + default = pkgs.texlive.withPackages (ps: [ps.chktex]); + description = '' + The chktex package to use. + Must have the `chktex` executable. + ''; + }; + + onOpenAndSave = mkBool false '' + Lint using chktex after opening and saving a file. + ''; + + onEdit = mkBool false "Lint using chktex after editing a file."; + + additionalArgs = mkOption { + type = listOf str; + default = []; + description = '' + 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; + default = 300; + description = "Delay in milliseconds before reporting diagnostics."; + }; + + allowedPatterns = mkOption { + type = listOf str; + default = []; + description = '' + A list of regular expressions used to filter the list of reported + diagnostics. + If specified, only diagnostics that match at least one of the + specified patterns are sent to the client. + + See also `texlab.diagnostics.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 + diagnostics. + If specified, only diagnostics that match none of the specified + patterns are sent to the client. + + See also `texlab.diagnostics.allowedPatterns`. + ''; + }; + }; + + experimental = { + followPackageLinks = mkBool false '' + If set to `true`, dependencies of custom packages are resolved and + included in the dependency graph. + ''; + + mathEnvironments = mkOption { + type = listOf str; + default = []; + description = '' + Allows extending the list of environments which the server considers + as math environments (for example `align*` or `equation`). + ''; + }; + + enumEnvironments = mkOption { + type = listOf str; + default = []; + description = '' + Allows extending the list of environments which the server considers + as enumeration environments (for example `enumerate` or `itemize`). + ''; + }; + + verbatimEnvironments = mkOption { + type = listOf str; + default = []; + description = '' + Allows extending the list of environments which the server considers + as verbatim environments (for example `minted` or `lstlisting`). + This can be used to suppress diagnostics from environments that do + not contain LaTeX code. + ''; + }; + + citationCommands = mkOption { + type = listOf str; + default = []; + description = '' + Allows extending the list of commands which the server considers as + citation commands (for example `\cite`). + + Hint: Additional commands need to be written without a leading `\` + (e.g. `foo` instead of `\foo`). + ''; + }; + + labelDefinitionCommands = mkOption { + type = listOf str; + default = []; + description = '' + Allows extending the list of `\label`-like commands. + + Hint: Additional commands need to be written without a leading `\` + (e.g. `foo` instead of `\foo`). + ''; + }; + + labelReferenceCommands = mkOption { + type = listOf str; + default = []; + description = '' + Allows extending the list of `\ref`-like commands. + + Hint: Additional commands need to be written without a leading `\` + (e.g. `foo` instead of `\foo`). + ''; + }; + + labelReferenceRangeCommands = mkOption { + type = listOf str; + default = []; + description = '' + Allows extending the list of `\crefrange`-like commands. + + Hint: Additional commands need to be written without a leading `\` + (e.g. `foo` instead of `\foo`). + ''; + }; + + labelDefinitionPrefixes = mkOption { + type = listOf (listOf str); + default = []; + description = '' + Allows associating a label definition command with a custom prefix. + Consider, + ``` + \newcommand{\theorem}[1]{\label{theorem:#1}} + \theorem{foo} + ``` + Then setting `texlab.experimental.labelDefinitionPrefixes` to + `[["theorem", "theorem:"]]` and adding `theorem` to + `texlab.experimental.labelDefinitionCommands` will make the server + recognize the `theorem:foo` label. + ''; + }; + + labelReferencePrefixes = mkOption { + type = listOf (listOf str); + default = []; + description = '' + Allows associating a label reference command with a custom prefix. + See `texlab.experimental.labelDefinitionPrefixes` for more details. + ''; + }; + }; + + extraLuaSettings = mkOption { + type = attrs; + default = {}; + example = { + foo = "bar"; + baz = 314; + }; + 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 = { + ... + + ... + + } + } + } + ``` + ''; + }; + + 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 + along with custom configs. + ''; + + package = mkOption { + type = package; + default = pdfViewer.package; + description = '' + The package to use as your PDF viewer. + This viewer needs to support Synctex. + + By default it is set to the package of the pdfViewer option. + ''; + }; + + executable = mkOption { + type = str; + default = pdfViewer.executable; + description = '' + Defines the executable of the PDF previewer. The previewer needs to + support SyncTeX. + + By default it is set to the executable of the pdfViewer option. + ''; + }; + + args = mkOption { + type = listOf str; + default = pdfViewer.args; + 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. + + By default it is set to the args of the pdfViewer option. + + 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.unsigned; + default = 80; + description = '' + Defines the maximum amount of characters per line when formatting + BibTeX files. + + Setting this value to 0 will disable this option. + ''; + }; + + 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 to `true`, 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 functionality may not be working, please follow + https://github.com/latex-lsp/texlab/pull/1311 for status updates. + ''; + }; + }; + }; + + config = mkIf cfg.enable ( + let + # ----- Setup Config ----- + # Command to start the LSP + setupConfig.cmd = ["${texlabCfg.package}/bin/texlab"]; + + # Create texlab settings section + setupConfig.settings.texlab = ( + { + # -- Completion -- + completion.matcher = texlabCfg.completion.matcher; + + # -- Diagnostics -- + diagnosticsDelay = texlabCfg.diagnostics.delay; + diagnostics = { + inherit (texlabCfg.diagnostics) allowedPatterns ignoredPatterns; + }; + + # -- Experimental -- + experimental = texlabCfg.experimental; + + # -- Formatters -- + inherit (texlabCfg.formatter) formatterLineLength bibtexFormatter latexFormatter; + + # -- Inlay Hints -- + inlayHints = texlabCfg.inlayHints; + + # -- Latex Indent -- + latexindent = texlabCfg.latexindent; + } + # + # -- Build -- + // (optionalAttrs cfg.build.enable { + build = { + inherit + (cfg.build) + onSave + useFileList + auxDirectory + logDirectory + pdfDirectory + filename + forwardSearchAfter + ; + inherit (builderCfg) args; + executable = "${builderCfg.package}/bin/${builderCfg.executable}"; + }; + }) + # + # -- Chktex -- + // (optionalAttrs texlabCfg.chktex.enable { + chktex = { + inherit (texlabCfg.chktex) onOpenAndSave onEdit additionalArgs; + }; + }) + # + # -- Forward Search -- + // (optionalAttrs texlabCfg.forwardSearch.enable { + forwardSearch = { + inherit (texlabCfg.forwardSearch) args; + executable = "${texlabCfg.forwardSearch.package}/bin/${texlabCfg.forwardSearch.executable}"; + }; + }) + # + # -- Symbols -- + // (optionalAttrs texlabCfg.symbols.enable { + 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; + }; + }) + # + # -- Extra Settings -- + // texlabCfg.extraLuaSettings + ); + 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]; + }) + ]) + ); +} diff --git a/modules/plugins/languages/tex/pdfViewer/default.nix b/modules/plugins/languages/tex/pdfViewer/default.nix new file mode 100644 index 00000000..20eed52a --- /dev/null +++ b/modules/plugins/languages/tex/pdfViewer/default.nix @@ -0,0 +1,112 @@ +{ + config, + lib, + pkgs, + ... +}: let + inherit (lib.options) mkOption; + inherit (lib.types) str package listOf; + + cfg = config.vim.languages.tex; + + pdfViewer = {name, ...}: { + options = { + enable = lib.mkEnableOption "${builtins.toString name} pdf viewer"; + + name = mkOption { + type = str; + example = "okular"; + description = '' + The name of the pdf viewer to use. + + This value will be automatically set when any of the viewers are + enabled. + + This value will be automatically set to the value of the parent + attribute set. ex. `...tex.pdfViewer..name = "$${name}"` + This value cannot and should not be changed to be different from this + parent value. + + Default values already exist such as `...tex.pdfViewer.okular` but + you can override the default values or created completely custom + pdf viewers should you wish. + ''; + }; + + package = mkOption { + type = package; + example = pkgs.kdePackages.okular; + description = "The package of the pdf viewer to use."; + }; + + executable = mkOption { + type = str; + default = "${builtins.toString name}"; + description = '' + The executable for the pdf viewer to use. + + It will be called as `/bin/`. + + By default, the name of the pdf viewer will be used. + ''; + }; + + args = mkOption { + type = listOf str; + default = []; + description = '' + The command line arguments to use when calling the pdf viewer command. + + These will be called as + `/bin/ ...`. + ''; + }; + }; + + # The name of the pdf viewer must be set to the parent attribute set name. + config.name = lib.mkForce name; + }; +in { + imports = [ + ./premadePdfViewers.nix + ]; + + options.vim.languages.tex.pdfViewer = mkOption { + type = with lib.types; attrsOf (submodule pdfViewer); + default = {}; + example = { + zathura.enable = true; + + customOkular = { + enable = false; + package = pkgs.kdePackages.okular; + executable = "okular"; + args = [ + "--unique" + "file:%p#src:%l%f" + ]; + }; + }; + description = '' + Define the pdf viewer to use for viewing compiled tex documents. + ''; + }; + + config = let + # List form of all pdf viewers. + pdfViewers = builtins.attrValues cfg.pdfViewer; + + countPdfViewers = viewers: (lib.lists.count (x: x.enable) viewers); + in { + assertions = [ + { + # Assert that there is only one enabled pdf viewer. + assertion = (countPdfViewers pdfViewers) < 2; + message = '' + The nvf-tex-language implementation does not support having more than + 1 pdf viewers enabled. + ''; + } + ]; + }; +} diff --git a/modules/plugins/languages/tex/pdfViewer/getEnabledPdfViewer.nix b/modules/plugins/languages/tex/pdfViewer/getEnabledPdfViewer.nix new file mode 100644 index 00000000..e8790c84 --- /dev/null +++ b/modules/plugins/languages/tex/pdfViewer/getEnabledPdfViewer.nix @@ -0,0 +1,22 @@ +{ + config, + lib, + ... +}: let + # The attribute set of pdf viewers in this configuration. + pdfViewers = config.vim.languages.tex.pdfViewer; + + # The list of pdf viewers in this configuration. + pdfViewersList = builtins.attrValues pdfViewers; + + # The list of enabled pdf viewers. + enabledPdfViewersList = builtins.filter (x: x.enable) pdfViewersList; + + # The number of enabled pdf viewers. + enabledPdfViewersCount = lib.lists.count (x: x.enable) pdfViewersList; +in + if (enabledPdfViewersCount == 0) + # Use the fallback if no pdf viewer was enabled. + then pdfViewers.fallback + # Otherwise get the first enabled viewer. + else builtins.head enabledPdfViewersList diff --git a/modules/plugins/languages/tex/pdfViewer/premadePdfViewers.nix b/modules/plugins/languages/tex/pdfViewer/premadePdfViewers.nix new file mode 100644 index 00000000..622a058e --- /dev/null +++ b/modules/plugins/languages/tex/pdfViewer/premadePdfViewers.nix @@ -0,0 +1,80 @@ +{ + pkgs, + lib, + ... +}: let + inherit (lib) mkDefault mkForce; + + mkPdfViewerDefaults = { + package, + executable, + args ? [], + }: { + package = mkDefault package; + executable = mkDefault executable; + args = mkDefault args; + }; +in { + config.vim.languages.tex.pdfViewer = { + okular = mkPdfViewerDefaults { + package = pkgs.kdePackages.okular; + executable = "okular"; + args = [ + "--unique" + "file:%p#src:%l%f" + ]; + }; + + sioyek = mkPdfViewerDefaults { + package = pkgs.sioyek; + executable = "sioyek"; + args = [ + "--reuse-window" + "--execute-command" + "toggle_synctex" + "--inverse-search" + "texlab inverse-search -i \"%%1\" -l %%2" + "--forward-search-file" + "%f" + "--forward-search-line" + "%l" + "%p" + ]; + }; + + qpdfview = mkPdfViewerDefaults { + package = pkgs.qpdfview; + executable = "qpdfview"; + args = [ + "--unique" + "%p#src:%f:%l:1" + ]; + }; + + zathura = mkPdfViewerDefaults { + package = pkgs.zathura; + executable = "zathura"; + args = [ + "--synctex-forward" + "%l:1:%f" + "%p" + ]; + }; + + # This is a special pdf viewer. It is force set to a basic and known + # working configuration of okular and is used where needed in the + # rest of the tex language configuration encase no other pdf viewer + # was enabled. + # It cannot be enabled on its own and exists purely as a fallback + # option for internal use. + fallback = { + enable = mkForce false; + package = mkForce pkgs.kdePackages.okular; + executable = mkForce "okular"; + args = mkForce [ + "--unique" + "file:%p#src:%l%f" + ]; + }; + }; +} diff --git a/modules/plugins/languages/tex/treesitter.nix b/modules/plugins/languages/tex/treesitter.nix new file mode 100644 index 00000000..d9c3754b --- /dev/null +++ b/modules/plugins/languages/tex/treesitter.nix @@ -0,0 +1,35 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (lib.modules) mkIf mkMerge; + inherit (lib.nvim.types) mkGrammarOption; + + mkEnableTreesitterOption = lib.nvim.types.mkEnableTreesitterOption config.vim.languages.enableTreesitter; + + cfg = config.vim.languages.tex; +in { + options.vim.languages.tex.treesitter = { + latex = { + enable = mkEnableTreesitterOption "latex"; + package = mkGrammarOption pkgs "latex"; + }; + bibtex = { + enable = mkEnableTreesitterOption "bibtex"; + 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]; + }) + ]); +} diff --git a/npins/sources.json b/npins/sources.json index 919ef20d..f88f9289 100644 --- a/npins/sources.json +++ b/npins/sources.json @@ -1682,14 +1682,14 @@ "type": "Git", "repository": { "type": "GitHub", - "owner": "otavioschwanck", + "owner": "isaacST08", "repo": "new-file-template.nvim" }, "branch": "master", "submodules": false, - "revision": "6ac66669dbf2dc5cdee184a4fe76d22465ca67e8", - "url": "https://github.com/otavioschwanck/new-file-template.nvim/archive/6ac66669dbf2dc5cdee184a4fe76d22465ca67e8.tar.gz", - "hash": "0c7378c3w6bniclp666rq15c28akb0sjy58ayva0wpyin4k26hl3" + "revision": "dc3a58b1f490c86075c96670b9eb81370c2f2ca1", + "url": "https://github.com/isaacST08/new-file-template.nvim/archive/dc3a58b1f490c86075c96670b9eb81370c2f2ca1.tar.gz", + "hash": "0y6ip3k6bjaf32x1y1p6mmkwwdi71yvwr6klr26m252jrg8352pf" }, "nix-develop-nvim": { "type": "Git",