Выбор проектов в WezTerm

от
Soft    wezterm, config, terminal, projects

English version
Мне нравится WezTerm за то, что в нём используются lua-конфигурации, вместо скучных json/yaml/toml-файлов, а также в нём есть мощный и настраиваемый мультиплексор. В этой статье я покажу как создать переключатель проектов, который создаст именованное рабочее пространство, разделит окно и выполнит указанные команды.
wezterm.png

Мультиплексор
Начнем с базовой конфигурации, чтобы понять возможности мультиплексирования. Представьте, что после запуска терминала мы хотим получить готовое рабочее пространство с узкой панелью для spotify-player справа, очень маленькой панелью внизу и основной рабочей панелью с погодой.
  1. -- ~/.config/wezterm.lua
  2. local wezterm = require 'wezterm'
  3.  
  4. wezterm.on('gui-startup', function()
  5.   local _, pane_main, window = wezterm.mux.spawn_window{
  6.     workspace = 'main',
  7.     cwd = '~/dev'
  8.   }
  9.   local pane_spoti = pane_main:split {
  10.     direction = "Right",
  11.     size = 0.12,
  12.     cwd = '/tmp',
  13.   }
  14.   local pane_bottom = pane_main:split {
  15.     direction = "Bottom",
  16.     size = 0.05,
  17.   }
  18.  
  19.   window:gui_window():maximize()
  20.   pane_main:activate()
  21.   pane_main:send_text "curl wttr.in?2\ncd "
  22.   pane_spoti:send_text 'spotify_player\n'
  23.   pane_bottom:send_text "date\n"
  24. end)

Мы используем событие gui-startup для настройки запуска WezTerm, в котором создаём новое окно с одной панелью pane_main, именем рабочего пространства main и некоторой директорией ~/dev. Затем мы разделяем pane_main вправо с размером 0.12 и другой директорией. Эта панель имеет название pane_spoti. То же самое для pane_bottom.

Дальше всё просто: разворачиваем окно WezTerm на весь экран, активируем главную панель и выполняем несколько команд в каждой панели.

wezterm-mux.png

Мы только что настроили лишь одно рабочее пространство, но вы можете создать сколько угодно, если укажете в новом окне workspace = 'something'.


Проекты
В этот раз мы используем аналогичный подход к мультиплексированию, но, вместо события gui-startup, запустим диалог выбора проекта с помощью комбинации клавиш:
  1. -- ~/.config/wezterm.lua
  2. local wezterm = require 'wezterm'
  3. local projects = require 'projects'
  4.  
  5. -- .. config ..
  6.  
  7. -- leader = { mods="CTRL", key = '\\', timeout_milliseconds = 1200 },
  8. keys = {
  9.   -- projects
  10.   {mods="LEADER", key="p", action=projects.choose_project()},
  11.   {mods="CTRL|SHIFT", key="k", action=projects.choose_project()},
  12.   -- rest key bindings...
  13. }

Здесь я настроил одно и то же действие для двух разных привязок: комбинации клавиш и лидера. Используйте ту, которая вам больше нравится, или обе, как я.

В choose_project мы построим список проектов и будем использовать InputSelector с включенной опцией fuzzy:
  1. function list_projects()
  2.   -- список проектов
  3.   -- смотрите следующий блок кода
  4. end
  5.  
  6. function module.choose_project()
  7.   local choices = {}
  8.   for key, _ in pairs(list_projects()) do
  9.     table.insert(choices, { id = key, label = key })
  10.   end
  11.   table.sort(choices, function(a, b) return a.id < b.id end)
  12.   return wezterm.action.InputSelector {
  13.     title = 'Projects',
  14.     choices = choices,
  15.     fuzzy = true,
  16.     fuzzy_description = 'Enter a project name: ',
  17.     action = wezterm.action_callback(function(window, pane, id, label)
  18.       if not id then return end
  19.       local projects = list_projects()
  20.       if not projects[id] then return end
  21.       local project = projects[id]
  22.       return project(window, pane)
  23.     end)
  24.   }
  25. end
inputselector_simple.png

В list_projects мы укажем объект с именами проектов и функциями их инициализации. Для одной панели это будет single_project, а для других, более сложных конфигураций проектов, будет отдельная функция:
  1. -- ~/.config/projects.lua
  2. local wezterm = require 'wezterm'
  3. local utils = require 'utils'
  4. local module = {}
  5.  
  6. -- Creates a project with single workspace
  7. local function single_project(name, command)
  8.   return function(window, pane)
  9.     return utils.workspace_single_pane( window, pane, name, command)
  10.   end
  11. end
  12.  
  13. local function project_blog(window, pane)
  14.   local cwd = '/m/blog'
  15.   return utils.workspace_double_pane(
  16.     window, pane, "Left", "blog",
  17.     'cd ' .. cwd .. '/content\nyazi\n',
  18.     'cd ' .. cwd .. '\nzola serve'
  19.   )
  20. end
  21.  
  22. local function list_projects()
  23.   return {
  24.     -- projects
  25.     blog = project_blog,
  26.     -- apps
  27.     app_spoti = single_project('spotify', 'spoti\n'),
  28.     app_notes = single_project('notes', 'cd ~/notes/main\nmoar "./$(fzf)"\n'),
  29.     app_budget = single_project('budget', 'cd /opt/actual && docker-compose up'),
  30.     -- ssh zellij
  31.     ssh_anm = single_project('ssh anm', 'ssh anm -t "zellij a"\n'),
  32.     ssh_gm = single_project('ssh gm', 'ssh gm -t "zellij a main"\n'),
  33.     ssh_oracle = single_project('ssh oracle', 'ssh oracle -t "zellij a"\n'),
  34.   }
  35. end
  36.  
  37. -- function module.choose_project()
  38. --   смотрите предыдущий блок кода
  39. -- end
  40.  
  41. return module

Здесь я использую только одно- и двухпанельное рабочее пространство, но вы можете модифицировать этот пример и создать мощные проекты с динамическими проверками, установкой переменных окружения, использованием файлов .env, различных условий и т.д. Развлекайтесь! :)

Если проект нужно запускать часто, стоит подумать о том, чтобы запускать его напрямую по нажатию сочетания клавиш:
  1. -- ~/.config/projects.lua
  2. function module.blog()
  3.   return wezterm.action_callback(project_blog)
  4. end
  1. -- ~/.config/wezterm.lua
  2. keys = {
  3.   {mods="LEADER", key="b", action=projects.blog()},
  4.   -- остальные сочетания клавиш...
  5. }

Полная конфигурация: github.com/annimon-tutorials/wezterm-projects


Стилизация
Этот серый диалог выбора выглядит так скучно, давайте добавим ему немного цвета и иконок. Вы можете использовать wezterm.format, чтобы применить стили к fuzzy_description:
  1. function module.choose_project()
  2.   -- ...
  3.     fuzzy = true,
  4.     fuzzy_description = wezterm.format {
  5.       { Attribute = { Intensity = 'Bold' }},
  6.       { Foreground = { Color = '#aaffaa' }},
  7.       { Text = 'Enter a project name: '}
  8.     },
  9.   -- ...
  10. end

Вместо определения собственных цветов можно использовать AnsiColor с 16 значениями: Black, Maroon, Green, Olive, Navy, Purple, Teal, Silver, Grey, Red, Lime, Yellow, Blue, Fuchsia, Aqua или White.
  1. { Foreground = { AnsiColor = 'Lime' }},

Для использования иконок смотрите страницу wezterm.nerdfonts.

Список проектов необходимо немного изменить:
  1. local function list_projects()
  2.   return {
  3.     -- projects
  4.     blog = {
  5.       label = wezterm.format {
  6.         { Foreground = { AnsiColor = 'Fuchsia' }},
  7.         { Text = wezterm.nerdfonts.md_typewriter },
  8.         { Foreground = { AnsiColor = 'White' }},
  9.         { Text = ' Blog' },
  10.       },
  11.       action = project_blog
  12.     },
  13.     -- apps
  14.     app_spoti = {
  15.       label = wezterm.format {
  16.         { Foreground = { AnsiColor = 'Green' }},
  17.         { Text = wezterm.nerdfonts.md_spotify },
  18.         { Foreground = { AnsiColor = 'White' }},
  19.         { Text = ' Spoti' },
  20.       },
  21.       action = single_project('spotify', 'spoti\n')
  22.     },
  23.     -- other
  24.   }
  25. end

И функцию выбора проекта тоже:
  1. function module.choose_project()
  2.   local choices = {}
  3.   for key, value in pairs(list_projects()) do
  4.     table.insert(choices, { label = value.label, id = key })
  5.   end
  6.   table.sort(choices, function(a, b) return a.id < b.id end)
  7.   -- ...
  8.   action = wezterm.action_callback(function(window, pane, id, label)
  9.     -- ...
  10.     local project = projects[id].action
  11.     return project(window, pane)
  12.   end)
  13. end)

Вот что у меня получилось
inputselector.png

Полный конфиг: github.com/annimon-tutorials/wezterm-projects
  • +1
  • views 45