modules: start breaking down core modules; simplify tree structure

This commit is contained in:
raf 2024-02-17 04:02:15 +03:00
commit 370913e827
No known key found for this signature in database
GPG key ID: 02D1DD3FA08B6B29
242 changed files with 178 additions and 124 deletions

View file

@ -0,0 +1,113 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) addDescriptionsToMappings mkIf optional boolToString optionalString;
cfg = config.vim.lsp;
usingNvimCmp = config.vim.autocomplete.enable && config.vim.autocomplete.type == "nvim-cmp";
self = import ./module.nix {inherit config lib pkgs;};
mappingDefinitions = self.options.vim.lsp.mappings;
mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions;
mkBinding = binding: action: "vim.api.nvim_buf_set_keymap(bufnr, 'n', '${binding.value}', '<cmd>lua ${action}<CR>', {noremap=true, silent=true, desc='${binding.description}'})";
in {
config = mkIf cfg.enable {
vim.startPlugins = optional usingNvimCmp "cmp-nvim-lsp";
vim.autocomplete.sources = {"nvim_lsp" = "[LSP]";};
vim.luaConfigRC.lsp-setup = ''
vim.g.formatsave = ${boolToString cfg.formatOnSave};
local attach_keymaps = function(client, bufnr)
${mkBinding mappings.goToDeclaration "vim.lsp.buf.declaration()"}
${mkBinding mappings.goToDefinition "vim.lsp.buf.definition()"}
${mkBinding mappings.goToType "vim.lsp.buf.type_definition()"}
${mkBinding mappings.listImplementations "vim.lsp.buf.implementation()"}
${mkBinding mappings.listReferences "vim.lsp.buf.references()"}
${mkBinding mappings.nextDiagnostic "vim.diagnostic.goto_next()"}
${mkBinding mappings.previousDiagnostic "vim.diagnostic.goto_prev()"}
${mkBinding mappings.openDiagnosticFloat "vim.diagnostic.open_float()"}
${mkBinding mappings.documentHighlight "vim.lsp.buf.document_highlight()"}
${mkBinding mappings.listDocumentSymbols "vim.lsp.buf.document_symbol()"}
${mkBinding mappings.addWorkspaceFolder "vim.lsp.buf.add_workspace_folder()"}
${mkBinding mappings.removeWorkspaceFolder "vim.lsp.buf.remove_workspace_folder()"}
${mkBinding mappings.listWorkspaceFolders "print(vim.inspect(vim.lsp.buf.list_workspace_folders()))"}
${mkBinding mappings.listWorkspaceSymbols "vim.lsp.buf.workspace_symbol()"}
${mkBinding mappings.hover "vim.lsp.buf.hover()"}
${mkBinding mappings.signatureHelp "vim.lsp.buf.signature_help()"}
${mkBinding mappings.renameSymbol "vim.lsp.buf.rename()"}
${mkBinding mappings.codeAction "vim.lsp.buf.code_action()"}
${mkBinding mappings.format "vim.lsp.buf.format()"}
${mkBinding mappings.toggleFormatOnSave "vim.b.disableFormatSave = not vim.b.disableFormatSave"}
end
-- Enable formatting
local augroup = vim.api.nvim_create_augroup("LspFormatting", {})
format_callback = function(client, bufnr)
if vim.g.formatsave then
vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
vim.api.nvim_create_autocmd("BufWritePre", {
group = augroup,
buffer = bufnr,
callback = function()
${
if config.vim.lsp.null-ls.enable
then ''
if vim.b.disableFormatSave then
return
end
local function is_null_ls_formatting_enabled(bufnr)
local file_type = vim.api.nvim_buf_get_option(bufnr, "filetype")
local generators = require("null-ls.generators").get_available(
file_type,
require("null-ls.methods").internal.FORMATTING
)
return #generators > 0
end
if is_null_ls_formatting_enabled(bufnr) then
vim.lsp.buf.format({
bufnr = bufnr,
filter = function(client)
return client.name == "null-ls"
end
})
else
vim.lsp.buf.format({
bufnr = bufnr,
})
end
''
else "
vim.lsp.buf.format({
bufnr = bufnr,
})
"
}
end,
})
end
end
${optionalString (config.vim.ui.breadcrumbs.enable) ''local navic = require("nvim-navic")''}
default_on_attach = function(client, bufnr)
attach_keymaps(client, bufnr)
format_callback(client, bufnr)
${optionalString (config.vim.ui.breadcrumbs.enable) ''
-- let navic attach to buffers
if client.server_capabilities.documentSymbolProvider then
navic.attach(client, bufnr)
end
''}
end
local capabilities = vim.lsp.protocol.make_client_capabilities()
${optionalString usingNvimCmp "capabilities = require('cmp_nvim_lsp').default_capabilities()"}
'';
};
}

View file

@ -0,0 +1,21 @@
_: {
imports = [
# nvim lsp support
./config.nix
./module.nix
./lspconfig
./lspsaga
./null-ls
# lsp plugins
./lspsaga
./nvim-code-action-menu
./trouble
./lsp-signature
./lightbulb
./lspkind
./lsplines
./nvim-docs-view
];
}

View file

@ -0,0 +1,22 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf nvim;
cfg = config.vim.lsp;
in {
config = mkIf (cfg.enable && cfg.lightbulb.enable) {
vim.startPlugins = ["nvim-lightbulb"];
vim.configRC.lightbulb = nvim.dag.entryAnywhere ''
autocmd CursorHold,CursorHoldI * lua require'nvim-lightbulb'.update_lightbulb()
'';
vim.luaConfigRC.lightbulb = nvim.dag.entryAnywhere ''
-- Enable trouble diagnostics viewer
require'nvim-lightbulb'.setup()
'';
};
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./lightbulb.nix
./config.nix
];
}

View file

@ -0,0 +1,13 @@
{
config,
lib,
...
}: let
inherit (lib) mkEnableOption;
in {
options.vim.lsp = {
lightbulb = {
enable = mkEnableOption "Lightbulb for code actions. Requires an emoji font";
};
};
}

View file

@ -0,0 +1,27 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf nvim optionalString;
cfg = config.vim.lsp;
in {
config = mkIf (cfg.enable && cfg.lspSignature.enable) {
vim.startPlugins = [
"lsp-signature"
];
vim.luaConfigRC.lsp-signature = nvim.dag.entryAnywhere ''
-- Enable lsp signature viewer
require("lsp_signature").setup({
${optionalString (config.vim.ui.borders.plugins.lsp-signature.enable) ''
bind = true, -- This is mandatory, otherwise border config won't get registered.
handler_opts = {
border = "${config.vim.ui.borders.plugins.lsp-signature.style}"
}
''}
})
'';
};
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./lsp-signature.nix
./config.nix
];
}

View file

@ -0,0 +1,13 @@
{
config,
lib,
...
}: let
inherit (lib) mkEnableOption;
in {
options.vim.lsp = {
lspSignature = {
enable = mkEnableOption "lsp signature viewer";
};
};
}

View file

@ -0,0 +1,32 @@
{
pkgs,
config,
lib,
...
}: let
inherit (lib) mkIf mkMerge nvim optionalString mapAttrs;
cfg = config.vim.lsp;
in {
config = mkIf cfg.lspconfig.enable (mkMerge [
{
vim.lsp.enable = true;
vim.startPlugins = ["nvim-lspconfig"];
vim.luaConfigRC.lspconfig = nvim.dag.entryAfter ["lsp-setup"] ''
local lspconfig = require('lspconfig')
${
# TODO: make border style configurable
optionalString (config.vim.ui.borders.enable) ''
require('lspconfig.ui.windows').default_options.border = '${config.vim.ui.borders.globalStyle}'
''
}
'';
}
{
vim.luaConfigRC = mapAttrs (_: v: (nvim.dag.entryAfter ["lspconfig"] v)) cfg.lspconfig.sources;
}
]);
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./config.nix
./lspconfig.nix
];
}

View file

@ -0,0 +1,18 @@
{
pkgs,
config,
lib,
...
}: let
inherit (lib) mkEnableOption mkOption types;
in {
options.vim.lsp.lspconfig = {
enable = mkEnableOption "nvim-lspconfig, also enabled automatically";
sources = mkOption {
description = "nvim-lspconfig sources";
type = with types; attrsOf str;
default = {};
};
};
}

View file

@ -0,0 +1,20 @@
{
pkgs,
config,
lib,
...
}: let
inherit (lib) mkIf nvim;
cfg = config.vim.lsp;
in {
config = mkIf (cfg.enable && cfg.lspkind.enable) {
vim.startPlugins = ["lspkind"];
vim.luaConfigRC.lspkind = nvim.dag.entryAnywhere ''
local lspkind = require'lspkind'
local lspkind_opts = {
mode = '${cfg.lspkind.mode}'
}
'';
};
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./config.nix
./lspkind.nix
];
}

View file

@ -0,0 +1,22 @@
{
pkgs,
config,
lib,
...
}: let
inherit (lib) mkEnableOption mkOption types;
cfg = config.vim.lsp;
in {
options.vim.lsp = {
lspkind = {
enable = mkEnableOption "vscode-like pictograms for lsp [lspkind]";
mode = mkOption {
description = "Defines how annotations are shown";
type = with types; enum ["text" "text_symbol" "symbol_text" "symbol"];
default = "symbol_text";
};
};
};
}

View file

@ -0,0 +1,20 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf nvim;
cfg = config.vim.lsp;
in {
config = mkIf (cfg.enable && cfg.lsplines.enable) {
vim.startPlugins = ["lsp-lines"];
vim.luaConfigRC.lsplines = nvim.dag.entryAfter ["lspconfig"] ''
require("lsp_lines").setup()
vim.diagnostic.config({
virtual_text = false,
})
'';
};
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./config.nix
./lsplines.nix
];
}

View file

@ -0,0 +1,9 @@
{lib, ...}: let
inherit (lib) mkEnableOption;
in {
options.vim.lsp = {
lsplines = {
enable = mkEnableOption "diagnostics using virtual lines on top of the real line of code. [lsp_lines]";
};
};
}

View file

@ -0,0 +1,49 @@
{
config,
lib,
...
}: let
inherit (lib) addDescriptionsToMappings mkIf mkSetLuaBinding mkMerge nvim optionalString;
cfg = config.vim.lsp;
self = import ./lspsaga.nix {inherit lib;};
mappingDefinitions = self.options.vim.lsp.lspsaga.mappings;
mappings = addDescriptionsToMappings cfg.lspsaga.mappings mappingDefinitions;
in {
config = mkIf (cfg.enable && cfg.lspsaga.enable) {
vim.startPlugins = ["lspsaga"];
vim.maps.visual = mkSetLuaBinding mappings.codeAction "require('lspsaga.codeaction').range_code_action";
vim.maps.normal = mkMerge [
(mkSetLuaBinding mappings.lspFinder "require('lspsaga.provider').lsp_finder")
(mkSetLuaBinding mappings.renderHoveredDoc "require('lspsaga.hover').render_hover_doc")
(mkSetLuaBinding mappings.smartScrollUp "function() require('lspsaga.action').smart_scroll_with_saga(-1) end")
(mkSetLuaBinding mappings.smartScrollDown "function() require('lspsaga.action').smart_scroll_with_saga(1) end")
(mkSetLuaBinding mappings.rename "require('lspsaga.rename').rename")
(mkSetLuaBinding mappings.previewDefinition "require('lspsaga.provider').preview_definition")
(mkSetLuaBinding mappings.showLineDiagnostics "require('lspsaga.diagnostic').show_line_diagnostics")
(mkSetLuaBinding mappings.showCursorDiagnostics "require('lspsaga.diagnostic').show_cursor_diagnostics")
(mkSetLuaBinding mappings.nextDiagnostic "require('lspsaga.diagnostic').navigate('next')")
(mkSetLuaBinding mappings.previousDiagnostic "require('lspsaga.diagnostic').navigate('prev')")
(mkIf (!cfg.nvimCodeActionMenu.enable) (mkSetLuaBinding mappings.codeAction "require('lspsaga.codeaction').code_action"))
(mkIf (!cfg.lspSignature.enable) (mkSetLuaBinding mappings.signatureHelp "require('lspsaga.signaturehelp').signature_help"))
];
vim.luaConfigRC.lspsage = nvim.dag.entryAnywhere ''
-- Enable lspsaga
local saga = require 'lspsaga'
saga.init_lsp_saga({
${optionalString (config.vim.ui.borders.plugins.lspsaga.enable) ''
border_style = '${config.vim.ui.borders.plugins.lspsaga.style}',
''}
})
'';
};
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./lspsaga.nix
./config.nix
];
}

View file

@ -0,0 +1,28 @@
{lib, ...}: let
inherit (lib) mkEnableOption mkMappingOption;
in {
options.vim.lsp.lspsaga = {
enable = mkEnableOption "LSP Saga";
mappings = {
lspFinder = mkMappingOption "LSP Finder [LSPSaga]" "<leader>lf";
renderHoveredDoc = mkMappingOption "Rendered hovered docs [LSPSaga]" "<leader>lh";
smartScrollUp = mkMappingOption "Smart scroll up [LSPSaga]" "<C-f>";
smartScrollDown = mkMappingOption "Smart scroll up [LSPSaga]" "<C-b>";
rename = mkMappingOption "Rename [LSPSaga]" "<leader>lr";
previewDefinition = mkMappingOption "Preview definition [LSPSaga]" "<leader>ld";
showLineDiagnostics = mkMappingOption "Show line diagnostics [LSPSaga]" "<leader>ll";
showCursorDiagnostics = mkMappingOption "Show cursor diagnostics [LSPSaga]" "<leader>lc";
nextDiagnostic = mkMappingOption "Next diagnostic [LSPSaga]" "<leader>ln";
previousDiagnostic = mkMappingOption "Previous diagnostic [LSPSaga]" "<leader>lp";
codeAction = mkMappingOption "Code action [LSPSaga]" "<leader>ca";
signatureHelp = mkMappingOption "Signature help [LSPSaga]" "<leader>ls";
};
};
}

View file

@ -0,0 +1,70 @@
{lib, ...}: let
inherit (lib) mkEnableOption mkMappingOption;
in {
options.vim.lsp = {
enable = mkEnableOption "LSP, also enabled automatically through null-ls and lspconfig options";
formatOnSave = mkEnableOption "format on save";
mappings = {
goToDefinition =
mkMappingOption "Go to definition"
"<leader>lgd";
goToDeclaration =
mkMappingOption "Go to declaration"
"<leader>lgD";
goToType =
mkMappingOption "Go to type"
"<leader>lgt";
listImplementations =
mkMappingOption "List implementations"
"<leader>lgi";
listReferences =
mkMappingOption "List references"
"<leader>lgr";
nextDiagnostic =
mkMappingOption "Go to next diagnostic"
"<leader>lgn";
previousDiagnostic =
mkMappingOption "Go to previous diagnostic"
"<leader>lgp";
openDiagnosticFloat =
mkMappingOption "Open diagnostic float"
"<leader>le";
documentHighlight =
mkMappingOption "Document highlight"
"<leader>lH";
listDocumentSymbols =
mkMappingOption "List document symbols"
"<leader>lS";
addWorkspaceFolder =
mkMappingOption "Add workspace folder"
"<leader>lwa";
removeWorkspaceFolder =
mkMappingOption "Remove workspace folder"
"<leader>lwr";
listWorkspaceFolders =
mkMappingOption "List workspace folders"
"<leader>lwl";
listWorkspaceSymbols =
mkMappingOption "List workspace symbols"
"<leader>lws";
hover =
mkMappingOption "Trigger hover"
"<leader>lh";
signatureHelp =
mkMappingOption "Signature help"
"<leader>ls";
renameSymbol =
mkMappingOption "Rename symbol"
"<leader>ln";
codeAction =
mkMappingOption "Code action"
"<leader>la";
format =
mkMappingOption "Format"
"<leader>lf";
toggleFormatOnSave =
mkMappingOption "Toggle format on save"
"<leader>ltf";
};
};
}

View file

@ -0,0 +1,37 @@
{
pkgs,
config,
lib,
...
}: let
inherit (lib) mkIf mkMerge nvim mapAttrs;
cfg = config.vim.lsp;
in {
config = mkIf cfg.null-ls.enable (mkMerge [
{
vim.lsp.enable = true;
vim.startPlugins = ["none-ls"];
vim.luaConfigRC.null_ls-setup = nvim.dag.entryAnywhere ''
local null_ls = require("null-ls")
local null_helpers = require("null-ls.helpers")
local null_methods = require("null-ls.methods")
local ls_sources = {}
'';
vim.luaConfigRC.null_ls = nvim.dag.entryAfter ["null_ls-setup" "lsp-setup"] ''
require('null-ls').setup({
debug = false,
diagnostics_format = "[#{m}] #{s} (#{c})",
debounce = 250,
default_timeout = 5000,
sources = ls_sources,
on_attach=default_on_attach
})
'';
}
{
vim.luaConfigRC = mapAttrs (_: v: (nvim.dag.entryBetween ["null_ls"] ["null_ls-setup"] v)) cfg.null-ls.sources;
}
]);
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./config.nix
./null-ls.nix
];
}

View file

@ -0,0 +1,20 @@
{
pkgs,
config,
lib,
...
}: let
inherit (lib) mkEnableOption mkOption types;
cfg = config.vim.lsp;
in {
options.vim.lsp.null-ls = {
enable = mkEnableOption "null-ls, also enabled automatically";
sources = mkOption {
description = "null-ls sources";
type = with types; attrsOf str;
default = {};
};
};
}

View file

@ -0,0 +1,30 @@
{
config,
lib,
...
}: let
inherit (lib) addDescriptionsToMappings mkIf mkSetBinding nvim;
cfg = config.vim.lsp;
self = import ./nvim-code-action-menu.nix {inherit lib;};
mappingDefinitions = self.options.vim.lsp.nvimCodeActionMenu.mappings;
mappings = addDescriptionsToMappings cfg.nvimCodeActionMenu.mappings mappingDefinitions;
in {
config = mkIf (cfg.enable && cfg.nvimCodeActionMenu.enable) {
vim.startPlugins = ["nvim-code-action-menu"];
vim.maps.normal = mkSetBinding mappings.open ":CodeActionMenu<CR>";
vim.luaConfigRC.code-action-menu = nvim.dag.entryAnywhere ''
-- border configuration
vim.g.code_action_menu_window_border = '${config.vim.ui.borders.plugins.code-action-menu.style}'
-- show individual sections of the code action menu
${lib.optionalString (cfg.nvimCodeActionMenu.show.details) "vim.g.code_action_menu_show_details = true"}
${lib.optionalString (cfg.nvimCodeActionMenu.show.diff) "vim.g.code_action_menu_show_diff = true"}
${lib.optionalString (cfg.nvimCodeActionMenu.show.actionKind) "vim.g.code_action_menu_show_action_kind = true"}
'';
};
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./nvim-code-action-menu.nix
./config.nix
];
}

View file

@ -0,0 +1,19 @@
{lib, ...}: let
inherit (lib) mkEnableOption mkMappingOption;
in {
options.vim.lsp = {
nvimCodeActionMenu = {
enable = mkEnableOption "nvim code action menu";
show = {
details = mkEnableOption "Show details" // {default = true;};
diff = mkEnableOption "Show diff" // {default = true;};
actionKind = mkEnableOption "Show action kind" // {default = true;};
};
mappings = {
open = mkMappingOption "Open code action menu [nvim-code-action-menu]" "<leader>ca";
};
};
};
}

View file

@ -0,0 +1,35 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf nvim addDescriptionsToMappings mkSetBinding mkMerge;
inherit (builtins) toString;
cfg = config.vim.lsp.nvim-docs-view;
self = import ./nvim-docs-view.nix {inherit lib;};
mappingDefinitions = self.options.vim.lsp.nvim-docs-view.mappings;
mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions;
in {
config = mkIf cfg.enable {
vim = {
lsp.enable = true;
startPlugins = ["nvim-docs-view"];
luaConfigRC.nvim-docs-view = nvim.dag.entryAnywhere ''
require("docs-view").setup {
position = "${cfg.position}",
width = ${toString cfg.width},
height = ${toString cfg.height},
update_mode = "${cfg.updateMode}",
}
'';
maps.normal = mkMerge [
(mkSetBinding mappings.viewToggle "<cmd>DocsViewToggle<CR>")
(mkSetBinding mappings.viewUpdate "<cmd>DocsViewUpdate<CR>")
];
};
};
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./config.nix
./nvim-docs-view.nix
];
}

View file

@ -0,0 +1,46 @@
{lib, ...}: let
inherit (lib) mkEnableOption mkOption types mkMappingOption;
in {
options.vim.lsp.nvim-docs-view = {
enable = mkEnableOption "nvim-docs-view, for displaying lsp hover documentation in a side panel.";
position = mkOption {
type = types.enum ["left" "right" "top" "bottom"];
default = "right";
description = ''
Where to open the docs view panel
'';
};
height = mkOption {
type = types.int;
default = 10;
description = ''
Height of the docs view panel if the position is set to either top or bottom
'';
};
width = mkOption {
type = types.int;
default = 60;
description = ''
Width of the docs view panel if the position is set to either left or right
'';
};
updateMode = mkOption {
type = types.enum ["auto" "manual"];
default = "auto";
description = ''
Determines the mechanism used to update the docs view panel content.
- If auto, the content will update upon cursor move.
- If manual, the content will only update once :DocsViewUpdate is called
'';
};
mappings = {
viewToggle = mkMappingOption "Open or close the docs view panel" "lvt";
viewUpdate = mkMappingOption "Manually update the docs view panel" "lvu";
};
};
}

View file

@ -0,0 +1,32 @@
{
config,
lib,
...
}: let
inherit (lib) addDescriptionsToMappings mkIf mkMerge mkSetBinding nvim;
cfg = config.vim.lsp;
self = import ./trouble.nix {inherit lib;};
mappingDefinitions = self.options.vim.lsp.trouble.mappings;
mappings = addDescriptionsToMappings cfg.trouble.mappings mappingDefinitions;
in {
config = mkIf (cfg.enable && cfg.trouble.enable) {
vim.startPlugins = ["trouble"];
vim.maps.normal = mkMerge [
(mkSetBinding mappings.toggle "<cmd>TroubleToggle<CR>")
(mkSetBinding mappings.workspaceDiagnostics "<cmd>TroubleToggle workspace_diagnostics<CR>")
(mkSetBinding mappings.documentDiagnostics "<cmd>TroubleToggle document_diagnostics<CR>")
(mkSetBinding mappings.lspReferences "<cmd>TroubleToggle lsp_references<CR>")
(mkSetBinding mappings.quickfix "<cmd>TroubleToggle quickfix<CR>")
(mkSetBinding mappings.locList "<cmd>TroubleToggle loclist<CR>")
];
vim.luaConfigRC.trouble = nvim.dag.entryAnywhere ''
-- Enable trouble diagnostics viewer
require("trouble").setup {}
'';
};
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./trouble.nix
./config.nix
];
}

View file

@ -0,0 +1,18 @@
{lib, ...}: let
inherit (lib) mkEnableOption mkMappingOption;
in {
options.vim.lsp = {
trouble = {
enable = mkEnableOption "trouble diagnostics viewer";
mappings = {
toggle = mkMappingOption "Toggle trouble [trouble]" "<leader>xx";
workspaceDiagnostics = mkMappingOption "Workspace diagnostics [trouble]" "<leader>lwd";
documentDiagnostics = mkMappingOption "Document diagnostics [trouble]" "<leader>ld";
lspReferences = mkMappingOption "LSP References [trouble]" "<leader>lr";
quickfix = mkMappingOption "QuickFix [trouble]" "<leader>xq";
locList = mkMappingOption "LOCList [trouble]" "<leader>xl";
};
};
};
}