From dd7ada6f13d75dff283c791528ebffed610caa8b Mon Sep 17 00:00:00 2001 From: Abdelrahman Abounegm Date: Tue, 26 Dec 2023 21:43:56 +0300 Subject: [PATCH 1/3] Add type errors to the cache and check them when confirming if the file has changed --- rzk/src/Language/Rzk/VSCode/Env.hs | 9 ++++++--- rzk/src/Language/Rzk/VSCode/Handlers.hs | 19 +++++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/rzk/src/Language/Rzk/VSCode/Env.hs b/rzk/src/Language/Rzk/VSCode/Env.hs index 1305f78e7..820782805 100644 --- a/rzk/src/Language/Rzk/VSCode/Env.hs +++ b/rzk/src/Language/Rzk/VSCode/Env.hs @@ -3,10 +3,11 @@ module Language.Rzk.VSCode.Env where import Control.Concurrent.STM import Control.Monad.Reader import Language.LSP.Server +import Language.Rzk.Free.Syntax (VarIdent) import Language.Rzk.VSCode.Config (ServerConfig) -import Rzk.TypeCheck (Decl') +import Rzk.TypeCheck (Decl', TypeErrorInScopedContext) -type RzkTypecheckCache = [(FilePath, [Decl'])] +type RzkTypecheckCache = [(FilePath, [Decl'], [TypeErrorInScopedContext VarIdent])] data RzkEnv = RzkEnv { rzkEnvTypecheckCache :: TVar RzkTypecheckCache @@ -37,7 +38,9 @@ resetCacheForFiles :: [FilePath] -> LSP () resetCacheForFiles paths = lift $ do typecheckCache <- asks rzkEnvTypecheckCache liftIO $ atomically $ do - modifyTVar typecheckCache (takeWhile ((`notElem` paths) . fst)) + modifyTVar typecheckCache (takeWhile ((`notElem` paths) . fst3)) + where + fst3 (a,_,_) = a -- | Get the current state of the cache. getCachedTypecheckedModules :: LSP RzkTypecheckCache diff --git a/rzk/src/Language/Rzk/VSCode/Handlers.hs b/rzk/src/Language/Rzk/VSCode/Handlers.hs index 09695553b..f3382b2de 100644 --- a/rzk/src/Language/Rzk/VSCode/Handlers.hs +++ b/rzk/src/Language/Rzk/VSCode/Handlers.hs @@ -102,7 +102,8 @@ typecheckFromConfigFile = do rawPaths <- liftIO $ globDir (map compile (include config)) rootPath let paths = concatMap sort rawPaths - cachedModules <- getCachedTypecheckedModules + typecheckedCachedModules <- getCachedTypecheckedModules + let cachedModules = map (\(a,b,_) -> (a,b)) typecheckedCachedModules let cachedPaths = map fst cachedModules modifiedFiles = paths \\ cachedPaths @@ -126,7 +127,8 @@ typecheckFromConfigFile = do -- cache well-typed modules logInfo (show (length checkedModules) ++ " modules successfully typechecked") logInfo (show (length errors) ++ " errors found") - cacheTypecheckedModules checkedModules + let checkedModules' = map (\(path, decls) -> (path, decls, filter ((== path) . filepathOfTypeError) errors)) checkedModules + cacheTypecheckedModules checkedModules' return (errors, checkedModules) -- Reset all published diags @@ -202,8 +204,10 @@ provideCompletions req res = do logDebug ("Found " ++ show (length cachedModules) ++ " modules in the cache") let currentFile = fromMaybe "" $ uriToFilePath $ req ^. params . textDocument . uri -- Take all the modules up to and including the currently open one - let modules = takeWhileInc ((/= currentFile) . fst) cachedModules + let modules = map ignoreErrors $ takeWhileInc ((/= currentFile) . fst3) cachedModules where + fst3 (a,_,_) = a + ignoreErrors (a,b,_) = (a,b) takeWhileInc _ [] = [] takeWhileInc p (x:xs) | p x = x : takeWhileInc p xs @@ -287,13 +291,16 @@ data IsChanged -- | Detects if the given path has changes in its declaration compared to what's in the cache isChanged :: RzkTypecheckCache -> FilePath -> LSP IsChanged isChanged cache path = toIsChanged $ do - cachedDecls <- maybeToEitherLSP $ lookup path cache + let cacheWithoutDecls = map (\(p, _, e) -> (p, e)) cache + let cacheWithoutErrors = map (\(p, d, _) -> (p, d)) cache + errors <- maybeToEitherLSP $ lookup path cacheWithoutDecls + cachedDecls <- maybeToEitherLSP $ lookup path cacheWithoutErrors module' <- toExceptTLifted $ parseModuleFile path e <- toExceptTLifted $ try @SomeException $ evaluate $ - defaultTypeCheck (typecheckModulesWithLocationIncremental (takeWhile ((/= path) . fst) cache) [(path, module')]) + defaultTypeCheck (typecheckModulesWithLocationIncremental (takeWhile ((/= path) . fst) cacheWithoutErrors) [(path, module')]) (checkedModules, _errors) <- toExceptT $ return e decls' <- maybeToEitherLSP $ lookup path checkedModules - return $ if decls' == cachedDecls + return $ if null errors && decls' == cachedDecls then NotChanged else HasChanged where From 517cb35d191ab7ad6d880c7a25f2d945e3c971ce Mon Sep 17 00:00:00 2001 From: Nikolai Kudasov Date: Mon, 1 Apr 2024 11:45:28 +0300 Subject: [PATCH 2/3] Use proper record, not anonymous triple --- rzk/src/Language/Rzk/VSCode/Env.hs | 14 ++++++++------ rzk/src/Language/Rzk/VSCode/Handlers.hs | 17 ++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/rzk/src/Language/Rzk/VSCode/Env.hs b/rzk/src/Language/Rzk/VSCode/Env.hs index 820782805..f2328e33e 100644 --- a/rzk/src/Language/Rzk/VSCode/Env.hs +++ b/rzk/src/Language/Rzk/VSCode/Env.hs @@ -7,9 +7,14 @@ import Language.Rzk.Free.Syntax (VarIdent) import Language.Rzk.VSCode.Config (ServerConfig) import Rzk.TypeCheck (Decl', TypeErrorInScopedContext) -type RzkTypecheckCache = [(FilePath, [Decl'], [TypeErrorInScopedContext VarIdent])] +data RzkCachedModule = RzkCachedModule + { cachedModuleDecls :: [Decl'] + , cachedModuleErrors :: [TypeErrorInScopedContext VarIdent] + } + +type RzkTypecheckCache = [(FilePath, RzkCachedModule)] -data RzkEnv = RzkEnv +newtype RzkEnv = RzkEnv { rzkEnvTypecheckCache :: TVar RzkTypecheckCache } @@ -19,7 +24,6 @@ defaultRzkEnv = do return RzkEnv { rzkEnvTypecheckCache = typecheckCache } - type LSP = LspT ServerConfig (ReaderT RzkEnv IO) -- | Override the cache with given typechecked modules. @@ -38,9 +42,7 @@ resetCacheForFiles :: [FilePath] -> LSP () resetCacheForFiles paths = lift $ do typecheckCache <- asks rzkEnvTypecheckCache liftIO $ atomically $ do - modifyTVar typecheckCache (takeWhile ((`notElem` paths) . fst3)) - where - fst3 (a,_,_) = a + modifyTVar typecheckCache (takeWhile ((`notElem` paths) . fst)) -- | Get the current state of the cache. getCachedTypecheckedModules :: LSP RzkTypecheckCache diff --git a/rzk/src/Language/Rzk/VSCode/Handlers.hs b/rzk/src/Language/Rzk/VSCode/Handlers.hs index f3382b2de..0aa9ec186 100644 --- a/rzk/src/Language/Rzk/VSCode/Handlers.hs +++ b/rzk/src/Language/Rzk/VSCode/Handlers.hs @@ -4,6 +4,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -Wno-orphans #-} +{-# LANGUAGE RecordWildCards #-} module Language.Rzk.VSCode.Handlers ( typecheckFromConfigFile, @@ -103,7 +104,7 @@ typecheckFromConfigFile = do let paths = concatMap sort rawPaths typecheckedCachedModules <- getCachedTypecheckedModules - let cachedModules = map (\(a,b,_) -> (a,b)) typecheckedCachedModules + let cachedModules = map (\(path, RzkCachedModule{..}) -> (path, cachedModuleDecls)) typecheckedCachedModules let cachedPaths = map fst cachedModules modifiedFiles = paths \\ cachedPaths @@ -127,7 +128,7 @@ typecheckFromConfigFile = do -- cache well-typed modules logInfo (show (length checkedModules) ++ " modules successfully typechecked") logInfo (show (length errors) ++ " errors found") - let checkedModules' = map (\(path, decls) -> (path, decls, filter ((== path) . filepathOfTypeError) errors)) checkedModules + let checkedModules' = map (\(path, decls) -> (path, RzkCachedModule decls (filter ((== path) . filepathOfTypeError) errors))) checkedModules cacheTypecheckedModules checkedModules' return (errors, checkedModules) @@ -204,10 +205,9 @@ provideCompletions req res = do logDebug ("Found " ++ show (length cachedModules) ++ " modules in the cache") let currentFile = fromMaybe "" $ uriToFilePath $ req ^. params . textDocument . uri -- Take all the modules up to and including the currently open one - let modules = map ignoreErrors $ takeWhileInc ((/= currentFile) . fst3) cachedModules + let modules = map ignoreErrors $ takeWhileInc ((/= currentFile) . fst) cachedModules where - fst3 (a,_,_) = a - ignoreErrors (a,b,_) = (a,b) + ignoreErrors (path, RzkCachedModule{..}) = (path, cachedModuleDecls) takeWhileInc _ [] = [] takeWhileInc p (x:xs) | p x = x : takeWhileInc p xs @@ -291,10 +291,9 @@ data IsChanged -- | Detects if the given path has changes in its declaration compared to what's in the cache isChanged :: RzkTypecheckCache -> FilePath -> LSP IsChanged isChanged cache path = toIsChanged $ do - let cacheWithoutDecls = map (\(p, _, e) -> (p, e)) cache - let cacheWithoutErrors = map (\(p, d, _) -> (p, d)) cache - errors <- maybeToEitherLSP $ lookup path cacheWithoutDecls - cachedDecls <- maybeToEitherLSP $ lookup path cacheWithoutErrors + let cacheWithoutErrors = map (fmap cachedModuleDecls) cache + errors <- maybeToEitherLSP $ cachedModuleErrors <$> lookup path cache + cachedDecls <- maybeToEitherLSP $ cachedModuleDecls <$> lookup path cache module' <- toExceptTLifted $ parseModuleFile path e <- toExceptTLifted $ try @SomeException $ evaluate $ defaultTypeCheck (typecheckModulesWithLocationIncremental (takeWhile ((/= path) . fst) cacheWithoutErrors) [(path, module')]) From e6e47ea399d37a3cf9b99086ad683db8b7ca53a3 Mon Sep 17 00:00:00 2001 From: Nikolai Kudasov Date: Mon, 1 Apr 2024 12:08:44 +0300 Subject: [PATCH 3/3] Fix caching in presence of errors --- rzk/src/Language/Rzk/VSCode/Handlers.hs | 6 +++--- rzk/src/Rzk/TypeCheck.hs | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/rzk/src/Language/Rzk/VSCode/Handlers.hs b/rzk/src/Language/Rzk/VSCode/Handlers.hs index 0aa9ec186..49ecdf329 100644 --- a/rzk/src/Language/Rzk/VSCode/Handlers.hs +++ b/rzk/src/Language/Rzk/VSCode/Handlers.hs @@ -135,7 +135,7 @@ typecheckFromConfigFile = do -- Reset all published diags -- TODO: remove this after properly grouping by path below, after which there can be an empty list of errors -- TODO: handle clearing diagnostics for files that got removed from the project (rzk.yaml) - forM_ paths $ \path -> do + forM_ modifiedFiles $ \path -> do publishDiagnostics 0 (filePathToNormalizedUri path) Nothing (partitionBySource []) -- Report parse errors to the client @@ -297,9 +297,9 @@ isChanged cache path = toIsChanged $ do module' <- toExceptTLifted $ parseModuleFile path e <- toExceptTLifted $ try @SomeException $ evaluate $ defaultTypeCheck (typecheckModulesWithLocationIncremental (takeWhile ((/= path) . fst) cacheWithoutErrors) [(path, module')]) - (checkedModules, _errors) <- toExceptT $ return e + (checkedModules, errors') <- toExceptT $ return e decls' <- maybeToEitherLSP $ lookup path checkedModules - return $ if null errors && decls' == cachedDecls + return $ if null errors' && null errors && decls' == cachedDecls then NotChanged else HasChanged where diff --git a/rzk/src/Rzk/TypeCheck.hs b/rzk/src/Rzk/TypeCheck.hs index c5a307e12..027c1acd7 100644 --- a/rzk/src/Rzk/TypeCheck.hs +++ b/rzk/src/Rzk/TypeCheck.hs @@ -5,8 +5,6 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE TypeSynonymInstances #-} module Rzk.TypeCheck where import Control.Applicative ((<|>))