Skip to content

Commit

Permalink
Fix performance regression introduced by filepath normalisation (hask…
Browse files Browse the repository at this point in the history
…ell/ghcide#303)

We already normalise filepaths in NormalizedFilePath. haskell-lsp
changed things such that the conversion from Uri to NormalizedUri
normalises the filepath again which caused a significant slowdown in
GetFileExists.

We already have a wrapper for converting from NormalizedFilePath to
NormalizedUri so this PR changes this wrapper to inline the definition
without the additional layer of normalisation.

fixes haskell/ghcide#298
  • Loading branch information
cocreature authored Jan 3, 2020
1 parent 139dbfa commit 7a573a5
Showing 1 changed file with 39 additions and 1 deletion.
40 changes: 39 additions & 1 deletion ghcide/src/Development/IDE/Types/Location.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ import Data.Binary
import Data.Maybe as Maybe
import Data.Hashable
import Data.String
import qualified Data.Text as T
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 Language.Haskell.LSP.Types as LSP (
filePathToUri
Expand Down Expand Up @@ -65,7 +70,40 @@ uriToFilePath' uri
| otherwise = LSP.uriToFilePath uri

filePathToUri' :: NormalizedFilePath -> NormalizedUri
filePathToUri' = toNormalizedUri . filePathToUri . fromNormalizedFilePath
filePathToUri' (NormalizedFilePath 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) =
NormalizedUri $ T.pack $ escapeURIString isUnescapedInURI $ unEscapeString $ T.unpack t

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
Expand Down

0 comments on commit 7a573a5

Please sign in to comment.