home/xmonad: Choose projects plugin #5

Merged
pleshevskiy merged 3 commits from xmonad_projects into main 2022-09-12 00:07:55 +03:00

View file

@ -8,9 +8,11 @@
-- --
import qualified Codec.Binary.UTF8.String as UTF8 import qualified Codec.Binary.UTF8.String as UTF8
import Control.Monad (replicateM_, sequence_)
import qualified DBus as D import qualified DBus as D
import qualified DBus.Client as D import qualified DBus.Client as D
import qualified Data.Map as M import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Monoid import Data.Monoid
import System.Exit import System.Exit
import XMonad import XMonad
@ -38,8 +40,10 @@ import XMonad.Layout.LimitWindows (limitWindows)
import XMonad.Layout.NoBorders (smartBorders) import XMonad.Layout.NoBorders (smartBorders)
import XMonad.Layout.Spacing (spacing) import XMonad.Layout.Spacing (spacing)
import XMonad.Layout.ThreeColumns (ThreeCol (..)) import XMonad.Layout.ThreeColumns (ThreeCol (..))
import XMonad.Prompt (XPConfig, XPType (..), XPrompt (..), mkComplFunFromList', mkXPromptWithModes)
import qualified XMonad.StackSet as W import qualified XMonad.StackSet as W
import XMonad.Util.EZConfig import XMonad.Util.EZConfig
import qualified XMonad.Util.ExtensibleState as XS
import XMonad.Util.Run import XMonad.Util.Run
-- The preferred terminal program, which is used in a binding below and by -- The preferred terminal program, which is used in a binding below and by
@ -101,7 +105,7 @@ main = mkDbusClient >>= main'
main' :: D.Client -> IO () main' :: D.Client -> IO ()
main' dbus = main' dbus =
xmonad . docks . ewmhFullscreen . ewmh $ xmonad . docks . ewmhFullscreen . ewmh . dynProjects $
def def
{ -- simple stuff { -- simple stuff
terminal = myTerminal, terminal = myTerminal,
@ -122,6 +126,8 @@ main' dbus =
logHook = myPolybarLogHook dbus, logHook = myPolybarLogHook dbus,
startupHook = myStartupHook startupHook = myStartupHook
} }
where
dynProjects = dynamicProjects myProjects
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Polybar settings (needs DBus client). -- Polybar settings (needs DBus client).
@ -191,7 +197,9 @@ myLayout = avoidStruts . smartBorders $ (Mirror tiled ||| tiled ||| column3 |||
-- per-workspace layout choices. -- per-workspace layout choices.
-- --
-- By default, do nothing. -- By default, do nothing.
myStartupHook = return () myStartupHook = do
spawn "kotatogram-desktop"
spawn "nheko"
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Status bars and logging -- Status bars and logging
@ -249,99 +257,101 @@ myManageHook = manageApps
-- Key bindings. Add, modify or remove key bindings here. -- Key bindings. Add, modify or remove key bindings here.
-- --
myKeys conf = myKeys conf =
let easyMotionConfig = mkKeymap conf $
def apps_kb ++ workspaces_kb ++ windows_kb ++ layout_kb ++ system_kb ++ misc_kb
{ sKeys = AnyKeys [xK_a, xK_o, xK_e, xK_u, xK_h, xK_t, xK_n, xK_s] where
} apps_kb =
[ -- launch a terminal
("M-S-<Return>", spawn $ XMonad.terminal conf),
-- launch a 'flameshot' to screenshot
("M-S-s", safeSpawn "flameshot" ["gui"]),
-- launch 'librewolf' browser
("M-S-b", spawn "librewolf"),
-- launch 'dmenu_run' to choose applications
("M-p", spawn "dmenu_run")
-- Open calculator
-- ("<XF86Calculator>", spawn "gnome-calculator"),
]
apps_kb = workspaces_kb =
[ -- launch a terminal --
("M-S-<Return>", spawn $ XMonad.terminal conf), -- mod-[1..9], Switch to workspace N
-- launch a 'flameshot' to screenshot -- mod-shift-[1..9], Move client to workspace N
("M-S-s", safeSpawn "flameshot" ["gui"]), --
-- launch 'librewolf' browser [ ("M-" ++ m ++ show k, windows $ f i)
("M-S-b", spawn "librewolf"), | (i, k) <- zip (XMonad.workspaces conf) [1 .. 9],
-- launch 'dmenu_run' to choose applications (f, m) <- [(W.greedyView, ""), (W.shift, "S-")]
("M-p", spawn "dmenu_run") ]
-- Open calculator
-- ("<XF86Calculator>", spawn "gnome-calculator"),
]
workspaces_kb = windows_kb =
[ -- close focused window
("M4-S-c", kill),
-- Resize viewed windows to the correct size
("M-n", refresh),
-- Easy moution to focus windows
("M-s", selectWindow easyMotionConfig >>= (`whenJust` windows . W.focusWindow)),
-- Move focus to the next window
("M-j", windows W.focusDown),
-- Move focus to the previous window
("M-k", windows W.focusUp),
-- Move focus to the master window
("M-m", windows W.focusMaster),
-- Swap the focused window and the master window
("M-<Return>", windows W.swapMaster),
-- Swap the focused window with the next window
("M-S-j", windows W.swapDown),
-- Swap the focused window with the previous window
("M-S-k", windows W.swapUp)
]
where
easyMotionConfig =
def
{ sKeys = AnyKeys [xK_a, xK_o, xK_e, xK_u, xK_h, xK_t, xK_n, xK_s]
}
layout_kb =
[ -- Rotate through the available layout algorithms
("M-<Space>", cycleThroughLayouts ["Full", "Mirror Spacing Tall"]),
("M-<Tab>", cycleThroughLayouts ["Spacing ThreeCol", "Spacing Tall", "Mirror Spacing Tall"]),
-- Reset the layouts on the current workspace to default
("M-S-<Space>", setLayout $ XMonad.layoutHook conf),
-- Shrink the master area
("M-h", sendMessage Shrink),
-- Expand the master area
("M-l", sendMessage Expand),
-- Push window back into tiling
("M-t", withFocused $ windows . W.sink),
-- Increment the number of windows in the master area
("M-,", sendMessage $ IncMasterN 1),
-- Deincrement the number of windows in the master area
("M-.", sendMessage $ IncMasterN (-1))
-- Toggle the status bar gap
-- Use this binding with avoidStruts from Hooks.ManageDocks.
-- See also the statusBar function from Hooks.DynamicLog.
-- --
-- mod-[1..9], Switch to workspace N -- , ("M-b", sendMessage ToggleStruts)
-- mod-shift-[1..9], Move client to workspace N ]
--
[ ("M-" ++ m ++ show k, windows $ f i)
| (i, k) <- zip (XMonad.workspaces conf) [1 .. 9],
(f, m) <- [(W.greedyView, ""), (W.shift, "S-")]
]
windows_kb = system_kb =
[ -- close focused window [ -- Lock screen
("M4-S-c", kill), ("M4-l", spawn "betterlockscreen --lock --display 1 -- -e"),
-- Resize viewed windows to the correct size -- Quit xmonad
("M-n", refresh), ("M4-S-q", io exitSuccess)
-- Easy moution to focus windows ]
("M-s", selectWindow easyMotionConfig >>= (`whenJust` windows . W.focusWindow)),
-- Move focus to the next window
("M-j", windows W.focusDown),
-- Move focus to the previous window
("M-k", windows W.focusUp),
-- Move focus to the master window
("M-m", windows W.focusMaster),
-- Swap the focused window and the master window
("M-<Return>", windows W.swapMaster),
-- Swap the focused window with the next window
("M-S-j", windows W.swapDown),
-- Swap the focused window with the previous window
("M-S-k", windows W.swapUp)
]
layout_kb = misc_kb =
[ -- Rotate through the available layout algorithms [ ("M-o", projectPrompt def),
("M-<Space>", cycleThroughLayouts ["Full", "Mirror Spacing Tall"]), -- Change volume
("M-<Tab>", cycleThroughLayouts ["Spacing ThreeCol", "Spacing Tall", "Mirror Spacing Tall"]), ("<XF86AudioMute>", spawn "amixer -q sset Master toggle"),
-- Reset the layouts on the current workspace to default ("<XF86AudioRaiseVolume>", spawn "amixer -q sset Master 5%+"),
("M-S-<Space>", setLayout $ XMonad.layoutHook conf), ("<XF86AudioLowerVolume>", spawn "amixer -q sset Master 5%-")
-- Shrink the master area ]
("M-h", sendMessage Shrink),
-- Expand the master area
("M-l", sendMessage Expand),
-- Push window back into tiling
("M-t", withFocused $ windows . W.sink),
-- Increment the number of windows in the master area
("M-,", sendMessage $ IncMasterN 1),
-- Deincrement the number of windows in the master area
("M-.", sendMessage $ IncMasterN (-1))
-- Toggle the status bar gap
-- Use this binding with avoidStruts from Hooks.ManageDocks.
-- See also the statusBar function from Hooks.DynamicLog.
--
-- , ("M-b", sendMessage ToggleStruts)
]
system_kb =
[ -- Lock screen
("M4-l", spawn "betterlockscreen --lock --display 1 -- -e"),
-- Quit xmonad
("M4-S-q", io exitSuccess)
]
misc_kb =
[ -- Change volume
("<XF86AudioMute>", spawn "amixer -q sset Master toggle"),
("<XF86AudioRaiseVolume>", spawn "amixer -q sset Master 5%+"),
("<XF86AudioLowerVolume>", spawn "amixer -q sset Master 5%-")
]
in mkKeymap conf $
apps_kb ++ workspaces_kb ++ windows_kb ++ layout_kb ++ system_kb ++ misc_kb
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Mouse bindings: default actions bound to mouse events -- Mouse bindings: default actions bound to mouse events
-- --
myMouseBindings (XConfig {XMonad.modMask = modm}) = myMouseBindings (XConfig {XMonad.modMask = modm}) =
M.fromList Map.fromList
-- mod-button1, Set the window to floating mode and move by dragging -- mod-button1, Set the window to floating mode and move by dragging
[ ((modm, button1), \w -> focus w >> mouseMoveWindow w >> windows W.shiftMaster), [ ((modm, button1), \w -> focus w >> mouseMoveWindow w >> windows W.shiftMaster),
-- mod-button2, Raise the window to the top of the stack -- mod-button2, Raise the window to the top of the stack
@ -350,3 +360,115 @@ myMouseBindings (XConfig {XMonad.modMask = modm}) =
((modm, button3), \w -> focus w >> mouseResizeWindow w >> windows W.shiftMaster) ((modm, button3), \w -> focus w >> mouseResizeWindow w >> windows W.shiftMaster)
-- you may also bind events to the mouse scroll wheel (button4 and button5) -- you may also bind events to the mouse scroll wheel (button4 and button5)
] ]
myProjects :: [Project]
myProjects =
[ -- my work
Project
{ projectName = "bm-back",
projectStartHook = do
let workdir = "~/projects/binarymanagement/bm-back"
spawn $ terminal' workdir $ Just "docker-compose -f docker-compose.dev.yml up -d"
replicateM_ 3 $ spawn $ terminal workdir
},
Project
{ projectName = "bm-front",
projectStartHook = do
let workdir = "~/projects/binarymanagement/bm-front"
replicateM_ 3 $ spawn $ terminal workdir
},
-- personal
Project
{ projectName = "myconfig",
projectStartHook = replicateM_ 2 $ spawn $ terminal "~/repos/myconfig"
},
Project
{ projectName = "tas",
projectStartHook = replicateM_ 2 $ spawn $ terminal "~/repos/tas"
},
-- community
Project
{ projectName = "dexios",
projectStartHook = replicateM_ 2 $ spawn $ terminal "~/repos/dexios"
},
Project
{ projectName = "home-manager (nix)",
projectStartHook = replicateM_ 2 $ spawn $ terminal "~/repos/home-manager"
}
]
where
terminal :: String -> String
terminal wd = terminal' wd Nothing
terminal' :: String -> Maybe String -> String
terminal' wd' cmd' =
"alacritty" ++ workdir ++ command
where
workdir = " --working-directory " ++ wd'
command = case cmd' of
Just c -> " --command " ++ c
_ -> ""
------------------------------------------------------------------------
-- Project
type ProjectName = String
data Project = Project
{ projectName :: !ProjectName,
projectStartHook :: !(X ())
}
type ProjectTable = Map ProjectName Project
data ProjectState = ProjectState {projects :: !ProjectTable}
instance ExtensionClass ProjectState where
initialValue = ProjectState Map.empty
data ProjectPrompt = ProjectPrompt XPConfig [ProjectName]
dynamicProjects :: [Project] -> XConfig a -> XConfig a
dynamicProjects ps c =
c {startupHook = dynamicProjectsStartupHook ps <> startupHook c}
dynamicProjectsStartupHook :: [Project] -> X ()
dynamicProjectsStartupHook ps = XS.modify go
where
go :: ProjectState -> ProjectState
go s = s {projects = update $ projects s}
update :: ProjectTable -> ProjectTable
update = Map.union (Map.fromList $ map entry ps)
entry :: Project -> (ProjectName, Project)
entry p = (projectName p, p)
instance XPrompt ProjectPrompt where
showXPrompt (ProjectPrompt _ _) = "Choose Project: "
completionFunction (ProjectPrompt c ns) = mkComplFunFromList' c ns
modeAction (ProjectPrompt _ _) buf auto = do
let name = if null auto then buf else auto
ps <- XS.gets projects
case Map.lookup name ps of
Just p -> openProject p
Nothing -> return ()
openProject :: Project -> X ()
openProject p = do
-- kill all windows in the current workspace
wins <- gets (W.integrate' . W.stack . W.workspace . W.current . windowset)
sequence_ $ map killWindow wins
projectStartHook p
projectPrompt :: XPConfig -> X ()
projectPrompt c = do
ps <- XS.gets projects
let names = Map.keys ps
modes = [XPT $ ProjectPrompt c names]
mkXPromptWithModes modes c