1647 lines
52 KiB
Org Mode
1647 lines
52 KiB
Org Mode
#+PROPERTY: header-args :mkdirp yes
|
||
|
||
* Overall Structure
|
||
|
||
This is the basic structure of the home.nix file. Individual sections are defined later.
|
||
|
||
#+BEGIN_SRC nix :noweb no-export :tangle target/home.nix
|
||
{ config, pkgs, kube, ... }:
|
||
|
||
{
|
||
home.packages = with pkgs; [
|
||
<<packages>>
|
||
];
|
||
|
||
<<path>>
|
||
|
||
programs = {
|
||
<<programs>>
|
||
};
|
||
|
||
fonts.fontconfig.enable = true;
|
||
|
||
xdg = {
|
||
enable = true;
|
||
<<extra_xdg>>
|
||
};
|
||
}
|
||
#+END_SRC
|
||
* Packages
|
||
:PROPERTIES:
|
||
:header-args: :noweb-ref packages
|
||
:END:
|
||
|
||
Home-manager "packages" are simply binaries that are placed on the path, without any attached
|
||
configuration. The code blocks in this section contain package lists that go under the packages
|
||
section above.
|
||
|
||
~SBCL~ is the current leading open-source common lisp compiler. It requires any libraries you want
|
||
to be availble to be installed using withPackages. Many lisp projects I work on have this overridden
|
||
in a ~nix~ devshell I think I'd also like to use it for scripting / exploratory development, though,
|
||
so I'm installing it globally with a useful set of libraries. I should curate this somewhat
|
||
carefully so it doesn't get carried away...
|
||
|
||
#+BEGIN_SRC nix
|
||
rlwrap
|
||
(sbcl.withPackages (ps: with ps; [
|
||
alexandria
|
||
cl-markup
|
||
cl-mock
|
||
cl-rdkafka
|
||
clack
|
||
clack-handler-woo
|
||
dbd-mysql
|
||
dbi
|
||
dexador
|
||
fare-csv
|
||
group-by
|
||
lack-request
|
||
lack-response
|
||
local-time
|
||
log4cl
|
||
mito
|
||
myway
|
||
prove
|
||
prove-asdf
|
||
replic
|
||
serapeum
|
||
shasht
|
||
slynk
|
||
smug
|
||
split-sequence
|
||
str
|
||
sxql-composer
|
||
trivia
|
||
trivial-ws
|
||
trivial-open-browser
|
||
trivial-timeout
|
||
uuid
|
||
]))
|
||
#+END_SRC
|
||
|
||
[[https://github.com/str4d/rage][~rage~]] is a rust implementation of [[https://pkg.go.dev/filippo.io/age][~age~]]. It implements a simple system for generating public /
|
||
private encryption keys, and encrypting / decrypting files with those keys. [[https://github.com/str4d/age-plugin-yubikey][~age-plugin-yubikey~]], as
|
||
the name implies, allows generating and storing ~age~ keys on a Yubikey. ~rage~ is required over
|
||
~age~ when using ~age.el~, because it supports ~pinentry~. That allows it to prompt for yubikey pins
|
||
using a dialog, rather than a command line prompt.
|
||
|
||
#+BEGIN_SRC nix
|
||
rage
|
||
age-plugin-yubikey
|
||
#+END_SRC
|
||
|
||
~dust~, ~fd~, ~ripgrep~, and ~xh~ are rust re-implementations of a few longtime Linux command-line
|
||
tools. They boost better speed, or simply more streamlined features.
|
||
|
||
| Command | Original |
|
||
| ~dust~ | ~du~ |
|
||
| ~fd~ | ~find~ |
|
||
| ~ripgrep~ | ~grep~ |
|
||
| ~xh~ | ~curl~ |
|
||
|
||
#+BEGIN_SRC nix
|
||
dust
|
||
fd
|
||
ripgrep
|
||
xh
|
||
#+END_SRC
|
||
|
||
[[https://ledger-cli.org/][~ledger~]] is how I manage my budget. It's a plain-text accounting tool that's really awesome.
|
||
|
||
#+BEGIN_SRC nix
|
||
ledger
|
||
#+END_SRC
|
||
|
||
~nil~ is an LSP server for nix files.
|
||
|
||
#+BEGIN_SRC nix
|
||
nil
|
||
#+END_SRC
|
||
|
||
[[https://github.com/magic-wormhole/magic-wormhole][Magic Wormhole]] is a quick-and-easy way to securely transfer a file or directory between any two computers in the world, assuming that you have an out-of-band way to communicate a passphrase between them. Once installed, the actual command is ~wormhole~.
|
||
|
||
#+begin_src nix
|
||
magic-wormhole
|
||
#+end_src
|
||
|
||
These are the other programs I have installed globally. A few (like ~awscli~, ~mysql-client~, etc.)
|
||
are still Shibumi-specific; ideally I'd find another way to install them.
|
||
|
||
#+BEGIN_SRC nix
|
||
aws-iam-authenticator
|
||
awscli
|
||
iosevka
|
||
kube
|
||
kubectl
|
||
nerd-fonts.jetbrains-mono
|
||
openssh
|
||
tokei
|
||
yubikey-manager
|
||
#+END_SRC
|
||
|
||
* Program Configurations
|
||
:PROPERTIES:
|
||
:header-args: :noweb-ref programs :noweb no-export
|
||
:END:
|
||
|
||
This section contains applications whose config files will be generated by Home Manager. Home
|
||
Manager calls these "programs" (as opposed to "packages", which are just binaries to put on the
|
||
path).
|
||
|
||
** Simple Programs
|
||
|
||
Although Home-Manager generates config files for these programs, they don't need their own dedicated
|
||
configuration. I'm not sure what benefit there is to enabling ~jq~ as a Home Manager program, rather
|
||
than just as an available package, but there doesn't seem to be a downside.
|
||
|
||
#+BEGIN_SRC nix
|
||
bash.enable = true;
|
||
gh.enable = true;
|
||
home-manager.enable = true;
|
||
jq.enable = true;
|
||
lazygit.enable = true;
|
||
#+END_SRC
|
||
|
||
** Bat
|
||
|
||
~bat~ is a less alternative that performs syntax highlighting for a variety of languages. It also
|
||
provides syntax-highlighting versions of a few other commands. It's honestly not incredibly useful,
|
||
but still nice to have.
|
||
|
||
#+BEGIN_SRC nix
|
||
bat.enable = true;
|
||
bat.config.style = "numbers";
|
||
#+END_SRC
|
||
|
||
** Direnv
|
||
|
||
~direnv~ allows loading per-project paths as I navigate to their directories on the command line. I
|
||
leverage this with the emacs ~envrc~ package.
|
||
|
||
#+BEGIN_SRC nix
|
||
direnv.enable = true;
|
||
direnv.nix-direnv.enable = true;
|
||
#+END_SRC
|
||
|
||
** Eza
|
||
|
||
~eza~ is an ~ls~ replacement that offers icons, colors, and some easier command-line options.
|
||
|
||
#+BEGIN_SRC nix
|
||
eza.enable = true;
|
||
eza.enableBashIntegration = true;
|
||
eza.enableZshIntegration = true;
|
||
eza.icons = "auto";
|
||
#+END_SRC
|
||
|
||
** Fzf
|
||
|
||
~fzf~ offers a variety of fuzzy finders right from the shell. I haven't been using this, so I'll
|
||
turn it off for now.
|
||
|
||
#+BEGIN_SRC nix
|
||
fzf.defaultCommand = "fd --type f";
|
||
fzf.fileWidgetCommand = "fd --type f";
|
||
fzf.fileWidgetOptions = ["--preview" "'bat --color=always --style=numbers --line-range=:500 {}'"];
|
||
fzf.changeDirWidgetCommand = "fd --type d";
|
||
fzf.tmux.enableShellIntegration = true;
|
||
#+END_SRC
|
||
|
||
** Git
|
||
|
||
We all know what ~git~ does.
|
||
|
||
#+BEGIN_SRC nix
|
||
git.enable = true;
|
||
git.settings.user.name = "Joe Frikker";
|
||
git.settings.user.email = "joe.frikker@shibumi.com";
|
||
delta.enable = true;
|
||
delta.enableGitIntegration = true;
|
||
#+END_SRC
|
||
|
||
** Helix
|
||
|
||
The best text editor ever? Heresy!
|
||
|
||
#+BEGIN_SRC nix
|
||
helix.enable = true;
|
||
helix.defaultEditor = true;
|
||
|
||
helix.settings.editor = {
|
||
auto-format = false;
|
||
auto-pairs = false;
|
||
auto-save.after-delay.enable = true;
|
||
auto-save.after-delay.timeout = 1000;
|
||
cursorline = true;
|
||
idle-timeout = 0;
|
||
rulers = [100];
|
||
text-width = 100;
|
||
lsp.goto-reference-include-declaration = false;
|
||
cursor-shape.insert = "bar";
|
||
smart-tab.supersede-menu = true;
|
||
whitespace.render.newline = "all";
|
||
};
|
||
|
||
helix.settings.keys.normal.space = {
|
||
space = "file_picker";
|
||
"," = "buffer_picker";
|
||
# b = "@:sh wezterm cli spawn --cwd . -- tig blame <C-r>%<ret>";
|
||
};
|
||
|
||
|
||
helix.languages.language-server.eslint = {
|
||
args = ["--stdio"];
|
||
command = "vscode-eslint-language-server";
|
||
config.nodePath = "";
|
||
config.quiet = false;
|
||
config.rulesCustomizations = [];
|
||
config.run = "onType";
|
||
config.validate = "on";
|
||
config.codeAction.disableRuleComment.enable = true;
|
||
config.codeAction.disableRuleComment.location = "separateLine";
|
||
config.codeAction.showDocumentation.enable = true;
|
||
config.problems.shortenToSingleLine = false;
|
||
};
|
||
|
||
helix.languages.language-server.jdtls.config.java = {
|
||
autobuild.enabled = true;
|
||
completion.maxResults = 1000;
|
||
format.settings.url = "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml";
|
||
};
|
||
|
||
helix.languages.language-server.rust-analyzer.config.files.watcher = "client";
|
||
|
||
helix.languages.language = [
|
||
{
|
||
name = "typescript";
|
||
language-servers = [ "typescript-language-server" "vscode-eslint-language-server" ];
|
||
indent.tab-width = 4;
|
||
indent.unit = " ";
|
||
}
|
||
{
|
||
name = "tsx";
|
||
language-servers = [ "typescript-language-server" "vscode-eslint-language-server" ];
|
||
indent.tab-width = 4;
|
||
indent.unit = " ";
|
||
}
|
||
{
|
||
name = "java";
|
||
indent.tab-width = 4;
|
||
indent.unit = " ";
|
||
}
|
||
];
|
||
#+END_SRC
|
||
|
||
** Starship
|
||
|
||
~starship~ gives me a nice shell prompt, mostly showing the versions of relevant software I have
|
||
installed. It's shell-agnostic, so it works with both bash and zsh.
|
||
|
||
#+BEGIN_SRC nix :noweb no-export
|
||
starship.enable = true;
|
||
starship.settings = {
|
||
add_newline = false;
|
||
container.disabled = true;
|
||
git_branch.symbol = " ";
|
||
git_status.ahead = "⇡\${count}";
|
||
git_status.behind = "⇣\${count}";
|
||
git_status.diverged = "⇕⇡\${ahead_count}⇣\${behind_count}";
|
||
git_status.format = "([\\[\$conflicted\$staged\$ahead_behind\\]](\$style) )";
|
||
java.symbol = " ";
|
||
line_break.disabled = true;
|
||
nix_shell.symbol = " ";
|
||
nodejs.symbol = " ";
|
||
package.disabled = true;
|
||
palette = "catppuccin_frappe";
|
||
python.symbol = " ";
|
||
package.symbol = " ";
|
||
rust.symbol = " ";
|
||
docker_context.disabled = true;
|
||
shell.disabled = false;
|
||
} //
|
||
<<starship_colors>>
|
||
;
|
||
#+END_SRC
|
||
|
||
** Tmux
|
||
|
||
I'm using ~wezterm~ instead of ~tmux~ for now, so this is disabled, but I'm keeping the config here
|
||
in case I need it later.
|
||
|
||
#+BEGIN_SRC nix
|
||
tmux.baseIndex = 1;
|
||
tmux.clock24 = true;
|
||
tmux.customPaneNavigationAndResize = true;
|
||
tmux.historyLimit = 50000;
|
||
tmux.enable = true;
|
||
tmux.escapeTime = 0;
|
||
tmux.keyMode = "vi";
|
||
tmux.mouse = true;
|
||
tmux.shell = pkgs.zsh + "/bin/zsh";
|
||
tmux.shortcut = "Space";
|
||
tmux.tmuxinator.enable = true;
|
||
tmux.terminal = "xterm-256color";
|
||
tmux.extraConfig = ''
|
||
bind-key -T copy-mode-vi 'v' send -X begin-selection
|
||
bind-key -T copy-mode-vi 'y' send -X copy-selection-and-cancel
|
||
|
||
bind-key -T launcher g popup -h 100% -w 100% -E lazygit
|
||
bind-key g switch-client -Tlauncher
|
||
set -g default-command ""
|
||
'';
|
||
#+END_SRC
|
||
** Wezterm
|
||
|
||
~wezterm~ is a fast terminal emulator and multiplexer. It is configured using lua. We'll define its
|
||
config in two blocks, so we can get lua syntax highlighting in org. First, the nix configuration:
|
||
|
||
#+BEGIN_SRC nix :noweb no-export
|
||
wezterm.enable = true;
|
||
wezterm.extraConfig = ''
|
||
local config = {}
|
||
<<wezterm_colors>>
|
||
<<wezterm>>
|
||
'';
|
||
#+END_SRC
|
||
|
||
Then the real lua config:
|
||
|
||
#+BEGIN_SRC lua :noweb-ref wezterm
|
||
config.default_prog = { '/Users/jfrikker/.nix-profile/bin/zsh', '-l' }
|
||
config.font_size = 13.0
|
||
-- config.font = wezterm.font("JetBrains Mono")
|
||
config.inactive_pane_hsb = {
|
||
saturation = 0.5,
|
||
brightness = 0.5,
|
||
}
|
||
config.pane_focus_follows_mouse = true
|
||
config.switch_to_last_active_tab_when_closing_tab = true
|
||
config.tab_bar_at_bottom = true
|
||
config.use_fancy_tab_bar = false
|
||
config.window_decorations = "RESIZE"
|
||
#+END_SRC
|
||
|
||
I use a workspace switcher plugin, bouned to ~cmd-s~, that lets me quickly switch between
|
||
per-project workspaces using a single OS window.
|
||
|
||
#+BEGIN_SRC lua :noweb-ref wezterm
|
||
local workspace_switcher = wezterm.plugin.require("https://github.com/MLFlexer/smart_workspace_switcher.wezterm")
|
||
workspace_switcher.zoxide_path='~/.nix-profile/bin/zoxide'
|
||
#+END_SRC
|
||
|
||
Set up a default window layout when switching to a workspace that hasn't been opened in this session
|
||
yet. This will create three tabs, caled "edit", "run", and "term", and open helix in the first
|
||
one. I use "edit" for running the editor (obviously), "run" for building / running the application,
|
||
and "term" for miscellaneous other tasks. I sometimes split the "run" window into multiple panes if
|
||
the project requires multiple things to run simultaneously.
|
||
|
||
#+BEGIN_SRC lua :noweb-ref wezterm
|
||
wezterm.on('smart_workspace_switcher.workspace_switcher.created', function(window, path, workspace)
|
||
local editor = window:active_tab()
|
||
editor:set_title("edit")
|
||
editor:panes()[1]:send_text("hx\n")
|
||
local run = window:spawn_tab {}
|
||
run:set_title "run"
|
||
local term = window:spawn_tab {}
|
||
term:set_title "term"
|
||
editor:activate()
|
||
end)
|
||
|
||
#+END_SRC
|
||
|
||
Put the current workspace name in the lower-right corner of the window. Taken from
|
||
[https://alexplescan.com/posts/2024/08/10/wezterm/].
|
||
|
||
#+BEGIN_SRC lua :noweb-ref wezterm
|
||
local function segments_for_right_status(window)
|
||
return {
|
||
window:active_workspace(),
|
||
}
|
||
end
|
||
|
||
wezterm.on('update-status',
|
||
function(window, _)
|
||
local SOLID_LEFT_ARROW = utf8.char(0xe0b2)
|
||
local segments = segments_for_right_status(window)
|
||
|
||
local color_scheme = window:effective_config().resolved_palette
|
||
-- Note the use of wezterm.color.parse here, this returns
|
||
-- a Color object, which comes with functionality for lightening
|
||
-- or darkening the colour (amongst other things).
|
||
local bg = wezterm.color.parse(color_scheme.background)
|
||
local fg = color_scheme.foreground
|
||
|
||
-- Each powerline segment is going to be coloured progressively
|
||
-- darker/lighter depending on whether we're on a dark/light colour
|
||
-- scheme. Let's establish the "from" and "to" bounds of our gradient.
|
||
local gradient_to, gradient_from = bg
|
||
gradient_from = gradient_to:lighten(0.2)
|
||
|
||
-- Yes, WezTerm supports creating gradients, because why not?! Although
|
||
-- they'd usually be used for setting high fidelity gradients on your terminal's
|
||
-- background, we'll use them here to give us a sample of the powerline segment
|
||
-- colours we need.
|
||
local gradient = wezterm.color.gradient(
|
||
{
|
||
orientation = 'Horizontal',
|
||
colors = { gradient_from, gradient_to },
|
||
},
|
||
#segments -- only gives us as many colours as we have segments.
|
||
)
|
||
|
||
-- We'll build up the elements to send to wezterm.format in this table.
|
||
local elements = {}
|
||
|
||
for i, seg in ipairs(segments) do
|
||
local is_first = i == 1
|
||
|
||
if is_first then
|
||
table.insert(elements, { Background = { Color = 'none' } })
|
||
end
|
||
table.insert(elements, { Foreground = { Color = gradient[i] } })
|
||
table.insert(elements, { Text = SOLID_LEFT_ARROW })
|
||
|
||
table.insert(elements, { Foreground = { Color = fg } })
|
||
table.insert(elements, { Background = { Color = gradient[i] } })
|
||
table.insert(elements, { Text = ' ' .. seg .. ' ' })
|
||
end
|
||
|
||
window:set_right_status(wezterm.format(elements))
|
||
end)
|
||
#+END_SRC
|
||
|
||
Now we'll set up some keybindings:
|
||
|
||
| Key | Action |
|
||
| s-s | Switch Workspace |
|
||
| s-- | Vertical Split |
|
||
| s-\ | Horizontal split |
|
||
| s-hjkl | Switch to the pane in that direction |
|
||
| s-g | Launch ~lazygit~ in a new tab. The tab closes when ~lazygit~ exits. |
|
||
| s-z | Toggle full-screen zoom for the active pane. |
|
||
| s-t | Launch a terminal in a new tab. |
|
||
| s-d | Close the current tab (with confirmation). |
|
||
| s-123456789 | Switch to the tab with the corresponding number. |
|
||
|
||
#+BEGIN_SRC lua :noweb-ref wezterm
|
||
config.leader = { key = ' ', mods = 'CTRL', timeout_milliseconds = 10000 }
|
||
config.keys = {
|
||
{
|
||
key = "s",
|
||
mods = "CMD",
|
||
action = workspace_switcher.switch_workspace(),
|
||
},
|
||
{
|
||
key = "-",
|
||
mods = "CMD",
|
||
action = wezterm.action.SplitVertical {},
|
||
},
|
||
{
|
||
key = "\\",
|
||
mods = "CMD",
|
||
action = wezterm.action.SplitHorizontal {},
|
||
},
|
||
{
|
||
key = "h",
|
||
mods = "CMD",
|
||
action = wezterm.action.ActivatePaneDirection "Left",
|
||
},
|
||
{
|
||
key = "j",
|
||
mods = "CMD",
|
||
action = wezterm.action.ActivatePaneDirection "Down",
|
||
},
|
||
{
|
||
key = "k",
|
||
mods = "CMD",
|
||
action = wezterm.action.ActivatePaneDirection "Up",
|
||
},
|
||
{
|
||
key = "l",
|
||
mods = "CMD",
|
||
action = wezterm.action.ActivatePaneDirection "Right",
|
||
},
|
||
{
|
||
key = "g",
|
||
mods = "CMD",
|
||
action = wezterm.action.SpawnCommandInNewTab {
|
||
args = { wezterm.home_dir .. "/.nix-profile/bin/zsh", "-l", "-c", "lazygit" }
|
||
}
|
||
},
|
||
{
|
||
key = "t",
|
||
mods = "CMD",
|
||
action = wezterm.action.SpawnCommandInNewTab {}
|
||
},
|
||
{
|
||
key = 'z',
|
||
mods = 'CMD',
|
||
action = wezterm.action.TogglePaneZoomState,
|
||
},
|
||
{
|
||
key = 'd',
|
||
mods = 'CMD',
|
||
action = wezterm.action.CloseCurrentTab { confirm = true },
|
||
},
|
||
}
|
||
|
||
for i = 1, 9 do
|
||
-- CTRL+ALT + number to activate that tab
|
||
table.insert(config.keys, {
|
||
key = tostring(i),
|
||
mods = 'LEADER',
|
||
action = wezterm.action.ActivateTab(i - 1),
|
||
})
|
||
end
|
||
|
||
return config
|
||
#+END_SRC
|
||
|
||
** Zsh
|
||
|
||
This is my default shell for now. Unfortunately, nix requires a posix-compliant shell, so bash or
|
||
zsh it is.
|
||
|
||
#+BEGIN_SRC nix
|
||
zsh.enable = true;
|
||
zsh.oh-my-zsh.enable = true;
|
||
zsh.syntaxHighlighting.enable = true;
|
||
zsh.autosuggestion.enable = true;
|
||
zsh.historySubstringSearch.enable = true;
|
||
zsh.history.share = false;
|
||
zsh.cdpath = ["~/source"];
|
||
#+END_SRC
|
||
|
||
Actually, nix *really* requires ~bash~. This is a plugin that allows using zsh from nix-shell.
|
||
|
||
#+BEGIN_SRC nix
|
||
zsh.plugins = [
|
||
{
|
||
name = "zsh-nix-shell";
|
||
file = "nix-shell.plugin.zsh";
|
||
src = pkgs.fetchFromGitHub {
|
||
owner = "chisui";
|
||
repo = "zsh-nix-shell";
|
||
rev = "v0.7.0";
|
||
sha256 = "149zh2rm59blr2q458a5irkfh82y3dwdich60s9670kl3cl5h2m1";
|
||
};
|
||
}
|
||
];
|
||
#+END_SRC
|
||
|
||
* Emacs
|
||
|
||
Emacs is a huge portion of this configuration, so although it's a home-manager "program", I'm
|
||
breaking it out into its own top-level section. Many of the sections here have two code blocks: a
|
||
nix code block listing the emacs packages to include, and an elisp code block to configure them in
|
||
emacs.
|
||
|
||
#+BEGIN_SRC nix :noweb no-export :noweb-ref programs
|
||
emacs.enable = true;
|
||
emacs.extraPackages = epkgs: with epkgs; [
|
||
<<emacs_packages>>
|
||
];
|
||
emacs.extraConfig = ''
|
||
<<emacs_early>>
|
||
<<emacs_config>>
|
||
'';
|
||
#+END_SRC
|
||
|
||
** Keybindings
|
||
These keybindings are scattered throughout this file, I'm collecting them all here to make it easier
|
||
to track them.
|
||
|
||
| Key | Normal Key | Action | Mode |
|
||
| s-b | | [[id:6D1A8B35-3093-4DA6-A5AA-2A0D45112BAB][Switch project buffer]] | |
|
||
| s-B | | [[id:6D1A8B35-3093-4DA6-A5AA-2A0D45112BAB][Switch buffer]] | |
|
||
| s-f | C-x p f | [[id:A2F7FF5C-AFCC-44D4-9AA5-3498F6D6D5E0::workspace_keybinds][Open file in current project]] | |
|
||
| s-F | C-x p p | [[id:A2F7FF5C-AFCC-44D4-9AA5-3498F6D6D5E0::workspace_keybinds][Switch project]] | |
|
||
| S-s | C-x t RET | [[id:A2F7FF5C-AFCC-44D4-9AA5-3498F6D6D5E0::workspace_keybinds][Switch tab]] | |
|
||
| C-c a | | [[id:3B0BFCDC-DFD5-42A0-97D8-3B032B128AF7][Org agenda]] | |
|
||
| C-c A | | [[id:5CCB676B-9FF0-47B4-8AF2-10FD3899A646][Eglot code actions]] | prog |
|
||
| C-c c | | [[id:3B0BFCDC-DFD5-42A0-97D8-3B032B128AF7][Org capture]] | |
|
||
| C-c D | | [[id:5CCB676B-9FF0-47B4-8AF2-10FD3899A646][Show flymake diagnostics]] | prog |
|
||
| C-c h | | [[id:6D1A8B35-3093-4DA6-A5AA-2A0D45112BAB][Consult shell history]] | |
|
||
| C-c I | | [[id:5CCB676B-9FF0-47B4-8AF2-10FD3899A646][Eglot find implementation]] | prog |
|
||
| C-c l | | [[id:3B0BFCDC-DFD5-42A0-97D8-3B032B128AF7][Org store link]] | |
|
||
| C-c n | | [[id:3B0BFCDC-DFD5-42A0-97D8-3B032B128AF7][Org Roam prefix]] | |
|
||
| C-c R | | [[id:5CCB676B-9FF0-47B4-8AF2-10FD3899A646][Eglot rename]] | prog |
|
||
| C-c u | | [[id:5CCB676B-9FF0-47B4-8AF2-10FD3899A646][Cycle string inflection]] | various |
|
||
| C-c M-g | | [[id:4F426E77-CE03-4BF8-8D1B-7791C7C02B28][Magit file dispatch]] | |
|
||
|
||
** Global Settings
|
||
These are just some general settings that apply everywhere. First, a few packages:
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
doom-modeline
|
||
ligature
|
||
mixed-pitch
|
||
ace-window
|
||
#+END_SRC
|
||
|
||
Turn off some default settings that I don't find useful.
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(setq inhibit-startup-screen t)
|
||
(setq initial-scratch-message nil)
|
||
(menu-bar-mode -1)
|
||
(scroll-bar-mode -1)
|
||
(tool-bar-mode -1)
|
||
(setq make-backup-files nil)
|
||
(setq split-window-threshold 200)
|
||
(setq split-height-threshold 200)
|
||
(setq view-read-only t)
|
||
#+END_SRC
|
||
|
||
Make sure which-key mode is always on. The on-the-fly keymap help it gives is indespensable.
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(which-key-mode)
|
||
#+END_SRC
|
||
|
||
Automatically refresh ~dired~ buffers when switching to them, instead of requiring an explicit
|
||
refresh.
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(setq dired-auto-revert-buffer t)
|
||
#+END_SRC
|
||
|
||
Similarly, highlighting the line the cursor is on is really useful for figuring out where you are,
|
||
especially on monitors with a lot of vertical space.
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(global-hl-line-mode)
|
||
#+END_SRC
|
||
|
||
From the docs: "If ‘complete’, TAB first tries to indent the current line, and if the line was
|
||
already indented, then try to complete the thing at point." Recommended by [[https://github.com/minad/corfu][the ~corfu~ readme]].
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(setq tab-always-indent 'complete)
|
||
#+END_SRC
|
||
|
||
Set the cursor to be a bar. I'm not sure what the benefit would be of a block cursor, since the
|
||
current character isn't really "selected" like it would be in Helix. When I had God mode set up, I
|
||
switched to a block cursor during normal mode, just because I was so used to it from Helix and
|
||
Vim. Now, I don't think there's really a need to ever switch.
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(setq-default cursor-type 'bar)
|
||
#+END_SRC
|
||
|
||
Emacs is supposed to be all about automation (maybe?). There's basically never a case where I don't
|
||
want changes I've made to be immediately saved to disk. So let's have Emacs do that. There's still a
|
||
fairly long (~5 sec) delay before changes are saved, which is kind of annoying. Maybe I'll look into
|
||
that at some point?
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(auto-save-visited-mode)
|
||
#+END_SRC
|
||
|
||
Repeat-mode takes care of eliminating some annoying duplicate keypresses. I mostly use repeat-mode
|
||
for two-character commands that need to be repeated; ~devil~ also has some single-key-with-modifier
|
||
repeats set up that only apply in that mode.
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(repeat-mode)
|
||
#+END_SRC
|
||
|
||
Save the minibuffer history to disk, so recently-used options show up at the top of the picker
|
||
across restarts.
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(savehist-mode)
|
||
#+END_SRC
|
||
|
||
The doom modeline looks nice. I'm turning off IRC notifications, because it was slow to update (and
|
||
it would update after every command, slowing down everything). I don't need the workspace name when
|
||
I have the tab bar at the top of the window. The other settings just clear up some space, and make
|
||
things look at little nicer.
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(setq doom-modeline-irc nil)
|
||
(setq doom-modeline-workspace-name nil)
|
||
(setq doom-modeline-buffer-encoding nil)
|
||
(setq doom-modeline-vcs-max-length 30)
|
||
(doom-modeline-mode)
|
||
#+END_SRC
|
||
|
||
Ligature mode combines character sequences like <= into a single on-screen glyph. It looks kind of
|
||
cool, so I'm turning it on in prog mode. The enabled ligatures are font-specific, taken from [[https://github.com/mickeynp/ligature.el/wiki#jetbrains-mono][the
|
||
ligature wiki]].
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package ligature
|
||
:after prog-mode
|
||
:config
|
||
(ligature-set-ligatures 'prog-mode
|
||
'("--" "---" "==" "===" "!=" "!==" "=!="
|
||
"=:=" "=/=" "<=" ">=" "&&" "&&&" "&=" "++" "+++" "***" ";;" "!!"
|
||
"??" "???" "?:" "?." "?=" "<:" ":<" ":>" ">:" "<:<" "<>" "<<<" ">>>"
|
||
"<<" ">>" "||" "-|" "_|_" "|-" "||-" "|=" "||=" "##" "###" "####"
|
||
"#{" "#[" "]#" "#(" "#?" "#_" "#_(" "#:" "#!" "#=" "^=" "<$>" "<$"
|
||
"$>" "<+>" "<+" "+>" "<*>" "<*" "*>" "</" "</>" "/>" "<!--" "<#--"
|
||
"-->" "->" "->>" "<<-" "<-" "<=<" "=<<" "<<=" "<==" "<=>" "<==>"
|
||
"==>" "=>" "=>>" ">=>" ">>=" ">>-" ">-" "-<" "-<<" ">->" "<-<" "<-|"
|
||
"<=|" "|=>" "|->" "<->" "<~~" "<~" "<~>" "~~" "~~>" "~>" "~-" "-~"
|
||
"~@" "[||]" "|]" "[|" "|}" "{|" "[<" ">]" "|>" "<|" "||>" "<||"
|
||
"|||>" "<|||" "<|>" "..." ".." ".=" "..<" ".?" "::" ":::" ":=" "::="
|
||
":?" ":?>" "//" "///" "/*" "*/" "/=" "//=" "/==" "@_" "__" "???"
|
||
"<:<" ";;;"))
|
||
(global-ligature-mode t))
|
||
#+END_SRC
|
||
|
||
Use variable-width fonts in text buffers. ~mixed-pitch-mode~ keeps most code blocks as monospace.
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(add-hook 'text-mode-hook 'mixed-pitch-mode)
|
||
|
||
(defun no-mixed-pitch ()
|
||
(mixed-pitch-mode -1))
|
||
|
||
(add-hook 'yaml-ts-mode-hook 'no-mixed-pitch)
|
||
(add-hook 'mhtml-mode 'no-mixed-pitch)
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package ace-window
|
||
:bind (("M-o" . ace-window)
|
||
("s-o" . ace-window))
|
||
:config
|
||
(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
|
||
(setq aw-dispatch-always t))
|
||
#+END_SRC
|
||
|
||
Set up a few project- / workspace-related key bindings.
|
||
|
||
#+name: workspace_keybinds
|
||
#+BEGIN_SRC elisp :noweb no :noweb-ref emacs_config
|
||
(bind-keys ("s-f" . project-find-file)
|
||
("s-F" . project-switch-project))
|
||
(bind-keys :map tab-bar-mode-map
|
||
("s-s" . tab-bar-switch-to-tab))
|
||
#+END_SRC
|
||
|
||
Don't run a server. I'm not using this, I can always turn it back on if I need to.
|
||
|
||
#+BEGIN_SRC elisp :noweb no :noweb-ref emacs_config
|
||
(server-mode -1)
|
||
#+END_SRC
|
||
|
||
All the beeping is annoying. This flashes a huge icon over the screen, which is still annoying, but
|
||
less so?
|
||
|
||
#+BEGIN_SRC elisp :noweb no :noweb-ref emacs_config
|
||
(setq visible-bell t)
|
||
#+END_SRC
|
||
|
||
I'm trying this out. This disables visual selections. It's bold, which is why I like it :)
|
||
|
||
#+BEGIN_SRC elisp :noweb no :noweb-ref emacs_config
|
||
(transient-mark-mode -1)
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb no :noweb-ref emacs_config
|
||
(use-package tab-bar
|
||
:config
|
||
(tab-bar-history-mode)
|
||
(defvar-keymap tab-bar-repeat-map
|
||
:repeat t
|
||
"<left>" #'tab-bar-history-back
|
||
"<right>" #'tab-bar-history-forward))
|
||
#+END_SRC
|
||
|
||
** Editing
|
||
|
||
This configuration affects general text editing, across all modes. First, some packages:
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
avy
|
||
devil
|
||
edit-indirect
|
||
god-mode
|
||
string-inflection
|
||
#+END_SRC
|
||
|
||
I find Emacs to be almost unusable on a normal keyboard unless there's some way to avoid all the
|
||
modifier keys. I tried Evil mode first. But although I love Vim, it just seemed to be hiding too
|
||
much of the Emacs native functionality. The keybindings were also much harder to configure. I tried
|
||
God mode, which has the benefit of re-using the normal Emacs keybindings and commands. It was
|
||
annoying, though, because some read-only buffers (like magit buffers) have their own "normal-mode"
|
||
commands that don't use modifiers. It felt jarring to switch to "insert mode" to use those
|
||
keys. It's also hard to repeat sequences of M-f, M-b, etc. I could have made a ton of repeat maps,
|
||
but I'm worried that having too many repeat maps will also be annoying (I'll have to explicitly
|
||
cancel them more frequently). In the end, I opted for Devil mode. It avoids modifier keys, and has
|
||
its own devil-mode-only repeat maps that don't interfere when using normal modifier key sequences.
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package devil
|
||
:disable t
|
||
:config
|
||
(global-devil-mode)
|
||
(setq devil-repeatable-keys
|
||
'(("%k /")
|
||
("%k d")
|
||
("%k k")
|
||
("%k m ^")
|
||
("%k m b" "%k m f" "%k m a" "% k m e")
|
||
("%k m @" "%k m h")
|
||
("%k m y")
|
||
("%k p" "%k n" "%k b" "%k f" "%k a" "%k e")
|
||
("%k s")
|
||
("%k m d" "%k m <backspace>"))))
|
||
|
||
(use-package god-mode
|
||
:config
|
||
(bind-keys :map god-local-mode-map
|
||
("i" . (lambda () (interactive) (god-mode-all -1)))
|
||
("z" . repeat))
|
||
(bind-keys ("<escape>" . (lambda () (interactive) (god-mode-all 1))))
|
||
(defun my-god-mode-update-cursor-type ()
|
||
(setq cursor-type (cond
|
||
(org-present-mode nil)
|
||
((or god-local-mode buffer-read-only) 'box)
|
||
(t 'bar))))
|
||
|
||
(add-hook 'post-command-hook #'my-god-mode-update-cursor-type)
|
||
(which-key-enable-god-mode-support))
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package avy
|
||
:bind ("M-j" . avy-goto-char-timer))
|
||
|
||
(defun avy-goto-char-timer-embark ()
|
||
(interactive)
|
||
(require 'avy)
|
||
(require 'embark)
|
||
(let ((avy-action-oneshot
|
||
(lambda (pt)
|
||
(unwind-protect
|
||
(save-excursion
|
||
(goto-char pt)
|
||
(embark-act))
|
||
(select-window
|
||
(cdr (ring-ref avy-ring 0)))))))
|
||
(avy-goto-char-timer)))
|
||
|
||
(bind-keys
|
||
("M-J" . avy-goto-char-timer-embark))
|
||
|
||
(use-package isearch
|
||
:custom
|
||
(isearch-wrap-pause 'no-ding))
|
||
|
||
(use-package string-inflection
|
||
:commands
|
||
string-inflection-java-style-cycle
|
||
string-inflection-rust-style-cycle
|
||
:config
|
||
(defun string-inflection-rust-style-cycle-function (str)
|
||
(cond
|
||
((string-inflection-snake-case-p str)
|
||
(string-inflection-upcase-function str))
|
||
((string-inflection-upcase-p str)
|
||
(string-inflection-pascal-case-function str))
|
||
(t
|
||
(string-inflection-snake-case-function str))))
|
||
(defun string-inflection-rust-style-cycle ()
|
||
(interactive)
|
||
(string-inflection--symbol-or-region #'string-inflection-rust-style-cycle-function))
|
||
(defvar-keymap string-inflection-java-map
|
||
:repeat t
|
||
"u" #'string-inflection-java-style-cycle)
|
||
(defvar-keymap string-inflection-rust-map
|
||
:repeat t
|
||
"u" #'string-inflection-rust-style-cycle))
|
||
|
||
(use-package edit-indirect
|
||
:bind ("C-x 4 n" . edit-indirect-region))
|
||
#+END_SRC
|
||
|
||
** Language Support
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
consult-eglot
|
||
haskell-ts-mode
|
||
jenkinsfile-mode
|
||
lispy
|
||
lua-mode
|
||
nix-mode
|
||
rust-mode
|
||
smartparens
|
||
sly
|
||
sly-macrostep
|
||
sql-indent
|
||
tree-sitter
|
||
treesit-grammars.with-all-grammars
|
||
yaml-pro
|
||
#+END_SRC
|
||
|
||
I had some weird problems with jsx code where the default M-q would just do a text fill rather than
|
||
a re-indent. The solution seems to be to only do the ~syntax-ppss~ stuff if tree-sitter isn't
|
||
available? It seems to work for now anyway...
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package prog-mode
|
||
:config
|
||
(defun prog-fill-reindent-defun (&optional argument)
|
||
"Refill or reindent the paragraph or defun that contains point.
|
||
|
||
If the point is in a string or a comment, fill the paragraph that
|
||
contains point or follows point.
|
||
|
||
Otherwise, reindent the function definition that contains point
|
||
or follows point."
|
||
(interactive "P")
|
||
(save-excursion
|
||
(let ((treesit-text-node
|
||
(and (treesit-available-p)
|
||
(treesit-parser-list)
|
||
(treesit-node-match-p
|
||
(treesit-node-at (point)) 'text t))))
|
||
(if (or treesit-text-node
|
||
(and (not (and (treesit-available-p) (treesit-parser-list))) (nth 8 (syntax-ppss)))
|
||
(re-search-forward "\\s-*\\s<" (line-end-position) t))
|
||
(fill-paragraph argument (region-active-p))
|
||
(beginning-of-defun)
|
||
(let ((start (point)))
|
||
(end-of-defun)
|
||
(indent-region start (point) nil)))))))
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package nix-mode
|
||
:mode "\\.nix\\'")
|
||
|
||
(use-package nix-flake
|
||
:bind ("C-x p n" . project-nix-flake)
|
||
:config
|
||
(defun project-nix-flake ()
|
||
(interactive)
|
||
(nix-flake (project-root (project-current t)))))
|
||
|
||
(use-package lua-mode
|
||
:mode "\\.lua\\'")
|
||
|
||
(use-package jenkinsfile-mode
|
||
:mode "\\.jenkinsfile\\'")
|
||
|
||
(setq major-mode-remap-alist '((rust-mode . rust-mode)))
|
||
|
||
(use-package rust-mode
|
||
:init (setq rust-mode-treesitter-derive t))
|
||
|
||
(defun yaml-validate ()
|
||
(interactive)
|
||
(compile (concat "yamllint -d '{extends: default, rules: {indentation: {indent-sequences: false}, braces: {max-spaces-inside: 1}}}' " (buffer-file-name)) t))
|
||
|
||
(use-package yaml-ts-mode
|
||
:mode "\\.yaml\\'")
|
||
|
||
(use-package yaml-pro
|
||
:hook (yaml-ts-mode . yaml-pro-ts-mode))
|
||
|
||
(use-package sly
|
||
:hook lisp-mode-hook
|
||
:custom
|
||
(inferior-lisp-program "sbcl"))
|
||
|
||
(use-package sly-macrostep
|
||
:defer t
|
||
:after sly
|
||
:init
|
||
(add-to-list 'sly-contribs 'sly-macrostep))
|
||
|
||
(use-package consult-eglot
|
||
:commands
|
||
consult-eglot-symbols)
|
||
|
||
(use-package typescript-ts-mode
|
||
:mode "\\.ts\\'" ("\\.tsx\\'" . tsx-ts-mode)
|
||
:custom
|
||
(typescript-ts-mode-indent-offset 4)
|
||
(defun tsx-ts-mode--indent-compatibility-b893426 ()
|
||
"Indent rules helper, to handle different releases of tree-sitter-tsx.
|
||
Check if a node type is available, then return the right indent rules."
|
||
;; handle https://github.com/tree-sitter/tree-sitter-typescript/commit/b893426b82492e59388a326b824a346d829487e8
|
||
(condition-case nil
|
||
(progn (treesit-query-capture 'tsx '((jsx_fragment) @capture))
|
||
`(((match "<" "jsx_fragment") parent 0)
|
||
((parent-is "jsx_fragment") parent typescript-ts-mode-indent-offset)))
|
||
(treesit-query-error
|
||
`(((match "<" "jsx_text") parent 0)
|
||
((parent-is "jsx_text") grand-parent typescript-ts-mode-indent-offset))))))
|
||
|
||
(use-package rust-ts-mode
|
||
:mode "\\.rs\\'"
|
||
:config
|
||
(bind-keys :map rust-ts-mode-map
|
||
("C-c u" . string-inflection-rust-style-cycle)))
|
||
|
||
(use-package java-ts-mode
|
||
:mode "\\.java\\'"
|
||
:config
|
||
(bind-keys :map java-ts-mode-map
|
||
("C-c u" . string-inflection-java-style-cycle))
|
||
(push `((n-p-gp nil "block" "lambda_expression") parent-bol java-ts-mode-indent-offset)
|
||
(cdar java-ts-mode--indent-rules))
|
||
(push `((n-p-gp "}" "block" "lambda_expression") parent-bol 0)
|
||
(cdar java-ts-mode--indent-rules)))
|
||
|
||
(use-package sql-indent
|
||
:hook (sql-mode-hook . sqlind-minor-mode))
|
||
|
||
(use-package haskell-ts-mode
|
||
:mode "\\.hs\\'"
|
||
:custom (haskell-ts-use-indent tc))
|
||
|
||
(use-package flymake
|
||
:bind
|
||
(:map prog-mode-map
|
||
("C-c D" . flymake-show-project-diagnostics)
|
||
("M-n" . flymake-goto-next-error)
|
||
("M-p" . flymake-goto-prev-error)))
|
||
|
||
(use-package eglot
|
||
:commands eglot
|
||
:custom
|
||
(eglot-ignored-server-capabilities '(:inlayHintProvider))
|
||
:config
|
||
(bind-keys :map eglot-mode-map
|
||
("C-c A" . eglot-code-actions)
|
||
("C-c R" . eglot-rename)
|
||
("C-c I" . eglot-find-implementation))
|
||
|
||
(add-hook 'eglot-managed-mode-hook (lambda () (eglot-inlay-hints-mode -1)) nil t))
|
||
|
||
(use-package lispy
|
||
:hook (emacs-lisp-mode lisp-mode)
|
||
:init (setq lispy-compat '(god-mode edebug))
|
||
:config
|
||
(bind-keys :map lispy-mode-map
|
||
("i" . (lambda ()
|
||
(interactive)
|
||
(if god-global-mode
|
||
(god-mode-all -1)
|
||
(special-lispy-tab))))))
|
||
|
||
(use-package smartparens
|
||
:hook
|
||
emacs-lisp-mode
|
||
common-lisp-mode
|
||
:config
|
||
(add-to-list 'sp-lisp-modes 'sly-mrepl-mode)
|
||
(require 'smartparens-config)
|
||
(setq sp-highlight-pair-overlay nil
|
||
sp-highlight-wrap-overlay nil
|
||
sp-highlight-wrap-tag-overlay nil))
|
||
|
||
(setq-default indent-tabs-mode nil)
|
||
(setq-default tab-width 4)
|
||
(global-tree-sitter-mode 1)
|
||
(setq-default fill-column 100)
|
||
(defun jf-init-fill-column ()
|
||
(display-fill-column-indicator-mode 1))
|
||
|
||
(defvar jf/class-name-to-file-search-path (list "src/main/java" "src/test/java"))
|
||
|
||
(defun jf/class-name-to-file (class-name)
|
||
(let ((root (project-root (project-current t)))
|
||
(fragment (format "%s.java" (replace-regexp-in-string "\\." "/" class-name)))
|
||
(result))
|
||
(dolist (p jf/class-name-to-file-search-path result)
|
||
(unless result
|
||
(let ((path (format "%s/%s" p fragment)))
|
||
(when (file-exists-p (concat root path))
|
||
(setq result path)))))))
|
||
|
||
(defun jf/compile-class-to-file ()
|
||
(jf/class-name-to-file (concat (match-string 1) (match-string 2))))
|
||
|
||
;(add-to-list 'compilation-error-regexp-alist-alist
|
||
; '(java-stack-trace .
|
||
; ("^[[:space:]]*at \\(\\(?:[[:lower:]]+\\.\\)+\\)[^(]+(\\([[:alnum:]]+\\)\\.java:\\([[:digit:]]+\\))"
|
||
; jf/compile-class-to-file 3)))
|
||
;(add-to-list 'compilation-error-regexp-alist 'java-stack-trace)
|
||
|
||
(add-hook 'prog-mode-hook 'jf-init-fill-column)
|
||
(add-hook 'java-mode-hook (lambda () (c-set-offset 'arglist-intro '+)))
|
||
|
||
(dolist (hook
|
||
(list 'grep-mode-hook
|
||
'flymake-project-diagnostics-mode-hook
|
||
'xref--xref-buffer-mode-hook
|
||
'embark-collect-mode-hook
|
||
'compilation-mode-hook
|
||
'sly-mrepl-mode-hook
|
||
'eglot-list-connections-mode-hook))
|
||
(add-hook hook 'tab-line-mode))
|
||
|
||
(let ((bottom-window-params '((side . bottom)
|
||
(slot . 1)
|
||
(window-height . 0.33)
|
||
(window-parameters (no-delete-other-windows . t))))
|
||
(right-window-params '((side . right)
|
||
(window-width . 0.5)
|
||
(window-parameters (no-delete-other-windows . t)))))
|
||
(setopt display-buffer-alist
|
||
(append
|
||
(mapcar (lambda (buffer) `(,buffer
|
||
(display-buffer-reuse-window display-buffer-in-side-window)
|
||
,@bottom-window-params))
|
||
(list
|
||
"\\*Flymake diagnostics *"
|
||
"\\*Embark Export *"
|
||
"\\*Embark Collect *"
|
||
"\\*xref*"
|
||
"\\*compilation\\*"
|
||
"\\*sly-mrepl *"
|
||
"\\*Messages\\*"
|
||
"\\*EGLOT connections\\*"))
|
||
(mapcar (lambda (buffer) `(,buffer
|
||
(display-buffer-reuse-window display-buffer-in-side-window)
|
||
,@right-window-params))
|
||
(list
|
||
"magit: *"
|
||
"magit-revision: *"
|
||
"\\*eldoc\\*")))))
|
||
|
||
(setq ediff-split-window-function 'split-window-horizontally)
|
||
#+END_SRC
|
||
|
||
** Envrc
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
envrc
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package envrc
|
||
:hook (after-init . envrc-global-mode)
|
||
:custom
|
||
(envrc-show-summary-in-minibuffer nil))
|
||
#+END_SRC
|
||
|
||
** Magit
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
consult-gh
|
||
diff-hl
|
||
forge
|
||
magit
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package magit
|
||
:bind (("C-x g" . magit-status)
|
||
("s-g" . magit-status)
|
||
("C-x M-g" . magit-dispatch)
|
||
("C-c M-g" . magit-file-dispatch))
|
||
:custom
|
||
(magit-save-repository-buffers 'dontask)
|
||
:config
|
||
(set-face-attribute 'git-commit-summary nil :inherit nil)
|
||
(defun magit-fetch-all-on-load ()
|
||
(magit-run-git-async "fetch" "--all"))
|
||
(add-hook 'magit-status-mode-hook 'magit-fetch-all-on-load)
|
||
(remove-hook 'magit-status-headers-hook 'magit-insert-tags-header))
|
||
|
||
(use-package forge
|
||
:after magit)
|
||
|
||
(use-package diff-hl
|
||
:after magit
|
||
:config
|
||
(global-diff-hl-mode)
|
||
(add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh))
|
||
#+END_SRC
|
||
|
||
** Vertico
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
cape
|
||
consult
|
||
corfu
|
||
embark
|
||
embark-consult
|
||
marginalia
|
||
orderless
|
||
vertico
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package consult
|
||
:bind (
|
||
;; ("C-c M-x" . consult-mode-command)
|
||
("C-c h" . consult-history)
|
||
;; ("C-c k" . consult-kmacro)
|
||
;; ("C-c m" . consult-man)
|
||
;; ([remap Info-search] . consult-info)
|
||
|
||
;; ("C-x M-:" . consult-complex-command)
|
||
("C-x b" . consult-buffer)
|
||
("s-b" . consult-project-buffer)
|
||
("s-B" . consult-buffer)
|
||
("C-x 4 b" . consult-buffer-other-window)
|
||
("C-x 5 b" . consult-buffer-other-frame)
|
||
("C-x t b" . consult-buffer-other-tab)
|
||
("C-x r b" . consult-bookmark)
|
||
("C-x p b" . consult-project-buffer)
|
||
|
||
("M-#" . consult-register-load)
|
||
("M-'" . consult-register-store)
|
||
("C-M-#" . consult-register)
|
||
("M-y" . consult-yank-pop)
|
||
("M-g e" . consult-compile-error)
|
||
("M-g f" . consult-flymake)
|
||
("M-g g" . consult-goto-line)
|
||
("M-g M-g" . consult-goto-line)
|
||
("M-g o" . consult-org-heading)
|
||
("M-g m" . consult-mark)
|
||
("M-g k" . consult-global-mark)
|
||
("M-g i" . consult-imenu)
|
||
("M-g I" . consult-imenu-multi)
|
||
("M-s r" . consult-ripgrep)
|
||
("M-s l" . consult-line)
|
||
("M-s L" . consult-line-multi)
|
||
("M-s e" . consult-isearch-history)
|
||
:map isearch-mode-map
|
||
("M-e" . consult-isearch-history)
|
||
("M-s e" . consult-isearch-history)
|
||
("M-s l" . consult-line)
|
||
("M-s L" . consult-line-multi)
|
||
:map minibuffer-local-map
|
||
("M-s" . consult-history)
|
||
("M-r" . consult-history))
|
||
:hook (completion-list-mode . consult-preview-at-point-mode)
|
||
:init
|
||
(advice-add #'register-preview :override #'consult-register-window)
|
||
(setq register-preview-delay 0.5)
|
||
|
||
;; (setq xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref)
|
||
|
||
:config
|
||
|
||
(consult-customize
|
||
consult-theme :preview-key '(:debounce 0.2 any)
|
||
consult-ripgrep consult-git-grep consult-grep consult-man
|
||
consult-bookmark consult-recent-file consult-xref
|
||
consult--source-bookmark consult--source-file-register
|
||
consult--source-recent-file consult--source-project-recent-file
|
||
;; :preview-key "M-."
|
||
:preview-key '(:debounce 0.4 any))
|
||
|
||
(setq consult-narrow-key "<") ;; "C-+"
|
||
)
|
||
|
||
(use-package vertico
|
||
:custom
|
||
(vertico-count 25)
|
||
:config
|
||
(vertico-mode))
|
||
|
||
;; (use-package vertico-buffer
|
||
;; :after vertico
|
||
;; :config
|
||
;; (vertico-buffer-mode))
|
||
|
||
(use-package orderless
|
||
:custom
|
||
(completion-styles '(orderless))
|
||
(orderless-matching-styles '(orderless-literal orderless-flex)))
|
||
|
||
(use-package marginalia
|
||
:config
|
||
(marginalia-mode))
|
||
|
||
(use-package embark
|
||
:bind
|
||
(("C-." . embark-act) ;; pick some comfortable binding
|
||
))
|
||
|
||
(use-package corfu
|
||
:custom
|
||
(corfu-cycle t)
|
||
(corfu-popupinfo-mode)
|
||
(corfu-quit-at-boundary nil)
|
||
(corfu-on-exact-match 'show)
|
||
:config
|
||
(global-corfu-mode))
|
||
|
||
;; Consult users will also want the embark-consult package.
|
||
(use-package embark-consult
|
||
:hook (embark-collect-mode . consult-preview-at-point-mode))
|
||
|
||
(use-package cape
|
||
:config
|
||
(add-hook 'completion-at-point-functions #'cape-file))
|
||
#+END_SRC
|
||
|
||
** Org
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
org-modern
|
||
org-present
|
||
org-roam
|
||
ox-slack
|
||
verb
|
||
visual-fill-column
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package org
|
||
:mode ("\\.org\\'" . org-mode)
|
||
:bind (("C-c a" . org-agenda)
|
||
("C-c c" . org-capture)
|
||
("C-c l" . org-store-id-link))
|
||
:config
|
||
(add-hook 'org-mode-hook 'visual-line-mode)
|
||
(setq org-link-keep-stored-after-insertion t
|
||
org-todo-keywords
|
||
'((sequence
|
||
"TODO(o)"
|
||
"IN_PROGRESS(p!)"
|
||
"WAITING(w@)"
|
||
"|"
|
||
"DONE(d!)"
|
||
"CANCELLED(c@)"))
|
||
org-capture-templates
|
||
'(("t" "Todo" entry
|
||
(here)
|
||
"** TODO %?\n- State \"TODO\" %U\n")
|
||
("w" "Work stuff")
|
||
("wp" "Work Project" entry
|
||
(file+headline "~/org/todo/work.org.age" "Next Actions")
|
||
"*** PROJ %?\n- State \"PROJ\" %U\n")
|
||
("wa" "Work Next Action" entry
|
||
(file+headline "~/org/todo/work.org.age" "Next Actions")
|
||
"*** TODO %?\n- State \"TODO\" %U\n")
|
||
("p" "Personal stuff")
|
||
("pp" "Personal Project" entry
|
||
(file+headline "~/org/todo/personal.org.age" "Next Actions")
|
||
"*** PROJ %?\n- State \"PROJ\" %U\n")
|
||
("pa" "Personal Next Action" entry
|
||
(file+headline "~/org/todo/personal.org.age" "Next Actions")
|
||
"*** TODO %?\n- State \"TODO\" %U\n"))
|
||
org-agenda-files '("~/org/todo/work.org.age" "~/org/todo/personal.org.age")
|
||
org-agenda-todo-ignore-scheduled 'future
|
||
org-agenda-tags-todo-honor-ignore-options t
|
||
org-enforce-todo-dependencies t
|
||
org-agenda-dim-blocked-tasks 'invisible)
|
||
(set-face-attribute 'org-level-1 nil :font "Iosevka Aile")
|
||
(set-face-attribute 'org-level-2 nil :font "Iosevka Aile")
|
||
(set-face-attribute 'org-level-3 nil :font "Iosevka Aile")
|
||
(set-face-attribute 'org-level-4 nil :font "Iosevka Aile")
|
||
(set-face-attribute 'org-level-5 nil :font "Iosevka Aile")
|
||
(set-face-attribute 'org-level-6 nil :font "Iosevka Aile")
|
||
(set-face-attribute 'org-level-7 nil :font "Iosevka Aile")
|
||
(set-face-attribute 'org-level-8 nil :font "Iosevka Aile"))
|
||
|
||
(use-package org-present
|
||
:commands (org-present)
|
||
:config
|
||
(defun my/org-present-start ()
|
||
(setq-local my/org-present-face-remapping-cookies nil)
|
||
;; Tweak font sizes
|
||
(dolist (face '((default :height 2.0)
|
||
(header-line :height 4.0 :background "#303446")
|
||
(org-document-title :height 1.75)
|
||
(org-code :height 2.0)
|
||
(org-verbatim :height 2.0)
|
||
(org-block :height 1.25)
|
||
(org-block-begin-line :height 0.7)
|
||
(org-level-1 :height 3.0)
|
||
(org-level-2 :height 3.0)
|
||
(org-level-3 :height 3.0)
|
||
(org-level-4 :height 3.0)
|
||
(org-level-5 :height 3.0)
|
||
(org-level-6 :height 3.0)
|
||
(org-level-7 :height 3.0)
|
||
(org-level-8 :height 3.0)))
|
||
(push (apply #'face-remap-add-relative face) my/org-present-face-remapping-cookies))
|
||
|
||
;; Set a blank header line string to create blank space at the top
|
||
(setq header-line-format " ")
|
||
|
||
;; Display inline images automatically
|
||
(org-display-inline-images)
|
||
(org-present-hide-cursor)
|
||
|
||
;; Center the presentation and wrap lines
|
||
(visual-fill-column-mode 1)
|
||
(setq visual-fill-column-center-text t)
|
||
;; (visual-line-mode 1)
|
||
)
|
||
|
||
(defun my/org-present-end ()
|
||
;; Reset font customizations
|
||
(dolist (cookie my/org-present-face-remapping-cookies)
|
||
(face-remap-remove-relative cookie))
|
||
|
||
;; Clear the header line string so that it isn't displayed
|
||
(setq header-line-format nil)
|
||
|
||
;; Stop displaying inline images
|
||
(org-remove-inline-images)
|
||
(org-present-show-cursor)
|
||
|
||
;; Stop centering the document
|
||
(visual-fill-column-mode 0)
|
||
;; (visual-line-mode 0)
|
||
)
|
||
|
||
(add-hook 'org-present-mode-hook 'my/org-present-start)
|
||
(add-hook 'org-present-mode-quit-hook 'my/org-present-end))
|
||
|
||
(use-package org-roam
|
||
:bind (("C-c n l" . org-roam-buffer-toggle)
|
||
("C-c n f" . org-roam-node-find)
|
||
("C-c n g" . org-roam-graph)
|
||
("C-c n i" . org-roam-node-insert)
|
||
("C-c n c" . org-roam-capture))
|
||
:config
|
||
(setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
|
||
(org-roam-db-autosync-mode))
|
||
|
||
(use-package org-modern
|
||
:hook org-mode-hook)
|
||
|
||
(use-package verb
|
||
:after org
|
||
:bind-keymap
|
||
(:map org-mode-map
|
||
("C-c C-r" . verb-command-map)))
|
||
#+END_SRC
|
||
|
||
** Terminal
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
eshell-vterm
|
||
vterm
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package em-term
|
||
:config
|
||
(push "hx" eshell-visual-commands))
|
||
|
||
(use-package vterm
|
||
:commands vterm)
|
||
|
||
(use-package eshell-vterm
|
||
:after eshell
|
||
:config
|
||
(eshell-vterm-mode))
|
||
#+END_SRC
|
||
|
||
** Slack
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
slack
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package lui
|
||
:defer t
|
||
:custom
|
||
(lui-time-stamp-format "[%Y-%m-%d %H:%M]"))
|
||
|
||
(use-package slack
|
||
:commands
|
||
slack-start
|
||
:custom
|
||
(slack-render-profile-images-p nil)
|
||
(slack-buffer-function #'switch-to-buffer()))
|
||
#+END_SRC
|
||
|
||
** Kubernetes
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
kubed
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package kubed
|
||
:bind-keymap
|
||
("s-k" . kubed-prefix-map))
|
||
#+END_SRC
|
||
|
||
** Ledger
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
ledger-mode
|
||
#+END_SRC
|
||
|
||
** Age
|
||
|
||
#+BEGIN_SRC nix :noweb-ref emacs_packages
|
||
age
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref emacs_config
|
||
(use-package age
|
||
:custom
|
||
(age-default-identity "~/source/dotfiles/keys/yubi1age.txt")
|
||
(age-default-recipient "~/source/dotfiles/keys/myself.txt")
|
||
:config
|
||
(setenv "PINENTRY_PROGRAM" "pinentry-mac")
|
||
(age-file-enable))
|
||
#+END_SRC
|
||
|
||
* Theme
|
||
|
||
I like Catppuccin Frappe as my theme. Let's use it in as many places as we can.
|
||
|
||
** Bat
|
||
|
||
#+BEGIN_SRC nix :noweb-ref programs
|
||
bat.config.theme = "catppuccin-frappe";
|
||
bat.themes = {
|
||
catppuccin-frappe = {
|
||
src = pkgs.fetchFromGitHub {
|
||
owner = "catppuccin";
|
||
repo = "sublime-text"; # Bat uses sublime syntax for its themes
|
||
rev = "3d8625d937d89869476e94bc100192aa220ce44a";
|
||
sha256 = "3ABUsfJpb6RO6AiuuSL5gwDofJIwC5tlEMzBrlY9/s0=";
|
||
};
|
||
file = "Frappe.tmTheme";
|
||
};
|
||
};
|
||
#+END_SRC
|
||
|
||
** Helix
|
||
|
||
#+BEGIN_SRC nix :noweb-ref programs
|
||
helix.settings.theme = "catppuccin_frappe";
|
||
#+END_SRC
|
||
|
||
** Tmux
|
||
|
||
#+BEGIN_SRC nix :noweb-ref programs
|
||
tmux.plugins = [
|
||
{
|
||
plugin = pkgs.tmuxPlugins.catppuccin;
|
||
extraConfig = ''set -g @catppuccin_window_tabs_enabled on
|
||
set -g @catppuccin_window_default_text "#W"
|
||
set -g @catppuccin_window_current_text "#W"'';
|
||
}
|
||
];
|
||
#+END_SRC
|
||
|
||
** Lazygit
|
||
|
||
#+BEGIN_SRC nix :noweb-ref extra_xdg
|
||
configFile."lazygit/config.yml" = {
|
||
enable = true;
|
||
source = pkgs.fetchFromGitHub
|
||
{
|
||
owner = "catppuccin";
|
||
repo = "lazygit";
|
||
rev = "b2ecb6d41b6f54a82104879573c538e8bdaeb0bf"; # Replace with the latest commit hash
|
||
sha256 = "9BBmWRcjNaJE9T0RKVEJaSnkrbMom0CLYE8PzAT6yFw=";
|
||
} + /themes/frappe.yml;
|
||
};
|
||
#+END_SRC
|
||
|
||
** Starship
|
||
|
||
#+NAME: starship_colors
|
||
#+BEGIN_SRC nix
|
||
builtins.fromTOML (builtins.readFile
|
||
(pkgs.fetchFromGitHub
|
||
{
|
||
owner = "catppuccin";
|
||
repo = "starship";
|
||
rev = "3e3e54410c3189053f4da7a7043261361a1ed1bc"; # Replace with the latest commit hash
|
||
sha256 = "soEBVlq3ULeiZFAdQYMRFuswIIhI9bclIU8WXjxd7oY=";
|
||
} + /palettes/frappe.toml))
|
||
#+END_SRC
|
||
|
||
** Wezterm
|
||
|
||
#+NAME: wezterm_colors
|
||
#+BEGIN_SRC lua
|
||
config.color_scheme = 'Catppuccin Frappe'
|
||
#+END_SRC
|
||
|
||
** Emacs
|
||
|
||
#+BEGIN_SRC nix :noweb-ref "emacs_packages"
|
||
catppuccin-theme
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC elisp :noweb-ref "emacs_config"
|
||
(use-package catppuccin-theme
|
||
:custom
|
||
(catppuccin-flavor 'frappe)
|
||
:config
|
||
(load-theme 'catppuccin :no-confirm))
|
||
|
||
;; (set-face-attribute 'default nil :font "JetBrainsMono Nerd Font" :height 130)
|
||
;; (set-face-attribute 'fixed-pitch nil :font "JetBrainsMono Nerd Font" :height 130)
|
||
(set-face-attribute 'default nil :font "Iosevka" :height 130 :width 'expanded)
|
||
(set-face-attribute 'fixed-pitch nil :font "Iosevka" :height 130 :width 'expanded)
|
||
(set-face-attribute 'variable-pitch nil :font "Iosevka Etoile" :height 130)
|
||
#+END_SRC
|