From 73f5c199e525cfcb5939971f745d90068b2eb6f2 Mon Sep 17 00:00:00 2001 From: Steven Arcangeli Date: Sun, 27 Aug 2023 20:15:18 -0700 Subject: doc: flesh out config options documentation --- .github/generate.py | 77 +++++++++++++++++++++------ .github/nvim_doc_tools | 2 +- README.md | 114 +++++++++++++++++++++++----------------- doc/conform.txt | 137 ++++++++++++++++++++++++++++++++++++++++++++++++- lua/conform/init.lua | 8 +-- tests/options_doc.lua | 61 ++++++++++++++++++++++ 6 files changed, 331 insertions(+), 68 deletions(-) create mode 100644 tests/options_doc.lua diff --git a/.github/generate.py b/.github/generate.py index 3f4fa27..f317534 100755 --- a/.github/generate.py +++ b/.github/generate.py @@ -1,38 +1,49 @@ import os import os.path import re -from typing import List - -from nvim_doc_tools import ( - Vimdoc, - VimdocSection, - generate_md_toc, - parse_functions, - read_nvim_json, - render_md_api, - render_vimdoc_api, - replace_section, -) +from dataclasses import dataclass +from functools import lru_cache +from typing import Dict, List + +from nvim_doc_tools import (Vimdoc, VimdocSection, generate_md_toc, indent, + parse_functions, read_nvim_json, render_md_api, + render_vimdoc_api, replace_section, wrap) HERE = os.path.dirname(__file__) ROOT = os.path.abspath(os.path.join(HERE, os.path.pardir)) README = os.path.join(ROOT, "README.md") DOC = os.path.join(ROOT, "doc") VIMDOC = os.path.join(DOC, "conform.txt") +OPTIONS = os.path.join(ROOT, "tests", "options_doc.lua") -def update_formatter_list(): - formatters = sorted( +@dataclass +class Formatter: + name: str + description: str + url: str + + +@lru_cache +def get_all_formatters() -> List[Formatter]: + names = sorted( [ os.path.splitext(file)[0] for file in os.listdir(os.path.join(ROOT, "lua", "conform", "formatters")) ] ) + formatters = [] + for name in names: + meta = read_nvim_json(f'require("conform.formatters.{name}").meta') + formatters.append(Formatter(name, **meta)) + return formatters + + +def update_formatter_list(): formatter_lines = ["\n"] - for formatter in formatters: - meta = read_nvim_json(f'require("conform.formatters.{formatter}").meta') + for formatter in get_all_formatters(): formatter_lines.append( - f"- [{formatter}]({meta['url']}) - {meta['description']}\n" + f"- [{formatter.name}]({formatter.url}) - {formatter.description}\n" ) replace_section( README, @@ -42,6 +53,19 @@ def update_formatter_list(): ) +def update_options(): + option_lines = ["\n", "```lua\n"] + with open(OPTIONS, "r", encoding="utf-8") as f: + option_lines.extend(f.readlines()) + option_lines.extend(["```\n", "\n"]) + replace_section( + README, + r"^$", + r"^$", + option_lines, + ) + + def add_md_link_path(path: str, lines: List[str]) -> List[str]: ret = [] for line in lines: @@ -70,12 +94,30 @@ def update_readme_toc(): ) +def gen_options_vimdoc() -> VimdocSection: + section = VimdocSection("Options", "conform-options", ["\n", ">lua\n"]) + with open(OPTIONS, "r", encoding="utf-8") as f: + section.body.extend(indent(f.readlines(), 4)) + section.body.append("<\n") + return section + + +def gen_formatter_vimdoc() -> VimdocSection: + section = VimdocSection("Formatters", "conform-formatters", ["\n"]) + for formatter in get_all_formatters(): + line = f"`{formatter.name}` - {formatter.description}\n" + section.body.extend(wrap(line, sub_indent=len(formatter.name) + 3)) + return section + + def generate_vimdoc(): doc = Vimdoc("conform.txt", "conform") funcs = parse_functions(os.path.join(ROOT, "lua", "conform", "init.lua")) doc.sections.extend( [ + gen_options_vimdoc(), VimdocSection("API", "conform-api", render_vimdoc_api("conform", funcs)), + gen_formatter_vimdoc(), ] ) @@ -86,6 +128,7 @@ def generate_vimdoc(): def main() -> None: """Update the README""" update_formatter_list() + update_options() update_md_api() update_readme_toc() generate_vimdoc() diff --git a/.github/nvim_doc_tools b/.github/nvim_doc_tools index 4260b37..3432f03 160000 --- a/.github/nvim_doc_tools +++ b/.github/nvim_doc_tools @@ -1 +1 @@ -Subproject commit 4260b374395d963b8ae74134908e70650f591d2d +Subproject commit 3432f0308c96ad95b7337c68b5f143c6b9916308 diff --git a/README.md b/README.md index 890be3b..449a4ff 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Formatter plugin for Neovim - [Installation](#installation) - [Setup](#setup) - [Formatters](#formatters) -- [Custom formatters](#custom-formatters) +- [Options](#options) - [API](#api) - [format(opts)](#formatopts) - [list_formatters(bufnr)](#list_formattersbufnr) @@ -121,12 +121,6 @@ require("conform").setup({ }) ``` -You can also modify `formatters_by_ft` directly - -```lua -require("conform").formatters_by_ft.lua = { "stylua" } -``` - Then you can use `conform.format()` just like you would `vim.lsp.buf.format()`. For example, to format on save: ```lua @@ -140,24 +134,17 @@ vim.api.nvim_create_autocmd("BufWritePre", { As a shortcut, conform will optionally set up this format-on-save autocmd for you -```lua -require("conform").setup({ - format_on_save = true, -}) -``` - -You can also use an option table and it will get passed to `conform.format()` - ```lua require("conform").setup({ format_on_save = { + -- These options will be passed to conform.format() timeout_ms = 500, lsp_fallback = true, }, }) ``` -See [conform.format()](#formatopts) for more details about capabilities and parameters. +See [conform.format()](#formatopts) for more details about the parameters. To view configured and available formatters, as well as to see the path to the log file, run `:checkhealth conform` @@ -207,44 +194,78 @@ To view configured and available formatters, as well as to see the path to the l - [zigfmt](https://github.com/ziglang/zig) - Reformat Zig source into canonical form. -## Custom formatters +## Options + +A complete list of all configuration options -You can create your own custom formatters + ```lua require("conform").setup({ - formatters = { - my_formatter = { - -- This can be a string or a function that returns a string - command = 'my_cmd', - -- OPTIONAL - all fields below this are optional - -- A list of strings, or a function that returns a list of strings - args = { "--stdin-from-filename", "$FILENAME" }, - -- Send file contents to stdin, read new contents from stdout (default true) - -- When false, will create a temp file (will appear in "$FILENAME" args). The temp - -- file is assumed to be modified in-place by the format command. - stdin = true, - -- A function the calculates the directory to run the command in - cwd = require("conform.util").root_file({ ".editorconfig", "package.json" }), - -- When cwd is not found, don't run the formatter (default false) - require_cwd = true, - -- When returns false, the formatter will not be used - condition = function(ctx) - return vim.fs.basename(ctx.filename) ~= "README.md" - end, - -- Exit codes that indicate success (default {0}) - exit_codes = { 0, 1 }, - } - } + -- Map of filetype to formatters + formatters_by_ft = { + lua = { "stylua" }, + -- Conform will use the first available formatter in the list + javascript = { "prettier_d", "prettier" }, + -- Formatters can also be specified with additional options + python = { + formatters = { "isort", "black" }, + -- Run formatters one after another instead of stopping at the first success + run_all_formatters = true, + -- Don't run these formatters as part of the format_on_save autocmd (see below) + format_on_save = false, + }, + }, + -- If this is set, Conform will run the formatter on save. + -- It will pass the table to conform.format(). + format_on_save = { + -- I recommend these options. See :help conform.format for details. + lsp_fallback = true, + timeout_ms = 500, + }, + -- Set the log level. Use `:checkhealth conform` to see the location of the log file. + log_level = vim.log.levels.ERROR, + -- Define custom formatters here + formatters = { + my_formatter = { + -- This can be a string or a function that returns a string + command = "my_cmd", + -- OPTIONAL - all fields below this are optional + -- A list of strings, or a function that returns a list of strings + args = { "--stdin-from-filename", "$FILENAME" }, + -- Send file contents to stdin, read new contents from stdout (default true) + -- When false, will create a temp file (will appear in "$FILENAME" args). The temp + -- file is assumed to be modified in-place by the format command. + stdin = true, + -- A function the calculates the directory to run the command in + cwd = require("conform.util").root_file({ ".editorconfig", "package.json" }), + -- When cwd is not found, don't run the formatter (default false) + require_cwd = true, + -- When returns false, the formatter will not be used + condition = function(ctx) + return vim.fs.basename(ctx.filename) ~= "README.md" + end, + -- Exit codes that indicate success (default {0}) + exit_codes = { 0, 1 }, + }, + -- These can also be a function that returns the formatter + other_formatter = function() + return { + command = "my_cmd", + } + end, + }, }) -``` - -Again, you can also set these directly -```lua -require("conform").formatters.my_formatter = { ... } +-- You can set formatters_by_ft and formatters directly +require("conform").formatters_by_ft.lua = { "stylua" } +require("conform").formatters.my_formatter = { + command = "my_cmd", +} ``` + + ## API @@ -264,6 +285,7 @@ Format a buffer | | lsp_fallback | `nil\|boolean` | Attempt LSP formatting if no formatters are available. Defaults to false. | Returns: + | Type | Desc | | ------- | ------------------------------------- | | boolean | True if any formatters were attempted | diff --git a/doc/conform.txt b/doc/conform.txt index 859c780..5e5cabc 100644 --- a/doc/conform.txt +++ b/doc/conform.txt @@ -3,7 +3,76 @@ -------------------------------------------------------------------------------- CONTENTS *conform-contents* - 1. Api |conform-api| + 1. Options |conform-options| + 2. Api |conform-api| + 3. Formatters |conform-formatters| + +-------------------------------------------------------------------------------- +OPTIONS *conform-options* + +>lua + require("conform").setup({ + -- Map of filetype to formatters + formatters_by_ft = { + lua = { "stylua" }, + -- Conform will use the first available formatter in the list + javascript = { "prettier_d", "prettier" }, + -- Formatters can also be specified with additional options + python = { + formatters = { "isort", "black" }, + -- Run formatters one after another instead of stopping at the first success + run_all_formatters = true, + -- Don't run these formatters as part of the format_on_save autocmd (see below) + format_on_save = false, + }, + }, + -- If this is set, Conform will run the formatter on save. + -- It will pass the table to conform.format(). + format_on_save = { + -- I recommend these options. See :help conform.format for details. + lsp_fallback = true, + timeout_ms = 500, + }, + -- Set the log level. Use `:checkhealth conform` to see the location of the log file. + log_level = vim.log.levels.ERROR, + -- Define custom formatters here + formatters = { + my_formatter = { + -- This can be a string or a function that returns a string + command = "my_cmd", + -- OPTIONAL - all fields below this are optional + -- A list of strings, or a function that returns a list of strings + args = { "--stdin-from-filename", "$FILENAME" }, + -- Send file contents to stdin, read new contents from stdout (default true) + -- When false, will create a temp file (will appear in "$FILENAME" args). The temp + -- file is assumed to be modified in-place by the format command. + stdin = true, + -- A function the calculates the directory to run the command in + cwd = require("conform.util").root_file({ ".editorconfig", "package.json" }), + -- When cwd is not found, don't run the formatter (default false) + require_cwd = true, + -- When returns false, the formatter will not be used + condition = function(ctx) + return vim.fs.basename(ctx.filename) ~= "README.md" + end, + -- Exit codes that indicate success (default {0}) + exit_codes = { 0, 1 }, + }, + -- These can also be a function that returns the formatter + other_formatter = function() + return { + command = "my_cmd", + } + end, + }, + }) + + -- You can set formatters_by_ft and formatters directly + require("conform").formatters_by_ft.lua = { "stylua" } + require("conform").formatters.my_formatter = { + command = "my_cmd", + } +< -------------------------------------------------------------------------------- API *conform-api* @@ -35,5 +104,71 @@ list_formatters({bufnr}): conform.FormatterInfo[] *conform.list_formatter list_all_formatters(): conform.FormatterInfo[] *conform.list_all_formatters* List information about all filetype-configured formatters +-------------------------------------------------------------------------------- +FORMATTERS *conform-formatters* + +`autoflake` - Removes unused imports and unused variables as reported by + pyflakes. +`autopep8` - A tool that automatically formats Python code to conform to the PEP + 8 style guide. +`black` - The uncompromising Python code formatter. +`clang_format` - Tool to format C/C++/… code according to a set of rules and + heuristics. +`cljstyle` - Formatter for Clojure code. +`cmake_format` - Parse cmake listfiles and format them nicely. +`dart_format` - Replace the whitespace in your program with formatting that + follows Dart guidelines. +`dfmt` - Formatter for D source code. +`elm_format` - elm-format formats Elm source code according to a standard set of + rules based on the official [Elm Style Guide](https://elm- + lang.org/docs/style-guide). +`erb_format` - Format ERB files with speed and precision. +`eslint_d` - Like ESLint, but faster. +`fish_indent` - Indent or otherwise prettify a piece of fish code. +`gdformat` - A formatter for Godot's gdscript. +`gofmt` - Formats go programs. +`gofumpt` - Enforce a stricter format than gofmt, while being backwards + compatible. That is, gofumpt is happy with a subset of the formats + that gofmt is happy with. +`goimports` - Updates your Go import lines, adding missing ones and removing + unreferenced ones. +`htmlbeautifier` - A normaliser/beautifier for HTML that also understands + embedded Ruby. Ideal for tidying up Rails templates. +`isort` - Python utility / library to sort imports alphabetically and + automatically separate them into sections and by type. +`jq` - Command-line JSON processor. +`nixfmt` - nixfmt is a formatter for Nix code, intended to apply a uniform + style. +`nixpkgs_fmt` - nixpkgs-fmt is a Nix code formatter for nixpkgs. +`ocamlformat` - Auto-formatter for OCaml code. +`pg_format` - PostgreSQL SQL syntax beautifier. +`prettier` - Prettier is an opinionated code formatter. It enforces a consistent + style by parsing your code and re-printing it with its own rules that + take the maximum line length into account, wrapping code when + necessary. +`prettierd` - prettier, as a daemon, for ludicrous formatting speed. +`rubocop` - Ruby static code analyzer and formatter, based on the community Ruby + style guide. +`rustfmt` - A tool for formatting rust code according to style guidelines. +`scalafmt` - Code formatter for Scala. +`shfmt` - A shell parser, formatter, and interpreter with `bash` support. +`sql_formatter` - A whitespace formatter for different query languages. +`stylua` - An opinionated code formatter for Lua. +`swift_format` - Swift formatter from apple. Requires building from source with + `swift build`. +`swiftformat` - SwiftFormat is a code library and command-line tool for + reformatting `swift` code on macOS or Linux. +`terraform_fmt` - The terraform-fmt command rewrites `terraform` configuration + files to a canonical format and style. +`uncrustify` - A source code beautifier for C, C++, C#, ObjectiveC, D, Java, + Pawn and Vala. +`xmlformat` - xmlformatter is an Open Source Python package, which provides + formatting of XML documents. +`yamlfix` - A configurable YAML formatter that keeps comments. +`yamlfmt` - yamlfmt is an extensible command line tool or library to format yaml + files. +`yapf` - Yet Another Python Formatter. +`zigfmt` - Reformat Zig source into canonical form. + ================================================================================ vim:tw=80:ts=2:ft=help:norl:syntax=help: diff --git a/lua/conform/init.lua b/lua/conform/init.lua index 5b8bd5b..c81cf82 100644 --- a/lua/conform/init.lua +++ b/lua/conform/init.lua @@ -7,8 +7,7 @@ local M = {} ---@field available boolean ---@field available_msg? string ----@class (exact) conform.FormatterConfig ----@field meta conform.FormatterMeta +---@class (exact) conform.StaticFormatterConfig ---@field command string|fun(ctx: conform.Context): string ---@field args? string[]|fun(ctx: conform.Context): string[] ---@field cwd? fun(ctx: conform.Context): nil|string @@ -17,6 +16,9 @@ local M = {} ---@field condition? fun(ctx: conform.Context): boolean ---@field exit_codes? integer[] Exit codes that indicate success (default {0}) +---@class (exact) conform.FormatterConfig : conform.StaticFormatterConfig +---@field meta conform.FormatterMeta + ---@class (exact) conform.FormatterMeta ---@field url string ---@field description string @@ -36,7 +38,7 @@ local M = {} ---@type table M.formatters_by_ft = {} ----@type table +---@type table M.formatters = {} M.setup = function(opts) diff --git a/tests/options_doc.lua b/tests/options_doc.lua new file mode 100644 index 0000000..4360b92 --- /dev/null +++ b/tests/options_doc.lua @@ -0,0 +1,61 @@ +require("conform").setup({ + -- Map of filetype to formatters + formatters_by_ft = { + lua = { "stylua" }, + -- Conform will use the first available formatter in the list + javascript = { "prettier_d", "prettier" }, + -- Formatters can also be specified with additional options + python = { + formatters = { "isort", "black" }, + -- Run formatters one after another instead of stopping at the first success + run_all_formatters = true, + -- Don't run these formatters as part of the format_on_save autocmd (see below) + format_on_save = false, + }, + }, + -- If this is set, Conform will run the formatter on save. + -- It will pass the table to conform.format(). + format_on_save = { + -- I recommend these options. See :help conform.format for details. + lsp_fallback = true, + timeout_ms = 500, + }, + -- Set the log level. Use `:checkhealth conform` to see the location of the log file. + log_level = vim.log.levels.ERROR, + -- Define custom formatters here + formatters = { + my_formatter = { + -- This can be a string or a function that returns a string + command = "my_cmd", + -- OPTIONAL - all fields below this are optional + -- A list of strings, or a function that returns a list of strings + args = { "--stdin-from-filename", "$FILENAME" }, + -- Send file contents to stdin, read new contents from stdout (default true) + -- When false, will create a temp file (will appear in "$FILENAME" args). The temp + -- file is assumed to be modified in-place by the format command. + stdin = true, + -- A function the calculates the directory to run the command in + cwd = require("conform.util").root_file({ ".editorconfig", "package.json" }), + -- When cwd is not found, don't run the formatter (default false) + require_cwd = true, + -- When returns false, the formatter will not be used + condition = function(ctx) + return vim.fs.basename(ctx.filename) ~= "README.md" + end, + -- Exit codes that indicate success (default {0}) + exit_codes = { 0, 1 }, + }, + -- These can also be a function that returns the formatter + other_formatter = function() + return { + command = "my_cmd", + } + end, + }, +}) + +-- You can set formatters_by_ft and formatters directly +require("conform").formatters_by_ft.lua = { "stylua" } +require("conform").formatters.my_formatter = { + command = "my_cmd", +} -- cgit v1.2.3-70-g09d2