Use NormalizedFilePath and adapt types of haskell-lsp-0.21 (#479)

* Use custom version of h-l-t

* Use normalized path functions from h-l-t

* Restore empty path corner case

* Create a wrapper over NFP to override IsString

* Use maybe instead fromMaybe

* Use patched version of lsp-types in all yaml files

* Remove unused import

* Rename specific NormalizeFilePath to NormalizeFilePath'

* Remove specific newtype and IsString instance

* Use released haskell-lsp-0.21

* Adapt to type changes of haskell-lsp-0.21

* Add tags field to CompletionItem

* Fix test case about empty file path

* Correct stack.yaml used in azure ci cache

* Build ghcide including tests in windows azure ci

* Qualify haskell-lsp modules to avoid name clashes
This commit is contained in:
Javier Neira 2020-03-23 09:07:04 +01:00 committed by GitHub
parent 209be0b162
commit 9951f35b08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 87 additions and 197 deletions

View File

@ -17,7 +17,7 @@ jobs:
- checkout: self - checkout: self
- task: Cache@2 - task: Cache@2
inputs: inputs:
key: stack-cache-v2 | $(Agent.OS) | $(Build.SourcesDirectory)/stack.yaml | $(Build.SourcesDirectory)/ghcide.cabal key: stack-cache-v2 | $(Agent.OS) | $(Build.SourcesDirectory)/$(STACK_YAML) | $(Build.SourcesDirectory)/ghcide.cabal
path: .azure-cache path: .azure-cache
cacheHitVar: CACHE_RESTORED cacheHitVar: CACHE_RESTORED
displayName: "Cache stack artifacts" displayName: "Cache stack artifacts"

View File

@ -46,11 +46,9 @@ jobs:
stack install alex --stack-yaml $STACK_YAML stack install alex --stack-yaml $STACK_YAML
stack build --only-dependencies --stack-yaml $STACK_YAML stack build --only-dependencies --stack-yaml $STACK_YAML
displayName: 'stack build --only-dependencies' displayName: 'stack build --only-dependencies'
- bash: stack test --ghc-options=-Werror --stack-yaml $STACK_YAML || stack test --ghc-options=-Werror --stack-yaml $STACK_YAML || stack test --ghc-options=-Werror --stack-yaml $STACK_YAML - bash: stack test --no-run-tests --ghc-options=-Werror --stack-yaml $STACK_YAML
# ghcide stack tests are flaky, see https://github.com/digital-asset/daml/issues/2606.
displayName: 'stack test --ghc-options=-Werror' displayName: 'stack test --ghc-options=-Werror'
# TODO: Enable when failing tests are fixed or marked as broken. See https://github.com/digital-asset/ghcide/issues/474 # TODO: run test suite when failing tests are fixed or marked as broken. See https://github.com/digital-asset/ghcide/issues/474
condition: False
- bash: | - bash: |
mkdir -p .azure-cache mkdir -p .azure-cache
tar -vczf .azure-cache/stack-root.tar.gz $(cygpath $STACK_ROOT) tar -vczf .azure-cache/stack-root.tar.gz $(cygpath $STACK_ROOT)

View File

@ -146,8 +146,8 @@ main = do
ide <- initialise def (cradleRules >> mainRule) (pure $ IdInt 0) (showEvent lock) (logger Info) noopDebouncer options vfs ide <- initialise def (cradleRules >> mainRule) (pure $ IdInt 0) (showEvent lock) (logger Info) noopDebouncer options vfs
putStrLn "\nStep 6/6: Type checking the files" putStrLn "\nStep 6/6: Type checking the files"
setFilesOfInterest ide $ HashSet.fromList $ map toNormalizedFilePath files setFilesOfInterest ide $ HashSet.fromList $ map toNormalizedFilePath' files
results <- runActionSync ide $ uses TypeCheck $ map toNormalizedFilePath files results <- runActionSync ide $ uses TypeCheck $ map toNormalizedFilePath' files
let (worked, failed) = partition fst $ zip (map isJust results) files let (worked, failed) = partition fst $ zip (map isJust results) files
when (failed /= []) $ when (failed /= []) $
putStr $ unlines $ "Files that failed:" : map ((++) " * " . snd) failed putStr $ unlines $ "Files that failed:" : map ((++) " * " . snd) failed
@ -183,7 +183,7 @@ kick = do
-- | Print an LSP event. -- | Print an LSP event.
showEvent :: Lock -> FromServerMessage -> IO () showEvent :: Lock -> FromServerMessage -> IO ()
showEvent _ (EventFileDiagnostics _ []) = return () showEvent _ (EventFileDiagnostics _ []) = return ()
showEvent lock (EventFileDiagnostics (toNormalizedFilePath -> file) diags) = showEvent lock (EventFileDiagnostics (toNormalizedFilePath' -> file) diags) =
withLock lock $ T.putStrLn $ showDiagnosticsColored $ map (file,ShowDiag,) diags withLock lock $ T.putStrLn $ showDiagnosticsColored $ map (file,ShowDiag,) diags
showEvent lock e = withLock lock $ print e showEvent lock e = withLock lock $ print e
@ -199,7 +199,7 @@ loadSession dir = liftIO $ do
let session :: Maybe FilePath -> Action HscEnvEq let session :: Maybe FilePath -> Action HscEnvEq
session file = do session file = do
-- In the absence of a cradle file, just pass the directory from where to calculate an implicit cradle -- In the absence of a cradle file, just pass the directory from where to calculate an implicit cradle
let cradle = toNormalizedFilePath $ fromMaybe dir file let cradle = toNormalizedFilePath' $ fromMaybe dir file
use_ LoadCradle cradle use_ LoadCradle cradle
return $ \file -> session =<< liftIO (cradleLoc file) return $ \file -> session =<< liftIO (cradleLoc file)

View File

@ -36,8 +36,8 @@ import qualified System.Directory.Extra as IO
import System.Environment (lookupEnv) import System.Environment (lookupEnv)
import System.FilePath.Posix (addTrailingPathSeparator, import System.FilePath.Posix (addTrailingPathSeparator,
(</>)) (</>))
import Language.Haskell.LSP.Messages as LSP import qualified Language.Haskell.LSP.Messages as LSP
import Language.Haskell.LSP.Types as LSP import qualified Language.Haskell.LSP.Types as LSP
import Data.Aeson (ToJSON(toJSON)) import Data.Aeson (ToJSON(toJSON))
import Development.IDE.Types.Logger (logDebug) import Development.IDE.Types.Logger (logDebug)

View File

@ -43,8 +43,8 @@ library
filepath, filepath,
haddock-library >= 1.8, haddock-library >= 1.8,
hashable, hashable,
haskell-lsp-types == 0.20.*, haskell-lsp-types == 0.21.*,
haskell-lsp == 0.20.*, haskell-lsp == 0.21.*,
mtl, mtl,
network-uri, network-uri,
prettyprinter-ansi-terminal, prettyprinter-ansi-terminal,

View File

@ -93,7 +93,7 @@ computePackageDeps
computePackageDeps env pkg = do computePackageDeps env pkg = do
let dflags = hsc_dflags env let dflags = hsc_dflags env
case lookupInstalledPackage dflags pkg of case lookupInstalledPackage dflags pkg of
Nothing -> return $ Left [ideErrorText (toNormalizedFilePath noFilePath) $ Nothing -> return $ Left [ideErrorText (toNormalizedFilePath' noFilePath) $
T.pack $ "unknown package: " ++ show pkg] T.pack $ "unknown package: " ++ show pkg]
Just pkgInfo -> return $ Right $ depends pkgInfo Just pkgInfo -> return $ Right $ depends pkgInfo

View File

@ -146,9 +146,10 @@ fileExistsFast getLspId vfs file = do
WorkspaceDidChangeWatchedFiles WorkspaceDidChangeWatchedFiles
(Just (A.toJSON regOptions)) (Just (A.toJSON regOptions))
regOptions = regOptions =
DidChangeWatchedFilesRegistrationOptions { watchers = List [watcher] } DidChangeWatchedFilesRegistrationOptions { _watchers = List [watcher] }
watcher = FileSystemWatcher { globPattern = fromNormalizedFilePath fp watchKind = WatchKind { _watchCreate = True, _watchChange = False, _watchDelete = True}
, kind = Just 5 -- Create and Delete events only watcher = FileSystemWatcher { _globPattern = fromNormalizedFilePath fp
, _kind = Just watchKind
} }
eventer $ ReqRegisterCapability req eventer $ ReqRegisterCapability req
@ -174,29 +175,3 @@ getFileExistsVFS vfs file = do
handle (\(_ :: IOException) -> return False) $ handle (\(_ :: IOException) -> return False) $
(isJust <$> getVirtualFile vfs (filePathToUri' file)) ||^ (isJust <$> getVirtualFile vfs (filePathToUri' file)) ||^
Dir.doesFileExist (fromNormalizedFilePath file) Dir.doesFileExist (fromNormalizedFilePath file)
--------------------------------------------------------------------------------------------------
-- The message definitions below probably belong in haskell-lsp-types
data DidChangeWatchedFilesRegistrationOptions = DidChangeWatchedFilesRegistrationOptions
{ watchers :: List FileSystemWatcher
}
instance A.ToJSON DidChangeWatchedFilesRegistrationOptions where
toJSON DidChangeWatchedFilesRegistrationOptions {..} =
A.object ["watchers" A..= watchers]
data FileSystemWatcher = FileSystemWatcher
{ -- | The glob pattern to watch.
-- For details on glob pattern syntax, check the spec: https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#workspace_didChangeWatchedFiles
globPattern :: String
-- | The kind of event to subscribe to. Defaults to all.
-- Defined as a bitmap of Create(1), Change(2), and Delete(4)
, kind :: Maybe Int
}
instance A.ToJSON FileSystemWatcher where
toJSON FileSystemWatcher {..} =
A.object
$ ["globPattern" A..= globPattern]
++ [ "kind" A..= x | Just x <- [kind] ]

View File

@ -96,7 +96,7 @@ data CPPDiag
diagsFromCPPLogs :: FilePath -> [CPPLog] -> [FileDiagnostic] diagsFromCPPLogs :: FilePath -> [CPPLog] -> [FileDiagnostic]
diagsFromCPPLogs filename logs = diagsFromCPPLogs filename logs =
map (\d -> (toNormalizedFilePath filename, ShowDiag, cppDiagToDiagnostic d)) $ map (\d -> (toNormalizedFilePath' filename, ShowDiag, cppDiagToDiagnostic d)) $
go [] logs go [] logs
where where
-- On errors, CPP calls logAction with a real span for the initial log and -- On errors, CPP calls logAction with a real span for the initial log and
@ -118,7 +118,8 @@ diagsFromCPPLogs filename logs =
_code = Nothing, _code = Nothing,
_source = Just "CPP", _source = Just "CPP",
_message = T.unlines $ cdMessage d, _message = T.unlines $ cdMessage d,
_relatedInformation = Nothing _relatedInformation = Nothing,
_tags = Nothing
} }

View File

@ -80,14 +80,14 @@ useE :: IdeRule k v => k -> NormalizedFilePath -> MaybeT Action v
useE k = MaybeT . use k useE k = MaybeT . use k
useNoFileE :: IdeRule k v => k -> MaybeT Action v useNoFileE :: IdeRule k v => k -> MaybeT Action v
useNoFileE k = useE k "" useNoFileE k = useE k emptyFilePath
usesE :: IdeRule k v => k -> [NormalizedFilePath] -> MaybeT Action [v] usesE :: IdeRule k v => k -> [NormalizedFilePath] -> MaybeT Action [v]
usesE k = MaybeT . fmap sequence . uses k usesE k = MaybeT . fmap sequence . uses k
defineNoFile :: IdeRule k v => (k -> Action v) -> Rules () defineNoFile :: IdeRule k v => (k -> Action v) -> Rules ()
defineNoFile f = define $ \k file -> do defineNoFile f = define $ \k file -> do
if file == "" then do res <- f k; return ([], Just res) else if file == emptyFilePath then do res <- f k; return ([], Just res) else
fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file" fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file"
@ -130,7 +130,7 @@ getHieFile file mod = do
getHomeHieFile :: NormalizedFilePath -> Action ([a], Maybe HieFile) getHomeHieFile :: NormalizedFilePath -> Action ([a], Maybe HieFile)
getHomeHieFile f = do getHomeHieFile f = do
pm <- use_ GetParsedModule f pm <- use_ GetParsedModule f
let normal_hie_f = toNormalizedFilePath hie_f let normal_hie_f = toNormalizedFilePath' hie_f
hie_f = ml_hie_file $ ms_location $ pm_mod_summary pm hie_f = ml_hie_file $ ms_location $ pm_mod_summary pm
mbHieTimestamp <- use GetModificationTime normal_hie_f mbHieTimestamp <- use GetModificationTime normal_hie_f
srcTimestamp <- use_ GetModificationTime f srcTimestamp <- use_ GetModificationTime f
@ -292,9 +292,10 @@ reportImportCyclesRule =
, _message = "Cyclic module dependency between " <> showCycle mods , _message = "Cyclic module dependency between " <> showCycle mods
, _code = Nothing , _code = Nothing
, _relatedInformation = Nothing , _relatedInformation = Nothing
, _tags = Nothing
} }
where loc = srcSpanToLocation (getLoc imp) where loc = srcSpanToLocation (getLoc imp)
fp = toNormalizedFilePath $ srcSpanToFilename (getLoc imp) fp = toNormalizedFilePath' $ srcSpanToFilename (getLoc imp)
getModuleName file = do getModuleName file = do
pm <- use_ GetParsedModule file pm <- use_ GetParsedModule file
pure (moduleNameString . moduleName . ms_mod $ pm_mod_summary pm) pure (moduleNameString . moduleName . ms_mod $ pm_mod_summary pm)

View File

@ -414,7 +414,7 @@ shakeRun IdeState{shakeExtras=ShakeExtras{..}, ..} acts =
Right _ -> "completed" Right _ -> "completed"
profile = case res of profile = case res of
Right (_, Just fp) -> Right (_, Just fp) ->
let link = case filePathToUri' $ toNormalizedFilePath fp of let link = case filePathToUri' $ toNormalizedFilePath' fp of
NormalizedUri _ x -> x NormalizedUri _ x -> x
in ", profile saved at " <> T.unpack link in ", profile saved at " <> T.unpack link
_ -> "" _ -> ""
@ -473,13 +473,13 @@ useWithStale :: IdeRule k v
useWithStale key file = head <$> usesWithStale key [file] useWithStale key file = head <$> usesWithStale key [file]
useNoFile :: IdeRule k v => k -> Action (Maybe v) useNoFile :: IdeRule k v => k -> Action (Maybe v)
useNoFile key = use key "" useNoFile key = use key emptyFilePath
use_ :: IdeRule k v => k -> NormalizedFilePath -> Action v use_ :: IdeRule k v => k -> NormalizedFilePath -> Action v
use_ key file = head <$> uses_ key [file] use_ key file = head <$> uses_ key [file]
useNoFile_ :: IdeRule k v => k -> Action v useNoFile_ :: IdeRule k v => k -> Action v
useNoFile_ key = use_ key "" useNoFile_ key = use_ key emptyFilePath
uses_ :: IdeRule k v => k -> [NormalizedFilePath] -> Action [v] uses_ :: IdeRule k v => k -> [NormalizedFilePath] -> Action [v]
uses_ key files = do uses_ key files = do
@ -819,7 +819,7 @@ filterDiagnostics ::
DiagnosticStore -> DiagnosticStore ->
DiagnosticStore DiagnosticStore
filterDiagnostics keep = filterDiagnostics keep =
HMap.filterWithKey (\uri _ -> maybe True (keep . toNormalizedFilePath) $ uriToFilePath' $ fromNormalizedUri uri) HMap.filterWithKey (\uri _ -> maybe True (keep . toNormalizedFilePath') $ uriToFilePath' $ fromNormalizedUri uri)
filterVersionMap filterVersionMap
:: HMap.HashMap NormalizedUri (Set.Set TextDocumentVersion) :: HMap.HashMap NormalizedUri (Set.Set TextDocumentVersion)

View File

@ -40,7 +40,7 @@ import qualified Outputable as Out
diagFromText :: T.Text -> D.DiagnosticSeverity -> SrcSpan -> T.Text -> FileDiagnostic diagFromText :: T.Text -> D.DiagnosticSeverity -> SrcSpan -> T.Text -> FileDiagnostic
diagFromText diagSource sev loc msg = (toNormalizedFilePath $ srcSpanToFilename loc,ShowDiag,) diagFromText diagSource sev loc msg = (toNormalizedFilePath' $ srcSpanToFilename loc,ShowDiag,)
Diagnostic Diagnostic
{ _range = srcSpanToRange loc { _range = srcSpanToRange loc
, _severity = Just sev , _severity = Just sev
@ -48,6 +48,7 @@ diagFromText diagSource sev loc msg = (toNormalizedFilePath $ srcSpanToFilename
, _message = msg , _message = msg
, _code = Nothing , _code = Nothing
, _relatedInformation = Nothing , _relatedInformation = Nothing
, _tags = Nothing
} }
-- | Produce a GHC-style error from a source span and a message. -- | Produce a GHC-style error from a source span and a message.
@ -80,7 +81,7 @@ srcSpanToFilename (RealSrcSpan real) = FS.unpackFS $ srcSpanFile real
srcSpanToLocation :: SrcSpan -> Location srcSpanToLocation :: SrcSpan -> Location
srcSpanToLocation src = srcSpanToLocation src =
-- important that the URI's we produce have been properly normalized, otherwise they point at weird places in VS Code -- important that the URI's we produce have been properly normalized, otherwise they point at weird places in VS Code
Location (fromNormalizedUri $ filePathToUri' $ toNormalizedFilePath $ srcSpanToFilename src) (srcSpanToRange src) Location (fromNormalizedUri $ filePathToUri' $ toNormalizedFilePath' $ srcSpanToFilename src) (srcSpanToRange src)
isInsideSrcSpan :: Position -> SrcSpan -> Bool isInsideSrcSpan :: Position -> SrcSpan -> Bool
p `isInsideSrcSpan` r = sp <= p && p <= ep p `isInsideSrcSpan` r = sp <= p && p <= ep

View File

@ -139,7 +139,7 @@ moduleImportPath (takeDirectory . fromNormalizedFilePath -> pathDir) pm
-- A for module A.B -- A for module A.B
modDir = modDir =
takeDirectory $ takeDirectory $
fromNormalizedFilePath $ toNormalizedFilePath $ fromNormalizedFilePath $ toNormalizedFilePath' $
moduleNameSlashes $ GHC.moduleName mod' moduleNameSlashes $ GHC.moduleName mod'
-- | An 'HscEnv' with equality. Two values are considered equal -- | An 'HscEnv' with equality. Two values are considered equal

View File

@ -58,7 +58,7 @@ locateModuleFile :: MonadIO m
-> m (Maybe NormalizedFilePath) -> m (Maybe NormalizedFilePath)
locateModuleFile dflags exts doesExist isSource modName = do locateModuleFile dflags exts doesExist isSource modName = do
let candidates = let candidates =
[ toNormalizedFilePath (prefix </> M.moduleNameSlashes modName <.> maybeBoot ext) [ toNormalizedFilePath' (prefix </> M.moduleNameSlashes modName <.> maybeBoot ext)
| prefix <- importPaths dflags, ext <- exts] | prefix <- importPaths dflags, ext <- exts]
findM doesExist candidates findM doesExist candidates
where where

View File

@ -55,7 +55,7 @@ request label getResults notFound found ide (TextDocumentPositionParams (TextDoc
logAndRunRequest :: T.Text -> (NormalizedFilePath -> Position -> Action b) -> IdeState -> Position -> String -> IO b logAndRunRequest :: T.Text -> (NormalizedFilePath -> Position -> Action b) -> IdeState -> Position -> String -> IO b
logAndRunRequest label getResults ide pos path = do logAndRunRequest label getResults ide pos path = do
let filePath = toNormalizedFilePath path let filePath = toNormalizedFilePath' path
logInfo (ideLogger ide) $ logInfo (ideLogger ide) $
label <> " request at position " <> T.pack (showPosition pos) <> label <> " request at position " <> T.pack (showPosition pos) <>
" in file: " <> T.pack path " in file: " <> T.pack path

View File

@ -30,7 +30,7 @@ import Development.IDE.Core.OfInterest
whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO () whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO ()
whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath'
setHandlersNotifications :: PartialHandlers c setHandlersNotifications :: PartialHandlers c
setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
@ -62,7 +62,7 @@ setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
let events = let events =
mapMaybe mapMaybe
(\(FileEvent uri ev) -> (\(FileEvent uri ev) ->
(, ev /= FcDeleted) . toNormalizedFilePath (, ev /= FcDeleted) . toNormalizedFilePath'
<$> LSP.uriToFilePath uri <$> LSP.uriToFilePath uri
) )
( F.toList fileEvents ) ( F.toList fileEvents )

View File

@ -37,7 +37,7 @@ moduleOutline
:: LSP.LspFuncs c -> IdeState -> DocumentSymbolParams -> IO (Either ResponseError DSResult) :: LSP.LspFuncs c -> IdeState -> DocumentSymbolParams -> IO (Either ResponseError DSResult)
moduleOutline _lsp ideState DocumentSymbolParams { _textDocument = TextDocumentIdentifier uri } moduleOutline _lsp ideState DocumentSymbolParams { _textDocument = TextDocumentIdentifier uri }
= case uriToFilePath uri of = case uriToFilePath uri of
Just (toNormalizedFilePath -> fp) -> do Just (toNormalizedFilePath' -> fp) -> do
mb_decls <- runAction ideState $ use GetParsedModule fp mb_decls <- runAction ideState $ use GetParsedModule fp
pure $ Right $ case mb_decls of pure $ Right $ case mb_decls of
Nothing -> DSDocumentSymbols (List []) Nothing -> DSDocumentSymbols (List [])

View File

@ -64,7 +64,7 @@ codeAction lsp state (TextDocumentIdentifier uri) _range CodeActionContext{_diag
-- logInfo (ideLogger ide) $ T.pack $ "Code action req: " ++ show arg -- logInfo (ideLogger ide) $ T.pack $ "Code action req: " ++ show arg
contents <- LSP.getVirtualFileFunc lsp $ toNormalizedUri uri contents <- LSP.getVirtualFileFunc lsp $ toNormalizedUri uri
let text = Rope.toText . (_text :: VirtualFile -> Rope.Rope) <$> contents let text = Rope.toText . (_text :: VirtualFile -> Rope.Rope) <$> contents
mbFile = toNormalizedFilePath <$> uriToFilePath uri mbFile = toNormalizedFilePath' <$> uriToFilePath uri
(ideOptions, parsedModule, join -> env) <- runAction state $ (ideOptions, parsedModule, join -> env) <- runAction state $
(,,) <$> getIdeOptions (,,) <$> getIdeOptions
<*> getParsedModule `traverse` mbFile <*> getParsedModule `traverse` mbFile
@ -85,7 +85,7 @@ codeLens
-> IO (Either ResponseError (List CodeLens)) -> IO (Either ResponseError (List CodeLens))
codeLens _lsp ideState CodeLensParams{_textDocument=TextDocumentIdentifier uri} = do codeLens _lsp ideState CodeLensParams{_textDocument=TextDocumentIdentifier uri} = do
fmap (Right . List) $ case uriToFilePath' uri of fmap (Right . List) $ case uriToFilePath' uri of
Just (toNormalizedFilePath -> filePath) -> do Just (toNormalizedFilePath' -> filePath) -> do
_ <- runAction ideState $ runMaybeT $ useE TypeCheck filePath _ <- runAction ideState $ runMaybeT $ useE TypeCheck filePath
diag <- getDiagnostics ideState diag <- getDiagnostics ideState
hDiag <- getHiddenDiagnostics ideState hDiag <- getHiddenDiagnostics ideState

View File

@ -66,7 +66,7 @@ getCompletionsLSP lsp ide
contents <- LSP.getVirtualFileFunc lsp $ toNormalizedUri uri contents <- LSP.getVirtualFileFunc lsp $ toNormalizedUri uri
fmap Right $ case (contents, uriToFilePath' uri) of fmap Right $ case (contents, uriToFilePath' uri) of
(Just cnts, Just path) -> do (Just cnts, Just path) -> do
let npath = toNormalizedFilePath path let npath = toNormalizedFilePath' path
(ideOpts, compls) <- runAction ide $ do (ideOpts, compls) <- runAction ide $ do
opts <- getIdeOptions opts <- getIdeOptions
compls <- useWithStale ProduceCompletions npath compls <- useWithStale ProduceCompletions npath

View File

@ -132,7 +132,7 @@ occNameToComKind ty oc
mkCompl :: IdeOptions -> CompItem -> CompletionItem mkCompl :: IdeOptions -> CompItem -> CompletionItem
mkCompl IdeOptions{..} CI{origName,importedFrom,thingType,label,isInfix,docs} = mkCompl IdeOptions{..} CI{origName,importedFrom,thingType,label,isInfix,docs} =
CompletionItem label kind ((colon <>) <$> typeText) CompletionItem label kind (List []) ((colon <>) <$> typeText)
(Just $ CompletionDocMarkup $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator docs') (Just $ CompletionDocMarkup $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator docs')
Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet) Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet)
Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
@ -186,13 +186,13 @@ getArgText typ = argText
mkModCompl :: T.Text -> CompletionItem mkModCompl :: T.Text -> CompletionItem
mkModCompl label = mkModCompl label =
CompletionItem label (Just CiModule) Nothing CompletionItem label (Just CiModule) (List []) Nothing
Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
mkImportCompl :: T.Text -> T.Text -> CompletionItem mkImportCompl :: T.Text -> T.Text -> CompletionItem
mkImportCompl enteredQual label = mkImportCompl enteredQual label =
CompletionItem m (Just CiModule) (Just label) CompletionItem m (Just CiModule) (List []) (Just label)
Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
where where
@ -200,13 +200,13 @@ mkImportCompl enteredQual label =
mkExtCompl :: T.Text -> CompletionItem mkExtCompl :: T.Text -> CompletionItem
mkExtCompl label = mkExtCompl label =
CompletionItem label (Just CiKeyword) Nothing CompletionItem label (Just CiKeyword) (List []) Nothing
Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
mkPragmaCompl :: T.Text -> T.Text -> CompletionItem mkPragmaCompl :: T.Text -> T.Text -> CompletionItem
mkPragmaCompl label insertText = mkPragmaCompl label insertText =
CompletionItem label (Just CiKeyword) Nothing CompletionItem label (Just CiKeyword) (List []) Nothing
Nothing Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet) Nothing Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet)
Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing

View File

@ -38,7 +38,8 @@ ideErrorText fp msg = (fp, ShowDiag, LSP.Diagnostic {
_code = Nothing, _code = Nothing,
_source = Just "compiler", _source = Just "compiler",
_message = msg, _message = msg,
_relatedInformation = Nothing _relatedInformation = Nothing,
_tags = Nothing
}) })
-- | Defines whether a particular diagnostic should be reported -- | Defines whether a particular diagnostic should be reported

View File

@ -10,15 +10,15 @@ module Development.IDE.Types.Location
, Position(..) , Position(..)
, showPosition , showPosition
, Range(..) , Range(..)
, Uri(..) , LSP.Uri(..)
, NormalizedUri , LSP.NormalizedUri
, LSP.toNormalizedUri , LSP.toNormalizedUri
, LSP.fromNormalizedUri , LSP.fromNormalizedUri
, NormalizedFilePath , LSP.NormalizedFilePath
, fromUri , fromUri
, toNormalizedFilePath , emptyFilePath
, fromNormalizedFilePath , toNormalizedFilePath'
, filePathToUri , LSP.fromNormalizedFilePath
, filePathToUri' , filePathToUri'
, uriToFilePath' , uriToFilePath'
, readSrcSpan , readSrcSpan
@ -26,135 +26,40 @@ module Development.IDE.Types.Location
import Control.Applicative import Control.Applicative
import Language.Haskell.LSP.Types (Location(..), Range(..), Position(..)) import Language.Haskell.LSP.Types (Location(..), Range(..), Position(..))
import Control.DeepSeq
import Control.Monad import Control.Monad
import Data.Binary import Data.Hashable (Hashable(hash))
import Data.Maybe as Maybe
import Data.Hashable
import Data.String import Data.String
import qualified Data.Text as T
import FastString import FastString
import Network.URI
import System.FilePath
import qualified System.FilePath.Posix as FPP
import qualified System.FilePath.Windows as FPW
import System.Info.Extra
import qualified Language.Haskell.LSP.Types as LSP import qualified Language.Haskell.LSP.Types as LSP
import Language.Haskell.LSP.Types as LSP (
filePathToUri
, NormalizedUri(..)
, Uri(..)
, toNormalizedUri
, fromNormalizedUri
)
import SrcLoc as GHC import SrcLoc as GHC
import Text.ParserCombinators.ReadP as ReadP import Text.ParserCombinators.ReadP as ReadP
import GHC.Generics import Data.Maybe (fromMaybe)
toNormalizedFilePath' :: FilePath -> LSP.NormalizedFilePath
-- | Newtype wrapper around FilePath that always has normalized slashes.
-- The NormalizedUri and hash of the FilePath are cached to avoided
-- repeated normalisation when we need to compute them (which is a lot).
--
-- This is one of the most performance critical parts of ghcide, do not
-- modify it without profiling.
data NormalizedFilePath = NormalizedFilePath NormalizedUriWrapper !Int !FilePath
deriving (Generic, Eq, Ord)
instance NFData NormalizedFilePath where
instance Binary NormalizedFilePath where
put (NormalizedFilePath _ _ fp) = put fp
get = do
v <- Data.Binary.get :: Get FilePath
return (toNormalizedFilePath v)
instance Show NormalizedFilePath where
show (NormalizedFilePath _ _ fp) = "NormalizedFilePath " ++ show fp
instance Hashable NormalizedFilePath where
hash (NormalizedFilePath _ h _) = h
-- Just to define NFData and Binary
newtype NormalizedUriWrapper =
NormalizedUriWrapper { unwrapNormalizedFilePath :: NormalizedUri }
deriving (Show, Generic, Eq, Ord)
instance NFData NormalizedUriWrapper where
rnf = rwhnf
instance Hashable NormalizedUriWrapper where
instance IsString NormalizedFilePath where
fromString = toNormalizedFilePath
toNormalizedFilePath :: FilePath -> NormalizedFilePath
-- We want to keep empty paths instead of normalising them to "." -- We want to keep empty paths instead of normalising them to "."
toNormalizedFilePath "" = NormalizedFilePath (NormalizedUriWrapper emptyPathUri) (hash ("" :: String)) "" toNormalizedFilePath' "" = emptyFilePath
toNormalizedFilePath fp = toNormalizedFilePath' fp = LSP.toNormalizedFilePath fp
let nfp = normalise fp
in NormalizedFilePath (NormalizedUriWrapper $ filePathToUriInternal' nfp) (hash nfp) nfp
fromNormalizedFilePath :: NormalizedFilePath -> FilePath emptyFilePath :: LSP.NormalizedFilePath
fromNormalizedFilePath (NormalizedFilePath _ _ fp) = fp emptyFilePath = LSP.NormalizedFilePath emptyPathUri ""
-- | We use an empty string as a filepath when we dont have a file. -- | We use an empty string as a filepath when we dont have a file.
-- However, haskell-lsp doesnt support that in uriToFilePath and given -- However, haskell-lsp doesnt support that in uriToFilePath and given
-- that it is not a valid filepath it does not make sense to upstream a fix. -- that it is not a valid filepath it does not make sense to upstream a fix.
-- So we have our own wrapper here that supports empty filepaths. -- So we have our own wrapper here that supports empty filepaths.
uriToFilePath' :: Uri -> Maybe FilePath uriToFilePath' :: LSP.Uri -> Maybe FilePath
uriToFilePath' uri uriToFilePath' uri
| uri == fromNormalizedUri emptyPathUri = Just "" | uri == LSP.fromNormalizedUri emptyPathUri = Just ""
| otherwise = LSP.uriToFilePath uri | otherwise = LSP.uriToFilePath uri
emptyPathUri :: NormalizedUri emptyPathUri :: LSP.NormalizedUri
emptyPathUri = filePathToUriInternal' "" emptyPathUri = LSP.NormalizedUri (hash ("" :: String)) ""
filePathToUri' :: NormalizedFilePath -> NormalizedUri filePathToUri' :: LSP.NormalizedFilePath -> LSP.NormalizedUri
filePathToUri' (NormalizedFilePath (NormalizedUriWrapper u) _ _) = u filePathToUri' = LSP.normalizedFilePathToUri
filePathToUriInternal' :: FilePath -> NormalizedUri
filePathToUriInternal' fp = toNormalizedUri $ Uri $ T.pack $ LSP.fileScheme <> "//" <> platformAdjustToUriPath fp
where
-- The definitions below are variants of the corresponding functions in Language.Haskell.LSP.Types.Uri that assume that
-- the filepath has already been normalised. This is necessary since normalising the filepath has a nontrivial cost.
toNormalizedUri :: Uri -> NormalizedUri
toNormalizedUri (Uri t) =
let fp = T.pack $ escapeURIString isUnescapedInURI $ unEscapeString $ T.unpack t
in NormalizedUri (hash fp) fp
platformAdjustToUriPath :: FilePath -> String
platformAdjustToUriPath srcPath
| isWindows = '/' : escapedPath
| otherwise = escapedPath
where
(splitDirectories, splitDrive)
| isWindows =
(FPW.splitDirectories, FPW.splitDrive)
| otherwise =
(FPP.splitDirectories, FPP.splitDrive)
escapedPath =
case splitDrive srcPath of
(drv, rest) ->
convertDrive drv `FPP.joinDrive`
FPP.joinPath (map (escapeURIString unescaped) $ splitDirectories rest)
-- splitDirectories does not remove the path separator after the drive so
-- we do a final replacement of \ to /
convertDrive drv
| isWindows && FPW.hasTrailingPathSeparator drv =
FPP.addTrailingPathSeparator (init drv)
| otherwise = drv
unescaped c
| isWindows = isUnreserved c || c `elem` [':', '\\', '/']
| otherwise = isUnreserved c || c == '/'
fromUri :: LSP.NormalizedUri -> NormalizedFilePath
fromUri = toNormalizedFilePath . fromMaybe noFilePath . uriToFilePath' . fromNormalizedUri
fromUri :: LSP.NormalizedUri -> LSP.NormalizedFilePath
fromUri = fromMaybe (toNormalizedFilePath' noFilePath) . LSP.uriToNormalizedFilePath
noFilePath :: FilePath noFilePath :: FilePath
noFilePath = "<unknown>" noFilePath = "<unknown>"

View File

@ -2,9 +2,9 @@ resolver: nightly-2019-09-16
packages: packages:
- . - .
extra-deps: extra-deps:
- haskell-lsp-0.20.0.0 - haskell-lsp-0.21.0.0
- haskell-lsp-types-0.20.0.0 - haskell-lsp-types-0.21.0.0
- lsp-test-0.10.1.0 - lsp-test-0.10.2.0
- hie-bios-0.4.0 - hie-bios-0.4.0
- ghc-lib-parser-8.8.1 - ghc-lib-parser-8.8.1
- ghc-lib-8.8.1 - ghc-lib-8.8.1

View File

@ -2,9 +2,9 @@ resolver: nightly-2019-09-21
packages: packages:
- . - .
extra-deps: extra-deps:
- haskell-lsp-0.20.0.0 - haskell-lsp-0.21.0.0
- haskell-lsp-types-0.20.0.0 - haskell-lsp-types-0.21.0.0
- lsp-test-0.10.1.0 - lsp-test-0.10.2.0
- hie-bios-0.4.0 - hie-bios-0.4.0
- fuzzy-0.1.0.0 - fuzzy-0.1.0.0
- regex-pcre-builtin-0.95.1.1.8.43 - regex-pcre-builtin-0.95.1.1.8.43

View File

@ -5,9 +5,9 @@ packages:
extra-deps: extra-deps:
- aeson-1.4.6.0 - aeson-1.4.6.0
- base-orphans-0.8.2 - base-orphans-0.8.2
- haskell-lsp-0.20.0.0 - haskell-lsp-0.21.0.0
- haskell-lsp-types-0.20.0.0 - haskell-lsp-types-0.21.0.0
- lsp-test-0.10.1.0 - lsp-test-0.10.2.0
- rope-utf16-splay-0.3.1.0 - rope-utf16-splay-0.3.1.0
- filepattern-0.1.1 - filepattern-0.1.1
- js-dgtable-0.5.2 - js-dgtable-0.5.2

View File

@ -2,5 +2,9 @@ resolver: nightly-2020-02-13
packages: packages:
- . - .
extra-deps: extra-deps:
- haskell-lsp-0.21.0.0
- haskell-lsp-types-0.21.0.0
- lsp-test-0.10.2.0
nix: nix:
packages: [zlib] packages: [zlib]

View File

@ -1658,6 +1658,7 @@ completionTests
complItem label kind ty = CompletionItem complItem label kind ty = CompletionItem
{ _label = label { _label = label
, _kind = kind , _kind = kind
, _tags = List []
, _detail = (":: " <>) <$> ty , _detail = (":: " <>) <$> ty
, _documentation = Nothing , _documentation = Nothing
, _deprecated = Nothing , _deprecated = Nothing
@ -1675,6 +1676,7 @@ completionTests
keywordItem label = CompletionItem keywordItem label = CompletionItem
{ _label = label { _label = label
, _kind = Just CiKeyword , _kind = Just CiKeyword
, _tags = List []
, _detail = Nothing , _detail = Nothing
, _documentation = Nothing , _documentation = Nothing
, _deprecated = Nothing , _deprecated = Nothing
@ -2104,8 +2106,10 @@ findCodeAction doc range t = head <$> findCodeActions doc range [t]
unitTests :: TestTree unitTests :: TestTree
unitTests = do unitTests = do
testGroup "Unit" testGroup "Unit"
[ testCase "empty file path" $ [ testCase "empty file path does NOT work with the empty String literal" $
uriToFilePath' (fromNormalizedUri $ filePathToUri' "") @?= Just "" uriToFilePath' (fromNormalizedUri $ filePathToUri' "") @?= Just "."
, testCase "empty file path works using toNormalizedFilePath'" $
uriToFilePath' (fromNormalizedUri $ filePathToUri' (toNormalizedFilePath' "")) @?= Just ""
] ]
-- | Wrapper around 'LSPTest.openDoc'' that sends file creation events -- | Wrapper around 'LSPTest.openDoc'' that sends file creation events