Compare commits

...

10 Commits

Author SHA1 Message Date
Robot 6b798a74b1 tweak lsp 2024-04-03 10:29:35 -07:00
Robot 9ad33f1b4e add wether and ufo 2024-02-22 16:58:30 -08:00
Robot 8219bade80 add terminal keymaps 2024-02-01 09:48:34 -08:00
Robot 56a9373536 add blank indets 2023-12-18 18:26:30 -08:00
Robot 6e42bd3364 add notify 2023-12-18 18:14:06 -08:00
Robot b3ea8e685e fix rust analyzer 2023-12-18 17:12:10 -08:00
Robot 9c0cdd3249 change spinner 2023-11-03 13:43:11 -07:00
Robot 98860b4d76 add bacon 2023-11-03 13:43:04 -07:00
Robot 31aea2ab73 disable inlay hints 2023-09-25 14:08:40 -07:00
A_train63 58059644ed add colors 2023-09-20 15:52:21 -07:00
9 changed files with 639 additions and 22 deletions

View File

@ -1,5 +1,7 @@
local map = require("helpers.keys").map
-- Quick access to some common actions
map("n", "<leader>fw", "<cmd>w<cr>", "Write")
map("n", "<leader>fa", "<cmd>wa<cr>", "Write all")
@ -60,9 +62,21 @@ end, "Toggle between light and dark themes")
map("n", "<leader>ur", "<cmd>nohl<cr>", "Clear highlights")
-- Open config
map("n", "<leader>Nc", "<cmd>n ~/.config/nvim/init.lua<cr>", "Open Config")
map("n", "<leader>Nc", "<cmd>tabnew ~/.config/nvim/init.lua<cr>", "Open Config")
-- ToggleTerm config
-- Open Weather
map("n", "<leader>w", "<cmd>lua require'wttr'.get_forecast()<cr>", "Open Weather")
-- Open Common Files
map("n", "<leader>fm", "<cmd>tabnew ~/vex/roberts<cr>", "Open Roberts")
map("n", "<leader>fp", "<cmd>tabnew ~/vex/patch<cr>", "Open Patch")
-- New Tab Terminal (for more perminent use)
map("n", "<leader>ft", "<cmd>tabf term://fish<cr>i", "Open New Terminal")
map("t", "<Esc><leader>", "<C-\\><C-n>", "Return to normal mode")
-- Lazygit config
local Terminal = require('toggleterm.terminal').Terminal
local lazygit = Terminal:new({cmd = "lazygit", hidden = true, direction='tab'})
@ -70,3 +84,20 @@ function _lazygit_toggle()
lazygit:toggle()
end
map("n", "<leader>gg", "<cmd>lua _lazygit_toggle()<cr>", "open lazygit")
-- Bacon config
local bacon = Terminal:new({cmd = "bacon --job clippy", hidden = true, direction='tab'})
function _bacon_toggle()
bacon:toggle()
end
map("n", "<leader>h", "<cmd>lua _bacon_toggle()<cr>", "open bacon")
-- Wiki-tui config
local wiki = Terminal:new({cmd = "wiki-tui", hidden = true, direction='tab'})
function _wiki_toggle()
wiki:toggle()
end
map("n", "<leader>sl", "<cmd>lua _wiki_toggle()<cr>", "open wiki-tui")
map("n", "<leader>r", "<cmd>HackAuto<cr>", ":)")

View File

@ -6,6 +6,10 @@ local opts = {
termguicolors = true,
number = true,
relativenumber = true,
foldcolumn = '0',
foldlevel = 99,
foldlevelstart = 99,
foldenable = true,
}
-- Set options from table
@ -16,15 +20,21 @@ end
-- Set other options
local colorscheme = require("helpers.colorscheme")
vim.cmd.colorscheme(colorscheme)
vim.notify = require("notify")
vim.api.nvim_set_hl(0, "String", { fg = "#9ece6a", underline = false, bold = false })
vim.api.nvim_set_hl(0, "Type", { fg = "#00ff00", underline = false, bold = false })
vim.api.nvim_set_hl(0, "Structure", { fg = "#00ff00", underline = false, bold = false })
vim.api.nvim_set_hl(0, "Constant", { fg = "#fab387", underline = false, bold = true })
vim.api.nvim_set_hl(0, "@lsp.type.selfKeyword.rust", {fg = "#b00202", bold = true, underline=false})
vim.api.nvim_set_hl(0, "@lsp.type.typeParamater.rust", {fg = "#ffbf00", bold = true, underline=false})
vim.api.nvim_set_hl(0, "Identifier", { fg = "#ffffff", underline = false, bold = false })
vim.api.nvim_set_hl(0, "Function", { fg = "#025fb0", underline = false, bold = false })
vim.api.nvim_set_hl(0, "@lsp.typemod.variable.constant.rust", { fg = "#00ff00", underline = false, bold = false })
vim.api.nvim_set_hl(0, "@lsp.type.enumMember.rust", { fg = "#fab387", underline = false, bold = false })
vim.api.nvim_set_hl(0, "@lsp.type.interface", {fg = "#a402b0", bold = true, underline=false})
vim.api.nvim_set_hl(0, "Comment", {fg = "#989898", bold = false, underline=false})
vim.api.nvim_set_hl(0, "NvimTreeGitDirty", {fg = "#ffaa00", bold = false, underline=false})
vim.api.nvim_set_hl(0, "@string.escape", {fg = "#aa00ff", bold = false, underline=false})
vim.api.nvim_set_hl(0, "@type.builtin", {fg = "#00ff00", bold = false, underline=false})
vim.api.nvim_set_hl(0, "@lsp.mod.unsafe.rust", {bg = "#aa0000"})
-- vim.api.nvim_set_hl(0, "DiagnosticUnnecessary", {})

View File

@ -10,10 +10,17 @@ return {
"L3MON4D3/LuaSnip",
"saadparwaiz1/cmp_luasnip",
"rafamadriz/friendly-snippets",
-- "zbirenbaum/copilot.lua",
-- "zbirenbaum/copilot-cmp",
},
config = function()
local cmp = require("cmp")
local luasnip = require("luasnip")
-- require("copilot").setup({
-- suggestion = { enabled = false },
-- panel = { enabled = false },
-- })
-- require("copilot-cmp")
require("luasnip/loaders/from_vscode").lazy_load()
@ -43,7 +50,13 @@ return {
Event = "",
Operator = "",
TypeParameter = "",
-- Copilot = "",
}
-- local has_words_before = function()
-- if vim.api.nvim_buf_get_option(0, "buftype") == "prompt" then return false end
-- local line, col = unpack(vim.api.nvim_win_get_cursor(0))
-- return col ~= 0 and vim.api.nvim_buf_get_text(0, line-1, 0, line-1, col, {})[1]:match("^%s*$") == nil
-- end
cmp.setup({
snippet = {
@ -80,6 +93,7 @@ return {
end, { "i", "s" }),
}),
formatting = {
expandable_indicator = true,
fields = { "kind", "abbr", "menu" },
format = function(entry, vim_item)
-- Kind icons

499
lua/plugins/hack.lua Normal file
View File

@ -0,0 +1,499 @@
return {
{
"letieu/hacker.nvim",
opts = {
content =
[[//! # Functionality for making drivetrains and includes some basic ones.
pub mod motors;
// pub mod no_block;
use core::{
f64::consts::{TAU, PI},
ops::{
RangeInclusive,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
Div,
},
time::Duration,
};
use libm::{
sqrt,
pow,
};
use alloc::{
vec::Vec,
vec,
};
use crate::{prelude::*, println_blue};
use motors::MotorGroup;
pub struct Drive<T: MotorGroup, C: Config = DefaultConfig> {
pub left_motor: T,
pub right_motor: T,
pub config: C,
}
impl<T: MotorGroup, C: Config> DriveTrain for Drive<T, C> {
type Config = C;
type MotorError = T::MotorError;
fn move_i8(&mut self, l: i8, r: i8) -> Result<(), T::MotorError> {
self.left_motor.move_i8(l)?;
self.right_motor.move_i8(r)
}
fn move_voltage(&mut self, l: i32, r: i32) -> Result<(), T::MotorError> {
self.left_motor.move_voltage(l)?;
self.right_motor.move_voltage(r)
}
fn config(&self) -> &Self::Config {
&self.config
}
}
/// Represents the drivetrain of the robot
pub trait DriveTrain {
type Config: Config;
type MotorError;
///moves the sides of the drivetrain the specified voltadge
fn move_voltage(&mut self, left: i32, right: i32) -> Result<(), Self::MotorError>;
///moves the sides of the drivetrain the specified number
fn move_i8(&mut self, left: i8, right: i8) -> Result<(), Self::MotorError>;
fn config(&self) -> &Self::Config;
fn drive(&mut self, y: i8, x: i8) -> Result<(), Self::MotorError> {
let (left, right) = self.config().drive_math(y, x);
self.move_i8(left, right)?;
Ok(())
}
/// one frame of a pid loop, moves twards specified point
fn go_to(
&mut self,
heading: Angle,
init: Point,
to: Point,
pids: &mut PidPair,
) -> Result<(), Self::MotorError> {
let (frwdpidin, turnpidin) = Self::Config::go_to_pid_math(heading, init, to);
pids.0.add(frwdpidin);
pids.1.add(turnpidin);
let frwd = pids.0.get() as i32;
let turn = pids.1.get() as i32;
self.move_voltage(frwd - turn, frwd + turn)?;
Ok(())
}
/// blocks, drives the drivetrain forward to a point until the condition becomes false
fn go_to_while<F:FnMut()->bool>(
&mut self,
sensor:&Mutex<InertialSensor>,
position:&Mutex<Point>,
to:Point,
pids:&mut PidPair,
ctx:Context,
mut cond:F,
) -> Result<(), Self::MotorError> {
let mut l = Loop::new(Duration::from_millis(10));
while cond() {
self.go_to(
Angle::from_imu_read(sensor.lock().get_heading().warn_default()),
position.lock().clone(),
to.clone(),
pids
)?;
select! {
() = l.select() => continue,
() = ctx.done() => break,
}
}
self.move_voltage(0, 0);
Ok(())
}
/// one frame of a pid loop, turns twards the specified point
fn turn_to(
&mut self,
heading: Angle,
init: Point,
to: Point,
pid: &mut Pid,
reverse:bool
) -> Result<(), Self::MotorError> {
let delta_heading = match reverse {
false => init.angle_between(&to) - heading,
true => init.angle_between(&to) + Angle::from_rad(PI) - heading,
};
pid.add(delta_heading.as_rad_neg());
let mvolt = pid.get() as i32;
self.move_voltage(-mvolt, mvolt)
}
/// moves the sides of the drivetrain the specified voltadge, then waits for time
fn go_for(&mut self, left: i32, right: i32, time: Duration) -> Result<(), Self::MotorError> {
self.move_voltage(left, right)?;
Task::delay(time);
Ok(())
}
}
/// Represendts two pids controlling the forward (l, _), and turn (_, r) components of a drivetrain
pub type PidPair = (Pid, Pid);
/* /// Four wheeled holonomic drivetrain struct.
pub struct Holo<T:Motor>{
rightfronf:T,
leftfront:T,
rightback:T,
leftback:T,
}
/// WIP
pub trait Holonomic {
fn forward_math(&self, x:i8, y:i8, imu:Angle);
fn turn_math(&mut self, turn:i8);
} */
/// Anything with the ability to be configured as a driver profile.
pub trait Config {
///the math for how the drivetrain moves given stick input
fn drive_math(&self, y: i8, x: i8) -> (i8, i8);
/// the math for the values that the pids should be passed in when going to
/// point (frwd, turn).
fn go_to_pid_math(heading: Angle, init: Point, to: Point) -> (f64, f64) {
let change = to - init;
let relitive = change.rotate(-heading);
(relitive.x(), relitive.y())
}
}
pub struct DefaultConfig;
impl Config for DefaultConfig {
fn drive_math(&self, y: i8, x: i8) -> (i8, i8) {
//we want to truncate here
#[allow(clippy::cast_possible_truncation)]
let left = ((y as i16) + (x as i16)).clamp(-127, 127) as i8;
#[allow(clippy::cast_possible_truncation)]
let right = ((y as i16) - (x as i16)).clamp(-127, 127) as i8;
(left, right)
}
}
fn signum(n:f64)->f64{
if n<0.0{
-1.0
} else{
1.0
}
}
///Contains fuctionality for smooth acceleration to a destination
///
/// # examples
///
/// ```
/// #![no_std]
/// #![no_main]
///
/// struct MyRobot {
/// left_motor: Motor,
/// right_motor: Motor,
/// pid: Pid,
/// }
/// impl MyRobot {
/// ///moves the robot forward the specified number of ticks
/// fn move_dist(&mut self, distance: f64) -> Result<(), MotorError> {
/// self.left_motor.tare_position().unwrap();
/// self.right_motor.tare_position().unwrap();
/// self.pid.add(distance);
/// loop {
/// self.left_motor
/// .move_voltadge(self.pid.get_weight())
/// .unwrap();
/// self.right_motor
/// .move_voltadge(self.pid.get_weight())
/// .unwrap();
/// let err = dist - self.left_motor.get_position().unwrap();
/// self.pid.add(err);
/// if err == 0.0 {
/// break;
/// }
/// }
/// }
/// }
/// ```
#[derive(Debug, Clone)]
pub struct Pid {
kp: f64,
ki: f64,
kd: f64,
p: f64,
i: f64,
d: f64,
range: RangeInclusive<f64>,
max: Option<f64>,
}
impl Pid {
/// constructs new pid with the given weights, and the allowed range of the
/// internal values
pub const fn new(weights:PidWeights, range: RangeInclusive<f64>) -> Self {
Self {
kp: weights.kp,
ki: weights.ki,
kd: weights.kd,
p: 0.0,
i: 0.0,
d: 0.0,
range,
max: None,
}
}
pub fn add(&mut self, new: f64) {
self.d = self.p - new;
self.i += new;
self.p = new;
self.p = self.p.clamp(*self.range.start(), *self.range.end());
self.i = self.i.clamp(*self.range.start(), *self.range.end());
self.d = self.d.clamp(*self.range.start(), *self.range.end());
}
pub fn get(&self) -> f64 {
let max = self.max.unwrap_or(f64::INFINITY);
((self.p * self.kp) + (self.i * self.ki) + (self.d * self.kd)).clamp(-max, max)
}
pub const fn set_max(mut self, max: Option<f64>) -> Self {
self.max = max;
self
}
pub fn reset(&mut self) {
self.p = 0.0;
self.i = 0.0;
self.d = 0.0;
}
pub fn weights(&self) -> PidWeights {
PidWeights {
kp: self.kp,
ki: self.ki,
kd: self.kd,
}
}
pub fn set_weights(&mut self, weights:PidWeights) {
self.kp = weights.kp;
self.ki = weights.ki;
self.kd = weights.kd;
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PidWeights {
pub kp: f64,
pub ki: f64,
pub kd: f64,
}
impl PidWeights {
fn positive(&self) ->Self{
Self{
kp: self.kp.max(0.0),
ki: self.ki.max(0.0),
kd: self.kd.max(0.0),
}
}
fn max(&self, rhs: &Self) ->Self{
Self{
kp: self.kp.max(-rhs.kp),
ki: self.ki.max(-rhs.ki),
kd: self.kd.max(-rhs.kd),
}
}
fn max_magnitude(&self, mag: f64) -> Self {
let self_mag = self.magnitude();
if self_mag > mag {
&(self*mag)/self_mag
} else {
self.clone()
}
}
fn magnitude(&self)->f64{
sqrt(pow(self.kp, 2.0) + pow(self.ki, 2.0) + pow(self.kd, 2.0))
}
/// # NOTE: self is three independent vectors, not one
fn gradient_acent(self, responce: (f64, f64, f64)) -> PidWeights {
PidWeights{
kp: responce.0/self.kp,
ki: responce.1/self.ki,
kd: responce.2/self.kd
}
}
}
impl Add for PidWeights {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
kp: self.kp + rhs.kp,
ki: self.ki + rhs.ki,
kd: self.kd + rhs.kd
}
}
}
impl Sub for PidWeights {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
kp: self.kp - rhs.kp,
ki: self.ki - rhs.ki,
kd: self.kd - rhs.kd
}
}
}
impl Add for &PidWeights {
type Output = PidWeights;
fn add(self, rhs: Self) -> Self::Output {
PidWeights {
kp: self.kp + rhs.kp,
ki: self.ki + rhs.ki,
kd: self.kd + rhs.kd
}
}
}
impl Sub for &PidWeights {
type Output = PidWeights;
fn sub(self, rhs: Self) -> Self::Output {
PidWeights {
kp: self.kp - rhs.kp,
ki: self.ki - rhs.ki,
kd: self.kd - rhs.kd
}
}
}
impl Mul<f64> for &PidWeights {
type Output = PidWeights;
fn mul(self, rhs: f64) -> Self::Output {
PidWeights {
kp: self.kp * rhs,
ki: self.ki * rhs,
kd: self.kd * rhs
}
}
}
impl Div<f64> for &PidWeights {
type Output = PidWeights;
fn div(self, rhs: f64) -> Self::Output {
PidWeights {
kp: self.kp / rhs,
ki: self.ki / rhs,
kd: self.kd / rhs
}
}
}
impl AddAssign for PidWeights {
fn add_assign(&mut self, rhs: Self) {
*self = &*self + &rhs
}
}
impl SubAssign for PidWeights {
fn sub_assign(&mut self, rhs: Self) {
*self = &*self - &rhs
}
}
#[derive(Default, Clone, Debug)]
enum Weight {
All,
Kp,
Ki,
#[default]
Kd,
}
impl Weight {
fn next(&self) -> Self{
match self {
Self::All => Self::Kp,
Self::Kp => Self::Ki,
Self::Ki => Self::Kd,
Self::Kd => Self::All,
}
}
}
/// automagicaly tunes pids to maximize v
#[derive(Debug, Clone, Default)]
pub struct AutoTune<const N: usize>{
// keep track of all readings for human review
readings:Vec<(PidWeights, f64)>,
current:Vec<(PidWeights, f64)>,
/// the changes that was determened best (not indivudual values)
/// will only be none when algorithm hasnt run
last_target: Weight,
}
impl<const N: usize> AutoTune<N> {
pub fn new() -> Self{
assert_ne!(N, 0, "N must not be zero");
Self::default()
}
fn last_all(&self) -> &PidWeights {
let adjusted_len = self.readings.len() - self.last_target.clone() as usize - 1;
&self.readings[adjusted_len].0
}
fn last_change(&self) -> Option<PidWeights> {
// cycles 4 times in order [ kp, ki, kd, all ]
let adjusted_len = self.readings.len().checked_sub(self.last_target.clone() as usize)?.checked_sub(1)?;
Some(
&self.readings.get(adjusted_len)?.0
- &self.readings.get(adjusted_len.checked_sub(4)?)?.0
)
}
pub fn tune(&mut self, weight: PidWeights, dist: Distance, time: Duration, alpha: f64, max_magnitude: f64) -> PidWeights{
// GOAL: maximize velocity
let vel = dist/time.as_secs_f64(); // in cm/sec
if self.current.len() < N {
println!("averageing");
self.current.push((weight.clone(), vel));
return weight;
}
let ave = self.current.iter().fold((PidWeights {kp:0.0, ki:0.0, kd: 0.0}, 0.0), |acc, next| {
(&acc.0+&next.0, &acc.1+&next.1)
});
self.readings.push((&ave.0/N as f64, ave.1/N as f64));
self.current.clear();
self.last_target = self.last_target.next();
let last_change = self.last_change().unwrap_or_else(||(&weight * alpha).max_magnitude(max_magnitude));
let last_all = self.last_all().clone();
let out = match self.last_target {
Weight::All => {
PidWeights {
kp: last_all.kp + last_change.kp,
..last_all
}
},
Weight::Kp => {
PidWeights {
ki: last_all.ki + last_change.ki,
..last_all
}
},
Weight::Ki => {
PidWeights {
kd: last_all.kd + last_change.kd,
..last_all
}
},
Weight::Kd => {
let len = self.readings.len();
let last_vel = self.readings[len - 4].1;
let delta_reads = (self.readings[len - 3].1-last_vel, self.readings[len - 2].1-last_vel, self.readings[len - 1].1-last_vel);
let grad = (&last_change.clone().gradient_acent(delta_reads) * alpha).max_magnitude(max_magnitude);
println_blue!("grad={grad:?}\ndelta_reads={delta_reads:?}");
last_all + grad
}
};
println!("last_change={last_change:?}\nout={out:?}");
out
}
}]],
filetype = "rust",
}
}
}

13
lua/plugins/harpoon.lua Normal file
View File

@ -0,0 +1,13 @@
return {
{
"ThePrimeagen/harpoon",
config = function ()
require("harpoon").setup({})
local map = require("helpers.keys").map
map("n", "<leader>ma", require("harpoon.mark").add_file, "New mark")
map("n", "<leader>ms", require("harpoon.ui").toggle_quick_menu, "List marks")
map("n", "<leader>mj", require("harpoon.ui").nav_next, "Next mark")
map("n", "<leader>mk", require("harpoon.ui").nav_prev, "Previous mark")
end,
},
}

View File

@ -9,7 +9,9 @@ return {
"folke/neodev.nvim",
"RRethy/vim-illuminate",
"hrsh7th/cmp-nvim-lsp",
"simrat39/rust-tools.nvim",
"kevinhwang91/nvim-ufo",
"kevinhwang91/promise-async",
-- "simrat39/rust-tools.nvim",
},
config = function()
-- Set up Mason before anything else
@ -18,6 +20,7 @@ return {
ensure_installed = {
"lua_ls",
"pylsp",
"rust_analyzer",
},
automatic_installation = true,
})
@ -29,7 +32,13 @@ return {
require("neodev").setup()
-- Turn on LSP status information
require("fidget").setup()
require("fidget").setup({
progress = {
display = {
progress_icon = { "meter" }
}
}
})
-- Set up cool signs for diagnostics
local signs = { Error = "󰂭 ", Warn = "", Hint = "", Info = "" }
@ -51,7 +60,7 @@ return {
focusable = true,
style = "minimal",
border = "rounded",
source = "always",
source = true,
header = "",
prefix = "",
},
@ -87,6 +96,10 @@ return {
-- nvim-cmp supports additional completion capabilities, so broadcast that to servers
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require("cmp_nvim_lsp").default_capabilities(capabilities)
capabilities.textDocument.foldingRange = {
dynamicRegistration = false,
lineFoldingOnly = true,
}
-- Lua
require("lspconfig")["lua_ls"].setup({
@ -139,15 +152,34 @@ return {
},
},
})
local rt = require("rust-tools")
rt.setup({
server = {
on_attach= function(_, bufnr)
vim.keymap.set("n", "<C-space>", rt.hover_actions.hover_actions, {buffer = bufnr})
vim.keymap.set("n", "<Leader>a", rt.code_action_group.code_action_group, {buffer = bufnr})
end,
},
-- Rust
require("lspconfig")["rust_analyzer"].setup({
on_attach = on_attach,
capabilities = capabilities,
settings = {
hover = {
show = {
structFields = 8,
traitAssocItems = 8,
}
}
}
})
require('ufo').setup({})
-- local rt = require("rust-tools")
-- rt.setup({
-- server = {
-- on_attach= function(_, bufnr)
-- vim.keymap.set("n", "<C-space>", rt.hover_actions.hover_actions, {buffer = bufnr})
-- vim.keymap.set("n", "<Leader>a", rt.code_action_group.code_action_group, {buffer = bufnr})
-- end,
-- },
-- tools = {
-- inlay_hints = {
-- auto = false,
-- },
-- }
-- })
end,
},
}

View File

@ -11,5 +11,9 @@ return {
component_separators = "|",
section_separators = "",
},
sections = {
lualine_x = { "require'wttr'.text", "encoding", "fileformat", "filetype" }
}
})
end, }
end,
}

View File

@ -37,12 +37,25 @@ return {
end
},
{
"simrat39/symbols-outline.nvim",
config = function ()
vim.keymap.set('n', '<leader>t', '<cmd>SymbolsOutline<cr>')
end
"lukas-reineke/indent-blankline.nvim",
main = "ibl",
opts = {}
},
"simrat39/rust-tools.nvim",
"rcarriga/nvim-notify",
{
"lazymaniac/wttr.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"MunifTnjim/nui.nvim",
},
opts = {
location = "las+vegas",
format = 2,
}
},
-- {
-- "felipec/vim-sanegx",
-- event = "BufRead",
-- },
}

View File

@ -16,7 +16,8 @@ return {
b = { name = "Debugging" },
g = { name = "Git" },
N = { name = "Neovim" },
p = { name = "Plugins" }
p = { name = "Plugins" },
m = { name = "Harpoon" },
}
}
)