1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-09 10:00:04 +08:00
SpaceVim/bundle/nvim-surround/lua/nvim-surround/config.lua

886 lines
31 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local input = require("nvim-surround.input")
local functional = require("nvim-surround.functional")
local M = {}
---@type user_options
M.default_opts = {
keymaps = {
insert = "<C-g>s",
insert_line = "<C-g>S",
normal = "ys",
normal_cur = "yss",
normal_line = "yS",
normal_cur_line = "ySS",
visual = "S",
visual_line = "gS",
delete = "ds",
change = "cs",
change_line = "cS",
},
surrounds = {
["("] = {
add = { "( ", " )" },
find = function()
return M.get_selection({ motion = "a(" })
end,
delete = "^(. ?)().-( ?.)()$",
},
[")"] = {
add = { "(", ")" },
find = function()
return M.get_selection({ motion = "a)" })
end,
delete = "^(.)().-(.)()$",
},
["{"] = {
add = { "{ ", " }" },
find = function()
return M.get_selection({ motion = "a{" })
end,
delete = "^(. ?)().-( ?.)()$",
},
["}"] = {
add = { "{", "}" },
find = function()
return M.get_selection({ motion = "a}" })
end,
delete = "^(.)().-(.)()$",
},
["<"] = {
add = { "< ", " >" },
find = function()
return M.get_selection({ motion = "a<" })
end,
delete = "^(. ?)().-( ?.)()$",
},
[">"] = {
add = { "<", ">" },
find = function()
return M.get_selection({ motion = "a>" })
end,
delete = "^(.)().-(.)()$",
},
["["] = {
add = { "[ ", " ]" },
find = function()
return M.get_selection({ motion = "a[" })
end,
delete = "^(. ?)().-( ?.)()$",
},
["]"] = {
add = { "[", "]" },
find = function()
return M.get_selection({ motion = "a]" })
end,
delete = "^(.)().-(.)()$",
},
["'"] = {
add = { "'", "'" },
find = function()
return M.get_selection({ motion = "a'" })
end,
delete = "^(.)().-(.)()$",
},
['"'] = {
add = { '"', '"' },
find = function()
return M.get_selection({ motion = 'a"' })
end,
delete = "^(.)().-(.)()$",
},
["`"] = {
add = { "`", "`" },
find = function()
return M.get_selection({ motion = "a`" })
end,
delete = "^(.)().-(.)()$",
},
["i"] = { -- TODO: Add find/delete/change functions
add = function()
local left_delimiter = M.get_input("Enter the left delimiter: ")
local right_delimiter = left_delimiter and M.get_input("Enter the right delimiter: ")
if right_delimiter then
return { { left_delimiter }, { right_delimiter } }
end
end,
find = function() end,
delete = function() end,
},
["t"] = {
add = function()
local user_input = M.get_input("Enter the HTML tag: ")
if user_input then
local element = user_input:match("^<?([^%s>]*)")
local attributes = user_input:match("^<?[^%s>]*%s+(.-)>?$")
local open = attributes and element .. " " .. attributes or element
local close = element
return { { "<" .. open .. ">" }, { "</" .. close .. ">" } }
end
end,
find = function()
return M.get_selection({ motion = "at" })
end,
delete = "^(%b<>)().-(%b<>)()$",
change = {
target = "^<([^%s<>]*)().-([^/]*)()>$",
replacement = function()
local user_input = M.get_input("Enter the HTML tag: ")
if user_input then
local element = user_input:match("^<?([^%s>]*)")
local attributes = user_input:match("^<?[^%s>]*%s+(.-)>?$")
local open = attributes and element .. " " .. attributes or element
local close = element
return { { open }, { close } }
end
end,
},
},
["T"] = {
add = function()
local user_input = M.get_input("Enter the HTML tag: ")
if user_input then
local element = user_input:match("^<?([^%s>]*)")
local attributes = user_input:match("^<?[^%s>]*%s+(.-)>?$")
local open = attributes and element .. " " .. attributes or element
local close = element
return { { "<" .. open .. ">" }, { "</" .. close .. ">" } }
end
end,
find = function()
return M.get_selection({ motion = "at" })
end,
delete = "^(%b<>)().-(%b<>)()$",
change = {
target = "^<([^>]*)().-([^/]*)()>$",
replacement = function()
local user_input = M.get_input("Enter the HTML tag: ")
if user_input then
local element = user_input:match("^<?([^%s>]*)")
local attributes = user_input:match("^<?[^%s>]*%s+(.-)>?$")
local open = attributes and element .. " " .. attributes or element
local close = element
return { { open }, { close } }
end
end,
},
},
["f"] = {
add = function()
local result = M.get_input("Enter the function name: ")
if result then
return { { result .. "(" }, { ")" } }
end
end,
find = function()
if vim.g.loaded_nvim_treesitter then
local selection = M.get_selection({
query = {
capture = "@call.outer",
type = "textobjects",
},
})
if selection then
return selection
end
end
return M.get_selection({ pattern = "[^=%s%(%){}]+%b()" })
end,
delete = "^(.-%()().-(%))()$",
change = {
target = "^.-([%w_]+)()%(.-%)()()$",
replacement = function()
local result = M.get_input("Enter the function name: ")
if result then
return { { result }, { "" } }
end
end,
},
},
invalid_key_behavior = {
-- By default, we ignore control characters for adding/finding because they are more likely typos than
-- intentional. We choose NOT to for deletion, as users could have redefined the find key to something like
-- .-. In this case we should still trim a character from each side, instead of early returning nil.
add = function(char)
if not char or char:find("%c") then
return nil
end
return { { char }, { char } }
end,
find = function(char)
if not char or char:find("%c") then
return nil
end
return M.get_selection({
pattern = vim.pesc(char) .. ".-" .. vim.pesc(char),
})
end,
delete = function(char)
if not char then
return nil
end
return M.get_selections({
char = char,
pattern = "^(.)().-(.)()$",
})
end,
},
},
aliases = {
["a"] = ">",
["b"] = ")",
["B"] = "}",
["r"] = "]",
["q"] = { '"', "'", "`" },
["s"] = { "}", "]", ")", ">", '"', "'", "`" },
},
highlight = {
duration = 0,
},
move_cursor = "begin",
indent_lines = function(start, stop)
local b = vim.bo
-- Only re-indent the selection if a formatter is set up already
if start < stop and (b.equalprg ~= "" or b.indentexpr ~= "" or b.cindent or b.smartindent or b.lisp) then
vim.cmd(string.format("silent normal! %dG=%dG", start, stop))
require("nvim-surround.cache").set_callback("")
end
end,
}
--[====================================================================================================================[
Configuration Helper Functions
--]====================================================================================================================]
-- Gets input from the user.
---@param prompt string The input prompt.
---@return string|nil @The user input.
---@nodiscard
M.get_input = function(prompt)
return input.get_input(prompt)
end
-- Gets a selection from the buffer based on some heuristic.
---@param args { char: string|nil, motion: string|nil, pattern: string|nil, node: string|nil, query: { capture: string, type: string }|nil }
---@return selection|nil @The retrieved selection.
---@nodiscard
M.get_selection = function(args)
if args.char then
return M.get_find(args.char)(args.char)
elseif args.motion then
return require("nvim-surround.motions").get_selection(args.motion)
elseif args.node then
return require("nvim-surround.treesitter").get_selection(args.node)
elseif args.pattern then
return require("nvim-surround.patterns").get_selection(args.pattern)
elseif args.query then
return require("nvim-surround.queries").get_selection(args.query.capture, args.query.type)
else
vim.notify("Invalid key provided for `:h nvim-surround.config.get_selection()`.", vim.log.levels.ERROR)
end
end
-- Gets a pair of selections from the buffer based on some heuristic.
---@param args { char: string, pattern: string|nil, exclude: function|nil }
---@nodiscard
M.get_selections = function(args)
local selection = M.get_selection({ char = args.char })
if not selection then
return nil
end
if args.pattern then
return require("nvim-surround.patterns").get_selections(selection, args.pattern)
elseif args.exclude then
local outer_selection = M.get_opts().surrounds[args.char].find()
if not outer_selection then
return nil
end
vim.fn.cursor(outer_selection.first_pos)
local inner_selection = args.exclude()
if not inner_selection then
return nil
end
-- Properly exclude the inner selection from the outer selection
local selections = {
left = {
first_pos = outer_selection.first_pos,
last_pos = { inner_selection.first_pos[1], inner_selection.first_pos[2] - 1 },
},
right = {
first_pos = { inner_selection.last_pos[1], inner_selection.last_pos[2] + 1 },
last_pos = outer_selection.last_pos,
},
}
return selections
else
vim.notify("Invalid key provided for `:h nvim-surround.config.get_selections()`.", vim.log.levels.ERROR)
end
end
--[====================================================================================================================[
End of Helper Functions
--]====================================================================================================================]
-- Stores the global user-set options for the plugin.
M.user_opts = nil
-- Returns the buffer-local options for the plugin, or global options if buffer-local does not exist.
---@return options @The buffer-local options.
---@nodiscard
M.get_opts = function()
return vim.b[0].nvim_surround_buffer_opts or M.user_opts or {}
end
-- Returns the value that the input is aliased to, or the character if no alias exists.
---@param char string|nil The input character.
---@return string|nil @The aliased character if it exists, or the original if none exists.
---@nodiscard
M.get_alias = function(char)
local aliases = M.get_opts().aliases
if type(aliases[char]) == "string" then
return aliases[char]
end
return char
end
-- Gets a delimiter pair for a user-inputted character.
---@param char string|nil The user-given character.
---@param line_mode boolean Whether or not the delimiters should be put on new lines.
---@return delimiter_pair|nil @A pair of delimiters for the given input, or nil if not applicable.
---@nodiscard
M.get_delimiters = function(char, line_mode)
char = M.get_alias(char)
-- Get the delimiters, using invalid_key_behavior if the add function is undefined for the character
local delimiters = M.get_add(char)(char)
-- Add new lines if the addition is done line-wise
if delimiters and line_mode then
local lhs = delimiters[1]
local rhs = delimiters[2]
-- Trim whitespace after the leading delimiter and before the trailing delimiter
lhs[#lhs] = lhs[#lhs]:gsub("%s+$", "")
rhs[1] = rhs[1]:gsub("^%s+", "")
table.insert(rhs, 1, "")
table.insert(lhs, "")
end
return delimiters
end
-- Returns the add key for the surround associated with a given character, if one exists.
---@param char string|nil The input character.
---@return add_func @The function to get the delimiters to be added.
---@nodiscard
M.get_add = function(char)
char = M.get_alias(char)
if M.get_opts().surrounds[char] and M.get_opts().surrounds[char].add then
return M.get_opts().surrounds[char].add
end
return M.get_opts().surrounds.invalid_key_behavior.add
end
-- Returns the find key for the surround associated with a given character, if one exists.
---@param char string|nil The input character.
---@return find_func @The function to get the selection.
---@nodiscard
M.get_find = function(char)
char = M.get_alias(char)
if M.get_opts().surrounds[char] and M.get_opts().surrounds[char].find then
return M.get_opts().surrounds[char].find
end
return M.get_opts().surrounds.invalid_key_behavior.find
end
-- Returns the delete key for the surround associated with a given character, if one exists.
---@param char string|nil The input character.
---@return delete_func @The function to get the selections to be deleted.
---@nodiscard
M.get_delete = function(char)
char = M.get_alias(char)
if M.get_opts().surrounds[char] and M.get_opts().surrounds[char].delete then
return M.get_opts().surrounds[char].delete
end
return M.get_opts().surrounds.invalid_key_behavior.delete
end
-- Returns the change key for the surround associated with a given character, if one exists.
---@param char string|nil The input character.
---@return change_table @A table holding the target/replacement functions.
---@nodiscard
M.get_change = function(char)
char = M.get_alias(char)
if M.get_opts().surrounds[char] then
if M.get_opts().surrounds[char].change then
return M.get_opts().surrounds[char].change
else
return {
target = M.get_delete(char),
}
end
end
return M.get_change("invalid_key_behavior")
end
-- Translates the user-provided surround.add into the internal form.
---@param user_add user_add The user-provided add key.
---@return false|add_func|nil @The translated add key.
M.translate_add = function(user_add)
if type(user_add) ~= "table" then
return user_add
end
-- If the input is given as a pair of strings, or pair of string lists, wrap it in a function
return function()
return {
functional.to_list(user_add[1]),
functional.to_list(user_add[2]),
}
end
end
-- Translates the user-provided surround.find into the internal form.
---@param user_find user_find The user-provided find key.
---@return false|find_func|nil @The translated find key.
M.translate_find = function(user_find)
if type(user_find) ~= "string" then
return user_find
end
-- Treat the string as a Lua pattern, and find the selection
return function()
return M.get_selection({ pattern = user_find })
end
end
-- Translates the user-provided surround.delete into the internal form.
---@param char string The character used to activate the surround.
---@param user_delete user_delete The user-provided delete key.
---@return false|delete_func|nil @The translated delete key.
M.translate_delete = function(char, user_delete)
if type(user_delete) ~= "string" then
return user_delete
end
-- Treat the string as a Lua pattern, and find the selection
return function()
return M.get_selections({ char = char, pattern = user_delete })
end
end
-- Translates the user-provided surround.change into the internal form.
---@param char string The character used to activate the surround.
---@param user_change user_change|nil The user-provided change key.
---@return false|change_table|nil @The translated change key.
M.translate_change = function(char, user_change)
if type(user_change) ~= "table" then
return user_change
end
return {
target = M.translate_delete(char, user_change.target),
replacement = M.translate_add(user_change.replacement),
}
end
-- Translates the user-provided surround into the internal form.
---@param char string The character used to activate the surround.
---@param user_surround false|user_surround The user-provided surround.
---@return false|surround @The translated surround.
M.translate_surround = function(char, user_surround)
if not user_surround then
return false
end
return {
add = M.translate_add(user_surround.add),
find = M.translate_find(user_surround.find),
delete = M.translate_delete(char, user_surround.delete),
change = M.translate_change(char, user_surround.change),
}
end
-- Translates `invalid_key_behavior` into the internal form.
---@param invalid_surround false|user_surround The user-provided `invalid_key_behavior`.
---@return surround @The translated `invalid_key_behavior`.
M.translate_invalid_key_behavior = function(invalid_surround)
local noop_surround = {
add = function() end,
find = function() end,
delete = function() end,
change = {
target = function() end,
},
}
local invalid = M.translate_surround("invalid_key_behavior", invalid_surround)
if invalid == false then
return noop_surround
end
if invalid.add == false then
invalid.add = noop_surround.add
end
if invalid.find == false then
invalid.find = noop_surround.find
end
if invalid.delete == false then
invalid.delete = noop_surround.delete
end
if invalid.change == false then
invalid.change = noop_surround.change
elseif invalid.change ~= nil then
if invalid.change.target == false then
invalid.change.target = noop_surround.change.target
end
end
return invalid
end
-- Translates the user-provided configuration into the internal form.
---@param user_opts user_options The user-provided options.
---@return options @The translated options.
M.translate_opts = function(user_opts)
local opts = {}
for key, value in pairs(user_opts) do
if key == "surrounds" then
elseif key == "indent_lines" then
opts[key] = value or function() end
else
opts[key] = value
end
end
if not user_opts.surrounds then
return opts
end
opts.surrounds = {}
for char, user_surround in pairs(user_opts.surrounds) do
-- Support Vim's notation for special characters
char = vim.api.nvim_replace_termcodes(char, true, true, true)
-- Special case translation for `invalid_key_behavior`
if type(user_surround) ~= "nil" then
if char == "invalid_key_behavior" then
opts.surrounds[char] = M.translate_invalid_key_behavior(user_surround)
else
opts.surrounds[char] = M.translate_surround(char, user_surround)
end
end
end
return opts
end
-- Updates the buffer-local options for the plugin based on the input.
---@param base_opts options The base options that will be used for configuration.
---@param new_opts user_options|nil The new options to potentially override the base options.
---@return options The merged options.
M.merge_opts = function(base_opts, new_opts)
new_opts = new_opts or {}
local opts = vim.tbl_deep_extend("force", base_opts, M.translate_opts(new_opts))
return opts
end
-- Check if a keymap should be added before setting it.
---@param args table The arguments to set the keymap.
M.set_keymap = function(args)
-- If the keymap is disabled
if not args.lhs then
-- If the mapping is disabled globally, do nothing
if not M.user_opts.keymaps[args.name] then
return
end
-- Otherwise disable the global keymap
args.lhs = M.user_opts.keymaps[args.name]
args.rhs = "<NOP>"
end
vim.keymap.set(args.mode, args.lhs, args.rhs, args.opts)
end
-- Set up user-configured keymaps, globally or for the buffer.
---@param args { buffer: boolean } Whether the keymaps should be set for the buffer or not.
M.set_keymaps = function(args)
-- Set up <Plug> keymaps
M.set_keymap({
mode = "i",
lhs = "<Plug>(nvim-surround-insert)",
rhs = function()
require("nvim-surround").insert_surround({ line_mode = false })
end,
opts = {
buffer = args.buffer,
desc = "Add a surrounding pair around the cursor (insert mode)",
silent = true,
},
})
M.set_keymap({
mode = "i",
lhs = "<Plug>(nvim-surround-insert-line)",
rhs = function()
require("nvim-surround").insert_surround({ line_mode = true })
end,
opts = {
buffer = args.buffer,
desc = "Add a surrounding pair around the cursor, on new lines (insert mode)",
silent = true,
},
})
M.set_keymap({
mode = "n",
lhs = "<Plug>(nvim-surround-normal)",
rhs = function()
return require("nvim-surround").normal_surround({ line_mode = false })
end,
opts = {
buffer = args.buffer,
desc = "Add a surrounding pair around a motion (normal mode)",
expr = true,
silent = true,
},
})
M.set_keymap({
mode = "n",
lhs = "<Plug>(nvim-surround-normal-cur)",
rhs = function()
return "<Plug>(nvim-surround-normal)Vg_"
end,
opts = {
buffer = args.buffer,
desc = "Add a surrounding pair around the current line (normal mode)",
expr = true,
silent = true,
},
})
M.set_keymap({
mode = "n",
lhs = "<Plug>(nvim-surround-normal-line)",
rhs = function()
return require("nvim-surround").normal_surround({ line_mode = true })
end,
opts = {
buffer = args.buffer,
desc = "Add a surrounding pair around a motion, on new lines (normal mode)",
expr = true,
silent = true,
},
})
M.set_keymap({
mode = "n",
lhs = "<Plug>(nvim-surround-normal-cur-line)",
rhs = function()
return "^" .. tostring(vim.v.count1) .. "<Plug>(nvim-surround-normal-line)g_"
end,
opts = {
buffer = args.buffer,
desc = "Add a surrounding pair around the current line, on new lines (normal mode)",
expr = true,
silent = true,
},
})
M.set_keymap({
mode = "x",
lhs = "<Plug>(nvim-surround-visual)",
rhs = function()
local curpos = require("nvim-surround.buffer").get_curpos()
return string.format(
":lua require'nvim-surround'.visual_surround({ line_mode = false, curpos = { %d, %d }, curswant = %d })<CR>",
curpos[1],
curpos[2],
vim.fn.winsaveview().curswant
)
end,
opts = {
buffer = args.buffer,
desc = "Add a surrounding pair around a visual selection",
silent = true,
expr = true,
},
})
M.set_keymap({
mode = "x",
lhs = "<Plug>(nvim-surround-visual-line)",
rhs = function()
local curpos = require("nvim-surround.buffer").get_curpos()
return string.format(
":lua require'nvim-surround'.visual_surround({ line_mode = true, curpos = { %d, %d }, curswant = 0 })<CR>",
curpos[1],
curpos[2]
)
end,
opts = {
buffer = args.buffer,
desc = "Add a surrounding pair around a visual selection, on new lines",
silent = true,
expr = true,
},
})
M.set_keymap({
mode = "n",
lhs = "<Plug>(nvim-surround-delete)",
rhs = require("nvim-surround").delete_surround,
opts = {
buffer = args.buffer,
desc = "Delete a surrounding pair",
expr = true,
silent = true,
},
})
M.set_keymap({
mode = "n",
lhs = "<Plug>(nvim-surround-change)",
rhs = function()
return require("nvim-surround").change_surround({ line_mode = false })
end,
opts = {
buffer = args.buffer,
desc = "Change a surrounding pair",
expr = true,
silent = true,
},
})
M.set_keymap({
mode = "n",
lhs = "<Plug>(nvim-surround-change-line)",
rhs = function()
return require("nvim-surround").change_surround({ line_mode = true })
end,
opts = {
buffer = args.buffer,
desc = "Change a surrounding pair, putting replacements on new lines",
expr = true,
silent = true,
},
})
-- Set up user-defined keymaps
M.set_keymap({
name = "insert",
mode = "i",
lhs = M.get_opts().keymaps.insert,
rhs = "<Plug>(nvim-surround-insert)",
opts = {
desc = "Add a surrounding pair around the cursor (insert mode)",
},
})
M.set_keymap({
name = "insert_line",
mode = "i",
lhs = M.get_opts().keymaps.insert_line,
rhs = "<Plug>(nvim-surround-insert-line)",
opts = {
desc = "Add a surrounding pair around the cursor, on new lines (insert mode)",
},
})
M.set_keymap({
name = "normal",
mode = "n",
lhs = M.get_opts().keymaps.normal,
rhs = "<Plug>(nvim-surround-normal)",
opts = {
desc = "Add a surrounding pair around a motion (normal mode)",
},
})
M.set_keymap({
name = "normal_cur",
mode = "n",
lhs = M.get_opts().keymaps.normal_cur,
rhs = "<Plug>(nvim-surround-normal-cur)",
opts = {
desc = "Add a surrounding pair around the current line (normal mode)",
},
})
M.set_keymap({
name = "normal_line",
mode = "n",
lhs = M.get_opts().keymaps.normal_line,
rhs = "<Plug>(nvim-surround-normal-line)",
opts = {
desc = "Add a surrounding pair around a motion, on new lines (normal mode)",
},
})
M.set_keymap({
name = "normal_cur_line",
mode = "n",
lhs = M.get_opts().keymaps.normal_cur_line,
rhs = "<Plug>(nvim-surround-normal-cur-line)",
opts = {
desc = "Add a surrounding pair around the current line, on new lines (normal mode)",
},
})
M.set_keymap({
name = "visual",
mode = "x",
lhs = M.get_opts().keymaps.visual,
rhs = "<Plug>(nvim-surround-visual)",
opts = {
desc = "Add a surrounding pair around a visual selection",
},
})
M.set_keymap({
name = "visual_line",
mode = "x",
lhs = M.get_opts().keymaps.visual_line,
rhs = "<Plug>(nvim-surround-visual-line)",
opts = {
desc = "Add a surrounding pair around a visual selection, on new lines",
},
})
M.set_keymap({
name = "delete",
mode = "n",
lhs = M.get_opts().keymaps.delete,
rhs = "<Plug>(nvim-surround-delete)",
opts = {
desc = "Delete a surrounding pair",
},
})
M.set_keymap({
name = "change",
mode = "n",
lhs = M.get_opts().keymaps.change,
rhs = "<Plug>(nvim-surround-change)",
opts = {
desc = "Change a surrounding pair",
},
})
M.set_keymap({
name = "change_line",
mode = "n",
lhs = M.get_opts().keymaps.change_line,
rhs = "<Plug>(nvim-surround-change-line)",
opts = {
desc = "Change a surrounding pair, putting replacements on new lines",
},
})
end
-- Setup the global user options for all files.
---@param user_opts user_options|nil The user-defined options to be merged with default_opts.
M.setup = function(user_opts)
-- Overwrite default options with user-defined options, if they exist
M.user_opts = M.merge_opts(M.translate_opts(M.default_opts), user_opts)
M.set_keymaps({ buffer = false })
-- Configure highlight group, if necessary
if M.user_opts.highlight.duration then
vim.cmd.highlight("default link NvimSurroundHighlight Visual")
end
-- Intercept dot repeat action, remembering cursor position
local buffer = require("nvim-surround.buffer")
local nvim_surround = require("nvim-surround")
vim.on_key(function(key)
if key == "." and not nvim_surround.pending_surround then
nvim_surround.normal_curpos = buffer.get_curpos()
end
end)
end
-- Setup the user options for the current buffer.
---@param buffer_opts user_options|nil The buffer-local options to be merged with the global user_opts.
M.buffer_setup = function(buffer_opts)
-- Merge the given table into the existing buffer-local options, or global options otherwise
vim.b[0].nvim_surround_buffer_opts = M.merge_opts(M.get_opts(), buffer_opts)
M.set_keymaps({ buffer = true })
end
return M