Skip to content

Commit 42649df

Browse files
committed
better support for include paths
1 parent 6e86f66 commit 42649df

File tree

13 files changed

+352
-37
lines changed

13 files changed

+352
-37
lines changed

agda-language-server.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ library
5656
Agda.Parser
5757
Agda.Position
5858
Agda.Syntax.Abstract.More
59+
Agda.TypeChecking.Monad.Options.More
5960
Control.Concurrent.SizedChan
6061
Indexer
6162
Indexer.Indexer
@@ -194,6 +195,7 @@ test-suite als-test
194195
Test.Model
195196
Test.ModelMonad
196197
Test.SrcLoc
198+
Test.Uri
197199
Test.WASM
198200
TestData
199201
Agda
@@ -205,6 +207,7 @@ test-suite als-test
205207
Agda.Parser
206208
Agda.Position
207209
Agda.Syntax.Abstract.More
210+
Agda.TypeChecking.Monad.Options.More
208211
Control.Concurrent.SizedChan
209212
Indexer
210213
Indexer.Indexer

src/Agda/Interaction/Imports/More.hs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import Agda.Interaction.FindFile (
2222
import Agda.Interaction.Imports (Source (..))
2323
import qualified Agda.Interaction.Imports as Imp
2424
import Agda.Interaction.Library (OptionsPragma (..), _libPragmas)
25+
import Agda.Interaction.Library.More ()
2526
import Agda.Syntax.Common (TopLevelModuleName')
2627
import qualified Agda.Syntax.Concrete as C
2728
import Agda.Syntax.Parser (
@@ -57,6 +58,7 @@ import Agda.Syntax.TopLevelModuleName (
5758
rawTopLevelModuleNameForModule,
5859
#endif
5960
)
61+
import Agda.Syntax.Common.Pretty (Pretty, pretty, text, prettyAssign, (<+>))
6062
import Agda.TypeChecking.Monad
6163
( Interface,
6264
TCM,
@@ -92,8 +94,6 @@ import Control.Monad.Error.Class (
9294
)
9395
#if MIN_VERSION_Agda(2,8,0)
9496
import Agda.Utils.Singleton (singleton)
95-
#else
96-
import Agda.Syntax.Common.Pretty (pretty)
9797
#endif
9898

9999
srcFilePath :: SourceFile -> TCM AbsolutePath
@@ -193,3 +193,11 @@ moduleName file parsedModule = Bench.billTo [Bench.ModuleName] $ do
193193
}
194194
else return raw
195195
#endif
196+
197+
-- Orphan instances
198+
199+
instance Pretty Imp.Source where
200+
pretty src =
201+
text "Source"
202+
<+> pretty (Imp.srcModuleName src)
203+
<+> pretty (Imp.srcProjectLibs src)

src/Agda/Interaction/Imports/Virtual.hs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import Agda.Syntax.Parser (moduleParser, parseFile)
1919
import Agda.Syntax.Position (mkRangeFile)
2020
import qualified Agda.TypeChecking.Monad as TCM
2121
import Agda.Utils.FileName (AbsolutePath)
22+
import Agda.Utils.Maybe (maybeToList)
2223
import Control.Monad.IO.Class (MonadIO)
2324
import Control.Monad.Trans (lift)
2425
import qualified Data.Strict as Strict
@@ -27,6 +28,8 @@ import qualified Language.LSP.Protocol.Types as LSP
2728
import Language.LSP.Protocol.Types.Uri.More (uriToPossiblyInvalidAbsolutePath)
2829
import qualified Language.LSP.Server as LSP
2930
import qualified Language.LSP.VFS as VFS
31+
import Server.Model.AgdaLib (agdaLibToFile)
32+
import Server.Model.Monad (MonadAgdaLib (askAgdaLib))
3033

3134
data VSourceFile = VSourceFile
3235
{ vSrcFileSrcFile :: SourceFile,
@@ -67,10 +70,10 @@ vSrcFromUri normUri file = do
6770
#endif
6871

6972
-- | Based on @parseSource@
70-
parseVSource :: (TCM.MonadTCM m) => VSourceFile -> m Imp.Source
71-
parseVSource vSrcFile = TCM.liftTCM $ do
73+
parseVSource :: (TCM.MonadTCM m, TCM.MonadTrace m, MonadAgdaLib m) => VSourceFile -> m Imp.Source
74+
parseVSource vSrcFile = do
7275
let sourceFile = vSrcFileSrcFile vSrcFile
73-
f <- vSrcFilePath vSrcFile
76+
f <- TCM.liftTCM $ vSrcFilePath vSrcFile
7477

7578
let rf0 = mkRangeFile f Nothing
7679
TCM.setCurrentRange (Imp.beginningOfFile rf0) $ do
@@ -79,15 +82,16 @@ parseVSource vSrcFile = TCM.liftTCM $ do
7982
let txt = Text.unpack sourceStrict
8083

8184
parsedModName0 <-
82-
Imp.moduleName f . fst . fst =<< do
83-
Imp.runPMDropWarnings $ parseFile moduleParser rf0 txt
85+
TCM.liftTCM $
86+
Imp.moduleName f . fst . fst =<< do
87+
Imp.runPMDropWarnings $ parseFile moduleParser rf0 txt
8488

8589
let rf = mkRangeFile f $ Just parsedModName0
86-
((parsedMod, attrs), fileType) <- Imp.runPM $ parseFile moduleParser rf txt
87-
parsedModName <- Imp.moduleName f parsedMod
90+
((parsedMod, attrs), fileType) <- TCM.liftTCM $ Imp.runPM $ parseFile moduleParser rf txt
91+
parsedModName <- TCM.liftTCM $ Imp.moduleName f parsedMod
8892

89-
-- TODO: handle libs properly
90-
let libs = []
93+
agdaLib <- askAgdaLib
94+
let libs = maybeToList $ agdaLibToFile (vSrcUri vSrcFile) agdaLib
9195

9296
return
9397
Imp.Source

src/Agda/Interaction/Library/More.hs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ module Agda.Interaction.Library.More
99
)
1010
where
1111

12-
import Agda.Interaction.Library (LibM)
13-
import Agda.Interaction.Library.Base (LibErrorIO)
12+
import Agda.Interaction.Library (LibM, AgdaLibFile)
13+
import Agda.Interaction.Library.Base (LibErrorIO, libName, libFile, libIncludes)
1414
import Agda.Utils.Either (maybeRight)
1515
import Agda.Utils.Null (Null (empty))
1616
import Control.Category ((>>>))
1717
import Control.Monad.Except (runExceptT)
1818
import Control.Monad.IO.Class (MonadIO, liftIO)
1919
import Control.Monad.State.Lazy (evalStateT)
2020
import Control.Monad.Writer.Lazy (runWriterT)
21+
import Agda.Syntax.Common.Pretty (Pretty, pretty, text, (<+>))
22+
import Agda.Utils.Lens ((^.))
2123

2224
#if MIN_VERSION_Agda(2,8,0)
2325
-- Unneeded in 2.8.0 due to API changes
@@ -37,3 +39,10 @@ tryRunLibM =
3739
>>> flip evalStateT empty
3840
>>> fmap (fst >>> maybeRight)
3941
>>> liftIO
42+
43+
instance Pretty AgdaLibFile where
44+
pretty agdaLibFile =
45+
text "AgdaLibFile"
46+
<+> (pretty $ agdaLibFile ^. libName)
47+
<+> (pretty $ agdaLibFile ^. libFile)
48+
<+> (pretty $ agdaLibFile ^. libIncludes)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
module Agda.TypeChecking.Monad.Options.More (setCommandLineOptionsByLib) where
2+
3+
import Agda.Interaction.Options (CommandLineOptions (..))
4+
import qualified Agda.Interaction.Options.Lenses as Lens
5+
import Agda.TypeChecking.Monad (MonadTCM)
6+
import qualified Agda.TypeChecking.Monad as TCM
7+
import Agda.TypeChecking.Monad.Benchmark (updateBenchmarkingStatus)
8+
import Agda.Utils.FileName (AbsolutePath, absolute)
9+
import Agda.Utils.Lens ((^.))
10+
import qualified Agda.Utils.List1 as List1
11+
import Control.Monad.IO.Class (liftIO)
12+
import Language.LSP.Protocol.Types.Uri.More (uriToPossiblyInvalidFilePath)
13+
import Server.Model.AgdaLib (AgdaLib, agdaLibDependencies, agdaLibIncludes)
14+
import Server.Model.Monad (MonadAgdaLib (askAgdaLib))
15+
import System.Directory (getCurrentDirectory)
16+
17+
setCommandLineOptionsByLib ::
18+
(MonadTCM m, MonadAgdaLib m) =>
19+
CommandLineOptions ->
20+
m ()
21+
setCommandLineOptionsByLib opts = do
22+
root <- liftIO (absolute =<< getCurrentDirectory)
23+
setCommandLineOptionsByLib' root opts
24+
25+
setCommandLineOptionsByLib' ::
26+
(MonadTCM m, MonadAgdaLib m) =>
27+
AbsolutePath ->
28+
CommandLineOptions ->
29+
m ()
30+
setCommandLineOptionsByLib' root opts = do
31+
incs <- case optAbsoluteIncludePaths opts of
32+
[] -> do
33+
opts' <- setLibraryPathsByLib opts
34+
let incs = optIncludePaths opts'
35+
TCM.liftTCM $ TCM.setIncludeDirs incs root
36+
List1.toList <$> TCM.getIncludeDirs
37+
incs -> return incs
38+
TCM.modifyTC $ Lens.setCommandLineOptions opts {optAbsoluteIncludePaths = incs}
39+
TCM.liftTCM $ TCM.setPragmaOptions (optPragmaOptions opts)
40+
TCM.liftTCM updateBenchmarkingStatus
41+
42+
setLibraryPathsByLib ::
43+
(MonadTCM m, MonadAgdaLib m) =>
44+
CommandLineOptions ->
45+
m CommandLineOptions
46+
setLibraryPathsByLib o = do
47+
agdaLib <- askAgdaLib
48+
return $ addDefaultLibrariesByLib agdaLib o
49+
50+
-- TODO: resolve dependency libs; see setLibraryIncludes in Agda
51+
52+
addDefaultLibrariesByLib :: AgdaLib -> CommandLineOptions -> CommandLineOptions
53+
addDefaultLibrariesByLib agdaLib o
54+
| not (null $ optLibraries o) || not (optUseLibs o) = o
55+
| otherwise = do
56+
let libs = agdaLib ^. agdaLibDependencies
57+
let incs = uriToPossiblyInvalidFilePath <$> agdaLib ^. agdaLibIncludes
58+
o {optIncludePaths = incs ++ optIncludePaths o, optLibraries = libs}

src/Indexer.hs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ where
1212
import Agda.Interaction.FindFile (srcFilePath)
1313
#endif
1414
import qualified Agda.Interaction.Imports as Imp
15+
import qualified Agda.Interaction.Imports.More as Imp
16+
import Agda.Syntax.Common.Pretty (prettyShow)
1517
import qualified Agda.Syntax.Concrete as C
1618
import Agda.Syntax.Translation.ConcreteToAbstract (ToAbstract (toAbstract), TopLevel (TopLevel), TopLevelInfo)
1719
import qualified Agda.TypeChecking.Monad as TCM
20+
import Agda.TypeChecking.Monad.Options.More (setCommandLineOptionsByLib)
1821
import qualified Data.Map as Map
1922
import Indexer.Indexer (indexAst)
2023
import Indexer.Monad (execIndexerM)
@@ -25,20 +28,24 @@ import Server.Model.Monad (WithAgdaLibM)
2528
usingSrcAsCurrent :: Imp.Source -> WithAgdaLibM a -> WithAgdaLibM a
2629
#if MIN_VERSION_Agda(2,8,0)
2730
usingSrcAsCurrent src x = do
28-
TCM.liftTCM $
29-
TCM.setCurrentRange (C.modPragmas . Imp.srcModule $ src) $
30-
-- Now reset the options
31-
TCM.setCommandLineOptions . TCM.stPersistentOptions . TCM.stPersistentState =<< TCM.getTC
31+
TCM.liftTCM $ Imp.setOptionsFromSourcePragmas True src
32+
33+
TCM.setCurrentRange (C.modPragmas . Imp.srcModule $ src) $
34+
-- Now reset the options
35+
setCommandLineOptionsByLib . TCM.stPersistentOptions . TCM.stPersistentState =<< TCM.getTC
3236

3337
TCM.modifyTCLens TCM.stModuleToSourceId $ Map.insert (Imp.srcModuleName src) (Imp.srcOrigin src)
3438

35-
TCM.localTC (\e -> e {TCM.envCurrentPath = Just (TCM.srcFileId $ Imp.srcOrigin src)}) x
39+
TCM.localTC (\e -> e {TCM.envCurrentPath = Just (TCM.srcFileId $ Imp.srcOrigin src)}) $ do
40+
TCM.liftTCM $ Imp.setOptionsFromSourcePragmas False src
41+
-- includeDirs <- TCM.getIncludeDirs
42+
-- _ <- TCM.liftTCM $ TCM.typeError $ TCM.InternalError $ prettyShow includeDirs
43+
x
3644
#else
3745
usingSrcAsCurrent src x = do
38-
TCM.liftTCM $
39-
TCM.setCurrentRange (C.modPragmas . Imp.srcModule $ src) $
40-
-- Now reset the options
41-
TCM.setCommandLineOptions . TCM.stPersistentOptions . TCM.stPersistentState =<< TCM.getTC
46+
TCM.setCurrentRange (C.modPragmas . Imp.srcModule $ src) $
47+
-- Now reset the options
48+
setCommandLineOptionsByLib . TCM.stPersistentOptions . TCM.stPersistentState =<< TCM.getTC
4249

4350
TCM.modifyTCLens TCM.stModuleToSource $ Map.insert (Imp.srcModuleName src) (srcFilePath $ Imp.srcOrigin src)
4451

src/Language/LSP/Protocol/Types/Uri/More.hs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
module Language.LSP.Protocol.Types.Uri.More
22
( getNormalizedUri,
33
isUriAncestorOf,
4+
uriHeightAbove,
45
uriToPossiblyInvalidAbsolutePath,
6+
uriToPossiblyInvalidFilePath,
57
)
68
where
79

@@ -24,6 +26,40 @@ isUriAncestorOf :: LSP.NormalizedUri -> LSP.NormalizedUri -> Bool
2426
isUriAncestorOf ancestor descendant =
2527
getNormalizedUri ancestor `Text.isPrefixOf` getNormalizedUri descendant
2628

29+
-- | If @ancestor@ is an ancestor of @descendant@, then
30+
-- @uriHeightAbove ancestor descendant@ is the height of @ancestor@ above
31+
-- @descendant@.
32+
--
33+
-- For example, the height of @https://example.com/a/@ over
34+
-- @https://example.com/a/b/c/@ is 2.
35+
--
36+
-- This is a heuristic implementation and may need replacement if the heuristic
37+
-- leads to bugs.
38+
uriHeightAbove :: LSP.NormalizedUri -> LSP.NormalizedUri -> Int
39+
uriHeightAbove ancestor descendant =
40+
let suffix = pathBetween (getNormalizedUri ancestor) (getNormalizedUri descendant)
41+
path = stripSlash $ stripScheme suffix
42+
in countPathComponents path
43+
where
44+
pathBetween :: Text -> Text -> Text
45+
pathBetween a b = case Text.commonPrefixes a b of
46+
Just (_prefix, _suffixA, suffixB) -> suffixB
47+
Nothing -> ""
48+
49+
stripScheme :: Text -> Text
50+
stripScheme uri =
51+
let (prefix, suffix) = Text.breakOn "//" uri
52+
in if Text.null suffix then prefix else suffix
53+
54+
stripSlash :: Text -> Text
55+
stripSlash = Text.dropAround (== '/')
56+
57+
countPathComponents :: Text -> Int
58+
countPathComponents path =
59+
if Text.null path
60+
then 0
61+
else Text.count "/" path + 1
62+
2763
uriToPossiblyInvalidAbsolutePath :: (MonadIO m) => LSP.NormalizedUri -> m AbsolutePath
2864
uriToPossiblyInvalidAbsolutePath uri = do
2965
case LSP.uriToFilePath $ LSP.fromNormalizedUri uri of
@@ -32,3 +68,11 @@ uriToPossiblyInvalidAbsolutePath uri = do
3268

3369
uriToInvalidAbsolutePath :: LSP.NormalizedUri -> AbsolutePath
3470
uriToInvalidAbsolutePath = AbsolutePath . getNormalizedUri
71+
72+
uriToPossiblyInvalidFilePath :: LSP.NormalizedUri -> FilePath
73+
uriToPossiblyInvalidFilePath uri = case LSP.uriToFilePath $ LSP.fromNormalizedUri uri of
74+
Just path -> path
75+
Nothing -> uriToInvalidFilePath uri
76+
77+
uriToInvalidFilePath :: LSP.NormalizedUri -> FilePath
78+
uriToInvalidFilePath = Text.unpack . getNormalizedUri

src/Server/Handler/TextDocument/FileManagement.hs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ where
88
import Agda.Interaction.FindFile (SourceFile (SourceFile))
99
import qualified Agda.Interaction.Imports.More as Imp
1010
import Agda.Interaction.Imports.Virtual (parseVSource, vSrcFromUri)
11+
import Agda.Syntax.Common.Pretty (prettyShow)
1112
import Agda.TypeChecking.Monad (MonadTCM (liftTCM))
1213
import Agda.Utils.Lens ((^.))
1314
import Control.Monad.Trans (lift)
1415
import Data.Strict (Strict (toLazy))
16+
import qualified Data.Text as Text
1517
import Indexer (indexFile)
1618
import qualified Language.LSP.Protocol.Lens as LSP
1719
import qualified Language.LSP.Protocol.Message as LSP
@@ -30,7 +32,8 @@ didOpenHandler = notificationHandlerWithAgdaLib LSP.SMethod_TextDocumentDidOpen
3032
Nothing -> return ()
3133
Just vfile -> do
3234
vSourceFile <- vSrcFromUri uri vfile
33-
src <- liftTCM $ parseVSource vSourceFile
35+
src <- parseVSource vSourceFile
36+
lift $ LSP.sendNotification LSP.SMethod_WindowLogMessage $ LSP.LogMessageParams LSP.MessageType_Info $ Text.pack $ prettyShow src
3437
agdaFile <- indexFile src
3538
lift $ modifyModel $ Model.setAgdaFile uri agdaFile
3639

@@ -45,6 +48,7 @@ didSaveHandler = notificationHandlerWithAgdaLib LSP.SMethod_TextDocumentDidSave
4548
Nothing -> return ()
4649
Just vfile -> do
4750
vSourceFile <- vSrcFromUri uri vfile
48-
src <- liftTCM $ parseVSource vSourceFile
51+
src <- parseVSource vSourceFile
52+
lift $ LSP.sendNotification LSP.SMethod_WindowLogMessage $ LSP.LogMessageParams LSP.MessageType_Info $ Text.pack $ prettyShow src
4953
agdaFile <- indexFile src
5054
lift $ modifyModel $ Model.setAgdaFile uri agdaFile

0 commit comments

Comments
 (0)