summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--doc/conform.txt54
-rw-r--r--lua/conform/formatters/autopep8.lua2
-rw-r--r--lua/conform/formatters/clang_format.lua2
-rw-r--r--lua/conform/formatters/darker.lua2
-rw-r--r--lua/conform/formatters/deno_fmt.lua2
-rw-r--r--lua/conform/formatters/markdown-toc.lua2
-rw-r--r--lua/conform/formatters/phpcbf.lua2
-rw-r--r--lua/conform/formatters/prettier.lua2
-rw-r--r--lua/conform/formatters/prettierd.lua2
-rw-r--r--lua/conform/formatters/stylua.lua2
-rw-r--r--lua/conform/formatters/uncrustify.lua2
-rw-r--r--lua/conform/formatters/yapf.lua2
-rw-r--r--lua/conform/formatters/zprint.lua2
-rw-r--r--lua/conform/init.lua26
-rw-r--r--lua/conform/runner.lua21
-rw-r--r--lua/conform/util.lua62
-rwxr-xr-xscripts/generate.py40
-rw-r--r--tests/runner_spec.lua12
19 files changed, 147 insertions, 94 deletions
diff --git a/README.md b/README.md
index 60be045..44e6c34 100644
--- a/README.md
+++ b/README.md
@@ -337,7 +337,7 @@ require("conform").formatters.shfmt = {
}
-- prepend_args can be a function, just like args
require("conform").formatters.shfmt = {
- prepend_args = function(ctx)
+ prepend_args = function(self, ctx)
return { "-i", "2" }
end,
}
diff --git a/doc/conform.txt b/doc/conform.txt
index 66514cc..096c195 100644
--- a/doc/conform.txt
+++ b/doc/conform.txt
@@ -6,7 +6,7 @@ CONTENTS *conform-content
1. Options |conform-options|
2. Api |conform-api|
3. Formatters |conform-formatters|
- 4. Autoformat |conform-autoformat|
+ 4. Self argument migration |conform-self-args|
--------------------------------------------------------------------------------
OPTIONS *conform-options*
@@ -331,45 +331,27 @@ FORMATTERS *conform-formatter
`zprint` - Formatter for Clojure and EDN.
--------------------------------------------------------------------------------
-AUTOFORMAT *conform-autoformat*
+SELF ARGUMENT MIGRATION *conform-self-args*
-If you want more complex logic than the `format_on_save` option allows, you can
-write it yourself using your own autocmd. For example:
+The function arguments for formatter config functions have changed. Previously,
+they took a single `ctx` argument.
>lua
- -- if format_on_save is a function, it will be called during BufWritePre
- require("conform").setup({
- format_on_save = function(bufnr)
- -- Disable autoformat on certain filetypes
- local ignore_filetypes = { "sql", "java" }
- if vim.tbl_contains(ignore_filetypes, vim.bo[bufnr].filetype) then
- return
- end
- -- Disable with a global or buffer-local variable
- if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then
- return
- end
- -- Disable autoformat for files in a certain path
- local bufname = vim.api.nvim_buf_get_name(bufnr)
- if bufname:match("/node_modules/") then
- return
+ {
+ command = "phpcbf",
+ args = function(ctx)
+ return { "-q", "--stdin-path=" .. ctx.filename, "-" }
end
- -- ...additional logic...
- return { timeout_ms = 500, lsp_fallback = true }
- end,
- })
-
- -- There is a similar affordance for format_after_save, which uses BufWritePost.
- -- This is good for formatters that are too slow to run synchronously.
- require("conform").setup({
- format_after_save = function(bufnr)
- if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then
- return
+ }
+<Now, they take `self` as the first argument, and `ctx` as the second.
+>lua
+ {
+ command = "phpcbf",
+ args = function(self, ctx)
+ return { "-q", "--stdin-path=" .. ctx.filename, "-" }
end
- -- ...additional logic...
- return { lsp_fallback = true }
- end,
- })
-<
+ }
+<The config values that can be defined as functions are: `command`, `args`,
+`range_args`, `cwd`, `env`, and `condition`.
================================================================================
vim:tw=80:ts=2:ft=help:norl:syntax=help:
diff --git a/lua/conform/formatters/autopep8.lua b/lua/conform/formatters/autopep8.lua
index 3945e47..3d5b015 100644
--- a/lua/conform/formatters/autopep8.lua
+++ b/lua/conform/formatters/autopep8.lua
@@ -6,7 +6,7 @@ return {
},
command = "autopep8",
args = { "-" },
- range_args = function(ctx)
+ range_args = function(self, ctx)
return { "-", "--line-range", tostring(ctx.range.start[1]), tostring(ctx.range["end"][1]) }
end,
}
diff --git a/lua/conform/formatters/clang_format.lua b/lua/conform/formatters/clang_format.lua
index 44af877..2e68fc1 100644
--- a/lua/conform/formatters/clang_format.lua
+++ b/lua/conform/formatters/clang_format.lua
@@ -7,7 +7,7 @@ return {
},
command = "clang-format",
args = { "-assume-filename", "$FILENAME" },
- range_args = function(ctx)
+ range_args = function(self, ctx)
local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range)
local length = end_offset - start_offset
return {
diff --git a/lua/conform/formatters/darker.lua b/lua/conform/formatters/darker.lua
index 5f47e6b..9fe9b20 100644
--- a/lua/conform/formatters/darker.lua
+++ b/lua/conform/formatters/darker.lua
@@ -6,7 +6,7 @@ return {
description = "Run black only on changed lines.",
},
command = "darker",
- args = function(ctx)
+ args = function(self, ctx)
-- make sure pre-save doesn't lose changes while post-save respects
-- the revision setting potentially set in pyproject.toml
if vim.bo[ctx.buf].modified then
diff --git a/lua/conform/formatters/deno_fmt.lua b/lua/conform/formatters/deno_fmt.lua
index 7b48320..5e7f452 100644
--- a/lua/conform/formatters/deno_fmt.lua
+++ b/lua/conform/formatters/deno_fmt.lua
@@ -14,7 +14,7 @@ return {
description = "Use [Deno](https://deno.land/) to format TypeScript, JavaScript/JSON and markdown.",
},
command = "deno",
- args = function(ctx)
+ args = function(self, ctx)
return {
"fmt",
"-",
diff --git a/lua/conform/formatters/markdown-toc.lua b/lua/conform/formatters/markdown-toc.lua
index 129d50e..a7a9694 100644
--- a/lua/conform/formatters/markdown-toc.lua
+++ b/lua/conform/formatters/markdown-toc.lua
@@ -6,7 +6,7 @@ return {
},
command = "markdown-toc",
stdin = false,
- args = function(ctx)
+ args = function(self, ctx)
-- use the indentation set in the current buffer, effectively allowing us to
-- use values from .editorconfig
local indent = vim.bo[ctx.buf].expandtab and (" "):rep(vim.bo[ctx.buf].tabstop) or "\t"
diff --git a/lua/conform/formatters/phpcbf.lua b/lua/conform/formatters/phpcbf.lua
index 38f3155..6c6027e 100644
--- a/lua/conform/formatters/phpcbf.lua
+++ b/lua/conform/formatters/phpcbf.lua
@@ -9,7 +9,7 @@ return {
command = util.find_executable({
"vendor/bin/phpcbf",
}, "phpcbf"),
- args = function(ctx)
+ args = function(self, ctx)
return { "-q", "--stdin-path=" .. ctx.filename, "-" }
end,
stdin = true,
diff --git a/lua/conform/formatters/prettier.lua b/lua/conform/formatters/prettier.lua
index ea45ffb..52dda86 100644
--- a/lua/conform/formatters/prettier.lua
+++ b/lua/conform/formatters/prettier.lua
@@ -7,7 +7,7 @@ return {
},
command = util.from_node_modules("prettier"),
args = { "--stdin-filepath", "$FILENAME" },
- range_args = function(ctx)
+ range_args = function(self, ctx)
local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range)
return { "$FILENAME", "--range-start=" .. start_offset, "--range-end=" .. end_offset }
end,
diff --git a/lua/conform/formatters/prettierd.lua b/lua/conform/formatters/prettierd.lua
index 3cdc19e..386d441 100644
--- a/lua/conform/formatters/prettierd.lua
+++ b/lua/conform/formatters/prettierd.lua
@@ -7,7 +7,7 @@ return {
},
command = util.from_node_modules("prettierd"),
args = { "$FILENAME" },
- range_args = function(ctx)
+ range_args = function(self, ctx)
local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range)
return { "$FILENAME", "--range-start=" .. start_offset, "--range-end=" .. end_offset }
end,
diff --git a/lua/conform/formatters/stylua.lua b/lua/conform/formatters/stylua.lua
index d971932..25012bf 100644
--- a/lua/conform/formatters/stylua.lua
+++ b/lua/conform/formatters/stylua.lua
@@ -7,7 +7,7 @@ return {
},
command = "stylua",
args = { "--search-parent-directories", "--stdin-filepath", "$FILENAME", "-" },
- range_args = function(ctx)
+ range_args = function(self, ctx)
local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range)
return {
"--search-parent-directories",
diff --git a/lua/conform/formatters/uncrustify.lua b/lua/conform/formatters/uncrustify.lua
index 2782e96..aec93c2 100644
--- a/lua/conform/formatters/uncrustify.lua
+++ b/lua/conform/formatters/uncrustify.lua
@@ -5,7 +5,7 @@ return {
description = "A source code beautifier for C, C++, C#, ObjectiveC, D, Java, Pawn and Vala.",
},
command = "uncrustify",
- args = function(ctx)
+ args = function(self, ctx)
return { "-q", "-l", vim.bo[ctx.buf].filetype:upper() }
end,
}
diff --git a/lua/conform/formatters/yapf.lua b/lua/conform/formatters/yapf.lua
index fc3cfeb..63bff83 100644
--- a/lua/conform/formatters/yapf.lua
+++ b/lua/conform/formatters/yapf.lua
@@ -6,7 +6,7 @@ return {
},
command = "yapf",
args = { "--quiet" },
- range_args = function(ctx)
+ range_args = function(self, ctx)
return { "--quiet", "--lines", string.format("%d-%d", ctx.range.start[1], ctx.range["end"][1]) }
end,
}
diff --git a/lua/conform/formatters/zprint.lua b/lua/conform/formatters/zprint.lua
index 935a485..240b7ed 100644
--- a/lua/conform/formatters/zprint.lua
+++ b/lua/conform/formatters/zprint.lua
@@ -5,7 +5,7 @@ return {
description = "Formatter for Clojure and EDN.",
},
command = "zprint",
- range_args = function(ctx)
+ range_args = function(self, ctx)
return {
string.format(
"{:input {:range {:start %d :end %d :use-previous-!zprint? true :continue-after-!zprint-error? true}}}",
diff --git a/lua/conform/init.lua b/lua/conform/init.lua
index 50ae6ba..d980b8f 100644
--- a/lua/conform/init.lua
+++ b/lua/conform/init.lua
@@ -8,15 +8,16 @@ local M = {}
---@field available_msg? string
---@class (exact) conform.JobFormatterConfig
----@field command string|fun(ctx: conform.Context): string
----@field args? string|string[]|fun(ctx: conform.Context): string|string[]
----@field range_args? fun(ctx: conform.RangeContext): string|string[]
----@field cwd? fun(ctx: conform.Context): nil|string
+---@field command string|fun(self: conform.JobFormatterConfig, ctx: conform.Context): string
+---@field args? string|string[]|fun(self: conform.JobFormatterConfig, ctx: conform.Context): string|string[]
+---@field range_args? fun(self: conform.JobFormatterConfig, ctx: conform.RangeContext): string|string[]
+---@field cwd? fun(self: conform.JobFormatterConfig, ctx: conform.Context): nil|string
---@field require_cwd? boolean When cwd is not found, don't run the formatter (default false)
---@field stdin? boolean Send buffer contents to stdin (default true)
----@field condition? fun(ctx: conform.Context): boolean
+---@field condition? fun(self: conform.JobFormatterConfig, ctx: conform.Context): boolean
---@field exit_codes? integer[] Exit codes that indicate success (default {0})
----@field env? table<string, any>|fun(ctx: conform.Context): table<string, any>
+---@field env? table<string, any>|fun(self: conform.JobFormatterConfig, ctx: conform.Context): table<string, any>
+---@field options? table
---@class (exact) conform.LuaFormatterConfig
---@field format fun(self: conform.LuaFormatterConfig, ctx: conform.Context, lines: string[], callback: fun(err: nil|string, new_lines: nil|string[]))
@@ -33,8 +34,8 @@ local M = {}
---@class (exact) conform.FormatterConfigOverride : conform.JobFormatterConfig
---@field inherit? boolean
----@field command? string|fun(ctx: conform.Context): string
----@field prepend_args? string|string[]|fun(ctx: conform.Context): string|string[]
+---@field command? string|fun(self: conform.FormatterConfig, ctx: conform.Context): string
+---@field prepend_args? string|string[]|fun(self: conform.FormatterConfig, ctx: conform.Context): string|string[]
---@field options? table
---@class (exact) conform.FormatterMeta
@@ -606,6 +607,7 @@ end
---@param bufnr? integer
---@return conform.FormatterInfo
M.get_formatter_info = function(formatter, bufnr)
+ local util = require("conform.util")
if not bufnr or bufnr == 0 then
bufnr = vim.api.nvim_get_current_buf()
end
@@ -639,19 +641,21 @@ M.get_formatter_info = function(formatter, bufnr)
local command = config.command
if type(command) == "function" then
- command = command(ctx)
+ command = util.compat_call_with_self(formatter, config, command, ctx)
end
if vim.fn.executable(command) == 0 then
available = false
available_msg = "Command not found"
- elseif config.condition and not config.condition(ctx) then
+ elseif
+ config.condition and not util.compat_call_with_self(formatter, config, config.condition, ctx)
+ then
available = false
available_msg = "Condition failed"
end
local cwd = nil
if config.cwd then
- cwd = config.cwd(ctx)
+ cwd = util.compat_call_with_self(formatter, config, config.cwd, ctx)
if available and not cwd and config.require_cwd then
available = false
available_msg = "Root directory not found"
diff --git a/lua/conform/runner.lua b/lua/conform/runner.lua
index 9571588..8137ad1 100644
--- a/lua/conform/runner.lua
+++ b/lua/conform/runner.lua
@@ -8,25 +8,26 @@ local M = {}
---@class (exact) conform.RunOpts
---@field exclusive boolean If true, ensure only a single formatter is running per buffer
+---@param formatter_name string
---@param ctx conform.Context
---@param config conform.JobFormatterConfig
---@return string|string[]
-M.build_cmd = function(ctx, config)
+M.build_cmd = function(formatter_name, ctx, config)
local command = config.command
if type(command) == "function" then
- command = command(ctx)
+ command = util.compat_call_with_self(formatter_name, config, command, ctx)
end
---@type string|string[]
local args = {}
if ctx.range and config.range_args then
- ---@cast ctx conform.RangeContext
- args = config.range_args(ctx)
+ args = util.compat_call_with_self(formatter_name, config, config.range_args, ctx)
elseif config.args then
- if type(config.args) == "function" then
- args = config.args(ctx)
+ local computed_args = config.args
+ if type(computed_args) == "function" then
+ args = util.compat_call_with_self(formatter_name, config, computed_args, ctx)
else
---@diagnostic disable-next-line: cast-local-type
- args = config.args
+ args = computed_args
end
end
@@ -262,14 +263,14 @@ local function run_formatter(bufnr, formatter, config, ctx, input_lines, opts, c
return
end
---@cast config conform.JobFormatterConfig
- local cmd = M.build_cmd(ctx, config)
+ local cmd = M.build_cmd(formatter.name, ctx, config)
local cwd = nil
if config.cwd then
- cwd = config.cwd(ctx)
+ cwd = util.compat_call_with_self(formatter.name, config, config.cwd, ctx)
end
local env = config.env
if type(env) == "function" then
- env = env(ctx)
+ env = util.compat_call_with_self(formatter.name, config, env, ctx)
end
local buffer_text
diff --git a/lua/conform/util.lua b/lua/conform/util.lua
index bbb711a..0ff54de 100644
--- a/lua/conform/util.lua
+++ b/lua/conform/util.lua
@@ -10,11 +10,11 @@ end
---Search parent directories for a relative path to a command
---@param paths string[]
---@param default string
----@return fun(ctx: conform.Context): string
+---@return fun(self: conform.FormatterConfig, ctx: conform.Context): string
---@example
--- local cmd = require("conform.util").find_executable({ "node_modules/.bin/prettier" }, "prettier")
M.find_executable = function(paths, default)
- return function(ctx)
+ return function(self, ctx)
for _, path in ipairs(paths) do
local normpath = vim.fs.normalize(path)
local is_absolute = vim.startswith(normpath, "/")
@@ -46,9 +46,9 @@ M.find_executable = function(paths, default)
end
---@param files string|string[]
----@return fun(ctx: conform.Context): nil|string
+---@return fun(self: conform.FormatterConfig, ctx: conform.Context): nil|string
M.root_file = function(files)
- return function(ctx)
+ return function(self, ctx)
local found = vim.fs.find(files, { upward = true, path = ctx.dirname })[1]
if found then
return vim.fs.dirname(found)
@@ -101,8 +101,8 @@ M.wrap_callback = function(cb, wrapper)
end
---Helper function to add to the default args of a formatter.
----@param args string|string[]|fun(ctx: conform.Context): string|string[]
----@param extra_args string|string[]|fun(ctx: conform.Context): string|string[]
+---@param args string|string[]|fun(self: conform.FormatterConfig, ctx: conform.Context): string|string[]
+---@param extra_args string|string[]|fun(self: conform.FormatterConfig, ctx: conform.Context): string|string[]
---@param opts? { append?: boolean }
---@example
--- local util = require("conform.util")
@@ -113,12 +113,12 @@ end
--- })
M.extend_args = function(args, extra_args, opts)
opts = opts or {}
- return function(ctx)
+ return function(self, ctx)
if type(args) == "function" then
- args = args(ctx)
+ args = M.compat_call_with_self("unknown", self, args, ctx)
end
if type(extra_args) == "function" then
- extra_args = extra_args(ctx)
+ extra_args = M.compat_call_with_self("unknown", self, extra_args, ctx)
end
if type(args) == "string" then
if type(extra_args) ~= "string" then
@@ -143,7 +143,7 @@ M.extend_args = function(args, extra_args, opts)
end
---@param formatter conform.FormatterConfig
----@param extra_args string|string[]|fun(ctx: conform.Context): string|string[]
+---@param extra_args string|string[]|fun(self: conform.FormatterConfig, ctx: conform.Context): string|string[]
---@param opts? { append?: boolean }
---@example
--- local util = require("conform.util")
@@ -183,4 +183,46 @@ M.buf_get_changedtick = function(bufnr)
end
end
+---Returns true if the function takes no args or has self as the first arg
+---@param name string
+---@param fn function(...: any): T
+---@return boolean
+local function has_self_arg(name, fn)
+ local first_arg_name = nil
+ debug.sethook(function()
+ local info = debug.getinfo(3)
+ if info.name ~= "pcall" then
+ return
+ end
+ first_arg_name = debug.getlocal(2, 1)
+ error()
+ end, "c")
+ pcall(fn)
+ debug.sethook()
+
+ return first_arg_name == "self" or first_arg_name == nil
+end
+
+---@generic T
+---@param formatter_name string
+---@param self any
+---@param fn fun(...: any): T
+---@param ... any
+---@return T
+M.compat_call_with_self = function(formatter_name, self, fn, ...)
+ local has_self = has_self_arg(formatter_name, fn)
+ if has_self then
+ return fn(self, ...)
+ else
+ vim.notify_once(
+ string.format(
+ "[conform] formatter %s should take 'self' as the first argument for args, range_args, cwd, condition, and env functions (see :help conform-self-args)\nCompatibility will be dropped on 2024-03-01",
+ formatter_name
+ ),
+ vim.log.levels.WARN
+ )
+ return fn(...)
+ end
+end
+
return M
diff --git a/scripts/generate.py b/scripts/generate.py
index f9db2a2..b4da7b4 100755
--- a/scripts/generate.py
+++ b/scripts/generate.py
@@ -141,17 +141,41 @@ def gen_options_vimdoc() -> VimdocSection:
return section
-def gen_autocmd_vimdoc() -> VimdocSection:
- section = VimdocSection("Autoformat", "conform-autoformat", ["\n"])
+def gen_self_compat_vimdoc() -> VimdocSection:
+ section = VimdocSection("self argument migration", "conform-self-args", ["\n"])
section.body.extend(
wrap(
- "If you want more complex logic than the `format_on_save` option allows, you can write it yourself using your own autocmd. For example:"
+ "The function arguments for formatter config functions have changed. Previously, they took a single `ctx` argument."
+ )
+ )
+ section.body.append(
+ """>lua
+ {
+ command = "phpcbf",
+ args = function(ctx)
+ return { "-q", "--stdin-path=" .. ctx.filename, "-" }
+ end
+ }
+<"""
+ )
+ section.body.extend(
+ wrap("Now, they take `self` as the first argument, and `ctx` as the second.")
+ )
+ section.body.append(
+ """>lua
+ {
+ command = "phpcbf",
+ args = function(self, ctx)
+ return { "-q", "--stdin-path=" .. ctx.filename, "-" }
+ end
+ }
+<"""
+ )
+ section.body.extend(
+ wrap(
+ "The config values that can be defined as functions are: `command`, `args`, `range_args`, `cwd`, `env`, and `condition`."
)
)
- section.body.append(">lua\n")
- with open(AUTOFORMAT, "r", encoding="utf-8") as f:
- section.body.extend(indent(f.readlines(), 4))
- section.body.append("<\n")
return section
@@ -171,7 +195,7 @@ def generate_vimdoc():
gen_options_vimdoc(),
VimdocSection("API", "conform-api", render_vimdoc_api("conform", funcs)),
gen_formatter_vimdoc(),
- gen_autocmd_vimdoc(),
+ gen_self_compat_vimdoc(),
]
)
diff --git a/tests/runner_spec.lua b/tests/runner_spec.lua
index 020afac..c9cf75a 100644
--- a/tests/runner_spec.lua
+++ b/tests/runner_spec.lua
@@ -70,7 +70,7 @@ describe("runner", function()
}
local config = assert(conform.get_formatter_config("test"))
local ctx = runner.build_context(0, config)
- local cmd = runner.build_cmd(ctx, config)
+ local cmd = runner.build_cmd("", ctx, config)
assert.are.same({ "echo", vim.api.nvim_buf_get_name(bufnr) }, cmd)
end)
@@ -84,7 +84,7 @@ describe("runner", function()
}
local config = assert(conform.get_formatter_config("test"))
local ctx = runner.build_context(0, config)
- local cmd = runner.build_cmd(ctx, config)
+ local cmd = runner.build_cmd("", ctx, config)
assert.are.same({ "echo", vim.fs.dirname(vim.api.nvim_buf_get_name(bufnr)) }, cmd)
end)
@@ -99,7 +99,7 @@ describe("runner", function()
}
local config = assert(conform.get_formatter_config("test"))
local ctx = runner.build_context(0, config)
- local cmd = runner.build_cmd(ctx, config)
+ local cmd = runner.build_cmd("", ctx, config)
assert.are.same({ "echo", "--stdin" }, cmd)
end)
@@ -113,7 +113,7 @@ describe("runner", function()
}
local config = assert(conform.get_formatter_config("test"))
local ctx = runner.build_context(0, config)
- local cmd = runner.build_cmd(ctx, config)
+ local cmd = runner.build_cmd("", ctx, config)
assert.equal("echo " .. vim.api.nvim_buf_get_name(bufnr) .. " | patch", cmd)
end)
@@ -127,7 +127,7 @@ describe("runner", function()
}
local config = assert(conform.get_formatter_config("test"))
local ctx = runner.build_context(0, config)
- local cmd = runner.build_cmd(ctx, config)
+ local cmd = runner.build_cmd("", ctx, config)
assert.equal("echo " .. vim.fs.dirname(vim.api.nvim_buf_get_name(bufnr)) .. " | patch", cmd)
end)
@@ -142,7 +142,7 @@ describe("runner", function()
}
local config = assert(conform.get_formatter_config("test"))
local ctx = runner.build_context(0, config)
- local cmd = runner.build_cmd(ctx, config)
+ local cmd = runner.build_cmd("", ctx, config)
assert.equal("echo | patch", cmd)
end)
end)