cornelis-0.2.0.0: src/Cornelis/InfoWin.hs
{-# LANGUAGE OverloadedStrings #-}
module Cornelis.InfoWin (closeInfoWindows, showInfoWindow, buildInfoBuffer) where
import Prelude hiding (Left, Right)
import Control.Monad (unless)
import Control.Monad.State.Class
import Cornelis.Pretty
import Cornelis.Types
import Cornelis.Utils (withBufferStuff, windowsForBuffer, savingCurrentWindow, visibleBuffers)
import Data.Foldable (for_)
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Text as T
import Data.Traversable (for)
import qualified Data.Vector as V
import Neovim
import Neovim.API.Text
import Prettyprinter (layoutPretty, LayoutOptions (LayoutOptions), PageWidth (AvailablePerLine))
import Prettyprinter.Render.Text (renderStrict)
cornelisWindowVar :: Text
cornelisWindowVar = "cornelis_window"
getBufferVariableOfWindow :: NvimObject o => Text -> Window -> Neovim env (Maybe o)
getBufferVariableOfWindow variableName window = do
window_is_valid window >>= \case
False -> pure Nothing
True -> do
buffer <- window_get_buffer window
let getVariableValue = Just . fromObjectUnsafe <$> buffer_get_var buffer variableName
getVariableValue `catchNeovimException` const (pure Nothing)
closeInfoWindowsForUnseenBuffers :: Neovim CornelisEnv ()
closeInfoWindowsForUnseenBuffers = do
seen <- S.fromList . fmap snd <$> visibleBuffers
bufs <- gets cs_buffers
let known = M.keysSet bufs
unseen = known S.\\ seen
for_ unseen $ \b -> do
for_ (M.lookup b bufs) $ \bs -> do
ws <- windowsForBuffer $ iw_buffer $ bs_info_win bs
for_ ws $ flip nvim_win_close True
closeInfoWindows :: Neovim env ()
closeInfoWindows = do
ws <- vim_get_windows
for_ ws $ \w -> getBufferVariableOfWindow cornelisWindowVar w >>= \case
Just True -> nvim_win_close w True
_ -> pure ()
closeInfoWindowForBuffer :: BufferStuff -> Neovim CornelisEnv ()
closeInfoWindowForBuffer bs = do
let InfoBuffer ib = bs_info_win bs
ws <- windowsForBuffer ib
for_ ws $ flip nvim_win_close True
showInfoWindow :: Buffer -> Doc HighlightGroup -> Neovim CornelisEnv ()
showInfoWindow b doc = withBufferStuff b $ \bs -> do
let ib = bs_info_win bs
closeInfoWindowsForUnseenBuffers
vis <- visibleBuffers
ns <- asks ce_namespace
-- Check if the info win still exists, and if so, just modify it
found <- fmap or $
for vis $ \(w, vb) -> do
case vb == iw_buffer ib of
False -> pure False
True -> do
writeInfoBuffer ns ib doc
resizeInfoWin w ib
pure True
-- Otherwise we need to rebuild it
unless found $ do
closeInfoWindowForBuffer bs
writeInfoBuffer ns ib doc
ws <- windowsForBuffer b
for_ ws $ buildInfoWindow ib
buildInfoBuffer :: Neovim env InfoBuffer
buildInfoBuffer = do
b <-
-- not listed in the buffer list, is throwaway
nvim_create_buf False True
-- Setup things in the buffer
void $ buffer_set_var b cornelisWindowVar $ ObjectBool True
nvim_buf_set_option b "modifiable" $ ObjectBool False
nvim_buf_set_option b "filetype" $ ObjectString "agdainfo"
pure $ InfoBuffer b
buildInfoWindow :: InfoBuffer -> Window -> Neovim CornelisEnv Window
buildInfoWindow (InfoBuffer split_buf) w = savingCurrentWindow $ do
nvim_set_current_win w
max_height <- asks $ T.pack . show . cc_max_height . ce_config
max_width <- asks $ T.pack . show . cc_max_width . ce_config
asks (cc_split_location . ce_config) >>= \case
Vertical -> vim_command $ max_width <> " vsplit"
Horizontal -> vim_command $ max_height <> " split"
OnLeft -> vim_command $ "topleft " <> max_width <> " vsplit"
OnRight -> vim_command $ "botright " <> max_width <> " vsplit"
OnTop -> vim_command $ "topleft " <> max_height <> " split"
OnBottom -> vim_command $ "botright " <> max_height <> " split"
split_win <- nvim_get_current_win
nvim_win_set_buf split_win split_buf
-- Setup things in the window
nvim_win_set_option split_win "relativenumber" $ ObjectBool False
nvim_win_set_option split_win "number" $ ObjectBool False
resizeInfoWin split_win (InfoBuffer split_buf)
pure split_win
resizeInfoWin :: Window -> InfoBuffer -> Neovim CornelisEnv ()
resizeInfoWin w ib = do
t <- nvim_buf_get_lines (iw_buffer ib) 0 (-1) False
max_size <- asks $ cc_max_height . ce_config
let size = min max_size $ fromIntegral $ V.length t
asks (cc_split_location . ce_config) >>= \case
Vertical -> pure ()
Horizontal -> window_set_height w size
OnLeft -> pure ()
OnRight -> pure ()
OnTop -> window_set_height w size
OnBottom -> window_set_height w size
writeInfoBuffer :: Int64 -> InfoBuffer -> Doc HighlightGroup -> Neovim env ()
writeInfoBuffer ns iw doc = do
-- TODO(sandy): Bad choice for a window, but good enough?
w <- nvim_get_current_win
width <- window_get_width w
let sds = layoutPretty (LayoutOptions (AvailablePerLine (fromIntegral width) 0.8)) doc
(hls, sds') = renderWithHlGroups sds
s = T.lines $ renderStrict sds'
let b = iw_buffer iw
nvim_buf_set_option b "modifiable" $ ObjectBool True
buffer_set_lines b 0 (-1) True $ V.fromList s
for_ (concatMap spanInfoHighlights hls) $ \(InfoHighlight (l, sc) ec hg) ->
nvim_buf_add_highlight
b ns
(T.pack $ show hg)
l sc ec
nvim_buf_set_option b "modifiable" $ ObjectBool False