packages feed

static-ls-0.1.2: src/StaticLS/IDE/Workspace/Symbol.hs

{-# LANGUAGE RecordWildCards #-}

module StaticLS.IDE.Workspace.Symbol where

import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Trans.Maybe (MaybeT (..))
import Data.Maybe (catMaybes, fromMaybe)
import qualified Data.Text as T
import Development.IDE.GHC.Util (printOutputable)
import Development.IDE.Types.Location
import GHC.Plugins hiding ((<>))
import qualified HieDb
import Language.LSP.Protocol.Types
import StaticLS.HIE.File (hieFilePathToSrcFilePath)
import StaticLS.Maybe
import StaticLS.StaticEnv (HasStaticEnv, runHieDbMaybeT)

symbolInfo :: (HasCallStack, HasStaticEnv m, MonadIO m) => T.Text -> m [SymbolInformation]
symbolInfo query = do
    mHiedbDefs <- runMaybeT . runHieDbMaybeT $ \hieDb -> HieDb.searchDef hieDb (T.unpack query)
    let hiedbDefs = fromMaybe [] mHiedbDefs
    symbols <- mapM defRowToSymbolInfo hiedbDefs
    pure (catMaybes symbols)

-- Copy from https://github.com/haskell/haskell-language-server/blob/c126332850d27abc8efa519f8437ff7ea28d4049/ghcide/src/Development/IDE/Spans/AtPoint.hs#L392
-- With following modification
-- a. instead of replying on `modInfoSrcFile` (which is only present when hiedb index with `--src-base-dir`)
--    we could find src file path from `hieFilePathToSrcFilePath`
defRowToSymbolInfo :: (HasStaticEnv m, MonadIO m) => HieDb.Res HieDb.DefRow -> m (Maybe SymbolInformation)
defRowToSymbolInfo (HieDb.DefRow{..} HieDb.:. _) = runMaybeT $ do
    do
        srcFile <- hieFilePathToSrcFilePath defSrc
        let file = toUri srcFile
            loc = Location file range
        kind <- toAlt mKind
        pure $
            SymbolInformation
                { _name = printOutputable defNameOcc
                , _kind = kind
                , _tags = Nothing
                , _containerName = Nothing
                , _deprecated = Nothing
                , _location = loc
                }
  where
    mKind
        | isVarOcc defNameOcc = Just SymbolKind_Variable
        | isDataOcc defNameOcc = Just SymbolKind_Constructor
        | isTcOcc defNameOcc = Just SymbolKind_Struct
        | otherwise = Nothing
    range = Range start end
    start = Position (fromIntegral $ defSLine - 1) (fromIntegral $ defSCol - 1)
    end = Position (fromIntegral $ defELine - 1) (fromIntegral $ defECol - 1)

toUri :: FilePath -> Uri
toUri = fromNormalizedUri . filePathToUri' . toNormalizedFilePath'