{-# START_FILE {{name}}.cabal #-}
name:                {{name}}
version:             0.1.0.0
synopsis:            Haskell Neovim plugins
description:         Personal project to manage plugin dependencies.
author:              {{author-name}}{{^author-name}}Author name here{{/author-name}}
maintainer:          {{author-email}}{{^author-email}}example@example.com{{/author-email}}
copyright:           {{copyright}}{{^copyright}}{{year}}{{^year}}2019{{/year}} {{author-name}}{{^author-name}}Author name here{{/author-name}}{{/copyright}}
-- Take a license that you like. Only relevant if you want to make your config
-- used by other people.
--license:             BSD3
--license-file:        LICENSE
category:            Neovim
build-type:          Simple
--extra-source-files:  README.md
cabal-version:       >=1.10

executable my-nvim-hs
  main-is:             nvim.hs
  hs-source-dirs:      ., nvim-hs
  other-modules:       Neovim.Example.Plugin
                     , Neovim.Example.Plugin.Fibonacci
                     , Neovim.Example.Plugin.Random
  build-depends:       base >= 4.7 && < 5
                     , nvim-hs >= 2 && < 3
                     -- The dependencies below are only needed for the example plugin
                     , random
                     , unliftio
  default-language:    Haskell2010

{-# START_FILE nvim-hs/Neovim/Example/Plugin.hs #-}
{-# LANGUAGE TemplateHaskell #-}
-- Template Haskell is used to remove a lot of manual boiler-plate from
-- declaring the functions you want to export.
module Neovim.Example.Plugin
    ( plugin
    ) where

import Neovim

import Neovim.Example.Plugin.Random (nextRandom, setNextRandom, randomNumbers)
import Neovim.Example.Plugin.Fibonacci (fibonacci)

plugin :: Neovim () NeovimPlugin
plugin = do
    randomPluginState <- randomNumbers
    wrapPlugin Plugin
        { environment = randomPluginState
        , exports =
            [ $(function' 'fibonacci) Sync
            -- Notice the quotation mark before the functin name, this is
            -- important!

            , $(function' 'nextRandom) Sync
            , $(function "SetNextRandom" 'setNextRandom) Async
            ]
        }

{-# START_FILE nvim-hs/Neovim/Example/Plugin/Random.hs #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TemplateHaskell #-}
module Neovim.Example.Plugin.Random
    ( nextRandom
    , setNextRandom
    , randomNumbers
    ) where

import Neovim
import System.Random (newStdGen, randoms)
import UnliftIO.STM  (TVar, atomically, readTVar, modifyTVar, newTVarIO)

-- | This type alias encodes the type of your plugin's environment, namely
-- '(TVar [Int16)' in this case.
--
-- Since this plugin needs to store some state, we have to put it in a mutable
-- variable. I chose TVar here because I like the Software Transactional Memory
-- library.
type MyNeovim a = Neovim (TVar [Int16]) a

-- | This is the start up code. It initializes the random number generator and
-- returns a convenient list of random numbers. It returns the environment and
-- is executed in the startup code, so this is the only place where you can't
-- use the type alias defined above.
--
-- Neovim isn't so good with big numbers, so limit to 16 bits.
randomNumbers :: Neovim startupEnv (TVar [Int16])
randomNumbers = do
    g <- liftIO newStdGen -- Create a new seed for a pseudo random number generator
    newTVarIO (randoms g) -- Put an infinite list of random numbers into a TVar

-- | Get the next random number and update the state of the list.
nextRandom :: MyNeovim Int16
nextRandom = do
    tVarWithRandomNumbers <- ask
    atomically $ do
        -- pick the head of our list of random numbers
        r <- head <$> readTVar tVarWithRandomNumbers

        -- Since we do not want to return the same number all over the place
        -- remove the head of our list of random numbers
        modifyTVar tVarWithRandomNumbers tail

        return r


-- | You probably don't want this in a random number generator, but this shows
-- hoy you can edit the state of a stateful plugin.
setNextRandom :: Int16 -> MyNeovim ()
setNextRandom n = do
    tVarWithRandomNumbers <- ask

    -- cons n to the front of the infinite list
    atomically $ modifyTVar tVarWithRandomNumbers (n:)

{-# START_FILE nvim-hs/Neovim/Example/Plugin/Fibonacci.hs #-}
module Neovim.Example.Plugin.Fibonacci
    ( fibonacci
    ) where

import Neovim

-- | All fibonacci numbers.
fibonacciNumbers :: [Integer]
fibonacciNumbers = 0:fibs -- Since were using !! to index an element in a list, we need a 0 in front
    where fibs = 1:scanl1 (+) fibs

-- | Neovim is not really good with big numbers, so we return a 'String' here.
fibonacci :: Int -> Neovim env String
fibonacci n = return . show $ fibonacciNumbers !! n

{-# START_FILE nvim.hs #-}
import Neovim

import qualified Neovim.Example.Plugin as Example

main :: IO ()
main = do
    neovim defaultConfig
        { plugins = plugins defaultConfig ++ [ Example.plugin ]
        }

{-# START_FILE plugin/nvim-hs.vim #-}
call nvimhs#start(expand('<sfile>:p:h:h'), '{{{name}}}', [])